package main

import (
	"fmt"

	h "git.bullercodeworks.com/brian/adventofcode/helpers"
)

func main() {
	inp := h.StdinToStringSlice()
	var starting []int
	for i := range inp {
		starting = append(starting, int(inp[i][len(inp[i])-1]-'0'))
	}
	part1(starting)
	fmt.Println()
	part2(starting)
}

type player struct {
	number          int
	position, score int
}

func (p *player) move(spaces int) {
	p.position = (p.position + spaces) % 10
	if p.position == 0 {
		p.position = 10
	}
	p.score += p.position
}

type die struct {
	value int
	rolls int
}

func (d *die) roll() int {
	d.rolls++
	d.value++
	if d.value > 100 {
		d.value = 1
	}
	return d.value
}

func part1(pos []int) {
	var players []player
	for i := range pos {
		players = append(players, player{number: i + 1, position: pos[i]})
	}
	die := die{value: 0}
	pTurn := 0
	for players[0].score < 1000 && players[1].score < 1000 {
		turn := die.roll() + die.roll() + die.roll()
		players[pTurn].move(turn)
		pTurn = (pTurn + 1) % len(players)
	}
	var loser player
	for i := range players {
		if loser.number == 0 || players[i].score < loser.score {
			loser = players[i]
		}
	}
	fmt.Println("# Part 1")
	fmt.Printf("Player %d loses, score %d\n", (loser.number), loser.score*die.rolls)
}

func part2(pos []int) {
	var players []player
	for i := range pos {
		players = append(players, player{number: i + 1, position: pos[i]})
	}

	diracIt(pos[0], pos[1])
}

type gameState struct {
	p1, p2 int
	s1, s2 int
}

// Run the games with the dirac die.
func diracIt(p1, p2 int) {
	move := func(pos, roll int) int {
		pos += roll
		pos = pos % 10
		if pos == 0 {
			return 10
		}
		return pos
	}

	states := map[gameState]int64{{p1, p2, 0, 0}: 1}
	rolls := map[int]int64{}
	for r1 := 1; r1 <= 3; r1++ {
		for r2 := 1; r2 <= 3; r2++ {
			for r3 := 1; r3 <= 3; r3++ {
				rolls[r1+r2+r3] += 1
			}
		}
	}
	var p1w, p2w int64 = 0, 0
	for len(states) != 0 {
		for turn := 1; turn <= 2; turn++ {
			nextStates := map[gameState]int64{}
			for state, count := range states {
				for roll, rCount := range rolls {
					p1, p2 := state.p1, state.p2
					s1, s2 := state.s1, state.s2
					n := count * rCount

					if turn == 1 {
						p1 = move(p1, roll)
						s1 += p1
						if s1 > 20 {
							p1w += n
						} else {
							nextStates[gameState{p1, p2, s1, s2}] += n
						}
					} else {
						p2 = move(p2, roll)
						s2 += p2
						if s2 > 20 {
							p2w += n
						} else {
							nextStates[gameState{p1, p2, s1, s2}] += n
						}
					}
				}
			}
			states = nextStates
		}
	}
	fmt.Println("# Part 2")
	fmt.Printf("Player 1 Wins: %d\nPlayer 2 Wins: %d\n", p1w, p2w)
}
