summaryrefslogtreecommitdiffstats
path: root/21.py
blob: 674d95e57a6205e1e95e70ced2dd605ca0930c89 (plain)
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)))