diff --git a/2021/21/README.md b/2021/21/README.md new file mode 100644 index 0000000..3a856f1 --- /dev/null +++ b/2021/21/README.md @@ -0,0 +1,58 @@ +# 2021 Day 21: Dirac Dice +Copyright (c) Eric Wastl +#### [Direct Link](https://adventofcode.com/2021/day/21) + +## Part 1 + +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 2 + +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?** diff --git a/2021/21/code.py b/2021/21/code.py new file mode 100644 index 0000000..206f9b2 --- /dev/null +++ b/2021/21/code.py @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2021 Akumatic +# +# https://adventofcode.com/2021/day/21 + +def read_file(filename: str = "input.txt") -> tuple: + with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f: + return [int(line[28:]) for line in f.read().strip().split("\n")] + +def game_deterministic(positions: list) -> tuple: + pos = positions[:] + player, rolls, die = 0, 0, 0 + scores = [0, 0] + while max(scores) < 1000: + for _ in range(3): + die = (die + 1) % 100 or 100 + rolls += 1 + pos[player] = (pos[player] + die) % 10 or 10 + scores[player] += pos[player] + player = (player + 1) % 2 + return rolls, scores + +def game_dirac(move_frequency: dict, wins: list, positions: list, + scores: list = [0, 0], player: int = 0, win_mult: int = 1) -> None: + next_player = (player + 1) % 2 + for moves in move_frequency: + pos, scr = positions[:], scores[:] + pos[player] = (pos[player] + moves) % 10 or 10 + scr[player] += pos[player] + next_mult = move_frequency[moves] * win_mult + if scr[player] < 21: + game_dirac(move_frequency, wins, pos, scr, next_player, next_mult) + else: + wins[player] += next_mult + +def part1(start_positions: list) -> int: + rolls, scores = game_deterministic(start_positions) + return rolls * min(scores) + +def part2(start_positions: list) -> int: + move_frequency = {3: 1, 4: 3, 5: 6, 6: 7, 7: 6, 8: 3, 9: 1} + wins = [0, 0] + game_dirac(move_frequency, wins, start_positions) + return max(wins) + +if __name__ == "__main__": + vals = read_file() + print(f"Part 1: {part1(vals)}") + print(f"Part 2: {part2(vals)}") + \ No newline at end of file diff --git a/2021/21/input.txt b/2021/21/input.txt new file mode 100644 index 0000000..fc17973 --- /dev/null +++ b/2021/21/input.txt @@ -0,0 +1,2 @@ +Player 1 starting position: 3 +Player 2 starting position: 10 diff --git a/2021/21/solution.txt b/2021/21/solution.txt new file mode 100644 index 0000000..6b875cc --- /dev/null +++ b/2021/21/solution.txt @@ -0,0 +1,2 @@ +Part 1: 713328 +Part 2: 92399285032143 diff --git a/2021/21/test_code.py b/2021/21/test_code.py new file mode 100644 index 0000000..1bf3c63 --- /dev/null +++ b/2021/21/test_code.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2021 Akumatic + +from code import * + +def test(): + vals = read_file("test_input.txt") + assert part1(vals) == 739785 + print("Passed Part 1") + assert part2(vals) == 444356092776315 + print("Passed Part 2") + +if __name__ == "__main__": + test() diff --git a/2021/21/test_input.txt b/2021/21/test_input.txt new file mode 100644 index 0000000..3f69194 --- /dev/null +++ b/2021/21/test_input.txt @@ -0,0 +1,2 @@ +Player 1 starting position: 4 +Player 2 starting position: 8 diff --git a/2021/README.md b/2021/README.md index 34945be..27f0508 100644 --- a/2021/README.md +++ b/2021/README.md @@ -35,7 +35,7 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day | 18 | :white_check_mark: | :white_check_mark: | [Solution](18/code.py) | [Day 18](https://adventofcode.com/2021/day/18) | | 19 | :white_check_mark: | :white_check_mark: | [Solution](19/code.py) | [Day 19](https://adventofcode.com/2021/day/19) | | 20 | :white_check_mark: | :white_check_mark: | [Solution](20/code.py) | [Day 20](https://adventofcode.com/2021/day/20) | -| 21 | | | | | +| 21 | :white_check_mark: | :white_check_mark: | [Solution](21/code.py) | [Day 21](https://adventofcode.com/2021/day/21) | | 22 | | | | | | 23 | | | | | | 24 | | | | |