Exemplo n.º 1
0
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
Exemplo n.º 2
0
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

        self.sample_radius = 100
        self.k = 0.1
        self.field = PotentialFieldsCalculator(self.grid, self.sample_radius, 80.0, 0.0, 0.3, 0.45)

    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:
            # update the grid
            self.grid.update(self.bzrc.get_occgrid(tank.index))

            # move
            self.create_command(tank, time_diff)

        self.grid.draw_grid()

        results = self.bzrc.do_commands(self.commands)

    def create_command(self, tank, time_diff):
        """Set command to move to given coordinates."""

        #pprint (vars(tank))

        speed = 1
        angvel = 0
        shoot = True
        kp = 1
        kd = -.07

        if time_diff <= 0:
            return

        # get potential field at this location and add to enemy coords
        x, y = self.field.calculate_potential(tank.x, tank.y)

        # PD Controller - angle
        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

        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

        #print "speed", speed

        #self.printSurrounding(tank.x, tank.y, 3)

        command = Command(tank.index, speed, angvel, shoot)
        self.commands.append(command)
        self.old_angle[tank.index] = tank.angle

    def printSurrounding(self, x, y, radius):
        print ':::New Grid:::', x, y
        surroundingGrid = self.grid.getSurroundings(x, y, radius)
        for x in range(len(surroundingGrid)):
            print surroundingGrid[x]

    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

    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