This commit is contained in:
2022-12-23 19:45:56 -08:00
commit 8792e5275a
77 changed files with 31154 additions and 0 deletions

237
solutions/day23/day23.go Normal file
View 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
}
}