diff --git a/2021/aoc/aoc.go b/2021/aoc/aoc.go index 78b46b6..470503b 100644 --- a/2021/aoc/aoc.go +++ b/2021/aoc/aoc.go @@ -3,6 +3,8 @@ package aoc import ( "bufio" "os" + "strconv" + "strings" ) func ReadLines(path string) (lines []string) { @@ -18,3 +20,19 @@ func ReadLines(path string) (lines []string) { } return lines } + +func ReadFile(path string) string { + b, err := os.ReadFile(path) + if err != nil { + panic(err) + } + return strings.TrimSpace(string(b)) +} + +func MustInt(s string) int { + i, err := strconv.Atoi(s) + if err != nil { + panic(err) + } + return i +} diff --git a/2021/day4/main.go b/2021/day4/main.go new file mode 100644 index 0000000..5d2f778 --- /dev/null +++ b/2021/day4/main.go @@ -0,0 +1,148 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/fatih/color" + "github.com/jdkaplan/advent-of-code/aoc" +) + +type input struct { + numbers []int + boards []board +} + +func parseInput(text string) input { + sections := strings.Split(text, "\n\n") + var nums []int + for _, n := range strings.Split(sections[0], ",") { + nums = append(nums, aoc.MustInt(n)) + } + var boards []board + for _, block := range sections[1:] { + b := newBoard() + for r, row := range strings.Split(block, "\n") { + for c, n := range strings.Fields(row) { + b.nums[r][c] = aoc.MustInt(n) + } + } + boards = append(boards, b) + } + return input{numbers: nums, boards: boards} +} + +type board struct { + nums *[5][5]int + marked *[5][5]bool + won bool +} + +func newBoard() board { + return board{ + nums: &[5][5]int{}, + marked: &[5][5]bool{}, + } +} + +func (b board) String() string { + var s strings.Builder + for r, row := range b.nums { + for c, n := range row { + if b.marked[r][c] { + color.New(color.FgGreen, color.Bold).Fprintf(&s, "%2d ", n) + } else { + fmt.Fprintf(&s, "%2d ", n) + } + } + s.WriteRune('\n') + } + return s.String() +} + +func (b *board) call(x int) (win bool) { + if b.won { + return false + } + for r, row := range b.nums { + for c, n := range row { + if n == x { + b.marked[r][c] = true + if b.win(r, c) { + return true + } + } + } + } + return +} + +func (b board) win(r, c int) bool { + return b.winRow(r) || b.winCol(c) +} + +func (b board) winRow(r int) bool { + for c := 0; c < 5; c++ { + if !b.marked[r][c] { + return false + } + } + return true +} + +func (b board) winCol(c int) bool { + for r := 0; r < 5; r++ { + if !b.marked[r][c] { + return false + } + } + return true +} + +func (b board) score(x int) int { + var sum int + for r, row := range b.nums { + for c, n := range row { + if !b.marked[r][c] { + sum += n + } + } + } + return x * sum +} + +func part1(text string) int { + inp := parseInput(text) + for _, n := range inp.numbers { + for _, b := range inp.boards { + if b.call(n) { + return b.score(n) + } + } + } + return len(inp.numbers) +} + +func part2(text string) int { + inp := parseInput(text) + var wins int + for _, n := range inp.numbers { + for i, b := range inp.boards { + if b.call(n) { + // Set on the original in inp, not the copy in b. + inp.boards[i].won = true + wins++ + if wins == len(inp.boards) { + return b.score(n) + } + } + } + } + return len(inp.numbers) +} + +func main() { + text := aoc.ReadFile("./input/day4.txt") + fmt.Println(part1(text)) + fmt.Println(part2(text)) +} diff --git a/2021/go.mod b/2021/go.mod index f833a57..62552b8 100644 --- a/2021/go.mod +++ b/2021/go.mod @@ -1,3 +1,11 @@ module github.com/jdkaplan/advent-of-code go 1.17 + +require github.com/fatih/color v1.13.0 + +require ( + github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect +) diff --git a/2021/go.sum b/2021/go.sum index e69de29..6ab7ee4 100644 --- a/2021/go.sum +++ b/2021/go.sum @@ -0,0 +1,11 @@ +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=