Last active
November 6, 2018 21:15
-
-
Save JNeiger/73802a57607539b768618fc2d5ec752e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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