180 lines
3.3 KiB
Go
180 lines
3.3 KiB
Go
package twentytwo
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
type player struct {
|
|
deck []int
|
|
}
|
|
|
|
func (p *player) score() int {
|
|
score := 0
|
|
for i := 0; i < len(p.deck); i++ {
|
|
score += p.deck[i] * (len(p.deck) - i)
|
|
}
|
|
|
|
return score
|
|
}
|
|
|
|
type combat struct {
|
|
one, two player
|
|
}
|
|
|
|
func (c *combat) load(filename string) error {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
one := false
|
|
two := false
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
if scanner.Text() == "Player 1:" {
|
|
one = true
|
|
continue
|
|
}
|
|
|
|
if scanner.Text() == "" {
|
|
one = false
|
|
two = false
|
|
continue
|
|
}
|
|
|
|
if scanner.Text() == "Player 2:" {
|
|
two = true
|
|
continue
|
|
}
|
|
|
|
card, err := strconv.Atoi(scanner.Text())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if one {
|
|
c.one.deck = append(c.one.deck, card)
|
|
} else if two {
|
|
c.two.deck = append(c.two.deck, card)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *combat) play() (round int) {
|
|
for {
|
|
round++
|
|
|
|
// play cards
|
|
played := []int{c.one.deck[0], c.two.deck[0]}
|
|
c.one.deck = c.one.deck[1:]
|
|
c.two.deck = c.two.deck[1:]
|
|
|
|
if played[0] > played[1] {
|
|
c.one.deck = append(c.one.deck, played[0], played[1])
|
|
} else if played[0] < played[1] {
|
|
c.two.deck = append(c.two.deck, played[1], played[0])
|
|
} else {
|
|
panic("TIES ARE LIKELY IN PART 2")
|
|
}
|
|
|
|
if len(c.one.deck) == 0 || len(c.two.deck) == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return round
|
|
}
|
|
|
|
func (c *combat) recursivePlay() (winner int) {
|
|
previousRounds := map[string]bool{}
|
|
for {
|
|
currentRound := ""
|
|
for _, c := range c.one.deck {
|
|
currentRound += fmt.Sprintf("%d,", c)
|
|
}
|
|
currentRound += "|"
|
|
for _, c := range c.two.deck {
|
|
currentRound += fmt.Sprintf("%d,", c)
|
|
}
|
|
|
|
if previousRounds[currentRound] {
|
|
return 1
|
|
} else {
|
|
previousRounds[currentRound] = true
|
|
}
|
|
|
|
// Draw cards as normal
|
|
played := []int{c.one.deck[0], c.two.deck[0]}
|
|
c.one.deck = c.one.deck[1:]
|
|
c.two.deck = c.two.deck[1:]
|
|
|
|
w := 0
|
|
if len(c.one.deck) >= played[0] && len(c.two.deck) >= played[1] {
|
|
rPOne := player{}
|
|
for i, c := range c.one.deck {
|
|
if i >= played[0] {
|
|
break
|
|
}
|
|
rPOne.deck = append(rPOne.deck, c)
|
|
}
|
|
|
|
rPTwo := player{}
|
|
for i, c := range c.two.deck {
|
|
if i >= played[1] {
|
|
break
|
|
}
|
|
rPTwo.deck = append(rPTwo.deck, c)
|
|
}
|
|
rC := combat{
|
|
one: rPOne,
|
|
two: rPTwo,
|
|
}
|
|
w = rC.recursivePlay()
|
|
} else if played[0] > played[1] {
|
|
w = 1
|
|
} else if played[1] > played[0] {
|
|
w = 2
|
|
}
|
|
|
|
if w == 1 {
|
|
c.one.deck = append(c.one.deck, played[0], played[1])
|
|
} else if w == 2 {
|
|
c.two.deck = append(c.two.deck, played[1], played[0])
|
|
}
|
|
|
|
if len(c.one.deck) == 0 {
|
|
return 2
|
|
} else if len(c.two.deck) == 0 {
|
|
return 1
|
|
}
|
|
}
|
|
}
|
|
|
|
// PartOne What is the winning player's score
|
|
func PartOne() string {
|
|
c := combat{}
|
|
if err := c.load("twentytwo/input.txt"); err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
rounds := c.play()
|
|
return fmt.Sprintf("The game lasted %d rounds with a score of %d - %d", rounds, c.one.score(), c.two.score())
|
|
}
|
|
|
|
// PartTwo What is the winning player's score after recursive combat
|
|
func PartTwo() string {
|
|
c := combat{}
|
|
if err := c.load("twentytwo/input.txt"); err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
winner := c.recursivePlay()
|
|
return fmt.Sprintf("The winner was player %d with a score of %d - %d", winner, c.one.score(), c.two.score())
|
|
}
|