Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into python-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
mtheall committed Oct 30, 2023
2 parents 826ad12 + 94d9a2e commit ad427a3
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 80 deletions.
2 changes: 1 addition & 1 deletion python-mtheall/Arena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ std::array<std::uint32_t, 34> const soccarIndexMapping = {
makeKey (-3584, 2484),
makeKey ( 3584, 2484),
makeKey ( 0, 2816),
makeKey (- 940, 3310),
makeKey (- 940, 3308),
makeKey ( 940, 3308),
makeKey (-3072, 4096),
makeKey ( 3072, 4096),
Expand Down
2 changes: 1 addition & 1 deletion python-mtheall/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ wheel: build
$(PYTHON) ../setup.py bdist_wheel --py-limited-api=cp34

install: wheel
$(PYTHON) -m pip install --force-reinstall --user ./dist/RocketSim-1.3.0a6-cp34-abi3-linux_x86_64.whl
$(PYTHON) -m pip install --force-reinstall --user ./dist/RocketSim-1.3.0a7-cp34-abi3-linux_x86_64.whl

clean:
$(RM) -r build/ dist/ RocketSim.egg-info/
56 changes: 30 additions & 26 deletions python-mtheall/regression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,51 +153,55 @@ def test_gym_state_inverse(self):
# cars should spawn mirror of each other
arena.reset_kickoff()

for i in range(100):
controls = rs.CarControls(throttle=1.0, yaw=1.0, pitch=1.0, roll=1.0, jump=True)
for i in range(150):
if i < 10:
# jump for a few frames to avoid flipping
controls = rs.CarControls(throttle=1.0, jump=True)
else:
controls = rs.CarControls(throttle=1.0, yaw=1.0, pitch=1.0, roll=1.0, jump=True)

car_a.set_controls(controls)
car_b.set_controls(controls)
arena.step()

gym_state = arena.get_gym_state()

state_a = gym_state[3][0] # blue team, standard
state_b = gym_state[4][1] # orange team, inverted

# pos
self.assertTrue(abs(gym_state[3][0][11] - gym_state[4][1][11]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][12] - gym_state[4][1][12]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][13] - gym_state[4][1][13]) < 1e-3)
if not np.amax(np.absolute(state_a[11:14] - state_b[11:14])) < 1e-3:
print(arena.tick_count, state_a[11:14], state_b[11:14])
self.assertTrue(np.amax(np.absolute(state_a[11:14] - state_b[11:14])) < 1e-3)

# quat
q1 = gym_state[3][0][14:18]
q2 = gym_state[4][1][14:18]
q1 = state_a[14:18]
q2 = state_b[14:18]
t1 = np.amax(np.absolute(q1 + q2))
t2 = np.amax(np.absolute(q1 - q2))
self.assertTrue(min(t1, t2) < 1e-6)
if not min(t1, t2) < 1e-3:
print(arena.tick_count, q1, q2)
self.assertTrue(min(t1, t2) < 1e-3)

# vel
self.assertTrue(abs(gym_state[3][0][18] - gym_state[4][1][18]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][19] - gym_state[4][1][19]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][20] - gym_state[4][1][20]) < 1e-3)
if not np.amax(np.absolute(state_a[18:21] - state_b[18:21])) < 1e-3:
print(arena.tick_count, state_a[18:21], state_b[18:21])
self.assertTrue(np.amax(np.absolute(state_a[18:21] - state_b[18:21])) < 1e-3)

# ang_vel
self.assertTrue(abs(gym_state[3][0][21] - gym_state[4][1][21]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][22] - gym_state[4][1][22]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][23] - gym_state[4][1][23]) < 1e-3)
if not np.amax(np.absolute(state_a[21:24] - state_b[21:24])) < 1e-3:
print(arena.tick_count, state_a[21:24], state_b[21:24])
self.assertTrue(np.amax(np.absolute(state_a[21:24] - state_b[21:24])) < 1e-3)

# mat3
self.assertTrue(abs(gym_state[3][0][24] - gym_state[4][1][24]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][25] - gym_state[4][1][25]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][26] - gym_state[4][1][26]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][27] - gym_state[4][1][27]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][28] - gym_state[4][1][28]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][29] - gym_state[4][1][29]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][30] - gym_state[4][1][30]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][31] - gym_state[4][1][31]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][32] - gym_state[4][1][32]) < 1e-3)
if not np.amax(np.absolute(state_a[24:33] - state_b[24:33])) < 1e-3:
print(arena.tick_count, state_a[24:33], state_b[24:33])
self.assertTrue(np.amax(np.absolute(state_a[24:33] - state_b[24:33])) < 1e-3)

