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) }