Skip to content

Commit

Permalink
feat(2024): add day 12
Browse files Browse the repository at this point in the history
  • Loading branch information
believer committed Dec 12, 2024
1 parent d64d885 commit 7a220ed
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go/2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day
| [Day 9: Disk Fragmenter](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day09/main.go) | 🌟 | 6384282079460 | 🌟 | 6408966547049 |
| [Day 10: Hoof It](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day10/main.go) | 🌟 | 652 | 🌟 | 1432 |
| [Day 11: Plutonian Pebbles](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day11/main.go) | 🌟 | 187738 | 🌟 | 223767210249237 |
| [Day 12: Garden Groups](https://github.com/believer/advent-of-code/blob/master/go/2024/puzzles/day12/main.go) | 🌟 | 1522850 | 🌟 | 953738 |

## Benchmarks

Expand All @@ -43,6 +44,7 @@ Using Go's built-in benchmarking with the [testing](https://pkg.go.dev/testing#h
| 9 | 381476181 ns/op | 171042257 ns/op | |
| 10 | 1424599 ns/op | 1789071 ns/op | |
| 11 | 424021 ns/op | 15488584 ns/op | |
| 12 | 10984420 ns/op | 16856988 ns/op | |

\* compared to first solution

Expand Down
158 changes: 158 additions & 0 deletions go/2024/puzzles/day12/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package main

import (
"fmt"

"github.com/believer/aoc-2024/utils/files"
)

// BFS to find plant regions and perimeters
// For part 2 find corners of areas which show how many sides it has
func main() {
fmt.Println("Part 1: ", part1("input.txt"))
fmt.Println("Part 2: ", part2("input.txt"))
}

type Location struct {
r, c int
}

var directions = [][]int{
{-1, 0}, // Above
{0, 1}, // Right
{1, 0}, // Below
{0, -1}, // Left
}

func part1(name string) int {
plants := files.ReadLines(name)
grid := map[Location]byte{}
visited := map[Location]bool{}

rows := len(plants)
cols := len(plants[0])
price := 0

for r := range rows {
for c := range cols {
grid[Location{r, c}] = plants[r][c]
}
}

for r := range rows {
for c := range cols {
area, perimeter := findPlantBed(grid, visited, Location{r, c})
price += len(area) * perimeter
}
}

return price
}

func part2(name string) int {
plants := files.ReadLines(name)
grid := map[Location]byte{}
visited := map[Location]bool{}

rows := len(plants)
cols := len(plants[0])
price := 0

for r := range rows {
for c := range cols {
grid[Location{r, c}] = plants[r][c]
}
}

for r := range rows {
for c := range cols {
area, _ := findPlantBed(grid, visited, Location{r, c})
price += len(area) * findCorners(area)
}
}

return price
}

// The number of corners shows how many sides we have
func findCorners(area map[Location]bool) int {
corners := 0

cornerChecks := []struct {
offsets []Location
requiredStates []bool
}{
// Outer corners
{[]Location{{0, -1}, {-1, 0}}, []bool{false, false}}, // Top left
{[]Location{{0, 1}, {-1, 0}}, []bool{false, false}}, // Top right
{[]Location{{0, -1}, {1, 0}}, []bool{false, false}}, // Bottom left
{[]Location{{0, 1}, {1, 0}}, []bool{false, false}}, // Bottom right

// Inner corners
{[]Location{{-1, -1}, {-1, 0}, {0, -1}}, []bool{false, true, true}}, // Inside top left
{[]Location{{-1, 1}, {-1, 0}, {0, 1}}, []bool{false, true, true}}, // Inside top right
{[]Location{{1, -1}, {1, 0}, {0, -1}}, []bool{false, true, true}}, // Inside bottom left
{[]Location{{1, 1}, {1, 0}, {0, 1}}, []bool{false, true, true}}, // Inside bottom right
}

for a := range area {
for _, check := range cornerChecks {
match := true

for i, offset := range check.offsets {
neighbor := Location{a.r + offset.r, a.c + offset.c}

if _, ok := area[neighbor]; ok != check.requiredStates[i] {
match = false
break
}
}

if match {
corners++
}
}
}

return corners
}

func findPlantBed(grid map[Location]byte, visited map[Location]bool, start Location) (map[Location]bool, int) {
queue := []Location{start}
perimeter := 0
area := map[Location]bool{}

for len(queue) > 0 {
current := queue[0]
queue = queue[1:]

if _, ok := visited[current]; ok {
continue
}

neighbors := getNeighbors(grid, current)
perimeter += 4 - len(neighbors)
area[current] = true
visited[current] = true

for _, neighbor := range neighbors {
queue = append(queue, neighbor)
}
}

return area, perimeter
}

func getNeighbors(grid map[Location]byte, current Location) []Location {
neighbors := []Location{}

for _, d := range directions {
next := Location{current.r + d[0], current.c + d[1]}

if value, ok := grid[next]; ok && value == grid[current] {
neighbors = append(neighbors, next)
}
}

return neighbors
}
51 changes: 51 additions & 0 deletions go/2024/puzzles/day12/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestPart1(t *testing.T) {
t.Run("Part 1", func(t *testing.T) {
expected := 140
actual := part1("test-input.txt")
assert.Equal(t, expected, actual)
})
}

func TestPart1Medium(t *testing.T) {
t.Run("Part 1", func(t *testing.T) {
expected := 772
actual := part1("test-input-medium.txt")
assert.Equal(t, expected, actual)
})
}

func TestPart1Large(t *testing.T) {
t.Run("Part 1", func(t *testing.T) {
expected := 1930
actual := part1("test-input-large.txt")
assert.Equal(t, expected, actual)
})
}

func TestPart2(t *testing.T) {
t.Run("Part 2", func(t *testing.T) {
expected := 80
actual := part2("test-input.txt")
assert.Equal(t, expected, actual)
})
}

func BenchmarkPart1(b *testing.B) {
for i := 0; i < b.N; i++ {
part1("input.txt")
}
}

func BenchmarkPart2(b *testing.B) {
for i := 0; i < b.N; i++ {
part2("input.txt")
}
}
10 changes: 10 additions & 0 deletions go/2024/puzzles/day12/test-input-large.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
5 changes: 5 additions & 0 deletions go/2024/puzzles/day12/test-input-medium.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OOOOO
OXOXO
OOOOO
OXOXO
OOOOO
4 changes: 4 additions & 0 deletions go/2024/puzzles/day12/test-input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AAAA
BBCD
BBCC
EEEC

0 comments on commit 7a220ed

Please sign in to comment.