# pyr
self.assertTrue(abs(gym_state[3][0][33] - gym_state[4][1][33]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][34] - gym_state[4][1][34]) < 1e-3)
self.assertTrue(abs(gym_state[3][0][35] - gym_state[4][1][35]) < 1e-3)
if not np.amax(np.absolute(state_a[33:36] - state_b[33:36])) < 1e-3:
print(arena.tick_count, state_a[33:36], state_b[33:36])
self.assertTrue(np.amax(np.absolute(state_a[33:36] - state_b[33:36])) < 1e-3)

if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion python-mtheall/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@ def test_boost_pad_order(self):
(-3584.0, 2484.0),
( 3584.0, 2484.0),
( 0.0, 2816.0),
(- 940.0, 3310.0),
(- 940.0, 3308.0),
( 940.0, 3308.0),
(-3072.0, 4096.0),
( 3072.0, 4096.0),
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def build_extension(self, ext):

setup(
name = "RocketSim",
version = "1.3.0a6",
version = "1.3.0a7",
description = "This is Rocket League!",
cmdclass = {"build_ext": build_ext_ex},
ext_modules = [RocketSim]
Expand Down
4 changes: 3 additions & 1 deletion src/RLConst.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace RLConst {
BALL_DRAG = 0.03f, // Net-velocity drag multiplier
BALL_FRICTION = 0.35f,
BALL_RESTITUTION = 0.6f, // Bounce factor
BALL_HOOPS_Z_VEL = 1000, // Z impulse applied to hoops ball on kickoff

CAR_MAX_SPEED = 2300.f,
BALL_MAX_SPEED = 6000.f,
Expand Down Expand Up @@ -112,6 +113,7 @@ namespace RLConst {
CAR_AUTOFLIP_TORQUE = 50,
CAR_AUTOFLIP_TIME = 0.4f,
CAR_AUTOFLIP_NORMZ_THRESH = M_SQRT1_2,
CAR_AUTOFLIP_ROLL_THRESH = 2.8f,

CAR_AUTOROLL_FORCE = 100,
CAR_AUTOROLL_TORQUE = 80,
Expand Down Expand Up @@ -228,7 +230,7 @@ namespace RLConst {
{-3584.f, 2484.f, 70.f },
{3584.f, 2484.f, 70.f },
{0.f, 2816.f, 70.f },
{-940.f, 3310.f, 70.f },
{-940.f, 3308.f, 70.f },
{940.f, 3308.f, 70.f },
{-1792.f, 4184.f, 70.f },
{1792.f, 4184.f, 70.f },
Expand Down
93 changes: 49 additions & 44 deletions src/Sim/Arena/Arena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ void Arena::ResetToRandomKickoff(int seed) {
} else if (gameMode == GameMode::SNOWDAY) {
// Don't freeze
ballState.vel.z = FLT_EPSILON;
} else if (isHoops) {
ballState.vel.z = BALL_HOOPS_Z_VEL;
}
ball->SetState(ballState);

Expand Down Expand Up @@ -777,34 +779,9 @@ void Arena::Step(int ticksToSimulate) {

ball->_FinishPhysicsTick(_mutatorConfig);

if (hasArenaStuff) {
if (_goalScoreCallback.func != NULL) { // Potentially fire goal score callback

if (gameMode == GameMode::SOCCAR) {
float ballPosY = ball->_rigidBody.m_worldTransform.m_origin.y() * BT_TO_UU;
if (abs(ballPosY) > (RLConst::SOCCAR_GOAL_SCORE_BASE_THRESHOLD_Y + _mutatorConfig.ballRadius)) {
// Orange goal is at positive Y, so if the ball's Y is positive, it's in orange goal and thus blue scored
Team scoringTeam = (ballPosY > 0) ? Team::BLUE : Team::ORANGE;
_goalScoreCallback.func(this, scoringTeam, _goalScoreCallback.userInfo);
}
} else if (gameMode == GameMode::HOOPS) {

if (ball->_rigidBody.m_worldTransform.m_origin.z() < RLConst::HOOPS_GOAL_SCORE_THRESHOLD_Z * UU_TO_BT) {
constexpr float
SCALE_Y = 0.9f,
OFFSET_Y = 2770.f,
RADIUS_SQ = 716 * 716;

Vec ballPos = ball->_rigidBody.m_worldTransform.m_origin * BT_TO_UU;
float dx = ballPos.x;
float dy = abs(ballPos.y) * SCALE_Y - OFFSET_Y;
float distSq = dx * dx + dy * dy;
if (distSq < RADIUS_SQ) {
Team scoringTeam = (ballPos.y > 0) ? Team::BLUE : Team::ORANGE;
_goalScoreCallback.func(this, scoringTeam, _goalScoreCallback.userInfo);
}
}
}
if (_goalScoreCallback.func != NULL) { // Potentially fire goal score callback
if (IsBallScored()) {
_goalScoreCallback.func(this, RS_TEAM_FROM_Y(ball->_rigidBody.m_worldTransform.m_origin.y()), _goalScoreCallback.userInfo);
}
}

Expand All @@ -816,46 +793,74 @@ void Arena::Stop() {
_stop = true;
}

bool Arena::IsBallProbablyGoingIn(float maxTime) const {
bool Arena::IsBallProbablyGoingIn(float maxTime, float extraMargin) const {
if (gameMode == GameMode::SOCCAR) {
Vec ballPos = ball->_rigidBody.m_worldTransform.m_origin * UU_TO_BT;
Vec ballVel = ball->_rigidBody.m_linearVelocity * UU_TO_BT;
Vec ballPos = ball->_rigidBody.m_worldTransform.m_origin * BT_TO_UU;
Vec ballVel = ball->_rigidBody.m_linearVelocity * BT_TO_UU;

if (ballVel.y < FLT_EPSILON)
if (abs(ballVel.y) < FLT_EPSILON)
return false;

float scoreDirSgn = RS_SGN(ballVel.y);
float goalScoreY = (RLConst::SOCCAR_GOAL_SCORE_BASE_THRESHOLD_Y + _mutatorConfig.ballRadius) * scoreDirSgn;
float distToGoal = abs(ballPos.y - scoreDirSgn);
float goalY = RLConst::SOCCAR_GOAL_SCORE_BASE_THRESHOLD_Y * scoreDirSgn;
float distToGoal = abs(ballPos.y - goalY);

float timeToGoal = distToGoal / abs(ballVel.y);

if (timeToGoal > maxTime)
return false;

// Roughly account for drag
timeToGoal *= 1 + powf(1 - _mutatorConfig.ballDrag, timeToGoal);

Vec extrapPosWhenScore = ballPos + (ballVel * timeToGoal) + (_mutatorConfig.gravity * timeToGoal * timeToGoal) / 2;

// From: https://github.com/RLBot/RLBot/wiki/Useful-Game-Values
constexpr float
APPROX_GOAL_HALF_WIDTH = 892.755f,
APPROX_GOAL_HEIGHT = 642.775;

float SCORE_MARGIN = _mutatorConfig.ballRadius * 0.64f;
float scoreMargin = _mutatorConfig.ballRadius * 0.1f + extraMargin;

if (extrapPosWhenScore.z > APPROX_GOAL_HEIGHT + SCORE_MARGIN)
if (extrapPosWhenScore.z > APPROX_GOAL_HEIGHT + scoreMargin)
return false; // Too high

if (abs(extrapPosWhenScore.x) > APPROX_GOAL_HALF_WIDTH + SCORE_MARGIN)
if (abs(extrapPosWhenScore.x) > APPROX_GOAL_HALF_WIDTH + scoreMargin)
return false; // Too far to the side

// Ok it's probably gonna score, or at least be very close
return true;
} else {
// TODO: Support for hoops
RS_ERR_CLOSE("Arena::IsBallProbablyGoingIn() not supported in non-soccar gamemode");
// TODO: Support for hoops (not as easy but reasonable), heatseeker (uhhh), and snowday (oh god)
RS_ERR_CLOSE("Arena::IsBallProbablyGoingIn() is only supported for soccar");
return false;
}
}

RSAPI bool Arena::IsBallScored() const {
switch (gameMode) {
case GameMode::SOCCAR:
case GameMode::HEATSEEKER:
case GameMode::SNOWDAY:
{
float ballPosY = ball->_rigidBody.m_worldTransform.m_origin.y() * BT_TO_UU;
return abs(ballPosY) > (RLConst::SOCCAR_GOAL_SCORE_BASE_THRESHOLD_Y + _mutatorConfig.ballRadius);
}
case GameMode::HOOPS:
{
if (ball->_rigidBody.m_worldTransform.m_origin.z() < RLConst::HOOPS_GOAL_SCORE_THRESHOLD_Z * UU_TO_BT) {
constexpr float
SCALE_Y = 0.9f,
OFFSET_Y = 2770.f,
RADIUS_SQ = 716 * 716;

Vec ballPos = ball->_rigidBody.m_worldTransform.m_origin * BT_TO_UU;
float dx = ballPos.x;
float dy = abs(ballPos.y) * SCALE_Y - OFFSET_Y;
float distSq = dx * dx + dy * dy;
return distSq < RADIUS_SQ;
} else {
return false;
}
}
default:
return false;
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/Sim/Arena/Arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ class Arena {
// Returns true if the ball is probably going in, does not account for wall or ceiling bounces
// NOTE: Purposefully overestimates, just like the real RL's shot prediction
// To check which goal it will score in, use the ball's velocity
RSAPI bool IsBallProbablyGoingIn(float maxTime = 2.f) const;
// Margin can be manually adjusted with extraMargin (negative to prevent overestimating)
RSAPI bool IsBallProbablyGoingIn(float maxTime = 2.f, float extraMargin = 0) const;

// Returns true if the ball is in the net
// Works for all gamemodes (and does nothing in THE_VOID)
RSAPI bool IsBallScored() const;

// Free all associated memory
RSAPI ~Arena();
Expand Down
3 changes: 3 additions & 0 deletions src/Sim/Ball/Ball.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ void Ball::SetState(const BallState& state) {
_rigidBody.setAngularVelocity(state.angVel);

_velocityImpulseCache = { 0,0,0 };
_internalState.updateCounter = 0;
}

btCollisionShape* MakeBallCollisionShape(GameMode gameMode, const MutatorConfig& mutatorConfig, btVector3& localIntertia) {
Expand Down Expand Up @@ -140,6 +141,8 @@ void Ball::_FinishPhysicsTick(const MutatorConfig& mutatorConfig) {
_rigidBody.m_angularVelocity =
Math::RoundVec(_rigidBody.m_angularVelocity, 0.00001);
}

_internalState.updateCounter++;
}

bool Ball::IsSphere() const {
Expand Down
5 changes: 5 additions & 0 deletions src/Sim/Ball/Ball.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
#include "../../../libsrc/bullet3-3.24/BulletCollision/CollisionShapes/btSphereShape.h"

struct BallState {
// Incremented every update, reset when SetState() is called
// Used for telling if a stateset occured
// Not serialized
uint64_t updateCounter = 0;

// Position in world space
Vec pos = { 0, 0, RLConst::BALL_REST_Z };

Expand Down
1 change: 1 addition & 0 deletions src/Sim/BallPredTracker/BallPredTracker.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include "../Arena/Arena.h"

// An external tool struct that predicts the ball of a given arena
struct BallPredTracker {
Arena* ballPredArena;
std::vector<BallState> predData;
Expand Down
16 changes: 12 additions & 4 deletions src/Sim/Car/Car.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void Car::SetState(const CarState& state) {
_velocityImpulseCache = { 0, 0, 0 };

_internalState = state;
_internalState.updateCounter = 0;
}

void Car::Demolish(float respawnDelay) {
Expand Down Expand Up @@ -203,6 +204,8 @@ void Car::_FinishPhysicsTick(const MutatorConfig& mutatorConfig) {
_rigidBody.m_angularVelocity =
Math::RoundVec(_rigidBody.m_angularVelocity, 0.00001);
}

_internalState.updateCounter++;
}

void Car::_BulletSetup(GameMode gameMode, btDynamicsWorld* bulletWorld, const MutatorConfig& mutatorConfig) {
Expand Down Expand Up @@ -764,12 +767,17 @@ void Car::_UpdateAutoFlip(float tickTime, const MutatorConfig& mutatorConfig, bo
_internalState.worldContact.contactNormal.z > CAR_AUTOFLIP_NORMZ_THRESH
) {

// TODO: Slow :(
Angle angles = Angle::FromRotMat(_internalState.rotMat);
_internalState.autoFlipTimer = CAR_AUTOFLIP_TIME * (abs(angles.roll) / M_PI);
_internalState.autoFlipTorqueScale = (angles.roll > 0) ? 1 : -1;
_internalState.isAutoFlipping = true;

_rigidBody.applyCentralImpulse(-GetUpDir() * CAR_AUTOFLIP_IMPULSE * UU_TO_BT * CAR_MASS_BT);
float absRoll = abs(angles.roll);
if (absRoll > CAR_AUTOFLIP_ROLL_THRESH) {
_internalState.autoFlipTimer = CAR_AUTOFLIP_TIME * (absRoll / M_PI);
_internalState.autoFlipTorqueScale = (angles.roll > 0) ? 1 : -1;
_internalState.isAutoFlipping = true;

_rigidBody.applyCentralImpulse(-GetUpDir() * CAR_AUTOFLIP_IMPULSE * UU_TO_BT * CAR_MASS_BT);
}
}

if (_internalState.isAutoFlipping) {
Expand Down
8 changes: 8 additions & 0 deletions src/Sim/Car/Car.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
#include "../../../src/Sim/btVehicleRL/btVehicleRL.h"

struct CarState {

// Incremented every update, reset when SetState() is called
// Used for telling if a stateset occured
// Not serialized
uint64_t updateCounter = 0;

// Position in world space (UU)
Vec pos = { 0, 0, 17 };

Expand Down Expand Up @@ -93,6 +99,8 @@ enum class Team : byte {
ORANGE = 1
};

#define RS_TEAM_FROM_Y(y) ((y) < 0 ? Team::BLUE : Team::ORANGE)

class Car {
public:
// Configuration for this car
Expand Down
Loading

0 comments on commit ad427a3

Please sign in to comment.