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  }