226 lines
5.8 KiB
Go
226 lines
5.8 KiB
Go
package day16
|
|
|
|
import (
|
|
"adventofcode2024/utils/dijkstra"
|
|
"adventofcode2024/utils/grid2d"
|
|
"adventofcode2024/utils/inputs"
|
|
"fmt"
|
|
_ "strings"
|
|
)
|
|
|
|
type (
|
|
Vec struct{ x, y int }
|
|
Dir struct{ x, y int }
|
|
Cell rune
|
|
Move struct {
|
|
p Vec
|
|
d Dir
|
|
}
|
|
Maze struct {
|
|
height, width int
|
|
start Vec
|
|
end Vec
|
|
grid *grid2d.Grid[Cell]
|
|
}
|
|
)
|
|
|
|
const (
|
|
WALL Cell = '#'
|
|
CORRIDOR Cell = '.'
|
|
START Cell = 'S'
|
|
END Cell = 'E'
|
|
PATH Cell = '*'
|
|
)
|
|
|
|
var (
|
|
NORTH = Dir{0, -1}
|
|
EAST = Dir{1, 0}
|
|
SOUTH = Dir{0, 1}
|
|
WEST = Dir{-1, 0}
|
|
DIRECTIONS = []Dir{NORTH, EAST, SOUTH, WEST}
|
|
)
|
|
|
|
func Part1(input string) int {
|
|
maze := Maze{}.Parse(input)
|
|
fmt.Println(maze)
|
|
data := inputGraph(*maze)
|
|
graph := dijkstra.CreateGraph(data)
|
|
data.From.Label = "EAST"
|
|
path, dist := dijkstra.GetShortestPath(data.From, data.To, graph)
|
|
for _, p := range path {
|
|
maze.grid.Set(p.X, p.Y, PATH)
|
|
}
|
|
fmt.Println(maze)
|
|
return dist
|
|
}
|
|
func Part2(input string) int {
|
|
maze := Maze{}.Parse(input)
|
|
fmt.Println(maze)
|
|
data := inputGraph(*maze)
|
|
graph := dijkstra.CreateGraph(data)
|
|
data.From.Label = "START"
|
|
data.To.Label = "NORTH"
|
|
paths, _ := dijkstra.GetAllShortestPaths(data.From, data.To, graph)
|
|
bestPaths := make(map[Vec]struct{})
|
|
for _, path := range paths {
|
|
for _, node := range path {
|
|
bestPaths[Vec{x: node.X, y: node.Y}] = struct{}{}
|
|
}
|
|
}
|
|
return len(bestPaths)
|
|
}
|
|
|
|
func inputGraph(maze Maze) dijkstra.InputGraph {
|
|
data := dijkstra.InputGraph{}
|
|
data.From = dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "START"}
|
|
data.To = dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: "END"}
|
|
for y := 0; y < maze.height; y++ {
|
|
for x := 0; x < maze.width; x++ {
|
|
if maze.grid.Get(x, y) == WALL {
|
|
continue
|
|
}
|
|
for _, dir := range DIRECTIONS {
|
|
nx, ny := x+dir.x, y+dir.y
|
|
if nx < 0 || ny < 0 || nx >= maze.width || ny >= maze.height {
|
|
continue
|
|
}
|
|
if maze.grid.Get(nx, ny) == WALL {
|
|
continue
|
|
}
|
|
for _, d := range getAllowedDirections(dir) {
|
|
ss := dirToString(dir)
|
|
ds := dirToString(d)
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: x, Y: y, Label: ss},
|
|
Destination: dijkstra.Point{X: nx, Y: ny, Label: ds},
|
|
Weight: func() int {
|
|
if dir == d {
|
|
return 1
|
|
} else {
|
|
return 1001
|
|
}
|
|
}(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: "END"},
|
|
Destination: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: dirToString(EAST)},
|
|
Weight: 0,
|
|
})
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: "END"},
|
|
Destination: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: dirToString(WEST)},
|
|
Weight: 0,
|
|
})
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: "END"},
|
|
Destination: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: dirToString(NORTH)},
|
|
Weight: 0,
|
|
})
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: "END"},
|
|
Destination: dijkstra.Point{X: maze.end.x, Y: maze.end.y, Label: dirToString(SOUTH)},
|
|
Weight: 0,
|
|
})
|
|
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "START"},
|
|
Destination: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: dirToString(EAST)},
|
|
Weight: 0,
|
|
})
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "START"},
|
|
Destination: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: dirToString(WEST)},
|
|
Weight: 2000,
|
|
})
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "START"},
|
|
Destination: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: dirToString(NORTH)},
|
|
Weight: 1000,
|
|
})
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "START"},
|
|
Destination: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: dirToString(SOUTH)},
|
|
Weight: 1000,
|
|
})
|
|
|
|
data.Graph = append(data.Graph, dijkstra.InputData{
|
|
Source: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "EAST"},
|
|
Destination: dijkstra.Point{X: maze.start.x, Y: maze.start.y, Label: "NORTH"},
|
|
Weight: 1000,
|
|
})
|
|
|
|
return data
|
|
}
|
|
|
|
func dirToString(dir Dir) string {
|
|
switch dir {
|
|
case NORTH:
|
|
return "NORTH"
|
|
case EAST:
|
|
return "EAST"
|
|
case SOUTH:
|
|
return "SOUTH"
|
|
case WEST:
|
|
return "WEST"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
func getAllowedDirections(direction Dir) []Dir {
|
|
switch direction {
|
|
case NORTH:
|
|
return []Dir{NORTH, EAST, WEST}
|
|
case SOUTH:
|
|
return []Dir{SOUTH, EAST, WEST}
|
|
case WEST:
|
|
return []Dir{WEST, NORTH, SOUTH}
|
|
case EAST:
|
|
return []Dir{EAST, NORTH, SOUTH}
|
|
}
|
|
return []Dir{}
|
|
}
|
|
|
|
func (m Maze) Parse(input string) *Maze {
|
|
m.grid = inputs.ToGrid2D(input, "\n", "", '?', func(c string) Cell { return Cell(c[0]) })
|
|
m.height, m.width = m.grid.SizeY(), m.grid.SizeX()
|
|
for y := 0; y < m.height; y++ {
|
|
for x := 0; x < m.width; x++ {
|
|
c := m.grid.Get(x, y)
|
|
switch c {
|
|
case WALL, CORRIDOR:
|
|
m.grid.Set(x, y, c)
|
|
case START:
|
|
m.grid.Set(x, y, CORRIDOR)
|
|
m.start = Vec{x, y}
|
|
case END:
|
|
m.grid.Set(x, y, CORRIDOR)
|
|
m.end = Vec{x, y}
|
|
}
|
|
}
|
|
}
|
|
return &m
|
|
}
|
|
|
|
func (m *Maze) String() string {
|
|
s := ""
|
|
for y := 0; y < m.height; y++ {
|
|
for x := 0; x < m.width; x++ {
|
|
if y == m.start.y && x == m.start.x {
|
|
s += string(START)
|
|
} else if y == m.end.y && x == m.end.x {
|
|
s += string(END)
|
|
} else {
|
|
s += string(m.grid.Get(x, y))
|
|
}
|
|
}
|
|
s += "\n"
|
|
}
|
|
return s
|
|
}
|