1
2
3
4
5
6
7 package main
8
9 import (
10 "errors"
11 "fmt"
12 )
13
14
15
16 func _SliceEqual[Elem comparable](s1, s2 []Elem) bool {
17 if len(s1) != len(s2) {
18 return false
19 }
20 for i, v1 := range s1 {
21 v2 := s2[i]
22 if v1 != v2 {
23 isNaN := func(f Elem) bool { return f != f }
24 if !isNaN(v1) || !isNaN(v2) {
25 return false
26 }
27 }
28 }
29 return true
30 }
31
32
33
34
35 type _Graph[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct {
36 nodes []_Node
37 }
38
39
40 type _NodeC[_Edge any] interface {
41 comparable
42 Edges() []_Edge
43 }
44
45
46 type _EdgeC[_Node any] interface {
47 comparable
48 Nodes() (a, b _Node)
49 }
50
51
52 func _New[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]](nodes []_Node) *_Graph[_Node, _Edge] {
53 return &_Graph[_Node, _Edge]{nodes: nodes}
54 }
55
56
57
58
59 type nodePath[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct {
60 node _Node
61 path []_Edge
62 }
63
64
65
66
67 func (g *_Graph[_Node, _Edge]) ShortestPath(from, to _Node) ([]_Edge, error) {
68 visited := make(map[_Node]bool)
69 visited[from] = true
70 workqueue := []nodePath[_Node, _Edge]{nodePath[_Node, _Edge]{from, nil}}
71 for len(workqueue) > 0 {
72 current := workqueue
73 workqueue = nil
74 for _, np := range current {
75 edges := np.node.Edges()
76 for _, edge := range edges {
77 a, b := edge.Nodes()
78 if a == np.node {
79 a = b
80 }
81 if !visited[a] {
82 ve := append([]_Edge(nil), np.path...)
83 ve = append(ve, edge)
84 if a == to {
85 return ve, nil
86 }
87 workqueue = append(workqueue, nodePath[_Node, _Edge]{a, ve})
88 visited[a] = true
89 }
90 }
91 }
92 }
93 return nil, errors.New("no path")
94 }
95
96 type direction int
97
98 const (
99 north direction = iota
100 ne
101 east
102 se
103 south
104 sw
105 west
106 nw
107 up
108 down
109 )
110
111 func (dir direction) String() string {
112 strs := map[direction]string{
113 north: "north",
114 ne: "ne",
115 east: "east",
116 se: "se",
117 south: "south",
118 sw: "sw",
119 west: "west",
120 nw: "nw",
121 up: "up",
122 down: "down",
123 }
124 if str, ok := strs[dir]; ok {
125 return str
126 }
127 return fmt.Sprintf("direction %d", dir)
128 }
129
130 type mazeRoom struct {
131 index int
132 exits [10]int
133 }
134
135 type mazeEdge struct {
136 from, to int
137 dir direction
138 }
139
140
141 func (m mazeRoom) Edges() []mazeEdge {
142 var r []mazeEdge
143 for i, exit := range m.exits {
144 if exit != 0 {
145 r = append(r, mazeEdge{
146 from: m.index,
147 to: exit,
148 dir: direction(i),
149 })
150 }
151 }
152 return r
153 }
154
155
156
157
158 func (e mazeEdge) Nodes() (mazeRoom, mazeRoom) {
159 m1, ok := zork[e.from]
160 if !ok {
161 panic("bad edge")
162 }
163 m2, ok := zork[e.to]
164 if !ok {
165 panic("bad edge")
166 }
167 return m1, m2
168 }
169
170
171
172 var zork = map[int]mazeRoom{
173 11: {exits: [10]int{north: 11, south: 12, east: 14}},
174 12: {exits: [10]int{south: 11, north: 14, east: 13}},
175 13: {exits: [10]int{west: 12, north: 14, up: 16}},
176 14: {exits: [10]int{west: 13, north: 11, east: 15}},
177 15: {exits: [10]int{south: 14}},
178 16: {exits: [10]int{east: 17, north: 13, sw: 18}},
179 17: {exits: [10]int{west: 16}},
180 18: {exits: [10]int{down: 16, east: 19, west: 18, up: 22}},
181 19: {exits: [10]int{up: 29, west: 18, ne: 15, east: 20, south: 30}},
182 20: {exits: [10]int{ne: 19, west: 20, se: 21}},
183 21: {exits: [10]int{north: 20}},
184 22: {exits: [10]int{north: 18, east: 24, down: 23, south: 28, west: 26, nw: 22}},
185 23: {exits: [10]int{east: 22, west: 28, up: 24}},
186 24: {exits: [10]int{ne: 25, down: 23, nw: 28, sw: 26}},
187 25: {exits: [10]int{sw: 24}},
188 26: {exits: [10]int{west: 16, sw: 24, east: 28, up: 22, north: 27}},
189 27: {exits: [10]int{south: 26}},
190 28: {exits: [10]int{east: 22, down: 26, south: 23, west: 24}},
191 29: {exits: [10]int{west: 30, nw: 29, ne: 19, south: 19}},
192 30: {exits: [10]int{west: 29, south: 19}},
193 }
194
195 func TestShortestPath() {
196
197
198
199
200
201
202 for k := range zork {
203 r := zork[k]
204 r.index = k
205 zork[k] = r
206 }
207
208 var nodes []mazeRoom
209 for idx, room := range zork {
210 mridx := room
211 mridx.index = idx
212 nodes = append(nodes, mridx)
213 }
214 g := _New[mazeRoom, mazeEdge](nodes)
215 path, err := g.ShortestPath(zork[11], zork[30])
216 if err != nil {
217 panic(fmt.Sprintf("%v", err))
218 }
219 var steps []direction
220 for _, edge := range path {
221 steps = append(steps, edge.dir)
222 }
223 want := []direction{east, west, up, sw, east, south}
224 if !_SliceEqual(steps, want) {
225 panic(fmt.Sprintf("ShortestPath returned %v, want %v", steps, want))
226 }
227 }
228
229 func main() {
230 TestShortestPath()
231 }
232
View as plain text