Day 24
This commit is contained in:
248
day24/common/blizzard.go
Normal file
248
day24/common/blizzard.go
Normal file
@@ -0,0 +1,248 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
|
||||
type Tile byte
|
||||
|
||||
const (
|
||||
Empty Tile = iota
|
||||
Wall
|
||||
Blizzard
|
||||
)
|
||||
|
||||
|
||||
type Map struct {
|
||||
tiles [][][]Tile
|
||||
steps int
|
||||
height int
|
||||
width int
|
||||
cache [][][]int
|
||||
}
|
||||
|
||||
func (tileMap *Map) InitCache() {
|
||||
tileMap.cache = make([][][]int, (tileMap.height - 2) * (tileMap.width - 2))
|
||||
for i := 0; i < len(tileMap.cache); i++ {
|
||||
tileMap.cache[i] = make([][]int, tileMap.height)
|
||||
for j := 0; j < len(tileMap.cache[i]); j++ {
|
||||
tileMap.cache[i][j] = make([]int, tileMap.width)
|
||||
for k := 0; k < len(tileMap.cache[i][j]); k++ {
|
||||
tileMap.cache[i][j][k] = math.MaxInt32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tileMap *Map) GetDistanceToEnd(currentSteps int) int {
|
||||
tileMap.InitCache()
|
||||
|
||||
startSteps := currentSteps % tileMap.steps
|
||||
startCol := 0
|
||||
for i := 0; i < tileMap.width; i++ {
|
||||
if tileMap.tiles[startSteps][0][i] == Empty {
|
||||
startCol = i
|
||||
break
|
||||
}
|
||||
}
|
||||
tileMap.cache[startSteps][0][startCol] = currentSteps
|
||||
tileMap.PropagateDistanceFromTile(startSteps, startCol, 0, currentSteps)
|
||||
|
||||
endCol := 0
|
||||
for i := 0; i < tileMap.width; i++ {
|
||||
if tileMap.tiles[startSteps][len(tileMap.tiles[startSteps])-1][i] == Empty {
|
||||
endCol = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
minDistance := math.MaxInt32
|
||||
for i := 0; i < tileMap.steps; i++ {
|
||||
if tileMap.cache[i][len(tileMap.tiles[0])-1][endCol] < minDistance {
|
||||
minDistance = tileMap.cache[i][len(tileMap.tiles[0])-1][endCol]
|
||||
}
|
||||
}
|
||||
return minDistance
|
||||
}
|
||||
|
||||
func (tileMap *Map) GetDistanceToStart(currentSteps int) int {
|
||||
tileMap.InitCache()
|
||||
|
||||
startSteps := currentSteps % tileMap.steps
|
||||
endCol := 0
|
||||
for i := 0; i < tileMap.width; i++ {
|
||||
if tileMap.tiles[startSteps][len(tileMap.tiles[startSteps])-1][i] == Empty {
|
||||
endCol = i
|
||||
break
|
||||
}
|
||||
}
|
||||
tileMap.cache[startSteps][len(tileMap.tiles[startSteps])-1][endCol] = currentSteps
|
||||
tileMap.PropagateDistanceFromTile(startSteps, endCol, len(tileMap.tiles[startSteps])-1, currentSteps)
|
||||
|
||||
startCol := 0
|
||||
for i := 0; i < tileMap.width; i++ {
|
||||
if tileMap.tiles[startSteps][0][i] == Empty {
|
||||
startCol = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
minDistance := math.MaxInt32
|
||||
for i := 0; i < tileMap.steps; i++ {
|
||||
if tileMap.cache[i][0][startCol] < minDistance {
|
||||
minDistance = tileMap.cache[i][0][startCol]
|
||||
}
|
||||
}
|
||||
return minDistance
|
||||
}
|
||||
|
||||
func (tileMap *Map) PropagateDistanceFromTile(step, x, y, currentDistance int) {
|
||||
nextStep := (step + 1) % tileMap.steps
|
||||
|
||||
if x > 0 && tileMap.tiles[nextStep][y][x-1] == Empty && currentDistance + 1 < tileMap.cache[nextStep][y][x-1] {
|
||||
tileMap.cache[nextStep][y][x-1] = currentDistance + 1
|
||||
tileMap.PropagateDistanceFromTile(nextStep, x-1, y, currentDistance+1)
|
||||
}
|
||||
if x < tileMap.width - 1 && tileMap.tiles[nextStep][y][x+1] == Empty && currentDistance + 1 < tileMap.cache[nextStep][y][x+1] {
|
||||
tileMap.cache[nextStep][y][x+1] = currentDistance + 1
|
||||
tileMap.PropagateDistanceFromTile(nextStep, x+1, y, currentDistance+1)
|
||||
}
|
||||
if y > 0 && tileMap.tiles[nextStep][y-1][x] == Empty && currentDistance + 1 < tileMap.cache[nextStep][y-1][x] {
|
||||
tileMap.cache[nextStep][y-1][x] = currentDistance + 1
|
||||
tileMap.PropagateDistanceFromTile(nextStep, x, y-1, currentDistance+1)
|
||||
}
|
||||
if y < tileMap.height - 1 && tileMap.tiles[nextStep][y+1][x] == Empty && currentDistance + 1 < tileMap.cache[nextStep][y+1][x] {
|
||||
tileMap.cache[nextStep][y+1][x] = currentDistance + 1
|
||||
tileMap.PropagateDistanceFromTile(nextStep, x, y+1, currentDistance+1)
|
||||
}
|
||||
if tileMap.tiles[nextStep][y][x] == Empty && currentDistance + 1 < tileMap.cache[nextStep][y][x] {
|
||||
tileMap.cache[nextStep][y][x] = currentDistance + 1
|
||||
tileMap.PropagateDistanceFromTile(nextStep, x, y, currentDistance+1)
|
||||
}
|
||||
}
|
||||
|
||||
func (tileMap *Map) Print(step int) {
|
||||
for i := 0; i < len(tileMap.tiles[step]); i++ {
|
||||
for j := 0; j < len(tileMap.tiles[step][i]); j++ {
|
||||
switch tileMap.tiles[step][i][j] {
|
||||
case Empty:
|
||||
fmt.Print(".")
|
||||
case Wall:
|
||||
fmt.Print("#")
|
||||
case Blizzard:
|
||||
fmt.Print("O")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func Parse(scanner bufio.Scanner) Map {
|
||||
tileMap := Map{}
|
||||
width := 0
|
||||
height := 0
|
||||
walls := make(map[int]map[int]struct{})
|
||||
blizzards := make(map[int]map[int]map[int]struct{})
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
blizzards[i] = make(map[int]map[int]struct{})
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
width = len(line)
|
||||
walls[height] = make(map[int]struct{})
|
||||
for i := 0; i < 4; i++ {
|
||||
blizzards[i][height] = make(map[int]struct{})
|
||||
}
|
||||
|
||||
for i := range line {
|
||||
switch line[i] {
|
||||
case '#':
|
||||
walls[height][i] = struct{}{}
|
||||
case '>':
|
||||
blizzards[0][height][i] = struct{}{}
|
||||
case 'v':
|
||||
blizzards[1][height][i] = struct{}{}
|
||||
case '<':
|
||||
blizzards[2][height][i] = struct{}{}
|
||||
case '^':
|
||||
blizzards[3][height][i] = struct{}{}
|
||||
}
|
||||
}
|
||||
height++
|
||||
}
|
||||
|
||||
tileMap.tiles = make([][][]Tile, (height - 2) * (width - 2))
|
||||
for i := 0; i < height - 2; i++ {
|
||||
for j := 0; j < width - 2; j++ {
|
||||
tileMap.tiles[i * (width - 2) + j] = make([][]Tile, height)
|
||||
for row := range walls {
|
||||
tileMap.tiles[i * (width - 2) + j][row] = make([]Tile, width)
|
||||
for col := range walls[row] {
|
||||
tileMap.tiles[i * (width - 2) + j][row][col] = Wall
|
||||
}
|
||||
}
|
||||
|
||||
newBlizzards := make(map[int]map[int]map[int]struct{})
|
||||
for k := 0; k < 4; k++ {
|
||||
newBlizzards[k] = make(map[int]map[int]struct{})
|
||||
for l := 0; l < height; l++ {
|
||||
newBlizzards[k][l] = make(map[int]struct{})
|
||||
}
|
||||
}
|
||||
|
||||
for row := range blizzards[0] {
|
||||
for col := range blizzards[0][row] {
|
||||
tileMap.tiles[i * (width - 2) + j][row][col] = Blizzard
|
||||
if col == width - 2 {
|
||||
newBlizzards[0][row][1] = struct{}{}
|
||||
} else {
|
||||
newBlizzards[0][row][col+1] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for row := range blizzards[1] {
|
||||
for col := range blizzards[1][row] {
|
||||
tileMap.tiles[i * (width - 2) + j][row][col] = Blizzard
|
||||
if row == height - 2 {
|
||||
newBlizzards[1][1][col] = struct{}{}
|
||||
} else {
|
||||
newBlizzards[1][row+1][col] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for row := range blizzards[2] {
|
||||
for col := range blizzards[2][row] {
|
||||
tileMap.tiles[i * (width - 2) + j][row][col] = Blizzard
|
||||
if col == 1 {
|
||||
newBlizzards[2][row][width-2] = struct{}{}
|
||||
} else {
|
||||
newBlizzards[2][row][col-1] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for row := range blizzards[3] {
|
||||
for col := range blizzards[3][row] {
|
||||
tileMap.tiles[i * (width - 2) + j][row][col] = Blizzard
|
||||
if row == 1 {
|
||||
newBlizzards[3][height-2][col] = struct{}{}
|
||||
} else {
|
||||
newBlizzards[3][row-1][col] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
blizzards = newBlizzards
|
||||
}
|
||||
}
|
||||
|
||||
tileMap.height = height
|
||||
tileMap.width = width
|
||||
tileMap.steps = (height - 2) * (width - 2)
|
||||
|
||||
return tileMap
|
||||
}
|
||||
Reference in New Issue
Block a user