229 lines
5.6 KiB
Go
229 lines
5.6 KiB
Go
package common
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
|
|
type Tile byte
|
|
|
|
const (
|
|
Empty Tile = iota
|
|
Elf
|
|
)
|
|
|
|
|
|
type Direction byte
|
|
|
|
const (
|
|
East Direction = iota
|
|
West
|
|
North
|
|
South
|
|
)
|
|
|
|
func (direction Direction) GetNext() Direction {
|
|
switch direction {
|
|
case North:
|
|
return South
|
|
case South:
|
|
return West
|
|
case West:
|
|
return East
|
|
default:
|
|
return North
|
|
}
|
|
}
|
|
|
|
|
|
type Coordinates struct {
|
|
x int
|
|
y int
|
|
}
|
|
|
|
|
|
type Map struct {
|
|
tiles map[Coordinates]Tile
|
|
currentRound Direction
|
|
boundedCoordinates []Coordinates
|
|
}
|
|
|
|
func (tileMap *Map) Round() bool {
|
|
nextCoordinates := map[Coordinates][]Coordinates{}
|
|
isFinalRound := true
|
|
|
|
for coordinates, tile := range tileMap.tiles {
|
|
if tile == Elf {
|
|
neighbor := false
|
|
outer:
|
|
for i := -1; i <= 1; i++ {
|
|
for j := -1; j <= 1; j++ {
|
|
if i != 0 || j != 0 {
|
|
if tile, ok := tileMap.tiles[Coordinates{coordinates.x+i, coordinates.y+j}]; ok && tile == Elf {
|
|
neighbor = true
|
|
break outer
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !neighbor {
|
|
continue
|
|
}
|
|
|
|
var nextC Coordinates
|
|
round := tileMap.currentRound
|
|
move := false
|
|
|
|
loop:
|
|
for i := 0; i < 4; i++ {
|
|
switch round {
|
|
case North:
|
|
tile1, ok1 := tileMap.tiles[Coordinates{coordinates.x-1, coordinates.y-1}]
|
|
tile2, ok2 := tileMap.tiles[Coordinates{coordinates.x, coordinates.y-1}]
|
|
tile3, ok3 := tileMap.tiles[Coordinates{coordinates.x+1, coordinates.y-1}]
|
|
nextC = Coordinates{coordinates.x, coordinates.y-1}
|
|
if (!ok1 || tile1 == Empty) && (!ok2 || tile2 == Empty) && (!ok3 || tile3 == Empty) {
|
|
move = true
|
|
break loop
|
|
}
|
|
case South:
|
|
tile1, ok1 := tileMap.tiles[Coordinates{coordinates.x-1, coordinates.y+1}]
|
|
tile2, ok2 := tileMap.tiles[Coordinates{coordinates.x, coordinates.y+1}]
|
|
tile3, ok3 := tileMap.tiles[Coordinates{coordinates.x+1, coordinates.y+1}]
|
|
nextC = Coordinates{coordinates.x, coordinates.y+1}
|
|
if (!ok1 || tile1 == Empty) && (!ok2 || tile2 == Empty) && (!ok3 || tile3 == Empty) {
|
|
move = true
|
|
break loop
|
|
}
|
|
case West:
|
|
tile1, ok1 := tileMap.tiles[Coordinates{coordinates.x-1, coordinates.y-1}]
|
|
tile2, ok2 := tileMap.tiles[Coordinates{coordinates.x-1, coordinates.y}]
|
|
tile3, ok3 := tileMap.tiles[Coordinates{coordinates.x-1, coordinates.y+1}]
|
|
nextC = Coordinates{coordinates.x-1, coordinates.y}
|
|
if (!ok1 || tile1 == Empty) && (!ok2 || tile2 == Empty) && (!ok3 || tile3 == Empty) {
|
|
move = true
|
|
break loop
|
|
}
|
|
case East:
|
|
tile1, ok1 := tileMap.tiles[Coordinates{coordinates.x+1, coordinates.y-1}]
|
|
tile2, ok2 := tileMap.tiles[Coordinates{coordinates.x+1, coordinates.y}]
|
|
tile3, ok3 := tileMap.tiles[Coordinates{coordinates.x+1, coordinates.y+1}]
|
|
nextC = Coordinates{coordinates.x+1, coordinates.y}
|
|
if (!ok1 || tile1 == Empty) && (!ok2 || tile2 == Empty) && (!ok3 || tile3 == Empty) {
|
|
move = true
|
|
break loop
|
|
}
|
|
}
|
|
round = round.GetNext()
|
|
}
|
|
|
|
if move {
|
|
if _, okc := nextCoordinates[nextC]; !okc {
|
|
nextCoordinates[nextC] = []Coordinates{}
|
|
}
|
|
nextCoordinates[nextC] = append(nextCoordinates[nextC], coordinates)
|
|
}
|
|
}
|
|
}
|
|
|
|
for nextC := range nextCoordinates {
|
|
if len(nextCoordinates[nextC]) == 1 {
|
|
tileMap.tiles[nextCoordinates[nextC][0]] = Empty
|
|
tileMap.tiles[nextC] = Elf
|
|
isFinalRound = false
|
|
}
|
|
}
|
|
|
|
tileMap.currentRound = tileMap.currentRound.GetNext()
|
|
|
|
return isFinalRound
|
|
}
|
|
|
|
func (tileMap *Map) GetEmpty() int {
|
|
nEmpty := 0
|
|
for x := tileMap.boundedCoordinates[0].x; x <= tileMap.boundedCoordinates[1].x; x++ {
|
|
for y := tileMap.boundedCoordinates[0].y; y <= tileMap.boundedCoordinates[1].y; y++ {
|
|
if tileMap.tiles[Coordinates{x, y}] == Empty {
|
|
nEmpty++
|
|
}
|
|
}
|
|
}
|
|
return nEmpty
|
|
}
|
|
|
|
func (tileMap *Map) Bound() {
|
|
minX := math.MaxInt32
|
|
minY := math.MaxInt32
|
|
maxX := math.MinInt32
|
|
maxY := math.MinInt32
|
|
|
|
for coordinates, tile := range tileMap.tiles {
|
|
if tile == Elf {
|
|
if coordinates.x < minX {
|
|
minX = coordinates.x
|
|
}
|
|
if coordinates.x > maxX {
|
|
maxX = coordinates.x
|
|
}
|
|
if coordinates.y < minY {
|
|
minY = coordinates.y
|
|
}
|
|
if coordinates.y > maxY {
|
|
maxY = coordinates.y
|
|
}
|
|
}
|
|
}
|
|
|
|
tileMap.boundedCoordinates = []Coordinates{{minX, minY}, {maxX, maxY}}
|
|
|
|
for x := minX; x <= maxX; x++ {
|
|
for y := minY; y <= maxY; y++ {
|
|
c := Coordinates{x, y}
|
|
if _, ok := tileMap.tiles[c]; !ok {
|
|
tileMap.tiles[c] = Empty
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (tileMap *Map) Print() {
|
|
for y := tileMap.boundedCoordinates[0].y; y <= tileMap.boundedCoordinates[1].y; y++ {
|
|
for x := tileMap.boundedCoordinates[0].x; x <= tileMap.boundedCoordinates[1].x; x++ {
|
|
switch tileMap.tiles[Coordinates{x, y}] {
|
|
case Empty:
|
|
fmt.Print(".")
|
|
case Elf:
|
|
fmt.Print("#")
|
|
}
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
|
|
|
|
func Parse(scanner bufio.Scanner) Map {
|
|
tileMap := Map{}
|
|
tileMap.tiles = make(map[Coordinates]Tile)
|
|
|
|
i := 0
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
for j := range line {
|
|
switch line[j] {
|
|
case '.':
|
|
tileMap.tiles[Coordinates{j, i}] = Empty
|
|
case '#':
|
|
tileMap.tiles[Coordinates{j, i}] = Elf
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
|
|
tileMap.currentRound = North
|
|
|
|
return tileMap
|
|
}
|