def paint(self): """ Paints the player and its aim indicator on the screen. Also paints the targets for the aimed at tile if it has any. """ super(Player, self).paint() if self.removing_tile: if g.in_map(*self.get_aim_tile()) and g.get_img(*self.get_aim_tile()).destroy is not None: aim = "remove_aim" else: aim = "remove_aim_fail" else: aim = "aim" x = ((self.last_aim_tile[0]*c.TILE_SIZE) + (c.TILE_SIZE - g.images[aim].get_size()[0]) / 2) y = ((self.last_aim_tile[1]*c.TILE_SIZE) + (c.TILE_SIZE - g.images[aim].get_size()[1]) / 2) g.screen.blit(g.images[aim].get(), (x, y)) # When you aim at a factory, display the set targets for that tile if g.in_map(*self.last_aim_tile) and g.get_img(*self.last_aim_tile).factory_output: x, y = self.last_aim_tile if g.map[x][y].good_targets: for good_target in list(g.map[x][y].good_targets.values()): g.screen.blit(g.images["tile_target_aim"].get(), (good_target[0]*c.TILE_SIZE + (c.TILE_SIZE - g.images["tile_target_aim"].get_size()[0]) / 2, good_target[1]*c.TILE_SIZE + (c.TILE_SIZE - g.images["tile_target_aim"].get_size()[1]) / 2)) # Show the direction the selected launcher tile is shooting if type(g.map[x][y]) == tiles.LauncherTile and g.map[x][y].shoot_direction != (0, 0): g.screen.blit(g.images["tile_target_aim"].get(), ((g.map[x][y].shoot_direction[0]+x)*c.TILE_SIZE + (c.TILE_SIZE - g.images["tile_target_aim"].get_size()[0]) / 2, ((g.map[x][y].shoot_direction[1]+y)*c.TILE_SIZE + (c.TILE_SIZE - g.images["tile_target_aim"].get_size()[1]) / 2)))
def goods_pathfind(self, target_goods): """ Finds a path from the start tile, (the current tile of the entity) to the nearest factory tile that can recieve the passed-in goods type. "goods" should be a string with the type of goods that the entity will be carrying. returns True if a suitable tile was found and False if no suitable tile was found """ start = self.get_tile() # The open_dict and closed_dict both follow the format: # (key_x, key_y): [g, (came_from_x, came_from_y)] # This is a simplified version of the pathfind function. It doesn't use heuristics because it doesn't # know where to go yet. It only uses the G value, which is the distance to the tile it came from. if type(start) != tuple: raise Exception("Value given by self.get_tile() to PathingEntity.goods_pathfind() is not tuple") # The open dictionary is a dictionary keeping the currently checkable tiles # The starting tile is identified as not having a came_from-tuple. open_dict = {start: [0]} # The closed dictionary is a dicionary keeping the currently checked tiles closed_dict = {} current = None while len(open_dict.keys()) > 0: lowest_value = None # Get the lowest value for key in open_dict.keys(): value = open_dict[key][0] if lowest_value is None: lowest_value = value current = key elif value < lowest_value: lowest_value = value current = key # Move current from the open dictionary to the closed one closed_dict[current] = open_dict[current] del open_dict[current] # If we're done here done = False deliver_tiles = [] # This code block makes sure the items are delivered to the tile next to the tile instead of diagonally if (g.in_map(current[0]+1, current[1]) and g.get_img(current[0]+1, current[1]).factory_input): deliver_tiles.append((current[0]+1, current[1])) elif (g.in_map(current[0]-1, current[1]) and g.get_img(current[0]-1, current[1]).factory_input): deliver_tiles.append((current[0]-1, current[1])) elif (g.in_map(current[0], current[1]+1) and g.get_img(current[0], current[1]+1).factory_input): deliver_tiles.append((current[0], current[1]+1)) elif (g.in_map(current[0], current[1]-1) and g.get_img(current[0], current[1]-1).factory_input): deliver_tiles.append((current[0], current[1]-1)) if deliver_tiles: for deliver_tile in deliver_tiles: try: for good_name in list(g.map[deliver_tile[0]][deliver_tile[1]].requests.keys()): if good_name == target_goods: if g.map[deliver_tile[0]][deliver_tile[1]].requests[target_goods] > 0: done = True g.map[deliver_tile[0]][deliver_tile[1]].requests[target_goods] -= 1 break # Break out of the goods for loop, not the entire while loop if done: break except AttributeError: pass if done is True: full_path = [] self.deliver_tile = deliver_tile try: while current != start: full_path.append(current) current = closed_dict[current][1] except: print(closed_dict) print(full_path) # Trying to understand the heisenbug! D: import pdb, sys e, m, tb = sys.exc_info() pdb.post_mortem(tb) full_path.reverse() self.path = full_path self.next_target_tile() self.home_tile = self.get_tile() return True # Move through all neighbours for i in range(current[0]-1, current[0]+2): for j in range(current[1]-1, current[1]+2): neighbour = (i, j) if 0 <= neighbour[0] < len(g.map) and 0 <= neighbour[1] < len(g.map[0]): # Get the G score, the cost to go back to the start if neighbour[0] == current[0]: if neighbour[1] == current[1]: continue else: g_score = closed_dict[current][0] + 10 else: if neighbour[1] == current[1]: g_score = closed_dict[current][0] + 10 else: # Make sure the pathfinding doesn't try to go through blocks diagonally. if (g.get_img(neighbour[0], current[1]).collides and g.get_img(current[0], neighbour[1]).collides): # If the blocks on either side of the diagonal walk is collidable, skip this one if neighbour not in closed_dict: closed_dict[neighbour] = None continue # If it can travel diagonally, give it a cost of the square root of two. g_score = closed_dict[current][0] + 14 if c.IMAGES[g.map[neighbour[0]][neighbour[1]].type].collides is False or neighbour == start: # Check if this neighbour can be added the the open_dict and do so if so if neighbour not in closed_dict.keys(): if neighbour not in open_dict.keys() or g_score < open_dict[neighbour][0]: open_dict[neighbour] = [g_score, current] else: if neighbour not in closed_dict.keys() and neighbour not in open_dict.keys(): closed_dict[neighbour] = None self.path = [] self.stop_moving() return False
def pathfind(self, end): """ Finds a path from start to end tiles using A* algorithm "start" and "end" are tuples with x and y coordinates of a tile returns True if it succeded and False if it couldn't find a path """ start = self.get_tile() # The open_dict and closed_dict both follow the format: # (key_x, key_y): [f, g, h, (came_from_x, came_from_y)] # F, G and H. G is the length to the start tile, H is the Heuristics # estimate of the distance to the end tile and F = G + H if type(start) != tuple or type(end) != tuple: raise Exception("Value passed to PathingEntity.pathfind() is not tuple") # The open dictionary is a dictionary keeping the currently checkable tiles # The starting tile is identified as not having a came_from-tuple. open_dict = {start: [self._heuristic_cost_estimate(start, end), 0, self._heuristic_cost_estimate(start, end)]} # The closed dictionary is a dicionary keeping the currently checked tiles closed_dict = {} current = None while len(open_dict.keys()) > 0: lowest_value = None # Get the lowest value for key in open_dict.keys(): value = open_dict[key][0] if lowest_value is None: lowest_value = value current = key else: if value < lowest_value: lowest_value = value current = key # Move current from the open dictionary to the closed one closed_dict[current] = open_dict[current] del open_dict[current] # If we're done here deliver_tile = None if g.get_img(*end).collides and current != end: # Find a tile next to the target tile to stand on if it collides if (g.in_map(current[0] + 1, current[1]) and (current[0] + 1, current[1]) == end): deliver_tile = (current[0] + 1, current[1]) elif (g.in_map(current[0] - 1, current[1]) and (current[0] - 1, current[1]) == end): deliver_tile = (current[0] - 1, current[1]) elif (g.in_map(current[0], current[1] + 1) and (current[0], current[1] + 1) == end): deliver_tile = (current[0], current[1] + 1) elif (g.in_map(current[0], current[1] - 1) and (current[0], current[1] - 1) == end): deliver_tile = (current[0], current[1] - 1) elif current == end: deliver_tile = current if deliver_tile is not None: # closed_dict[current] full_path = [] while current != start: full_path.append(current) current = closed_dict[current][3] full_path.reverse() self.deliver_tile = deliver_tile self.path = full_path self.next_target_tile() return True # # Old version # if done is True: # full_path = [] # self.deliver_tile = deliver_tile # while len(closed_dict[current]) > 1: # full_path.append(current) # current = closed_dict[current][1] # full_path.reverse() # self.path = full_path # self.next_target_tile() # self.home_tile = self.get_tile() # return # Move through all neighbours for i in range(current[0]-1, current[0]+2): for j in range(current[1]-1, current[1]+2): neighbour = (i, j) if 0 <= neighbour[0] < len(g.map) and 0 <= neighbour[1] < len(g.map[0]): # Get the G score, the cost to go back to the start if neighbour[0] == current[0]: if neighbour[1] == current[1]: continue else: g_score = closed_dict[current][1] + 10 else: if neighbour[1] == current[1]: g_score = closed_dict[current][1] + 10 else: # Make sure the pathfinding doesn't try to go through blocks diagonally. if (g.get_img(neighbour[0], current[1]).collides and g.get_img(current[0], neighbour[1]).collides): # If the blocks on either side of the diagonal walk is collidable, skip this one closed_dict[neighbour] = None continue # If it can travel diagonally, give it a cost of the square root of two. g_score = closed_dict[current][1] + 14 if c.IMAGES[g.map[neighbour[0]][neighbour[1]].type].collides is False: # Check if this neighbour can be added the the open_dict and do so if so if neighbour not in closed_dict.keys(): if neighbour not in open_dict.keys() or g_score < open_dict[neighbour][1]: h_score = self._heuristic_cost_estimate(neighbour, end) f_score = g_score + h_score open_dict[neighbour] = [f_score, g_score, h_score, current] else: closed_dict[neighbour] = None self.path = [] self.stop_moving() return False