Skip to content

Commit

Permalink
Various features
Browse files Browse the repository at this point in the history
Arena.stop()
Arena.multi_step()

Arena memory weight mode

Release GIL for Arena.step() and Arena.multi_step()

Stop simulation on callback exceptions

Consistent random kickoff by seed
  • Loading branch information
mtheall committed Sep 15, 2023
1 parent b8616e5 commit 41ee292
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 52 deletions.
398 changes: 357 additions & 41 deletions python-mtheall/Arena.cpp

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions python-mtheall/MemoryWeightMode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "Module.h"

#include <cstddef>
#include <cstring>

namespace RocketSim::Python
{
PyTypeObject *MemoryWeightMode::Type = nullptr;

PyType_Slot MemoryWeightMode::Slots[] = {
{0, nullptr},
};

PyType_Spec MemoryWeightMode::Spec = {
.name = "RocketSim.MemoryWeightMode",
.basicsize = sizeof (MemoryWeightMode),
.itemsize = 0,
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE,
.slots = MemoryWeightMode::Slots,
};
}
11 changes: 11 additions & 0 deletions python-mtheall/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ struct PyModuleDef Module = {

extern "C" Py_EXPORTED_SYMBOL PyObject *PyInit_RocketSim () noexcept
{
PyEval_InitThreads ();

auto m = RocketSim::Python::PyObjectRef::steal (PyModule_Create (&Module));
if (!m)
return nullptr;
Expand Down Expand Up @@ -172,6 +174,7 @@ extern "C" Py_EXPORTED_SYMBOL PyObject *PyInit_RocketSim () noexcept
MAKE_TYPE (CarState);
MAKE_TYPE (DemoMode);
MAKE_TYPE (GameMode);
MAKE_TYPE (MemoryWeightMode);
MAKE_TYPE (MutatorConfig);
MAKE_TYPE (RotMat);
MAKE_TYPE (Team);
Expand Down Expand Up @@ -217,6 +220,14 @@ extern "C" Py_EXPORTED_SYMBOL PyObject *PyInit_RocketSim () noexcept
"DISABLED",
PyObjectRef::stealObject (PyLong_FromLong (static_cast<long> (::DemoMode::DISABLED))));

// MemoryWeightMode
SET_TYPE_ATTR (RocketSim::Python::MemoryWeightMode::Type,
"HEAVY",
PyObjectRef::stealObject (PyLong_FromLong (static_cast<long> (ArenaMemWeightMode::HEAVY))));
SET_TYPE_ATTR (RocketSim::Python::MemoryWeightMode::Type,
"LIGHT",
PyObjectRef::stealObject (PyLong_FromLong (static_cast<long> (ArenaMemWeightMode::LIGHT))));

// CarConfig
SET_TYPE_ATTR (RocketSim::Python::CarConfig::Type,
"OCTANE",
Expand Down
36 changes: 35 additions & 1 deletion python-mtheall/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,23 @@ bool DictSetValue (PyObject *dict_, char const *key_, PyObject *value_) noexcept

PyObject *PyDeepCopy (void *obj_, PyObject *memo_) noexcept;

class GIL
{
public:
~GIL () noexcept
{
PyGILState_Release (m_state);
}

GIL () noexcept
: m_state (PyGILState_Ensure ())
{
}

private:
PyGILState_STATE m_state;
};

struct GameMode
{
PyObject_HEAD
Expand Down Expand Up @@ -95,6 +112,15 @@ struct DemoMode
static PyType_Spec Spec;
};

struct MemoryWeightMode
{
PyObject_HEAD

static PyTypeObject *Type;
static PyType_Slot Slots[];
static PyType_Spec Spec;
};

struct Vec
{
PyObject_HEAD
Expand Down Expand Up @@ -554,9 +580,12 @@ struct MutatorConfig

struct Arena
{
class ThreadPool;

PyObject_HEAD

std::shared_ptr<::Arena> arena;
std::shared_ptr<ThreadPool> threadPool;
std::map<std::uint32_t, PyRef<Car>> *cars;
std::unordered_map<::BoostPad *, PyRef<BoostPad>> *boostPads;
std::vector<PyRef<BoostPad>> *boostPadsByIndex;
Expand All @@ -578,7 +607,9 @@ struct Arena
std::uint64_t lastGoalTick;
std::uint64_t lastGymStateTick;

bool stepException;
mutable PyObject *stepExceptionType;
mutable PyObject *stepExceptionValue;
mutable PyObject *stepExceptionTraceback;

static PyTypeObject *Type;
static PyMemberDef Members[];
Expand Down Expand Up @@ -613,6 +644,9 @@ struct Arena
static PyObject *SetGoalScoreCallback (Arena *self_, PyObject *args_, PyObject *kwds_) noexcept;
static PyObject *SetMutatorConfig (Arena *self_, PyObject *args_, PyObject *kwds_) noexcept;
static PyObject *Step (Arena *self_, PyObject *args_, PyObject *kwds_) noexcept;
static PyObject *Stop (Arena *self_) noexcept;

static PyObject *MultiStep (PyObject *dummy_, PyObject *args_, PyObject *kwds_) noexcept;

static void HandleBallTouchCallback (::Arena *arena_, ::Car *car_, void *userData_) noexcept;
static void
Expand Down
6 changes: 6 additions & 0 deletions python-mtheall/PyRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ class PyRef
return m_object;
}

template <typename U>
bool operator< (PyRef<U> that_) const noexcept
{
return borrowObject () < that_.borrowObject ();
}

/// \brief Relinquish reference
T *gift () noexcept
{
Expand Down
86 changes: 85 additions & 1 deletion python-mtheall/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,8 @@ def compare(self, arena_a, arena_b):
self.assertEqual(state_a.prev_locked_car_id, state_b.prev_locked_car_id)

def test_basic(self):
pass
arena = rs.Arena(rs.GameMode.THE_VOID, rs.MemoryWeightMode.LIGHT)
arena = rs.Arena(rs.GameMode.THE_VOID, rs.MemoryWeightMode.HEAVY)

def test_boost_pad_order(self):
arena = rs.Arena(rs.GameMode.THE_VOID)
Expand Down Expand Up @@ -1445,6 +1446,89 @@ def test_car_order(self):
for car, arena_car in zip(cars, arena_cars):
self.assertIs(car, arena_car)

def test_stop(self):
arena = rs.Arena(rs.GameMode.SOCCAR)
car = random_car(arena)

tick = [0]
def ball_touch_callback(arena: rs.Arena, car: rs.Car, data):
arena.stop()
data[0] = arena.tick_count

arena.set_ball_touch_callback(ball_touch_callback, tick)

for i in range(100):
update_controls(arena.ball, car)
arena.step(8)
if tick[0] > 0:
break

self.assertEqual(arena.tick_count, tick[0] + 1)

def test_multi_step(self):
arenas = [rs.Arena(rs.GameMode.SOCCAR, rs.MemoryWeightMode.HEAVY) for i in range(24)]

for arena in arenas:
car = arena.add_car(rs.Team.BLUE)
arena.reset_kickoff(seed=999)

for arena in arenas[1:]:
self.compare(arena, arenas[0])

rs.Arena.multi_step(arenas, 1)

for i in range(10000 // 8):
for arena in arenas:
for car in arena.get_cars():
update_controls(arena.ball, car)
rs.Arena.multi_step(arenas, 8)

for arena in arenas[1:]:
self.compare(arena, arenas[0])

def test_multi_step_exception(self):
class BallTouchError(Exception):
def __init__(self):
super().__init__()

def ball_touch_callback(arena: rs.Arena, car: rs.Car, data):
data[0][data[1]] = arena.tick_count
raise BallTouchError()

arenas = [rs.Arena(rs.GameMode.SOCCAR, rs.MemoryWeightMode.HEAVY) for i in range(8)]
touched = [0 for _ in arenas]

for i, arena in enumerate(arenas):
car = arena.add_car(rs.Team.BLUE)
arena.reset_kickoff(seed=999)
arena.set_ball_touch_callback(ball_touch_callback, [touched, i])

for arena in arenas[1:]:
self.compare(arena, arenas[0])

with self.assertRaises(BallTouchError):
for i in range(10000 // 8):
for arena in arenas:
for car in arena.get_cars():
update_controls(arena.ball, car)
rs.Arena.multi_step(arenas, 8)

# Each arena should stop on the tick that touched
for i, arena in enumerate(arenas):
self.assertEqual(arena.tick_count, touched[i] + 1)

before = [arena.tick_count for arena in arenas]
with self.assertRaisesRegex(RuntimeError, "Unexpected type"):
arenas.append(0)
rs.Arena.multi_step(arenas, 8)
self.assertEqual(before, [arena.tick_count for arena in arenas[:-1]])

before = [arena.tick_count for arena in arenas[:-1]]
with self.assertRaisesRegex(RuntimeError, "Duplicate arena detected"):
arenas[-1] = arenas[0]
rs.Arena.multi_step(arenas, 8)
self.assertEqual(before, [arena.tick_count for arena in arenas[:-1]])

def test_clone(self):
arena = rs.Arena(rs.GameMode.SOCCAR)

Expand Down
27 changes: 19 additions & 8 deletions src/Sim/Arena/Arena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "../../../libsrc/bullet3-3.24/BulletCollision/CollisionShapes/btBoxShape.h"
#include "../../../libsrc/bullet3-3.24/BulletCollision/CollisionShapes/btSphereShape.h"

#include <array>

RSAPI void Arena::SetMutatorConfig(const MutatorConfig& mutatorConfig) {

bool
Expand Down Expand Up @@ -120,16 +122,19 @@ void Arena::SetGoalScoreCallback(GoalScoreEventFn callbackFunc, void* userInfo)
_goalScoreCallback.userInfo = userInfo;
}

namespace
{
template <typename T, std::size_t... I>
auto make_array(std::index_sequence<I...>)
{
return std::array<T, sizeof...(I)>{I...};
}
}

void Arena::ResetToRandomKickoff(int seed) {
using namespace RLConst;

// TODO: Make shuffling of kickoff setup more efficient (?)

static thread_local std::vector<int> kickoffOrder;
if (kickoffOrder.empty()) {
for (int i = 0; i < CAR_SPAWN_LOCATION_AMOUNT; i++)
kickoffOrder.push_back(i);
}
auto kickoffOrder = make_array<int> (std::make_index_sequence<CAR_SPAWN_LOCATION_AMOUNT>{});

if (seed == -1) {
std::default_random_engine& randEngine = Math::GetRandEngine();
Expand Down Expand Up @@ -640,7 +645,9 @@ Car* Arena::DeserializeNewCar(DataStreamIn& in, Team team) {
}

void Arena::Step(int ticksToSimulate) {
for (int i = 0; i < ticksToSimulate; i++) {
_stop = false;

for (int i = 0; i < ticksToSimulate && !_stop; i++) {

_bulletWorld.setWorldUserInfo(this);

Expand Down Expand Up @@ -739,6 +746,10 @@ void Arena::Step(int ticksToSimulate) {
}
}

void Arena::Stop() {
_stop = true;
}

bool Arena::IsBallProbablyGoingIn(float maxTime) const {
if (gameMode == GameMode::SOCCAR) {
Vec ballPos = ball->_rigidBody.m_worldTransform.m_origin * UU_TO_BT;
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,6 +156,9 @@ class Arena {
// Simulate everything in the arena for a given number of ticks
RSAPI void Step(int ticksToSimulate = 1);

// Stop simulation
RSAPI void Stop();

RSAPI void ResetToRandomKickoff(int seed = -1);

// Returns true if the ball is probably going in, does not account for wall or ceiling bounces
Expand Down Expand Up @@ -199,7 +202,9 @@ class Arena {
}

private:

// Whether to stop
bool _stop = false;

// Constructor for use by Arena::Create()
Arena(GameMode gameMode, ArenaMemWeightMode memWeightMode, float tickRate = 120);

Expand Down

0 comments on commit 41ee292

Please sign in to comment.