def __init__(self, bzrc): self.bzrc = bzrc self.constants = self.bzrc.get_constants() self.commands = [] mytanks, othertanks, flags, shots = self.bzrc.get_lots_o_stuff() self.numoftanks = len(mytanks) self.timeuntilshot = [0]*self.numoftanks self.ismoving = [True]*self.numoftanks # Create Grid of points truepositive = float(self.constants['truepositive']) truenegative = float(self.constants['truenegative']) self.grid = Grid(800, 800, truepositive, truenegative) self.grid.init_window(800, 800) # For PD Control self.old_angle = [0]*self.numoftanks self.old_speed = [0]*self.numoftanks # For graph self.final_goals = [0]*self.numoftanks self.graph = OccupancyGraph(self.grid) self.current_goals = [None]*self.numoftanks self.goal_paths = [] for i in xrange(self.numoftanks): self.goal_paths.append(Path())
class Agent(object): """Class handles all command and control logic for a teams tanks.""" def __init__(self, bzrc): self.bzrc = bzrc self.constants = self.bzrc.get_constants() self.commands = [] mytanks, othertanks, flags, shots = self.bzrc.get_lots_o_stuff() self.numoftanks = len(mytanks) self.timeuntilshot = [0]*self.numoftanks self.ismoving = [True]*self.numoftanks # Create Grid of points truepositive = float(self.constants['truepositive']) truenegative = float(self.constants['truenegative']) self.grid = Grid(800, 800, truepositive, truenegative) self.grid.init_window(800, 800) # For PD Control self.old_angle = [0]*self.numoftanks self.old_speed = [0]*self.numoftanks # For graph self.final_goals = [0]*self.numoftanks self.graph = OccupancyGraph(self.grid) self.current_goals = [None]*self.numoftanks self.goal_paths = [] for i in xrange(self.numoftanks): self.goal_paths.append(Path()) def tick(self, time_diff): """Some time has passed; decide what to do next.""" mytanks, othertanks, flags, shots = self.bzrc.get_lots_o_stuff() self.mytanks = mytanks self.commands = [] for tank in mytanks: occGrid = self.bzrc.get_occgrid(tank.index) # update the grid self.grid.update(occGrid) # update the graph self.graph.updateGraph(occGrid[0][0], occGrid[0][1], len(occGrid[1]), len(occGrid[1][0])) # move self.create_command(tank, time_diff) self.grid.draw_grid() results = self.bzrc.do_commands(self.commands) def attack_enemies(self, tank): """Find the closest enemy and chase it, shooting as you go.""" best_enemy = None best_dist = 2 * float(self.constants['worldsize']) for enemy in self.enemies: if enemy.status != 'alive': continue dist = math.sqrt((enemy.x - tank.x)**2 + (enemy.y - tank.y)**2) if dist < best_dist: best_dist = dist best_enemy = enemy if best_enemy is None: command = Command(tank.index, 0, 0, False) self.commands.append(command) else: self.move_to_position(tank, best_enemy.x, best_enemy.y) def move_to_position(self, tank, target_x, target_y): """Set command to move to given coordinates.""" target_angle = math.atan2(target_y - tank.y, target_x - tank.x) relative_angle = self.normalize_angle(target_angle - tank.angle) command = Command(tank.index, 1, 2 * relative_angle, True) self.commands.append(command) def create_command(self, tank, time_diff): """Set command to move to given coordinates.""" speed = 1 angvel = 0 shoot = True kp = 1 kd = -.07 goal_threshold = 15.0 goal_timer_threshold = 15 if time_diff <= 0: return # see if we need a new visibility graph and path goal = self.goal_paths[tank.index].get_next(tank.x, tank.y, goal_threshold) if (self.current_goals[tank.index] == None): self.current_goals[tank.index] = (goal, 0.0) current_goal = self.current_goals[tank.index][0] goal_timer = self.current_goals[tank.index][1] if goal is None or ( current_goal == goal and goal_timer > goal_timer_threshold ): self.final_goals[tank.index] = self.findFogOfWar(tank.x, tank.y, 800, 800) start = (tank.x, tank.y) end = self.final_goals[tank.index] search_alg = search.AStar(self.graph) new_path = search_alg.run(start, end) #self.graph.displayGraph() self.goal_paths[tank.index].set_path(new_path) goal = self.goal_paths[tank.index].get_next(tank.x, tank.y, goal_threshold) elif current_goal != goal: self.current_goals[tank.index] = (goal, 0.0) elif goal_timer < goal_timer_threshold: self.current_goals[tank.index] = (goal, goal_timer + time_diff) if goal is not None: # PD Controller - angle x = goal[0] - tank.x y = goal[1] - tank.y target_angle = math.atan2(y, x) angle_remaining = self.angle_remaining(tank.angle, target_angle) angvel = self.pd_angvel(tank, target_angle, time_diff) if angvel > 1: angvel = 1 elif angvel < -1: angvel = -1 # PD Controller - speed if abs(angle_remaining) <= math.pi / 6: speed = self.pd_speed(tank, x, y, time_diff) else: speed = 1 - abs(angle_remaining / math.pi) if speed > 1: speed = 1.0 elif speed < -1: speed = -1.0 command = Command(tank.index, speed, angvel, shoot) self.commands.append(command) self.old_angle[tank.index] = tank.angle def pd_angvel(self, tank, target_angle, time_diff): """PD Controller for the angular velocity of the tank.""" kp = 1.0 kd = -0.2 angle_remaining = self.angle_remaining(tank.angle, target_angle) differential = self.angle_remaining(self.old_angle[tank.index], tank.angle) / time_diff angvel = ( kp * angle_remaining ) + ( kd * differential ) return angvel def pd_speed(self, tank, target_x, target_y, time_diff): """PD Controller for the speed of the tank.""" """ kp = 1.0 kd = -0.2 x_remaining = target_x - tank.x y_remaining = target_y - tank.y distance_remaining = math.sqrt( ( x_remaining ) ** 2 + ( y_remaining ) ** 2 ) current_speed = math.sqrt( tank.vx ** 2 + tank.vy ** 2 ) differential = current_speed - self.old_speed[tank.index] / time_diff speed = ( kp * distance_remaining ) + ( kd * differential ) return speed """ speed = 1.0 return speed def angle_remaining(self, tank_angle, target_angle): """Find the angle remaining (in radians) between the tank and the target.""" tank_angle = self.normalize_angle(tank_angle) target_angle = self.normalize_angle(target_angle) # If the angles are on the same hemisphere, target - tank. if tank_angle * target_angle > 0: return target_angle - tank_angle # Otherwise they are on opposite hemispheres. positive_angle = max(tank_angle, target_angle) negative_angle = min(tank_angle, target_angle) tank_positive = True if tank_angle < 0: tank_positive = False # Compare the angles to turn right and left. right_angle = -1 * ( positive_angle - negative_angle ) left_angle = 2 * math.pi - positive_angle + negative_angle if not tank_positive: temp_angle = -1 * right_angle right_angle = -1 * left_angle left_angle = temp_angle # Pick the smallest angle. if abs(right_angle) <= abs(left_angle): return right_angle return left_angle def normalize_angle(self, angle): """Make any angle be between +/- pi.""" angle -= 2 * math.pi * int (angle / (2 * math.pi)) if angle <= -math.pi: angle += 2 * math.pi elif angle > math.pi: angle -= 2 * math.pi return angle def findFogOfWar(self, initialX, initialY, width, height): k_choosePoint = 0.001 x, y = int(initialX), int(initialY) steps = 0 fowX, fowY = None, None direction = 0 while fowX is None or fowY is None: if direction == 0: startX, startY = x, y if direction % 2 == 0: steps += 1 x, y = self.moveInDirection(steps, direction, x, y, width, height) if self.isFogOfWar(x, y) and random.random() < k_choosePoint: fowX, fowY = x, y direction += 1 if direction > 3: direction = 0 if startX == x and startY == y: break return (fowX, fowY) def moveInDirection(self, steps, direction, x, y, width, height): for step in xrange(steps): if direction == 0: if x + 1 < width / 2: x += 1 elif direction == 1: if y + 1 < height / 2: y += 1 elif direction == 2: if x - 1 > -1 * width / 2: x -= 1 elif direction == 3: if y - 1 > -1 * height / 2: y -= 1 return x, y def isFogOfWar(self, x, y): pOcc = self.grid.getAt(x, y).probabilityOccupied #if 0.4 < pOcc and pOcc < 0.6: if 0.5 == pOcc: return True return False