Commit a73c5230 authored by Zachary Seguin's avatar Zachary Seguin

Add initial path finding implementation

parent 9d0691ab
Pipeline #111 failed with stages
in 38 seconds
......@@ -15,11 +15,11 @@ segments:
cost: 0
reverse:
destination: 1
cost: 0
cost: 50
3:
type: straight
length: 1000
connections:
reverse:
destination: 2
cost: 0
\ No newline at end of file
cost: 50
\ No newline at end of file
......@@ -4,6 +4,7 @@ import (
"io/ioutil"
"log"
"git.zacharyseguin.ca/trainsim/trainsim/path"
"git.zacharyseguin.ca/trainsim/trainsim/track"
"gopkg.in/yaml.v2"
)
......@@ -26,4 +27,17 @@ func main() {
}
log.Println(track)
// Find a path between two segments
finder := path.Dijkstra{}
path, err := finder.FindPath(&track, "1", "3")
if err != nil {
log.Fatal(err)
}
log.Println("found path", path.Cost)
for _, seg := range path.Segments {
log.Println(seg.ID)
}
}
package path
import (
"math"
"git.zacharyseguin.ca/trainsim/trainsim/track"
)
type Dijkstra struct {
}
func (f *Dijkstra) FindPath(tr *track.Track, from string, to string) (*Path, error) {
dist := make(map[string]float64)
prev := make(map[string]string)
unvisited := make([]string, 0)
for id := range tr.Segments {
dist[id] = math.Inf(1)
prev[id] = ""
unvisited = append(unvisited, id)
}
dist[from] = 0
for len(unvisited) > 0 {
// Find the node with the shortest distance
var node string
sdist := math.Inf(1)
nindx := -1
for indx, nid := range unvisited {
if !math.IsInf(dist[nid], 1) && dist[nid] < sdist {
node = nid
sdist = dist[nid]
nindx = indx
}
}
if math.IsInf(sdist, 1) {
break
}
// Replace with last item, them remove last item
unvisited[nindx] = unvisited[len(unvisited)-1]
unvisited = unvisited[:len(unvisited)-1]
// Add each neighbour
for _, connection := range tr.Segments[node].Connections {
alt := dist[node] + float64(connection.Cost)
if alt < dist[connection.Destination] {
dist[connection.Destination] = alt
prev[connection.Destination] = node
}
}
}
if !math.IsInf(dist[to], 1) {
path := Path{
Cost: int(dist[to]),
Segments: make([]*track.Segment, 0),
}
node := to
for node != from {
path.Segments = append([]*track.Segment{tr.Segments[node]}, path.Segments...)
node = prev[node]
}
path.Segments = append([]*track.Segment{tr.Segments[from]}, path.Segments...)
return &path, nil
}
return nil, nil
}
package path
import (
"git.zacharyseguin.ca/trainsim/trainsim/track"
)
// Path describes a path between two segments
type Path struct {
Cost int
Segments []*track.Segment
}
// Finder describes a path finder.
type Finder interface {
FindPath(track *track.Track, from string, to string) (*Path, error)
}
......@@ -2,6 +2,7 @@ package track
// Segment of track.
type Segment struct {
ID string `yaml:"id" json:"id"`
Type SegmentType `yaml:"type" json:"type"`
// Length in metres.
......
......@@ -4,9 +4,25 @@ import (
"fmt"
)
type Segments map[string]*Segment
// Track details the track.
type Track struct {
Segments map[string]Segment `yaml:"segments" json:"segments"`
Segments Segments `yaml:"segments" json:"segments"`
}
func (segs *Segments) UnmarshalYAML(unmarshal func(interface{}) error) error {
aux := make(Segments, 0)
if err := unmarshal(aux); err != nil {
return err
}
for id := range aux {
aux[id].ID = id
}
*segs = aux
return nil
}
// Validate that the track is valid.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment