mirror of https://github.com/golang/go.git
cmd/go2go: add pointer variant to graph package
Change-Id: Ib5cf28be42943ea816c64740f5698a5716d3dec1
This commit is contained in:
parent
07382406ea
commit
fad64b3083
|
|
@ -27,6 +27,9 @@ func New(type Node, Edge G)(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(type Node, Edge G) struct {
|
||||
node Node
|
||||
path []Edge
|
||||
|
|
@ -63,3 +66,61 @@ func (g *Graph(Node, Edge)) ShortestPath(from, to Node) ([]Edge, error) {
|
|||
}
|
||||
return nil, errors.New("no path")
|
||||
}
|
||||
|
||||
// GraphP is a version of Grgaph that uses pointers. This is for testing.
|
||||
// I'm not sure which approach will be better in practice, or whether
|
||||
// this indicates a problem with the draft design.
|
||||
type GraphP(type Node, Edge GP) struct {
|
||||
nodes []*Node
|
||||
}
|
||||
|
||||
// GP is the contract that the GraphP Node and Edge types must implement.
|
||||
contract GP(Node, Edge) {
|
||||
Node Edges() []*Edge
|
||||
Edge Nodes() (a, b *Node)
|
||||
}
|
||||
|
||||
// NewP creates a new GraphP from a collection of Nodes.
|
||||
func NewP(type Node, Edge GP)(nodes []*Node) *GraphP(Node, Edge) {
|
||||
return &GraphP(Node, Edge){nodes: nodes}
|
||||
}
|
||||
|
||||
// nodePathP 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 nodePathP(type Node, Edge GP) 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 *GraphP(Node, Edge)) ShortestPath(from, to *Node) ([]*Edge, error) {
|
||||
visited := make(map[*Node]bool)
|
||||
visited[from] = true
|
||||
workqueue := [](nodePathP(Node, Edge)){nodePathP(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, nodePathP(Node, Edge){a, ve})
|
||||
visited[a] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errors.New("no path")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,17 +27,17 @@ const (
|
|||
)
|
||||
|
||||
func (dir direction) String() string {
|
||||
strs := map[direction]string {
|
||||
strs := map[direction]string{
|
||||
north: "north",
|
||||
ne: "ne",
|
||||
east: "east",
|
||||
se: "se",
|
||||
ne: "ne",
|
||||
east: "east",
|
||||
se: "se",
|
||||
south: "south",
|
||||
sw: "sw",
|
||||
west: "west",
|
||||
nw: "nw",
|
||||
up: "up",
|
||||
down: "down",
|
||||
sw: "sw",
|
||||
west: "west",
|
||||
nw: "nw",
|
||||
up: "up",
|
||||
down: "down",
|
||||
}
|
||||
if str, ok := strs[dir]; ok {
|
||||
return str
|
||||
|
|
@ -52,7 +52,7 @@ type mazeRoom struct {
|
|||
|
||||
type mazeEdge struct {
|
||||
from, to int
|
||||
dir direction
|
||||
dir direction
|
||||
}
|
||||
|
||||
// Edges returns the exits from the room.
|
||||
|
|
@ -62,8 +62,8 @@ func (m mazeRoom) Edges() []mazeEdge {
|
|||
if exit != 0 {
|
||||
r = append(r, mazeEdge{
|
||||
from: m.index,
|
||||
to: exit,
|
||||
dir: direction(i),
|
||||
to: exit,
|
||||
dir: direction(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -86,26 +86,26 @@ func (e mazeEdge) Nodes() (mazeRoom, mazeRoom) {
|
|||
// 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
|
||||
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(t *testing.T) {
|
||||
|
|
@ -141,3 +141,71 @@ func TestShortestPath(t *testing.T) {
|
|||
t.Errorf("ShortestPath returned %v, want %v", steps, want)
|
||||
}
|
||||
}
|
||||
|
||||
// A version of the above using pointer types.
|
||||
|
||||
var zorkP = make(map[int]*mazeRoomP)
|
||||
|
||||
type mazeRoomP struct {
|
||||
index int
|
||||
exits [10]int
|
||||
}
|
||||
|
||||
type mazeEdgeP struct {
|
||||
from, to int
|
||||
dir direction
|
||||
}
|
||||
|
||||
func (m *mazeRoomP) Edges() []*mazeEdgeP {
|
||||
var r []*mazeEdgeP
|
||||
for i, exit := range m.exits {
|
||||
if exit != 0 {
|
||||
r = append(r, &mazeEdgeP{
|
||||
from: m.index,
|
||||
to: exit,
|
||||
dir: direction(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (e *mazeEdgeP) Nodes() (*mazeRoomP, *mazeRoomP) {
|
||||
m1, ok := zorkP[e.from]
|
||||
if !ok {
|
||||
panic("bad edge")
|
||||
}
|
||||
m2, ok := zorkP[e.to]
|
||||
if !ok {
|
||||
panic("bad edge")
|
||||
}
|
||||
return m1, m2
|
||||
}
|
||||
|
||||
func TestShortestPathP(t *testing.T) {
|
||||
var nodes []*mazeRoomP
|
||||
var start, stop *mazeRoomP
|
||||
for k, v := range zork {
|
||||
mr := &mazeRoomP{index: k, exits: v.exits}
|
||||
zorkP[k] = mr
|
||||
nodes = append(nodes, mr)
|
||||
if k == 11 {
|
||||
start = mr
|
||||
} else if k == 30 {
|
||||
stop = mr
|
||||
}
|
||||
}
|
||||
g := NewP(mazeRoomP, mazeEdgeP)(nodes)
|
||||
path, err := g.ShortestPath(start, stop)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var steps []direction
|
||||
for _, edge := range path {
|
||||
steps = append(steps, edge.dir)
|
||||
}
|
||||
want := []direction{east, west, up, sw, east, south}
|
||||
if !slices.Equal(steps, want) {
|
||||
t.Errorf("ShortestPath returned %v, want %v", steps, want)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue