add day18
This commit is contained in:
191
day18/maze.go
Normal file
191
day18/maze.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
type Direction uint8
|
||||
|
||||
const (
|
||||
Up Direction = iota
|
||||
Down Direction = iota
|
||||
Left Direction = iota
|
||||
Right Direction = iota
|
||||
)
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
type Position struct {
|
||||
coord [2]uint
|
||||
direc Direction
|
||||
}
|
||||
|
||||
func (p Position) StepUnbound(d Direction) Position {
|
||||
switch {
|
||||
case d == Down: p.coord[1]++
|
||||
case d == Up: p.coord[1]--
|
||||
case d == Left: p.coord[0]--
|
||||
case d == Right: p.coord[0]++
|
||||
}
|
||||
p.direc = d
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
type Maze struct {
|
||||
start [2]uint
|
||||
end [2]uint
|
||||
field [][]byte
|
||||
}
|
||||
|
||||
func NewMaze(byte_positions string, bytes_to_load uint, size [2]uint) *Maze {
|
||||
/* size = [2]unit{x,y} */
|
||||
|
||||
var maze Maze
|
||||
|
||||
// create board
|
||||
maze.field = make([][]byte, 0, size[1] + 2)
|
||||
for i := range size[1] + 2 {
|
||||
line := make([]byte, size[0] + 2)
|
||||
for j := range line {
|
||||
if i == 0 || uint(i) == size[1] + 1 || j == 0 || uint(j) == size[0] + 1 {
|
||||
line[j] = '#'
|
||||
} else {
|
||||
line[j] = '.'
|
||||
}
|
||||
}
|
||||
maze.field = append(maze.field, line)
|
||||
}
|
||||
|
||||
// add byte corruptions
|
||||
lines := strings.Split(byte_positions, "\n")
|
||||
for i, line := range lines {
|
||||
if uint(i) >= bytes_to_load { break }
|
||||
x, err := strconv.Atoi(strings.Split(line, ",")[0])
|
||||
check(err)
|
||||
y, err := strconv.Atoi(strings.Split(line, ",")[1])
|
||||
check(err)
|
||||
|
||||
maze.field[y + 1][x + 1] = '#'
|
||||
}
|
||||
|
||||
// set start/ end
|
||||
maze.start = [2]uint{1,1}
|
||||
maze.end = size
|
||||
|
||||
return &maze
|
||||
}
|
||||
|
||||
func (m *Maze) Step(pos Position) []Position {
|
||||
out := make([]Position, 0, 4)
|
||||
|
||||
if m.field[pos.coord[1] + 1][pos.coord[0]] == '.' && pos.direc != Up {
|
||||
out = append(out, pos.StepUnbound(Down))
|
||||
}
|
||||
if m.field[pos.coord[1] - 1][pos.coord[0]] == '.' && pos.direc != Down {
|
||||
out = append(out, pos.StepUnbound(Up))
|
||||
}
|
||||
if m.field[pos.coord[1]][pos.coord[0] + 1] == '.' && pos.direc != Left {
|
||||
out = append(out, pos.StepUnbound(Right))
|
||||
}
|
||||
if m.field[pos.coord[1]][pos.coord[0] - 1] == '.' && pos.direc != Right {
|
||||
out = append(out, pos.StepUnbound(Left))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *Maze) Start() Position {
|
||||
return Position{m.start, Right}
|
||||
}
|
||||
|
||||
func (m *Maze) Finished(pos Position) bool {
|
||||
return m.end == pos.coord
|
||||
}
|
||||
|
||||
func (m *Maze) Print() {
|
||||
for _, line := range m.field {
|
||||
fmt.Println(string(line))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
type MazeGraph struct {
|
||||
cost uint
|
||||
children []*MazeGraph
|
||||
}
|
||||
|
||||
var new_maze_graph_visited map[Position]uint
|
||||
|
||||
func NewMazeGraph(m *Maze, start Position, cost uint) *MazeGraph {
|
||||
children := make([]*MazeGraph, 0, 3)
|
||||
|
||||
// finish reached?
|
||||
if m.Finished(start) { return &MazeGraph{ cost, children } }
|
||||
|
||||
// check for loop
|
||||
value, exists := new_maze_graph_visited[start]
|
||||
if exists {
|
||||
if value <= cost { return nil }
|
||||
}
|
||||
new_maze_graph_visited[start] = cost
|
||||
|
||||
// build subgraphs/ children
|
||||
for _, position := range m.Step(start) {
|
||||
child := NewMazeGraph(m, position, cost + 1)
|
||||
if child != nil { children = append(children, child) }
|
||||
}
|
||||
|
||||
// found dead end
|
||||
if len(children) == 0 { return nil }
|
||||
|
||||
// collapse if there is only one possible way
|
||||
if len(children) == 0 { return children[0] }
|
||||
|
||||
return &MazeGraph{ cost: cost, children: children }
|
||||
}
|
||||
|
||||
func (graph *MazeGraph) Leaves() []*MazeGraph {
|
||||
out := make([]*MazeGraph, 0)
|
||||
|
||||
if len(graph.children) == 0 {
|
||||
out = append(out, graph)
|
||||
return out
|
||||
}
|
||||
|
||||
for _, g := range graph.children {
|
||||
out = append(out, g.Leaves()...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
func check(err error) { if err != nil { panic(err) } }
|
||||
|
||||
|
||||
func main() {
|
||||
dat, err := os.ReadFile("data.txt")
|
||||
check(err)
|
||||
|
||||
input := strings.TrimSpace(string(dat))
|
||||
maze := NewMaze(input, 1024, [2]uint{71,71})
|
||||
//maze.Print()
|
||||
|
||||
var best *MazeGraph
|
||||
new_maze_graph_visited = make(map[Position]uint)
|
||||
|
||||
for _, leave := range NewMazeGraph(maze, maze.Start(), 0).Leaves() {
|
||||
if best == nil || best.cost > leave.cost { best = leave }
|
||||
}
|
||||
fmt.Println(best.cost)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user