Skip to content

Commit 2c1a597

Browse files
committed
Day 13
1 parent 3d10329 commit 2c1a597

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package common.datastructures
2+
3+
import kotlin.math.abs
4+
5+
6+
data class LongPoint(val col: Long, val row: Long) {
7+
val x = col
8+
val y = row
9+
10+
companion object {
11+
val Zero = LongPoint(0, 0)
12+
13+
fun parse(line: String): LongPoint {
14+
val (x, y) = line.split(",").map { it.toLong() }
15+
return LongPoint(x, y)
16+
}
17+
}
18+
19+
operator fun plus(other: LongPoint) = LongPoint(col + other.col, row + other.row)
20+
operator fun minus(other: LongPoint) = LongPoint(col - other.col, row - other.row)
21+
operator fun times(n: Long) = LongPoint(col * n, row * n)
22+
23+
fun manhattanDistTo(other: LongPoint) = abs(x - other.x) + abs(y - other.y)
24+
25+
fun inBounds(numRows: Long, numCols: Long): Boolean {
26+
return x in 0..<numCols && y in 0..<numRows
27+
}
28+
}

src/main/kotlin/common/ext/String.kt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package common.ext
2+
3+
fun String.substringBetween(delimiterBefore: String, delimiterAfter: String): String {
4+
return substringAfter(delimiterBefore).substringBefore(delimiterAfter)
5+
}

src/main/kotlin/y24/Day13.kt

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package y24
2+
3+
import common.puzzle.solvePuzzle
4+
import common.puzzle.Input
5+
import common.puzzle.Puzzle
6+
import common.datastructures.*
7+
import common.ext.*
8+
import common.util.*
9+
import java.util.*
10+
import kotlin.math.*
11+
import kotlin.system.exitProcess
12+
13+
14+
fun main() = solvePuzzle(year = 2024, day = 13) { Day13(it) }
15+
16+
class Day13(val input: Input) : Puzzle {
17+
private val costA = 3
18+
private val costB = 1
19+
20+
data class Machine(
21+
val a: LongPoint,
22+
val b: LongPoint,
23+
val prize: LongPoint,
24+
)
25+
26+
private fun parseButton(line: String): LongPoint {
27+
val x = line.substringBetween("X+", ",").toLong()
28+
val y = line.substringAfter("Y+").toLong()
29+
return LongPoint(x, y)
30+
}
31+
32+
private fun parsePrize(line: String): LongPoint {
33+
val x = line.substringBetween("X=", ",").toLong()
34+
val y = line.substringAfter("Y=").toLong()
35+
return LongPoint(x, y)
36+
}
37+
38+
private fun parseInput(lines: List<String>): List<Machine> {
39+
val n = (lines.size + 1) / 4
40+
return (0..<n).map { i ->
41+
val a = parseButton(lines[i * 4])
42+
val b = parseButton(lines[i * 4 + 1])
43+
val prize = parsePrize(lines[i * 4 + 2])
44+
Machine(a, b, prize)
45+
}
46+
}
47+
48+
private fun minCost(machine: Machine): Long? {
49+
// Using Cramer's method
50+
val d = (machine.a.x * machine.b.y) - (machine.b.x * machine.a.y)
51+
if (d == 0L) {
52+
return null
53+
}
54+
55+
val dx = (machine.prize.x * machine.b.y) - (machine.b.x * machine.prize.y)
56+
val dy = (machine.a.x * machine.prize.y) - (machine.prize.x * machine.a.y)
57+
58+
if (dx % d != 0L) {
59+
return null
60+
}
61+
val m = dx / d
62+
63+
if (dy %d != 0L) {
64+
return null
65+
}
66+
val n = dy / d
67+
68+
return m * costA + n * costB
69+
}
70+
71+
override fun solveLevel1(): Any {
72+
val machines = parseInput(input.lines)
73+
return machines.mapNotNull { minCost(it) }.sum()
74+
}
75+
76+
override fun solveLevel2(): Any {
77+
val machines = parseInput(input.lines).map { machine ->
78+
machine.copy(
79+
prize = machine.prize.copy(
80+
col = machine.prize.col + 10000000000000L,
81+
row = machine.prize.row + 10000000000000L,
82+
)
83+
)
84+
}
85+
return machines.mapNotNull { minCost(it) }.sum()
86+
}
87+
}

src/test/kotlin/y24/Day13Test.kt

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package y24
2+
3+
import common.puzzle.Input
4+
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Test
6+
7+
internal class Day13Test {
8+
private val sample = Input("""
9+
Button A: X+94, Y+34
10+
Button B: X+22, Y+67
11+
Prize: X=8400, Y=5400
12+
13+
Button A: X+26, Y+66
14+
Button B: X+67, Y+21
15+
Prize: X=12748, Y=12176
16+
17+
Button A: X+17, Y+86
18+
Button B: X+84, Y+37
19+
Prize: X=7870, Y=6450
20+
21+
Button A: X+69, Y+23
22+
Button B: X+27, Y+71
23+
Prize: X=18641, Y=10279
24+
""".trimIndent())
25+
26+
private val day = Day13(sample)
27+
28+
@Test
29+
fun solveLevel1() {
30+
assertEquals(480L, day.solveLevel1())
31+
}
32+
33+
@Test
34+
fun solveLevel2() {
35+
// No sample
36+
}
37+
}

0 commit comments

Comments
 (0)