|  | // run | 
|  |  | 
|  | // Copyright 2021 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "fmt" | 
|  | ) | 
|  |  | 
|  | // _SliceEqual reports whether two slices are equal: the same length and all | 
|  | // elements equal. All floating point NaNs are considered equal. | 
|  | func _SliceEqual[Elem comparable](s1, s2 []Elem) bool { | 
|  | if len(s1) != len(s2) { | 
|  | return false | 
|  | } | 
|  | for i, v1 := range s1 { | 
|  | v2 := s2[i] | 
|  | if v1 != v2 { | 
|  | isNaN := func(f Elem) bool { return f != f } | 
|  | if !isNaN(v1) || !isNaN(v2) { | 
|  | return false | 
|  | } | 
|  | } | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | // A Graph is a collection of nodes. A node may have an arbitrary number | 
|  | // of edges. An edge connects two nodes. Both nodes and edges must be | 
|  | // comparable. This is an undirected simple graph. | 
|  | type _Graph[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct { | 
|  | nodes []_Node | 
|  | } | 
|  |  | 
|  | // _NodeC is the constraints on a node in a graph, given the _Edge type. | 
|  | type _NodeC[_Edge any] interface { | 
|  | comparable | 
|  | Edges() []_Edge | 
|  | } | 
|  |  | 
|  | // _EdgeC is the constraints on an edge in a graph, given the _Node type. | 
|  | type _EdgeC[_Node any] interface { | 
|  | comparable | 
|  | Nodes() (a, b _Node) | 
|  | } | 
|  |  | 
|  | // _New creates a new _Graph from a collection of Nodes. | 
|  | func _New[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]](nodes []_Node) *_Graph[_Node, _Edge] { | 
|  | return &_Graph[_Node, _Edge]{nodes: nodes} | 
|  | } | 
|  |  | 
|  | // nodePath holds the path to a node during ShortestPath. | 
|  | // This should ideally be a type defined inside ShortestPath, | 
|  | // but the translator tool doesn't support that. | 
|  | type nodePath[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct { | 
|  | node _Node | 
|  | path []_Edge | 
|  | } | 
|  |  | 
|  | // ShortestPath returns the shortest path between two nodes, | 
|  | // as an ordered list of edges. If there are multiple shortest paths, | 
|  | // which one is returned is unpredictable. | 
|  | func (g *_Graph[_Node, _Edge]) ShortestPath(from, to _Node) ([]_Edge, error) { | 
|  | visited := make(map[_Node]bool) | 
|  | visited[from] = true | 
|  | workqueue := []nodePath[_Node, _Edge]{nodePath[_Node, _Edge]{from, nil}} | 
|  | for len(workqueue) > 0 { | 
|  | current := workqueue | 
|  | workqueue = nil | 
|  | for _, np := range current { | 
|  | edges := np.node.Edges() | 
|  | for _, edge := range edges { | 
|  | a, b := edge.Nodes() | 
|  | if a == np.node { | 
|  | a = b | 
|  | } | 
|  | if !visited[a] { | 
|  | ve := append([]_Edge(nil), np.path...) | 
|  | ve = append(ve, edge) | 
|  | if a == to { | 
|  | return ve, nil | 
|  | } | 
|  | workqueue = append(workqueue, nodePath[_Node, _Edge]{a, ve}) | 
|  | visited[a] = true | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return nil, errors.New("no path") | 
|  | } | 
|  |  | 
|  | type direction int | 
|  |  | 
|  | const ( | 
|  | north direction = iota | 
|  | ne | 
|  | east | 
|  | se | 
|  | south | 
|  | sw | 
|  | west | 
|  | nw | 
|  | up | 
|  | down | 
|  | ) | 
|  |  | 
|  | func (dir direction) String() string { | 
|  | strs := map[direction]string{ | 
|  | north: "north", | 
|  | ne:    "ne", | 
|  | east:  "east", | 
|  | se:    "se", | 
|  | south: "south", | 
|  | sw:    "sw", | 
|  | west:  "west", | 
|  | nw:    "nw", | 
|  | up:    "up", | 
|  | down:  "down", | 
|  | } | 
|  | if str, ok := strs[dir]; ok { | 
|  | return str | 
|  | } | 
|  | return fmt.Sprintf("direction %d", dir) | 
|  | } | 
|  |  | 
|  | type mazeRoom struct { | 
|  | index int | 
|  | exits [10]int | 
|  | } | 
|  |  | 
|  | type mazeEdge struct { | 
|  | from, to int | 
|  | dir      direction | 
|  | } | 
|  |  | 
|  | // Edges returns the exits from the room. | 
|  | func (m mazeRoom) Edges() []mazeEdge { | 
|  | var r []mazeEdge | 
|  | for i, exit := range m.exits { | 
|  | if exit != 0 { | 
|  | r = append(r, mazeEdge{ | 
|  | from: m.index, | 
|  | to:   exit, | 
|  | dir:  direction(i), | 
|  | }) | 
|  | } | 
|  | } | 
|  | return r | 
|  | } | 
|  |  | 
|  | // Nodes returns the rooms connected by an edge. | 
|  | // | 
|  | //go:noinline | 
|  | func (e mazeEdge) Nodes() (mazeRoom, mazeRoom) { | 
|  | m1, ok := zork[e.from] | 
|  | if !ok { | 
|  | panic("bad edge") | 
|  | } | 
|  | m2, ok := zork[e.to] | 
|  | if !ok { | 
|  | panic("bad edge") | 
|  | } | 
|  | return m1, m2 | 
|  | } | 
|  |  | 
|  | // The first maze in Zork. Room indexes based on original Fortran data file. | 
|  | // You are in a maze of twisty little passages, all alike. | 
|  | var zork = map[int]mazeRoom{ | 
|  | 11: {exits: [10]int{north: 11, south: 12, east: 14}}, // west to Troll Room | 
|  | 12: {exits: [10]int{south: 11, north: 14, east: 13}}, | 
|  | 13: {exits: [10]int{west: 12, north: 14, up: 16}}, | 
|  | 14: {exits: [10]int{west: 13, north: 11, east: 15}}, | 
|  | 15: {exits: [10]int{south: 14}},                   // Dead End | 
|  | 16: {exits: [10]int{east: 17, north: 13, sw: 18}}, // skeleton, etc. | 
|  | 17: {exits: [10]int{west: 16}},                    // Dead End | 
|  | 18: {exits: [10]int{down: 16, east: 19, west: 18, up: 22}}, | 
|  | 19: {exits: [10]int{up: 29, west: 18, ne: 15, east: 20, south: 30}}, | 
|  | 20: {exits: [10]int{ne: 19, west: 20, se: 21}}, | 
|  | 21: {exits: [10]int{north: 20}}, // Dead End | 
|  | 22: {exits: [10]int{north: 18, east: 24, down: 23, south: 28, west: 26, nw: 22}}, | 
|  | 23: {exits: [10]int{east: 22, west: 28, up: 24}}, | 
|  | 24: {exits: [10]int{ne: 25, down: 23, nw: 28, sw: 26}}, | 
|  | 25: {exits: [10]int{sw: 24}}, // Grating room (up to Clearing) | 
|  | 26: {exits: [10]int{west: 16, sw: 24, east: 28, up: 22, north: 27}}, | 
|  | 27: {exits: [10]int{south: 26}}, // Dead End | 
|  | 28: {exits: [10]int{east: 22, down: 26, south: 23, west: 24}}, | 
|  | 29: {exits: [10]int{west: 30, nw: 29, ne: 19, south: 19}}, | 
|  | 30: {exits: [10]int{west: 29, south: 19}}, // ne to Cyclops Room | 
|  | } | 
|  |  | 
|  | func TestShortestPath() { | 
|  | // The Zork maze is not a proper undirected simple graph, | 
|  | // as there are some one way paths (e.g., 19 -> 15), | 
|  | // but for this test that doesn't matter. | 
|  |  | 
|  | // Set the index field in the map. Simpler than doing it in the | 
|  | // composite literal. | 
|  | for k := range zork { | 
|  | r := zork[k] | 
|  | r.index = k | 
|  | zork[k] = r | 
|  | } | 
|  |  | 
|  | var nodes []mazeRoom | 
|  | for idx, room := range zork { | 
|  | mridx := room | 
|  | mridx.index = idx | 
|  | nodes = append(nodes, mridx) | 
|  | } | 
|  | g := _New[mazeRoom, mazeEdge](nodes) | 
|  | path, err := g.ShortestPath(zork[11], zork[30]) | 
|  | if err != nil { | 
|  | panic(fmt.Sprintf("%v", err)) | 
|  | } | 
|  | var steps []direction | 
|  | for _, edge := range path { | 
|  | steps = append(steps, edge.dir) | 
|  | } | 
|  | want := []direction{east, west, up, sw, east, south} | 
|  | if !_SliceEqual(steps, want) { | 
|  | panic(fmt.Sprintf("ShortestPath returned %v, want %v", steps, want)) | 
|  | } | 
|  | } | 
|  |  | 
|  | func main() { | 
|  | TestShortestPath() | 
|  | } |