Exemple #1
0
 def __init__(self, world):
     self._world = world
     self._communicate = Controller()
     self._kick_off = False
Exemple #2
0
class Task(object):
    def __init__(self, world):
        self._world = world
        self._communicate = Controller()
        self._kick_off = False

    @property
    def world(self):
        return self._world

    @world.setter
    def world(self, world):
        self._world = world

    """
    Strategies
    """

    def task_defender_kick_off(self):
        print ("Please kick off")
        if self.task_kick_off():
            print ("kick off completed")
            return self.task_defender()
        else:
            return False

    def task_defender(self):
        """
        Robot will strive to stay in the defending region, avoid other robots, and intercept ball.
        Once it has the ball, it shall pass to teammate.
        If the ball is in the attacking region, it shall just protect the goal and wait.
        """

        # while the ball is with us, just go to it. When you've got it, pass to teammate
        if self.ball_in_defender_region():
            print ("Ball in defender region")
            # if the ball is in the attacker region as well, we need to check who's closer - attacker or us
            if self.ball_in_attacker_region() and not self.are_we_closer_than_teammate():
                print ("Ball is in the attacker region as well, and teammate is closer: sit and wait for the ball")
                self.task_sit_between_goal_and_ball()

            # we're good to go, get ball and kick to teammate
            self.task_grab_rotate_kick()
        # the ball is in just the attacker region, our task is to be half way between the ball and the goal
        else:
            print "Ball is not in defender region, sit and wait for ball"
            self.task_sit_between_goal_and_ball()

        # always return false, this means this task will keep running
        return False

    def task_kick_off(self):
        """
        Robot will kick the ball to kick off position
        """
        # assume we have the ball already in grabber and grabbers close
        if not self._kick_off:
            # This is if we're shooting right to left
            # kick_off_x = self.world.defender_region.left - 15
            # kick_off_y = self.world.pitch_boundary_top - 10
            # this is if we're shooting left to right
            if self._world.our_side == "right":
                kick_off_x = self.world.defender_region.right + 15
                kick_off_y = self.world.pitch_boundary_bottom - 10
            else:
                kick_off_x = self.world.defender_region.left - 15
                kick_off_y = self.world.pitch_boundary_top - 10

            if self.rotate_to_alignment(kick_off_x, kick_off_y):
                if self.ungrab_ball():
                    # kick ball to teammate
                    distance = self._world.our_robot.get_displacement_to_point(kick_off_x, kick_off_y)
                    self._kick_off = True
                    return self.kick_ball(distance_to_kick=distance)
            return False
        else:
            return True

    def task_attacker(self):
        """
        Robot will not enter defending region. It will stay in attacking region.
        If the ball is in the defending region (which it cannot enter), it will wait to receive the ball from teammate.
        If the ball is in the attacking region, it will strive to grab the ball.
        Once it has the ball, it will look to shoot it in the goal.
        """
        self.task_grab_rotate_kick_in_goal()

        # always return false, this means this task will keep running
        return False

    def task_attacker_kick_off(self):
        print ("Please kick off")
        if self.task_kick_off():
            print ("kick off completed")
            return self.task_attacker()
        else:
            return False

    def task_penalty(self):
        """
        Robot will aim to take a penalty, playing by the penalty rules. Assumes robot is holding ball
        """
        # wait a random amount of time
        random_number = randint(1, 5)
        time.sleep(random_number)
        x = self.world.their_goal.x
        y = self.world.their_goal.y - 30

        if self.rotate_to_alignment(x, y):
            return self.task_kick_ball_in_goal()
        return False

    def task_penalty_goalie(self):
        """
        Robot will be goalie for penality. Robot will randomly open grabbers, close them, while facing the ball
        """
        if self.rotate_to_ball():
            if self.ungrab_ball():
                time.sleep(20)
                self.grab_ball()

        return False

    """
    Big tasks; these are made up of smaller tasks
    """

    def task_vision(self):
        pass

    def task_rotate_and_grab(self):
        # rotate to face the ball
        if self.rotate_to_ball():
            # wait till ball has stopped
            if self._world.ball.speed < 5:
                # move to the ball with grabbers open
                if self.ungrab_ball():
                    if self.task_move_to_ball():
                        if self.grab_ball():
                            return self.ball_received()
        return False

    # Assuming we're facing the right direction
    def task_grab_rotate_kick(self):
        """
        Opens grabbers and moves to the ball, grabs it, rotates to teammate, kicks it to teammate.
        Does not check if ball is received by teammate
        """
        print ("Go grab the ball, kick it to teammate")
        # if self.ungrab_ball():
        if self.rotate_to_ball():
            #
            if self.move_to_ball():
                # if self.ungrab_ball():
                if self.ball_received():
                    # grab the ball we've just be given
                    if self.grab_ball():
                        # rotate to face the other robot
                        if self.rotate_to_alignment(self._world.teammate.x, self._world.teammate.y):
                            if self.ungrab_ball():
                                # kick ball to teammate
                                distance = self._world.our_robot.get_displacement_to_point(
                                    self._world.teammate.x, self._world.teammate.y
                                )
                                return self.kick_ball(distance_to_kick=distance)

        return False

    def task_grab_rotate_kick_in_goal(self):
        """
        Opens grabbers and moves to the ball, grabs it, rotates to goal, kicks it to goal.
        Does not check if ball is received by teammate
        """
        print ("Go grab the ball, kick it to goal")
        # if self.ungrab_ball():
        if not self.ball_received():
            print ("ball not received, go to ball")
            if self.rotate_to_ball():
                #
                self.move_to_ball()

        else:
            if self.grab_ball():
                print ("ball received. kick in goal")
                # rotate to face the other robot
                if self.rotate_to_alignment(self.world.their_goal.x, self.world.their_goal.y):
                    if self.ungrab_ball():
                        # kick ball to teammate
                        distance = self._world.our_robot.get_displacement_to_point(
                            self.world.their_goal.x, self.world.their_goal.y
                        )
                        return self.kick_ball(distance_to_kick=distance)

        return False

    def task_move_to_ball(self):
        # If the ball isn't moving, we can just move to it
        if self._world.ball.speed < 5:
            if self.rotate_to_ball():
                return self.move_to_ball()
            else:
                return False
        # If the ball IS moving, we need to predict where it's going and move there...
        else:
            # move to predicted stopping point, but only when the ball is deceleration
            if self.world.ball.acceleration[0] < 0:
                predicted_x = self.world.ball.predicted_stopping_coordinates_x
                predicted_y = self.world.ball.predicted_stopping_coordinates_y

                if self.rotate_to_alignment(predicted_x, predicted_y):
                    return self.move_to_coordinates(predicted_x, predicted_y)
            return False

    def task_move_and_grab_ball(self):
        # If we're happy with rotation and movement, grab the ball
        if self.rotate_to_ball():
            if self.ungrab_ball():
                if self.move_to_ball():
                    return self.grab_ball()
        return False

    def task_kick_ball_in_goal(self):
        # If we're happy with rotation to face goal, ungarb and kick the ball
        if self.rotate_to_alignment(self.world.their_goal.x, self.world.their_goal.y):
            if self.ungrab_ball():
                return self.kick_ball()
        return False

    def task_sit_between_goal_and_ball(self):
        # work out co-ordinates half way between goal and ball
        midpoint_x, midpoint_y = helper.calculate_midpoint(
            self.world.our_goal.x, self.world.our_goal.y, self.world.ball.x, self.world.ball.y
        )

        print (
            "our goal x, our goal y, ball x, ball y",
            self.world.our_goal.x,
            self.world.our_goal.y,
            self.world.ball.x,
            self.world.ball.y,
        )
        if self.rotate_to_alignment(midpoint_x, midpoint_y):
            if self.move_to_coordinates(midpoint_x, midpoint_y):
                return self.ungrab_ball()
        return False

    """
    Smaller tasks
    """

    def move_to_coordinates(self, x, y):
        """
        Given a specific robot, it will try and move this robot to a given co-ordinate, assuming it is facing the correct way
        already
        :param target_vector
        """
        print ("Move to coordinates x, y", x, y)

        # Calculate how long we need to run the motor for
        distance = self._world.our_robot.get_displacement_to_point(x, y)

        # are we gonna hit anyone in this time
        safety_check = self.safety_check(x, y)
        if safety_check:
            if distance < 40:
                return True
            else:
                calculated_duration = self.calculate_motor_duration(distance)

                # Tell arduino to move for the duration we've calculated
                self._communicate.move_duration(calculated_duration)

                # Wait until this task has completed
                if calculated_duration > 0:
                    time.sleep(calculated_duration / 1000)
                # Returns false which means we'll get more data from vision first, run this function again, to verify ok
                return False
        else:
            print ("Safety check failed -> routing to the nearest possible edge")
            return self.move_to_coordinates(safety_check[0], safety_check[1])

    def rotate_to_ball(self):
        print ("Rotate to ball")
        return self.rotate_to_alignment(self._world.ball.x, self._world.ball.y)

    def move_to_ball(self):
        return self.move_to_coordinates(self._world.ball.x, self._world.ball.y)

    def rotate_to_alignment(self, x, y):
        """
        Given a specific robot, it will rotate to face a specific angle

        :param x:
        :param y:
        """
        angle_to_rotate = self._world.our_robot.get_rotation_to_point(x, y)
        distance = self._world.our_robot.get_displacement_to_point(x, y)

        # If the angle of rotation is less than 15 degrees, leave it how it is
        if (
            (15 >= angle_to_rotate >= -15 and distance > 40)
            or (5 >= angle_to_rotate >= -5 and distance <= 40)
            or (25 >= angle_to_rotate >= -25 and distance <= 30)
        ):
            return True
        else:
            duration = self.calculate_motor_duration_turn(angle_to_rotate)
            wait_time = self._communicate.turn(duration)
            time.sleep(abs(wait_time))
            # Returns false which means we'll get more data from vision first, run this function again, to verify ok
            return False

    def ungrab_ball(self):
        # if the grabbers are already open, don't do anything
        print ("Ungrab ball")
        # if not self.world.our_robot.grabbers_open:
        print "Grabbers aren't open, please open them"
        wait_time = self._communicate.ungrab()
        time.sleep(wait_time)
        self.world.our_robot.grabbers_open = True
        return True

    def grab_ball(self):
        # if the grabbers are already closed, don't do anything
        print ("Grab ball")
        # if self.world.our_robot.grabbers_open:
        wait_time = self._communicate.grab()
        time.sleep(wait_time)
        self.world.our_robot.grabbers_open = False
        return True

    def kick_ball(self, distance_to_kick=None):
        print ("Kick ball")
        if distance_to_kick:
            power = self.calculate_kick_power(distance_to_kick)
        else:
            power = 1

        wait_time = self._communicate.kick(power)
        time.sleep(wait_time)
        return True

    """
    Helper methods
    """

    def are_we_closer_than_teammate(self):
        distance_for_us = self.world.our_robot.get_displacement_to_point(self.world.ball.x, self.world.ball.y)
        distance_for_teammate = self.world.teammate.get_displacement_to_point(self.world.ball.x, self.world.ball.y)

        if distance_for_us > distance_for_teammate:
            return False
        else:
            return True

    def ball_received(self):
        # calculate displacement from us to ball
        distance = self._world.our_robot.get_displacement_to_point(self._world.ball.x, self._world.ball.y)
        print (" we are ", distance, " away from ball")
        if distance < 33:
            return True
        else:
            return False

    def ball_received_by_teammate(self):
        # calculate displacement from us to ball
        distance = self._world.teammate.get_displacement_to_point(self._world.ball.x, self._world.ball.y)

        if distance < 50:
            return True
        else:
            return False

    def ball_in_defender_region(self):
        if self._world.defender_region.contains(self._world.ball.x, self._world.ball.y):
            return True
        else:
            return False

    def ball_in_attacker_region(self):
        if self._world.attacker_region.contains(self._world.ball.x, self._world.ball.y):
            return True
        else:
            return False

    def safety_check(self, resultant_x, resultant_y):
        """
        Before any movement is called, this is called. This essentially checks if the movement we're about to do will hit someone
        else (roughly).

        Check we aren't gonna run into a wall either

        :param resultant_y:
        :param resultant_x:
        :return: bool
        """

        print ("Safety check. Our robot is at, x, y", self.world.our_robot.x, self.world.our_robot.y)

        # we're aiming for these co-ordinates, but realistically we aren't gonna move in a straight line
        # so let's see if there's any robots in our path (but not loop through every possibility, just every +padding
        # certaintly not most efficient way but who cares anymore...
        check_x = self.world.our_robot.x
        check_y = self.world.our_robot.y

        if self.world.safety_padding <= 0:
            self.world.safety_padding = 1

        while check_x <= resultant_x and check_y <= resultant_y:
            # is this co-ordinate within (z) units of other robots? if so we need to stop and think
            robots = [self.world.teammate, self.world.their_defender, self.world.their_attacker]
            for robot in robots:
                if (abs(resultant_x - robot.x) <= self.world.robot_safety_padding) and abs(
                    resultant_y - robot.y
                ) <= self.world.robot_safety_padding:

                    print ("TOo close to another robot")
                    # if this robot is moving, don't do anything
                    if robot.speed > 5:
                        return resultant_x, resultant_y
                    # robot is unlikely to move, let's re-route
                    else:
                        # this needs to be implemented
                        return resultant_x, resultant_y

            check_x += self.world.safety_padding
            check_y += self.world.safety_padding

        # check if we're going to run into a wall
        print ("Trying to move to resultant_x, resultant_y", resultant_x, resultant_y)
        if self.world.pitch_boundary_bottom - self.world.safety_padding <= resultant_y:
            print ("Trying to go somewhere greater than the greatest (bottom) boundary")
            return resultant_x, self.world.pitch_boundary_bottom - self.world.safety_padding
        if self.world.pitch_boundary_top + self.world.safety_padding >= resultant_y:
            print ("Trying to go somewhere less than the lowest (top) boundary")
            return resultant_x, self.world.pitch_boundary_top + self.world.safety_padding
        if self.world.pitch_boundary_left + self.world.safety_padding >= resultant_x:
            print ("Trying to go somewhere less than the left boundary")
            return self.world.pitch_boundary_left + self.world.safety_padding, resultant_y
        if self.world.pitch_boundary_right - self.world.safety_padding <= resultant_x:
            print ("Trying to go somewhere greater than the right boundary")
            return self.world.pitch_boundary_right - self.world.safety_padding, resultant_y

        # we're good to move here
        return True

    @staticmethod
    def calculate_kick_power(distance):
        """
        Given a distance to kick, crudely calculates the power for the kicker; this is only used for ball kicking atm
        :param distance:
        """
        # power is between 0.0 and 1.0, assume distance given is between 0.0 and 2.0. this function needs improving
        power = distance / 2
        print ("calculated power is ", power)

        if power > 1:
            return 1

        return power

    @staticmethod
    def calculate_motor_duration_turn(angle_to_rotate):
        """
        :param angle_to_rotate: given in degrees
        """
        # crude angle -> duration conversion
        duration = 100 + (abs(angle_to_rotate) * 5.5)

        if angle_to_rotate < 0:
            duration = -duration

        return -duration

    @staticmethod
    def calculate_motor_duration(distance):
        """
        Given a distance to travel, we need to know how long to run the motor for
        :param distance: provided in metres
        """

        # some crude distance -> duration measure. assumes 10cm of movement equates to 100ms, past the initial 100ms
        duration = 20 + (distance * 7.5)
        return duration