243 lines
4.8 KiB
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
|
|
}
|