class Map: def __init__(self): self.map = [] self.finder = AStarFinder(diagonal_movement=DiagonalMovement.never) def load(self, path): im = Image.open(path) w, h = im.size v = list( map(lambda p: 1 if (p[0] + p[1] + p[2]) // 3 > 127 else 0, im.getdata())) self.grid = Grid(matrix=[v[i * w:(i + 1) * w] for i in range(h)]) def width(self): return len(self.map[0]) def height(self): return len(self.map) def find_path(self, a, b): start = self.grid.node(a[0], a[1]) end = self.grid.node(b[0], b[1]) path, runs = self.finder.find_path(start, end, self.grid) self.grid.cleanup() return path
def dtw_path(map_array): # Create grid using the map array grid = Grid(matrix=map_array) x_max, y_max = map_array.shape # Create grid points at start of each, and the end of each # node in (y,x) format start = grid.node(0, 0) end = grid.node(y_max - 1, x_max - 1) # Path find finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path_dtw, runs = finder.find_path(start, end, grid) # Return the path return path_dtw
def optimal_path(mtrx, coords): # Create grid from matrix grid = Grid(matrix=mtrx) # Create starting point node from coordinates start = grid.node(coords[0][1], coords[0][0]) # Create target point node from coordinates end = grid.node(coords[1][1], coords[1][0]) # Initialize finder as instance of AStarFinder that allows for diagonal movement finder = AStarFinder(diagonal_movement=DiagonalMovement.always) # Find optimal path and number of runs between starting point and target point path, runs = finder.find_path(start, end, grid) print('Number of runs: ', runs) print('Path length: ', len(path), '\n') return path
def route(start, end, matrix) -> path: """ Take a matrix of the level in the form wighted numbers, a start and stop point, and return a path between them. param: start: (x, y) location of the monster param: end: (x, y) location of the player param: matrix: a 2d list of the level. 0s are walls, numbers greater than 0 are weighted """ grid = Grid(matrix=matrix) start = grid.node(start[0], start[1]) end = grid.node(end[0], end[1]) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, grid) return path
def __init__(self, map_id: int = 0): """ Parameters ---------- map_id: int Which version of the game to play. Legal values are 0 and 1. Default is 0. Corresponds to the hard-coded maps MAP0 and MAP1 defined in the same module. """ if map_id == 0: used_map = MAP0 elif map_id == 1: used_map = MAP1 # if adding more values here, also adapt docstring above. else: raise NotImplementedError() # get arrays of walls and reward fields: self.map_walls, self.map_rewards = get_maps(used_map) self.rews_where = np.where(self.map_rewards > 0) # get list of coordinates for reward fields: self.rew_coords = get_rew_coords(self.map_rewards) # get number of rewards in map_id: self.num_rewards = len(self.rews_where[0]) # calculate all possible paths, using the 'pathfinding' library. self.paths = {} matrix = np.swapaxes(np.abs(self.map_walls - 1.0), 0, 1).tolist() finder = AStarFinder(diagonal_movement=DiagonalMovement.never) for i in range(self.num_rewards): self.paths[i] = {} for j in range(self.num_rewards): grid = Grid(matrix=matrix) start = grid.node(self.rews_where[0][i], self.rews_where[1][i]) end = grid.node(self.rews_where[0][j], self.rews_where[1][j]) path, _ = finder.find_path(start, end, grid) path = [[int(x[0]), int(x[1])] for x in path] self.paths[i][j] = path super(MazeWorld, self).__init__() # set observation space and action space: self.observation_space = MultiBinary(self.num_rewards * 2) self.action_space = Discrete(self.num_rewards) self.current_state = None self.terminated = True
def find_path(matrix, start_x, start_y, end_x, end_y, tile_size, map_height): """ Creates a path from a matrix of barriers, a start position, a desired end position, the size of tiles used in a map, and the map's height (in tiles). """ # TODO: Get map height from matrix instead of it being a parameter grid = Grid(matrix=matrix) start_x = clamp(start_x, 0, start_x) start_y = clamp(start_y, 0, start_y) end_x = clamp(end_x, 0, end_x) end_y = clamp(end_y, 0, end_y) print(len(matrix)) print((int(start_x / tile_size), map_height - int(start_y / tile_size), (int(end_x / tile_size), map_height - int(end_y / tile_size)))) # For some reason the y value is inverted. Probably has to do with the grid # Hence map_height - ..., # TODO: Figure out why bottom of the map causes IndexError try: start = grid.node(int(start_x / tile_size), map_height - int(start_y / tile_size) - 1) end = grid.node(int(end_x / tile_size), map_height - int(end_y / tile_size)) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, grid) except IndexError: print("Out of range.") path = [] # Again, subtracting by the tile height otherwise the path is flipped path_resized = [(position[0] * tile_size, (map_height - position[1]) * tile_size) for position in path] # print('operations:', runs, 'path length:', len(path)) # print(grid.grid_str(path=path, start=start, end=end)) return path_resized
def update(self): # definitions for later calculations xgrid = (self.rect.x + self.width - 1)// 20 ygrid = (self.rect.y + self.height - 1) // 20 centrex = self.rect.x + self.width // 2 centrey = self.rect.y + self.height // 2 #pathfinding logic ## if the target square has been reached sets the next one if (self.targetx == centrex) and (self.targety == centrey): if ((pacman.rect.x // 20 > 0) and (pacman.rect.y // 20 > 0) and (pacman.rect.x // 20 + 1 <= maxsizex - 1 ) and (pacman.rect.y // 20 + 1 <= maxsizey -1 )): start = self.grid.node(ygrid, xgrid) end = self.grid.node((pacman.rect.y + pacman.rect.height // 2) // 20, (pacman.rect.x + pacman.rect.width // 2) // 20) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) #self.grid.cleanup() path, runs = finder.find_path(start, end, self.grid) self.grid.cleanup() if len(path) <= 1: oneblock = True else: oneblock = False self.targetx = (int(path[1][1]) * 20 + 10) self.targety = (int(path[1][0]) * 20 + 10) #print(path) #print('operations:', runs, 'path length:', len(path)) #print(self.grid.grid_str(path=path, start=start, end=end)) ## moves the enemy to the target square else: speedmultiplyer = 0 xdir = 0 ydir = 0 if centrex < self.targetx: speedmultiplyer += 1 xdir = 1 elif centrex > self.targetx: speedmultiplyer += 1 xdir = -1 elif centrey < self.targety: speedmultiplyer += 1 ydir = 1 elif centrey > self.targety: speedmultiplyer += 1 ydir = -1 self.rect.x += xdir * enemymovespeed / speedmultiplyer self.rect.y += ydir * enemymovespeed / speedmultiplyer
def __init__(self, world): self.world = world self.map = world.map self.pathfinder = AStarFinder( diagonal_movement=DiagonalMovement.never, time_limit=0.006 ) self.path_cache_duration = 1000
def find_path(board, start, end, trim_tip=False, prune_tip=False): tip_stack = board.agent_snake.tip_stack() head_distance = board.pt_distance(start, end) if prune_tip or trim_tip and head_distance > tip_stack: logger.debug('Trimming tip of snake with {} tips stacked and {} away.'.format(tip_stack, head_distance)) board_c = deepcopy(board) board_c[end.y][end.x] = None grid = Grid(matrix=board_c) else: grid = Grid(matrix=board) start_pt = grid.node(start.x, start.y) end_pt = grid.node(end.x, end.y) finder = AStarFinder() path, _ = finder.find_path(start_pt, end_pt, grid) return path
def doPF(im): startX, startY = pickPoint(im) endX, endY = pickPoint(im) print(startX, startY, " -> ", endX, endY) matrix = makePathMatrix(im) grid = Grid(matrix=matrix) start = grid.node(startX, startY) end = grid.node(endX, endY) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, grid) print('operations:', runs, 'path length:', len(path)) return path
def findPath(start, end): # Function that queries the pathfinding library for path global pathMatrix grid = Grid(matrix=pathMatrix) start = grid.node(start[0], start[1]) end = grid.node(end[0], end[1]) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) path, runs = finder.find_path(start, end, grid) print('operations:', runs, 'path length:', len(path)) print(grid.grid_str(path=path, start=start, end=end)) print(path) return path
def create_grid_and_find_path(matrix, start, end, time_limit, wrap=False): grid = Grid(matrix=matrix, wrap=wrap) start = grid.node(start.x, start.y) end = grid.node(end.x, end.y) # finder = AStarFinder(diagonal_movement=DiagonalMovement.never, heuristic='world_wrap') finder = AStarFinder(diagonal_movement=DiagonalMovement.never, time_limit=time_limit, heuristic='world_wrap') path, runs = finder.find_path(start, end, grid) return path # test()
def getPath(start, end): maze = [[0 for x in range(len(pathfinding_grid[0]))] for y in range(len(pathfinding_grid))] for x in pathfinding_grid: for e in x: maze[e.ex][e.ey] = e.difficulty if nodeFromCords(end[0]) > len(maze) - 1: print("target out of bounds") return [] if nodeFromCords(end[1]) > len(maze[0]) - 1: print("out of bounds") return [] #find the closest node to the real world cords. if pathfinding_grid[nodeFromCords(end[0])][nodeFromCords( end[1])].difficulty == 0: print("BAD TARGET POSITION") return [] grid = Grid(matrix=maze) start_node = grid.node(nodeFromCords(start[1]), nodeFromCords(start[0])) end_node = grid.node(nodeFromCords(end[1]), nodeFromCords(end[0])) #This accepts weights. So 0 is wall, 1+ is increasingly harder to traverse. finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start_node, end_node, grid) print('operations:', runs, 'path length:', len(path)) #print(grid.grid_str(path=path, start=start, end=end)) world_path = [] if path != None: for e in path: world_path.append((e[0] / npm, e[1] / npm)) #remove the first element in the list as that is the approx robot position. if len(world_path) > 1: world_path.pop(0) world_path[-1] = (end[1], end[0]) return world_path
def __init__(self): # in the end, `self.terrain_grid` will be a 2D array of values where zeros are obstacles # and all values greater than zero are edge weights for all edges connected to that node self.terrain_grid = [] with open("./resources/terrain.txt") as input: rows = input.readlines() # loop through entire terrain file and convert all chars to integers for row in rows: terrain_grid_row = [] for col in row: if col.isdigit(): terrain_grid_row.append(int(col)) self.terrain_grid.append(terrain_grid_row) # instantiate the Grid object using the newly converted terrain_grid # The pathfinding library requires this special object to work self.grid = Grid(matrix=self.terrain_grid) # define the A* pathfinding algorithm as the one we use. Another option is Dijkstra's self.finder = AStarFinder(diagonal_movement=DiagonalMovement.always)
def pathfinder(img_array, startpoint, endpoint): matrix = img_array grid = Grid(matrix=matrix) start = grid.node(startpoint[0], startpoint[1]) end = grid.node(endpoint[0], endpoint[1]) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, grid) grid_str = grid.grid_str(path=path, start=start, end=end) print('operations:', runs, 'path length:', len(path)) print(grid_str) img_array2 = img_array for node in path: col = node[0] row = node[1] img_array2[row][col] = 4 img_array2[startpoint[1]][startpoint[0]] = 2 img_array2[endpoint[1]][endpoint[0]] = 3 return img_array2
def path_finder(self, x0, y0, x1, y1): """ Description ----------- Finds a path between two points and returns a list of coordinates along the path Currently only checks for road-compatible paths by avoiding mountains and going on land when possible Params -------- x0,y0,x1,y1 - coordinates for two points to connect """ # Generate pathfinding grid matrix = [[0] * len(self.grid) for i in range(len(self.grid))] for column in self.grid: for cell in column: if cell.cell_type in [ CellTypes.land.value.LAND_3, CellTypes.land.value.LAND_4, CellTypes.land.value.LAND_5 ]: matIn = 0 # obstacles elif self.check_if_water(cell) or cell.cell_type in [ CellTypes.land.value.SAND_0, CellTypes.land.value.RIVER_BANK ]: matIn = 5 # water, only cross this if necessary elif cell.cell_type in [CellTypes.road.value.ROAD_DIRT]: matIn = 1 # use existing roads when possible else: matIn = 2 # free spaces matrix[cell.y][cell.x] = matIn pathGrid = Grid(matrix=matrix) # Find a path start = pathGrid.node(x0, y0) end = pathGrid.node(x1, y1) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, pathGrid) # Return the path return path
def extract_skeleton_trace(img, roi_grid_size): skel = skeletonize(img) skel = skel.astype(np.float32) kernel = np.float32([[1, 1, 1], [1, 10, 1], [1, 1, 1]]) output = ndimage.convolve(skel, kernel, mode='constant', cval=0.0) rr, cc = np.where(output == 11) endpoints = [(x, y) for x, y in zip(rr, cc)] low_resolution_endpoints = find_low_resolution_endpoints( img, roi_grid_size) used_endpoints = [] for lre in low_resolution_endpoints: used_endpoints.append( sorted(endpoints, key=lambda x: (x[0] - lre[0])**2 + (x[1] - lre[1])**2)[0]) print(f'Used endpoints are: {used_endpoints}') finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path = [] for s, e in zip(used_endpoints, used_endpoints[1:]): path += [s] * (roi_grid_size + 1) grid = Grid(matrix=skel.T) path += finder.find_path(grid.node(*s), grid.node(*e), grid)[0][1:-1] path += [used_endpoints[-1]] * (roi_grid_size + 1) path = path[0::roi_grid_size + 1] path = np.array(path) rr, cc = np.split(path, 2, axis=-1) ep_frame = np.zeros(img.shape) ep_frame[rr, cc] = 1 kernel = np.ones((roi_grid_size, roi_grid_size)) ep_frame = ndimage.convolve(ep_frame, kernel, mode='constant', cval=0.0) plt.imshow(np.stack([ep_frame, img, np.zeros(img.shape)], axis=-1)) plt.xlabel('X') plt.ylabel('Y') return path
def findPath(from_v, to_v): x_f = int(from_v.x) y_f = int(from_v.y) x_t = int(to_v.x) y_t = int(to_v.y) oldPathFrom = pathMatrix[y_f][x_f] oldPathTo = pathMatrix[y_t][x_t] pathMatrix[y_f][x_f] = 1 pathMatrix[y_t][x_t] = 1 gridPath = Grid(matrix=pathMatrix) start_p = gridPath.node(x_f, y_f) end = gridPath.node(x_t, y_t) finder = AStarFinder( diagonal_movement=DiagonalMovement.if_at_most_one_obstacle) path, runs = finder.find_path(start_p, end, gridPath) print(gridPath.grid_str(start=start_p, end=end, path=path)) pathMatrix[y_f][x_f] = oldPathFrom pathMatrix[y_t][x_t] = oldPathTo return path
def findPath(self, startCoord, endCoord): matrix = (self.map == 0).T.astype(int) # note here that the matrix is transposed matrix[startCoord[1], startCoord[0]] = 1 matrix[endCoord[1], endCoord[0]] = 1 # print(matrix) grid = Grid(matrix=matrix.tolist()) start = grid.node(startCoord[0], startCoord[1]) end = grid.node(endCoord[0], endCoord[1]) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) path, runs = finder.find_path(start, end, grid) # print('operations:', runs, 'path length:', len(path)) # print(grid.grid_str(path=path, start=start, end=end)) p = myPath(startCoord, endCoord, len(path), path) return p
def path_find(self, start, end): grid = Grid(matrix=self.mapn) start_node = grid.node(start[0], start[1]) end_node = grid.node(end[0], end[1]) path, runs = AStarFinder( diagonal_movement=DiagonalMovement.never).find_path( start_node, end_node, grid) return path
def find_path_single_world( cls, world: CoordinateGrid, start_x_y: Tuple[float, float], end_x_y: Tuple[float, float]) -> Union[List[Tuple[float, float]], None]: """ world: the world split up in a grid with open cells and obstacle cells start: the (x, y) coordinates of the starting point in inches (IMPORTANT: Not row and column) end: the (x, y) coordinates of the ending point in inches (IMPORTANT: Not row and column) """ start = world.get_nearest_tile(start_x_y) end = world.get_nearest_tile(end_x_y) finder = AStarFinder( diagonal_movement=DiagonalMovement.only_when_no_obstacle) path, runs = finder.find_path(start, end, world) if len(path) == 0: print("Warning unable to find PATH!!!!") return None return world.col_row_to_x_y_coordinates(path)[1:] + [end_x_y]
def Steps(self, obstacleClass=(Rock, Dirt, Ant)): matrix = [] for i in range(len(self.maps[0])): matrix.append([]) for j in range(len(self.maps[0][0])): matrix.append(0 if any( [isinstance(m[i][j], obstacleClass) for m in self.maps]) else 1) grid = Grid(matrix=matrix) start = grid.node(self.start.x, self.start.y) end = grid.node(self.dest.x, self.dest.y) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) path, runs = finder.find_path(start, end, grid) return [Point(n.x, n.y) for n in path]
def create_hallways(Map, regions): # Crea caminos que unan todas las regiones, sin ciclos # (MaxST con cada region como un nodo y su camino una arista) # print(f'len regions {len(regions)}') # Crear el objeto AStarFinder() finder = AStarFinder() first_region = regions[0] regions_visited = [first_region] regions_unresolved = regions[1:] # hallways = [] Map_ones = np.ones((len(Map), len(Map[0])), dtype=int) while regions_unresolved: grid = Grid(matrix=Map_ones) region_u = regions_visited.pop() region_v = regions_unresolved.pop() regions_visited.append(region_v) # Hasta aqui tenemos la region u y la region v, queremos hallar un camino que las una # Escogemos cualquier casilla de u y cualquiera de v y encontramos el camino minimo # que las una, con 'finder.find_path'. _u = region_u[0] _v = region_v[0] # Guardar las casillas u, v como nodos u = grid.node(_u[0], _u[1]) v = grid.node(_v[0], _v[1]) path, _ = finder.find_path(u, v, grid) # hallways.append(path) # Actualizar el mapa con los nuevos caminos for _grid in path: x = _grid[0] y = _grid[1] Map[y][x] = 0
def is_playable(start_end: Dict, room_matrix: List[List[int]], print_path: bool=False) -> bool: # TODO: invesgate # if len(start_end) < 2: # playable = True # return playable all_start_end = [] for k in start_end: all_start_end = all_start_end + start_end[k] grid = Grid(matrix=room_matrix) comb = list(combinations(all_start_end, 2)) playable = True # random.shuffle(comb) for _start, _end in comb: # print(_start, _end) # hardcode here # if (_start in start_end["D"] and _end in start_end["D"]) or (_start in start_end["S"] and _end in start_end["S"]): # abs_dis = abs(_start[0] - _end[0]) + abs(_start[1] - _end[1]) # if abs_dis < 4: # continue start = grid.node(_start[1], _start[0]) end = grid.node(_end[1], _end[0]) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) path, runs = finder.find_path(start, end, grid) if print_path and len(path) > 0: print('operations:', runs, 'path length:', len(path)) print(grid.grid_str(path=path, start=start, end=end)) grid.cleanup() if len(path) == 0: playable = False break return playable
def calc_path(data,q,grid): print('initiation') units={} for i in data: finder = AStarFinder(diagonal_movement=DiagonalMovement.never) table=Grid(matrix=grid) start=table.node(data[i][0][0],data[i][0][1]) end=table.node(data[i][1][0],data[i][1][1]) path, runs = finder.find_path(start, end, table) #units.append([i,path]) units[i]=path q.put(units)
def pathfind(blocks, less=False): matrix = np.ones((SIZE_X, SIZE_Y)) for i in blocks: matrix[i[0]][i[1]] = 0 matrix[0][10] = 1 matrix[0][9] = 1 matrix[19][10] = 1 matrix[19][9] = 1 grid = Grid(matrix=matrix) start = grid.node(10, 0) end = grid.node(10, 19) if less: finder = AStarFinder(diagonal_movement=1) else: finder = AStarFinder(diagonal_movement=2) (path, runs) = finder.find_path(start, end, grid) return path
def path(start, end, tmx, layers=None): # Create matrix for A* to read if not layers: layers = [0, 1] matrix = [[0] * tmx.width for row in range(0, tmx.height)] for layer in layers: for row in range(0, tmx.height): for column in range(0, tmx.width): tile = tmx.get_tile_properties(column, row, layer) if tile: if tile['wall'] == 'true': matrix[column][row] = 1 # Calculate path and return list of tiles along route grid = Grid(matrix=matrix) start = grid.node(*start) # format (30, 30) end = grid.node(*end) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, grid) return path
def generate(self): while True: self.maze = np.zeros([self.height, self.width]) for i in range(self.height): for j in range(self.width): if np.random.rand() <= self.obstacle_occupancy_prob: self.maze[i][j] = 1 temp_maze = 1 - copy.deepcopy(self.maze) grid = Grid(matrix=temp_maze) start = grid.node(self.agent_pose[0], self.agent_pose[1]) end = grid.node(self.target_pose[0], self.target_pose[1]) finder = AStarFinder(diagonal_movement=DiagonalMovement.always) path, runs = finder.find_path(start, end, grid) if len(path) > 0: break self.maze = 3 * self.maze self.maze[self.agent_pose[0]][self.agent_pose[1]] = 1 self.maze[self.target_pose[0]][self.target_pose[1]] = 2 self.top_bottom_wall = 3 * np.ones([1, self.maze.shape[1]]) self.maze = np.vstack( (self.top_bottom_wall, self.maze, self.top_bottom_wall)) self.left_right_wall = 3 * np.ones([self.maze.shape[0], 1]) self.maze = np.hstack( (self.left_right_wall, self.maze, self.left_right_wall)) self.target_pose = [self.target_pose[0] + 1, self.target_pose[1] + 1] self.agent_pose = [self.agent_pose[0] + 1, self.agent_pose[1] + 1] return self.maze, self.agent_pose, self.target_pose, path
def bfs(matrix, start, obstacles, target): new_matrix = [] for i, row in enumerate(matrix): new_matrix.append([]) for j, col in enumerate(row): if matrix[i][j] in obstacles: new_matrix[i].append(-1) else: new_matrix[i].append(1) grid = Grid(matrix=new_matrix) start = grid.node(start.x, start.y) end = grid.node(target.x, target.y) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) path, runs = finder.find_path(start, end, grid) # print('operations:', runs, 'path length:', len(path)) # print(grid.grid_str(path=path, start=start, end=end)) return list(map(lambda p: Point(p[0], p[1]), path))
def __init__(self, source: Union[str, TextIO]): """ Reads a new map from a file or a string :param source: the file-like object or string describing the map (not a filename!) """ if isinstance(source, str): input_string = source else: input_string = source.read() lines = input_string.splitlines() width, height, nCustomers, self.replyOffices = tuple(map(int,lines[0].split(' '))) self.customers = [] for line in lines[1:1 + nCustomers]: x, y, reward = map(int,line.split(' ')) self.customers.append(Map.Customer((x,y),reward)) cost_matrix = [] for line in lines[nCustomers + 1:] : cost_matrix.append([costs[char] for char in line]) self.grid = Grid(width, height, cost_matrix) self.finder = AStarFinder()
def create_waypoints(matrix, y_end, x_end): coords = [[0, 0], [3.5, 2.5], [3.5, 10.5], [3.5, 22.5], [10.5, 22.5], [20, 22.5], [34.5, 22.5], [34.5, 12], [34.5, 2.5], [19.5, 2.5], [19.5, 10.5]] reversed_test_maze = reverse.reverseZeroOne(matrix) grid = Grid(matrix=reversed_test_maze) start = grid.node(0, 0) end = grid.node(y_end, x_end) finder = AStarFinder(diagonal_movement=DiagonalMovement.never) path, runs = finder.find_path(start, end, grid) print(grid.grid_str(path=path, start=start, end=end)) for i in range(len(path)): (x, y) = path[i] path[i] = (y + 0.5, x + 0.5) return path
class ZombieDriver (Random): ''' The ZombieDriver AI would follow the player relentlessly around without any thought or self preservation mechanism! ''' fire_chance = 3 moving_chance = 80 turning_chance = 5 def __init__(self, world): self.world = world self.map = world.map self.pathfinder = AStarFinder( diagonal_movement=DiagonalMovement.never, time_limit=0.006 ) self.path_cache_duration = 1000 def tick(self, deltat, enemies): interesting_objects = [] for player in self.world.players: if player.tank is None: continue interesting_objects.append((player.tank.rect, 1)) for flag in self.world.un_flags: interesting_objects.append((flag.rect, 1.5)) for enemy in enemies: self.process_enemy(enemy, deltat, interesting_objects) self.fire_at_will(enemy, deltat, interesting_objects) def process_enemy(self, enemy, deltat, interesting_objects): enemy.update_path_duration(deltat) own_coords = enemy.rect own_grid_pos = self.map.world_to_grid_coords(own_coords.centerx, own_coords.centery) if enemy.path_duration <= self.path_cache_duration: self.continue_path(enemy, own_grid_pos) return heat_obj = None heat_weight = 0 for obj, weight in interesting_objects: dist = math.sqrt( abs(own_coords.centerx - obj.centerx) ** 2 + abs(own_coords.centery - obj.centery) ** 2 ) obj_weight = (1 / dist) * weight if obj_weight > heat_weight: heat_weight = obj_weight heat_obj = obj if heat_obj is None: logging.error("ZombieDriver AI finds nothing interesting on the map! How can that be!?") self.do_random_thing(enemy) return hobjx, hobjy = heat_obj.centerx, heat_obj.centery hobj_grid_pos = self.map.world_to_grid_coords(hobjx, hobjy) if hobj_grid_pos == enemy.current_target: enemy.reset_path_duration() self.continue_path(enemy, own_grid_pos) return # WTF!? You have to rebiuld the *whole* grid every time find_path is called! # Definately change to a better library or write A* myself. self.map.build_grid() pf_start = self.map.grid.node(*own_grid_pos) pf_end = self.map.grid.node(*hobj_grid_pos) path, runs = self.pathfinder.find_path( pf_start, pf_end, self.map.grid ) enemy.set_current_path(path) self.continue_path(enemy, own_grid_pos) def fire_at_will(self, enemy, deltat, interesting_objects): if len(enemy.bullets) >= enemy.max_bullets: return for obj, weight in interesting_objects: if not enemy.is_facing(self.map, obj): continue firing_roll = random.randint(0, 100) if firing_roll < self.fire_chance: enemy.fire() def continue_path(self, enemy, grid_pos): next_node = enemy.get_path_next(grid_pos) if next_node is None: self.do_random_thing(enemy) return direction = self.map.grid_direction(grid_pos, next_node) if direction == DIRECTION_NONE: logging.error("For some reason the calculate direction returned NONE after pathfinding") self.do_random_thing(enemy) else: enemy.move(direction)