249 lines
7.0 KiB
Go
249 lines
7.0 KiB
Go
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
|
|
}
|