ec258082870bacca97882eb92cede7a59d241f6d — Pablo Meier 1 year, 8 months ago
```Day 21 in Scala
```
```4 files changed, 134 insertions(+), 0 deletions(-)

A day21/Day21.scala
A day21/Makefile
A day21/input.txt
```
`M README.md => README.md +2 -0`
```@@ 24,6 24,7 @@ of the solutions 😄
* [Day 17][day17]: Raku
* [Day 18][day18]: Clojure
* [Day 20][day20]: Lua
+* [Day 21][day21]: Scala

@@ 44,4 45,5 @@ of the solutions 😄

```
`A day21/Day21.scala => day21/Day21.scala +117 -0`
```@@ 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")
+  }
+}

```
`A day21/Makefile => day21/Makefile +13 -0`
```@@ 0,0 1,13 @@
+BUILD=build
+
+run: build
+	scala -cp \$(BUILD) Day21
+
+build: prepare
+	scalac -d \$(BUILD) Day21.scala
+
+prepare:
+	[ -d \$(BUILD) ] || mkdir \$(BUILD)
+
+clean:
+	rm -rf \$(BUILD)

```
`A day21/input.txt => day21/input.txt +2 -0`
```@@ 0,0 1,2 @@
+Player 1 starting position: 2
+Player 2 starting position: 10

```