def on_enter_clear(self): # FIXME: what we really want is a less-precise LineKick # this will require a Capture behavior that doesn't wait for the ball to stop kick = skills.pivot_kick.PivotKick() # TODO: the below dribble speed is best for a 2011 bot # kick.dribble_speed = constants.Robot.Dribbler.MaxPower / 3.5 # we use low error thresholds here # the goalie isn't trying to make a shot, he just wants get the ball the **** out of there kick.aim_params['error_threshold'] = 1.0 kick.aim_params['max_steady_ang_vel'] = 12 # chip kick.chip_power = 1.0 kick.use_chipper = True kick.target = robocup.Segment( robocup.Point(-constants.Field.Width / 2, constants.Field.Length), robocup.Point(constants.Field.Width / 2, constants.Field.Length)) # FIXME: if the goalie has a fault, resort to bump self.add_subbehavior(kick, 'kick-clear', required=True)
def get_segments_from_rect(rect, threshold=0.75): outlist = [] currentx = rect.min_x() currenty = rect.max_y() # Loop through from top left to bottom right while currentx <= rect.max_x(): currenty = rect.max_y() # Don't include goal area. if constants.Field.TheirGoalZoneShape.contains_point(robocup.Point( currentx, rect.min_y())): continue while constants.Field.TheirGoalZoneShape.contains_point(robocup.Point( currentx, currenty)): currenty = currenty - threshold candiate = robocup.Segment( robocup.Point(currentx, rect.min_y()), robocup.Point(currentx, currenty)) outlist.extend([candiate]) currentx = currentx + threshold currentx = rect.min_x() return outlist
def execute_marking(self): move = self.subbehavior_with_name('move') move.pos = self.move_target arc_left = robocup.Arc( robocup.Point(-constants.Field.GoalFlat / 2, 0), constants.Field.ArcRadius + constants.Robot.Radius * 2, math.pi / 2, math.pi) arc_right = robocup.Arc( robocup.Point(constants.Field.GoalFlat / 2, 0), constants.Field.ArcRadius + constants.Robot.Radius * 2, 0, math.pi / 2) seg = robocup.Segment( robocup.Point( -constants.Field.GoalFlat / 2, constants.Field.ArcRadius + constants.Robot.Radius * 2), robocup.Point( constants.Field.GoalFlat / 2, constants.Field.ArcRadius + constants.Robot.Radius * 2)) if move.pos is not None: main.system_state().draw_circle(move.pos, 0.02, constants.Colors.Green, "Mark") main.system_state().draw_segment(seg, constants.Colors.Green, "Mark") main.system_state().draw_arc(arc_left, constants.Colors.Green, "Mark") main.system_state().draw_arc(arc_right, constants.Colors.Green, "Mark") # make the defender face the threat it's defending against if (self.robot is not None and self.block_line is not None): self.robot.face(self.block_line.get_pt(0)) if self.robot.has_ball() and not main.game_state().is_stopped(): self.robot.kick(0.75)
def eval_pass(from_point, to_point, excluded_robots=[]): # we make a pass triangle with the far corner at the ball and the opposing side touching the receiver's mouth # the side along the receiver's mouth is the 'receive_seg' # we then use the window evaluator on this scenario to see if the pass is open pass_angle = math.pi / 32.0 pass_dist = to_point.dist_to(from_point) pass_dir = to_point - from_point pass_perp = pass_dir.perp_ccw() receive_seg_half_len = math.tan(pass_angle) * pass_dist receive_seg = robocup.Segment(to_point + pass_perp * receive_seg_half_len, to_point + pass_perp * -receive_seg_half_len) win_eval = evaluation.window_evaluator.WindowEvaluator() win_eval.excluded_robots = excluded_robots windows, best = win_eval.eval_pt_to_seg(from_point, receive_seg) # this is our estimate of the likelihood of the pass succeeding # value can range from zero to one # we square the ratio of best to total to make it weigh more - we could raise it to higher power if we wanted if best != None: return 0.8 * (best.segment.length() / receive_seg.length())**2 else: # the pass is completely blocked return 0
class LineUp(composite_behavior.CompositeBehavior): DEBOUNCE_MAX = 3 y_start = 1.2 # sometimes we have issues if we're right in the corner, so we move it up a bit DefaultLine = robocup.Segment( robocup.Point(-constants.Field.Width / 2 + constants.Robot.Radius, constants.Robot.Radius + y_start), robocup.Point(-constants.Field.Width / 2 + constants.Robot.Radius, (constants.Robot.Radius * 2 + 0.1 + y_start) * 6)) def __init__(self, line=None): super().__init__(continuous=False) self.line = line if line is not None else LineUp.DefaultLine self.debounce_count = 0 self.add_transition(behavior.Behavior.State.start, behavior.Behavior.State.running, lambda: True, 'immediately') self.add_transition(behavior.Behavior.State.running, behavior.Behavior.State.completed, lambda: self.all_subbehaviors_completed(), 'all robots reach target positions') self.add_transition(behavior.Behavior.State.completed, behavior.Behavior.State.running, lambda: not self.all_subbehaviors_completed(), 'robots arent lined up') def restart(self): super().restart() self.debounce_count = 0 # override superclass implementation of all_subbehaviors_completed() to # count unassigned subbehaviors as "done running" def all_subbehaviors_completed(self): for bhvr in self.all_subbehaviors(): if not bhvr.is_done_running() and (not isinstance( bhvr, SingleRobotBehavior) or bhvr.robot is not None): self.debounce_count = 0 return False # Give us a few cycles to change our mind if self.debounce_count < LineUp.DEBOUNCE_MAX: self.debounce_count += 1 return False else: return True def execute_running(self): for i in range(6): pt = self._line.get_pt(0) + (self.diff * float(i)) behavior = self.subbehavior_with_name("robot" + str(i)) behavior.pos = pt @property def line(self): return self._line @line.setter def line(self, value): self._line = value self.diff = (self._line.get_pt(1) - self._line.get_pt(0) ).normalized() * (self._line.length() / 5.0) # add subbehaviors for all robots, instructing them to line up self.remove_all_subbehaviors() for i in range(6): pt = self._line.get_pt(0) + (self.diff * float(i)) self.add_subbehavior(skills.move.Move(pt), name="robot" + str(i), required=False, priority=6 - i)
def execute_intercept(self): ball_path = robocup.Segment( main.ball().pos, main.ball().pos + main.ball().vel.normalized() * 10.0) dest = ball_path.nearest_point(self.robot.pos) self.robot.move_to(dest)
class Goalie(single_robot_composite_behavior.SingleRobotCompositeBehavior): OFFSET = 0.1 MaxX = constants.Field.GoalWidth / 2.0 RobotSegment = robocup.Segment( robocup.Point(-MaxX, constants.Robot.Radius + OFFSET), robocup.Point(MaxX, constants.Robot.Radius + OFFSET)) OpponentFacingThreshold = math.pi / 8.0 class State(enum.Enum): ## Normal gameplay, stay towards the side of the goal that the ball is on. defend = 1 ## Opponent has a ball and is prepping a shot we should block. block = 2 ## The ball is moving towards our goal and we should catch it. intercept = 3 ## Get the ball out of our defense area. clear = 4 ## Prepare to block the opponent's penalty shot setup_penalty = 5 ## Keep calm and wait for the ball to be valid. chill = 6 def __init__(self): super().__init__(continuous=True) for substate in Goalie.State: self.add_state(substate, behavior.Behavior.State.running) self.add_transition(behavior.Behavior.State.start, Goalie.State.chill, lambda: True, "immediately") self.add_transition(Goalie.State.chill, Goalie.State.defend, lambda: main.ball().valid, "ball is valid") non_chill_states = [s for s in Goalie.State if s != Goalie.State.chill] # if ball is invalid, chill for state in non_chill_states: self.add_transition(state, Goalie.State.chill, lambda: not main.ball().valid, "ball is invalid") for state in non_chill_states: self.add_transition( state, Goalie.State.setup_penalty, lambda: main.game_state( ).is_their_penalty() and main.game_state().is_setup_state(), "setting up for opponent penalty") for state in [ s2 for s2 in non_chill_states if s2 != Goalie.State.intercept ]: self.add_transition( state, Goalie.State.intercept, lambda: evaluation.ball.is_moving_towards_our_goal( ) and not self.robot_is_facing_our_goal(evaluation.ball. opponent_with_ball()), "ball coming towards our goal") for state in [ s2 for s2 in non_chill_states if s2 != Goalie.State.clear ]: self.add_transition( state, Goalie.State.clear, lambda: evaluation.ball.is_in_our_goalie_zone( ) and not main.game_state().is_their_penalty( ) and not evaluation.ball.is_moving_towards_our_goal( ) and evaluation.ball.opponent_with_ball() is None, "ball in our goalie box, but not headed toward goal") for state in [ s2 for s2 in non_chill_states if s2 != Goalie.State.defend ]: self.add_transition( state, Goalie.State.defend, lambda: not evaluation.ball.is_in_our_goalie_zone( ) and not evaluation.ball.is_moving_towards_our_goal( ) and not main.game_state().is_their_penalty() and not self. robot_is_facing_our_goal(evaluation.ball.opponent_with_ball()), 'not much going on') for state in [ s2 for s2 in non_chill_states if s2 != Goalie.State.block ]: self.add_transition( state, Goalie.State.block, lambda: not evaluation.ball.is_in_our_goalie_zone() and not evaluation.ball.is_moving_towards_our_goal() and self. robot_is_facing_our_goal(evaluation.ball.opponent_with_ball()), "opponents have possession") def robot_is_facing_our_goal(self, robot): if robot is None: return False goal_robot = robot.pos - robocup.Point(0, 0) angle = goal_robot.normalized().angle() - math.pi robot_angle = robot.angle * math.pi / 180. self.robot.add_text(str(angle - robot_angle), (255, 255, 255), "OurRobot") if abs(angle - robot_angle) < self.OpponentFacingThreshold: return True else: return False # note that execute_running() gets called BEFORE any of the execute_SUBSTATE methods gets called def execute_running(self): if self.robot != None: self.robot.face(main.ball().pos) self.robot.set_planning_priority(planning_priority.GOALIE) def execute_chill(self): if self.robot != None: self.robot.move_to( robocup.Point(0, constants.Robot.Radius + Goalie.OFFSET)) def execute_setup_penalty(self): pt = robocup.Point(0, constants.Field.PenaltyDist) penalty_kicker = min(main.their_robots(), key=lambda r: (r.pos - pt).mag()) angle_rad = penalty_kicker.angle shot_line = robocup.Line( penalty_kicker.pos, penalty_kicker.pos + robocup.Point.direction(angle_rad)) dest = Goalie.RobotSegment.line_intersection(shot_line) if dest is None: self.robot.move_to( robocup.Point(0, constants.Robot.Radius + Goalie.OFFSET)) else: dest.x = max(-Goalie.MaxX + constants.Robot.Radius, dest.x) dest.x = min(Goalie.MaxX - constants.Robot.Radius, dest.x) # Shots don't follow the top threat, they follow the inverse # FIXME this is kind of a hack dest.x = -dest.x self.robot.move_to(dest) def on_enter_clear(self): # FIXME: what we really want is a less-precise LineKick # this will require a Capture behavior that doesn't wait for the ball to stop kick = skills.pivot_kick.PivotKick() # TODO: the below dribble speed is best for a 2011 bot # kick.dribble_speed = constants.Robot.Dribbler.MaxPower / 3.5 # we use low error thresholds here # the goalie isn't trying to make a shot, he just wants get the ball the **** out of there kick.aim_params['error_threshold'] = 1.0 kick.aim_params['max_steady_ang_vel'] = 12 # chip kick.chip_power = 1.0 kick.use_chipper = True kick.target = robocup.Segment( robocup.Point(-constants.Field.Width / 2, constants.Field.Length), robocup.Point(constants.Field.Width / 2, constants.Field.Length)) # FIXME: if the goalie has a fault, resort to bump self.add_subbehavior(kick, 'kick-clear', required=True) def on_exit_clear(self): self.remove_subbehavior('kick-clear') def on_enter_intercept(self): i = skills.intercept.Intercept() self.add_subbehavior(i, 'intercept', required=True) def execute_intercept(self): ball_path = robocup.Segment( main.ball().pos, main.ball().pos + main.ball().vel.normalized() * 10.0) dest = ball_path.nearest_point(self.robot.pos) self.robot.move_to(dest) def on_exit_intercept(self): self.remove_subbehavior('intercept') def execute_block(self): opposing_kicker = evaluation.ball.opponent_with_ball() if opposing_kicker is not None: winEval = robocup.WindowEvaluator(main.system_state()) winEval.excluded_robots = [self.robot] best = winEval.eval_pt_to_our_goal(main.ball().pos)[1] if best is not None: shot_line = robocup.Line(opposing_kicker.pos, main.ball().pos) block_line = robocup.Line( robocup.Point( best.segment.get_pt(0).x - constants.Robot.Radius, constants.Robot.Radius), robocup.Point( best.segment.get_pt(1).x + constants.Robot.Radius, constants.Robot.Radius)) main.system_state().draw_line(block_line, (255, 0, 0), "Debug") dest = block_line.line_intersection(shot_line) dest.x = min(Goalie.MaxX, dest.x) dest.x = max(-Goalie.MaxX, dest.x) self.robot.move_to(dest) return self.robot.move_to( robocup.Point(0, constants.Robot.Radius + Goalie.OFFSET)) def execute_defend(self): dest_x = main.ball().pos.x / constants.Field.Width * Goalie.MaxX self.robot.move_to( robocup.Point(dest_x, constants.Robot.Radius + Goalie.OFFSET)) def role_requirements(self): reqs = super().role_requirements() for req in role_assignment.iterate_role_requirements_tree_leaves(reqs): req.required_shell_id = self.shell_id if self.shell_id != None else -1 return reqs @property def shell_id(self): return self._shell_id @shell_id.setter def shell_id(self, value): self._shell_id = value
def recalculate(self): goalie = self.subbehavior_with_name('goalie') defender1 = self.subbehavior_with_name('defender1') defender2 = self.subbehavior_with_name('defender2') behaviors = [goalie, defender1, defender2] # if we don't have any bots to work with, don't waste time calculating if all(bhvr.robot == None for bhvr in behaviors): return # A threat to our goal - something we'll actively defend against class Threat: def __init__(self, source=None): self.source = source self.ball_acquire_chance = 1.0 self.shot_chance = 1.0 self.assigned_handlers = [] self.best_shot_window = None # an OpponentRobot or Point @property def source(self): return self._source @source.setter def source(self, value): self._source = value # our source can be a Point or an OpponentRobot, this method returns the location of it @property def pos(self): if self.source != None: return self.source if isinstance( self.source, robocup.Point) else self.source.pos # a list of our behaviors that will be defending against this threat # as of now only Defender and Goalie @property def assigned_handlers(self): return self._assigned_handlers @assigned_handlers.setter def assigned_handlers(self, value): self._assigned_handlers = value # our estimate of the chance that this threat will acquire the ball # 1.0 if it already has it # otherwise, a value from 0 to 1 gauging its likelihood to receive a pass @property def ball_acquire_chance(self): return self._ball_acquire_chance @ball_acquire_chance.setter def ball_acquire_chance(self, value): self._ball_acquire_chance = value # our estimate of the chance of this threat making its shot on the goal given that it gets/has the ball # NOTE: this is calculated excluding all of our robots on the field as obstacles @property def shot_chance(self): return self._shot_chance @shot_chance.setter def shot_chance(self, value): self._shot_chance = value # his best window on our goal @property def best_shot_window(self): return self._best_shot_window @best_shot_window.setter def best_shot_window(self, value): self._best_shot_window = value # our assessment of the risk of this threat # should be between 0 and 1 @property def score(self): return self.ball_acquire_chance * self.shot_chance # available behaviors we have to assign to threats # only look at ones that have robots # as we handle threats, we remove the handlers from this list unused_threat_handlers = list( filter(lambda bhvr: bhvr.robot != None, [goalie, defender1, defender2])) def set_block_lines_for_threat_handlers(threat): if len(threat.assigned_handlers) == 0: return # make sure goalie is in the middle if len(threat.assigned_handlers) > 1: if goalie in threat.assigned_handlers: idx = threat.assigned_handlers.index(goalie) if idx != 1: del threat.assigned_handlers[idx] threat.assigned_handlers.insert(1, goalie) if threat.best_shot_window != None: center_line = robocup.Line( threat.pos, threat.best_shot_window.segment.center()) else: center_line = robocup.Line( threat.pos, constants.Field.OurGoalSegment.center()) # find the angular width that each defender can block. We then space these out accordingly angle_widths = [] for handler in threat.assigned_handlers: dist_from_threat = handler.robot.pos.dist_to(threat.pos) w = min( 2.0 * math.atan2(constants.Robot.Radius, dist_from_threat), 0.15) angle_widths.append(w) # start on one edge of our available angle coverage and work counter-clockwise, # assigning block lines to the bots as we go spacing = 0.01 if len( threat.assigned_handlers ) < 3 else 0.0 # spacing between each bot in radians total_angle_coverage = sum(angle_widths) + (len(angle_widths) - 1) * spacing start_vec = center_line.delta().normalized() start_vec.rotate(robocup.Point(0, 0), -total_angle_coverage / 2.0) for i in range(len(angle_widths)): handler = threat.assigned_handlers[i] w = angle_widths[i] start_vec.rotate(robocup.Point(0, 0), w / 2.0) handler.block_line = robocup.Line(threat.pos, threat.pos + start_vec * 10) start_vec.rotate(robocup.Point(0, 0), w / 2.0 + spacing) def recalculate_threat_shot(threat_index): if not isinstance(threat_index, int): raise TypeError("threat_index should be an int") # ignore all of our robots excluded_robots = list(main.our_robots()) # behaviors before this threat are counted as obstacles in their TARGET position (where we've # assigned them to go, not where they are right now) hypothetical_obstacles = [] for t in threats[0:threat_index]: hypothetical_obstacles.extend( map(lambda bhvr: bhvr.move_target, t.assigned_handlers)) threat = threats[threat_index] self.win_eval.excluded_robots.clear() for r in excluded_robots: self.win_eval.add_excluded_robot(r) _, threat.best_shot_window = self.win_eval.eval_pt_to_our_goal( threat.pos) if threat.best_shot_window is not None: threat.shot_chance = threat.best_shot_window.shot_success else: threat.shot_chance = 0.0 threats = [] # secondary threats are those that are somewhat close to our goal and open for a pass # if they're farther than this down the field, we don't consider them threats threat_max_y = constants.Field.Length / 2.0 potential_threats = [ opp for opp in main.their_robots() if opp.pos.y < threat_max_y ] # find the primary threat # if the ball is not moving OR it's moving towards our goal, it's the primary threat # if it's moving, but not towards our goal, the primary threat is the robot on their team most likely to catch it if main.ball().vel.mag() > 0.4: # the line the ball's moving along ball_travel_line = robocup.Line(main.ball().pos, main.ball().pos + main.ball().vel) # this is a shot on the goal! if evaluation.ball.is_moving_towards_our_goal(): ball_threat = Threat(main.ball().pos) ball_threat.ball_acquire_chance = 1.0 ball_threat.shot_chance = 1.0 threats.append(ball_threat) else: # Check for a bot that's about to capture this ball and potentially shoot on the goal # potential_receivers is an array of (OpponentRobot, angle) tuples, where the angle # is the angle between the ball_travel_line and the line from the ball to the opponent # bot - this is our metric for receiver likeliness. potential_receivers = [] for opp in potential_threats: # see if the bot is in the direction the ball is moving if (opp.pos - ball_travel_line.get_pt(0)).dot( ball_travel_line.delta()) > 0: # calculate the angle and add it to the list if it's within reason nearest_pt = ball_travel_line.nearest_point(opp.pos) dx = (nearest_pt - main.ball().pos).mag() dy = (opp.pos - nearest_pt).mag() angle = abs(math.atan2(dy, dx)) if angle < math.pi / 4.0: potential_receivers.append((opp, 1.0)) # choose the receiver with the smallest angle from the ball travel line if len(potential_receivers) > 0: best_receiver_tuple = min( potential_receivers, key=lambda rcrv_tuple: rcrv_tuple[1]) if best_receiver_tuple != None: receiver_threat = Threat(best_receiver_tuple[0]) receiver_threat.ball_acquire_chance = 0.9 # note: this value is arbitrary receiver_threat.shot_chance = 0.9 # FIXME: calculate this threats.append(receiver_threat) else: ball_threat = Threat(main.ball().pos) ball_threat.ball_acquire_chance = 1.0 ball_threat.shot_chance = 0.9 threats.append(ball_threat) else: # primary threat is the ball or the opponent holding it opp_with_ball = evaluation.ball.opponent_with_ball() threat = Threat( opp_with_ball if opp_with_ball != None else main.ball().pos) threat.ball_acquire_chance = 1.0 threat.shot_chance = 1.0 # FIXME: calculate, don't use 1.0 threats.append(threat) # if an opponent has the ball or is potentially about to receive the ball, # we look at potential receivers of it as threats if isinstance(threats[0].source, robocup.OpponentRobot): for opp in filter(lambda t: t.visible, potential_threats): pass_chance = evaluation.passing.eval_pass( main.ball().pos, opp.pos, excluded_robots=[opp]) # give it a small chance because the obstacles in the way could move soon and we don't want to consider it a zero threatos, ) if pass_chance < 0.001: pass_chance = 0.4 # record the threat threat = Threat(opp) threat.ball_acquire_chance = pass_chance threats.append(threat) # Now we evaluate this opponent's shot on the goal # exclude robots that have already been assigned to handle other threats self.win_eval.excluded_robots.clear() for r in map(lambda bhvr: bhvr.robot, unused_threat_handlers): self.win_eval.add_excluded_robot(r) _, threat.best_shot_window = self.win_eval.eval_pt_to_our_goal( opp.pos) if threat.best_shot_window is not None: threat.shot_chance = threat.best_shot_window.shot_success else: threat.shot_chance = 0.0 if threat.shot_chance == 0: # gve it a small chance because the shot could clear up a bit later and we don't want to consider it a zero threat threat.shot_chance = 0.2 else: # the ball isn't possessed by an opponent, so we just look at opponents with shots on the goal for opp in potential_threats: # record the threat lurker = Threat(opp) lurker.ball_acquire_chance = 0.5 # note: this is a bullshit value threats.append(lurker) recalculate_threat_shot(len(threats) - 1) # only consider the top three threats threats.sort(key=lambda threat: threat.score, reverse=True) threats = threats[0:3] # print("sorted threats:") # for idx, t in enumerate(threats): # print("t[" + str(idx) + "]: " + str(t.source) + "shot: " + str(t.shot_chance) + "; pass:"******"; score:" + str(t.score)) # print("sorted threat scores: " + str(list(map(lambda t: str(t.score) + " " + str(t.source), threats)))) # print("Unused handlers: " + str(unused_threat_handlers)) # print("---------------------") smart = False if not smart: # only deal with top two threats threats_to_block = threats[0:2] # print('threats to block: ' + str(list(map(lambda t: t.source, threats_to_block)))) threat_idx = 0 while len(unused_threat_handlers) > 0: threats_to_block[threat_idx].assigned_handlers.append( unused_threat_handlers[0]) del unused_threat_handlers[0] threat_idx = (threat_idx + 1) % len(threats_to_block) for t_idx, t in enumerate(threats_to_block): recalculate_threat_shot(t_idx) set_block_lines_for_threat_handlers(t) else: # assign all of our defenders to do something while len(unused_threat_handlers) > 0: # prioritize by threat score, highest first top_threat = max(threats, key=lambda threat: threat.score) # assign the next handler to this threat handler = unused_threat_handlers[0] top_threat.assigned_handlers.append(handler) del unused_threat_handlers[0] # reassign the block line for each handler of this threat set_block_lines_for_threat_handlers(top_threat) # recalculate the shot now that we have recalculate_threat_shot(0) # tell the bots where to move / what to block and draw some debug stuff for idx, threat in enumerate(threats): # recalculate, including all current bots # FIXME: do we want this? # recalculate_threat_shot(idx) # the line they'll be shooting down/on if threat.best_shot_window != None: shot_line = robocup.Segment( threat.pos, threat.best_shot_window.segment.center()) else: shot_line = robocup.Segment(threat.pos, robocup.Point(0, 0)) # debug output if self.debug: for handler in threat.assigned_handlers: # handler.robot.add_text("Marking: " + str(threat.source), constants.Colors.White, "Defense") main.system_state().draw_circle(handler.move_target, 0.02, constants.Colors.Blue, "Defense") # draw some debug stuff if threat.best_shot_window != None: # draw shot triangle pts = [ threat.pos, threat.best_shot_window.segment.get_pt(0), threat.best_shot_window.segment.get_pt(1) ] shot_color = (255, 0, 0, 150) # translucent red main.system_state().draw_polygon(pts, shot_color, "Defense") main.system_state().draw_segment( threat.best_shot_window.segment, constants.Colors.Red, "Defense") self.win_eval.excluded_robots.clear() _, best_window = self.win_eval.eval_pt_to_our_goal( threat.pos) if best_window is not None: chance = best_window.shot_success else: chance = 0.0 main.system_state().draw_text( "Shot: " + str(int(threat.shot_chance * 100.0)) + "% / " + str(int(chance * 100)) + "%", shot_line.center(), constants.Colors.White, "Defense") # draw pass lines if idx > 0: pass_line = robocup.Segment(main.ball().pos, threat.pos) main.system_state().draw_line(pass_line, constants.Colors.Red, "Defense") main.system_state().draw_text( "Pass: "******"%", pass_line.center(), constants.Colors.White, "Defense")
def execute_marking(self): #main.system_state().draw_line(robocup.Line(self._area.get_pt(0), self._area.get_pt(1)), (127,0,255), "Defender") self.block_robot = self.find_robot_to_block() if self.block_robot is not None: # self.robot.add_text("Blocking Robot " + str(self.block_robot.shell_id()), (255,255,255), "RobotText") pass if self.robot.pos.near_point(robocup.Point(0, 0), self._opponent_avoid_threshold): self.robot.set_avoid_opponents(False) else: self.robot.set_avoid_opponents(True) target = None if self.block_robot is None: target = main.ball().pos + main.ball().vel * 0.3 else: target = self.block_robot.pos + self.block_robot.vel * 0.3 goal_line = robocup.Segment( robocup.Point(-constants.Field.GoalWidth / 2.0, 0), robocup.Point(constants.Field.GoalWidth / 2.0, 0)) self._win_eval.excluded_robots = [self.robot] # TODO defenders should register themselves with some static list on init # TODO make this happen in python-land # for (Defender *f : otherDefenders) # { # if (f->robot) # { # _winEval.exclude.push_back(f->robot->pos); # } # } windows = self._win_eval.eval_pt_to_seg(target, goal_line)[0] best = None goalie = main.our_robot_with_id(main.root_play().goalie_id) if goalie is not None and self.side is not Defender.Side.center: for window in windows: if best is None: best = window elif self.side is Defender.Side.left and window.segment.center.x < goalie.pos.x and window.segment.length > best.segment.length: best = window elif self.side is Defender.Side.right and window.segment.center.x > goalie.pos.x and window.segment.length > best.segment.length: best = window else: best_dist = 0 for window in windows: seg = robocup.Segment(window.segment.center(), main.ball().pos) new_dist = seg.dist_to(self.robot.pos) if best is None or new_dist < best_dist: best = window best_dist = new_dist shoot_seg = None if best is not None: if self.block_robot is not None: dirvec = robocup.Point.direction(self.block_robot.angle * (math.pi / 180.0)) shoot_seg = robocup.Segment( self.block_robot.pos, self.block_robot.pos + dirvec * 7.0) else: shoot_seg = robocup.Segment( main.ball().pos, main.ball().pos + main.ball().vel.normalized() * 7.0) need_task = False if best is not None: winseg = best.segment if main.ball().vel.magsq() > 0.03 and winseg.segment_intersection( shoot_seg) != None: self.robot.move_to(shoot_seg.nearest_point(self.robot.pos)) self.robot.face_none() else: winsize = winseg.length() if winsize < constants.Ball.Radius: need_task = True else: arc = robocup.Circle(robocup.Point(0, 0), self._defend_goal_radius) shot = robocup.Line(winseg.center(), target) dest = [robocup.Point(0, 0), robocup.Point(0, 0)] intersected, dest[0], dest[1] = shot.intersects_circle(arc) if intersected: self.robot.move_to( dest[0] if dest[0].y > 0 else dest[1]) if self.block_robot is not None: self.robot.face(self.block_robot.pos) else: self.robot.face(main.ball().pos) else: need_task = True if need_task: self.robot.face(main.ball().pos) backVec = robocup.Point(1, 0) backPos = robocup.Point(-constants.Field.Width / 2, 0) shotVec = robocup.Point(main.ball().pos - self.robot.pos) backVecRot = robocup.Point(backVec.perp_ccw()) facing_back_line = (backVecRot.dot(shotVec) < 0) if not facing_back_line and self.robot.has_ball(): if self.robot.has_chipper(): self.robot.chip(255) else: self.robot.kick(255)
def execute_area_marking(self): if self.robot.pos.near_point(robocup.Point(0, 0), self._opponent_avoid_threshold): self.robot.set_avoid_opponents(False) else: self.robot.set_avoid_opponents(True) goal_target = robocup.Point(0, -constants.Field.GoalDepth / 2.0) goal_line = robocup.Segment( robocup.Point(-constants.Field.GoalWidth / 2.0, 0), robocup.Point(constants.Field.GoalWidth / 2.0, 0)) if self.side is Defender.Side.left: goal_line.get_pt(1).x = 0 goal_line.get_pt(1).y = 0 if self.side is Defender.Side.right: goal_line.get_pt(0).x = 0 goal_line.get_pt(0).y = 0 for robot in main.system_state().their_robots: self._win_eval.excluded_robots.append(robot) if main.root_play().goalie_id is not None: self._win_eval.excluded_robots.append( main.our_robot_with_id(main.root_play().goalie_id)) # TODO (cpp line 186) # windows = self._win_eval. windows = [] best = None angle = 0.0 for window in windows: if best is None: best = window angle = window.a0 - window.a1 elif window.a0 - window.a1 > angle: best = window angle = window.a0 - window.a1 shootline = robocup.Segment(robocup.Point(0, 0), robocup.Point(0, 0)) if best is not None: angle = (best.a0 + best.a1) / 2.0 shootline = robocup.Segment( self._win_eval.origin(), robocup.Point.direction( angle * (math.pi / 180.0))) # FIXME :no origin. main.system_state().draw_line(shootline, (255, 0, 0), "Defender") need_task = False if best is not None: winseg = best.segment arc = robocup.Circle(robocup.Point(0, 0), self._defend_goal_radius) shot = robocup.Line(shootline.get_pt(0), shootline.get_pt(1)) dest = [robocup.Point(0, 0), robocup.Point(0, 0)] intersected, dest[0], dest[1] = shot.intersects_circle(arc) if intersected: self.robot.move_to(dest[0] if dest[0].y > 0 else dest[1]) else: need_task = True if need_task: self.robot.face(main.ball().pos) if main.ball().pos.y < constants.Field.Length / 2.0: self.robot.set_dribble_speed(255) backVec = robocup.Point(1, 0) backPos = robocup.Point(-constants.Field.Width / 2, 0) shotVec = robocup.Point(main.ball().pos - self.robot.pos) backVecRot = robocup.Point(backVec.perp_ccw()) facing_back_line = (backVecRot.dot(shotVec) < 0) if not facing_back_line and self.robot.has_ball(): if self.robot.has_chipper(): self.robot.chip(255) else: self.robot.kick(255)
class SubmissiveGoalie( single_robot_composite_behavior.SingleRobotCompositeBehavior): MaxX = constants.Field.GoalWidth / 2.0 SegmentY = constants.Robot.Radius + 0.05 # The segment we stay on during the 'block' state # It's right in front of the goal RobotSegment = robocup.Segment(robocup.Point(-MaxX, SegmentY), robocup.Point(MaxX, SegmentY)) class State(enum.Enum): "Actively blocking based on a given threat" block = 2 "The ball is moving towards our goal and we should catch it." intercept = 3 "Get the ball out of our defense area." clear = 4 def __init__(self): super().__init__(continuous=True) for substate in SubmissiveGoalie.State: self.add_state(substate, behavior.Behavior.State.running) self.add_transition(behavior.Behavior.State.start, SubmissiveGoalie.State.block, lambda: True, "immediately") non_block_states = [ s for s in SubmissiveGoalie.State if s != SubmissiveGoalie.State.block ] for state in [ s2 for s2 in SubmissiveGoalie.State if s2 != SubmissiveGoalie.State.intercept ]: self.add_transition( state, SubmissiveGoalie.State.intercept, lambda: evaluation.ball.is_moving_towards_our_goal(), "ball coming towards our goal") for state in [ s2 for s2 in SubmissiveGoalie.State if s2 != SubmissiveGoalie.State.clear ]: self.add_transition( state, SubmissiveGoalie.State.clear, lambda: evaluation.ball.is_in_our_goalie_zone() and not evaluation.ball.is_moving_towards_our_goal() and main.ball( ).vel.mag() < 0.4 and evaluation.ball.opponent_with_ball() is None, "ball in our goalie box, but not headed toward goal") for state in non_block_states: self.add_transition( state, SubmissiveGoalie.State.block, lambda: not evaluation.ball.is_in_our_goalie_zone( ) and not evaluation.ball.is_moving_towards_our_goal(), 'ball not in goal or moving towards it') self.block_line = None self._move_target = robocup.Point(0, 0) # the line we expect a threat to shoot from # sits on the intersection of this line and the goalie segment @property def block_line(self): return self._block_line @block_line.setter def block_line(self, value): self._block_line = value if self.block_line == None: self._move_target = SubmissiveGoalie.RobotSegment.center() else: self._move_target = self.block_line.line_intersection( SubmissiveGoalie.RobotSegment) self._move_target.x = min( max(self._move_target.x, -SubmissiveGoalie.MaxX), SubmissiveGoalie.MaxX) # The point we'll be going to in order to block the given block_line @property def move_target(self): return self._move_target # note that execute_running() gets called BEFORE any of the execute_SUBSTATE methods gets called def execute_running(self): self.robot.face(main.ball().pos) def on_enter_clear(self): # FIXME: what we really want is a less-precise LineKick # this will require a Capture behavior that doesn't wait for the ball to stop kick = skills.pivot_kick.PivotKick() # TODO: the below dribble speed is best for a 2011 bot # kick.dribble_speed = constants.Robot.Dribbler.MaxPower / 3.5 # we use low error thresholds here # the goalie isn't trying to make a shot, he just wants get the ball the **** out of there kick.aim_params['error_threshold'] = 1.0 kick.aim_params['max_steady_ang_vel'] = 12 # chip kick.chip_power = constants.Robot.Chipper.MaxPower kick.use_chipper = True kick.target = robocup.Segment( robocup.Point(-constants.Field.Width / 2, constants.Field.Length), robocup.Point(constants.Field.Width / 2, constants.Field.Length)) # FIXME: if the goalie has a fault, resort to bump self.add_subbehavior(kick, 'kick-clear', required=True) def on_exit_clear(self): self.remove_subbehavior('kick-clear') def on_enter_intercept(self): i = skills.intercept.Intercept() i.shape_constraint = SubmissiveGoalie.RobotSegment self.add_subbehavior(i, 'intercept', required=True) def on_exit_intercept(self): self.remove_subbehavior('intercept') def on_enter_block(self): move = skills.move.Move() self.add_subbehavior(move, 'move', required=True) def execute_block(self): move = self.subbehavior_with_name('move') move.pos = self.move_target def on_exit_block(self): self.remove_subbehavior('move') def role_requirements(self): reqs = super().role_requirements() for req in role_assignment.iterate_role_requirements_tree_leaves(reqs): req.required_shell_id = self.shell_id return reqs @property def shell_id(self): return self._shell_id @shell_id.setter def shell_id(self, value): self._shell_id = value
class Field: Length = 9.00 Width = 6.00 Border = 0.70 LineWidth = 0.01 GoalWidth = 1.000 GoalDepth = 0.180 GoalHeight = 0.160 # Distance of the penalty marker from the goal line PenaltyDist = 1.0 PenaltyDiam = 0.010 # Radius of the goal arcs ArcRadius = 1.0 # diameter of the center circle CenterRadius = 0.5 CenterDiameter = 1.0 # flat area for defense markings GoalFlat = 0.5 FloorLength = Length + 2.0 * Border FloorWidth = Width + 2.0 * Border CenterPoint = robocup.Point(0.0, Length / 2.0) OurGoalZoneShape = robocup.CompositeShape() OurGoalZoneShape.add_shape( robocup.Circle(robocup.Point(-GoalFlat / 2.0, 0), ArcRadius)) OurGoalZoneShape.add_shape( robocup.Circle(robocup.Point(GoalFlat / 2.0, 0), ArcRadius)) OurGoalZoneShape.add_shape( robocup.Rect(robocup.Point(-GoalFlat / 2.0, ArcRadius), robocup.Point(GoalFlat / 2.0, 0))) TheirGoalShape = robocup.CompositeShape() TheirGoalShape.add_shape( robocup.Circle(robocup.Point(-GoalFlat / 2.0, Length), ArcRadius)) TheirGoalShape.add_shape( robocup.Circle(robocup.Point(GoalFlat / 2.0, Length), ArcRadius)) TheirGoalShape.add_shape( robocup.Rect(robocup.Point(-GoalFlat / 2.0, Length), robocup.Point(GoalFlat / 2.0, Length - ArcRadius))) FieldBorders = [ robocup.Line(robocup.Point(-Width / 2.0, 0), robocup.Point(-Width / 2.0, Length)), robocup.Line(robocup.Point(-Width / 2.0, Length), robocup.Point(Width / 2.0, Length)), robocup.Line(robocup.Point(Width / 2.0, Length), robocup.Point(Width / 2.0, 0)), robocup.Line(robocup.Point(Width / 2.0, 0), robocup.Point(-Width / 2.0, 0)) ] FieldRect = robocup.Rect(robocup.Point(-Width / 2.0, 0), robocup.Point(Width / 2.0, Length)) TheirGoalSegment = robocup.Segment(robocup.Point(GoalWidth / 2.0, Length), robocup.Point(-GoalWidth / 2.0, Length)) OurGoalSegment = robocup.Segment(robocup.Point(GoalWidth / 2.0, 0), robocup.Point(-GoalWidth / 2.0, 0)) TheirHalf = robocup.Rect(robocup.Point(-Width / 2, Length), robocup.Point(Width / 2, Length / 2)) OurHalf = robocup.Rect(robocup.Point(-Width / 2, 0), robocup.Point(Width / 2, Length / 2))
def setFieldConstantsFromField_Dimensions(value): Field.Length = value.Length() Field.Width = value.Width() Field.Border = value.Border() Field.LineWidth = value.LineWidth() Field.GoalWidth = value.GoalWidth() Field.GoalDepth = value.GoalDepth() Field.GoalHeight = value.GoalHeight() Field.PenaltyDist = value.PenaltyDist() Field.PenaltyDiam = value.PenaltyDiam() Field.ArcRadius = value.ArcRadius() Field.CenterRadius = value.CenterRadius() Field.CenterDiameter = value.CenterDiameter() Field.GoalFlat = value.GoalFlat() Field.FloorLength = value.FloorLength() Field.FloorWidth = value.FloorWidth() Field.CenterPoint = robocup.Point(0.0, Field.Length / 2.0) Field.OurGoalZoneShape = robocup.CompositeShape() Field.OurGoalZoneShape.add_shape( robocup.Circle(robocup.Point(-Field.GoalFlat / 2.0, 0), Field.ArcRadius)) Field.OurGoalZoneShape.add_shape( robocup.Circle(robocup.Point(Field.GoalFlat / 2.0, 0), Field.ArcRadius)) Field.OurGoalZoneShape.add_shape( robocup.Rect(robocup.Point(-Field.GoalFlat / 2.0, Field.ArcRadius), robocup.Point(Field.GoalFlat / 2.0, 0))) Field.TheirGoalShape = robocup.CompositeShape() Field.TheirGoalShape.add_shape( robocup.Circle(robocup.Point(-Field.GoalFlat / 2.0, Field.Length), Field.ArcRadius)) Field.TheirGoalShape.add_shape( robocup.Circle(robocup.Point(Field.GoalFlat / 2.0, Field.Length), Field.ArcRadius)) Field.TheirGoalShape.add_shape( robocup.Rect( robocup.Point(-Field.GoalFlat / 2.0, Field.Length), robocup.Point(Field.GoalFlat / 2.0, Field.Length - Field.ArcRadius))) Field.TheirGoalSegment = robocup.Segment( robocup.Point(Field.GoalWidth / 2.0, Field.Length), robocup.Point(-Field.GoalWidth / 2.0, Field.Length)) Field.OurGoalSegment = robocup.Segment( robocup.Point(Field.GoalWidth / 2.0, 0), robocup.Point(-Field.GoalWidth / 2.0, 0)) Field.TheirHalf = robocup.Rect( robocup.Point(-Field.Width / 2, Field.Length), robocup.Point(Field.Width / 2, Field.Length / 2)) Field.OurHalf = robocup.Rect( robocup.Point(-Field.Width / 2, 0), robocup.Point(Field.Width / 2, Field.Length / 2)) Field.FieldBorders = [ robocup.Line(robocup.Point(-Width / 2.0, 0), robocup.Point(-Width / 2.0, Length)), robocup.Line(robocup.Point(-Width / 2.0, Length), robocup.Point(Width / 2.0, Length)), robocup.Line(robocup.Point(Width / 2.0, Length), robocup.Point(Width / 2.0, 0)), robocup.Line(robocup.Point(Width / 2.0, 0), robocup.Point(-Width / 2.0, 0)) ] Field.FieldRect = robocup.Rect(robocup.Point(-Width / 2.0, 0), robocup.Point(Width / 2.0, Length))
def set_defender_block_lines(self, threats_to_block, assigned_handlers): goalie = self.subbehavior_with_name('goalie') defender1 = self.subbehavior_with_name('defender1') defender2 = self.subbehavior_with_name('defender2') # Check keep defenders from occupying the same spot # it will break if you change this handlers = [goalie, defender1, defender2] # For each threat for threat_idx in range(len(threats_to_block)): # Get the threat pos and score threat = threats_to_block[threat_idx] # Grab the list of handlers assigned to this threat assigned_handler = assigned_handlers[threat_idx] # Exclude any robots we are about to assign to find the threats best shot self.kick_eval.excluded_robots.clear() for handler in handlers: self.kick_eval.add_excluded_robot(handler.robot) # Add opp robot into exclude list, assuming it is not a ball if (threat[2] is not None): self.kick_eval.add_excluded_robot(threat[2]) # If nobody is assigned, move to next one if len(assigned_handler) == 0: continue # Put goalie in the middle if possible if len(assigned_handler) > 1: if goalie in assigned_handler: idx = assigned_handler.index(goalie) if idx != 1: del assigned_handler[idx] assigned_handler.insert(1, goalie) # Get best shot from that threat postiion point, shot_chance = self.kick_eval.eval_pt_to_our_goal(threat[0]) shot_line = robocup.Line(threat[0], point) # find the angular width that each defender can block. We then space these out accordingly angle_widths = [] for handler in assigned_handler: dist_from_threat = handler.robot.pos.dist_to(threat[0]) w = min( 2.0 * math.atan2(constants.Robot.Radius, dist_from_threat), 0.15) angle_widths.append(w) # start on one edge of our available angle coverage and work counter-clockwise, # assigning block lines to the bots as we go spacing = 0.01 if len( assigned_handler ) < 3 else 0.0 # spacing between each bot in radians total_angle_coverage = sum(angle_widths) + (len(angle_widths) - 1) * spacing start_vec = shot_line.delta().normalized() start_vec.rotate(robocup.Point(0, 0), -total_angle_coverage / 2.0) for i in range(len(angle_widths)): handler = assigned_handler[i] w = angle_widths[i] start_vec.rotate(robocup.Point(0, 0), w / 2.0) handler.block_line = robocup.Line(threat[0], threat[0] + start_vec * 10) start_vec.rotate(robocup.Point(0, 0), w / 2.0 + spacing) # Draw all the debug stuff if self.debug: main.debug_drawer().draw_line(shot_line, constants.Colors.Red, "Defense-Shot Line") main.debug_drawer().draw_text( "Shot: " + str(int(shot_chance * 100.0)), threat[0], constants.Colors.White, "Defense-Shot Percent") # Other threats besides ball if threat_idx > 0: pass_line = robocup.Segment(main.ball().pos, threat[0]) main.debug_drawer().draw_line(pass_line, constants.Colors.Red, "Defense-Pass Line") # keep defenders from occupying the same spot # only matters if there are 2 defenders (and the goalie) if len(handlers) == 3: handler1 = handlers[1] handler2 = handlers[2] #vector between the 2 points overlap = handler2.move_target - handler1.move_target #if the robots overlap if overlap.mag() < (2 * constants.Robot.Radius) + .005: #move the robots away from each other overlap = overlap - (overlap.normalized() * 1.8 * constants.Robot.Radius) handler1._move_target += overlap handler2._move_target -= overlap
def create_lineup(self): xsize = constants.Field.Width / 2 - .5 return robocup.Segment(robocup.Point(xsize, .25), robocup.Point(xsize, 1.5))