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)))