package main import ( "fmt" "slices" "sync" "git.mstar.dev/mstar/aoc24/util" "git.mstar.dev/mstar/goutils/sliceutils" ) type Block struct { Char rune Counter int } type Coordinate struct { X, Y int } type Area struct { area [][]Block //Y, X rwl sync.RWMutex } func convertLinesIntoBlockMap(lines []string) Area { out := [][]Block{} for _, line := range lines { bordLine := []Block{} for _, char := range line { // All blocks have four different blocks bordering in the beginning bordLine = append(bordLine, Block{char, 0}) } out = append(out, bordLine) } return Area{out, sync.RWMutex{}} } func (c Coordinate) Left() Coordinate { return Coordinate{c.X - 1, c.Y} } func (c Coordinate) Right() Coordinate { return Coordinate{c.X + 1, c.Y} } func (c Coordinate) Up() Coordinate { return Coordinate{c.X, c.Y - 1} } func (c Coordinate) Down() Coordinate { return Coordinate{c.X, c.Y + 1} } func (a *Area) Get(x, y int) *Block { if y < 0 || y >= len(a.area) || x < 0 || x >= len(a.area[0]) { return nil } a.rwl.RLock() defer a.rwl.RUnlock() return &a.area[y][x] } func (a *Area) GetCoord(c Coordinate) *Block { if c.Y < 0 || c.Y >= len(a.area) || c.X < 0 || c.X >= len(a.area[0]) { return nil } a.rwl.RLock() defer a.rwl.RUnlock() return &a.area[c.Y][c.X] } func (a *Area) CountEdgesAtCoord(c Coordinate) int { block := a.GetCoord(c) if block == nil { return 1 } acc := 0 left := a.GetCoord(c.Left()) if left == nil || left.Char != block.Char { acc++ } right := a.GetCoord(c.Right()) if right == nil || right.Char != block.Char { acc++ } up := a.GetCoord(c.Up()) if up == nil || up.Char != block.Char { acc++ } down := a.GetCoord(c.Down()) if down == nil || down.Char != block.Char { acc++ } return acc } func (a *Area) CollectRegion(at Coordinate) []Coordinate { coords := []Coordinate{at} self := a.GetCoord(at) self.Counter++ left := a.GetCoord(at.Left()) if left != nil && left.Char == self.Char && left.Counter != self.Counter { coords = append(coords, a.CollectRegion(at.Left())...) } right := a.GetCoord(at.Right()) if right != nil && right.Char == self.Char && right.Counter != self.Counter { coords = append(coords, a.CollectRegion(at.Right())...) } Up := a.GetCoord(at.Up()) if Up != nil && Up.Char == self.Char && Up.Counter != self.Counter { coords = append(coords, a.CollectRegion(at.Up())...) } Down := a.GetCoord(at.Down()) if Down != nil && Down.Char == self.Char && Down.Counter != self.Counter { coords = append(coords, a.CollectRegion(at.Down())...) } return coords } func (a *Area) GetAllAreas() [][]Coordinate { areas := [][]Coordinate{} for iy, line := range a.area { inner: for ix := range line { tmp := a.CollectRegion(Coordinate{ix, iy}) for _, a := range areas { if sliceutils.CompareUnordered(a, tmp) { continue inner } } areas = append(areas, tmp) } } return areas } // Filter for coordinates where a section borders on a different section func filterBorderLeft(coords []Coordinate, area *Area) []Coordinate { return sliceutils.Filter(coords, func(t Coordinate) bool { self := area.GetCoord(t) other := area.GetCoord(t.Left()) return other == nil || self.Char != other.Char }) } func filterBorderRight(coords []Coordinate, area *Area) []Coordinate { return sliceutils.Filter(coords, func(t Coordinate) bool { self := area.GetCoord(t) other := area.GetCoord(t.Right()) return other == nil || self.Char != other.Char }) } func filterBorderUp(coords []Coordinate, area *Area) []Coordinate { return sliceutils.Filter(coords, func(t Coordinate) bool { self := area.GetCoord(t) other := area.GetCoord(t.Up()) return other == nil || self.Char != other.Char }) } func filterBorderDown(coords []Coordinate, area *Area) []Coordinate { return sliceutils.Filter(coords, func(t Coordinate) bool { self := area.GetCoord(t) other := area.GetCoord(t.Down()) return other == nil || self.Char != other.Char }) } func splitSameX(coords []Coordinate) [][]Coordinate { axis := [][]Coordinate{} axisLabeled := map[int][]Coordinate{} for _, coord := range coords { axisLabeled[coord.X] = append(axisLabeled[coord.X], coord) } for _, subCoords := range axisLabeled { axis = append(axis, subCoords) } return axis } func splitSameY(coords []Coordinate) [][]Coordinate { axis := [][]Coordinate{} axisLabeled := map[int][]Coordinate{} for _, coord := range coords { axisLabeled[coord.Y] = append(axisLabeled[coord.Y], coord) } for _, subCoords := range axisLabeled { axis = append(axis, subCoords) } return axis } func countUniqueEdgesLeft(coords []Coordinate, area *Area) int { acc := 0 leftBorders := filterBorderLeft(coords, area) // fmt.Printf("All left borders: %v\n", leftBorders) bordersLeftPerAxis := splitSameX(leftBorders) // fmt.Printf("Same left: %v\n", bordersLeftPerAxis) for _, borders := range bordersLeftPerAxis { // acc++ slices.SortFunc(borders, func(a, b Coordinate) int { return a.Y - b.Y }) // fmt.Printf("Axis left: %v\n", borders) prevY := -500 for _, coord := range borders { if coord.Y-1 != prevY { acc++ } prevY = coord.Y } } fmt.Printf("Left borders: %v\n", acc) return acc } func countUniqueEdgesRight(coords []Coordinate, area *Area) int { acc := 0 bordersLeftPerAxis := splitSameX(filterBorderRight(coords, area)) // fmt.Printf("Same right: %v\n", bordersLeftPerAxis) for _, borders := range bordersLeftPerAxis { slices.SortFunc(borders, func(a, b Coordinate) int { return a.Y - b.Y }) // acc++ prevX := -500 for _, coord := range borders { if coord.Y-1 != prevX { acc++ } prevX = coord.Y } } fmt.Printf("Right borders: %v\n", acc) return acc } func countUniqueEdgesUp(coords []Coordinate, area *Area) int { acc := 0 bordersLeftPerAxis := splitSameY(filterBorderUp(coords, area)) fmt.Printf("Same up: %v\n", bordersLeftPerAxis) for _, borders := range bordersLeftPerAxis { // acc++ slices.SortFunc(borders, func(a, b Coordinate) int { return a.X - b.X }) fmt.Printf("Sorted up axis: %v\n", borders) prevX := -500 for _, coord := range borders { if coord.X-1 != prevX { acc++ fmt.Println("Up inc") } prevX = coord.X } } fmt.Printf("Up borders: %v\n", acc) return acc } func countUniqueEdgesDown(coords []Coordinate, area *Area) int { acc := 0 bordersLeftPerAxis := splitSameY(filterBorderDown(coords, area)) for _, borders := range bordersLeftPerAxis { // acc++ slices.SortFunc(borders, func(a, b Coordinate) int { return a.X - b.X }) fmt.Printf("Same down: %v\n", bordersLeftPerAxis) prevX := -500 for _, coord := range borders { if coord.X-1 != prevX { fmt.Println("Bigdiff") acc++ } prevX = coord.X } } fmt.Printf("Down borders: %v\n", acc) return acc } func countUniqueEdges(coords []Coordinate, area *Area) int { return countUniqueEdgesLeft( coords, area, ) + countUniqueEdgesRight( coords, area, ) + countUniqueEdgesUp( coords, area, ) + countUniqueEdgesDown( coords, area, ) } func main() { area := convertLinesIntoBlockMap(util.FileContentToNonEmptyLines(util.LoadFileFromArgs())) allAreas := area.GetAllAreas() totalAcc := 0 for _, subArea := range allAreas { tmp := 0 for _, coord := range subArea { tmp += area.CountEdgesAtCoord(coord) } totalAcc += tmp * len(subArea) } fmt.Printf("Task 1: %d\n", totalAcc) // fmt.Println(countUniqueEdges(allAreas[2], &area)) // return edgesAcc := 0 for _, subArea := range allAreas { tmp := countUniqueEdges(subArea, &area) fmt.Printf( "Area %s has %d edges with len %d\n", string(area.GetCoord(subArea[0]).Char), tmp, len(subArea), ) // fmt.Println(tmp * len(subArea)) edgesAcc += tmp * len(subArea) } fmt.Printf("Task 2: %d\n", edgesAcc) } // c2: l14 e22 // f3: l10 e12 // e7: