Skip to content

Commit 823a595

Browse files
authored
Merge pull request #24 from sraaphorst/D13
Day 13 complete.
2 parents 773be03 + cb12df5 commit 823a595

File tree

4 files changed

+133
-5
lines changed

4 files changed

+133
-5
lines changed

src/main/kotlin/common/intpos2d/intpos2d.kt

+23-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ package common.intpos2d
55

66
typealias IntPos2D = Pair<Int, Int>
77

8+
val Zero2D = IntPos2D(0, 0)
9+
10+
fun IntPos2D.isZero() =
11+
this == Zero2D
12+
13+
fun IntPos2D.isNotZero() =
14+
!isZero()
15+
816
operator fun IntPos2D.plus(other: IntPos2D) =
917
IntPos2D(this.first + other.first, this.second + other.second)
1018

@@ -20,6 +28,21 @@ operator fun Int.times(pos: IntPos2D): IntPos2D =
2028
operator fun IntPos2D.times(factor: Int) =
2129
factor * this
2230

31+
operator fun IntPos2D.times(other: IntPos2D): IntPos2D =
32+
IntPos2D(first * other.first, second * other.second)
33+
34+
operator fun IntPos2D.rem(modulus: Int): IntPos2D =
35+
IntPos2D(this.first % modulus, this.second % modulus)
36+
37+
operator fun IntPos2D.div(denominator: Int): IntPos2D =
38+
IntPos2D(this.first / denominator, this.second / denominator)
39+
40+
infix fun IntPos2D.dot(other: IntPos2D): Int =
41+
first * other.first + second * other.second
42+
43+
fun IntPos2D.x(): Int = first
44+
fun IntPos2D.y(): Int = second
45+
2346
enum class Direction(val delta: IntPos2D) {
2447
NORTH(IntPos2D(-1, 0)),
2548
EAST(IntPos2D(0, 1)),
@@ -48,10 +71,6 @@ enum class Direction(val delta: IntPos2D) {
4871
}
4972
}
5073

51-
//fun IntPos2D.neighbours(rows: Int, cols: Int): List<IntPos2D> =
52-
// Direction.entries.map { this + it.delta }
53-
// .filter { it.first in 0 until rows && it.second in 0 until rows }
54-
5574
val Diagonals: Set<Pair<Direction, Direction>> = setOf(
5675
Pair(Direction.NORTH, Direction.WEST),
5776
Pair(Direction.WEST, Direction.SOUTH),

src/main/kotlin/day13/day13.kt

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Advent of Code 2024, Day 13.
2+
// By Sebastian Raaphorst, 2024.
3+
4+
package day13
5+
6+
import common.aocreader.fetchAdventOfCodeInput
7+
import common.intpos2d.*
8+
import common.runner.timedFunction
9+
import java.math.BigInteger
10+
11+
private typealias BigIntPos = Pair<BigInteger, BigInteger>
12+
13+
data class Machine(val deltaA: IntPos2D, val deltaB: IntPos2D, val prizePos: IntPos2D) {
14+
/**
15+
* Let a be the number of times we hit button A, and b be the number of times we hit button B.
16+
* We want an integer solution to the equation:
17+
* [dAx dBx] (a) = (px)
18+
* [dAy dBy] (b) (py)
19+
*/
20+
fun calculateSolution(adjustment: BigIntPos): BigIntPos? {
21+
val determinant = deltaA.x() * deltaB.y() - deltaB.x() * deltaA.y()
22+
if (determinant == 0) return null
23+
val detBigInteger = determinant.toBigInteger()
24+
25+
val adjPx = prizePos.x().toBigInteger() + adjustment.first
26+
val adjPy = prizePos.y().toBigInteger() + adjustment.second
27+
val abDet = BigIntPos(
28+
deltaB.y().toBigInteger() * adjPx - deltaB.x().toBigInteger() * adjPy,
29+
deltaA.x().toBigInteger() * adjPy - deltaA.y().toBigInteger() * adjPx)
30+
31+
val detX = abDet.first % detBigInteger
32+
val detY = abDet.second % detBigInteger
33+
34+
return if (detX == BigInteger.ZERO && detY == BigInteger.ZERO)
35+
BigIntPos(abDet.first / detBigInteger, abDet.second / detBigInteger)
36+
else null
37+
}
38+
39+
companion object {
40+
val TokenCostA = 3.toBigInteger()
41+
val TokenCostB = BigInteger.ONE
42+
43+
private val MachineRegex = """[XY][+=](\d+)""".toRegex()
44+
45+
fun parse(input: String): Machine {
46+
val matches = MachineRegex.findAll(input).map { it.groupValues[1].toInt() }.toList()
47+
val (deltaA, deltaB, prizePos) = matches.chunked(2).map { IntPos2D(it[0], it[1]) }
48+
return Machine(deltaA, deltaB, prizePos)
49+
}
50+
}
51+
}
52+
53+
fun parse(input: String): List<Machine> =
54+
input.split("""[\r\n]{2}""".toRegex()).map { Machine.parse(it.trim()) }
55+
56+
fun answer(input: String, adjustment: BigIntPos = BigIntPos(BigInteger.ZERO, BigInteger.ZERO)): BigInteger =
57+
parse(input)
58+
.mapNotNull { it.calculateSolution(adjustment) }
59+
.sumOf { sol -> sol.first * Machine.TokenCostA + sol.second * Machine.TokenCostB }
60+
61+
fun answer1(input: String): BigInteger =
62+
answer(input)
63+
64+
private val adjustment2 = BigInteger("10000000000000")
65+
66+
fun answer2(input: String): BigInteger =
67+
answer(input, BigIntPos(adjustment2, adjustment2))
68+
69+
fun main() {
70+
val input = fetchAdventOfCodeInput(2024, 13)
71+
println("--- Day 13: Claw Contraption ---")
72+
timedFunction("Part 1") { answer1(input) } // 28262
73+
timedFunction("Part 2") { answer2(input) } // 101406661266314
74+
}

src/test/kotlin/day12/day12.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Advent of Code 2024, Day 12
1+
// Advent of Code 2024, Day 12.
22
// By Sebastian Raaphorst, 2024.
33

44
package day12

src/test/kotlin/day13/day13.kt

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Advent of Code 2024, Day 13.
2+
// By Sebastian Raaphorst, 2024.
3+
4+
package day13
5+
6+
import org.junit.jupiter.api.Test
7+
import kotlin.test.assertEquals
8+
9+
class Day13 {
10+
companion object {
11+
val input1 =
12+
"""
13+
Button A: X+94, Y+34
14+
Button B: X+22, Y+67
15+
Prize: X=8400, Y=5400
16+
17+
Button A: X+26, Y+66
18+
Button B: X+67, Y+21
19+
Prize: X=12748, Y=12176
20+
21+
Button A: X+17, Y+86
22+
Button B: X+84, Y+37
23+
Prize: X=7870, Y=6450
24+
25+
Button A: X+69, Y+23
26+
Button B: X+27, Y+71
27+
Prize: X=18641, Y=10279
28+
""".trimIndent()
29+
}
30+
31+
@Test
32+
fun `Problem 1 example`() {
33+
assertEquals(480.toBigInteger(), answer1(input1))
34+
}
35+
}

0 commit comments

Comments
 (0)