github.com/hzck/speedroute@v0.0.0-20201115191102-403b7d0e443f/algorithm/algorithm.go (about)

     1  // Package algorithm calculates the shortest path in a directed, weighted graph with a set of requirements.
     2  package algorithm
     3  
     4  import (
     5  	"container/heap"
     6  
     7  	m "github.com/hzck/speedroute/model"
     8  )
     9  
    10  // Route takes a created graph object and finds the shortest path from start to end, returning
    11  // that path as a list of edges.
    12  func Route(graph *m.Graph) []*m.Edge {
    13  	if graph.StartNode() == nil || graph.EndNode() == nil {
    14  		return nil
    15  	}
    16  	addMinPathLeft(graph)
    17  	startPath := m.CreatePath()
    18  	startPath.AddRewards(graph.StartNode().Rewards())
    19  	pq := &m.PrioQueue{}
    20  	heap.Init(pq)
    21  	addNodeEdgesToPrioQueue(pq, graph.StartNode(), startPath)
    22  	for path := prioPath(pq); path != nil; path = prioPath(pq) {
    23  		node := path.Edges()[len(path.Edges())-1].To()
    24  		if node == graph.EndNode() {
    25  			return path.Edges()
    26  		}
    27  		addNodeEdgesToPrioQueue(pq, node, path)
    28  	}
    29  	return nil
    30  }
    31  
    32  func addNodeEdgesToPrioQueue(pq *m.PrioQueue, node *m.Node, path *m.Path) {
    33  	for _, edge := range node.FromEdges() {
    34  		ok, i := path.PossibleRoute(edge)
    35  		if ok {
    36  			newPath := path.Copy()
    37  			newPath.AddEdge(edge, i)
    38  			heap.Push(pq, newPath)
    39  		}
    40  	}
    41  }
    42  
    43  func prioPath(pq *m.PrioQueue) *m.Path {
    44  	if pq.Len() > 0 {
    45  		return heap.Pop(pq).(*m.Path)
    46  	}
    47  	return nil
    48  }
    49  
    50  func addMinPathLeft(graph *m.Graph) {
    51  	dp := &m.DijkstraPrio{}
    52  	heap.Init(dp)
    53  	visited := make(map[*m.Node]bool)
    54  	endNode := graph.EndNode()
    55  	endNode.SetMinPathLeft(0)
    56  	visited[endNode] = true
    57  	for _, edge := range endNode.ToEdges() {
    58  		node := edge.From()
    59  		node.SetMinPathLeft(edge.FastestTime())
    60  		heap.Push(dp, node)
    61  	}
    62  	if dp.Len() > 0 {
    63  		for node := heap.Pop(dp).(*m.Node); dp.Len() > 0; node = heap.Pop(dp).(*m.Node) {
    64  			visited[node] = true
    65  			for _, edge := range node.ToEdges() {
    66  				innerNode := edge.From()
    67  				if !visited[innerNode] {
    68  					innerNode.SetMinPathLeft(edge.FastestTime() + node.MinPathLeft())
    69  					heap.Push(dp, innerNode)
    70  				}
    71  			}
    72  		}
    73  	}
    74  }