diff --git a/2021/d21/ex1/ex1.py b/2021/d21/ex1/ex1.py deleted file mode 100755 index 5967837..0000000 --- a/2021/d21/ex1/ex1.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python - -import itertools -import sys -from typing import Iterable, Iterator, List, NamedTuple, Tuple, TypeVar - -T = TypeVar("T") - - -def grouper(iterable: Iterable[T], n: int) -> Iterator[Tuple[T, ...]]: - args = [iter(iterable)] * n - return itertools.zip_longest(*args) - - -def take(n: int, iterable: Iterable[T]) -> List[T]: - return list(itertools.islice(iterable, n)) - - -class PlayerStats(NamedTuple): - position: int - score: int - - -class GameState(NamedTuple): - p1: PlayerStats - p2: PlayerStats - total_rolls: int - - -WINNING_SCORE = 1000 - - -def solve(input: List[str]) -> int: - def parse() -> Tuple[int, int]: - p1, p2 = input[0].split(" ")[-1], input[1].split(" ")[-1] - return int(p1), int(p2) - - def deterministic_die() -> Iterator[int]: - return itertools.cycle(range(1, 100 + 1)) - - def do_turn(stats: PlayerStats, rolls: Iterator[int]) -> PlayerStats: - position, score = stats - roll = sum(take(3, rolls)) - position = ((position - 1 + roll) % 10) + 1 - score += position - return PlayerStats(position, score) - - def play_to_end(intial_state: GameState) -> GameState: - p1, p2, total_rolls = intial_state - die_rolls = deterministic_die() - - while True: - p1 = do_turn(p1, die_rolls) - total_rolls += 3 - if p1.score >= WINNING_SCORE: - break - p2 = do_turn(p2, die_rolls) - total_rolls += 3 - if p2.score >= WINNING_SCORE: - break - - return GameState(p1, p2, total_rolls) - - position1, position2 = parse() - p1, p2, total_rolls = play_to_end( - GameState(PlayerStats(position1, 0), PlayerStats(position2, 0), 0) - ) - # The loser *must* have the lowest score - return min(p1.score, p2.score) * total_rolls - - -def main() -> None: - input = [line.strip() for line in sys.stdin.readlines()] - print(solve(input)) - - -if __name__ == "__main__": - main() diff --git a/2021/d21/ex1/input b/2021/d21/ex1/input deleted file mode 100644 index 1002be5..0000000 --- a/2021/d21/ex1/input +++ /dev/null @@ -1,2 +0,0 @@ -Player 1 starting position: 8 -Player 2 starting position: 5 diff --git a/2021/d21/ex2/ex2.py b/2021/d21/ex2/ex2.py deleted file mode 100755 index 5184285..0000000 --- a/2021/d21/ex2/ex2.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python - -import functools -import itertools -import sys -from typing import Iterable, Iterator, List, NamedTuple, Tuple, TypeVar - -T = TypeVar("T") - - -def grouper(iterable: Iterable[T], n: int) -> Iterator[Tuple[T, ...]]: - args = [iter(iterable)] * n - return itertools.zip_longest(*args) - - -def take(n: int, iterable: Iterable[T]) -> List[T]: - return list(itertools.islice(iterable, n)) - - -class PlayerStats(NamedTuple): - position: int - score: int - - -ROLL_TO_UNIVERSES = { - 3: 1, - 4: 3, - 5: 6, - 6: 7, - 7: 6, - 8: 3, - 9: 1, -} - -WINNING_SCORE = 21 - - -def solve(input: List[str]) -> int: - def parse() -> Tuple[int, int]: - p1, p2 = input[0].split(" ")[-1], input[1].split(" ")[-1] - return int(p1), int(p2) - - def do_turn(stats: PlayerStats, roll: int) -> PlayerStats: - position, score = stats - position = ((position - 1 + roll) % 10) + 1 - score += position - return PlayerStats(position, score) - - @functools.cache - def play_universes(p1: PlayerStats, p2: PlayerStats) -> Tuple[int, int]: - p1_wins, p2_wins = 0, 0 - - # Only 9 different outcomes from a 3d3 roll, with differing probabilities - for roll, roll_probability in ROLL_TO_UNIVERSES.items(): - new_p1 = do_turn(p1, roll) - if new_p1.score >= WINNING_SCORE: - # Account for differing number of split universes when counting a win - p1_wins += roll_probability - continue - - # Exchange p1 and p2 roles, count their wins, and account for number of splits - new_p2_wins, new_p1_wins = play_universes(p2, new_p1) - p1_wins += new_p1_wins * roll_probability - p2_wins += new_p2_wins * roll_probability - - return p1_wins, p2_wins - - p1, p2 = parse() - p1_wins, p2_wins = play_universes(PlayerStats(p1, 0), PlayerStats(p2, 0)) - return max(p1_wins, p2_wins) - - -def main() -> None: - input = [line.strip() for line in sys.stdin.readlines()] - print(solve(input)) - - -if __name__ == "__main__": - main() diff --git a/2021/d21/ex2/input b/2021/d21/ex2/input deleted file mode 100644 index 1002be5..0000000 --- a/2021/d21/ex2/input +++ /dev/null @@ -1,2 +0,0 @@ -Player 1 starting position: 8 -Player 2 starting position: 5