Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed ball touches ground detection #9

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 132 additions & 9 deletions rlbottraining/common_graders/rl_graders.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This module contains graders which mimic the the behaviour of Rocket League custom training.
"""

import math

from dataclasses import dataclass
from typing import Optional, Mapping, Union
Expand All @@ -15,27 +16,149 @@
from rlbottraining.common_graders.compound_grader import CompoundGrader
from rlbottraining.common_graders.timeout import FailOnTimeout, PassOnTimeout
from rlbottraining.common_graders.goal_grader import PassOnGoalForAllyTeam
from rlbot.training.training import Pass, Fail, Grade
import copy


class RocketLeagueStrikerGrader(CompoundGrader):
"""
A Grader which aims to match the striker training.
"""

def __init__(self, timeout_seconds=4.0, ally_team=0):
def __init__(self, timeout_seconds=4.0, ally_team=0, timeout_override=False, ground_override=False):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add documentation about what the overrides do.
Will users of this class care about this configurability?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably users will not care, I cant think of a good use case beyond testing the grader.

self.timeout_override = timeout_override
self.ground_override = ground_override
super().__init__([
PassOnGoalForAllyTeam(ally_team),
FailOnBallOnGroundAfterTimeout(timeout_seconds),
FailOnBallOnGround(),
FailOnTimeout(timeout_seconds),
])

class FailOnBallOnGroundAfterTimeout(FailOnTimeout):
def on_tick(self, tick: TrainingTickPacket) -> Optional[Grade]:
grades = [grader.on_tick(tick) for grader in self.graders]
return self.grade_chooser(grades)

def grade_chooser(self, grades) -> Optional[Grade]:
"""
Chooses the importance of the grades
"""

timeout = isinstance(grades[2], Fail) # True if timed out, false otherwise
ball_on_ground = isinstance(grades[1], Fail) # True if ball touched the ground, false otherwise
goal = isinstance(grades[0], Pass) # True if ball there was a goal, false otherwise

if goal: # scoring and touching the ground on the same tick prefer scoring
return grades[0]
elif timeout:
if self.timeout_override:
return grades[2]
elif ball_on_ground:
return grades[1]
elif self.ground_override and ball_on_ground:
return grades[1]
return None


class FailOnBallOnGround(Grader):
def __init__(self):
self.previous_ball = None

class FailDueToGroundHit(Fail):
def __init__(self):
pass

def __repr__(self):
return f'{super().__repr__()}: Ball hit the ground'


def set_previous_info(self, ball):
self.previous_ball = copy.deepcopy(ball)

def on_tick(self, tick: TrainingTickPacket) -> Optional[Grade]:
grade = super().on_tick(tick)
if grade is None:
packet = tick.game_tick_packet
ball = packet.game_ball.physics
hit_ground = False

if self.previous_ball is None:
self.set_previous_info(ball)
return None
assert isinstance(grade, FailOnTimeout.FailDueToTimeout)
ball = tick.game_tick_packet.game_ball.physics
if ball.location.z < 100 and ball.velocity.z >= 0:
return grade

max_ang_vel = 5.9999601985025075 #Max angular velocity possible
previous_angular_velocity_norm = math.sqrt(self.previous_ball.angular_velocity.x**2 +
self.previous_ball.angular_velocity.y**2 +
self.previous_ball.angular_velocity.z**2 )

angular_velocity_norm = math.sqrt(ball.angular_velocity.x**2 +
ball.angular_velocity.y**2 +
ball.angular_velocity.z**2 )

if ball.location.z <= 1900: #Making sure it doesnt count the ceiling

if ball.angular_velocity.x != self.previous_ball.angular_velocity.x or \
ball.angular_velocity.y != self.previous_ball.angular_velocity.y or \
ball.angular_velocity.z != self.previous_ball.angular_velocity.z:
# If the ball hit anything its angular velocity will change in at least one axis
if (previous_angular_velocity_norm or angular_velocity_norm) == max_ang_vel:
'''
Implement correct way of dealing with maximum angular velocity
angular velocity gets rescaled which may change an axis that truly did not change, only got rescaled
'''
#Todo: implement detection for this case
self.set_previous_info(ball)
return None

elif self.previous_ball.angular_velocity.z == ball.angular_velocity.z:
'''
Ball hit a flat horizontal surface
this only changes angular velocity z
we still have to deal with distingushing from a ground touch, or a bot touch
This will not hold true if the ball is being pushed on the ground by a bot.
Todo: detect pushing from bot
'''
print('INFO')
print(packet.game_ball.latest_touch.time_seconds)
print(packet.game_info.seconds_elapsed - (2/60))

if packet.game_ball.latest_touch.time_seconds >= (packet.game_info.seconds_elapsed - (2/60)):
'''
if there was a touch this tick the bot may be dribbling
'''
#Todo: distinguish dribble from pushing on ground.
#Todo: write tests to test pushing.
self.set_previous_info(ball)
return None
else:
hit_ground = True
else:
'''
detect if is being pushed
'''
if packet.game_ball.latest_touch.time_seconds != packet.game_info.seconds_elapsed:
'''
ball not hit by a bot on this tick
'''
self.set_previous_info(ball)
return None
else:
'''
detect if its pushing along the ground or not
'''
#Todo: implement detection for this case
pass

velocity_norm = math.sqrt(ball.velocity.x**2 +
ball.velocity.y**2 +
ball.velocity.z**2)


previous_velocity_norm = math.sqrt(self.previous_ball.velocity.x ** 2 +
self.previous_ball.velocity.y ** 2 +
self.previous_ball.velocity.z ** 2 )
if previous_velocity_norm == velocity_norm == 0:
'''
ball is stopped on ground
'''
hit_ground = True
self.set_previous_info(ball)
if hit_ground:
return self.FailDueToGroundHit()
Empty file.
10 changes: 10 additions & 0 deletions rlbottraining/example_bots/prop_bot/prop_bot.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Locations]
# Path to loadout config from runner
looks_config = ./prop_bot_looks.cfg

# Bot's python file.
# Only need this if RLBot controlled
python_file = prop_bot.py

# The name that will be displayed in game
name = Prop Bot
14 changes: 14 additions & 0 deletions rlbottraining/example_bots/prop_bot/prop_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
from rlbot.utils.structures.game_data_struct import GameTickPacket

class PropBot(BaseAgent):
"""
A bot which just sits there like a prop.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BrickBot already does this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brick bot has handbrake on, this causes the ball to move it quite a bit when it hits it.

