#!/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))