mirror of
https://github.com/onyx-and-iris/aoc2024.git
synced 2026-04-08 18:13:36 +00:00
add day-12 + benchmarks
This commit is contained in:
6
day-12/internal/one/benchmark
Normal file
6
day-12/internal/one/benchmark
Normal file
@@ -0,0 +1,6 @@
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/onyx-and-iris/aoc2024/day-12/internal/one
|
||||
cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
|
||||
BenchmarkSolve-12 1000000000 0.03239 ns/op
|
||||
ok github.com/onyx-and-iris/aoc2024/day-12/internal/one 0.236s
|
||||
8
day-12/internal/one/direction.go
Normal file
8
day-12/internal/one/direction.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package one
|
||||
|
||||
const (
|
||||
N = iota
|
||||
E
|
||||
S
|
||||
W
|
||||
)
|
||||
37
day-12/internal/one/explore.go
Normal file
37
day-12/internal/one/explore.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package one
|
||||
|
||||
import (
|
||||
"github.com/onyx-and-iris/aoc2024/day-12/internal/queue"
|
||||
)
|
||||
|
||||
func explore(start point, graph *graph) path {
|
||||
queue := queue.New[point]()
|
||||
queue.Enqueue(start)
|
||||
visited := make(map[point]struct{})
|
||||
var perimeter int
|
||||
|
||||
for !queue.IsEmpty() {
|
||||
current := queue.Dequeue()
|
||||
|
||||
_, ok := visited[current]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
visited[current] = struct{}{}
|
||||
|
||||
for _, n := range neighbours(current) {
|
||||
if graph.isOutOfBounds(n) {
|
||||
perimeter++
|
||||
continue
|
||||
}
|
||||
|
||||
if graph.valueAt(n) != graph.valueAt(current) {
|
||||
perimeter++
|
||||
continue
|
||||
}
|
||||
queue.Enqueue(n)
|
||||
}
|
||||
}
|
||||
|
||||
return path{visited, perimeter}
|
||||
}
|
||||
46
day-12/internal/one/graph.go
Normal file
46
day-12/internal/one/graph.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package one
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type point struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func newPoint(x, y int) point {
|
||||
return point{x, y}
|
||||
}
|
||||
|
||||
type graph struct {
|
||||
data []string
|
||||
}
|
||||
|
||||
func newGraph() *graph {
|
||||
return &graph{}
|
||||
}
|
||||
|
||||
func (g *graph) String() string {
|
||||
return strings.Join(g.data, "\n")
|
||||
}
|
||||
|
||||
func (g *graph) isOutOfBounds(p point) bool {
|
||||
return p.x < 0 || p.y < 0 || p.y >= len(g.data) || p.x >= len(g.data[p.y])
|
||||
}
|
||||
|
||||
func (g *graph) valueAt(p point) rune {
|
||||
return rune(g.data[p.y][p.x])
|
||||
}
|
||||
|
||||
func (g *graph) debug(visited map[point]struct{}) string {
|
||||
log.Debugf("path for %s", string(g.valueAt(firstPointFromMap(visited))))
|
||||
temp := slices.Clone(g.data)
|
||||
for point := range visited {
|
||||
temp[point.y] = replaceAtIndex(temp[point.y], 'x', point.x)
|
||||
}
|
||||
return strings.Join(temp, "\n")
|
||||
}
|
||||
10
day-12/internal/one/neighbours.go
Normal file
10
day-12/internal/one/neighbours.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package one
|
||||
|
||||
func neighbours(p point) [4]point {
|
||||
return [4]point{
|
||||
{p.x, p.y - 1}, // N
|
||||
{p.x + 1, p.y}, // E
|
||||
{p.x, p.y + 1}, // S
|
||||
{p.x - 1, p.y}, // W
|
||||
}
|
||||
}
|
||||
6
day-12/internal/one/path.go
Normal file
6
day-12/internal/one/path.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package one
|
||||
|
||||
type path struct {
|
||||
visited map[point]struct{}
|
||||
perimeter int
|
||||
}
|
||||
42
day-12/internal/one/solve.go
Normal file
42
day-12/internal/one/solve.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package one
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Solve(buf []byte) (int, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
graph, err := parseLines(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var totalCost int
|
||||
totalAreaVisited := make(map[point]struct{})
|
||||
for i := 0; i < len(graph.data); i++ {
|
||||
for j := 0; j < len(graph.data[i]); j++ {
|
||||
start := newPoint(j, i)
|
||||
if graph.isOutOfBounds(start) {
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok := totalAreaVisited[start]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
totalAreaVisited[start] = struct{}{}
|
||||
|
||||
path := explore(start, graph)
|
||||
log.Debugf("\n%s\n", graph.debug(path.visited))
|
||||
totalCost += len(path.visited) * path.perimeter
|
||||
|
||||
for point := range path.visited {
|
||||
totalAreaVisited[point] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalCost, nil
|
||||
}
|
||||
15
day-12/internal/one/solve_internal_test.go
Normal file
15
day-12/internal/one/solve_internal_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package one
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:embed testdata/input.txt
|
||||
var data []byte
|
||||
|
||||
func BenchmarkSolve(b *testing.B) {
|
||||
os.Stdout, _ = os.Open(os.DevNull)
|
||||
Solve(data)
|
||||
}
|
||||
35
day-12/internal/one/util.go
Normal file
35
day-12/internal/one/util.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package one
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"maps"
|
||||
)
|
||||
|
||||
func parseLines(r io.Reader) (*graph, error) {
|
||||
graph := newGraph()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
graph.data = append(graph.data, scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
func replaceAtIndex(s string, r rune, i int) string {
|
||||
out := []rune(s)
|
||||
out[i] = r
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func firstPointFromMap(visited map[point]struct{}) point {
|
||||
for k := range maps.Keys(visited) {
|
||||
return k
|
||||
}
|
||||
panic("unable to fetch points from map")
|
||||
}
|
||||
Reference in New Issue
Block a user