adventofcode/2021/day04/main.go
2021-12-05 11:31:07 -06:00

198 lines
3.5 KiB
Go

package main
import (
"fmt"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
boards := buildBoards(inp[1:])
draws := parseDraws(inp[0])
part1(boards, draws)
part2(boards, draws)
}
func part1(boards []Board, draws []int) {
var winners []Board
for i := range draws {
for b := range boards {
if boards[b].Mark(draws[i]) {
winners = append(winners, boards[b])
}
}
if len(winners) > 0 {
break
}
}
for w := range winners {
fmt.Println("# Part 1")
fmt.Println(winners[w])
fmt.Println("Score:", winners[w].Score())
fmt.Println()
}
}
func part2(boards []Board, draws []int) {
var winners []Board
for i := range draws {
for b := range boards {
if boards[b].Mark(draws[i]) {
winners = append(winners, boards[b])
}
}
// Remove all winners from boards
for i := range winners {
for b := range boards {
if winners[i].String() == boards[b].String() {
boards = append(boards[:b], boards[b+1:]...)
break
}
}
}
if len(boards) == 0 {
break
}
}
fmt.Println("# Part 2")
fmt.Println("Last Winner")
fmt.Println(winners[len(winners)-1])
fmt.Println("Score:", winners[len(winners)-1].Score())
fmt.Println()
}
func parseDraws(inp string) []int {
var ret []int
pts := strings.Split(inp, ",")
for i := range pts {
ret = append(ret, h.Atoi(pts[i]))
}
return ret
}
type Spot struct {
value int
marked bool
}
type Board struct {
spots map[h.Coordinate]Spot
draws []int
}
func NewBoard(inp []string) Board {
b := Board{
spots: make(map[h.Coordinate]Spot),
}
for y := range inp {
row := strings.Fields(strings.TrimSpace(inp[y]))
for x := range row {
v := h.Atoi(row[x])
b.spots[h.Coordinate{X: x, Y: y}] = Spot{value: v}
}
}
return b
}
func buildBoards(inp []string) []Board {
var boards []Board
var boardStr []string
for i := range inp {
if inp[i] == "" {
// start/end of a board
if len(boardStr) == 5 {
boards = append(boards, NewBoard(boardStr))
}
boardStr = []string{}
} else {
boardStr = append(boardStr, inp[i])
}
}
if len(boardStr) == 5 {
boards = append(boards, NewBoard(boardStr))
}
return boards
}
// Mark adds a number to the draws and returns whether this board has won
func (b *Board) Mark(v int) bool {
b.draws = append(b.draws, v)
for k := range b.spots {
s := b.spots[k]
if b.spots[k].value == v {
s.marked = true
b.spots[k] = s
return b.Won()
}
}
return b.Won()
}
func (b *Board) Won() bool {
for y := 0; y < 5; y++ {
if b.CheckRowForWin(y) {
return true
}
}
for x := 0; x < 5; x++ {
if b.CheckColForWin(x) {
return true
}
}
return false
}
func (b *Board) CheckRowForWin(y int) bool {
for x := 0; x < 5; x++ {
if b.spots[c(x, y)].marked == false {
return false
}
}
return true
}
func (b *Board) CheckColForWin(x int) bool {
for y := 0; y < 5; y++ {
if b.spots[c(x, y)].marked == false {
return false
}
}
return true
}
func (b *Board) Score() int {
var unmarked int
for y := 0; y < 5; y++ {
for x := 0; x < 5; x++ {
if b.spots[c(x, y)].marked == false {
unmarked += b.spots[c(x, y)].value
}
}
}
return unmarked * b.draws[len(b.draws)-1]
}
func (b Board) String() string {
var ret string
for y := 0; y < 5; y++ {
for x := 0; x < 5; x++ {
c := c(x, y)
marked := " "
if b.spots[c].marked {
marked = "X"
}
ret = fmt.Sprintf("%s%s: %2d[%v] ", ret, c, b.spots[c].value, marked)
}
ret = ret + "\n"
}
return ret
}
func c(x, y int) h.Coordinate {
return h.Coordinate{X: x, Y: y}
}