196 lines
3.8 KiB
Go
196 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
start := time.Now()
|
|
partOne()
|
|
duration := time.Since(start)
|
|
partTwo()
|
|
duration2 := time.Since(start)
|
|
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
|
}
|
|
|
|
func makeScanner(test bool) *bufio.Scanner {
|
|
var f *os.File
|
|
if test {
|
|
f, _ = os.Open("inputs/testinput")
|
|
} else {
|
|
f, _ = os.Open("inputs/input")
|
|
}
|
|
reader := bufio.NewReader(f)
|
|
return bufio.NewScanner(reader)
|
|
}
|
|
|
|
func mustAtoi(line string) int {
|
|
i, err := strconv.Atoi(line)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return i
|
|
}
|
|
|
|
func convertDraws(d []string) []int {
|
|
r := make([]int, len(d))
|
|
for i := range d {
|
|
r[i] = mustAtoi(d[i])
|
|
}
|
|
return r
|
|
}
|
|
|
|
func contains(i int, a []int) bool {
|
|
for _, j := range a {
|
|
if i == j {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// a bingo card like this:
|
|
// 60 79 46 9 58
|
|
// 97 81 6 94 84
|
|
// 38 40 17 61 29
|
|
// 11 28 0 91 15
|
|
// 24 77 34 59 36
|
|
// turns into this:
|
|
// rows: [][60 79 46 9 58][97 81 6 94 84]...
|
|
// columns: [][60 97 38 11 24][79 81 40 28 77]...
|
|
// so that checking all possible hits is easier
|
|
// we use maps and not arrays to also make the lookups quicker
|
|
|
|
type bingoCard struct {
|
|
allNumbers []int
|
|
rows []map[int]struct{}
|
|
columns []map[int]struct{}
|
|
winner bool
|
|
}
|
|
|
|
func readBingoCard(scanner *bufio.Scanner) bingoCard {
|
|
card := bingoCard{
|
|
allNumbers: make([]int, 0),
|
|
rows: make([]map[int]struct{}, 5),
|
|
columns: make([]map[int]struct{}, 5),
|
|
}
|
|
// what a pain
|
|
card.rows[0] = make(map[int]struct{})
|
|
card.rows[1] = make(map[int]struct{})
|
|
card.rows[2] = make(map[int]struct{})
|
|
card.rows[3] = make(map[int]struct{})
|
|
card.rows[4] = make(map[int]struct{})
|
|
card.columns[0] = make(map[int]struct{})
|
|
card.columns[1] = make(map[int]struct{})
|
|
card.columns[2] = make(map[int]struct{})
|
|
card.columns[3] = make(map[int]struct{})
|
|
card.columns[4] = make(map[int]struct{})
|
|
|
|
for i := 0; i < 5; i++ {
|
|
scanner.Scan()
|
|
row := strings.Split(scanner.Text(), " ")
|
|
var col int
|
|
for _, p := range row {
|
|
if p != "" {
|
|
num := mustAtoi(p)
|
|
card.allNumbers = append(card.allNumbers, num)
|
|
card.rows[i][num] = struct{}{}
|
|
card.columns[col][num] = struct{}{}
|
|
col++
|
|
}
|
|
}
|
|
}
|
|
return card
|
|
}
|
|
|
|
func checkBoard(currentNumbers []int, card bingoCard) bool {
|
|
for _, row := range card.rows {
|
|
hits := 0
|
|
for _, i := range currentNumbers {
|
|
if _, ok := row[i]; ok {
|
|
hits++
|
|
}
|
|
}
|
|
if hits == 5 {
|
|
return true
|
|
}
|
|
}
|
|
for _, col := range card.columns {
|
|
hits := 0
|
|
for _, i := range currentNumbers {
|
|
if _, ok := col[i]; ok {
|
|
hits++
|
|
}
|
|
}
|
|
if hits == 5 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func scoreBoard(currentNumbers []int, card bingoCard) int {
|
|
totalUncalled := 0
|
|
for _, i := range card.allNumbers {
|
|
if !contains(i, currentNumbers) {
|
|
totalUncalled += i
|
|
}
|
|
}
|
|
return totalUncalled * currentNumbers[len(currentNumbers)-1]
|
|
}
|
|
|
|
func partOne() {
|
|
scanner := makeScanner(false)
|
|
|
|
scanner.Scan()
|
|
draws := convertDraws(strings.Split(scanner.Text(), ","))
|
|
cards := []bingoCard{}
|
|
|
|
for scanner.Scan() {
|
|
cards = append(cards, readBingoCard(scanner))
|
|
}
|
|
for i := range draws {
|
|
for _, card := range cards {
|
|
winner := checkBoard(draws[0:i], card)
|
|
if winner {
|
|
fmt.Println(scoreBoard(draws[0:i], card))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func partTwo() {
|
|
scanner := makeScanner(false)
|
|
|
|
scanner.Scan()
|
|
draws := convertDraws(strings.Split(scanner.Text(), ","))
|
|
cards := []bingoCard{}
|
|
|
|
for scanner.Scan() {
|
|
cards = append(cards, readBingoCard(scanner))
|
|
}
|
|
|
|
winnerCount := 0
|
|
for i := range draws {
|
|
for j, card := range cards {
|
|
if !card.winner {
|
|
winner := checkBoard(draws[0:i], card)
|
|
if winner {
|
|
cards[j].winner = true
|
|
winnerCount++
|
|
if winnerCount == len(cards) {
|
|
// that was the last one!
|
|
fmt.Println(scoreBoard(draws[0:i], card))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|