class LeeroyJenkins(Tactic):

    MINIMUM_DISTANCE_FOR_SMALL_KICK = 3000

    def __init__(self,
                 game_state: GameState,
                 player: Player,
                 target: Pose = Pose(),
                 args: Optional[List[str]] = None):
        super().__init__(game_state, player, target, args)
        self.go_kick_tactic = None
        self.current_state = self.go_kick_low
        self.next_state = self.go_kick_low

    def go_kick_low(self):
        if self._is_close_enough_from_goal():
            self.go_kick_tactic = None
            self.next_state = self.go_kick_high
            return Idle
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(
                self.game_state,
                self.player,
                kick_force=KickForce.LOW,
                target=self.game_state.field.their_goal_pose)
        if self.go_kick_tactic.status_flag == Flags.SUCCESS:
            self.go_kick_tactic.status_flag = Flags.INIT
        return self.go_kick_tactic.exec()

    def go_kick_high(self):
        if not self._is_close_enough_from_goal():
            self.go_kick_tactic = None
            self.next_state = self.go_kick_low
            return Idle
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(self.game_state,
                                         self.player,
                                         kick_force=KickForce.HIGH,
                                         auto_update_target=True)
        if self.go_kick_tactic.status_flag == Flags.SUCCESS:
            self.go_kick_tactic.status_flag = Flags.INIT
        return self.go_kick_tactic.exec()

    def _is_close_enough_from_goal(self):
        return (GameState().field.their_goal_pose - self.game_state.ball_position).norm <= \
               self.MINIMUM_DISTANCE_FOR_SMALL_KICK
class LeeroyJenkins(Tactic):

    MINIMUM_DISTANCE_FOR_SMALL_KICK = 3000

    def __init__(self, game_state: GameState, player: Player, target: Pose = Pose(), args: Optional[List[str]]=None):
        super().__init__(game_state, player, target, args)
        self.go_kick_tactic = None
        self.current_state = self.go_kick_low
        self.next_state = self.go_kick_low

    def go_kick_low(self):
        if self._is_close_enough_from_goal():
            self.go_kick_tactic = None
            self.next_state = self.go_kick_high
            return Idle
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(self.game_state, self.player, kick_force=KickForce.LOW,
                                                     target=self.game_state.field.their_goal_pose)
        if self.go_kick_tactic.status_flag == Flags.SUCCESS:
            self.go_kick_tactic.status_flag = Flags.INIT
        return self.go_kick_tactic.exec()

    def go_kick_high(self):
        if not self._is_close_enough_from_goal():
            self.go_kick_tactic = None
            self.next_state = self.go_kick_low
            return Idle
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(self.game_state, self.player, kick_force=KickForce.HIGH,
                                                     auto_update_target=True)
        if self.go_kick_tactic.status_flag == Flags.SUCCESS:
            self.go_kick_tactic.status_flag = Flags.INIT
        return self.go_kick_tactic.exec()

    def _is_close_enough_from_goal(self):
        return (GameState().field.their_goal_pose - self.game_state.ball_position).norm <= \
               self.MINIMUM_DISTANCE_FOR_SMALL_KICK
