Skip to content

Commit

Permalink
2024 - Day 17 - part 1&2 - the latter more or less copied from the ko…
Browse files Browse the repository at this point in the history
…tlin-slack-aoc-channel.
  • Loading branch information
fmmr committed Dec 17, 2024
1 parent 6bd8316 commit 1aad7d1
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/main/kotlin/no/rodland/advent_2024/Day17.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package no.rodland.advent_2024

import no.rodland.advent.Day

// template generated: 17/12/2024
// Fredrik Rødland 2024

class Day17(val input: List<String>) : Day<String, Long, Day17.Computer> {
private val comp = input.parse()

override fun partOne(): String {
return comp.run()
}

// solution more-or-less from: https://kotlinlang.slack.com/archives/C87V9MQFK/p1734411603798899
override fun partTwo(): Long {
return recurse(comp.opcodes)
}

private fun recurse(target: List<Int>): Long {
var a = if (target.size == 1) 0 else recurse(target.drop(1)) shl 3
while (comp.copy(a = a).run() != target.joinToString(",")) {
a++
}
return a
}

fun run(a: Long): String = comp.copy(a = a).run()

data class Computer(var a: Long, var b: Long, var c: Long, val opcodes: List<Int>) {
private var ip = 0
private val output = mutableListOf<Int>()

fun output() = output.joinToString(",")
fun output(operand: Int) = output.add(operand)

private fun combo(i: Int): Long {
return when (i) {
in 0..3 -> i.toLong()
4 -> a
5 -> b
6 -> c
7 -> error("Reserved combo operand: $i")
else -> error("Invalid combo operand: $i")
}
}

fun run(): String {
while (ip < opcodes.size) {
val opcode = opcodes[ip]
val operand = opcodes[ip + 1]
when (opcode) {
0 -> a = a shr combo(operand).toInt()
1 -> b = b xor operand.toLong()
2 -> b = combo(operand) % 8
3 -> if (a != 0L) ip = operand - 2
4 -> b = b xor c
5 -> output((combo(operand) % 8).toInt())
6 -> b = a shr combo(operand).toInt()
7 -> c = a shr combo(operand).toInt()
else -> error("Invalid opcode: $opcode")
}
ip += 2
}
return output()
}
}

override fun List<String>.parse(): Computer {
val a = get(0).substringAfter("A: ").toLong()
val b = get(1).substringAfter("B: ").toLong()
val c = get(2).substringAfter("C: ").toLong()
val op = get(4).substringAfter("Program: ").split(",").map { it.toInt() }
return Computer(a, b, c, op)
}

override val day = "17".toInt()
}
128 changes: 128 additions & 0 deletions src/test/kotlin/no/rodland/advent_2024/Day17Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package no.rodland.advent_2024

import no.rodland.advent.*
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import readFile

//
// run: download_aoc_input.sh to download input
//

@Suppress("ClassName", "SpellCheckingInspection")
@DisableSlow
internal class Day17Test {
private val data17 = "2024/input_17.txt".readFile()
private val test17 = "2024/input_17_test.txt".readFile()
private val test17Part2 = "2024/input_17_test_part2.txt".readFile()

private val resultTestOne = "4,6,3,5,6,3,5,2,1,0"
private val resultTestTwo = 117440L
private val resultOne = "4,0,4,7,1,2,7,1,6"
private val resultTwo = 202322348616234L

val test = run {
val liveDay = Day17(data17)
val testDay = Day17(test17)
val testDayPart2 = Day17(test17Part2)
AOCTestSuite<Any?, Unit, Unit>(
AOCTest({ liveDay.partOne() }, Unit, resultOne, 1000, liveDay.day, Part.ONE, true),
AOCTest({ liveDay.partTwo() }, Unit, resultTwo, 1000, liveDay.day, Part.TWO, true),
AOCTest({ testDay.partOne() }, Unit, resultTestOne, 1, liveDay.day, Part.ONE, false),
AOCTest({ testDayPart2.partTwo() }, Unit, resultTestTwo, 1, liveDay.day, Part.TWO, false),
AOCTest<Unit, Unit>({ Day17(data17) }, Unit, Unit, 100, liveDay.day, Part.INIT, live = true),
AOCTest<Unit, Unit>({ Day17(test17Part2) }, Unit, Unit, 100, liveDay.day, Part.INIT, live = false),
)
}

@Nested
inner class Init {
@Test
fun `17,-,bst,1`() {
val computer = Day17.Computer(0, 0, 9, listOf(2, 6))
computer.run({ computer.b }, 1)
}

@Test
fun `17,-,out,1`() {
val computer = Day17.Computer(10, 0, 0, listOf(5, 0, 5, 1, 5, 4))
computer.run({ computer.output() }, "0,1,2")
}

@Test
fun `17,-,out2,1`() {
val computer = Day17.Computer(2024, 0, 0, listOf(0, 1, 5, 4, 3, 0))
computer.run({ computer.output() to computer.a }, "4,2,5,6,7,7,7,7,3,1,0" to 0L)
}

@Test
fun `17,-,bxl,1`() {
val computer = Day17.Computer(0, 29, 0, listOf(1, 7))
computer.run({ computer.b }, 26)
}

@Test
fun `17,-,bxc,1`() {
val computer = Day17.Computer(0, 2024, 43690, listOf(4, 0))
computer.run({ computer.b }, 44354)
}

@Test
fun `17,-,original,1`() {
val org = listOf(0, 3, 5, 4, 3, 0)
val computer = Day17.Computer(117440, 0, 0, org)
computer.run({ computer.output() }, org.joinToString(","))
}

private fun <T> Day17.Computer.run(function: (Unit) -> T, expected: T, part: Part = Part.ONE) {
run()
report(AOCTest(function, Unit, expected, 1, "17".toInt(), part, false, "2,6"))
}

@Test
fun `17,-,example,1`() {
report(AOCTest({ "123".toInt() }, Unit, 123, 5, "17".toInt(), Part.TWO, false, "example"))
}

@Test
fun `17,-,example,2`() {
report(test.initTest.copy())
}

@Test
fun `17,-,test,init`() {
report(test.initTest)
}

@Test
fun `17,-,live,init`() {
report(test.initLive)
}
}

@Nested
inner class `Part 1` {
@Test
fun `17,1,test`() {
report(test.testPart1)
}

@Test
fun `17,1,live,1`() {
report(test.livePart1)
}
}

@Nested
inner class `Part 2` {
@Test
fun `17,2,test`() {
report(test.testPart2)
}

@Test
fun `17,2,live,1`() {
report(test.livePart2)
}
}
}
5 changes: 5 additions & 0 deletions src/test/resources/2024/input_17_test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Register A: 729
Register B: 0
Register C: 0

Program: 0,1,5,4,3,0
5 changes: 5 additions & 0 deletions src/test/resources/2024/input_17_test_part2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Register A: 2024
Register B: 0
Register C: 0

Program: 0,3,5,4,3,0

0 comments on commit 1aad7d1

Please sign in to comment.