mirror of
https://github.com/RussellChamp/advent-of-code-2021.git
synced 2026-10-27 21:52:32 +00:00
277 lines
6.0 KiB
Go
277 lines
6.0 KiB
Go
/* 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()
|
|
}
|
|
|
|
type Card = [][]uint8
|
|
|
|
func readData(input *os.File) ([]uint8, []Card) {
|
|
scanner := bufio.NewScanner(input)
|
|
|
|
var draws []uint8
|
|
|
|
if scanner.Scan() {
|
|
for _, val := range strings.Split(scanner.Text(), ",") {
|
|
iVal, err := strconv.Atoi(val)
|
|
check(err)
|
|
|
|
draws = append(draws, uint8(iVal))
|
|
}
|
|
}
|
|
|
|
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)
|
|
var newRow []uint8
|
|
for _, strVal := range rowVals {
|
|
val, err := strconv.Atoi(strVal)
|
|
check(err)
|
|
newRow = append(newRow, uint8(val))
|
|
}
|
|
//fmt.Printf("Attempting to push [%s] to card %d row %d {%s}\n", line, cardIdx, rowIdx, cards[cardIdx])
|
|
cards[cardIdx] = append(cards[cardIdx], newRow)
|
|
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
|
|
}
|
|
|
|
func markCards(cards []Card, marks []MarkedCard, draw uint8) []MarkedCard {
|
|
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 {
|
|
for cIdx, value := range row {
|
|
if !mark[rIdx][cIdx] {
|
|
|
|
score += int(value)
|
|
}
|
|
}
|
|
}
|
|
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
|
|
lastDraw := uint8(0)
|
|
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
|
|
|
|
lastDraw = draw
|
|
score *= int(draw)
|
|
|
|
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
|
|
lastDraw := uint8(0)
|
|
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
|
|
|
|
lastDraw = draw
|
|
score *= int(draw)
|
|
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)
|
|
}
|