"""

def get_output(self, game_tick_packet: GameTickPacket) -> SimpleControllerState:
seconds = game_tick_packet.game_info.seconds_elapsed
controller_state = SimpleControllerState()
controller_state.steer = 0
controller_state.handbrake = 0
return controller_state
56 changes: 56 additions & 0 deletions rlbottraining/example_bots/prop_bot/prop_bot_looks.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[Bot Loadout]
# Primary Color selection
team_color_id = 11
# Secondary Color selection
custom_color_id = 74
# Car type (Octane, Merc, etc
car_id = 23
# Type of decal
decal_id = 1618
# Wheel selection
wheels_id = 1656
# Boost selection
boost_id = 0
# Antenna Selection
antenna_id = 0
# Hat Selection
hat_id = 0
# Paint Type (for first color)
paint_finish_id = 1978
# Paint Type (for secondary color)
custom_finish_id = 1978
# Engine Audio Selection
engine_audio_id = 1786
# Car trail Selection
trails_id = 1898
# Goal Explosion Selection
goal_explosion_id = 1971

[Bot Loadout Orange]
# Primary Color selection
team_color_id = 11
# Secondary Color selection
custom_color_id = 74
# Car type (Octane, Merc, etc
car_id = 23
# Type of decal
decal_id = 1618
# Wheel selection
wheels_id = 1656
# Boost selection
boost_id = 0
# Antenna Selection
antenna_id = 0
# Hat Selection
hat_id = 0
# Paint Type (for first color)
paint_finish_id = 1978
# Paint Type (for secondary color)
custom_finish_id = 1978
# Engine Audio Selection
engine_audio_id = 1786
# Car trail Selection
trails_id = 1898
# Goal Explosion Selection
goal_explosion_id = 1971

1 change: 1 addition & 0 deletions rlbottraining/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class BotConfigs:
Contains paths to example bots included in this repo.
"""
brick_bot = _example_bot_dir / 'brick_bot' / 'brick_bot.cfg'
prop_bot = _example_bot_dir / 'prop_bot' / 'prop_bot.cfg'
simple_bot = _example_bot_dir / 'simple_bot' / 'simple_bot.cfg'
line_goalie = _example_bot_dir / 'line_goalie' / 'line_goalie.cfg'

Expand Down
Empty file.
Loading