Files

276 lines
6.0 KiB
Go
Raw Permalink Normal View History

2021-12-04 13:54:20 -05:00
/* Day 4: Giant Squid */
// TODO: Redo this whole thing with bit arrays instead
package main
import (
"AoC2021/utils/timer"
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func check(e error) {
if e != nil {
fmt.Println("Oh snap!")
panic(e)
}
}
func main() {
fmt.Println("--- Day 4: Giant Squid ---")
timer.Start()
part1()
timer.Tick()
fmt.Println()
part2()
timer.Tick()
fmt.Println()
}
2021-12-04 14:09:54 -05:00
type Card = [][]uint8
2021-12-04 13:54:20 -05:00
2021-12-04 14:09:54 -05:00
func readData(input *os.File) ([]uint8, []Card) {
2021-12-04 13:54:20 -05:00
scanner := bufio.NewScanner(input)
2021-12-04 14:09:54 -05:00
var draws []uint8
2021-12-04 13:54:20 -05:00
if scanner.Scan() {
2021-12-04 14:09:54 -05:00
for _, val := range strings.Split(scanner.Text(), ",") {
iVal, err := strconv.Atoi(val)
check(err)
draws = append(draws, uint8(iVal))
}
2021-12-04 13:54:20 -05:00
}
var cards []Card
cardIdx := -1 // the first new line will initialize card 0
rowIdx := 0
// fmt.Printf("Created cards %s", cards)
for scanner.Scan() {
line := scanner.Text()
switch {
case line == "": // when we hit an epty line, initialize a new card
cards = append(cards, Card{})
cardIdx += 1
rowIdx = 0
//fmt.Printf("Added card %d to cards {%s}\n", cardIdx, cards)
default:
rowVals := strings.Fields(line)
2021-12-04 14:09:54 -05:00
var newRow []uint8
for _, strVal := range rowVals {
val, err := strconv.Atoi(strVal)
check(err)
newRow = append(newRow, uint8(val))
}
2021-12-04 13:54:20 -05:00
//fmt.Printf("Attempting to push [%s] to card %d row %d {%s}\n", line, cardIdx, rowIdx, cards[cardIdx])
2021-12-04 14:09:54 -05:00
cards[cardIdx] = append(cards[cardIdx], newRow)
2021-12-04 13:54:20 -05:00
rowIdx += 1
}
}
return draws, cards
}
type MarkedCard = [][]bool
func initializeMarks(cards []Card) []MarkedCard {
// create a number of marked cards equal to the number of bingo cards
cardsLen := len(cards)
marks := make([]MarkedCard, cardsLen)
// find the number of rows on a card
rowCount := len(cards[0])
colCount := len(cards[0][0])
for cIdx := 0; cIdx < cardsLen; cIdx++ {
marks[cIdx] = make([][]bool, rowCount)
for rIdx := 0; rIdx < rowCount; rIdx++ {
//fmt.Printf("Attempting to add row %d to card %d", rIdx, cIdx)
//fmt.Println(marks[cIdx])
marks[cIdx][rIdx] = make([]bool, colCount)
}
}
return marks
}
2021-12-04 14:09:54 -05:00
func markCards(cards []Card, marks []MarkedCard, draw uint8) []MarkedCard {
2021-12-04 13:54:20 -05:00
for cIdx, card := range cards {
for rowIdx, row := range card {
for colIdx, value := range row {
if value == draw {
marks[cIdx][rowIdx][colIdx] = true
}
}
}
}
return marks
}
// TODO: currently assumes only one card will win per round
func findWinningCardIdx(marks []MarkedCard) int {
for cIdx, card := range marks {
if matchesRow(card) || matchesColumn(card) {
return cIdx
}
}
return -1
}
func matchesRow(card MarkedCard) bool {
for _, row := range card {
marks := 0
for _, value := range row {
if value {
marks += 1
}
}
if marks == len(row) {
return true
}
}
return false
}
func matchesColumn(card MarkedCard) bool {
rowCount := len(card)
colCount := len(card[0])
for colIdx := 0; colIdx < colCount; colIdx++ {
marks := 0
for rowIdx := 0; rowIdx < rowCount; rowIdx++ {
if card[rowIdx][colIdx] {
marks += 1
}
}
if marks == colCount {
return true
}
}
return false
}
func getCardScore(card Card, mark MarkedCard) int {
score := 0
// sum up all the non-marked bingo spots
for rIdx, row := range card {
2021-12-04 14:09:54 -05:00
for cIdx, value := range row {
2021-12-04 13:54:20 -05:00
if !mark[rIdx][cIdx] {
2021-12-04 14:09:54 -05:00
score += int(value)
2021-12-04 13:54:20 -05:00
}
}
}
return score
}
func part1() {
fmt.Println("* Part 1 *")
fmt.Println(" Goal: Figure out which bingo board will win first.")
fmt.Println(" Answer: What will your final score be if you choose that board?")
input, err := os.Open("./input.txt")
check(err)
defer input.Close()
draws, cards := readData(input)
marks := initializeMarks(cards)
//fmt.Printf("Read in %d draws and %d cards\n", len(draws), len(cards))
//fmt.Printf("Initialized %d mark cards\n", len(marks))
//fmt.Printf("First card %s\n", cards[0])
//fmt.Println(marks[0])
drawTimes := 0
winningCardIdx := -1
2021-12-04 14:09:54 -05:00
lastDraw := uint8(0)
2021-12-04 13:54:20 -05:00
score := 0
for _, draw := range draws {
drawTimes += 1
marks = markCards(cards, marks, draw)
winningCardIdx = findWinningCardIdx(marks)
if winningCardIdx >= 0 {
winningCard := cards[winningCardIdx]
winningMarks := marks[winningCardIdx]
score = getCardScore(winningCard, winningMarks)
// multiply by the last drawn tile
2021-12-04 14:09:54 -05:00
lastDraw = draw
score *= int(draw)
2021-12-04 13:54:20 -05:00
break
}
}
fmt.Printf("After %d draws, card #%d won on a %d with a score of %d\n", drawTimes, winningCardIdx, lastDraw, score)
}
func part2() {
fmt.Println("* Part 2 *")
fmt.Println(" Goal: Figure out which board will win last.")
fmt.Println(" Answer: Once it wins, what would its final score be?")
input, err := os.Open("./input.txt")
check(err)
defer input.Close()
draws, cards := readData(input)
marks := initializeMarks(cards)
drawTimes := 0
2021-12-04 14:09:54 -05:00
lastDraw := uint8(0)
2021-12-04 13:54:20 -05:00
score := 0
foundLoser := false
for _, draw := range draws {
if foundLoser {
break
}
drawTimes += 1
marks = markCards(cards, marks, draw)
winningCardIdx := findWinningCardIdx(marks)
eliminatedCards := 0
for winningCardIdx > -1 {
// if there's only one card left, it's the losingist one
if len(marks) == 1 {
score = getCardScore(cards[0], marks[0])
// multiply by the last drawn tile
2021-12-04 14:09:54 -05:00
lastDraw = draw
score *= int(draw)
2021-12-04 13:54:20 -05:00
foundLoser = true
break
}
eliminatedCards += 1
// if a card wins, just remove it from the list of cards and don't track it anymore
cards[winningCardIdx] = cards[len(cards)-1]
cards[len(cards)-1] = Card{}
cards = cards[:len(cards)-1]
marks[winningCardIdx] = marks[len(marks)-1]
marks[len(marks)-1] = MarkedCard{}
marks = marks[:len(marks)-1]
winningCardIdx = findWinningCardIdx(marks)
}
if eliminatedCards > 0 {
//fmt.Printf("Round %d: Eliminated %d cards with a %s (%d remaining)\n", drawTimes, eliminatedCards, draw, len(cards))
}
}
//fmt.Println("Card: ", cards[0])
//fmt.Println("Mark: ", marks[0])
fmt.Printf("After %d draws, the losingist card won on a %d with a score of %d\n", drawTimes, lastDraw, score)
}