class App: def __init__(self): self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) self.clock = pygame.time.Clock() self.running = True self.state = 'main menu' self.algorithm_state = '' self.grid_square_length = 24 # self.load() self.start_end_checker = 0 self.mouse_drag = 0 # start and end nodes coordinates self.start_node_x = None self.start_node_y = None self.end_node_x = None self.end_node_y = None # wall nodes list self.wall_pos = wall_nodes_coords_list.copy() # main menu buttons self.bfs_button = Button(self, WHITE, 338, MAIN_BUTTON_Y_POS, MAIN_BUTTON_LENGTH, MAIN_BUTTON_HEIGHT, 'BFS') self.dfs_button = Button(self, WHITE, 558, MAIN_BUTTON_Y_POS, MAIN_BUTTON_LENGTH, MAIN_BUTTON_HEIGHT, 'DFS') self.astar_button = Button(self, WHITE, 778, MAIN_BUTTON_Y_POS, MAIN_BUTTON_LENGTH, MAIN_BUTTON_HEIGHT, 'Astar') self.dijkstra_button = Button(self, WHITE, 998, MAIN_BUTTON_Y_POS, MAIN_BUTTON_LENGTH, MAIN_BUTTON_HEIGHT, 'Dijkstra') # grid menu options self.start_end_node_button = Button(self, AQUAMARINE, 20, START_END_BUTTON_HEIGHT, GRID_BUTTON_LENGTH, GRID_BUTTON_HEIGHT, 'Start/End Node') self.wall_node_button = Button( self, AQUAMARINE, 20, START_END_BUTTON_HEIGHT + GRID_BUTTON_HEIGHT + BUTTON_SPACER, GRID_BUTTON_LENGTH, GRID_BUTTON_HEIGHT, 'Wall Node') self.reset_button = Button( self, AQUAMARINE, 20, START_END_BUTTON_HEIGHT + GRID_BUTTON_HEIGHT * 2 + BUTTON_SPACER * 2, GRID_BUTTON_LENGTH, GRID_BUTTON_HEIGHT, 'RESET') self.start_button = Button( self, AQUAMARINE, 20, START_END_BUTTON_HEIGHT + GRID_BUTTON_HEIGHT * 3 + BUTTON_SPACER * 3, GRID_BUTTON_LENGTH, GRID_BUTTON_HEIGHT, 'Visualize') self.main_menu_button = Button( self, AQUAMARINE, 20, START_END_BUTTON_HEIGHT + GRID_BUTTON_HEIGHT * 4 + BUTTON_SPACER * 4, GRID_BUTTON_LENGTH, GRID_BUTTON_HEIGHT, 'Main Menu') def run(self): while self.running: if self.state == 'main menu': self.main_menu_events() if self.state == 'grid window': self.grid_events() if self.state == 'draw S/E' or self.state == 'draw walls': self.draw_nodes() if self.state == 'start visualizing': self.execute_algo() if self.state == 'aftermath': self.reset_or_main_menu() pygame.quit() sys.exit() def load(self): # self.main_menu_background = pygame.image.load('background.png') # self.grid_background = pygame.image.load('grid.png') pass @staticmethod def draw_text(words, screen, pos, size, color, font_name, center=False): font = pygame.font.SysFont(font_name, size) text = font.render(words, False, color) text_size = text.get_size() if center: pos[0] = pos[0] - text_size[0] // 2 pos[1] = pos[1] - text_size[1] // 2 screen.blit(text, pos) def sketch_main_menu(self): # draw background # self.screen.blit(self.main_menu_background, (0, 0)) self.bfs_button.draw_button(AQUAMARINE) self.dfs_button.draw_button(AQUAMARINE) self.astar_button.draw_button(AQUAMARINE) self.dijkstra_button.draw_button(AQUAMARINE) def sketch_hotbar(self): self.screen.fill(BLACK) pygame.draw.rect(self.screen, WHITE, (0, 0, 240, 768), 0) # draw grid background # self.screen.blit(self.grid_background, (0, 0)) def sketch_grid(self): # add borders pygame.draw.rect(self.screen, ALICE, (240, 0, WIDTH, HEIGHT), 0) pygame.draw.rect(self.screen, AQUAMARINE, (264, 24, GRID_WIDTH, GRID_HEIGHT), 0) # draw grid with size 52, 30 for x in range(52): pygame.draw.line(self.screen, ALICE, (GS_X + x * self.grid_square_length, GS_Y), (GS_X + x * self.grid_square_length, GE_Y)) for y in range(30): pygame.draw.line(self.screen, ALICE, (GS_X, GS_Y + y * self.grid_square_length), (GE_X, GS_Y + y * self.grid_square_length)) def sketch_grid_buttons(self): # draw buttons self.start_end_node_button.draw_button(STEELBLUE) self.wall_node_button.draw_button(STEELBLUE) self.reset_button.draw_button(STEELBLUE) self.start_button.draw_button(STEELBLUE) self.main_menu_button.draw_button(STEELBLUE) def set_menu_buttons_color(self, color): self.start_end_node_button.color = color self.wall_node_button.color = color self.reset_button.color = color self.start_button.color = color self.main_menu_button.color = color def grid_window_buttons(self, pos, event): if event.type == pygame.MOUSEBUTTONDOWN: if self.start_end_node_button.isOver(pos): self.state = 'draw S/E' elif self.wall_node_button.isOver(pos): self.state = 'draw walls' elif self.reset_button.isOver(pos): self.execute_reset() elif self.start_button.isOver(pos): self.state = 'start visualizing' elif self.main_menu_button.isOver(pos): self.back_to_menu() if event.type == pygame.MOUSEMOTION: if self.start_end_node_button.isOver(pos): self.start_end_node_button.color = MINT elif self.wall_node_button.isOver(pos): self.wall_node_button.color = MINT elif self.reset_button.isOver(pos): self.reset_button.color = MINT elif self.start_button.isOver(pos): self.start_button.color = MINT elif self.main_menu_button.isOver(pos): self.main_menu_button.color = MINT else: self.set_menu_buttons_color(STEELBLUE) def grid_button_keep_color(self): if self.state == 'draw S/E': self.start_end_node_button.color = MINT elif self.state == 'draw walls': self.wall_node_button.color = MINT def execute_reset(self): self.start_end_checker = 0 # reset start and end nodes coordinates self.start_node_x = None self.start_node_y = None self.end_node_x = None self.end_node_y = None # reset wall nodes list self.wall_pos = wall_nodes_coords_list.copy() # reset state self.state = 'grid window' def back_to_menu(self): self.start_end_checker = 0 # reset start and end nodes coordinates self.start_node_x = None self.start_node_y = None self.end_node_x = None self.end_node_y = None # reset wall nodes list self.wall_pos = wall_nodes_coords_list.copy() # switch state self.state = 'main menu' def set_algo_buttons_color(self, color): self.bfs_button.color = color self.dfs_button.color = color self.astar_button.color = color self.dijkstra_button.color = color def main_menu_events(self): # draw background pygame.display.update() self.sketch_main_menu() self.draw_text('Path Finding', self.screen, [1200, 720], 28, WHITE, FONT, center=False) for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False pos = pygame.mouse.get_pos() if event.type == pygame.MOUSEBUTTONDOWN: if self.bfs_button.isOver(pos): self.algorithm_state = 'bfs' self.state = 'grid window' if self.dfs_button.isOver(pos): self.algorithm_state = 'dfs' self.state = 'grid window' if self.astar_button.isOver(pos): self.algorithm_state = 'astar' self.state = 'grid window' if self.dijkstra_button.isOver(pos): self.algorithm_state = 'dijkstra' self.state = 'grid window' if event.type == pygame.MOUSEMOTION: if self.bfs_button.isOver(pos): self.bfs_button.color = AQUAMARINE elif self.dfs_button.isOver(pos): self.dfs_button.color = AQUAMARINE elif self.astar_button.isOver(pos): self.astar_button.color = AQUAMARINE elif self.dijkstra_button.isOver(pos): self.dijkstra_button.color = AQUAMARINE else: self.set_algo_buttons_color(WHITE) def grid_events(self): self.sketch_hotbar() self.sketch_grid() self.sketch_grid_buttons() pygame.display.update() for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False pos = pygame.mouse.get_pos() self.grid_window_buttons(pos, event) def draw_nodes(self): self.grid_button_keep_color() self.sketch_grid_buttons() pygame.display.update() pos = pygame.mouse.get_pos() for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False self.grid_window_buttons(pos, event) if 264 < pos[0] < 1512 and 24 < pos[1] < 744: x_grid_pos = (pos[0] - 264) // 24 y_grid_pos = (pos[1] - 24) // 24 if event.type == pygame.MOUSEBUTTONDOWN: self.mouse_drag = 1 if self.state == 'draw S/E' and self.start_end_checker < 2: # color and coordinates for start node if self.start_end_checker == 0: node_color = TOMATO self.start_node_x = x_grid_pos + 1 self.start_node_y = y_grid_pos + 1 self.start_end_checker += 1 # color and coordinates for end node, making sure it's different from start node elif self.start_end_checker == 1 \ and (x_grid_pos + 1, y_grid_pos + 1) != (self.start_node_x, self.start_node_y): node_color = ROYALBLUE self.end_node_x = x_grid_pos + 1 self.end_node_y = y_grid_pos + 1 self.start_end_checker += 1 else: continue # draw on the grid pygame.draw.rect(self.screen, node_color, (264 + x_grid_pos * 24, 24 + y_grid_pos * 24, 24, 24), 0) elif event.type == pygame.MOUSEBUTTONUP: self.mouse_drag = 0 # check to see if mouse drag is available to draw wall nodes if self.mouse_drag == 1: # draw wall nodes and append them accordingly # check for duplicates in the list and if they are overlapping start/end nodes if self.state == 'draw walls': if (x_grid_pos + 1, y_grid_pos + 1) not in self.wall_pos \ and (x_grid_pos + 1, y_grid_pos + 1) != (self.start_node_x, self.start_node_y) \ and (x_grid_pos + 1, y_grid_pos + 1) != (self.end_node_x, self.end_node_y): pygame.draw.rect(self.screen, BLACK, (264 + x_grid_pos * 24, 24 + y_grid_pos * 24, 24, 24), 0) self.wall_pos.append( (x_grid_pos + 1, y_grid_pos + 1)) for x in range(52): pygame.draw.line( self.screen, ALICE, (GS_X + x * self.grid_square_length, GS_Y), (GS_X + x * self.grid_square_length, GE_Y)) for y in range(30): pygame.draw.line( self.screen, ALICE, (GS_X, GS_Y + y * self.grid_square_length), (GE_X, GS_Y + y * self.grid_square_length)) def execute_algo(self): for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False # BFS if self.algorithm_state == 'bfs': self.bfs = BreadthFirst(self, self.start_node_x, self.start_node_y, self.end_node_x, self.end_node_y, self.wall_pos) if self.start_node_x or self.end_node_x is not None: self.bfs.bfs_execute() if self.bfs.route_found: self.draw_path = VisualizePath(self.screen, self.start_node_x, self.start_node_y, self.bfs.route, []) self.draw_path.get_path_coords() self.draw_path.draw_path() else: self.draw_text('NO ROUTE FOUND!', self.screen, [768, 384], 50, RED, FONT, center=True) # DFS elif self.algorithm_state == 'dfs': self.dfs = DepthFirst(self, self.start_node_x, self.start_node_y, self.end_node_x, self.end_node_y, self.wall_pos) if self.start_node_x or self.end_node_x is not None: self.dfs.dfs_execute() if self.dfs.route_found: self.draw_path = VisualizePath(self.screen, self.start_node_x, self.start_node_y, self.dfs.route, []) self.draw_path.get_path_coords() self.draw_path.draw_path() else: self.draw_text('NO ROUTE FOUND!', self.screen, [768, 384], 50, RED, FONT, center=True) # ASTAR elif self.algorithm_state == 'astar': self.astar = Astar(self, self.start_node_x, self.start_node_y, self.end_node_x, self.end_node_y, self.wall_pos) if self.start_node_x or self.end_node_x is not None: self.astar.astar_execute() if self.astar.route_found: self.draw_path = VisualizePath(self.screen, self.start_node_x, self.start_node_y, None, self.astar.route) self.draw_path.draw_path() else: self.draw_text('NO ROUTE FOUND!', self.screen, [768, 384], 50, RED, FONT, center=True) # DIJKSTRA elif self.algorithm_state == 'dijkstra': self.dijkstra = Dijkstra(self, self.start_node_x, self.start_node_y, self.end_node_x, self.end_node_y, self.wall_pos) if self.start_node_x or self.end_node_x is not None: self.dijkstra.dijkstra_execute() if self.dijkstra.route_found: self.draw_path = VisualizePath(self.screen, self.start_node_x, self.start_node_y, None, self.dijkstra.route) self.draw_path.draw_path() else: self.draw_text('NO ROUTE FOUND!', self.screen, [768, 384], 50, RED, FONT, center=True) pygame.display.update() self.state = 'aftermath' def reset_or_main_menu(self): self.sketch_grid_buttons() pygame.display.update() pos = pygame.mouse.get_pos() for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False if event.type == pygame.MOUSEMOTION: if self.start_end_node_button.isOver(pos): self.start_end_node_button.color = MINT elif self.wall_node_button.isOver(pos): self.wall_node_button.color = MINT elif self.reset_button.isOver(pos): self.reset_button.color = MINT elif self.start_button.isOver(pos): self.start_button.color = MINT elif self.main_menu_button.isOver(pos): self.main_menu_button.color = MINT else: self.set_menu_buttons_color(STEELBLUE) if event.type == pygame.MOUSEBUTTONDOWN: if self.reset_button.isOver(pos): self.execute_reset() elif self.main_menu_button.isOver(pos): self.back_to_menu()