Skip to content

Commit

Permalink
perf(2024): add grid/point helper and refactor day 12
Browse files Browse the repository at this point in the history
  • Loading branch information
believer committed Dec 12, 2024
1 parent 7a220ed commit 9eb53db
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 67 deletions.
3 changes: 2 additions & 1 deletion go/2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +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 | |
| 12 | 6677348 ns/op | 12339733 ns/op | `39.21%` / `26.80%` |

\* compared to first solution

Expand All @@ -59,6 +59,7 @@ Using Go's built-in benchmarking with the [testing](https://pkg.go.dev/testing#h
| 5 | 1669175 ns/op | 17299190 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/1db858ae3d391319511787d8935c76eecdf6b22f/go/2024/puzzles/day05/main.go) |
| 7 | 126892714 ns/op | 10124683583 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/dd735747021ce43ca3a7427c529813139737271e/go/2024/puzzles/day07/main.go) |
| 7 | 110164692 ns/op | 7135839625 ns/op | `13.18%` / `29.52%` | [Link](https://github.com/believer/advent-of-code/blob/640d9604dfefa71f7bfef876750f378bd1a58a8b/go/2024/puzzles/day07/main.go) |
| 12 | 10984420 ns/op | 16856988 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/7a220ed0e6deae74d0a293615e6348e6ce1a9a22/go/2024/puzzles/day12/main.go) |

## Running

Expand Down
96 changes: 35 additions & 61 deletions go/2024/puzzles/day12/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,27 @@ import (
"fmt"

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

// BFS to find plant regions and perimeters
// For part 2 find corners of areas which show how many sides it has
// Added a similar Grid/Point solution like I've used in my Rust solutions
// in previous years. Made things faster (one dimensional array of bytes) and cleaner!
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])
lines := files.ReadLines(name)
plants := grid.New(lines)
visited := map[grid.Point]bool{}
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})
for y := range plants.Height {
for x := range plants.Width {
area, perimeter := findPlantBed(plants, visited, grid.Point{X: x, Y: y})
price += len(area) * perimeter
}
}
Expand All @@ -50,23 +33,14 @@ func part1(name string) int {
}

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

rows := len(plants)
cols := len(plants[0])
lines := files.ReadLines(name)
plants := grid.New(lines)
visited := map[grid.Point]bool{}
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})
for y := range plants.Height {
for x := range plants.Width {
area, _ := findPlantBed(plants, visited, grid.Point{X: x, Y: y})
price += len(area) * findCorners(area)
}
}
Expand All @@ -75,32 +49,32 @@ func part2(name string) int {
}

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

cornerChecks := []struct {
offsets []Location
offsets []grid.Point
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
{[]grid.Point{{Y: 0, X: -1}, {Y: -1, X: 0}}, []bool{false, false}}, // Top left
{[]grid.Point{{Y: 0, X: 1}, {Y: -1, X: 0}}, []bool{false, false}}, // Top right
{[]grid.Point{{Y: 0, X: -1}, {Y: 1, X: 0}}, []bool{false, false}}, // Bottom left
{[]grid.Point{{Y: 0, X: 1}, {Y: 1, X: 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
{[]grid.Point{{Y: -1, X: -1}, {Y: -1, X: 0}, {Y: 0, X: -1}}, []bool{false, true, true}}, // Inside top left
{[]grid.Point{{Y: -1, X: 1}, {Y: -1, X: 0}, {Y: 0, X: 1}}, []bool{false, true, true}}, // Inside top right
{[]grid.Point{{Y: 1, X: -1}, {Y: 1, X: 0}, {Y: 0, X: -1}}, []bool{false, true, true}}, // Inside bottom left
{[]grid.Point{{Y: 1, X: 1}, {Y: 1, X: 0}, {Y: 0, X: 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}
neighbor := a.Add(offset)

if _, ok := area[neighbor]; ok != check.requiredStates[i] {
match = false
Expand All @@ -117,10 +91,10 @@ func findCorners(area map[Location]bool) int {
return corners
}

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

for len(queue) > 0 {
current := queue[0]
Expand All @@ -130,7 +104,7 @@ func findPlantBed(grid map[Location]byte, visited map[Location]bool, start Locat
continue
}

neighbors := getNeighbors(grid, current)
neighbors := getNeighbors(g, current)
perimeter += 4 - len(neighbors)
area[current] = true
visited[current] = true
Expand All @@ -143,13 +117,13 @@ func findPlantBed(grid map[Location]byte, visited map[Location]bool, start Locat
return area, perimeter
}

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

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

if value, ok := grid[next]; ok && value == grid[current] {
if value, ok := g.Contains(next); ok && value == g.Get(current) {
neighbors = append(neighbors, next)
}
}
Expand Down
58 changes: 58 additions & 0 deletions go/2024/utils/grid/grid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package grid

/* Point
/* ====================================================== */

type Point struct {
X, Y int
}

var UP = Point{0, -1}
var DOWN = Point{0, 1}
var RIGHT = Point{1, 0}
var LEFT = Point{-1, 0}
var CARDINALS = []Point{UP, DOWN, LEFT, RIGHT}

func (p *Point) Add(p2 Point) Point {
return Point{p.X + p2.X, p.Y + p2.Y}
}

/* Grid
/* ====================================================== */

type Grid struct {
Data []byte
Height int
Width int
}

func New(lines []string) Grid {
data := []byte{}
height := len(lines)
width := len(lines[0])

for r := range height {
for c := range width {
current := lines[r][c]
data = append(data, current)
}
}

return Grid{
Data: data,
Height: height,
Width: width,
}
}

func (g *Grid) Get(p Point) byte {
return g.Data[g.Width*p.Y+p.X]
}

func (g *Grid) Contains(p Point) (byte, bool) {
if p.X >= 0 && p.X < g.Width && p.Y >= 0 && p.Y < g.Height {
return g.Get(p), true
}

return 0, false
}
5 changes: 0 additions & 5 deletions go/2024/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ package utils

import "strconv"

type Point struct {
x int
y int
}

// Ints
// -----------------------------------------------------------

Expand Down

0 comments on commit 9eb53db

Please sign in to comment.