github.com/hzck/speedroute@v0.0.0-20201115191102-403b7d0e443f/model/path.go (about) 1 package model 2 3 // Path holds information about a possible path, including edges, current length and current rewards. 4 type Path struct { 5 edges []*Edge 6 length int 7 rewards map[*Reward]int 8 } 9 10 // Edges returns a list of path edges. 11 func (path *Path) Edges() []*Edge { 12 return path.edges 13 } 14 15 // AddEdge adds an edge with length and its to node's rewards to the path. 16 func (path *Path) AddEdge(edge *Edge, i int) { 17 path.edges = append(path.edges, edge) 18 path.length += edge.Weights()[i].Time() 19 path.AddRewards(edge.To().Rewards()) 20 } 21 22 // Length returns the path length. 23 func (path *Path) Length() int { 24 return path.length 25 } 26 27 // Rewards returns the path rewards. 28 func (path *Path) Rewards() map[*Reward]int { 29 return path.rewards 30 } 31 32 // AddRewards adds a map of rewards to the path. 33 func (path *Path) AddRewards(rewards map[*Reward]int) { 34 for key, value := range rewards { 35 path.rewards[key] += value 36 } 37 } 38 39 // CreatePath is the constructor for path pointer. 40 func CreatePath() *Path { 41 path := new(Path) 42 path.rewards = make(map[*Reward]int) 43 return path 44 } 45 46 // IsLongerThan compares two paths to see which has best potential to be the shortest path. 47 func (path *Path) IsLongerThan(other *Path) bool { 48 pLength := path.Length() + path.Edges()[len(path.Edges())-1].To().MinPathLeft() 49 oLength := other.Length() + other.Edges()[len(other.Edges())-1].To().MinPathLeft() 50 if pLength != oLength { 51 return pLength > oLength 52 } 53 return len(path.Edges()) > len(other.Edges()) 54 } 55 56 // Copy copies a path values into another path. 57 func (path *Path) Copy() *Path { 58 pathCopy := CreatePath() 59 pathCopy.edges = make([]*Edge, len(path.Edges())) 60 copy(pathCopy.edges, path.Edges()) 61 pathCopy.length = path.Length() 62 for k, v := range path.Rewards() { 63 pathCopy.rewards[k] = v 64 } 65 return pathCopy 66 } 67 68 // PossibleRoute checks if an edge makes an eligible route to take for the current path. 69 func (path *Path) PossibleRoute(edge *Edge) (isPossible bool, index int) { 70 if path.rewardsAreNotNegativeOrUnique(edge.To()) { 71 return false, -1 72 } 73 74 nodeVisited := path.hasBeenVisited(edge) 75 76 if !edge.To().Revisitable() && nodeVisited { 77 return false, -1 78 } 79 80 rewardsChangedSinceLastVisit := path.rewardsChangedSinceLastVisit(edge) 81 82 if edge.To().Revisitable() && nodeVisited && !rewardsChangedSinceLastVisit { 83 return false, -1 84 } 85 86 return path.requirementsMet(edge) 87 } 88 89 func (path *Path) hasBeenVisited(edge *Edge) bool { 90 node := edge.To() 91 for i := 0; i < len(path.edges); i++ { 92 if path.edges[i].From() == node { 93 return true 94 } 95 } 96 return edge.From() == node 97 } 98 99 func (path *Path) rewardsChangedSinceLastVisit(edge *Edge) bool { 100 rewards := make(map[*Reward]int) 101 node := edge.To() 102 103 for k, v := range edge.From().Rewards() { 104 rewards[k] += v 105 } 106 107 if edge.From() == edge.To() { 108 return checkRewardsNotEmpty(rewards) 109 } 110 111 for i := len(path.edges) - 1; i >= 0; i-- { 112 for k, v := range path.edges[i].From().Rewards() { 113 rewards[k] += v 114 } 115 if path.edges[i].From() == node { 116 break 117 } 118 } 119 120 return checkRewardsNotEmpty(rewards) 121 } 122 123 func checkRewardsNotEmpty(rewards map[*Reward]int) bool { 124 for _, v := range rewards { 125 if v != 0 { 126 return true 127 } 128 } 129 130 return false 131 } 132 133 func (path *Path) rewardsAreNotNegativeOrUnique(node *Node) bool { 134 for reward, quantity := range node.Rewards() { 135 rewardCount := path.Rewards()[reward] 136 if rewardCount+quantity < 0 || (reward.Unique() && rewardCount > 0) { 137 return true 138 } 139 } 140 return false 141 } 142 143 func (path *Path) requirementsMet(edge *Edge) (result bool, index int) { 144 for i, weight := range edge.Weights() { 145 if path.weightRequirementsMet(weight) { 146 return true, i 147 } 148 } 149 return false, -1 150 } 151 152 func (path *Path) weightRequirementsMet(weight *Weight) bool { 153 for reward, quantity := range weight.Requirements() { 154 if path.countRewardsIncludingIsA(reward) < quantity { 155 return false 156 } 157 } 158 return true 159 } 160 161 func (path *Path) countRewardsIncludingIsA(reward *Reward) int { 162 count := path.Rewards()[reward] 163 for _, r := range reward.CanBe() { 164 count += path.countRewardsIncludingIsA(r) 165 } 166 return count 167 } 168 169 // PrioQueue is a list of Path pointers and implements heap interface. 170 type PrioQueue []*Path 171 172 // Len returns the length of the PriorityQueue. 173 func (pq PrioQueue) Len() int { 174 return len(pq) 175 } 176 177 // Less checks if one path is longer than another path in the PriorityQueue. 178 func (pq PrioQueue) Less(i, j int) bool { 179 return pq[j].IsLongerThan(pq[i]) 180 } 181 182 // Swap switches places of two paths in the PriorityQueue. 183 func (pq PrioQueue) Swap(i, j int) { 184 pq[i], pq[j] = pq[j], pq[i] 185 } 186 187 // Push adds a Path to the correct place in the PriorityQueue. 188 func (pq *PrioQueue) Push(x interface{}) { 189 *pq = append(*pq, x.(*Path)) 190 } 191 192 // Pop returns the node with the best potential path for the shortest path. 193 func (pq *PrioQueue) Pop() interface{} { 194 old := *pq 195 n := len(old) 196 x := old[n-1] 197 *pq = old[0 : n-1] 198 return x 199 }