🌅
This commit is contained in:
237
solutions/day23/day23.go
Normal file
237
solutions/day23/day23.go
Normal file
@@ -0,0 +1,237 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user