@@ 24,6 24,7 @@ of the solutions 😄
* [Day 17][day17]: Raku
* [Day 18][day18]: Clojure
* [Day 20][day20]: Lua
+* [Day 21][day21]: Scala
[day1]: https://git.sr.ht/~srpablo/advent_of_code_2021/tree/main/item/day1/day1.factor
[day2]: https://git.sr.ht/~srpablo/advent_of_code_2021/tree/main/item/day2/day2.sml
@@ 44,4 45,5 @@ of the solutions 😄
[day17]: https://git.sr.ht/~srpablo/advent_of_code_2021/tree/main/item/day17/day17.raku
[day18]: https://git.sr.ht/~srpablo/advent_of_code_2021/tree/main/item/day18/day18/src/day18/core.clj
[day20]: https://git.sr.ht/~srpablo/advent_of_code_2021/tree/main/item/day20/day20.lua
+ [day21]: https://git.sr.ht/~srpablo/advent_of_code_2021/tree/main/item/day21/Day21.scala
[aoc]: https://adventofcode.com/2021
@@ 0,0 1,117 @@
+import scala.collection.mutable.HashMap
+import scala.math.BigInt
+
+object Day21 {
+
+ def newPlayerPos(pos: Int, inc: Int): Int = {
+ var incBy = inc % 10
+ if (incBy == 0)
+ incBy = 10
+ var newPos = (pos + incBy) % 10
+ if (newPos == 0)
+ newPos = 10
+ newPos
+ }
+
+ def incOrReset(value: Int): Int = {
+ val newValue = value + 1
+ if (newValue == 101)
+ 100
+ newValue
+ }
+
+ def roll_die(position: Int): (Int, Int) = {
+ val p1 = incOrReset(position)
+ val p2 = incOrReset(p1)
+ val p3 = incOrReset(p2)
+ ((position + p1 + p2), p3)
+ }
+
+ def part1(): Int = {
+ var isP1Turn = true
+ var p1Score = 0
+ var p1Pos = 2
+ var p2Score = 0
+ var p2Pos = 10
+
+ var dieRolls = 0
+ var diePosition = 1
+
+ while (p1Score < 1000 && p2Score < 1000) {
+ dieRolls += 3
+ var (totalMove, dp) = roll_die(diePosition)
+ diePosition = dp
+ if (isP1Turn) {
+ p1Pos = newPlayerPos(p1Pos, totalMove)
+ p1Score += p1Pos
+ } else {
+ p2Pos = newPlayerPos(p2Pos, totalMove)
+ p2Score += p2Pos
+ }
+ isP1Turn = !isP1Turn
+ }
+
+ dieRolls * p1Score.min(p2Score)
+ }
+
+ def runQuantumGame(
+ p1Score: Int,
+ p1Pos: Int,
+ p2Score: Int,
+ p2Pos: Int,
+ isP1Turn: Boolean,
+ memo: HashMap[(Int, Int, Int, Int, Boolean), (BigInt, BigInt)]
+ ): (BigInt, BigInt) = {
+ val params = (p1Score, p1Pos, p2Score, p2Pos, isP1Turn)
+ memo.get(params) match {
+ case Some(v) => v
+ case None => {
+ val result = if (p1Score >= 21) {
+ (BigInt(1), BigInt(0))
+ }
+ else if (p2Score >= 21) {
+ (BigInt(0), BigInt(1))
+ }
+ else {
+ var allP1Wins = BigInt(0)
+ var allP2Wins = BigInt(0)
+
+ val die = List(1,2,3)
+ val combos = for { x <- die; y <- die; z <- die } yield (x, y, z)
+ for (roll <- combos) {
+ val totalMove = roll._1 + roll._2 + roll._3
+ val simulation: (BigInt, BigInt) = if (isP1Turn) {
+ val newP1Pos = newPlayerPos(p1Pos, totalMove)
+ val newP1Score = p1Score + newP1Pos
+ runQuantumGame(newP1Score, newP1Pos, p2Score, p2Pos, !isP1Turn, memo)
+ } else {
+ val newP2Pos = newPlayerPos(p2Pos, totalMove)
+ val newP2Score = p2Score + newP2Pos
+ runQuantumGame(p1Score, p1Pos, newP2Score, newP2Pos, !isP1Turn, memo)
+ }
+ val theseP1Wins = simulation._1
+ val theseP2Wins = simulation._2
+ allP1Wins += theseP1Wins
+ allP2Wins += theseP2Wins
+ }
+ (allP1Wins, allP2Wins)
+ }
+ memo.put(params, result)
+ result
+ }
+ }
+ }
+
+ def part2(): BigInt = {
+ val (allP1Wins, allP2Wins) = runQuantumGame(0, 2, 0, 10, true, HashMap())
+ allP1Wins.max(allP2Wins)
+ }
+
+
+ def main(args: Array[String]) = {
+ val p1 = part1()
+ println(s"Part 1: $p1")
+ val p2 = part2().toString()
+ println(s"Part 2: $p2")
+ }
+}