2024-12-16 07:18:41 +01:00

124 lines
4.4 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package aoc2024
import println
import readInput
/*
--- Day 13: Claw Contraption ---
https://adventofcode.com/2024/day/13
*/
fun main() {
val inlineTestInput = """
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279
"""
fun part1(input: List<String>): Int {
var i = 0
var sum = 0
while (i < input.size) {
val (ax, ay) = "Button A: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toInt() }
val (bx, by) = "Button B: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toInt() }
val (px, py) = "Prize: X=(\\d+), Y=(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toInt() }
i++
val xpos = IntArray(101 * 101)
val ypos = IntArray(101 * 101)
val solutions = ArrayList<Int>()
for (b in 1..100) {
for (a in 1..100) {
val op = (a - 1) + (b - 1) * 101
if ((xpos[op] == px) && (ypos[op] == py)) {
solutions.add(op)
}
if ((xpos[op] > px) || (ypos[op] > py)) break
xpos[a + (b - 1) * 101] = xpos[op] + ax
xpos[(a - 1) + b * 101] = xpos[op] + bx
ypos[a + (b - 1) * 101] = ypos[op] + ay
ypos[(a - 1) + b * 101] = ypos[op] + by
}
}
val minTokens = solutions.minOfOrNull { (it % 101) * 3 + (it / 101) }
sum += minTokens ?: 0
}
return sum
}
fun part2(input: List<String>): Long {
var i = 0
var sum = 0L
// 1. ax * pa + bx * pb = px
// 2. ay * pa + by * pb = py
// 3. pa * 3 + pb <- min
// 4. pa, pb in N (int > 0)
// ax * pa + bx * pb - px = ay * pa + by * pb - py
// (ax - ay) * pa + (bx - by) * pb + py - px = 0
// (ax - ay) * pa + (bx - by) * pb = px - py
while (i < input.size) {
val (ax, ay) = "Button A: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toLong() }
val (bx, by) = "Button B: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toLong() }
val (px, py) = "Prize: X=(\\d+), Y=(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toLong() + 10000000000000L }
i++
// val t1 = ax - ay
// val t2 = bx - by
// val t3 = px - py
// Linear Diophantine equations
/* The simplest linear Diophantine equation takes the form
a*x + b*x = c
where a, b and c are given integers.
The solutions are described by the following theorem:
This Diophantine equation has a solution (where x and y are integers),
IFF c is a multiple of the greatest common divisor of a and b.
Moreover, if (x, y) is a solution, then the other solutions have the
form (x + kv, y ku), where
- k is an arbitrary integer, and
- u and v are the quotients of a and b (respectively) by the greatest common divisor of a and b.
*/
// With our input data, all equations have exactly one solution
val pad = (px * by - py * bx)
val pan = (ax * by - ay * bx)
if (pad % pan == 0L) {
val pa = pad / pan
val pbd = (px - pa * ax)
if (pbd % bx == 0L) {
val pb = pbd / bx
sum += pa * 3 + pb
}
}
}
return sum
}
// test if implementation meets criteria from the description, like:
val testInput = inlineTestInput.trim().reader().readLines()
//val testInput = readInput("aoc2024/Day13_test")
val testInputPart1Result = part1(testInput)
println("Part 1 Test: $testInputPart1Result")
val testInputPart2Result = part2(testInput)
println("Part 2 Test: $testInputPart2Result")
check(testInputPart1Result == 480)
//check(testInputPart2Result == 0L)
val input = readInput("aoc2024/Day13")
part1(input).println()
part2(input).println()
}