class GoalKeeper(Tactic):

    MOVING_BALL_VELOCITY = 50  # mm/s
    DANGER_BALL_VELOCITY = 600  # mm/s

    def __init__(self, game_state: GameState, player: Player, target: Pose=Pose(),
                 penalty_kick=False, args: List[str]=None,):
        forbidden_area = [Area.pad(game_state.field.their_goal_area, KEEPOUT_DISTANCE_FROM_GOAL)]
        super().__init__(game_state, player, target, args, forbidden_areas=forbidden_area)

        self.current_state = self.defense
        self.next_state = self.defense

        self.target = Pose(self.game_state.field.our_goal, np.pi)  # Ignore target argument, always go for our goal

        self.go_kick_tactic = None # Used by clear
        self.last_intersection = None # For debug

        self.OFFSET_FROM_GOAL_LINE = Position(ROBOT_RADIUS + 10, 0)
        self.GOAL_LINE = self.game_state.field.our_goal_line

    def defense_dumb(self):
        dest_y = self.game_state.ball.position.y \
                 * self.game_state.goal_width / 2 / self.game_state.field.top
        position = self.game_state.field.our_goal - Position(ROBOT_RADIUS + 10, -dest_y)
        return MoveTo(Pose(position, np.pi))

    def defense(self):
        # Prepare to block the ball
        if self._is_ball_safe_to_kick() and self.game_state.ball.is_immobile():
            self.next_state = self.clear

        if self._ball_going_toward_goal():
            self.next_state = self.intercept
            return self.intercept()  # no time to loose

        circle_radius = self.game_state.field.goal_width / 2
        circle_center = self.game_state.field.our_goal - self.OFFSET_FROM_GOAL_LINE
        solutions = intersection_line_and_circle(circle_center,
                                                 circle_radius,
                                                 self.game_state.ball.position,
                                                 self._best_target_into_goal())
        # Their is one or two intersection on the circle, take the one on the field
        for solution in solutions:
            if solution.x < self.game_state.field.field_length / 2\
               and self.game_state.ball.position.x < self.game_state.field.field_length / 2:
                orientation_to_ball = (self.game_state.ball.position - self.player.position).angle
                return MoveTo(Pose(solution, orientation_to_ball),
                              cruise_speed=3,
                              end_speed=0)

        return MoveTo(Pose(self.game_state.field.our_goal, np.pi),
                      cruise_speed=3,
                      end_speed=0)

    def intercept(self):
        # Find the point where the ball will go
        if not self._ball_going_toward_goal() and not self.game_state.field.is_ball_in_our_goal_area():
            self.next_state = self.defense
        elif self.game_state.field.is_ball_in_our_goal_area() and self.game_state.ball.is_immobile():
            self.next_state = self.clear

        ball = self.game_state.ball
        where_ball_enter_goal = intersection_between_lines(self.GOAL_LINE.p1,
                                                           self.GOAL_LINE.p2,
                                                           ball.position,
                                                           ball.position + ball.velocity)

        # This is where the ball is going to enter the goal
        where_ball_enter_goal = closest_point_on_segment(where_ball_enter_goal, self.GOAL_LINE.p1, self.GOAL_LINE.p2)
        enter_goal_to_ball = Line(where_ball_enter_goal, ball.position)

        # The goalkeeper can not enter goal since there a line blocking vision
        end_segment = enter_goal_to_ball.direction * ROBOT_RADIUS + where_ball_enter_goal

        intersect_pts = closest_point_on_segment(self.player.position,
                                                 ball.position, end_segment)
        self.last_intersection = intersect_pts
        return MoveTo(Pose(intersect_pts, self.player.pose.orientation),  # It's a bit faster, to keep our orientation
                      cruise_speed=3,
                      end_speed=0,
                      ball_collision=False)

    def clear(self):
        # Move the ball to outside of the penality zone
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(self.game_state,
                                         self.player,
                                         auto_update_target=True,
                                         go_behind_distance=1.2*GRAB_BALL_SPACING,
                                         forbidden_areas=self.forbidden_areas)  # make it easier
        if not self._is_ball_safe_to_kick():
            self.next_state = self.defense
            self.go_kick_tactic = None
            return Idle
        else:
            return self.go_kick_tactic.exec()

    def _ball_going_toward_goal(self):
        upper_angle = (self.game_state.ball.position - self.GOAL_LINE.p2).angle + 5 * np.pi / 180.0
        lower_angle = (self.game_state.ball.position - self.GOAL_LINE.p1).angle - 5 * np.pi / 180.0
        ball_speed = self.game_state.ball.velocity.norm
        return (ball_speed > self.DANGER_BALL_VELOCITY and self.game_state.ball.velocity.x > 0) or \
               (ball_speed > self.MOVING_BALL_VELOCITY and upper_angle <= self.game_state.ball.velocity.angle <= lower_angle)

    def _is_ball_safe_to_kick(self):
        # Since defender can not kick the ball while inside the goal there are position where the ball is unreachable
        # The goalee must leave the goal area and kick the ball
        goal_area = self.game_state.field.our_goal_area
        width = KEEPOUT_DISTANCE_FROM_GOAL + ROBOT_DIAMETER
        area_in_front_of_goal = Area.from_limits(goal_area.top, goal_area.bottom,
                                                 goal_area.left, goal_area.left - width)
        return self.game_state.field.is_ball_in_our_goal_area() or \
               area_in_front_of_goal.point_inside(self.game_state.ball.position) and self._no_enemy_around_ball()

    def _best_target_into_goal(self):
        if 0 < len(self.game_state.enemy_team.available_players):
            enemy_player_with_ball = player_with_ball(min_dist_from_ball=200, our_team=False)
            if enemy_player_with_ball is not None:
                if player_pointing_toward_segment(enemy_player_with_ball, self.GOAL_LINE):
                    ball = self.game_state.ball
                    where_ball_enter_goal = intersection_between_lines(self.GOAL_LINE.p1,
                                                                       self.GOAL_LINE.p2,
                                                                       ball.position,
                                                                       ball.position +
                                                                       Position(1000 * np.cos(enemy_player_with_ball.pose.orientation),
                                                                                1000 * np.sin(enemy_player_with_ball.pose.orientation)))
                    where_ball_enter_goal = closest_point_on_segment(where_ball_enter_goal,
                                                                     self.GOAL_LINE.p1,
                                                                     self.GOAL_LINE.p2)
                    return where_ball_enter_goal

        return find_bisector_of_triangle(self.game_state.ball.position,
                                         self.GOAL_LINE.p2,
                                         self.GOAL_LINE.p1)

    def debug_cmd(self):
        if self.current_state == self.defense:
            return DebugCommandFactory().line(self.game_state.ball.position,
                                                self._best_target_into_goal(),
                                                timeout=0.1)
        elif self.current_state == self.intercept and self.last_intersection is not None:
            return DebugCommandFactory().line(self.game_state.ball.position,
                                                self.last_intersection,
                                                timeout=0.1)
        else:
            return []

    def _no_enemy_around_ball(self):
        closest = closest_players_to_point(self.game_state.ball_position, our_team=False)
        if len(closest) == 0:
            return True
        DANGEROUS_ENEMY_MIN_DISTANCE = 500
        return closest[0].distance > DANGEROUS_ENEMY_MIN_DISTANCE
