package day12 import ( "adventofcode2024/utils/grid2d" "adventofcode2024/utils/inputs" "fmt" ) type Plot struct { crop string visited bool region int x int y int } type Region struct { plots []Plot crop string id int area int perimteter int sides int } func Part1(input string) int { var val int region_id := 0 regions := make(map[int]Region) grid := inputs.ToGrid2D(input, "\n", "", Plot{}, func(c string) Plot { return Plot{crop: c, region: -1} }) fmt.Println(grid.StringWithFormatter(formatter)) for y := 0; y < grid.SizeY(); y++ { for x := 0; x < grid.SizeX(); x++ { var region Region plot := grid.Get(x, y) if plot.visited { continue } region_id++ region = Region{id: region_id, area: 1, crop: plot.crop, sides: 4} plot.region = region_id plot.visited = true plot.x = x plot.y = y grid.Set(x, y, plot) region.plots = append(region.plots, plot) regions[region_id] = region try_neighours(grid, ®ion, x, y) regions[region.id] = region } } for _, region := range regions { val += region.area * region.perimteter } return val } func Part2(input string) int { var val int region_id := 0 regions := make(map[int]Region) grid := inputs.ToGrid2D(input, "\n", "", Plot{}, func(c string) Plot { return Plot{crop: c, region: -1} }) fmt.Println(grid.StringWithFormatter(formatter)) for y := 0; y < grid.SizeY(); y++ { for x := 0; x < grid.SizeX(); x++ { var region Region plot := grid.Get(x, y) if plot.visited { continue } region_id++ region = Region{id: region_id, area: 1, crop: plot.crop} plot.region = region_id plot.visited = true plot.x = x plot.y = y grid.Set(x, y, plot) region.plots = append(region.plots, plot) regions[region_id] = region try_neighours(grid, ®ion, x, y) regions[region.id] = region } } for _, region := range regions { val += region.area * get_sides(grid, region) } return val } func formatter(p Plot, x int, y int) string { return fmt.Sprintf("%v", p.crop) } func try_neighours(grid *grid2d.Grid[Plot], region *Region, x int, y int) { directions := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}} plot := grid.Get(x, y) perimeters := 0 for _, dir := range directions { x1 := x + dir[0] y1 := y + dir[1] nplot := grid.Get(x1, y1) if nplot.crop == plot.crop { if !nplot.visited { region.area++ nplot.visited = true nplot.region = plot.region nplot.x = x1 nplot.y = y1 grid.Set(x1, y1, nplot) region.plots = append(region.plots, nplot) try_neighours(grid, region, x1, y1) } } else { perimeters++ } } } func get_sides(grid *grid2d.Grid[Plot], region Region) int { sides := 0 corners := [][][]int{{{-1, 0}, {0, -1}, {-1, -1}}, {{+1, 0}, {0, -1}, {+1, -1}}, {{-1, 0}, {0, +1}, {-1, +1}}, {{+1, 0}, {0, +1}, {+1, +1}}} for _, plot := range region.plots { for _, o_corners := range corners { c1 := grid.Get(plot.x + o_corners[0][0], plot.y + o_corners[0][1]) c2 := grid.Get(plot.x + o_corners[1][0], plot.y + o_corners[1][1]) c3 := grid.Get(plot.x + o_corners[2][0], plot.y + o_corners[2][1]) if c1.crop != region.crop && c2.crop != region.crop { sides++ } else if c1.crop == region.crop && c2.crop == region.crop && c3.crop != region.crop { sides++ } } } return sides }