From 531113757c0e93168383c09cf0bd220c63a560e7 Mon Sep 17 00:00:00 2001 From: Joao Pedro Leal Date: Wed, 13 Nov 2024 10:37:49 -0300 Subject: [PATCH 1/3] feat(passing): jump needles state when already on ideal angle to shoot --- neonfc_ssl/skills/passing.py | 1 + neonfc_ssl/strategies/passer.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/neonfc_ssl/skills/passing.py b/neonfc_ssl/skills/passing.py index 0d069e9..a7ba2f5 100644 --- a/neonfc_ssl/skills/passing.py +++ b/neonfc_ssl/skills/passing.py @@ -185,6 +185,7 @@ def __init__(self, coach, match): self.wait.add_transition(self.step_back, self.wait.check_complete) self.step_back.add_transition(self.turn, self.step_back.check_complete) + self.step_back.add_transition(self.step_forward, self.turn.check_complete) self.turn.add_transition(self.step_forward, self.turn.check_complete) self.step_forward.add_transition(self.passing, self.step_forward.check_complete) diff --git a/neonfc_ssl/strategies/passer.py b/neonfc_ssl/strategies/passer.py index db7ba32..74dc059 100644 --- a/neonfc_ssl/strategies/passer.py +++ b/neonfc_ssl/strategies/passer.py @@ -32,7 +32,7 @@ def _start(self): def decide(self): target = self._match.active_opposites[-1] - next = self.active.update() + next = self.active.update(robot=self._robot, ball=self._match.ball) if next != self.active: self.active = next self.active.start(self._robot, target=target) From 049231b44bce962533482044e3cf9ca0e35f3732 Mon Sep 17 00:00:00 2001 From: Joao Pedro Leal Date: Fri, 15 Nov 2024 13:27:19 -0300 Subject: [PATCH 2/3] fic(special_cases): fix stop cases strategies --- config.json | 2 +- neonfc_ssl/coach/simple_coach.py | 216 +++++++++++++----- neonfc_ssl/comm/grsim_comm.py | 9 +- neonfc_ssl/control/control.py | 10 +- neonfc_ssl/entities/robot_command.py | 2 + neonfc_ssl/game.py | 1 + neonfc_ssl/referee/ssl_game_controller.py | 12 +- neonfc_ssl/skills/passing.py | 4 +- .../state_controller/state_controller.py | 45 +++- neonfc_ssl/strategies/__init__.py | 1 + neonfc_ssl/strategies/ball_holder.py | 56 ++++- neonfc_ssl/strategies/cross_defender.py | 88 +++++++ neonfc_ssl/strategies/goalkeeper.py | 40 ++-- neonfc_ssl/strategies/passer.py | 2 +- .../strategies/prepare_ballplacement.py | 31 +-- neonfc_ssl/strategies/prepare_bh_penalty.py | 2 +- neonfc_ssl/strategies/prepare_kickoff.py | 11 +- .../strategies/prepare_secondary_kickoff.py | 30 +++ 18 files changed, 439 insertions(+), 123 deletions(-) create mode 100644 neonfc_ssl/strategies/cross_defender.py create mode 100644 neonfc_ssl/strategies/prepare_secondary_kickoff.py diff --git a/config.json b/config.json index 4897c90..6667c19 100644 --- a/config.json +++ b/config.json @@ -17,7 +17,7 @@ "event": "Test", "team_side": "left", "team_color": "blue", - "robots_ids": [0, 1, 2, 3], + "robots_ids": [0, 1, 2, 3, 4], "time_logging": false }, "logger": { diff --git a/neonfc_ssl/coach/simple_coach.py b/neonfc_ssl/coach/simple_coach.py index 1254cf2..fdeb394 100644 --- a/neonfc_ssl/coach/simple_coach.py +++ b/neonfc_ssl/coach/simple_coach.py @@ -2,7 +2,8 @@ from neonfc_ssl.coach import BaseCoach from neonfc_ssl.commons.math import distance_between_points from neonfc_ssl.strategies import (BaseStrategy, Receiver, BallHolder, GoalKeeper, Libero, LeftBack, RightBack, - PrepPenalty, PrepBallPlacement, PrepKickoff, PrepGKPenalty, PrepBHPenalty) + PrepPenalty, PrepBallPlacement, PrepKickoff, PrepGKPenalty, PrepBHPenalty, + PrepSecondKickoff) from scipy.optimize import linear_sum_assignment import numpy as np @@ -33,7 +34,7 @@ def _start(self): # 2 attackers self._libero_strategies = { - r_id: Libero(self, self._match, self.defensive_positions) for r_id in range(6) + r_id: Libero(self, self._match, self.defensive_positions) for r_id in range(15) } self._left_back_strategy = LeftBack(self, self._match) @@ -45,12 +46,15 @@ def _start(self): } self._secondary_attack_strategies[self._gk_id] = self._strategy_gk - # Prepare to fouls strategies - self.prepare_kickoff = PrepKickoff(self, self._match) - self.prepare_penalty = {robot.robot_id: PrepPenalty(self, self._match) for robot in self._robots} - self.prepare_gk_penalty = PrepGKPenalty(self, self._match) + # Especial cases strategies + self.ec_kickoff = PrepKickoff(self, self._match) + self.ec_kickoff_secondary = PrepSecondKickoff(self, self._match) + + self.ec_penalty = {robot.robot_id: PrepPenalty(self, self._match) for robot in self._robots} + self.ec_bh_penalty = PrepBHPenalty(self, self._match) + self.ec_gk_penalty = PrepGKPenalty(self, self._match) + self.prepare_freekick = PrepBallPlacement(self, self._match) - self.prepare_bh_penalty = PrepBHPenalty(self, self._match) def decide(self): if self.has_possession: @@ -62,54 +66,127 @@ def decide(self): self._fouls() def _fouls(self): - if self._match.game_state.current_state.name == 'PrepareKickOff': + # -- Stopped States -- # + # * Stop + # * BallPlacement + # * PreparePenalty + # * PrepareKickOff + + # -- Stop -- # + # all robots must be outside a 0.5-meter range from the ball + # in most cases it will only be an issue to the ball holder + # + # -- BallPlacement -- # + # should not be an issue in the division B + # + # -- PreparePenalty -- # + # keeper on the line + # one attacker near the ball + # all others 1 meter behind ball + # all robots should change their behavior + # + # -- PrepareKickOff -- # + # all robots on its sides + # should only be a problem to the ball holder + + # -- Running Especial Cases -- # + # + # -- Penalty -- # + # on penalty all non-participating robots (keeper and attacker)should stay in the prepare positions + # ball holder could use a special strategy + # + # -- KickOff -- # + # on kickoff only the one team may touch the ball + # ball holder should keep the prepare strategy if on defense + # ball holder could use a special strategy on attack + # + # -- FreeKick -- # + # on kickoff only the one team may touch the ball + # ball holder should have a different strategy + # ball holder could use a special strategy on attack + + current_state = self._match.game_state.current_state.name + our_team = self._match.game_state.current_state.color == self._match.team_color + + # Our kickoff + if current_state in ['PrepareKickOff', 'KickOff'] and our_team: + print('aaa') + for robot in self._active_robots: + if robot.strategy.name != 'Ball Holder' and robot.robot_id != self._gk_id: + robot.set_strategy(self.ec_kickoff_secondary) + break + for robot in self._active_robots: if robot.strategy.name == 'Ball Holder': - robot.set_strategy(self.prepare_kickoff) + robot.set_strategy(self.ec_kickoff) break - elif self._match.game_state.current_state.name == "PreparePenalty": - if self._match.game_state.current_state.color == self._match.opponent_color: - for robot in self._active_robots: - if robot.robot_id != self._gk_id: - robot.set_strategy(self.prepare_penalty[robot.robot_id]) - else: - robot.set_strategy(self.prepare_gk_penalty) - else: - for robot in self._active_robots: - if robot.strategy.name == 'Ball Holder': - robot.set_strategy(self.prepare_bh_penalty) - - elif self._match.game_state.current_state.name == "Penalty": - if self._match.game_state.current_state.color == self._match.opponent_color: - for robot in self._active_robots: - if robot.robot_id != self._gk_id: - robot.set_strategy(self.prepare_penalty[robot.robot_id]) - - elif self._match.game_state.current_state.name == 'BallPlacement' or self._match.game_state.current_state.name == 'FreeKick': + # Their kickoff + elif current_state in ['PrepareKickOff', 'KickOff'] and not our_team: + for robot in self._active_robots: + if robot.strategy.name == 'Ball Holder': + robot.set_strategy(self.ec_kickoff) + break + + # Our penalty + elif current_state in ["PreparePenalty"] and our_team: + for robot in self._active_robots: + if robot.strategy.name == 'Ball Holder': + robot.set_strategy(self.ec_bh_penalty) + + ## If especial strategy to shoot penalty + # elif current_state in ["Penalty"] and our_team: + # for robot in self._active_robots: + # if robot.strategy.name == 'Ball Holder': + # robot.set_strategy(self.ec_bh_penalty_shoot) + + # Their penalty + elif current_state in ["PreparePenalty", "Penalty"] and not our_team: + for robot in self._active_robots: + if robot.robot_id == self._gk_id: + robot.set_strategy(self.ec_gk_penalty) + else: + robot.set_strategy(self.ec_penalty[robot.robot_id]) + + # Our free kick + elif current_state in ['BallPlacement', 'Stop'] and our_team: + for robot in self._active_robots: + if robot.strategy.name == 'Ball Holder': + robot.set_strategy(self.prepare_freekick) + break + + # Their free kick + elif current_state in ['BallPlacement', 'FreeKick', 'Stop'] and not our_team: for robot in self._active_robots: if robot.strategy.name == 'Ball Holder': robot.set_strategy(self.prepare_freekick) break - - # n sei oq por no else ou se faz uma condição p todos as outras condições def _defending(self): # when in possession check the ball carrier (smaller time to ball), than it becomes ball carrier new_carrier = self._closest_non_keeper() - n_liberos = max(min(self._n_active_robots-2, 3), 1) - if self._use_left_back() or self._use_right_back(): + n_liberos = max(min(self._n_active_robots - 2, 3), 0) + if n_liberos > 0: + if self._use_left_back() or self._use_right_back(): + n_liberos -= 1 + + use_cross_def, cross_def_pos = self.use_cross_def() + if use_cross_def and n_liberos > 0: n_liberos -= 1 pos = self._libero_y_positions(n_liberos) - - if self._use_left_back(): - pos = np.append(pos, [self._left_back_strategy.expected_position()], axis=0) - n_liberos += 1 - if self._use_right_back(): - pos = np.append(pos, [self._right_back_strategy.expected_position()], axis=0) - n_liberos += 1 + + if n_liberos > 0: + if self._use_left_back(): + pos = np.append(pos, [self._left_back_strategy.expected_position()], axis=0) + n_liberos += 1 + if self._use_right_back(): + pos = np.append(pos, [self._right_back_strategy.expected_position()], axis=0) + n_liberos += 1 + if use_cross_def: + pos = np.append(pos, [cross_def_pos], axis=0) + n_liberos += 1 available_robots = self._clear_robot_list(self._active_robots[:], [self._gk_id, new_carrier]) available_robots.sort(key=lambda r: r.x) @@ -131,21 +208,30 @@ def _defending(self): def _use_right_back(self): field = self._match.field - limit = (field.fieldWidth - field.penaltyAreaWidth)/2 + 0.5 + limit = (field.fieldWidth - field.penaltyAreaWidth) / 2 + 0.2 - return self._match.ball.x < field.fieldLength/2 and self._match.ball.y < limit + return self._match.ball.x < field.fieldLength / 3 and self._match.ball.y < limit def _use_left_back(self): field = self._match.field - limit = (field.fieldWidth + field.penaltyAreaWidth)/2 - 0.5 + limit = (field.fieldWidth + field.penaltyAreaWidth) / 3 - 0.2 - return self._match.ball.x < field.fieldLength/2 and self._match.ball.y > limit + return self._match.ball.x < field.fieldLength / 3 and self._match.ball.y > limit def _closest_non_keeper(self): - sq_dist_to_ball = lambda r: np.sum(np.square(np.array(r)-self._match.ball)) \ - if r.robot_id != self._gk_id else np.inf + def can_be_bh(r): + double_kick = self._match.game_state.current_state.last_state + if double_kick is not None and double_kick.first_touch: + return r.robot_id != self._gk_id and r.robot_id != double_kick.first_touch_id + else: + return r.robot_id != self._gk_id + + sq_dist_to_ball = lambda r: np.sum(np.square(np.array(r) - self._match.ball)) if can_be_bh(r) else np.inf my_closest = min(self._match.active_robots, key=sq_dist_to_ball, default=None) - return my_closest.robot_id + if my_closest: + return my_closest.robot_id + else: + return 1 def _attacking(self): # when in possession check the ball carrier (smaller time to ball), than it becomes ball carrier @@ -180,14 +266,14 @@ def _ball_proj(self, x_robot, y_robot, theta, closest): ball = self._match.ball field = self._match.field - y_goal_min = (field.fieldWidth/2)-field.goalWidth/2 - y_goal_max = (field.fieldWidth/2)+field.goalWidth/2 + y_goal_min = (field.fieldWidth / 2) - field.goalWidth / 2 + y_goal_max = (field.fieldWidth / 2) + field.goalWidth / 2 x = field.penaltyAreaDepth + 0.2 - y_max = ((y_goal_max-ball.y)/(-ball.x))*(x-ball.x)+ball.y - y_min = ((y_goal_min-ball.y)/(-ball.x))*(x-ball.x)+ball.y + y_max = ((y_goal_max - ball.y) / (-ball.x)) * (x - ball.x) + ball.y + y_min = ((y_goal_min - ball.y) / (-ball.x)) * (x - ball.x) + ball.y if closest < 0.15: - y = math.tan(theta)*(x-x_robot)+y_robot + y = math.tan(theta) * (x - x_robot) + y_robot elif ball.x < field.leftPenaltyStretch[0] and ball.y < field.leftPenaltyStretch[1]: y = field.leftPenaltyStretch[1] - 0.2 @@ -201,7 +287,7 @@ def _ball_proj(self, x_robot, y_robot, theta, closest): y = ball.y else: - y = (ball.vy/ball.vx)*(x-ball.x)+ball.y + y = (ball.vy / ball.vx) * (x - ball.x) + ball.y y = y_max if y > y_max else y y = y_min if y < y_min else y @@ -228,7 +314,7 @@ def _libero_y_positions(self, n_robots): else: diameter = 0.2 - target -= diameter/2 + target -= diameter / 2 ang = math.atan2(ball.vx, ball.vy) if abs(ball.vx) > 0.05 else 0 for i in range(0, n_robots): @@ -255,12 +341,34 @@ def cost_matrix(self, desired_pos, defensive_robots): for i in range(0, n_robots): for j in range(0, n_robots): - cost_matrix[i][j] = (robot_pos[i][0]-desired_pos[j][0])**2+(robot_pos[i][1]-desired_pos[j][1])**2 + cost_matrix[i][j] = (robot_pos[i][0] - desired_pos[j][0]) ** 2 + ( + robot_pos[i][1] - desired_pos[j][1]) ** 2 lines, columns = linear_sum_assignment(cost_matrix) for robot, pos in zip(lines, columns): self.defensive_positions[defensive_robots[robot].robot_id] = desired_pos[pos] + def use_cross_def(self): + if self._n_active_robots < 5: + return False, None + + mark_robots = [ + r for r in self._match.opposites + if not r.missing and + r.x < self._match.field.fieldLength / 3 and + abs(r.y - self._match.ball.y) > self._match.field.fieldWidth / 3 + ] + + if len(mark_robots) == 0: + return False, None + + if self._match.ball.y > self._match.field.fieldWidth / 2: + mark_robots.sort(key=lambda r: r.y, reverse=False) + return True, (self._match.field.penaltyAreaDepth + 0.2, mark_robots[0].y + 0.2) + else: + mark_robots.sort(key=lambda r: r.y, reverse=True) + return True, (self._match.field.penaltyAreaDepth + 0.2, mark_robots[0].y - 0.2) + @staticmethod def _clear_robot_list(robot_list: list, rm_id): if isinstance(rm_id, int): diff --git a/neonfc_ssl/comm/grsim_comm.py b/neonfc_ssl/comm/grsim_comm.py index 2062d5d..c52ad40 100644 --- a/neonfc_ssl/comm/grsim_comm.py +++ b/neonfc_ssl/comm/grsim_comm.py @@ -68,6 +68,7 @@ def update(self): for robot in cmds: robot.global_speed_to_wheel_speed() + robot.global_speed_to_local_speed() command = commands.robot_commands.add() command.wheel1 = robot.wheel_speed[0] command.wheel2 = robot.wheel_speed[1] @@ -75,11 +76,11 @@ def update(self): command.wheel4 = robot.wheel_speed[3] command.kickspeedx = robot.kick_speed[0] command.kickspeedz = robot.kick_speed[1] - command.veltangent = 0 - command.velnormal = 0 - command.velangular = 0 + command.veltangent = robot.local_speed[0] + command.velnormal = robot.local_speed[1] + command.velangular = robot.local_speed[2] command.spinner = robot.spinner - command.wheelsspeed = True + command.wheelsspeed = False command.id = robot.robot.robot_id self.send(commands) diff --git a/neonfc_ssl/control/control.py b/neonfc_ssl/control/control.py index 7158657..369f023 100644 --- a/neonfc_ssl/control/control.py +++ b/neonfc_ssl/control/control.py @@ -135,8 +135,14 @@ def run_single_robot(self, command): # -- Opponent Robots -- # [path_planning.add_dynamic_obstacle(r, 0.2, np.array((r.vx, r.vy))) for r in self._match.active_opposites] - # [path_planning.add_dynamic_obstacle(r, 0.2, np.array((r.vx, r.vy))) - # for r in self._match.active_robots if r != command.robot] + + if not command.ignore_friendly_robots: + [path_planning.add_dynamic_obstacle(r, 0.2, np.array((r.vx, r.vy))) + for r in self._match.active_robots if r != command.robot] + + # -- Ball -- # + if not command.ignore_ball: + path_planning.add_dynamic_obstacle(self._match.ball, 0.2, speed=(0,0)) next_point = path_planning.find_path() diff --git a/neonfc_ssl/entities/robot_command.py b/neonfc_ssl/entities/robot_command.py index a991d25..47894ff 100644 --- a/neonfc_ssl/entities/robot_command.py +++ b/neonfc_ssl/entities/robot_command.py @@ -15,6 +15,8 @@ class RobotCommand: kick_speed: Tuple[float, float] = (0, 0) # vx, vz spinner: bool = False ignore_area: bool = False + ignore_ball: bool = True + ignore_friendly_robots: bool = True # Control move_speed: Optional[Tuple[float, float, float]] = None # vx, vy, omega diff --git a/neonfc_ssl/game.py b/neonfc_ssl/game.py index 6788c51..a40ae8c 100644 --- a/neonfc_ssl/game.py +++ b/neonfc_ssl/game.py @@ -118,6 +118,7 @@ def update(self): self.comm.freeze() else: self.comm.update() + # self.comm.update() t.append(time.time()) if self.config['match'].get('time_logging', False): self.logger.info(f"total: {1/(t[4]-t[0]):.2f} Hz") diff --git a/neonfc_ssl/referee/ssl_game_controller.py b/neonfc_ssl/referee/ssl_game_controller.py index 87a38f7..71ad417 100644 --- a/neonfc_ssl/referee/ssl_game_controller.py +++ b/neonfc_ssl/referee/ssl_game_controller.py @@ -4,6 +4,7 @@ import threading import logging from google.protobuf.json_format import MessageToJson +from google.protobuf.message import DecodeError from neonfc_ssl.protocols.gc.ssl_gc_referee_message_pb2 import Referee @@ -31,8 +32,15 @@ def run(self): while self.running: c = Referee() data = self.referee_sock.recv(1024) - c.ParseFromString(data) - self._referee_message = json.loads(MessageToJson(c)) + try: + c.ParseFromString(data) + self._referee_message = json.loads(MessageToJson(c)) + except DecodeError as e: + self.referee_sock.close() + del self.referee_sock + self.referee_sock = self._create_socket() + self.logger.error(e) + self._referee_message = {'command':'HALT'} self.stop() def stop(self): diff --git a/neonfc_ssl/skills/passing.py b/neonfc_ssl/skills/passing.py index a7ba2f5..842332e 100644 --- a/neonfc_ssl/skills/passing.py +++ b/neonfc_ssl/skills/passing.py @@ -21,7 +21,7 @@ def decide(self): return RobotCommand(spinner=True, robot=self.robot, move_speed=(0, 0, 0)) def check_complete(self): - return self.robot.match.ball.get_speed() < 0.01 + return self.robot.match.ball.get_speed() < 0.03 class StepBack(State): @@ -211,7 +211,7 @@ def start_pass(robot, ball): @staticmethod def stop_pass(robot, ball): - return distance_between_points(robot, ball) > 0.15 + return distance_between_points(robot, ball) > 0.18 class ChipPass(State): diff --git a/neonfc_ssl/state_controller/state_controller.py b/neonfc_ssl/state_controller/state_controller.py index 5abff9d..a34c2e5 100644 --- a/neonfc_ssl/state_controller/state_controller.py +++ b/neonfc_ssl/state_controller/state_controller.py @@ -1,7 +1,9 @@ from neonfc_ssl.algorithms.fsm import State from neonfc_ssl.commons.math import distance_between_points from time import time +from dataclasses import dataclass import logging +from typing import Optional class GameState(State): @@ -12,12 +14,20 @@ def __init__(self, name): self.ball_initial_position = None self.color = None self.position = None + self.last_state: Optional[LastStateInfo] = None - def start(self, match, color, position): + def start(self, match, color, position, last_state): self.start_time = time() self.ball_initial_position = (match.ball.x, match.ball.y) self.color = color self.position = position + self.last_state = last_state + + +@dataclass +class LastStateInfo: + first_touch: bool + first_touch_id: int class StateController: @@ -31,6 +41,8 @@ def __init__(self, match): console_handler.setFormatter(log_formatter) self.logger.addHandler(console_handler) + self.last_state: Optional[LastStateInfo] = None + # Following appendix B found in: https://robocup-ssl.github.io/ssl-rules/sslrules.pdf self.states = { 'Halt': GameState('Halt'), @@ -51,14 +63,16 @@ def trig(cmd, **kwargs): return trig def after_secs(delay): + @self.save_info(False) def trig(origin, **kwargs): return time() - origin.start_time >= delay return trig after_10 = after_secs(10) - def ball_moved(origin, ball, **kwargs): - return distance_between_points(ball, origin.ball_initial_position) <= 0.05 + @self.save_info(True) + def ball_moved(ball, **kwargs): + return ball.get_speed() >= 0.05 # _ -> Halt halt = on_ref_message('HALT') @@ -122,14 +136,33 @@ def ball_moved(origin, ball, **kwargs): self.states['FreeKick'].add_transition(self.states['Run'], after_10) self.current_state = self.states['Halt'] - self.current_state.start(self._match, None, None) + self.current_state.start(self._match, None, None, None) + + def save_info(self, touched): + def wrapper(f): + def wrapped(poss, *args, **kwargs): + out = f(*args, **kwargs) + if out: + self.last_state = LastStateInfo(touched, poss.current_closest.robot_id if touched else None) + return out + return wrapped + return wrapper def update(self, ref): - next_state = self.current_state.update(origin=self.current_state, cmd=ref['command'], ball=self._match.ball) + next_state = self.current_state.update( + origin=self.current_state, + cmd=ref['command'], + ball=self._match.ball, + poss=self._match.possession + ) + + # TODO: This lacks some way to reset the last touch info when the robot that did the initial touch can touch again + if next_state != self.current_state: self.logger.info(f"Changing state {self.current_state.name} -> {next_state.name}") self.current_state = next_state - self.current_state.start(self._match, ref['team'], ref['pos']) + self.current_state.start(self._match, ref['team'], ref['pos'], self.last_state) + self.last_state = None def is_stopped(self): return self.current_state.name in ["Stop", "PrepareKickOff", "BallPlacement", "PreparePenalty"] diff --git a/neonfc_ssl/strategies/__init__.py b/neonfc_ssl/strategies/__init__.py index be0f9dc..32895c7 100644 --- a/neonfc_ssl/strategies/__init__.py +++ b/neonfc_ssl/strategies/__init__.py @@ -13,3 +13,4 @@ from neonfc_ssl.strategies.prepare_penalty import PrepPenalty from neonfc_ssl.strategies.prepare_gk_penalty import PrepGKPenalty from neonfc_ssl.strategies.prepare_bh_penalty import PrepBHPenalty +from neonfc_ssl.strategies.prepare_secondary_kickoff import PrepSecondKickoff diff --git a/neonfc_ssl/strategies/ball_holder.py b/neonfc_ssl/strategies/ball_holder.py index d3a4e56..67739b9 100644 --- a/neonfc_ssl/strategies/ball_holder.py +++ b/neonfc_ssl/strategies/ball_holder.py @@ -34,7 +34,8 @@ def __init__(self, coach, match): 'go_to_ball': GoToBall(coach, match), 'wait': Wait(coach, match), 'shoot': Shoot(coach, match), - 'dribble': Dribble(coach, match) + 'dribble': Dribble(coach, match), + 'wait_outside_area': MoveToPose(coach, match) } def close_to_ball(): @@ -46,19 +47,31 @@ def wrapped(): return wrapped + def and_func(f1, f2): + def wrapped(): + return f1() and f2() + + return wrapped + def wrapped_stop_pass(): return SimplePass.stop_pass(robot=self._robot, ball=self._match.ball) - self.states['pass'].add_transition(self.states['go_to_ball'], wrapped_stop_pass) - self.states['wait'].add_transition(self.states['go_to_ball'], not_func(close_to_ball)) - self.states['shoot'].add_transition(self.states['go_to_ball'], not_func(close_to_ball)) + self.states['pass'].add_transition(self.states['go_to_ball'], and_func(wrapped_stop_pass, not_func(self.ball_inside_area))) + self.states['wait'].add_transition(self.states['go_to_ball'], and_func(not_func(close_to_ball), not_func(self.ball_inside_area))) + self.states['shoot'].add_transition(self.states['go_to_ball'], and_func(not_func(close_to_ball), not_func(self.ball_inside_area))) self.states['dribble'].add_transition(self.states['wait'], not_func(close_to_ball)) self.states['go_to_ball'].add_transition(self.states['wait'], close_to_ball) self.states['dribble'].add_transition(self.states['pass'], self.pass_transition) self.states['dribble'].add_transition(self.states['shoot'], self.shoot_transition) - self.states['wait'].add_transition(self.states['pass'], self.pass_transition) - self.states['wait'].add_transition(self.states['shoot'], self.shoot_transition) - self.states['wait'].add_transition(self.states['dribble'], self.dribble_transition) + self.states['wait'].add_transition(self.states['pass'], and_func(self.pass_transition, not_func(self.ball_inside_area))) + self.states['wait'].add_transition(self.states['shoot'], and_func(self.shoot_transition, not_func(self.ball_inside_area))) + self.states['wait'].add_transition(self.states['dribble'], and_func(self.dribble_transition, not_func(self.ball_inside_area))) + self.states['go_to_ball'].add_transition(self.states['wait_outside_area'], self.ball_inside_area) + self.states['pass'].add_transition(self.states['wait_outside_area'], self.ball_inside_area) + self.states['shoot'].add_transition(self.states['wait_outside_area'], self.ball_inside_area) + self.states['dribble'].add_transition(self.states['wait_outside_area'], self.ball_inside_area) + self.states['wait'].add_transition(self.states['wait_outside_area'], self.ball_inside_area) + self.states['wait_outside_area'].add_transition(self.states['wait'], not_func(self.ball_inside_area)) self.message = None self.passing_to = None @@ -85,6 +98,8 @@ def decide(self): self.active.start(self._robot, target=self._pass_target) elif self.active.name == "Dribble": self.active.start(self._robot, target=self._pass_target) + elif self.active.name == "MoveToPose": + self.active.start(self._robot, target=[self._match.field.fieldLength/2, self._match.field.fieldWidth/2, 0]) else: self.active.start(self._robot) @@ -97,7 +112,9 @@ def decide(self): ]).encode('ascii') self.sock.sendto(MESSAGE, (self.UDP_IP, self.UDP_PORT)) - return self.active.decide() + com = self.active.decide() + com.ignore_friendly_robots = False + return com def update_shooting_value(self): if self._robot.x > 9: @@ -153,6 +170,12 @@ def pass_transition(self): return False + def ball_inside_area(self): + f = self._match.field + b = self._match.ball + return (b.x <= f.penaltyAreaDepth and + (f.penaltyAreaWidth - f.fieldWidth)/2 <= b.y <= (f.penaltyAreaWidth + f.fieldWidth)/2) + def shoot_transition(self): if self._shooting_value >= self._pass_value: return True @@ -160,7 +183,24 @@ def shoot_transition(self): return False def _passing_targets(self): + return self._close_targets() + + def _close_targets(self): dx, dy = 0.1, 0.1 + + r = 1.5 + lwx, upx = min(self._robot.x + r, self._match.field.fieldLength), max(self._robot.x - r, 0) + lwy, upy = min(self._robot.y + r, self._match.field.fieldWidth), max(self._robot.y - r, 0) + + x = np.linspace(lwx + dx, upx - dx, 10) + y = np.linspace(lwy + dy, upy - dy, 10) + xs, ys = np.meshgrid(x, y) + + return np.transpose(np.array([xs.flatten(), ys.flatten()])) + + def _attack_targets(self): + dx, dy = 0.1, 0.1 + # Area 0 lwx, upx = 4.5, 9 lwy, upy = 0, 2 diff --git a/neonfc_ssl/strategies/cross_defender.py b/neonfc_ssl/strategies/cross_defender.py new file mode 100644 index 0000000..1553bc4 --- /dev/null +++ b/neonfc_ssl/strategies/cross_defender.py @@ -0,0 +1,88 @@ +from neonfc_ssl.strategies.base_strategy import BaseStrategy +from neonfc_ssl.skills import * +from neonfc_ssl.commons.math import distance_between_points +from math import atan2, tan, pi + + +class CrossDefender(BaseStrategy): + def __init__(self, coach, match): + super().__init__('LeftBack', coach, match) + + self.field = self._match.field + self.target = None + + self.states = { + 'move_to_left_side': MoveToPose(coach, match), + } + + def _start(self): + self.active = self.states['move_to_left_side'] + self.active.start(self._robot, target=self.defense_target) + + def decide(self): + # print(self.defensive_positions) + next = self.active.update() + + target = self.defense_target() + self.active = next + self.active.start(self._robot, target=target) + + return self.active.decide() + + def defense_target(self): + target_theta = atan2(-self._robot.y + self._match.ball.y, -self._robot.x + self._match.ball.x) + return *self.expected_position(), target_theta + + def _closest_opponent(self): + ball = self._match.ball + + closest = 100000 + for robot in self._match.opposites: + dist = distance_between_points([ball.x, ball.y], [robot.x, robot.y]) + if dist < closest: + closest = dist + theta = robot.theta - pi + x_robot = robot.x + y_robot = robot.y + data = [x_robot, y_robot, theta, closest] + return data + + def expected_position(self): + return self._defense_pos(*self._closest_opponent()) + + def _defense_pos(self, x_robot, y_robot, theta, closest): + y = self.y_position() + x = self.x_position(x_robot, y_robot, theta, closest) + + return x, y + + def x_position(self, x_robot, y_robot, theta, closest): + ball = self._match.ball + field = self._match.field + + # y = (field.fieldWidth - field.leftPenaltyStretch[1]) + 0.2 + # y = field.penaltyAreaWidth + ((field.fieldWidth-field.penaltyAreaWidth)/2) + 0.2 + + # x_min = (y-ball.y)*((-ball.x)/(y_goal_max-ball.y))+ball.x + # x_max = (y-ball.y)*((-ball.x)/(y_goal_min-ball.y))+ball.x + + y = self.y_position() + x_min = 0.15 + x_max = field.leftPenaltyStretch[0] + # x_max = field.penaltyAreaDepth + + if closest < 0.15: + x = ((y - y_robot) * (1 / tan(theta))) + x_robot + + elif abs(ball.vx) < 0.05: + x = ball.x + + else: + x = ((y - ball.y) * (ball.vx / ball.vy)) + ball.x + + return max(x_min, min(x_max, x)) + + def y_position(self): + field = self._match.field + return (field.fieldWidth - field.leftPenaltyStretch[1]) + 0.2 + diff --git a/neonfc_ssl/strategies/goalkeeper.py b/neonfc_ssl/strategies/goalkeeper.py index 6dca14c..bdce926 100644 --- a/neonfc_ssl/strategies/goalkeeper.py +++ b/neonfc_ssl/strategies/goalkeeper.py @@ -19,31 +19,31 @@ def __init__(self, coach, match): } def not_func(f): - def wrapped(): - return not f() + def wrapped(**kwargs): + return not f(**kwargs) return wrapped self.states["move_to_pose"].add_transition(self.states["go_to_ball"], self.go_to_ball_transition) - self.states["go_to_ball"].add_transition(self.states["pass"], self.pass_transition) + self.states["go_to_ball"].add_transition(self.states["pass"], SimplePass.start_pass) self.states["go_to_ball"].add_transition(self.states["move_to_pose"], not_func(self.go_to_ball_transition)) - self.states["pass"].add_transition(self.states["move_to_pose"], self.move_to_pose_transition) + self.states["pass"].add_transition(self.states["move_to_pose"], SimplePass.stop_pass) def _start(self): self.active = self.states['move_to_pose'] self.active.start(self._robot, target=self.defense) def decide(self): - next = self.active.update() - + next = self.active.update(robot=self._robot, ball=self._match.ball) if next.name != "MoveToPose": - self.active = next - - if self.active.name == "Pass": - _passing_to = Point(5, 4) - self.active.start(self._robot, target=_passing_to) + if next.name == "Pass": + if next.name != self.active.name: + self.active = next + _passing_to = Point(5, 4) + self.active.start(self._robot, target=_passing_to) else: + self.active = next self.active.start(self._robot) else: @@ -170,11 +170,11 @@ def defense(self): return[x, y, theta] def ball_in_area(self): - if (self._match.ball.x < 1) and (self._match.ball.y > 2) and (self._match.ball.y < 4): + if (self._match.ball.x < self.field.penaltyAreaDepth) and (self._match.ball.y > ((self.field.fieldWidth - self.field.penaltyAreaWidth)/2)) and (self._match.ball.y < ((self.field.fieldWidth - self.field.penaltyAreaWidth)/2)+self.field.penaltyAreaWidth): return True return False - def move_to_pose_transition(self): + def move_to_pose_transition(self, **kwargs): is_in_rect = point_in_rect([self._match.ball.x, self._match.ball.y], [self.field.leftFirstPost[0], self.field.leftFirstPost[1], self.field.goalWidth, 2 * self.field.fieldLength]) @@ -183,15 +183,19 @@ def move_to_pose_transition(self): return True return False - def go_to_ball_transition(self): + def go_to_ball_transition(self, **kwargs): area_ymin = (self.field.fieldWidth - self.field.penaltyAreaWidth)/2 area_ymax = area_ymin + self.field.penaltyAreaWidth - if (self._match.ball.x < self.field.penaltyAreaDepth) and (self._match.ball.y > area_ymin) and (self._match.ball.y < area_ymax): - return True - return False + ball_inside_area = ((self._match.ball.x < self.field.penaltyAreaDepth) and + (self._match.ball.y > area_ymin) and + (self._match.ball.y < area_ymax)) + + ball_slow = self._match.ball.get_speed() < 0.1 + + return ball_slow and ball_inside_area - def pass_transition(self): + def pass_transition(self, **kwargs): is_in_rect = point_in_rect([self._match.ball.x, self._match.ball.y], [self._robot.x - 0.15, self._robot.y - 0.15, 0.3, 0.3]) diff --git a/neonfc_ssl/strategies/passer.py b/neonfc_ssl/strategies/passer.py index 74dc059..b7be7f6 100644 --- a/neonfc_ssl/strategies/passer.py +++ b/neonfc_ssl/strategies/passer.py @@ -30,7 +30,7 @@ def _start(self): self.active.start(self._robot) def decide(self): - target = self._match.active_opposites[-1] + target = self._match.active_robots[0] next = self.active.update(robot=self._robot, ball=self._match.ball) if next != self.active: diff --git a/neonfc_ssl/strategies/prepare_ballplacement.py b/neonfc_ssl/strategies/prepare_ballplacement.py index a5a439e..d38dc94 100644 --- a/neonfc_ssl/strategies/prepare_ballplacement.py +++ b/neonfc_ssl/strategies/prepare_ballplacement.py @@ -1,6 +1,6 @@ from neonfc_ssl.skills import * from neonfc_ssl.strategies.base_strategy import BaseStrategy -from math import atan2 +from math import atan2, cos, sin class PrepBallPlacement(BaseStrategy): @@ -15,35 +15,26 @@ def __init__(self, coach, match): self.states['wait'].add_transition(self.states['move_to_pose'], self.move_to_pose_transition) def _start(self): - self.active = self.states['Wait'] + self.active = self.states['wait'] self.active.start(self._robot) def decide(self): next = self.active.update() - target = self.position + target = self.position() self.active = next self.active.start(self._robot, target=target) - return self.active.decide() + com = self.active.decide() + com.ignore_ball = False + return com def position(self): field = self._match.field - color = self._match.game_state.current_state.color - foul_pos = self._match.game_state.current_state.position - - if color == self._match.opponent_color: - if foul_pos[0] < field.fieldWidth/2: - x = foul_pos[0] - 0.7 - y = foul_pos[1] + 0.7 - - else: - x = foul_pos[0] - 0.7 - y = foul_pos[1] - 0.7 - - else: - #testar escanteio (recebe freekick ou corner?) - x = foul_pos[0] - 0.3 - y = foul_pos[1] + + ang = atan2(-self._match.ball.y + field.fieldWidth/2, -self._match.ball.x) + + x = self._match.ball.x + cos(ang)*0.65 + y = self._match.ball.y + sin(ang)*0.65 theta = atan2(-self._robot.y + self._match.ball.y, -self._robot.x + self._match.ball.x) diff --git a/neonfc_ssl/strategies/prepare_bh_penalty.py b/neonfc_ssl/strategies/prepare_bh_penalty.py index 85f5783..06356f4 100644 --- a/neonfc_ssl/strategies/prepare_bh_penalty.py +++ b/neonfc_ssl/strategies/prepare_bh_penalty.py @@ -17,7 +17,7 @@ def decide(self): return self.active.decide() def position(self): - x = self._match.ball.x - 0.13 + x = self._match.ball.x - 0.5 y = self._match.ball.y theta = atan2(-self._robot.y + self._match.ball.y, -self._robot.x + self._match.ball.x) diff --git a/neonfc_ssl/strategies/prepare_kickoff.py b/neonfc_ssl/strategies/prepare_kickoff.py index 36584da..161419f 100644 --- a/neonfc_ssl/strategies/prepare_kickoff.py +++ b/neonfc_ssl/strategies/prepare_kickoff.py @@ -13,17 +13,20 @@ def _start(self): self.active.start(self._robot, target=self.position(self._match.game_state.current_state.color)) def decide(self): - return self.active.decide() + com = self.active.decide() + com.ignore_ball = False + return com def position(self, color): field = self._match.field + if color == self._match.opponent_color: x = field.fieldLength/2 - 0.6 - + y = field.fieldWidth / 2 else: - x = field.fieldLength/2 - 0.3 + x = field.fieldLength/2 + 0.2 + y = field.fieldWidth / 2 + 0.2 - y = field.fieldWidth/2 theta = atan2(-self._robot.y + self._match.ball.y, -self._robot.x + self._match.ball.x) return [x, y, theta] \ No newline at end of file diff --git a/neonfc_ssl/strategies/prepare_secondary_kickoff.py b/neonfc_ssl/strategies/prepare_secondary_kickoff.py new file mode 100644 index 0000000..cdfd75d --- /dev/null +++ b/neonfc_ssl/strategies/prepare_secondary_kickoff.py @@ -0,0 +1,30 @@ +from neonfc_ssl.skills import * +from neonfc_ssl.strategies.base_strategy import BaseStrategy +from math import atan2 + + +class PrepSecondKickoff(BaseStrategy): + def __init__(self, coach, match): + super().__init__('SecondKickoff', coach, match) + + self.active = MoveToPose(coach, match) + + def _start(self): + self.active.start(self._robot, target=self.position(self._match.game_state.current_state.color)) + + def decide(self): + return self.active.decide() + + def position(self, color): + field = self._match.field + if color == self._match.opponent_color: + x = field.fieldLength / 2 - 2 + y = field.fieldWidth / 2 + + else: + x = field.fieldLength / 2 - 1 + y = field.fieldWidth / 2 - 0.5 + + theta = atan2(-self._robot.y + self._match.ball.y, -self._robot.x + self._match.ball.x) + + return [x, y, theta] \ No newline at end of file From caabeb4c75173434139212950c111ce86cdf9433 Mon Sep 17 00:00:00 2001 From: Joao Pedro Leal Date: Tue, 26 Nov 2024 16:47:18 -0300 Subject: [PATCH 3/3] fix(special_cases): fix state changes and behaviors that brake game rules --- neonfc_ssl/coach/simple_coach.py | 50 +++++++++++++++++-- neonfc_ssl/game.py | 2 +- neonfc_ssl/referee/ssl_game_controller.py | 3 +- neonfc_ssl/skills/passing.py | 2 +- .../state_controller/state_controller.py | 2 +- neonfc_ssl/strategies/ball_holder.py | 2 +- neonfc_ssl/strategies/goalkeeper.py | 2 +- neonfc_ssl/strategies/prepare_kickoff.py | 18 ++++++- 8 files changed, 70 insertions(+), 11 deletions(-) diff --git a/neonfc_ssl/coach/simple_coach.py b/neonfc_ssl/coach/simple_coach.py index fdeb394..aeaf42b 100644 --- a/neonfc_ssl/coach/simple_coach.py +++ b/neonfc_ssl/coach/simple_coach.py @@ -17,7 +17,7 @@ def _start(self): # Especial cases self._strategy_gk = GoalKeeper(self, self._match) self._strategy_bh = BallHolder(self, self._match) - self._gk_id = 0 + self._gk_id = 1 # ------ n=6 ------ # # 1 fixed keeper @@ -109,8 +109,7 @@ def _fouls(self): our_team = self._match.game_state.current_state.color == self._match.team_color # Our kickoff - if current_state in ['PrepareKickOff', 'KickOff'] and our_team: - print('aaa') + if current_state in ['KickOff'] and our_team: for robot in self._active_robots: if robot.strategy.name != 'Ball Holder' and robot.robot_id != self._gk_id: robot.set_strategy(self.ec_kickoff_secondary) @@ -121,6 +120,17 @@ def _fouls(self): robot.set_strategy(self.ec_kickoff) break + if current_state in ['PrepareKickOff'] and our_team: + for robot in self._active_robots: + if robot.strategy.name != 'Ball Holder' and robot.robot_id != self._gk_id: + robot.set_strategy(self.ec_kickoff_secondary) + break + + for robot in self._active_robots: + if robot.strategy.name == 'Ball Holder': + robot.set_strategy(self.prepare_freekick) + break + # Their kickoff elif current_state in ['PrepareKickOff', 'KickOff'] and not our_team: for robot in self._active_robots: @@ -205,7 +215,39 @@ def _defending(self): robot.set_strategy(self._libero_strategies[robot.robot_id]) else: robot.set_strategy(self._secondary_attack_strategies[robot.robot_id]) - + + def _use_shadow_defense(self): + closest = min(self._match.active_opposites, key=lambda r: distance_between_points(r, self._match.ball), default=None) + keeper = min(self._match.active_opposites, key=lambda r: distance_between_points(r, (self._match.field.fieldLength, self._match.field.fieldWidth/2)), default=None) + + def can_attack(r): + if r == closest or r == keeper: + return False + + if r.y > 0.75*self._match.field.fieldLength: + return False + + return True + + if len(self._match.active_robots) - len(self._match.active_opposites) < 2: + return False, None + + opps = self._match.active_opposites[:] + opps.sort(key=lambda opp: opp.x) + opps = list(filter(can_attack, opps)) + + if len(opps) == 0: + return False, None + + opp = opps[0] + field = self._match.field + ang = math.atan2(-opp.y + field.fieldWidth / 2, -opp.x) + + x = opp.x + math.cos(ang) * 0.65 + y = opp.y + math.sin(ang) * 0.65 + + return True, (x, y) + def _use_right_back(self): field = self._match.field limit = (field.fieldWidth - field.penaltyAreaWidth) / 2 + 0.2 diff --git a/neonfc_ssl/game.py b/neonfc_ssl/game.py index a40ae8c..009c000 100644 --- a/neonfc_ssl/game.py +++ b/neonfc_ssl/game.py @@ -55,7 +55,7 @@ def __init__(self) -> None: self.control = Control(self) # Output Layer - self.comm = GrComm(self) + self.comm = SerialComm(self) # Register exit handler atexit.register(self.stop_threads) diff --git a/neonfc_ssl/referee/ssl_game_controller.py b/neonfc_ssl/referee/ssl_game_controller.py index 71ad417..c03dbbf 100644 --- a/neonfc_ssl/referee/ssl_game_controller.py +++ b/neonfc_ssl/referee/ssl_game_controller.py @@ -61,7 +61,8 @@ def is_stopped(self): return self._referee_message.get('command') == 'STOP' def is_halted(self): - return self._referee_message.get('command') == 'HALT' + return (self._referee_message.get('command') == 'HALT' or + self._referee_message.get('command').startswith('TIMEOUT')) def simplify(self): return {"command": self.get_command(), "team": self.get_team(), "pos": self.get_designated_position()} diff --git a/neonfc_ssl/skills/passing.py b/neonfc_ssl/skills/passing.py index 842332e..cae740d 100644 --- a/neonfc_ssl/skills/passing.py +++ b/neonfc_ssl/skills/passing.py @@ -207,7 +207,7 @@ def decide(self): @staticmethod def start_pass(robot, ball): - return distance_between_points(robot, ball) < 0.1 + return distance_between_points(robot, ball) < 0.12 @staticmethod def stop_pass(robot, ball): diff --git a/neonfc_ssl/state_controller/state_controller.py b/neonfc_ssl/state_controller/state_controller.py index a34c2e5..4f3c6ad 100644 --- a/neonfc_ssl/state_controller/state_controller.py +++ b/neonfc_ssl/state_controller/state_controller.py @@ -72,7 +72,7 @@ def trig(origin, **kwargs): @self.save_info(True) def ball_moved(ball, **kwargs): - return ball.get_speed() >= 0.05 + return ball.get_speed() >= 0.07 # _ -> Halt halt = on_ref_message('HALT') diff --git a/neonfc_ssl/strategies/ball_holder.py b/neonfc_ssl/strategies/ball_holder.py index 67739b9..d3bd5a0 100644 --- a/neonfc_ssl/strategies/ball_holder.py +++ b/neonfc_ssl/strategies/ball_holder.py @@ -188,7 +188,7 @@ def _passing_targets(self): def _close_targets(self): dx, dy = 0.1, 0.1 - r = 1.5 + r = .7 lwx, upx = min(self._robot.x + r, self._match.field.fieldLength), max(self._robot.x - r, 0) lwy, upy = min(self._robot.y + r, self._match.field.fieldWidth), max(self._robot.y - r, 0) diff --git a/neonfc_ssl/strategies/goalkeeper.py b/neonfc_ssl/strategies/goalkeeper.py index bdce926..d439eb6 100644 --- a/neonfc_ssl/strategies/goalkeeper.py +++ b/neonfc_ssl/strategies/goalkeeper.py @@ -167,7 +167,7 @@ def defense(self): y = self.limit_y(x, y) - return[x, y, theta] + return [x, y, theta] def ball_in_area(self): if (self._match.ball.x < self.field.penaltyAreaDepth) and (self._match.ball.y > ((self.field.fieldWidth - self.field.penaltyAreaWidth)/2)) and (self._match.ball.y < ((self.field.fieldWidth - self.field.penaltyAreaWidth)/2)+self.field.penaltyAreaWidth): diff --git a/neonfc_ssl/strategies/prepare_kickoff.py b/neonfc_ssl/strategies/prepare_kickoff.py index 161419f..fd1b71f 100644 --- a/neonfc_ssl/strategies/prepare_kickoff.py +++ b/neonfc_ssl/strategies/prepare_kickoff.py @@ -1,5 +1,6 @@ from neonfc_ssl.skills import * from neonfc_ssl.strategies.base_strategy import BaseStrategy +from neonfc_ssl.commons.math import distance_between_points from math import atan2 @@ -7,12 +8,27 @@ class PrepKickoff(BaseStrategy): def __init__(self, coach, match): super().__init__('Kickoff', coach, match) - self.active = MoveToPose(coach, match) + self.skills = { + 'position': MoveToPose(coach, match), + 'pass': SimplePass(coach, match) + } + + self.skills['position'].add_transition(self.skills['pass'], self.skills['position'].complete) + + self.active = self.skills['position'] def _start(self): self.active.start(self._robot, target=self.position(self._match.game_state.current_state.color)) def decide(self): + next_state = self.active.update() + if next_state != self.active: + self.active = next_state + nearest = min(self._match.active_robots, + key=lambda x: distance_between_points(self._robot, x) if x != self._robot else float('inf'), + default=[0, 0]) + self.active.start(self._robot, target=nearest) + com = self.active.decide() com.ignore_ball = False return com