Exemple #4
0
class GoalKeeper(Tactic):

    MOVING_BALL_VELOCITY = 50  # mm/s
    DANGER_BALL_VELOCITY = 600  # mm/s

    def __init__(
            self,
            game_state: GameState,
            player: Player,
            target: Pose = Pose(),
            penalty_kick=False,
            args: List[str] = None,
    ):
        forbidden_area = [
            Area.pad(game_state.field.their_goal_area,
                     KEEPOUT_DISTANCE_FROM_GOAL)
        ]
        super().__init__(game_state,
                         player,
                         target,
                         args,
                         forbidden_areas=forbidden_area)

        self.current_state = self.defense
        self.next_state = self.defense

        self.target = Pose(
            self.game_state.field.our_goal,
            np.pi)  # Ignore target argument, always go for our goal

        self.go_kick_tactic = None  # Used by clear
        self.last_intersection = None  # For debug

        self.OFFSET_FROM_GOAL_LINE = Position(ROBOT_RADIUS + 10, 0)
        self.GOAL_LINE = self.game_state.field.our_goal_line

    def defense_dumb(self):
        dest_y = self.game_state.ball.position.y \
                 * self.game_state.goal_width / 2 / self.game_state.field.top
        position = self.game_state.field.our_goal - Position(
            ROBOT_RADIUS + 10, -dest_y)
        return MoveTo(Pose(position, np.pi))

    def defense(self):
        # Prepare to block the ball
        if self._is_ball_safe_to_kick() and self.game_state.ball.is_immobile():
            self.next_state = self.clear

        if self._ball_going_toward_goal():
            self.next_state = self.intercept
            return self.intercept()  # no time to loose

        circle_radius = self.game_state.field.goal_width / 2
        circle_center = self.game_state.field.our_goal - self.OFFSET_FROM_GOAL_LINE
        solutions = intersection_line_and_circle(circle_center, circle_radius,
                                                 self.game_state.ball.position,
                                                 self._best_target_into_goal())
        # Their is one or two intersection on the circle, take the one on the field
        for solution in solutions:
            if solution.x < self.game_state.field.field_length / 2\
               and self.game_state.ball.position.x < self.game_state.field.field_length / 2:
                orientation_to_ball = (self.game_state.ball.position -
                                       self.player.position).angle
                return MoveTo(Pose(solution, orientation_to_ball),
                              cruise_speed=3,
                              end_speed=0)

        return MoveTo(Pose(self.game_state.field.our_goal, np.pi),
                      cruise_speed=3,
                      end_speed=0)

    def intercept(self):
        # Find the point where the ball will go
        if not self._ball_going_toward_goal(
        ) and not self.game_state.field.is_ball_in_our_goal_area():
            self.next_state = self.defense
        elif self.game_state.field.is_ball_in_our_goal_area(
        ) and self.game_state.ball.is_immobile():
            self.next_state = self.clear

        ball = self.game_state.ball
        where_ball_enter_goal = intersection_between_lines(
            self.GOAL_LINE.p1, self.GOAL_LINE.p2, ball.position,
            ball.position + ball.velocity)

        # This is where the ball is going to enter the goal
        where_ball_enter_goal = closest_point_on_segment(
            where_ball_enter_goal, self.GOAL_LINE.p1, self.GOAL_LINE.p2)
        enter_goal_to_ball = Line(where_ball_enter_goal, ball.position)

        # The goalkeeper can not enter goal since there a line blocking vision
        end_segment = enter_goal_to_ball.direction * ROBOT_RADIUS + where_ball_enter_goal

        intersect_pts = closest_point_on_segment(self.player.position,
                                                 ball.position, end_segment)
        self.last_intersection = intersect_pts
        return MoveTo(
            Pose(intersect_pts, self.player.pose.orientation
                 ),  # It's a bit faster, to keep our orientation
            cruise_speed=3,
            end_speed=0,
            ball_collision=False)

    def clear(self):
        # Move the ball to outside of the penality zone
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(
                self.game_state,
                self.player,
                auto_update_target=True,
                go_behind_distance=1.2 * GRAB_BALL_SPACING,
                forbidden_areas=self.forbidden_areas)  # make it easier
        if not self._is_ball_safe_to_kick():
            self.next_state = self.defense
            self.go_kick_tactic = None
            return Idle
        else:
            return self.go_kick_tactic.exec()

    def _ball_going_toward_goal(self):
        upper_angle = (self.game_state.ball.position -
                       self.GOAL_LINE.p2).angle + 5 * np.pi / 180.0
        lower_angle = (self.game_state.ball.position -
                       self.GOAL_LINE.p1).angle - 5 * np.pi / 180.0
        ball_speed = self.game_state.ball.velocity.norm
        return (ball_speed > self.DANGER_BALL_VELOCITY and self.game_state.ball.velocity.x > 0) or \
               (ball_speed > self.MOVING_BALL_VELOCITY and upper_angle <= self.game_state.ball.velocity.angle <= lower_angle)

    def _is_ball_safe_to_kick(self):
        # Since defender can not kick the ball while inside the goal there are position where the ball is unreachable
        # The goalee must leave the goal area and kick the ball
        goal_area = self.game_state.field.our_goal_area
        width = KEEPOUT_DISTANCE_FROM_GOAL + ROBOT_DIAMETER
        area_in_front_of_goal = Area.from_limits(goal_area.top,
                                                 goal_area.bottom,
                                                 goal_area.left,
                                                 goal_area.left - width)
        return self.game_state.field.is_ball_in_our_goal_area() or \
               area_in_front_of_goal.point_inside(self.game_state.ball.position) and self._no_enemy_around_ball()

    def _best_target_into_goal(self):
        if 0 < len(self.game_state.enemy_team.available_players):
            enemy_player_with_ball = player_with_ball(min_dist_from_ball=200,
                                                      our_team=False)
            if enemy_player_with_ball is not None:
                if player_pointing_toward_segment(enemy_player_with_ball,
                                                  self.GOAL_LINE):
                    ball = self.game_state.ball
                    where_ball_enter_goal = intersection_between_lines(
                        self.GOAL_LINE.p1, self.GOAL_LINE.p2, ball.position,
                        ball.position + Position(
                            1000 *
                            np.cos(enemy_player_with_ball.pose.orientation),
                            1000 *
                            np.sin(enemy_player_with_ball.pose.orientation)))
                    where_ball_enter_goal = closest_point_on_segment(
                        where_ball_enter_goal, self.GOAL_LINE.p1,
                        self.GOAL_LINE.p2)
                    return where_ball_enter_goal

        return find_bisector_of_triangle(self.game_state.ball.position,
                                         self.GOAL_LINE.p2, self.GOAL_LINE.p1)

    def debug_cmd(self):
        if self.current_state == self.defense:
            return DebugCommandFactory().line(self.game_state.ball.position,
                                              self._best_target_into_goal(),
                                              timeout=0.1)
        elif self.current_state == self.intercept and self.last_intersection is not None:
            return DebugCommandFactory().line(self.game_state.ball.position,
                                              self.last_intersection,
                                              timeout=0.1)
        else:
            return []

    def _no_enemy_around_ball(self):
        closest = closest_players_to_point(self.game_state.ball_position,
                                           our_team=False)
        if len(closest) == 0:
            return True
        DANGEROUS_ENEMY_MIN_DISTANCE = 500
        return closest[0].distance > DANGEROUS_ENEMY_MIN_DISTANCE
