This commit is contained in:
2022-12-24 14:33:22 +01:00
parent 43ebc47a01
commit e42cd31ec0
3 changed files with 276 additions and 0 deletions

248
day24/common/blizzard.go Normal file
View 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
}