def testRandomMaze(): import random import matplotlib.pyplot as plt import time start_time = time.time() maze = Maze() plt.plot(0, 0, 'go') for i in range(10): x = random.randrange(0, 100) y = random.randrange(0, 100) plt.plot(x, y, 'bo') if Node.find_node(x, y) < 0: n = Node(x, y) r = random.choice(list(n.registry.copy())) n.connect_to(r) plt.plot([n.x, r.x], [n.y, r.y], 'r-') r = random.choice(list(n.registry.copy())) n.connect_to(r) plt.plot([n.x, r.x], [n.y, r.y], 'r-') else: print("Node %s already exist at (%d,%d)" % (Node.registry[Node.find_node(x, y)], x, y)) e = random.choice(Node.registry) maze.mark_exit(e) plt.plot(e.x, e.y, 'rd') try: print(maze.find_way_out()) except NoSolution: print("No solution") print("Time spent: " + str(time.time() - start_time)) plt.show()
def show_cursor_coordinate(self, event): "Display current cursor coordinate on top-right corner" self.delete('coordinate') x = round(event.x - self.width / 2) y = round(self.height / 2 - event.y) self.create_text(60, 20, text='x=%d, y=%d' % (x, y), tag='coordinate') if Node.find_node(x, y) >= 0: self.create_text(60, 40, text='Node %d' % Node.find_node(x, y), tag='coordinate')
def connect_nearest_manhattan(n: Node) -> bool: with warnings.catch_warnings(): warnings.filterwarnings('ignore') possible_y = dict_x[n.x] loc_y = possible_y.index(n.y) tc = set() # short for to be connected if loc_y > 0: tc.add((n.index, Node.find_node(n.x, possible_y[loc_y - 1]))) if loc_y < len(possible_y) - 1: tc.add((n.index, Node.find_node(n.x, possible_y[loc_y + 1]))) possible_x = dict_y[n.y] loc_x = possible_x.index(n.x) if loc_x > 0: tc.add((n.index, Node.find_node(possible_x[loc_x - 1], n.y))) if loc_x < len(possible_x) - 1: tc.add((n.index, Node.find_node(possible_x[loc_x + 1], n.y))) for n0, n1 in sorted(tc, key=sort_key): n0 = Node.registry[n0] n1 = Node.registry[n1] if self.connect(n0.index, n1.index, show_distance=show_distance, update=update): return True return False
def generate_maze(self, n, s=10, c=0.55, show_distance=True, update=True, show_node_num=True): """ :param n: number of nodes to be generated :param s: size of the maze. The function will run infinitely if n>s**2 :param c: chance of two nodes connecting each other when they don't have to :param update: Update canvas after each drawing :param show_distance: show distance on the coordinate plane between two connected nodes :return: None This function generate a maze on the coordinate plane so that 1. there is no isolated nodes 2. there is no diagonal connection between nodes 3. there is no overlapping edges """ _n = n size = s zoom = ( self.coordinate_plane.height - 20 ) // size # scale the maze to be size*size, height should be the same as min(height,width) for i in range(n): # add n of nodes to the map flag = False while not flag: x = random.randrange(0, size) - size // 2 y = random.randrange(0, size) - size // 2 if Node.find_node(x * zoom, y * zoom) < 0: flag = True self.add_node(x * zoom, y * zoom, update=update, show_number=show_node_num) # The part below is super inefficient and badly written but I DON'T CARE dict_x = {} # stored value will be x:y dict_y = {} # stored value will be y:x for x, y in Node.map_origin.keys(): try: dict_x[x].append(y) dict_x[x].sort() except KeyError: dict_x[x] = [y] try: dict_y[y].append(x) dict_y[y].sort() except KeyError: dict_y[y] = [x] to_be_connected = set( ) # in a tuple (n0,n1) where n0 and n1 are index number of a node nodes_with_edge = set() for x in dict_x: i = 0 while i < len(dict_x[x]) - 1: y0 = dict_x[x][i] y1 = dict_x[x][i + 1] if (not Node.find_node(x, y0) in nodes_with_edge and not Node.find_node(x, y1) in nodes_with_edge ) or random.random( ) <= c: # to ensure each node has at least one edge n0 = Node.find_node(x, y0) n1 = Node.find_node(x, y1) to_be_connected.add((n0, n1)) nodes_with_edge.add(n0) nodes_with_edge.add(n1) i += 1 for y in dict_y: i = 0 while i < len(dict_y[y]) - 1: x0 = dict_y[y][i] x1 = dict_y[y][i + 1] if (not Node.find_node(x0, y) in nodes_with_edge and not Node.find_node(x1, y) in nodes_with_edge ) or random.random( ) <= c: # to ensure each node has at least one edge n0 = Node.find_node(x0, y) n1 = Node.find_node(x1, y) to_be_connected.add((n0, n1)) nodes_with_edge.add(n0) nodes_with_edge.add(n1) i += 1 # Try to find intersections in the maze that is not a node hlines = {} # in format y:(x0,x1) where x0<x1 vlines = {} for n0, n1 in to_be_connected: n0 = Node.registry[n0] n1 = Node.registry[n1] if n0.x == n1.x: # this is a vertical line try: vlines[n0.x].append((n0.y, n1.y) if n0.y < n1.y else (n1.y, n0.y)) except KeyError: vlines[n0.x] = [(n0.y, n1.y) if n0.y < n1.y else (n1.y, n0.y)] else: # horizontal try: hlines[n0.y].append((n0.x, n1.x) if n0.x < n1.x else (n1.x, n0.x)) except KeyError: hlines[n0.y] = [(n0.x, n1.x) if n0.x < n1.x else (n1.x, n0.x)] for x in vlines: for v in vlines[x]: y0, y1 = v # y0<y1 for y in hlines: if y0 <= y <= y1: for h in hlines[y]: x0, x1 = h if x0 <= x <= x1: # this two lines intersect at (x,y) if Node.find_node(x, y) < 0: n = self.add_node( x, y, update=update, show_number=show_node_num) to_be_connected.add( (n.index, Node.find_node(x, y0))) to_be_connected.add( (n.index, Node.find_node(x, y1))) to_be_connected.add( (n.index, Node.find_node(x0, y))) to_be_connected.add( (n.index, Node.find_node(x1, y))) dict_x = {} dict_y = {} for x, y in Node.map_origin.keys(): try: dict_x[x].append(y) dict_x[x].sort() except KeyError: dict_x[x] = [y] try: dict_y[y].append(x) dict_y[y].sort() except KeyError: dict_y[y] = [x] def sort_key(t): # sort the edges by length n0 = Node.registry[t[0]] n1 = Node.registry[t[1]] return self.calculate_distance(n0.x, n0.y, n1.x, n1.y) with warnings.catch_warnings(): warnings.filterwarnings('ignore') for n0, n1 in sorted(to_be_connected, key=sort_key): self.connect(n0, n1, show_distance=show_distance, update=update) def connect_nearest_manhattan(n: Node) -> bool: with warnings.catch_warnings(): warnings.filterwarnings('ignore') possible_y = dict_x[n.x] loc_y = possible_y.index(n.y) tc = set() # short for to be connected if loc_y > 0: tc.add((n.index, Node.find_node(n.x, possible_y[loc_y - 1]))) if loc_y < len(possible_y) - 1: tc.add((n.index, Node.find_node(n.x, possible_y[loc_y + 1]))) possible_x = dict_y[n.y] loc_x = possible_x.index(n.x) if loc_x > 0: tc.add((n.index, Node.find_node(possible_x[loc_x - 1], n.y))) if loc_x < len(possible_x) - 1: tc.add((n.index, Node.find_node(possible_x[loc_x + 1], n.y))) for n0, n1 in sorted(tc, key=sort_key): n0 = Node.registry[n0] n1 = Node.registry[n1] if self.connect(n0.index, n1.index, show_distance=show_distance, update=update): return True return False def connect_nearest(n: Node) -> bool: with warnings.catch_warnings(): warnings.filterwarnings('ignore') for n0, n1 in sorted(zip(itertools.repeat(n), Node.registry), key=sort_key): if self.connect(n0, n1, show_distance=show_distance, update=update): return True return False import itertools for node in Node.registry.values( ): # Make sure every node is connected to the main maze try: self.search_route(node, self.origin) except NoSolution: t = 0 flag = connect_nearest_manhattan(node) while not flag: try: node = node.get_edge( random.choice([k for k in node._edges.keys()]) ).get_other( node.index ) # try to connect to the main maze from its neighbours flag = connect_nearest_manhattan(node) except IndexError: # An isolated node. Give up print('Isolated: Giving up on node' + str(node.index)) break t += 1 if t > _n: print('Timeout: Giving up on node' + str(node.index)) break # attempted more than the maze size. Give up. else: print("Succeeded: Fixed connection for node" + str(node.index))