-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsession_components.py
162 lines (132 loc) · 6.73 KB
/
session_components.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
from map_components import Node
from map_components import Edge
from map_components import Graph
from datetime import datetime
from queue import PriorityQueue
### Represents a route
class Route():
## Constructor
def __init__(self, startNodeID: str, endNodeID: str, avoidStairs: bool=False, avoidSteepTerrain: bool=False):
## TODO: This requires A*. We'll pass the startNodeID, endNodeID, and settings
## to A* which'll return a list of edges, which will become the self.edges var below
# edges = AStar.generateRoutePath(startNodeID, endNodeID, avoidStairs, avoidSteepTerrain)
pass
### Represents a route that is specifically saved to a schedule
class ScheduleRoute(Route):
## Constructor
def __init__(self, ID: str, scheduleID: str, name: str, startTime: datetime, endTime: datetime, edges: list[Edge]):
self.ID = ID
self.scheduleID = scheduleID
self.name = name
self.startTime = startTime
self.endTime = endTime
self.edges = edges
### Represents a schedule
class Schedule():
## Constructor
def __init__(self, ID: str, name: str, scheduleRoutes: list[ScheduleRoute]):
self.ID = ID
self.name = name
self.scheduleRoutes = scheduleRoutes
## Adds the given ScheduleRoute to this Schedule's scheduleRoutes
def addScheduleRoute(self, scheduleRoute: ScheduleRoute):
self.scheduleRoutes.append(scheduleRoute)
### Used to generate routes via A*
class AStar():
## Constructor
def __init__(self):
pass
## Returns a list of edges which connect the startNode and endNode
def generateRoutePath(self, graph: Graph, startNodeID: str, goalNodeID: str, avoidStairs: str=False, avoidSteepTerrain: str=False) -> list[str]:
# Return empty list if goal is start
if startNodeID == goalNodeID:
return []
# Create a copy of the graph for use in finding path
# (copy is needed so parent values get reset)
self.copyGraph = graph.getDeepCopy()
# Get nodes from nodeIDs
startNode: Node = self.copyGraph.getNodeFromID(startNodeID)
goalNode: Node = self.copyGraph.getNodeFromID(goalNodeID)
# Initialize startNode
startNode.gScore = 0.0
startNode.hScore = self.heuristicFunction(startNode, goalNode)
startNode.fScore = 0.0
# Initialize list of visited nodes
visitedNodes = []
# Initialize queue of nodes to visit
nodesToVisit: PriorityQueue = PriorityQueue()
# Place the start node on the min queue
nodesToVisit.put((0, startNode))
# Go until there are no more nodes to visit
while not nodesToVisit.empty():
# Get node with lowest F score
currentNode: Node = nodesToVisit.get()[1]
# Mark currentNode as visited
visitedNodes.append(currentNode)
# Iterate through edges
for currentEdge in currentNode.edges:
# Go to next edge if currenteEdge is a stair and user wants to avoid stairs
if currentEdge.isStair and avoidStairs:
continue
# Go to next edge if currenteEdge is steep terrain and user wants to avoid steep terrain
if currentEdge.isSteepTerrain and avoidSteepTerrain:
continue
# Get neighbor that the current edge connects to
neighbor: Node = currentEdge.getOtherNode(currentNode)
# Goal found!!!
if neighbor == goalNode:
# Set the neighbor (goal)'s parent as the current node's ID
neighbor.parentID = currentNode.ID
# Get and return the final path
return self.getPathFromGoalNode(neighbor)
# Goal not found, bulding found
elif neighbor.isBuilding:
# Can't go through building, just ignore
continue
# Goal not found, normal buliding found
else:
# Calculate fScore for current path to neighbor
tempGScore: float = currentNode.gScore + currentEdge.weight
tempHScore: float = self.heuristicFunction(neighbor, goalNode)
tempFScore: float = tempGScore + tempHScore
# If neighbor has not been visited before OR our current pathing is faster
if neighbor.fScore == float("inf") or neighbor.fScore > tempFScore:
# Update neighbor scores and parent
neighbor.gScore = tempGScore
neighbor.hScore = tempHScore
neighbor.fScore = tempFScore
neighbor.parentID = currentNode.ID
nodesToVisit.put((neighbor.fScore, neighbor))
# Path was not found
print("Path not found")
return None
## Returns the heuristic value of the given node for the path's goal node
def heuristicFunction(self, node: Node, goalNode: Node) -> float:
# Return Manhattan Distance
return (abs(node.xCoordinate - goalNode.xCoordinate) + abs(node.yCoordinate - goalNode.yCoordinate))
# Return Euclidian Distance (likely better for final implementation, as others are best for grids)
# return math.sqrt((node.xCoordinate - goalNode.xCoordinate)**2 + (node.yCoordinate - goalNode.yCoordinate)**2)
## Returns list of edges that form found path
def getPathFromGoalNode(self, goalNode: Node):
# Initialize list of edge IDs
edgeIDs : list[str] = []
# Initialize currentNode as goalNode
currentNode : Node = goalNode
# Go through chain of parents
while currentNode.ID != currentNode.parentID:
# Get the edge that connects these two nodes
for edge in currentNode.edges:
if edge.getOtherNode(currentNode).ID == currentNode.parentID:
# Add edge ID to list
edgeIDs.append(edge.ID)
# Update current node
currentNode = self.copyGraph.getNodeFromID(currentNode.parentID)
break
# All edges have been added, since entire path has been traversed backwards
return edgeIDs
### Represents a user
class User():
## Constructor
def __init__(self, ID: str, schedules: list[Schedule] = None):
self.ID = ID
self.schedules = schedules