diff --git a/day24/common/blizzard.go b/day24/common/blizzard.go new file mode 100644 index 0000000..ab5af2c --- /dev/null +++ b/day24/common/blizzard.go @@ -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 +} diff --git a/day24/ex1/main.go b/day24/ex1/main.go new file mode 100644 index 0000000..bd06f2c --- /dev/null +++ b/day24/ex1/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "aoc2022/day24/common" + "bufio" + "fmt" + "os" +) + +func main() { + tileMap := common.Parse(*bufio.NewScanner(os.Stdin)) + fmt.Println(tileMap.GetDistanceToEnd(0)) +} diff --git a/day24/ex2/main.go b/day24/ex2/main.go new file mode 100644 index 0000000..ebb4542 --- /dev/null +++ b/day24/ex2/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "aoc2022/day24/common" + "bufio" + "fmt" + "os" +) + +func main() { + tileMap := common.Parse(*bufio.NewScanner(os.Stdin)) + current := tileMap.GetDistanceToEnd(0) + current = tileMap.GetDistanceToStart(current) + fmt.Println(tileMap.GetDistanceToEnd(current)) +}