mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-19 03:23:27 +02:00
2021 day 4: playing lots of bingo :)
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/alexchao26/advent-of-code-go/cast"
|
||||
"github.com/alexchao26/advent-of-code-go/util"
|
||||
)
|
||||
|
||||
//go:embed input.txt
|
||||
var input string
|
||||
|
||||
func init() {
|
||||
// do this in init (not main) so test file has same input
|
||||
input = strings.TrimRight(input, "\n")
|
||||
if len(input) == 0 {
|
||||
panic("empty input.txt file")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var part int
|
||||
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
||||
flag.Parse()
|
||||
fmt.Println("Running part", part)
|
||||
|
||||
if part == 1 {
|
||||
ans := part1(input)
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
} else {
|
||||
ans := part2(input)
|
||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||
fmt.Println("Output:", ans)
|
||||
}
|
||||
}
|
||||
|
||||
func part1(input string) int {
|
||||
nums, boards := parseInput(input)
|
||||
|
||||
for _, n := range nums {
|
||||
for _, b := range boards {
|
||||
didWin := b.PickNum(n)
|
||||
if didWin {
|
||||
// multiply score of winning board by number that was just called
|
||||
return b.Score() * n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("a board should've won and returned from the loop")
|
||||
}
|
||||
|
||||
func part2(input string) int {
|
||||
nums, boards := parseInput(input)
|
||||
|
||||
lastWinningScore := -1
|
||||
alreadyWon := map[int]bool{}
|
||||
for _, n := range nums {
|
||||
for bi, b := range boards {
|
||||
if alreadyWon[bi] {
|
||||
continue
|
||||
}
|
||||
didWin := b.PickNum(n)
|
||||
if didWin {
|
||||
// WHICH BOARD WINS LAST
|
||||
lastWinningScore = b.Score() * n
|
||||
|
||||
// mark board as already won
|
||||
alreadyWon[bi] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastWinningScore
|
||||
|
||||
}
|
||||
|
||||
// BoardState maintains a parsed board and a boolean matrix of cells that have
|
||||
// been picked/marked
|
||||
type BoardState struct {
|
||||
board [][]int
|
||||
picked [][]bool
|
||||
}
|
||||
|
||||
func NewBoardState(board [][]int) BoardState {
|
||||
picked := make([][]bool, len(board))
|
||||
for i := range picked {
|
||||
picked[i] = make([]bool, len(board[0]))
|
||||
}
|
||||
return BoardState{
|
||||
board: board,
|
||||
picked: picked,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BoardState) PickNum(num int) bool {
|
||||
for r, rows := range b.board {
|
||||
for c, v := range rows {
|
||||
if v == num {
|
||||
b.picked[r][c] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is this fast enough to do on every "cycle"?
|
||||
// guess so. probably a constant time way to do this but oh well
|
||||
for i := 0; i < len(b.board); i++ {
|
||||
isFullRow, isFullCol := true, true
|
||||
// board is square so this works fine, otherwise would need another pair of nested loops
|
||||
for j := 0; j < len(b.board); j++ {
|
||||
// check row at index i
|
||||
if !b.picked[i][j] {
|
||||
isFullRow = false
|
||||
}
|
||||
// check col at index j
|
||||
if !b.picked[j][i] {
|
||||
isFullCol = false
|
||||
}
|
||||
}
|
||||
if isFullRow || isFullCol {
|
||||
// returns true if is winning board
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// false for incomplete board
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *BoardState) Score() int {
|
||||
var score int
|
||||
|
||||
for r, rows := range b.board {
|
||||
for c, v := range rows {
|
||||
// adds up all the non-picked/marked cells
|
||||
if !b.picked[r][c] {
|
||||
score += v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
func parseInput(input string) (nums []int, boards []BoardState) {
|
||||
lines := strings.Split(input, "\n\n")
|
||||
|
||||
for _, v := range strings.Split(lines[0], ",") {
|
||||
nums = append(nums, cast.ToInt(v))
|
||||
}
|
||||
|
||||
for _, grid := range lines[1:] {
|
||||
b := [][]int{}
|
||||
for _, line := range strings.Split(grid, "\n") {
|
||||
line = strings.ReplaceAll(line, " ", " ")
|
||||
for line[0] == ' ' {
|
||||
line = line[1:]
|
||||
}
|
||||
parts := strings.Split(line, " ")
|
||||
|
||||
row := []int{}
|
||||
for _, p := range parts {
|
||||
row = append(row, cast.ToInt(p))
|
||||
}
|
||||
b = append(b, row)
|
||||
}
|
||||
|
||||
boards = append(boards, NewBoardState(b))
|
||||
}
|
||||
return nums, boards
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var example = `7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
|
||||
|
||||
22 13 17 11 0
|
||||
8 2 23 4 24
|
||||
21 9 14 16 7
|
||||
6 10 3 18 5
|
||||
1 12 20 15 19
|
||||
|
||||
3 15 0 2 22
|
||||
9 18 13 17 5
|
||||
19 8 7 25 23
|
||||
20 11 10 24 4
|
||||
14 21 16 12 6
|
||||
|
||||
14 21 17 24 4
|
||||
10 16 15 9 19
|
||||
18 8 23 26 20
|
||||
22 11 13 6 5
|
||||
2 0 12 3 7`
|
||||
|
||||
func Test_part1(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "example",
|
||||
input: example,
|
||||
want: 4512,
|
||||
},
|
||||
{
|
||||
name: "actual",
|
||||
input: input,
|
||||
want: 58838,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := part1(tt.input); got != tt.want {
|
||||
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_part2(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "example",
|
||||
input: example,
|
||||
want: 1924,
|
||||
},
|
||||
{
|
||||
name: "actual",
|
||||
input: input,
|
||||
want: 6256,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := part2(tt.input); got != tt.want {
|
||||
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user