package main import ( "os"; "fmt"; "strings") // ---------------------------------------- type Direction uint8 const ( Up Direction = iota Down Direction = iota Left Direction = iota Right Direction = iota ) // ---------------------------------------- type Path struct { position [2]uint direction Direction } func (p Path) StepUnbound(d Direction) Path { switch { case d == Down: p.position[1]++ case d == Up: p.position[1]-- case d == Left: p.position[0]-- case d == Right: p.position[0]++ } p.direction = d return p } // ---------------------------------------- type Maze struct { start [2]uint end [2]uint field [][]byte } func NewMaze(str string) *Maze { var maze Maze for y, line := range strings.Split(str, "\n") { line_arr := make([]byte, len(line)) for x, char := range []byte(line) { switch { case char == 'S': line_arr[x] = '.' maze.start = [...]uint{uint(x), uint(y)} case char == 'E': line_arr[x] = '.' maze.end = [...]uint{uint(x), uint(y)} default: line_arr[x] = char } } maze.field = append(maze.field, line_arr) } return &maze } func (m *Maze) Step(path Path) []Path { out := make([]Path, 0, 4) if m.field[path.position[1] + 1][path.position[0]] == '.' && path.direction != Up { out = append(out, path.StepUnbound(Down)) } if m.field[path.position[1] - 1][path.position[0]] == '.' && path.direction != Down { out = append(out, path.StepUnbound(Up)) } if m.field[path.position[1]][path.position[0] + 1] == '.' && path.direction != Left { out = append(out, path.StepUnbound(Right)) } if m.field[path.position[1]][path.position[0] - 1] == '.' && path.direction != Right { out = append(out, path.StepUnbound(Left)) } return out } func (m *Maze) Start() Path { return Path{m.start, Right} } func (m *Maze) Finished(path Path) bool { return m.end == path.position } 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[Path]uint func NewMazeGraph(m *Maze, start Path, 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 _, path := range m.Step(start) { var child *MazeGraph if start.direction != path.direction { child = NewMazeGraph(m, path, cost + 1001) } else { child = NewMazeGraph(m, path, 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) var best *MazeGraph new_maze_graph_visited = make(map[Path]uint) for _, leave := range NewMazeGraph(maze, maze.Start(), 0).Leaves() { if best == nil || best.cost > leave.cost { best = leave } } fmt.Println(best.cost) }