2022-02-28 22:32:19 +00:00
|
|
|
// run
|
2021-03-15 17:27:06 +00:00
|
|
|
|
|
|
|
// 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"
|
|
|
|
)
|
|
|
|
|
|
|
|
// _Equal 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 contraints 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()
|
|
|
|
}
|