Exemple #5
0
class AlignToDefenseWall(Tactic):
    def __init__(self, game_state: GameState,
                 player: Player,
                 args: Optional[List[str]]=None,
                 robots_in_formation: Optional[List[Player]]=None,
                 object_to_block=None,
                 stay_away_from_ball=False,
                 cruise_speed=3):
        super().__init__(game_state, player, args=args)
        if object_to_block is None:
            object_to_block = GameState().ball
        self.object_to_block = object_to_block
        if robots_in_formation is None:
            self.robots_in_formation = [player]
        else:
            self.robots_in_formation = robots_in_formation
        assert isinstance(self.robots_in_formation[0], Player)

        self.cruise_speed = cruise_speed
        self.stay_away_from_ball = stay_away_from_ball
        self.go_kick_tactic = None
        self.player_number_in_formation = None
        self.wall_segment = None

        # Used for debug_cmd() visualization
        self.bisect_inter = None
        self.center_formation = None

        self.init_players_in_formation()

        self.current_state = self.main_state
        self.next_state = self.main_state

    def init_players_in_formation(self):
        self.player_number_in_formation = self.robots_in_formation.index(self.player)

    def compute_wall_segment(self):
        """
            We compute the position where the wall's robot can block the field of view of opposing robot with the ball.
            The field of view is defined as the triangle created by the ball and the goal_line extremities.
        """

        nb_robots = len(self.robots_in_formation)
        wall_segment_length = nb_robots * ROBOT_DIAMETER + GAP_IN_WALL * (nb_robots - 1)

        goal_line = self.game_state.field.our_goal_line
        bisection_angle = angle_between_three_points(goal_line.p2, self.object_to_block.position, goal_line.p1)

        # We calculate the farthest distance from the object which completely block its FOV of the goal
        object_to_center_formation_dist = wall_segment_length / tan(bisection_angle)

        self.bisect_inter = find_bisector_of_triangle(self.object_to_block.position, goal_line.p1, goal_line.p2)
        vec_object_to_goal_line_bisect = self.bisect_inter - self.object_to_block.position

        # The penalty zone used to be a circle and thus really easy to handle, but now it's a rectangle...
        # It easier to first create the smallest circle that fit the rectangle.
        min_radius_over_penality_zone = ROBOT_RADIUS + (self.game_state.field.our_goal_area.upper_left - self.game_state.field.our_goal).norm
        object_to_block_to_center_formation_dist = min(vec_object_to_goal_line_bisect.norm - min_radius_over_penality_zone,
                                                       object_to_center_formation_dist)

        self.center_formation = object_to_block_to_center_formation_dist * normalize(vec_object_to_goal_line_bisect) + self.object_to_block.position

        if self.stay_away_from_ball:
            if (self.game_state.ball_position - self.center_formation).norm < KEEPOUT_DISTANCE_FROM_BALL:
                self.center_formation = self._closest_point_away_from_ball()

        half_wall_segment = 0.5 * wall_segment_length * perpendicular(normalize(vec_object_to_goal_line_bisect))
        self.wall_segment = Line(self.center_formation + half_wall_segment,
                                 self.center_formation - half_wall_segment)

    def position_on_wall_segment(self):
        idx = self.player_number_in_formation
        length = ROBOT_RADIUS + idx * (ROBOT_DIAMETER + GAP_IN_WALL)
        return self.wall_segment.p1 + self.wall_segment.direction * length

    def debug_cmd(self):
        if self.wall_segment is None or self.player_number_in_formation != 0:
            return []
        return [DebugCommandFactory().line(self.wall_segment.p1,
                                           self.wall_segment.p2,
                                           timeout=0.1),
                DebugCommandFactory().line(self.center_formation,
                                           self.bisect_inter,
                                           timeout=0.1),
                DebugCommandFactory().line(self.game_state.ball_position,
                                           self.game_state.field.our_goal_line.p1,
                                           timeout=0.1),
                DebugCommandFactory().line(self.game_state.ball_position,
                                           self.game_state.field.our_goal_line.p2,
                                           timeout=0.1)
                ]

    def main_state(self):
        self.compute_wall_segment()
        if self.game_state.field.is_ball_in_our_goal_area():
            return Idle  # We must not block the goalkeeper
        elif self._should_ball_be_kick_by_wall() \
                and self._is_closest_not_goaler(self.player) \
                and self._no_enemy_around_ball():
            self.next_state = self.go_kick
        dest = self.position_on_wall_segment()
        dest_orientation = (self.object_to_block.position - dest).angle
        return MoveTo(Pose(dest,
                           dest_orientation), cruise_speed=self.cruise_speed)

    def go_kick(self):
        self.compute_wall_segment()
        if self.go_kick_tactic is None:
            self.go_kick_tactic = GoKick(self.game_state, self.player, target=self.game_state.field.their_goal_pose)

        if not self._should_ball_be_kick_by_wall() \
                or self.game_state.field.is_ball_in_our_goal_area() \
                or not self._is_closest_not_goaler(self.player) \
                or not self._no_enemy_around_ball():
            self.go_kick_tactic = None
            self.next_state = self.main_state
            return Idle
        else:
            return self.go_kick_tactic.exec()

    def _should_ball_be_kick_by_wall(self):
        return not self.stay_away_from_ball and \
               (self.position_on_wall_segment() - self.game_state.ball.position).norm < FETCH_BALL_ZONE_RADIUS

    def _is_closest_not_goaler(self, player):
        closest_players = closest_players_to_point(GameState().ball_position, our_team=True)
        if player == closest_players[0].player:
            return True
        return closest_players[0].player == self.game_state.get_player_by_role(Role.GOALKEEPER) \
               and player == closest_players[1].player

    def _closest_point_away_from_ball(self):
        inters = intersection_line_and_circle(self.game_state.ball_position, KEEPOUT_DISTANCE_FROM_BALL,
                                              self.object_to_block.position, self.bisect_inter)
        if len(inters) == 1:
            return inters[0]
        if (inters[0] - self.bisect_inter).norm < (inters[1] - self.bisect_inter).norm:
            return inters[0]
        else:
            return inters[1]

    def _no_enemy_around_ball(self):
        DANGEROUS_ENEMY_MIN_DISTANCE = 500
        ball_position = self.game_state.ball_position
        for enemy in self.game_state.enemy_team.available_players.values():
            if (enemy.position - ball_position).norm < DANGEROUS_ENEMY_MIN_DISTANCE:
                return False
        return True