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 }