238 lines
4.9 KiB
Go
238 lines
4.9 KiB
Go
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 "<nil>"
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|