from itertools import product #input = [4, 8] input = [1, 5] pos = [input[0] - 1, input[1] - 1] score = [0, 0] turn = 0 dice = 1 rolls = 0 def roll(): global dice global rolls res = dice dice += 1 rolls += 1 if dice == 101: dice = 1 return res def roll3(): global dice global rolls if dice < 98: res = dice * 3 + 3 dice += 3 rolls += 3 return res else: return roll() + roll() + roll() while score[0] < 1000 and score[1] < 1000: for i in range(2): pos[i] += roll3() pos[i] = pos[i] % 10 score[i] += pos[i] + 1 if score[i] >= 1000: print("Part 1:", score[(i + 1) % 2] * rolls) wincount = {} def calc_wincount(state): (pos, score, turn) = state wc = [0, 0] if score[0] >= 21: return [1, 0] elif score[1] >= 21: return [0, 1] for [a, b, c] in product(list(range(1, 4)), repeat=3): steps = a + b + c if turn == 0: newpos = ((pos[0] + steps) % 10, pos[1]) newscore = (score[0] + newpos[0] + 1, score[1]) else: newpos = (pos[0], (pos[1] + steps) % 10) newscore = (score[0], score[1] + newpos[1] + 1) [c1, c2] = wincount[(newpos, newscore, (turn + 1) % 2)] wc[0] += c1 wc[1] += c2 return wc for ascore in reversed(range(0, 31)): for bscore in reversed(range(0, 31)): for turn in range(2): for apos in range(0, 10): for bpos in range(0, 10): state = ((apos, bpos), (ascore, bscore), turn) wincount[state] = calc_wincount(state) print("Part 2:", max(wincount[((input[0] - 1, input[1] - 1), (0, 0), 0)]))