Skip to content

Commit

Permalink
Fix old Y21 puzzle
Browse files Browse the repository at this point in the history
Used new memoization functions to fix old Y2021 day that broke after a Kotlin upgrade (when computeIfAbsent started throwing CME on recursion)
  • Loading branch information
hibob224 committed Dec 19, 2024
1 parent 78134da commit d6ee914
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 34 deletions.
60 changes: 30 additions & 30 deletions src/main/kotlin/y2021/day21/Day21.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package y2021.day21

import utils.Memo2
import utils.getInputFile
import utils.memoize

fun main() {
println("Part one: ${Day21.solvePartOne()}")
Expand All @@ -21,14 +23,6 @@ object Day21 {
assert(input.size == 2)
}

private val diracRolls = (1..3).flatMap { a ->
(1..3).flatMap { b ->
(1..3).map { c ->
listOf(a, b, c)
}
}
}

fun solvePartOne(): Int {
var playerOnePos = input[0]
var playerOneScore = 0
Expand Down Expand Up @@ -69,33 +63,39 @@ object Day21 {
}

fun solvePartTwo(): Long {
val (p1Wins, p2Wins) = playRound(0, input[0], 0, input[1])
val playRound = Memo2<Pair<Int, Int>, Pair<Int, Int>, Pair<Long, Long>>::playRound.memoize()
val (p1Wins, p2Wins) = playRound(0 to input[0], 0 to input[1])
return maxOf(p1Wins, p2Wins)
}
}

private val diracRolls = (1..3).flatMap { a ->
(1..3).flatMap { b ->
(1..3).map { c ->
listOf(a, b, c)
}
}
}

// Memoization cache
private val cache = mutableMapOf<Pair<Pair<Int, Int>, Pair<Int, Int>>, Pair<Long, Long>>()
private fun Memo2<Pair<Int, Int>, Pair<Int, Int>, Pair<Long, Long>>.playRound(current: Pair<Int, Int>, other: Pair<Int, Int>): Pair<Long, Long> {
val (currentScore, currentPos) = current
val (otherScore, otherPos) = other
// Check if we already know the result for this argument combo in cache, or calculate it

private fun playRound(currentScore: Int, currentPos: Int, otherScore: Int, otherPos: Int): Pair<Long, Long> {
val cacheKey = (currentScore to currentPos) to (otherScore to otherPos)
// Check if we already know the result for this argument combo in cache, or calculate it
return cache.computeIfAbsent(cacheKey) {
return@computeIfAbsent if (currentScore >= 21) {
1L to 0L // P1 already won
} else if (otherScore >= 21) {
0L to 1L // P2 already won
} else {
// No winner yet, continue playing with every dirac roll combo and keep a tally of who wins
diracRolls.fold(0L to 0L) { scores, rolls ->
var newCurrentPos = currentPos + rolls.sum()
while (newCurrentPos > 10) {
newCurrentPos -= 10
}
val newCurrentScore = currentScore + newCurrentPos
val (p2Wins, p1Wins) = playRound(otherScore, otherPos, newCurrentScore, newCurrentPos)
scores.copy(first = scores.first + p1Wins, second = scores.second + p2Wins)
}
return if (currentScore >= 21) {
1L to 0L // P1 already won
} else if (otherScore >= 21) {
0L to 1L // P2 already won
} else {
// No winner yet, continue playing with every dirac roll combo and keep a tally of who wins
diracRolls.fold(0L to 0L) { scores, rolls ->
var newCurrentPos = currentPos + rolls.sum()
while (newCurrentPos > 10) {
newCurrentPos -= 10
}
val newCurrentScore = currentScore + newCurrentPos
val (p2Wins, p1Wins) = recurse(otherScore to otherPos, newCurrentScore to newCurrentPos)
scores.copy(first = scores.first + p1Wins, second = scores.second + p2Wins)
}
}
}
6 changes: 2 additions & 4 deletions src/test/kotlin/y2021/day21/Day21Test.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package y2021.day21

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

import org.junit.jupiter.api.Assertions.*

internal class Day21Test {

@Test
Expand All @@ -13,7 +12,6 @@ internal class Day21Test {

@Test
fun solvePartTwo() {
// TODO Kotlin upgrade has broken this solution, CME. Maybe fix one day
// assertEquals(492043106122795, Day21.solvePartTwo())
assertEquals(492043106122795, Day21.solvePartTwo())
}
}

0 comments on commit d6ee914

Please sign in to comment.