1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
from itertools import count
from re import compile as re_compile
from utils import open_day
from functools import cache, reduce
pattern = re_compile(r'Player \d+ starting position: (\d+)')
with open_day(21) as f:
positions = tuple(int(pattern.match(line).group(1)) - 1 for line in f)
def part1(positions):
poss = list(positions)
scores = [0 for _ in poss]
for roll in count():
rolled = (3 * roll + 1) * 3 + 3
player = roll % len(poss)
poss[player] += rolled
poss[player] %= 10
scores[player] += poss[player] + 1
if scores[player] >= 1000:
return [score * (roll + 1) * 3 for score in scores if score < 1000][0]
@cache
def part2(positions, scores=(0, 0), player=0, maxscore=21):
dirac_triples = ((1, 3), (3, 4), (6, 5), (7, 6), (6, 7), (3, 8), (1, 9))
def times(t, c): return (t[0] * c, t[1] * c)
def tupsum(a, b): return (a[0] + b[0], a[1] + b[1])
def turn(rolled):
nposs = list(positions)
nscores = list(scores)
nposs[player] += rolled
nposs[player] %= 10
nscores[player] += nposs[player] + 1
if nscores[player] >= maxscore:
return (player == 0, player != 0)
return part2(tuple(nposs), tuple(nscores), int(not player), maxscore)
return reduce(tupsum, (times(turn(roll), count) for count, roll in dirac_triples))
print(part1(positions))
print(max(part2(positions)))
|