package day23 import ( "bufio" "fmt" "os" ) type Direction int const ( North Direction = iota South East West ) type Point struct { x int y int } type ElfLocations map[Point]bool func extents(locations ElfLocations) (topLeft Point, bottomRight Point) { lowestX := 1000000000000000000 highestX := -1000000000000000000 lowestY := 1000000000000000000 highestY := -1000000000000000000 for location := range locations { if location.x < lowestX { lowestX = location.x } if location.x > highestX { highestX = location.x } if location.y < lowestY { lowestY = location.y } if location.y > highestY { highestY = location.y } } topLeft = Point{lowestX, lowestY} bottomRight = Point{highestX, highestY} return } func hasElf(x int, y int, locations ElfLocations) bool { _, exists := locations[Point{x, y}] return exists } func printMap(locations ElfLocations) { topLeft, bottomRight := extents(locations) fmt.Println("top left", topLeft) fmt.Println("bottom right", bottomRight) for y := topLeft.y; y <= bottomRight.y; y++ { for x := topLeft.x; x <= bottomRight.x; x++ { if hasElf(x, y, locations) { fmt.Printf("#") } else { fmt.Printf(".") } } fmt.Println() } } func directionIsEmpty(direction Direction, x int, y int, locations ElfLocations) bool { switch direction { case North: return !hasElf(x-1, y-1, locations) && !hasElf(x, y-1, locations) && !hasElf(x+1, y-1, locations) case South: return !hasElf(x-1, y+1, locations) && !hasElf(x, y+1, locations) && !hasElf(x+1, y+1, locations) case West: return !hasElf(x-1, y-1, locations) && !hasElf(x-1, y, locations) && !hasElf(x-1, y+1, locations) case East: return !hasElf(x+1, y-1, locations) && !hasElf(x+1, y, locations) && !hasElf(x+1, y+1, locations) } return false } func showDirection(dir *Direction) string { if dir == nil { return "" } switch *dir { case North: return "north" case South: return "south" case East: return "east" case West: return "west" } return "" } func proposeMove(x int, y int, directionPreference []Direction, locations ElfLocations) *Point { var firstEmpty *Direction = nil allEmpty := true for _, dir := range directionPreference { if directionIsEmpty(dir, x, y, locations) { if firstEmpty == nil { firstEmpty = new(Direction) *firstEmpty = dir } } else { allEmpty = false } } if allEmpty { return nil } if firstEmpty == nil { return nil } switch *firstEmpty { case North: return &Point{x, y - 1} case South: return &Point{x, y + 1} case East: return &Point{x + 1, y} case West: return &Point{x - 1, y} } fmt.Println("PANIC: fell through?!") return nil } func runRound(locations ElfLocations, preferredDirections []Direction) (newLocations ElfLocations, done bool) { suggestedMoves := map[Point]Point{} takenPoints := map[Point]int{} for location := range locations { proposed := proposeMove(location.x, location.y, preferredDirections, locations) if proposed != nil { fmt.Println("Proposed to move", location, "to", proposed) suggestedMoves[location] = *proposed curVal := takenPoints[*proposed] takenPoints[*proposed] = curVal + 1 } } newLocations = ElfLocations{} done = true for originalLocation := range locations { newPlace, hasNewPlace := suggestedMoves[originalLocation] if hasNewPlace && takenPoints[newPlace] <= 1 { newLocations[newPlace] = true done = false } else { newLocations[originalLocation] = true } } return } func Run(filename string) { file, err := os.Open(filename) if err != nil { fmt.Println("Error opening file:", err) return } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) elfLocations := ElfLocations{} y := 0 for scanner.Scan() { line := []rune(scanner.Text()) for x, r := range line { if r == '#' { elfLocations[Point{x, y}] = true } } y += 1 } file.Close() preferredDirections := []Direction{North, South, West, East} for i := 0; i < 10; i++ { fmt.Println("Round", i) printMap(elfLocations) newLocations, done := runRound(elfLocations, preferredDirections) preferredDirections = append(preferredDirections[1:], preferredDirections[0]) if done { fmt.Println("No one moves after this round, so we're done.") break } elfLocations = newLocations } printMap(elfLocations) topLeft, bottomRight := extents(elfLocations) emptyGround := 0 for x := topLeft.x; x <= bottomRight.x; x++ { for y := topLeft.y; y <= bottomRight.y; y++ { if !hasElf(x, y, elfLocations) { emptyGround += 1 } } } fmt.Println(emptyGround, "empty spots in smallest bounding box.") round := 11 for { fmt.Println("Starting round", round) newLocations, done := runRound(elfLocations, preferredDirections) if done { fmt.Println("No one moved after round", round) break } elfLocations = newLocations preferredDirections = append(preferredDirections[1:], preferredDirections[0]) round += 1 } }