Bladeren bron

day 21, fucking recursion!

metya 4 jaren geleden
bovenliggende
commit
bf065bd509
4 gewijzigde bestanden met toevoegingen van 148 en 0 verwijderingen
  1. 77 0
      day21_dirac_dice/README.md
  2. 2 0
      day21_dirac_dice/example.txt
  3. 2 0
      day21_dirac_dice/input.txt
  4. 67 0
      day21_dirac_dice/main.py

+ 77 - 0
day21_dirac_dice/README.md

@@ -0,0 +1,77 @@
+--- Day 21: Dirac Dice ---
+--------------------------
+
+There's not much to do as you slowly descend to the bottom of the ocean. The submarine computer challenges you to a nice game of *Dirac Dice*.
+
+
+This game consists of a single [die](https://en.wikipedia.org/wiki/Dice), two [pawns](https://en.wikipedia.org/wiki/Glossary_of_board_games#piece), and a game board with a circular track containing ten spaces marked `1` through `10` clockwise. Each player's *starting space* is chosen randomly (your puzzle input). Player 1 goes first.
+
+
+Players take turns moving. On each player's turn, the player rolls the die *three times* and adds up the results. Then, the player moves their pawn that many times *forward* around the track (that is, moving clockwise on spaces in order of increasing value, wrapping back around to `1` after `10`). So, if a player is on space `7` and they roll `2`, `2`, and `1`, they would move forward 5 times, to spaces `8`, `9`, `10`, `1`, and finally stopping on `2`.
+
+
+After each player moves, they increase their *score* by the value of the space their pawn stopped on. Players' scores start at `0`. So, if the first player starts on space `7` and rolls a total of `5`, they would stop on space `2` and add `2` to their score (for a total score of `2`). The game immediately ends as a win for any player whose score reaches *at least `1000`*.
+
+
+Since the first game is a practice game, the submarine opens a compartment labeled *deterministic dice* and a 100-sided die falls out. This die always rolls `1` first, then `2`, then `3`, and so on up to `100`, after which it starts over at `1` again. Play using this die.
+
+
+For example, given these starting positions:
+
+
+
+```
+Player 1 starting position: 4
+Player 2 starting position: 8
+
+```
+
+This is how the game would go:
+
+
+* Player 1 rolls `1`+`2`+`3` and moves to space `10` for a total score of `10`.
+* Player 2 rolls `4`+`5`+`6` and moves to space `3` for a total score of `3`.
+* Player 1 rolls `7`+`8`+`9` and moves to space `4` for a total score of `14`.
+* Player 2 rolls `10`+`11`+`12` and moves to space `6` for a total score of `9`.
+* Player 1 rolls `13`+`14`+`15` and moves to space `6` for a total score of `20`.
+* Player 2 rolls `16`+`17`+`18` and moves to space `7` for a total score of `16`.
+* Player 1 rolls `19`+`20`+`21` and moves to space `6` for a total score of `26`.
+* Player 2 rolls `22`+`23`+`24` and moves to space `6` for a total score of `22`.
+
+
+...after many turns...
+
+
+* Player 2 rolls `82`+`83`+`84` and moves to space `6` for a total score of `742`.
+* Player 1 rolls `85`+`86`+`87` and moves to space `4` for a total score of `990`.
+* Player 2 rolls `88`+`89`+`90` and moves to space `3` for a total score of `745`.
+* Player 1 rolls `91`+`92`+`93` and moves to space `10` for a final score, `1000`.
+
+
+Since player 1 has at least `1000` points, player 1 wins and the game ends. At this point, the losing player had `745` points and the die had been rolled a total of `993` times; `745 * 993 = *739785*`.
+
+
+Play a practice game using the deterministic 100-sided die. The moment either player wins, *what do you get if you multiply the score of the losing player by the number of times the die was rolled during the game?*
+
+
+--- Part Two ---
+----------------
+
+Now that you're warmed up, it's time to play the real game.
+
+
+A second compartment opens, this time labeled *Dirac dice*. Out of it falls a single three-sided die.
+
+
+As you experiment with the die, you feel a little strange. An informational brochure in the compartment explains that this is a *quantum die*: when you roll it, the universe *splits into multiple copies*, one copy for each possible outcome of the die. In this case, rolling the die always splits the universe into *three copies*: one where the outcome of the roll was `1`, one where it was `2`, and one where it was `3`.
+
+
+The game is played the same as before, although to prevent things from getting too far out of hand, the game now ends when either player's score reaches at least `*21*`.
+
+
+Using the same starting positions as in the example above, player 1 wins in `*444356092776315*` universes, while player 2 merely wins in `341960390180808` universes.
+
+
+Using your given starting positions, determine every possible outcome. *Find the player that wins in more universes; in how many universes does that player win?*
+
+

+ 2 - 0
day21_dirac_dice/example.txt

@@ -0,0 +1,2 @@
+Player 1 starting position: 4
+Player 2 starting position: 8

+ 2 - 0
day21_dirac_dice/input.txt

@@ -0,0 +1,2 @@
+Player 1 starting position: 3
+Player 2 starting position: 4

+ 67 - 0
day21_dirac_dice/main.py

@@ -0,0 +1,67 @@
+import os, sys
+
+task_dir = os.path.dirname(__file__)
+sys.path.append(f"{task_dir}/..")
+from get_tasks import get_input, generate_readme, check_example, bench
+from itertools import product
+from functools import cache
+from collections import Counter
+
+
+def virgin_warmup_game(player1=0, player2=0, pos1=4, pos2=8):
+    roll = 1
+    step = 0
+    while not (player1 >= 1000 or player2 >= 1000):
+        if step % 2 == 0:
+            pos1 = 10 if (s := (sum(range(roll, roll + 3)) + pos1) % 10) == 0 else s
+            player1 += pos1
+        else:
+            pos2 = 10 if (s := (sum(range(roll, roll + 3)) + pos2) % 10) == 0 else s
+            player2 += pos2
+        roll += 3
+        step += 3
+        roll = roll % 100 if roll > 100 else roll
+    return min(player1, player2) * step
+
+
+outcomes = Counter(map(sum, product([1, 2, 3], [1, 2, 3], [1, 2, 3])))
+
+@cache
+def chad_dirac_dice(player1=0, player2=0, pos1=4, pos2=8):
+    wins1, wins2 = 0, 0
+    if player1 >= 21:
+        return 1, 0
+    if player2 >= 21:
+        return 0, 1
+    for s, c in outcomes.items():
+        npos1 = 10 if (np := (s + pos1) % 10) == 0 else np
+        a, b = chad_dirac_dice(player2, player1 + npos1, pos2, npos1)
+        wins1 += b*c
+        wins2 += a*c
+    return wins1, wins2
+
+
+@bench
+def part1(input):
+    pos1 = int(input[0][-1])
+    pos2 = int(input[1][-1])
+    print("The answer of part1 is:", virgin_warmup_game(pos1=pos1, pos2=pos2))
+
+
+@bench
+def part2(input):
+    pos1 = int(input[0][-1])
+    pos2 = int(input[1][-1])
+    print("The answer of part2 is:", max(chad_dirac_dice(pos1=pos1, pos2=pos2)))
+
+
+if __name__ == "__main__":
+    input, example = get_input(task_dir, 21)
+
+    check_example(example, part1)
+    check_example(example, part2)
+
+    part1(input)
+    part2(input)
+
+    generate_readme(task_dir, 21)