Advent of Code 2023 day 13
12/13/2024, 5:00 AMkingsley
12/13/2024, 5:47 AMbj0
12/13/2024, 6:05 AMPetr Sýkora
12/13/2024, 6:05 AMYou estimate that each button would need to be pressed no more than 100 times to win a prize. How else would someone be expected to play?
trap and went Djikstra, only to realize it is simple set of linear equations with easy analitical solution 😄bj0
12/13/2024, 6:05 AMbj0
12/13/2024, 6:08 AMbj0
12/13/2024, 6:10 AMbj0
12/13/2024, 6:10 AMbj0
12/13/2024, 6:10 AMPetr Sýkora
12/13/2024, 6:22 AMAndriy
12/13/2024, 6:29 AMJan Durovec
12/13/2024, 6:42 AMdata class ClawMachine(val a: Pair<Long, Long>, val b: Pair<Long, Long>, val prize: Pair<Long, Long>) {
fun tokensToWin(): Long {
val xxx = a.first * prize.second - a.second * prize.first
val yyy = a.first * b.second - b.first * a.second
if (xxx.mod(yyy) != 0L) return 0
val bPresses = xxx / yyy
val aPresses = (prize.first - b.first * bPresses) / a.first
return 3 * aPresses + bPresses
}
}
bj0
12/13/2024, 6:47 AMNorbert Kiesel
12/13/2024, 6:50 AMclass Machine(val ax: Int, val ay: Int, val bx: Int, val by: Int, val px: Int, val py: Int) {
fun tokens(d: Long): Long {
val pxd = px + d
val pyd = py + d
val na = (pyd * bx - pxd * by) / (ay * bx - ax * by)
val nb = (pxd - ax * na) / bx
return if (na * ax + nb * bx == pxd && na * ay + nb * by == pyd) na * 3 + nb else 0
}
}
Jan Durovec
12/13/2024, 6:50 AMmod
check protects me from it since I'm working with Longs but I missed the ... / a.first
part 🤦Michael de Kaste
12/13/2024, 7:01 AMMichael de Kaste
12/13/2024, 7:02 AMtypealias Point = Pair<Int, Int>
val Point.y get() = first
val Point.x get() = second
but the input are x/y points.
So I paired them up using x to y
and then doing pair.y
but pair.y
returns x
That was a LOONNGGG debugging session for me todaymaiatoday
12/13/2024, 7:13 AMDan Fingal-Surma
12/13/2024, 7:20 AMDan Fingal-Surma
12/13/2024, 7:20 AMJonathan Kolberg
12/13/2024, 7:24 AMDan Fingal-Surma
12/13/2024, 7:25 AMEndre Deak
12/13/2024, 7:38 AMDan Fingal-Surma
12/13/2024, 7:51 AMDan Fingal-Surma
12/13/2024, 7:56 AMDan Fingal-Surma
12/13/2024, 7:59 AMDan Fingal-Surma
12/13/2024, 8:01 AMfun Machine.bestPlay(): Play? {
val zx = lcm(a.x, a.y) / a.x
val zy = lcm(a.x, a.y) / a.y
val bm = (zx * prize.x - zy * prize.y) / (zx * b.x - zy * b.y)
val am = (prize.x - b.x * bm) / a.x
if ((a * am) + (b * bm) != prize) return null
return Play(am, bm, am * 3 + bm)
}
vs
fun Machine.bestPlay(): Play? {
val bm = (a.x * prize.y - a.y * prize.x) / (a.x * b.y - a.y * b.x)
val am = (prize.y - b.y * bm) / a.y
if ((a * am) + (b * bm) != prize) return null
return Play(am, bm, am * 3 + bm)
}
Michael de Kaste
12/13/2024, 8:03 AMDan Fingal-Surma
12/13/2024, 8:24 AMDan Fingal-Surma
12/13/2024, 8:26 AMDan Fingal-Surma
12/13/2024, 9:00 AMDan Fingal-Surma
12/13/2024, 9:13 AMa1x+b1y = c1
a2x+b2y = c2
x = (c1 - b1y) / a1
a2 (c1 - b1y) / a1 + b2y = c2
a2 (c1 - b1y) + a1b2y = a1c2
a2c1 - a2b1y + a1b2y = a1c2
a1b2y - a2b1y = a1c2 - a2c1
(a1b2 - a2b1)y = a1c2 - a2c1
y = (a1c2 - a2c1) / (a1b2 - a2b1)
Dan Fingal-Surma
12/13/2024, 9:13 AMDan Fingal-Surma
12/13/2024, 9:23 AMAnirudh
12/13/2024, 9:24 AMpart Two:
0 Pt(94, 34) Pt(22, 67) P(x=10000000008400, y=10000000005400) 0 0
1 Pt(26, 66) Pt(67, 21) P(x=10000000012748, y=10000000012176) 118679050709 103199174542
2 Pt(17, 86) Pt(84, 37) P(x=10000000007870, y=10000000006450) 0 0
3 Pt(69, 23) Pt(27, 71) P(x=10000000018641, y=10000000010279) 102851800151 107526881786
test input: 875318608908
Max Thiele
12/13/2024, 9:47 AMLuan Tran
12/13/2024, 10:18 AMLuan Tran
12/13/2024, 10:38 AMMichael de Kaste
12/13/2024, 10:39 AMLuan Tran
12/13/2024, 10:40 AMMichael de Kaste
12/13/2024, 10:46 AMgenerateSequence { Day13() }.map {
measureTime { it.part1() } to measureTime { it.part2() }
}.take(10000).last()
Or something like that maybe?Michael de Kaste
12/13/2024, 10:46 AMMichael de Kaste
12/13/2024, 10:46 AMMichael de Kaste
12/13/2024, 10:46 AMphldavies
12/13/2024, 1:43 PMWarming up 2 puzzles over 5s for year 2024 day 13...
Warmup finished after 5.000116042s with 10393 iterations
year 2024 day 13 part 1
Scan took 17.571us 👑: 38714
Default took 211.683us (12.05x): 38714
year 2024 day 13 part 2
Scan took 17.624us 👑: 74015623345775
Default took 210.985us (11.97x): 74015623345775
ephemient
12/13/2024, 1:56 PMAnirudh
12/13/2024, 2:22 PMif (a * ax + b * bx == x && a * ay + b * by == y)
the equations being solved for a
and b
or countA
and countB
basically guarantee that those will be equal - it's how we derive the formula/equation we're solving.
mine only has the check for integer coefficients (because I use toDouble
in the equations).
possible answer: is it to verify that a
& b
don't have rounding errors due to division with Longs?Michael de Kaste
12/13/2024, 2:23 PMAnirudh
12/13/2024, 2:28 PMm % 1 == 0.0
is risky if the floating part is close to 0 but not exactly and would end up passing that check.
but I think Kotlin/Java math guarantees comparison with 0.0 being actually zero, right?Petr Sýkora
12/13/2024, 2:34 PMPetr Sýkora
12/13/2024, 2:42 PMAnirudh
12/13/2024, 2:45 PMtoDouble
and put in the rounding-check like everyone else.
thank you!Michael de Kaste
12/13/2024, 2:47 PMPetr Sýkora
12/13/2024, 2:50 PMAnirudh
12/13/2024, 2:51 PMa: Double
before the modulo 1 was:
a.toLong().toDouble() == a
🤣
while feels like a GW-BASIC hackMichael de Kaste
12/13/2024, 2:59 PMsealed interface SafeLong {
@JvmInline value class JustValue(val value: Long) : SafeLong
data object DivByZero : SafeLong
data class WithModulo(val value: Long, val module: Long) : SafeLong
}
😂phldavies
12/13/2024, 3:14 PMprivate infix fun Long.safeDiv(other: Long) = if (rem(other) == 0L) div(other) else null
private fun solve(ax: Long, ay: Long, bx: Long, by: Long, x: Long, y: Long): Long {
val b = ((x * ay - y * ax) safeDiv (bx * ay - by * ax)) ?: return 0
val a = ((x - b * bx) safeDiv ax) ?: return 0
return 3 * a + b
}
if you don't mind some boxingphldavies
12/13/2024, 3:26 PMprivate inline fun Long.safeDiv(other: Long, onFail: () -> Nothing) =
if (rem(other) == 0L) div(other) else onFail()
private fun solve(ax: Long, ay: Long, bx: Long, by: Long, x: Long, y: Long): Long {
val b = (x * ay - y * ax).safeDiv(bx * ay - by * ax) { return 0 }
val a = (x - b * bx).safeDiv(ax) { return 0 }
return 3 * a + b
}
Dan Fingal-Surma
12/13/2024, 6:15 PMEndre Deak
12/13/2024, 6:32 PMfun Double.isInt() = floor(this) == ceil(this)
Dan Fingal-Surma
12/13/2024, 6:54 PMJakub Gwóźdź
12/13/2024, 7:05 PMprize = posA * timesA + posB * timesB
from the very beginning, I just hadn't realized that it's a system of two equations with only two unknowns.
Obligatory visualization attached 😆Paul Woitaschek
12/13/2024, 8:33 PMPaul Woitaschek
12/13/2024, 8:33 PMphldavies
12/13/2024, 8:36 PMPaul Woitaschek
12/13/2024, 8:38 PMphldavies
12/13/2024, 8:39 PMJakub Gwóźdź
12/13/2024, 8:39 PMPaul Woitaschek
12/13/2024, 8:40 PMDan Fingal-Surma
12/13/2024, 8:41 PMephemient
12/13/2024, 8:42 PMJakub Gwóźdź
12/13/2024, 8:42 PMDan Fingal-Surma
12/13/2024, 8:46 PMDan Fingal-Surma
12/13/2024, 8:48 PMDan Fingal-Surma
12/13/2024, 8:48 PMval db = (a.x * prize.y - a.y * prize.x) / (a.x * b.y - a.y * b.x)
val da = (prize.y - b.y * db) / a.y
return if (a * da + b * db == prize) da * 3 + db else 0
Neil Banman
12/13/2024, 9:23 PMephemient
12/13/2024, 9:25 PMephemient
12/13/2024, 9:27 PMDan Fingal-Surma
12/13/2024, 9:27 PMRegex("""(\d+).*?(\d+).*?(\d+).*?(\d+).*?(\d+).*?(\d+)""")
Dan Fingal-Surma
12/13/2024, 9:28 PMval (ax, ay, bx, by, px, py) = pattern.find(lines.joinToString())!!.destructured
ephemient
12/13/2024, 9:29 PMList(6) { """(\d+)""" }.joinToString(".*?").toRegex()
Dan Fingal-Surma
12/13/2024, 9:30 PMDan Fingal-Surma
12/13/2024, 9:30 PMDan Fingal-Surma
12/13/2024, 9:31 PMval pattern = Regex(List(6) { "(\\d+)" }.joinToString(".*?"))
Dan Fingal-Surma
12/13/2024, 9:32 PMCharles Flynn
12/14/2024, 1:56 AMCharles Flynn
12/14/2024, 1:59 AMA
button 80
times and the B
button 40
times. This would line up the claw along the X
axis (because 80*94 + 40*22 = 8400
) and along the Y
axis (because 80*34 + 40*67 = 5400
). Doing this would cost 80*3
tokens for the A
presses and 40*1
for the B
presses, a total of 280
tokens.Charles Flynn
12/14/2024, 2:00 AMCharles Flynn
12/14/2024, 2:00 AMLuan Tran
12/14/2024, 2:01 AMNeil Banman
12/14/2024, 2:02 AMDan Fingal-Surma
12/14/2024, 2:11 AMephemient
12/14/2024, 2:11 AMCharles Flynn
12/14/2024, 2:12 AMephemient
12/14/2024, 2:12 AMCharles Flynn
12/14/2024, 2:13 AMDan Fingal-Surma
12/14/2024, 2:13 AMDan Fingal-Surma
12/14/2024, 2:13 AMCharles Flynn
12/14/2024, 2:14 AMNeil Banman
12/14/2024, 2:16 AMDan Fingal-Surma
12/14/2024, 2:16 AMvadzim
12/14/2024, 4:19 PMMath.ulp
is used for fuzzy equality checks with relative epsilon.
I was stuck for a while realizing that if A, B and Prize directions are all collinear it would require writing a separate sub-solution of weighted Diophantine equations using the extended Euclidean algorithm, but luckily there were no such cases in my input. 😅
https://github.com/vkopichenko/Contests/blob/main/adventofcode/src/2024/y24day13.kt
data class Line(val point: Vector, val direction: Vector)
infix fun Line.intersection(that: Line): Vector? =
// TODO: using Cramer's rule might be more efficient
(that.point - this.point)
.projectOnto(that.direction.perpendicular())
.unprojectOnto(this.direction)
?.plus(this.point)
Jaap Beetstra
12/18/2024, 8:25 AMdata class Puzzle13(val aX: Long, val aY: Long, val bX: Long, val bY: Long, val pX: Long, val pY: Long) {
private val solution = solve {
variable("a") * aX + variable("b") * bX eq pX
variable("a") * aY + variable("b") * bY eq pY
}
val minCost: Long?
get() {
val a = solution["a"].asLong() ?: return null
val b = solution["b"].asLong() ?: return null
return if (a >= 0 && b >= 0) 3 * a + b else null
}
fun part2() = copy(pX = pX + 10000000000000L, pY = pY + 10000000000000L)
}