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 position [2]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: cost, position: start.position, children: 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 } return &MazeGraph{ cost: cost, children: children, position: start.position } } 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 (graph *MazeGraph) PathsToLeaves(leave_cost uint) [][2]uint { out := make([][2]uint, 0) if len(graph.children) == 0 { // leave found if graph.cost == leave_cost { return append(out, graph.position) } else { return nil } } // collect from children for _, g := range graph.children { subpath := g.PathsToLeaves(leave_cost) if subpath != nil { out = append(out, subpath...) } } // if all children returned nil if len(out) == 0 { return nil } return append(out, graph.position) } // ---------------------------------------- func check(err error) { if err != nil { panic(err) } } func uniq_count(slice [][2]uint) int { m := make(map[[2]uint]bool) for _, s := range slice { m[s] = true } return len(m) } 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) maze_graph := NewMazeGraph(maze, maze.Start(), 0) for _, leave := range maze_graph.Leaves() { if best == nil || best.cost > leave.cost { best = leave } } fmt.Println(best.cost) fmt.Println(uniq_count(maze_graph.PathsToLeaves(best.cost))) }