194 lines
3.6 KiB
Go
194 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
var debugLevel int
|
|
|
|
const (
|
|
debugLo = 1
|
|
debugMd = 2
|
|
debugHi = 3
|
|
debugNo = -1
|
|
)
|
|
|
|
func main() {
|
|
fmt.Println("# Day 22")
|
|
inp := h.StdinToStringSlice()
|
|
|
|
debugLevel = h.Atoi(h.OptArgNumber(2, "-1"))
|
|
|
|
solve(inp, h.Atoi(h.OptArgNumber(1, "2")))
|
|
}
|
|
|
|
func print(s string, lvl int) {
|
|
if debugLevel != -1 && lvl >= debugLevel {
|
|
fmt.Print(s)
|
|
}
|
|
}
|
|
|
|
func solve(inp []string, part int) {
|
|
var player int
|
|
decks := make(map[int]*Deck)
|
|
for k := range inp {
|
|
if strings.TrimSpace(inp[k]) == "" {
|
|
continue
|
|
} else if strings.HasPrefix(inp[k], "Player ") {
|
|
player = h.Atoi(inp[k][7 : len(inp[k])-1])
|
|
decks[player] = &Deck{Player: player}
|
|
continue
|
|
} else {
|
|
decks[player].Add(h.Atoi(inp[k]))
|
|
}
|
|
}
|
|
|
|
if part == 1 {
|
|
solveNormal(decks[1], decks[2])
|
|
} else {
|
|
solveRecursive(decks[1], decks[2])
|
|
}
|
|
}
|
|
|
|
func solveNormal(deck1, deck2 *Deck) {
|
|
round := 1
|
|
winner := Winner(deck1, deck2)
|
|
for winner == -1 {
|
|
|
|
print(fmt.Sprintf("-- Round %d --\n%s\n%s\n", round, deck1, deck2), debugLo)
|
|
|
|
cmp1 := deck1.Draw()
|
|
cmp2 := deck2.Draw()
|
|
print(fmt.Sprintf("Player 1 plays: %d\nPlayer 2 plays: %d\n", cmp1, cmp2), debugLo)
|
|
if cmp1 > cmp2 {
|
|
print(fmt.Sprintln("Player 1 wins the round!"), debugLo)
|
|
deck1.Add(cmp1, cmp2)
|
|
} else if cmp2 > cmp1 {
|
|
print(fmt.Sprintln("Player 2 wins the round!"), debugLo)
|
|
deck2.Add(cmp2, cmp1)
|
|
}
|
|
round++
|
|
winner = Winner(deck1, deck2)
|
|
}
|
|
fmt.Printf("== Post-game results ==\n%s\n%s\n", deck1, deck2)
|
|
var score int
|
|
if winner == 1 {
|
|
score = deck1.Score()
|
|
} else {
|
|
score = deck2.Score()
|
|
}
|
|
|
|
fmt.Println("## Part 1:\nAnswer: ", score)
|
|
}
|
|
|
|
func solveRecursive(deck1, deck2 *Deck) {
|
|
winner := RPlayGame(deck1, deck2)
|
|
|
|
fmt.Printf("== Post-game results ==\n%s\n%s\n", deck1, deck2)
|
|
var score int
|
|
if winner == 1 {
|
|
score = deck1.Score()
|
|
} else {
|
|
score = deck2.Score()
|
|
}
|
|
fmt.Println("## Part 2:\nAnswer: ", score)
|
|
}
|
|
|
|
var totalGames int
|
|
|
|
func RPlayGame(deck1, deck2 *Deck) int {
|
|
var history []string
|
|
var repeat bool
|
|
for deck1.Len() > 0 && deck2.Len() > 0 {
|
|
history, repeat = RPlayRound(deck1, deck2, history)
|
|
if repeat {
|
|
return 1
|
|
}
|
|
}
|
|
return Winner(deck1, deck2)
|
|
}
|
|
|
|
func RPlayRound(deck1, deck2 *Deck, history []string) ([]string, bool) {
|
|
status := fmt.Sprintf("%s%s", deck1, deck2)
|
|
if h.StringSliceContains(history, status) {
|
|
return history, true
|
|
}
|
|
nextHistory := append(history, status)
|
|
c1, c2 := deck1.Draw(), deck2.Draw()
|
|
if deck1.Len() >= c1 && deck2.Len() >= c2 {
|
|
winner := RPlayGame(deck1.Copy(c1), deck2.Copy(c2))
|
|
switch winner {
|
|
case 1:
|
|
deck1.Add(c1, c2)
|
|
case 2:
|
|
deck2.Add(c2, c1)
|
|
}
|
|
return nextHistory, false
|
|
}
|
|
if c1 > c2 {
|
|
deck1.Add(c1, c2)
|
|
} else {
|
|
deck2.Add(c2, c1)
|
|
}
|
|
|
|
return nextHistory, false
|
|
}
|
|
|
|
func Winner(deck1, deck2 *Deck) int {
|
|
if deck2.Empty() {
|
|
return 1
|
|
} else if deck1.Empty() {
|
|
return 2
|
|
}
|
|
return -1
|
|
}
|
|
|
|
type Deck struct {
|
|
Player int
|
|
cards []int
|
|
}
|
|
|
|
func (d *Deck) Add(card ...int) {
|
|
d.cards = append(d.cards, card...)
|
|
}
|
|
|
|
func (d *Deck) Draw() int {
|
|
var ret int
|
|
ret, d.cards = d.cards[0], d.cards[1:]
|
|
return ret
|
|
}
|
|
|
|
func (d *Deck) Empty() bool {
|
|
return len(d.cards) == 0
|
|
}
|
|
|
|
func (d *Deck) Len() int {
|
|
return len(d.cards)
|
|
}
|
|
|
|
func (d *Deck) Score() int {
|
|
var ret int
|
|
for k := len(d.cards) - 1; k >= 0; k-- {
|
|
ret = ret + (d.cards[k] * (len(d.cards) - k))
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d *Deck) Copy(num int) *Deck {
|
|
ret := Deck{
|
|
Player: d.Player,
|
|
}
|
|
for k := 0; k < num && k < len(d.cards); k++ {
|
|
ret.Add(d.cards[k])
|
|
}
|
|
return &ret
|
|
|
|
}
|
|
|
|
func (d Deck) String() string {
|
|
return fmt.Sprintf("Player %d's deck: %v", d.Player, d.cards)
|
|
}
|