from queue import PriorityQueue from enum import Enum from battle import commander from battle import ROLE class Map(object): def __init__(self, enemy_items, diagonal_movement=True): self.diags = diagonal_movement self.width = 20 self.height = 40 self._data = [[TILE.EMPTY for y in range(self.height)]for x in range(self.width)] self.populate_map(enemy_items) self.evil_callback = 0 def populate_map(self, enemy_items): valid = lambda w: any([isinstance(w, int), isinstance(w, float)]) for entity in enemy_items: size = entity.get('size', 0) x,y = entity['coordinates'] role = entity['role'] if x+size>=self.width or y+size>=self.height: self.extend_map((x+size,y+size)) if not valid(x) or not valid(y): self.evil_callback+=1 continue x = int(x) y = int(y) for x0, y0 in self.get_rect_from_center((x,y), size): self._data[x0][y0] = MappedRoles.get(role, TILE.UNKNOWN) def extend_map(self,point): w,h = point if h>self.height: for y in self._data: y.extend([TILE.EMPTY for _ in range(h-self.height)]) self.height = h if w>self. for _ in range(w-self.width): self._data.append([TILE.EMPTY for _ in range(self.height)]) self.width = w def find_path(self, start, end, size=1, heuristic=None): """ A* """ if not heuristic: heuristic = lambda x,y:abs(x-end[0])+abs(y-end[1]) dest = set(self.get_rect_from_center(end, size)) start = tuple(start) fringe = PriorityQueue() fringe.put(start, 0) total_cost = {start:0} origin = {start:None} while not fringe.empty(): current = fringe.get() if current in dest: break cost = total_cost[current]+1 nodes = self.get_adjacent(current[0], current[1]) for node in nodes: x,y = node node_type = self._data[x][y] if (node_type == TILE.EMPTY or node in dest) and (node not in total_cost or cost < total_cost[node]): total_cost[node] = cost origin[node] = current fringe.put(node, cost+heuristic(x, y)) else: return [] path = [] n = current while n is not None: path.append(n) n = origin[n] path.reverse() print(path) return path def update(self, info): self.init_map(info) def get_bounds(self, x0,y0,x1,y1): xmin, xmax = (max(0, x0), min(self.width, x1)) ymin, ymax = (max(0, y0), min(self.height, y1)) return(xmin,ymin, xmax,ymax) def get_rect_from_center(self, pos, size): ## dist 0: single-point if size < 2: ## and single-tile bulidings are return [pos] ## handled in the same manner as of now even = size %2 == 0 dist = int(size/2) px,py = pos if even: ##x/ymax will be exclusive in the for loop! xmin, ymin, xmax, ymax = self.get_bounds(px-dist+1, py-dist+1, px+dist+1, py+dist+1) else: xmin, ymin, xmax, ymax = self.get_bounds(px-dist, py-dist, px+dist+1, py+dist+1) print('for {} dist:{} we get a rect ({})'.format(pos, size, (xmin, ymin, xmax-1, ymax-1))) cells = [] for x in range(xmin, xmax): for y in range(ymin, ymax): cells.append((x,y)) return cells def get_rect(self, top_left, size): cells = [] x0,y0 = top_left xmin,ymin, xmax,ymax = self.get_bounds(x0,y0,x0+size, y0+size) for x in range(xmin, xmax): for y in range(ymin, ymax): cells.append((x,y)) return cells def get_adjacent(self, x0,y0, diag = True): """ Returns a list of tuples of coords adjacent to (x0,y0) 7 8 9 4 @ 6 1 2 3 """ res =[] if x0>0: res.append((x0-1,y0)) # 4 if y0>0 and diag: res.append((x0-1,y0-1)) # 7 if y0 < self.height-1 and diag: res.append((x0-1,y0+1)) # 1 if y0>0: res.append((x0,y0-1)) # 8 if x0 < self.width-1: res.append((x0+1,y0)) # 6 if y0 < self.height-1 and diag: res.append((x0+1,y0+1)) # 3 if y0>0 and diag: res.append((x0+1,y0-1)) # 9 if y0 < self.height-1: res.append((x0,y0+1)) # 2 return [tuple(x) for x in res] def get_info(self): for x in range(self.width): for y in range(self.height): a = self._data[x][y] if a != TILE.EMPTY : print("[{},{}],{}".format(x,y,a)) def __getitem__(self, index): return self._data[index] class Brain(object): """ (0,0) is located in the northen corner of the map. X N->SE Y N-SW """ def __init__(self,client, init_terrain=True): self.me = client self.update() if init_terrain: self.terrain = Map(self.me.ask_enemy_items()) def update(self): info = self.me.ask_my_info() self.fire_range = info['firing_range'] self.pos = info['coordinates'] self.size = info.get('size', 0) def goto(self, pos, size = 1): return self.terrain.find_path(self.pos, pos, size) def midpoint(p1, p2): return ((p1[0]+p2[0])/2, (p1[1]+p2[1])/2) def dist(p1, p2): x,y = p1 ox,oy = p2 return pow((ox-x)*(ox-x) + (oy-y)*(oy-y), 1/2) def search_role(array,role): for e in array: if e['role'] == role: return True return False def search_and_destroy(data=None, *args, **kawargs): brain.update() center = unit.ask_center() cpos = center['coordinates'] assert brain.terrain[cpos[0]][cpos[1]] == TILE.CENTER, 'CENTER NOT FOUND' mypos = unit.ask_my_info()['coordinates'] if center["hit_points"]>0: if dist(cpos, mypos) < brain.fire_range: print('attacking center') unit.attack_item(center['id']) unit.subscribe_the_item_is_dead(center['id'], search_and_destroy) else: print('walking') p = path.pop() print(p) unit.move_to_point(p) unit.do_move(midpoint(cpos, mypos)) unit.subscribe_im_idle(search_and_destroy) else: print('attacking other') eid= unit.ask_nearest_enemy()['id'] unit.attack_item(eid) unit.subscribe_the_item_is_dead(eid, search_and_destroy) unit.move_to_point((mypos+[0,brain.terrain.evil_callback])) ####### TILE = Enum('TileContent', 'EMPTY UNIT ROCK BUILDING TURRET CENTER UNKNOWN') STATE = Enum("State", "ATTACKING FLEEING MOVING") MappedRoles = { ROLE.UNIT:TILE.UNIT, ROLE.BUILDING:TILE.BUILDING, ROLE.OBSTACLE:TILE.ROCK, ROLE.TOWER:TILE.TURRET, ROLE.CENTER:TILE.CENTER } if __name__ == "__main__": unit = commander.Client() brain = Brain(unit, True) cx,cy=unit.ask_center()['coordinates'] path = brain.goto((cx,cy),4) path.reverse() brain.terrain.get_rect_from_center((2,2),0) brain.terrain.get_rect_from_center((2,2),1) brain.terrain.get_rect_from_center((2,2),-1) brain.terrain.get_rect_from_center((2,2),2) brain.terrain.get_rect_from_center((5,5),3) search_and_destroy()