Skip to content

Instantly share code, notes, and snippets.

@JNeiger
Last active November 6, 2018 21:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JNeiger/73802a57607539b768618fc2d5ec752e to your computer and use it in GitHub Desktop.
Save JNeiger/73802a57607539b768618fc2d5ec752e to your computer and use it in GitHub Desktop.
import main
import robocup
import behavior
import constants
import enum
import standard_play
import evaluation.shooting
import evaluation.passing
import tactics.coordinated_pass
import skills.move
import skills.capture
class PassOrShoot(standard_play.StandardPlay):
## Note:
# Place all constants here so they are easy to see and change later on when
# we are tuning plays and such. There should be no "magic" numbers within
# the code
# You can reference these by PassOrShoot.LEFT_PASS_X_PERCENT
# How far left should the left receiver be in terms of % of 1/2 field width
LEFT_PASS_X_PERCENT = .5
# How far right should the right receiver be in terms of % of 1/2 field width
RIGHT_PASS_X_PERCENT = .5
# How far up the field should the left receiver be in terms of % of field length
LEFT_PASS_Y_PERCENT = .5
# How far up the field should the right receiver be in terms of % of field length
RIGHT_PASS_Y_PERCENT = .5
class State(enum.Enum):
## Note:
# All states are declared here. Order doesn't matter, but in general
# try to place them in the order they would execute (ish) and label
# them ascending from the number 1. Also place a short description of
# state above so it's easy for other people to see what's happening.
# Collect the ball / Move into position
setup = 1
# Pass the ball to the better robot
passing = 2
# Shoot the ball at goal
shooting = 3
def __init__(self):
super().__init__(continuous=False)
# Add all the states to running since we will never complete
# this play
for s in PassOrShoot.State:
self.add_state(s, behavior.Behavior.State.running)
## Note:
# When defining points in your play, always try to use percents of the field size
# since almost every field will be slightly different. Our sim is a different size compared to
# comp compared to our field at the shop etc.
# Where to setup the reciever robots on the left or right side
self.left_pass_pos = robocup.Point(-constants.Field.Width/2 * PassOrShoot.LEFT_PASS_X_PERCENT,
constants.Field.Length * PassOrShoot.LEFT_PASS_Y_PERCENT)
self.right_pass_pos = robocup.Point(constants.Field.Width/2 * PassOrShoot.RIGHT_PASS_X_PERCENT,
constants.Field.Length * PassOrShoot.RIGHT_PASS_Y_PERCENT)
## Note:
# This is a personal preference, but I like initializing my global variables here
# the constructor just so I know what the default value is later on. Additionally,
# I can put a comment down on what this variable represents
# Chance to score through the left/right pass or shooting directly
self.left_score_chance = 0
self.right_score_chance = 0
self.shot_score_chance = 0
# Target pass point (either left receiver or right) based on score chance
self.target_pass_pos = None
## Note:
# I like placing a comment in front of each transition just so
# you don't have to try and read the lambda to figure out what it is doing
#
## Note: You can't use self.all_subbehaviors_completed() as there is a defense
# implicitly added and their subbehaviors never complete
# Setup the starting transition
self.add_transition(behavior.Behavior.State.start,
PassOrShoot.State.setup, lambda: True,
'immediately')
# If we have a better chance to pass first, do that
self.add_transition(
PassOrShoot.State.setup,
PassOrShoot.State.passing,
lambda: self.all_basic_subbehaviors_completed() and
(self.shot_score_chance < self.left_score_chance or
self.shot_score_chance < self.right_score_chance),
'Passing is best')
# If the shot is better than both passes, do that
self.add_transition(
PassOrShoot.State.setup,
PassOrShoot.State.shooting,
lambda: self.all_basic_subbehaviors_completed() and
(self.shot_score_chance > self.left_score_chance and
self.shot_score_chance > self.right_score_chance),
'Shot is best')
# Finished pass, now shooting
self.add_transition(
PassOrShoot.State.passing,
PassOrShoot.State.shooting,
lambda: self.subbehavior_with_name('pass').is_done_running(),
'Passing done. Shooting')
# Restart the play after it finishes
self.add_transition(
PassOrShoot.State.shooting,
PassOrShoot.State.setup,
lambda: self.subbehavior_with_name('shot').is_done_running(),
'restarting')
# Checks if the 2 moves and capture are done
def all_basic_subbehaviors_completed(self):
return (self.subbehavior_with_name('left').is_done_running() and
self.subbehavior_with_name('right').is_done_running() and
self.subbehavior_with_name('collector').is_done_running())
## Note:
# I try to setup these on_enter, execute, and on_exit functions in the same order
# they appear in the state list. It's just a style thing and makes things nice
def on_enter_setup(self):
# Move two robots to their target pass receive locations and collect the ball
move_left = skills.move.Move(self.left_pass_pos)
move_right = skills.move.Move(self.right_pass_pos)
capture = skills.capture.Capture()
self.add_subbehavior(move_left, 'left', required=True)
self.add_subbehavior(move_right, 'right', required=True)
self.add_subbehavior(capture, 'collector', required=True)
## Note:
# I do all the calculations here instead of the transition lambda so you only have to
# do this calculation once. If you placed it in the lambda, it would happen twice.
# This matters on things like passing positioning calculations
def execute_setup(self):
# Get left and right passing chance
left_pass_chance = evaluation.passing.eval_pass(main.ball().pos, self.left_pass_pos)
right_pass_chance = evaluation.passing.eval_pass(main.ball().pos, self.right_pass_pos)
# Get left and right shooting chance
left_shot_chance = evaluation.shooting.eval_shot(self.left_pass_pos)
right_shot_chance = evaluation.shooting.eval_shot(self.right_pass_pos)
# Get just shooting chance
straight_shot_chance = evaluation.shooting.eval_shot(main.ball().pos)
# Combine
self.left_score_chance = left_pass_chance * left_shot_chance
self.right_score_chance = right_pass_chance * right_shot_chance
self.shot_score_chance = straight_shot_chance
## Note:
# I record the better target pass location here so everything
# is bundled together. All the pass decision stuff is here,
# the passing function only passes to the specific point
# If right pass is better
if self.left_score_chance < self.right_score_chance:
self.target_pass_pos = self.right_pass_pos
else: # If left pass is better
self.target_pass_pos = self.left_pass_pos
def on_exit_setup(self):
self.remove_subbehavior('left')
self.remove_subbehavior('right')
self.remove_subbehavior('collector')
def on_enter_passing(self):
# Add pass with target being the better robot to pass to
robot_pass = tactics.coordinated_pass.CoordinatedPass(self.target_pass_pos)
self.add_subbehavior(robot_pass, 'pass', required=True)
def on_exit_passing(self):
self.remove_subbehavior('pass')
def on_enter_shooting(self):
# Shoot anywhere on their goal segment
shot = skills.pivot_kick.PivotKick()
shot.target = constants.Field.TheirGoalSegment
self.add_subbehavior(shot, 'shot', required=True)
def on_exit_shooting(self):
self.remove_subbehavior('shot')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment