69 lines
1.8 KiB
Python
69 lines
1.8 KiB
Python
from tools.aoc import AOCDay
|
|
from tools.tools import cache
|
|
from typing import Any
|
|
|
|
|
|
# 1 way to roll a 3, 3 ways to roll a 4, ...
|
|
ROLLS = [(1, 3), (3, 4), (6, 5), (7, 6), (6, 7), (3, 8), (1, 9)]
|
|
|
|
|
|
@cache
|
|
def roll(pos1: int, pos2: int, score1: int = 0, score2: int = 0) -> (int, int):
|
|
if score2 >= 21:
|
|
return 0, 1
|
|
|
|
p1wins = p2wins = 0
|
|
|
|
for sub_universes, pos_inc in ROLLS:
|
|
new_pos1 = 1 + (pos_inc + pos1 - 1) % 10
|
|
# notice the switch-a-roo!
|
|
sub_p2wins, sub_p1wins = roll(pos2, new_pos1, score2, score1 + new_pos1)
|
|
p1wins += sub_p1wins * sub_universes
|
|
p2wins += sub_p2wins * sub_universes
|
|
|
|
return p1wins, p2wins
|
|
|
|
|
|
class Day(AOCDay):
|
|
inputs = [
|
|
[
|
|
(739785, "test_input21"),
|
|
(551901, "input21")
|
|
],
|
|
[
|
|
(444356092776315, "test_input21"),
|
|
(272847859601291, "input21")
|
|
]
|
|
]
|
|
|
|
def part1(self) -> Any:
|
|
pos = list(map(int, [x.split(": ")[1] for x in self.getInput()]))
|
|
scores = [0, 0]
|
|
target_score = 1000
|
|
rolls = 0
|
|
dice_roll = 0
|
|
while scores[0] < target_score and scores[1] < target_score:
|
|
for player in range(2):
|
|
this_roll = 0
|
|
rolls += 3
|
|
for _ in range(3):
|
|
dice_roll = 1 + (dice_roll % 100)
|
|
this_roll += dice_roll
|
|
|
|
pos[player] = 1 + (this_roll + pos[player] - 1) % 10
|
|
scores[player] += pos[player]
|
|
if scores[player] >= target_score:
|
|
break
|
|
|
|
return min(scores) * rolls
|
|
|
|
def part2(self) -> Any:
|
|
pos1, pos2 = map(int, [x.split(": ")[1] for x in self.getInput()])
|
|
wins1, wins2 = roll(pos1, pos2)
|
|
return max(wins1, wins2)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
day = Day(2021, 21)
|
|
day.run(verbose=True)
|