def least_cost_path(graph, start, dest, cost): reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance((edge[1], nbr))) # if the dest is not in reached, then no route was found if dest not in reached: return [] current = dest route = [current] # go through the reached vertices until we get back to start and append # each vertice that we "stop" at while current != start: current = reached[current] route.append(current) # reverse the list because we made a list that went from the dest to start route = route[::-1] return route
def least_cost_path(graph, start, dest, cost): reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance( (edge[1], nbr))) if dest not in reached: return [] current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] return path
def make_tree(freq_table): """ Constructs and returns the Huffman tree from the given frequency table. """ trees = BinaryHeap() trees.insert(TreeLeaf(None), 1) for (symbol, freq) in freq_table.items(): trees.insert(TreeLeaf(symbol), freq) while len(trees) > 1: right, rfreq = trees.popmin() left, lfreq = trees.popmin() trees.insert(TreeBranch(left, right), lfreq+rfreq) tree, _ = trees.popmin() return tree
def least_cost_path(graph, start, dest, location): """ *** NOTE: This function is a modified version of our previous *** *** implementation of Dijkstra's Algorithm from Assignment 1 *** *** Part 1. Comments in code indicate changes made. *** Find and return a least cost path in graph from start vertex to dest vertex. Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. location: A dictionary containing all the vertices on the screen. Keys of location are identifiers holding the vertices as values. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} events = BinaryHeap() events.insert((start, start), 0) while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] # each vertex in our graph is a tuple, so we use tuples to find neighbours # instead of single values to find the neighbours of a vertex for nbr in graph.neighbours(location[edge[1]]): # find the identifier/key value attached to nbr tuple nbrID = neighbour_identifier(nbr, location) # insert to binary heap appropriately events.insert((edge[1], nbrID), time + distance(location[edge[1]], nbr)) # FIND MINIMUM PATH IF POSSIBLE if dest not in reached: return [] # start at the dest and continously ind the parent of current vertex # until have reached starting vertex current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] # reverse the list so starts from the ghost return path
def least_cost_path_lrt(self, lrts, end): reached = {} events = BinaryHeap() #insert start location with time of 0 into the heap counter = 0 for key, val in lrts.items(): events.insert((val, val, 0), 0) while (len(events)) > 0: pair, time = events.popmin() #print(time) #account for heuristic so it is not compounded if (pair[2]): time -= pair[2] if pair[1] not in reached: # add to reached dictionary reached[pair[1]] = pair[0] #consider neighbors around each point and distance to each neighbor for neighbour in self.graph.neighbours(pair[1]): point1 = self.locations[pair[1]] point2 = self.locations[neighbour] # maybe we could save distances between objects in a dict, and reuse them? # add heuristic in for the form of euclidean distance. This will helps lead the searching algorithm # more towards the end location heuristic = euclidean_distance(point2, self.locations[end]) # add key and val to BinaryHeap called events events.insert( (pair[1], neighbour, heuristic), time + euclidean_distance(point1, point2) + heuristic) # end loop right after destination is popped if (pair[1] == end): break # use the get_path included with the breadth_first_search helper file to find path from reached dictionary path = None closestLRT = None for key, val in lrts.items(): if val in reached: path = get_path(reached, val, end) if path == None: pass else: closestLRT = key break return path, closestLRT
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] # burn vertex v, record predecessor u for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance((edge[1], nbr))) # if the dest is not in the reached dictionary, then return an empty list if dest not in reached: return [] # IMPLEMENTED FROM GET_PATH FUNCTION FROM breadth_first_search # finds minimum path # start at the dest and continuosly and find the parent of current vertex # until have reached starting vertex current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] return path
def least_cost_path(graph, start, dest, cost): # Implementation of Dijkstra's algorithm events = BinaryHeap() reached = dict() events.insert((start, start), 0) # Begin at time 0, at the start vertex while events: edge, time = events.popmin() # Get next burnt vertex if edge[1] not in reached: # If the destination is not been reached reached[edge[1]] = edge[0] # Keep track of where we came from for w in graph.neighbours(edge[1]): # Burn the neighbours!!!! events.insert(((edge[1]), w), (time + cost.distance( (edge[1], w)))) # Add the fuse return get_path(reached, start, dest) # return the path
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance( (edge[1], nbr))) # if the dest is not in reached, then no route was found if dest not in reached: return [] current = dest route = [current] # go through the reached vertices until we get back to start and append # each vertice that we "stop" at while current != start: current = reached[current] route.append(current) # reverse the list because we made a list that went from the dest to start route = route[::-1] return route
def least_cost_path(graph, start, dest, cost): """ Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ # initialize reached as a empty dict reached = {} # an instance of BinaryHeap class, initially empty events = BinaryHeap() events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: (u, v), time = events.popmin() if v not in reached: reached[v] = u # burn vertex v, record predecessor u nbr_list = graph.neighbours(v) for w in nbr_list: # new event: edge (v,w) started burning events.insert((v, w), time + cost.distance((v, w))) # using the get_path function from bfs file developed in class # get the path as a list, if no path then a empty list return get_path(reached, start, dest)
def least_cost_path(graph, start, dest, cost): """ Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: least: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. >>> g = Graph({1, 2, 3, 4, 5}, [(1, 4), (2, 3), (4, 2)]) >>> location = {1: (1, 0), 2: (4, 0), 3: (10, 0), 4: (2, 0), 5: (10, 10)} >>> cost = CostDistance(location) >>> print(least_cost_path(g, 1, 2, cost)) [1, 4, 2] >>> print(least_cost_path(g, 1, 3, cost)) [1, 4, 2, 3] >>> print(least_cost_path(g, 1, 5, cost)) [] >>> H = Graph({1, 2, 3, 4, 5, 6}, [(1, 2), (2, 3), (2, 4), (4, 3), (3, 5), (1, 6), (6, 5)]) >>> locationtwo = {1: (0, 0), 2: (1, 1), 3: (3, 1), 4: (2, 2), 5: (0, 6), 6: (-100, 100)} >>> testcost = CostDistance(locationtwo) >>> print(least_cost_path(H, 1, 5, testcost)) [1, 2, 3, 5] """ reached = {} events = BinaryHeap() events.insert((start, start), 0) while len(events) > 0: (u, v), time = events.popmin() if v not in reached.keys(): reached[v] = u for w in graph.neighbours(v): events.insert((v, w), time + cost.distance((v, w))) ''' reached is a dictionary of all visted verices. The value of each key is represented by the previous vertice visited to get to that vertice. If we cannot find our destination in reached, then that means there is no such path in the given graph to get from start to dest, and so we would return an empty array. otherwise, we make our list least by working in reverse from our dest, all the way back to start by using the keys in reached to find the previous vertice ''' if dest not in reached: return [] least = [dest] step = dest while step != start: least.append(reached[step]) step = reached[step] least.reverse() return least
def least_cost_path(graph, start, dest, cost): """ Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} #search tree events = BinaryHeap() #uses heap to store data on nodes and weights events.insert((start, start), 0) #starting vertex burns at time 0 edges = graph.get_edges while len(events) > 0: curr = events.popmin() u, v = curr[0][0], curr[0][1] time = curr[1] if v not in reached: reached[v] = u #burn the vertex v and add u to the record neighbors = graph.neighbours(v) for w in neighbors: newTime = time + cost.distance( (v, w) ) #stores the time taken to reach new node from previous events.insert((v, w), newTime) shortest_path = [] shortest_path.append(dest) next_node = reached[dest] while next_node != start: #iterates through the search tree from end to start, #collecting visited vertices and storing them in path array for el in reached: if el == next_node: shortest_path.append(next_node) next_node = reached[el] shortest_path.append(next_node) #removes any doubles: newList = [] for el in shortest_path: if el not in newList: newList.append(el) return list(reversed(newList))
class Maze: def __init__(self, width, length, height): """ Generates all maze cells and edges """ self.width = width self.length = length self.height = height self.generated = False self.graph = Graph((width, length, height)) self.edge_queue = BinaryHeap() self.edges_added = 0 self.inter_layer_edges = 0 for x in range(width): for y in range(length): for z in range(height): self.graph.add_vertex((x, y, z)) self.areas = UnionFind([cell.get_pos() for cell in self]) self.edge_choices = RandomSet(self.graph.get_all_edges()) self.queue_factor = len(self.edge_choices) // LAYER_FACTOR for _ in range(self.queue_factor): edge = self.edge_choices.pop_random() self._queue_edge(edge) self.player = self.graph.get_cell((0, 0, 0)) self.end_cell = self.graph.get_cell( (self.width - 1, self.length - 1, self.height - 1)) self.end_cell.is_end = True def __iter__(self): """ Returns an iterator to the start of the grid """ self._i = 0 self._j = 0 self._k = 0 return self def __next__(self): """ Returns the next cell in the grid """ if self._i < self.width and self._j < self.length \ and self._k < self.height: next_cell = self.graph.get_cell((self._i, self._j, self._k)) self._i += 1 if self._i == self.width: self._i = 0 self._j += 1 if self._j == self.length: self._j = 0 self._k += 1 return next_cell else: # Done iterating over cells raise StopIteration def get_layer(self, layer): return self.graph.get_layer(layer) def get_player(self): return self.player def generate(self): """ Links two random, adjacent cells from different partitions """ # Loop until we have removed a wall while not self.generated: # chooses a random item from all possible edges # and removes this choice if len(self.edge_choices) > 1: self._queue_edge(self.edge_choices.pop_random()) (p1, p2), _ = self.edge_queue.popmin() # adds an edge (removes a wall) if it is valid if self.areas.union(p1, p2): self.graph.add_edge((p1, p2)) return self.graph.get_cell(p1), self.graph.get_cell(p2) # Check if maze is completely generated if len(self.edge_queue) == 0: self.generated = True return None def _queue_edge(self, edge): """ Put an edge into a queue to be generated. We use this to limit the amount of inter-layer edges being generated by making them cost more, which adds them progressively later to the maze. Thus, they are less likely to connect disjoint partitions, and less likely to be generated at all """ if edge[0][2] != edge[1][2]: cost = self.queue_factor * self.inter_layer_edges self.inter_layer_edges += 1 else: cost = self.edges_added self.edges_added += 1 self.edge_queue.insert(edge, cost) def player_move(self, dx, dy, dz): """ checks if an edge exists between current and next cell if so, update player """ p1 = self.player.get_pos() p2 = self.player.x + dx, self.player.y + dy, self.player.z + dz c1 = self.graph.get_cell(p1) if self.graph.is_edge((p1, p2)): self.player = self.graph.get_cell(p2) c2 = self.graph.get_cell(p2) if c2.visited: c1.visited = False c2.visited = False else: c1.flip_visit() return c1, c2 else: return c1, c1 def check_layer(self, new_layer): """ Clamps layer to be valid """ if new_layer < 0: return 0 elif new_layer >= self.height: return self.height - 1 else: return new_layer
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: print("Loop 1") vertices, time = events.popmin() print(type(vertices)) print(type(vertices[0])) if vertices[1] not in reached: reached[vertices[1]] = vertices[0]# vertices[0] = reached[vertices[1]] burn vertex v, record predecessor u for n in graph.neighbours(vertices[1]): # new event: edge (v,w) started burning print("Loop 2") events.insert(([vertices[1]], n), time + cost.distance((vertices[1], n))) if vertices[1] == dest: break #return reached print("Done") #find path to see if a path exists # return empty list if dest cannot be reached/if no path from start to dest exists if dest not in reached: return [] # if the destination gets reached we can form a minimum path current = dest path = [current] while current != start: # print("Loop 3") print(dest) print(type(dest)) current = reached[current] #current = reached[current] path.append(current) #or while reached[current] != start: #path.append(reached[current]) # current = reached[current] path = path[::-1] return path