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
 | 
						|
}
 |