package main import ( "fmt" "math" "sort" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { inp := h.StdinToStringSlice() part1(inp) part2(inp) } func part1(inp []string) { field := plotElves(inp) for i := 0; i < 10; i++ { proposals := buildProposals(field, i) for k, v := range proposals { delete(field, v) field[k] = true } } fmt.Println("# Part 1") fmt.Println("Empty Tiles:", countEmpties(field)) } func part2(inp []string) { field := plotElves(inp) var lastHash string for i := 0; i < 100000000; i++ { proposals := buildProposals(field, i) for k, v := range proposals { delete(field, v) field[k] = true } currHash := fieldHash(field) if currHash == lastHash { fmt.Println("# Part 2") fmt.Println("Stable on round", i+1) return } lastHash = currHash } fmt.Println("# Part 2") fmt.Println("No solution found.") } func fieldHash(field map[h.Coordinate]bool) string { var sorted []h.Coordinate for k := range field { sorted = append(sorted, k) } sort.Slice(sorted, func(i, j int) bool { if sorted[i].X == sorted[j].X { return sorted[i].Y > sorted[j].Y } return sorted[i].X > sorted[j].X }) return fmt.Sprintf("%v", sorted) } func countEmpties(field map[h.Coordinate]bool) int { maxX, minX := math.MinInt, math.MaxInt maxY, minY := math.MinInt, math.MaxInt for k := range field { maxX = h.Max(maxX, k.X) minX = h.Min(minX, k.X) maxY = h.Max(maxY, k.Y) minY = h.Min(minY, k.Y) } var ret int for y := minY; y <= maxY; y++ { for x := minX; x <= maxX; x++ { if _, ok := field[h.Coordinate{X: x, Y: y}]; !ok { ret++ } } } return ret } func printField(field map[h.Coordinate]bool) { maxX, minX := math.MinInt, 0 maxY, minY := math.MinInt, 0 for k := range field { maxX = h.Max(maxX, k.X) minX = h.Min(minX, k.X) maxY = h.Max(maxY, k.Y) minY = h.Min(minY, k.Y) } maxX = maxX + 2 maxY = maxY + 2 for y := minY; y <= maxY; y++ { for x := minX; x <= maxX; x++ { if _, ok := field[h.Coordinate{X: x, Y: y}]; ok { fmt.Print("#") } else { fmt.Print(".") } } fmt.Println() } } func plotElves(inp []string) map[h.Coordinate]bool { field := make(map[h.Coordinate]bool) for y := range inp { for x := range inp[y] { if inp[y][x] == '#' { field[h.Coordinate{X: x, Y: y}] = true } } } return field } func buildProposals(field map[h.Coordinate]bool, round int) map[h.Coordinate]h.Coordinate { props := make(map[h.Coordinate]h.Coordinate) var errProps []h.Coordinate for k := range field { var neighbors []h.Coordinate for _, n := range k.GetAllNeighbors() { if _, ok := field[n]; ok { neighbors = append(neighbors, n) } } if len(neighbors) == 0 { continue } var hasNorth, hasEast, hasSouth, hasWest bool for _, n := range neighbors { if n.Y < k.Y { hasNorth = true } if n.Y > k.Y { hasSouth = true } if n.X < k.X { hasWest = true } if n.X > k.X { hasEast = true } } addProp := func(c h.Coordinate) { if _, ok := props[c]; ok { errProps = append(errProps, c) } else { props[c] = k } } testNorth := func(c h.Coordinate) bool { if !hasNorth { addProp(k.North()) return true } return false } testSouth := func(c h.Coordinate) bool { if !hasSouth { addProp(k.South()) return true } return false } testWest := func(c h.Coordinate) bool { if !hasWest { addProp(k.West()) return true } return false } testEast := func(c h.Coordinate) bool { if !hasEast { addProp(k.East()) return true } return false } findProposal := func(c h.Coordinate, tests ...func(h.Coordinate) bool) { for i := range tests { if tests[i](c) == true { return } } } switch round % 4 { case 0: // North First findProposal(k, testNorth, testSouth, testWest, testEast) case 1: // South First findProposal(k, testSouth, testWest, testEast, testNorth) case 2: // West First findProposal(k, testWest, testEast, testNorth, testSouth) case 3: // East First findProposal(k, testEast, testNorth, testSouth, testWest) } } // Now remove all conflicts for i := range errProps { delete(props, errProps[i]) } return props }