#!/usr/bin/env python # input: player 1 starts at 3, player 2 starts at 5 class Die(): def __init__(self): self.curr = 0 self.rolls = 0 def __iter__(self): return self def __next__(self): result = 0 for _ in range(3): self.curr += 1 if self.curr > 100: self.curr = 1 result += self.curr self.rolls += 3 return result % 10 def get_rolls(self): return self.rolls class Player(): def __init__(self, pos): self.pos = pos self.score = 0 def advance(self, dPos): self.pos += dPos self.pos %= 10 if self.pos == 0: self.pos = 10 self.score += self.pos def win(self): return self.score >= 1000 def get_score(self): return self.score def part1(): player1 = Player(3) player2 = Player(5) die = Die() isPlayer1 = True for roll in die: if isPlayer1: player1.advance(roll) else: player2.advance(roll) isPlayer1 = not isPlayer1 if player1.win() or player2.win(): losing_score = min(player1.get_score(), player2.get_score()) print(losing_score * die.get_rolls()) break # part 2 from collections import namedtuple, Counter from functools import cache from itertools import product tPlayer = namedtuple('tPlayer', ['pos', 'score']) rolls = Counter(sum(t) for t in product([1, 2, 3], repeat=3)) @cache def part2(players, playerNum): wins = [0, 0] for roll, count in rolls.items(): innerPlayers = list(players) pos, score = players[playerNum] pos = (pos + roll - 1) % 10 + 1 score += pos if score >= 21: wins[playerNum] += count else: innerPlayers[playerNum] = tPlayer(pos, score) recWins = part2(tuple(innerPlayers), (playerNum + 1)%2) for i in range(2): wins[i] += recWins[i] * count return wins if __name__ == '__main__': part1() wins = part2((tPlayer(3, 0), tPlayer(5, 0)), 0) print(max(wins))