adventofcode/2020/day22/main.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)
}