advent_of_code_2024/day_12/solution.go

243 lines
4.8 KiB
Go

package day_12
import "strings"
const Empty = '.'
type Map [][]rune
type Point struct {
x, y int
}
type Visited map[Point]bool
func (m Map) InMap(x, y int) bool {
return x >= 0 && x < len(m[0]) && y >= 0 && y < len(m)
}
func (m Map) At(x, y int) rune {
if m.InMap(x, y) {
return m[y][x]
}
return Empty
}
func (v Visited) At(x, y int) bool {
_, visited := v[Point{x, y}]
return visited
}
// task:https://adventofcode.com/2024/day/12
func SolveBasic(input string) int {
farmMap := loadMap(input)
visited := Visited{}
sum := 0
for y, row := range farmMap {
for x, value := range row {
p := Point{x, y}
field := Visited{}
if _, present := visited[p]; !present {
visit(farmMap, field, x, y, value)
area := len(field)
parameter := 0
for point := range field {
// mark as visited to not visit it more than once
visited[point] = true
if farmMap.At(point.x-1, point.y) != value {
parameter++
}
if farmMap.At(point.x+1, point.y) != value {
parameter++
}
if farmMap.At(point.x, point.y-1) != value {
parameter++
}
if farmMap.At(point.x, point.y+1) != value {
parameter++
}
}
sum += area * parameter
}
}
}
return sum
}
// task:https://adventofcode.com/2024/day/12#part2
func SolveComplex(input string) int {
farmMap := loadMap(input)
visited := Visited{}
sum := 0
for y, row := range farmMap {
for x, value := range row {
p := Point{x, y}
field := Visited{}
if _, present := visited[p]; !present {
visit(farmMap, field, x, y, value)
area := len(field)
parameter := countEdges(farmMap, field)
for point := range field {
// mark as visited to not visit it more than once
visited[point] = true
}
sum += area * parameter
}
}
}
return sum
}
// task:https://adventofcode.com/2024/day/12#part2
// there is no need to iterate over the whole table,
// more importantly there is no need to look at the set as we at the Map
func SolveComplexFast(input string) int {
farmMap := loadMap(input)
visited := Visited{}
sum := 0
for y, row := range farmMap {
for x, value := range row {
p := Point{x, y}
field := Visited{}
if _, present := visited[p]; !present {
visit(farmMap, field, x, y, value)
area := len(field)
parameter := countEdgesFast(farmMap, field, value)
for point := range field {
// mark as visited to not visit it more than once
visited[point] = true
}
sum += area * parameter
}
}
}
return sum
}
func countEdges(farmMap Map, field Visited) int {
borders := map[BorderPoint]bool{}
edges := 0
for y, row := range farmMap {
for x := range row {
if field.At(x, y) {
if !field.At(x-1, y) {
if !borders[BorderPoint{x, y - 1, LEFT}] {
edges++
}
borders[BorderPoint{x, y, LEFT}] = true
}
if !field.At(x, y-1) {
if !borders[BorderPoint{x - 1, y, TOP}] {
edges++
}
borders[BorderPoint{x, y, TOP}] = true
}
if !field.At(x, y+1) {
if !borders[BorderPoint{x - 1, y, BOTTOM}] {
edges++
}
borders[BorderPoint{x, y, BOTTOM}] = true
}
if !field.At(x+1, y) {
if !borders[BorderPoint{x, y - 1, RIGHT}] {
edges++
}
borders[BorderPoint{x, y, RIGHT}] = true
}
}
}
}
return edges
}
func countEdgesFast(farmMap Map, field Visited, value rune) int {
edges := 0
for p := range field {
hasN := farmMap.At(p.x, p.y-1) == value
hasW := farmMap.At(p.x-1, p.y) == value
hasS := farmMap.At(p.x, p.y+1) == value
hasE := farmMap.At(p.x+1, p.y) == value
hasNW := farmMap.At(p.x-1, p.y-1) == value
hasNE := farmMap.At(p.x+1, p.y-1) == value
hasSW := farmMap.At(p.x-1, p.y+1) == value
hasSE := farmMap.At(p.x+1, p.y+1) == value
// inner corner
if hasN && hasW && !hasNW {
edges++
}
if hasN && hasE && !hasNE {
edges++
}
if hasS && hasW && !hasSW {
edges++
}
if hasS && hasE && !hasSE {
edges++
}
// outer corner
if !hasN && !hasW {
edges++
}
if !hasN && !hasE {
edges++
}
if !hasS && !hasW {
edges++
}
if !hasS && !hasE {
edges++
}
}
return edges
}
type Direction int
const (
LEFT Direction = iota
TOP Direction = iota
RIGHT Direction = iota
BOTTOM Direction = iota
)
type BorderPoint struct {
x, y int
direction Direction
}
func visit(farmMap Map, field Visited, x int, y int, value rune) {
if _, visited := field[Point{x, y}]; visited {
return
}
if farmMap.At(x, y) == value {
field[Point{x, y}] = true
visit(farmMap, field, x-1, y, value)
visit(farmMap, field, x+1, y, value)
visit(farmMap, field, x, y-1, value)
visit(farmMap, field, x, y+1, value)
}
}
func loadMap(input string) Map {
topographicMap := Map{}
for _, line := range strings.Split(input, "\n") {
topographicMap = append(topographicMap, []rune(line))
}
return topographicMap
}