exercism/go/pov/pov.go
2017-08-24 12:10:01 -05:00

128 lines
2.5 KiB
Go

package pov
const testVersion = 2
// A Graph just holds all nodes in the graph
type Graph struct {
nodes []Node
}
func New() *Graph {
return new(Graph)
}
// A Node is a label and a slice of references to other nodes
type Node struct {
label string
connections []Node
}
func (g *Graph) AddNode(lbl string) {
if g.getNode(lbl) == nil {
g.nodes = append(g.nodes, Node{label: lbl})
}
}
func (g *Graph) getNode(lbl string) *Node {
for i := range g.nodes {
if g.nodes[i].label == lbl {
return &g.nodes[i]
}
}
return nil
}
func (g *Graph) AddArc(fr, to string) {
frN := g.getNode(fr)
if frN == nil {
g.AddNode(fr)
frN = g.getNode(fr)
}
toN := g.getNode(to)
if g.getNode(to) == nil {
g.AddNode(to)
toN = g.getNode(to)
}
frN.connections = append(frN.connections, *toN)
}
func (g *Graph) ArcList() []string {
var ret []string
for _, k := range g.nodes {
for _, l := range k.connections {
ret = append(ret, k.label+" -> "+l.label)
}
}
return ret
}
// getPath returns the shortest path from 'fr' to 'to'
func (g *Graph) getPath(fr, to string) []string {
if fr == to {
return []string{fr}
}
st := g.getNode(fr)
if st == nil {
// Couldn't find the starting node
return []string{}
}
var valPaths [][]string
for i := range st.connections {
if st.connections[i].label == to {
return []string{fr, to}
}
tst := g.getPath(st.connections[i].label, to)
if len(tst) > 0 {
tst = append([]string{fr}, tst...)
valPaths = append(valPaths, tst)
}
}
if len(valPaths) == 0 {
// No valid paths found
return []string{}
}
var ret []string
for i := range valPaths {
if len(valPaths[i]) < len(ret) || len(ret) == 0 {
ret = valPaths[i]
}
}
return ret
}
func (g *Graph) ChangeRoot(oldRoot, newRoot string) *Graph {
pth := g.getPath(oldRoot, newRoot)
if len(pth) == 0 {
// No valid path
return g
}
for oldRoot != newRoot {
// We just keep swapping the first two in pth until we're done
if len(pth) >= 2 {
n := g.getNode(pth[1])
if n == nil {
return g
}
p := g.getNode(pth[0])
if p == nil {
return g
}
p.removeConnection(n.label)
n.connections = append(n.connections, *p)
oldRoot = n.label
pth = g.getPath(oldRoot, newRoot)
}
}
return g
}
func (n *Node) removeConnection(to string) {
for i, k := range n.connections {
if k.label == to {
copy(n.connections[i:], n.connections[i+1:])
n.connections[len(n.connections)-1] = Node{}
n.connections = n.connections[:len(n.connections)-1]
}
}
}