diff --git a/README.md b/README.md index 0c9300e8..ad43fb4b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - [x] [Toy text RL envs](https://envpool.readthedocs.io/en/latest/env/toy_text.html): Catch, FrozenLake, Taxi, NChain, CliffWalking, Blackjack - [x] [ViZDoom single player](https://envpool.readthedocs.io/en/latest/env/vizdoom.html) - [x] [DeepMind Control Suite](https://envpool.readthedocs.io/en/latest/env/dm_control.html) -- [ ] [Box2D](https://envpool.readthedocs.io/en/latest/env/box2d.html) +- [x] [Box2D](https://envpool.readthedocs.io/en/latest/env/box2d.html) - [ ] Procgen - [ ] Minigrid diff --git a/docs/env/box2d.rst b/docs/env/box2d.rst index 8a34e6e4..a58af802 100644 --- a/docs/env/box2d.rst +++ b/docs/env/box2d.rst @@ -50,7 +50,7 @@ The episode will terminate if the hull gets in contact with the ground or if the walker exceeds the right end of the terrain length. -CarRacing-v1 +CarRacing-v2 ------------ The easiest control task to learn from pixels - a top-down racing environment. diff --git a/envpool/BUILD b/envpool/BUILD index cb901f28..4dd760cb 100644 --- a/envpool/BUILD +++ b/envpool/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) diff --git a/envpool/atari/BUILD b/envpool/atari/BUILD index 2578a897..c11e6bc0 100644 --- a/envpool/atari/BUILD +++ b/envpool/atari/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") diff --git a/envpool/box2d/BUILD b/envpool/box2d/BUILD index 5bffb44b..71a0564f 100644 --- a/envpool/box2d/BUILD +++ b/envpool/box2d/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") @@ -7,12 +21,17 @@ cc_library( name = "box2d_env", srcs = [ "bipedal_walker_env.cc", + "car_dynamics.cc", + "car_racing_env.cc", "lunar_lander_env.cc", "utils.cc", ], hdrs = [ "bipedal_walker.h", "bipedal_walker_env.h", + "car_dynamics.h", + "car_racing.h", + "car_racing_env.h", "lunar_lander_continuous.h", "lunar_lander_discrete.h", "lunar_lander_env.h", @@ -21,6 +40,7 @@ cc_library( deps = [ "//envpool/core:async_envpool", "@box2d", + "@opencv", ], ) @@ -71,6 +91,7 @@ py_test( requirement("gym"), requirement("box2d"), requirement("pygame"), + requirement("opencv-python-headless"), requirement("numpy"), ], ) diff --git a/envpool/box2d/__init__.py b/envpool/box2d/__init__.py index e31dd0ac..d4b3785e 100644 --- a/envpool/box2d/__init__.py +++ b/envpool/box2d/__init__.py @@ -18,12 +18,18 @@ from .box2d_envpool import ( _BipedalWalkerEnvPool, _BipedalWalkerEnvSpec, + _CarRacingEnvPool, + _CarRacingEnvSpec, _LunarLanderContinuousEnvPool, _LunarLanderContinuousEnvSpec, _LunarLanderDiscreteEnvPool, _LunarLanderDiscreteEnvSpec, ) +CarRacingEnvSpec, CarRacingDMEnvPool, CarRacingGymEnvPool = py_env( + _CarRacingEnvSpec, _CarRacingEnvPool +) + BipedalWalkerEnvSpec, BipedalWalkerDMEnvPool, BipedalWalkerGymEnvPool = py_env( _BipedalWalkerEnvSpec, _BipedalWalkerEnvPool ) @@ -41,6 +47,9 @@ ) = py_env(_LunarLanderDiscreteEnvSpec, _LunarLanderDiscreteEnvPool) __all__ = [ + "CarRacingEnvSpec", + "CarRacingDMEnvPool", + "CarRacingGymEnvPool", "BipedalWalkerEnvSpec", "BipedalWalkerDMEnvPool", "BipedalWalkerGymEnvPool", diff --git a/envpool/box2d/box2d_correctness_test.py b/envpool/box2d/box2d_correctness_test.py index bc5082a6..0303c356 100644 --- a/envpool/box2d/box2d_correctness_test.py +++ b/envpool/box2d/box2d_correctness_test.py @@ -13,8 +13,9 @@ # limitations under the License. """Unit tests for box2d environments correctness check.""" -from typing import Any, Dict, Tuple, no_type_check +from typing import Any, Dict, List, Tuple, no_type_check +# import cv2 import gym import numpy as np import pygame @@ -53,6 +54,11 @@ def test_lunar_lander_space(self) -> None: env1 = make_gym("LunarLanderContinuous-v2") self.run_space_check(env0, env1) + def test_car_racing_space(self) -> None: + env0 = gym.make("CarRacing-v2") + env1 = make_gym("CarRacing-v2") + self.run_space_check(env0, env1) + @staticmethod def heuristic_lunar_lander_policy( s: np.ndarray, continuous: bool @@ -114,6 +120,43 @@ def test_lunar_lander_correctness(self, num_envs: int = 30) -> None: self.solve_lunar_lander(num_envs, True) self.solve_lunar_lander(num_envs, False) + def solve_car_racing( + self, num_envs: int, action: List[float], target_reward: float + ) -> None: + env = make_gym("CarRacing-v2", num_envs=num_envs) + max_episode_steps = 100 + + env_id = np.arange(num_envs) + done = np.array([False] * num_envs) + obs = env.reset(env_id) + rewards = np.zeros(num_envs) + action = np.tile(action, (num_envs, 1)) + for _ in range(max_episode_steps): + obs, rew, terminated, truncated, info = env.step(action, env_id) + env_id = info["env_id"] + rewards[env_id] += rew + # cv2.imwrite("/tmp/car_racing-{}.jpg".format(i), obs[0]) + if np.all(done): + break + obs = obs[~done] + env_id = env_id[~done] + mean_reward = np.mean(rewards) + logging.info(f"{np.mean(rewards):.6f} ± {np.std(rewards):.6f}") + + self.assertTrue(abs(target_reward - mean_reward) < 1, (mean_reward)) + + def test_car_racing_correctness( + self, num_envs: int = 100, render: bool = False + ) -> None: + if render: + pygame.init() + pygame.display.init() + self.screen = pygame.display.set_mode((600, 400)) + self.clock = pygame.time.Clock() + self.solve_car_racing(num_envs, [0, 0.5, 0], 65) + self.solve_car_racing(num_envs, [0.1, 0.3, 0], 18.5) + self.solve_car_racing(num_envs, [0, 0.7, 0.1], 42.7) + @staticmethod @no_type_check def heuristic_bipedal_walker_policy( diff --git a/envpool/box2d/box2d_deterministic_test.py b/envpool/box2d/box2d_deterministic_test.py index 27f43b52..c5c3bc94 100644 --- a/envpool/box2d/box2d_deterministic_test.py +++ b/envpool/box2d/box2d_deterministic_test.py @@ -36,12 +36,16 @@ def run_deterministic_check( act_space = env0.action_space for _ in range(5000): action = np.array([act_space.sample() for _ in range(num_envs)]) - obs0 = env0.step(action)[0] - obs1 = env1.step(action)[0] - obs2 = env2.step(action)[0] + obs0, rew0, terminated, truncated, info0 = env0.step(action) + obs1, rew1, terminated, truncated, info1 = env1.step(action) + obs2, rew2, terminated, truncated, info2 = env2.step(action) np.testing.assert_allclose(obs0, obs1) self.assertFalse(np.allclose(obs0, obs2)) + def test_car_racing(self) -> None: + self.run_deterministic_check("CarRacing-v2") + self.run_deterministic_check("CarRacing-v2", max_episode_steps=3) + def test_bipedal_walker(self) -> None: self.run_deterministic_check("BipedalWalker-v3") self.run_deterministic_check("BipedalWalkerHardcore-v3") diff --git a/envpool/box2d/box2d_envpool.cc b/envpool/box2d/box2d_envpool.cc index 55759424..e34d4d94 100644 --- a/envpool/box2d/box2d_envpool.cc +++ b/envpool/box2d/box2d_envpool.cc @@ -13,10 +13,14 @@ // limitations under the License. #include "envpool/box2d/bipedal_walker.h" +#include "envpool/box2d/car_racing.h" #include "envpool/box2d/lunar_lander_continuous.h" #include "envpool/box2d/lunar_lander_discrete.h" #include "envpool/core/py_envpool.h" +using CarRacingEnvSpec = PyEnvSpec; +using CarRacingEnvPool = PyEnvPool; + using BipedalWalkerEnvSpec = PyEnvSpec; using BipedalWalkerEnvPool = PyEnvPool; @@ -29,6 +33,7 @@ using LunarLanderDiscreteEnvSpec = PyEnvSpec; using LunarLanderDiscreteEnvPool = PyEnvPool; PYBIND11_MODULE(box2d_envpool, m) { + REGISTER(m, CarRacingEnvSpec, CarRacingEnvPool) REGISTER(m, BipedalWalkerEnvSpec, BipedalWalkerEnvPool) REGISTER(m, LunarLanderContinuousEnvSpec, LunarLanderContinuousEnvPool) REGISTER(m, LunarLanderDiscreteEnvSpec, LunarLanderDiscreteEnvPool) diff --git a/envpool/box2d/car_dynamics.cc b/envpool/box2d/car_dynamics.cc new file mode 100644 index 00000000..573c0b49 --- /dev/null +++ b/envpool/box2d/car_dynamics.cc @@ -0,0 +1,331 @@ +/* + * Copyright 2021 Garena Online Private Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// https://github.com/openai/gym/blob/master/gym/envs/box2d/car_racing.py + +#include "car_dynamics.h" + +#include +#include +#include + +namespace box2d { + +b2PolygonShape GeneratePolygon(const float (*poly)[2], int size) { // NOLINT + std::vector vec_list; + vec_list.resize(size); + for (int i = 0; i < size; ++i) { + vec_list[i] = b2Vec2(poly[i][0] * kSize, poly[i][1] * kSize); + } + b2PolygonShape polygon; + polygon.Set(vec_list.data(), vec_list.size()); + return polygon; +} + +Car::Car(std::shared_ptr world, float init_angle, float init_x, + float init_y) + : world_(std::move(world)), hull_(nullptr), fuel_spent_(0) { + // Create hull + b2BodyDef bd; + bd.position.Set(init_x, init_y); + bd.angle = init_angle; + bd.type = b2_dynamicBody; + + hull_ = world_->CreateBody(&bd); + drawlist_.push_back(hull_); + + b2PolygonShape polygon1 = GeneratePolygon(kHullPoly1, 4); + hull_->CreateFixture(&polygon1, 1.f); + + b2PolygonShape polygon2 = GeneratePolygon(kHullPoly2, 4); + hull_->CreateFixture(&polygon2, 1.f); + + b2PolygonShape polygon3 = GeneratePolygon(kHullPoly3, 8); + hull_->CreateFixture(&polygon3, 1.f); + + b2PolygonShape polygon4 = GeneratePolygon(kHullPoly4, 4); + hull_->CreateFixture(&polygon4, 1.f); + + for (const auto* p : kWheelPos) { + float wx = p[0]; + float wy = p[1]; + + b2BodyDef bd; + bd.position.Set(init_x + wx * kSize, init_y + wy * kSize); + bd.angle = init_angle; + bd.type = b2_dynamicBody; + + b2PolygonShape polygon = GeneratePolygon(kWheelPoly, 4); + b2FixtureDef fd; + fd.shape = &polygon; + fd.density = 0.1; + fd.filter.categoryBits = 0x0020; + fd.filter.maskBits = 0x001; + fd.restitution = 0.0; + + auto* w = new Wheel(); + w->type = WHEEL_TYPE; + w->body = world_->CreateBody(&bd); + + drawlist_.push_back(w->body); + + w->body->CreateFixture(&fd); + w->wheel_rad = kWheelR * kSize; + + b2RevoluteJointDef rjd; + rjd.bodyA = hull_; + rjd.bodyB = w->body; + rjd.localAnchorA.Set(wx * kSize, wy * kSize); + rjd.localAnchorB.Set(0, 0); + rjd.referenceAngle = rjd.bodyB->GetAngle() - rjd.bodyA->GetAngle(); + rjd.enableMotor = true; + rjd.enableLimit = true; + rjd.maxMotorTorque = 180 * 900 * kSize * kSize; + rjd.motorSpeed = 0; + rjd.referenceAngle = rjd.bodyB->GetAngle() - rjd.bodyA->GetAngle(); + rjd.lowerAngle = -0.4; + rjd.upperAngle = +0.4; + rjd.type = b2JointType::e_revoluteJoint; + w->joint = static_cast(world_->CreateJoint(&rjd)); + // w->body->SetUserData(&w); + w->body->GetUserData().pointer = reinterpret_cast(w); + wheels_.push_back(w); + } +} +void Car::Gas(float g) { + g = std::min(std::max(g, 0.0f), 1.0f); + for (int i = 2; i < 4; i++) { + auto* w = wheels_[i]; + w->gas += std::min(g - w->gas, 0.1f); + } +} + +void Car::Brake(float b) { + for (auto& w : wheels_) { + w->brake = b; + } +} +void Car::Steer(float s) { + wheels_[0]->steer = s; + wheels_[1]->steer = s; +} +void Car::Step(float dt) { + for (auto* w : wheels_) { + // Steer each wheel + float dir = Sign(w->steer - w->joint->GetJointAngle()); + float val = abs(w->steer - w->joint->GetJointAngle()); + w->joint->SetMotorSpeed(dir * std::min(50.0f * val, 3.0f)); + + // Position => friction_limit + bool grass = true; + float friction_limit = kFrictionLimit * 0.6f; // Grass friction if no tile + for (auto* t : w->tiles) { + friction_limit = + std::max(friction_limit, kFrictionLimit * t->road_friction); + grass = false; + } + // Force + auto forw = w->body->GetWorldVector({0, 1}); + auto side = w->body->GetWorldVector({1, 0}); + auto v = w->body->GetLinearVelocity(); + auto vf = forw.x * v.x + forw.y * v.y; // forward speed + auto vs = side.x * v.x + side.y * v.y; // side speed + // WHEEL_MOMENT_OF_INERTIA*np.square(w.omega)/2 = E -- energy + // WHEEL_MOMENT_OF_INERTIA*w.omega * domega/dt = dE/dt = W -- power + // domega = dt*W/WHEEL_MOMENT_OF_INERTIA/w.omega + + // add small coef not to divide by zero + w->omega += (dt * kEnginePower * w->gas / kWheelMomentOfInertia / + (abs(w->omega) + 5.0f)); + fuel_spent_ += dt * kEnginePower * w->gas; + if (w->brake >= 0.9) { + w->omega = 0; + } else if (w->brake > 0) { + dir = -Sign(w->omega); + val = kBrakeForce * w->brake; + if (abs(val) > abs(w->omega)) { + val = abs(w->omega); // low speed => same as = 0 + } + w->omega += dir * val; + } + w->phase += w->omega * dt; + + auto vr = w->omega * w->wheel_rad; // rotating wheel speed + // force direction is direction of speed difference + auto f_force = -vf + vr; + auto p_force = -vs; + + // Physically correct is to always apply friction_limit until speed is + // equal. But dt is finite, that will lead to oscillations if difference is + // already near zero. + + // Random coefficient to cut oscillations in few steps (have no effect on + // friction_limit) + f_force *= 205000 * kSize * kSize; + p_force *= 205000 * kSize * kSize; + auto force = sqrt(f_force * f_force + p_force * p_force); + + // Skid trace + if (abs(force) > 2.0 * friction_limit) { + if (w->skid_particle && w->skid_particle->grass == grass && + w->skid_particle->poly.size() < 30) { + w->skid_particle->poly.emplace_back( + Vec2(w->body->GetPosition().x, w->body->GetPosition().y)); + } else if (w->skid_start == nullptr) { + w->skid_start = std::make_unique(w->body->GetPosition()); + } else { + w->skid_particle = CreateParticle(*(w->skid_start.get()), + w->body->GetPosition(), grass); + w->skid_start = nullptr; + } + + } else { + w->skid_start = nullptr; + w->skid_particle = nullptr; + } + + if (abs(force) > friction_limit) { + f_force /= force; + p_force /= force; + force = friction_limit; // Correct physics here + f_force *= force; + p_force *= force; + } + + w->omega -= dt * f_force * w->wheel_rad / kWheelMomentOfInertia; + + w->body->ApplyForceToCenter( + { + static_cast(p_force * side.x + f_force * forw.x), + static_cast(p_force * side.y + f_force * forw.y), + }, + true); + } +} + +std::shared_ptr Car::CreateParticle(b2Vec2 point1, b2Vec2 point2, + bool grass) { + cv::Scalar color = grass ? kMudColor : kWheelColor; + auto p = std::make_shared(point1, point2, grass, color); + particles_.emplace_back(p); + while (particles_.size() > 30) { + particles_.pop_front(); + } + return p; +} + +void Car::Draw(const cv::Mat& surf, float zoom, + const std::array& translation, float angle, + bool draw_particles) { + if (draw_particles) { + std::vector poly; + for (const auto& p : particles_) { + poly.clear(); + for (const auto& vec_tmp : p->poly) { + auto v = RotateRad(vec_tmp, angle); + poly.emplace_back(cv::Point(v.x * zoom + translation[0], + v.y * zoom + translation[1])); + } + cv::polylines(surf, poly, false, p->color, 2); + } + } + for (size_t i = 0; i < drawlist_.size(); i++) { + auto* body = drawlist_[i]; + auto trans = body->GetTransform(); + cv::Scalar color; + if (i == 0) { + color = cv::Scalar(0, 0, 204); // hull.color = (0.8, 0.0, 0.0) * 255 + } else { + color = cv::Scalar(0, 0, 0); // wheel.color = (0, 0, 0) + } + std::vector poly; + for (b2Fixture* f = body->GetFixtureList(); f != nullptr; + f = f->GetNext()) { + auto* shape = static_cast(f->GetShape()); + poly.clear(); + for (int j = 0; j < shape->m_count; j++) { + auto vec_tmp = Multiply(trans, shape->m_vertices[j]); + auto v = RotateRad(vec_tmp, angle); + poly.emplace_back(cv::Point(v.x * zoom + translation[0], + v.y * zoom + translation[1])); + } + cv::fillPoly(surf, poly, color); + + auto* user_data = + reinterpret_cast(body->GetUserData().pointer); + if (user_data == nullptr || user_data->type != WHEEL_TYPE) { + continue; + } + auto* obj = reinterpret_cast(user_data); + + auto a1 = obj->phase; + auto a2 = obj->phase + 1.2f; // radians + auto s1 = std::sin(a1); + auto s2 = std::sin(a2); + auto c1 = std::cos(a1); + auto c2 = std::cos(a2); + if (s1 > 0 && s2 > 0) { + continue; + } + if (s1 > 0) { + c1 = Sign(c1); + } + if (s2 > 0) { + c2 = Sign(c2); + } + + poly.clear(); + std::vector white_poly = { + Vec2(-kWheelW * kSize, +kWheelR * c1 * kSize), + Vec2(+kWheelW * kSize, +kWheelR * c1 * kSize), + Vec2(+kWheelW * kSize, +kWheelR * c2 * kSize), + Vec2(-kWheelW * kSize, +kWheelR * c2 * kSize), + }; + for (const auto& vec : white_poly) { + auto vec_tmp = Multiply(trans, vec); + auto v = RotateRad(vec_tmp, angle); + poly.emplace_back(cv::Point(v.x * zoom + translation[0], + v.y * zoom + translation[1])); + } + cv::fillPoly(surf, poly, kWheelWhite); + } + } +} + +void Car::Destroy() { + world_->DestroyBody(hull_); + hull_ = nullptr; + for (auto* w : wheels_) { + world_->DestroyBody(w->body); + delete w; + w = nullptr; + } + wheels_.clear(); +} + +float Car::GetFuelSpent() const { return fuel_spent_; } + +std::vector Car::GetGas() { return {wheels_[2]->gas, wheels_[3]->gas}; } + +std::vector Car::GetSteer() { + return {wheels_[0]->steer, wheels_[1]->steer}; +} + +std::vector Car::GetBrake() { + return {wheels_[0]->brake, wheels_[1]->brake, wheels_[2]->brake, + wheels_[3]->brake}; +} + +} // namespace box2d diff --git a/envpool/box2d/car_dynamics.h b/envpool/box2d/car_dynamics.h new file mode 100644 index 00000000..41ff9828 --- /dev/null +++ b/envpool/box2d/car_dynamics.h @@ -0,0 +1,153 @@ +/* + * Copyright 2021 Garena Online Private Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// https://github.com/openai/gym/blob/master/gym/envs/box2d/car_racing.py + +#ifndef ENVPOOL_BOX2D_CAR_DYNAMICS_H_ +#define ENVPOOL_BOX2D_CAR_DYNAMICS_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "opencv2/opencv.hpp" +#include "utils.h" + +namespace box2d { + +static const float kSize = 0.02; +static const float kEnginePower = 100000000.0f * kSize * kSize; +static const float kWheelMomentOfInertia = 4000.0f * kSize * kSize; +static const float kFrictionLimit = 1000000.0f * kSize * kSize; +static const float kWheelR = 27; +static const float kWheelW = 14; +static const float kBrakeForce = 15; // radians per second +static const float kWheelPos[4][2] = { // NOLINT + {-55, 80}, + {55, 80}, + {-55, -82}, + {55, -82}}; +static const float kHullPoly1[4][2] = { // NOLINT + {-60, 130}, + {60, 130}, + {60, 110}, + {-60, 110}}; +static const float kHullPoly2[4][2] = { // NOLINT + {-15, 120}, + {15, 120}, + {20, 20}, + {-20, 20}}; +static const float kHullPoly3[8][2] = { // NOLINT + {25, 20}, {50, -10}, {50, -40}, {20, -90}, + {-20, -90}, {-50, -40}, {-50, -10}, {-25, 20}}; +static const float kHullPoly4[4][2] = { // NOLINT + {-50, -120}, + {50, -120}, + {50, -90}, + {-50, -90}}; +static const float kWheelPoly[4][2] = { // NOLINT + {-kWheelW, +kWheelR}, + {+kWheelW, +kWheelR}, + {+kWheelW, -kWheelR}, + {-kWheelW, -kWheelR}}; + +static const cv::Scalar kRoadColor(102, 102, 102); +static const cv::Scalar kBgColor(102, 204, 102); +static const cv::Scalar kGrassColor(102, 230, 102); +static const cv::Scalar kWheelColor(0, 0, 0); +static const cv::Scalar kWheelWhite(77, 77, 77); +static const cv::Scalar kMudColor(0, 102, 102); + +enum UserDataType { INVALID = 1000, WHEEL_TYPE, TILE_TYPE }; + +class Particle { + public: + bool grass; + cv::Scalar color; + std::vector poly; + Particle(b2Vec2 p1, b2Vec2 p2, bool g, cv::Scalar c) + : grass(g), color(std::move(c)), poly({p1, p2}) {} +}; + +class UserData { + public: + UserDataType type{INVALID}; + b2Body* body; + int idx{-1}; +}; + +class Tile : public UserData { + public: + bool tile_road_visited{false}; + float road_friction; + cv::Scalar road_color; +}; + +class Wheel : public UserData { + public: + float wheel_rad{0}; + float gas{0}; + float brake{0}; + float steer{0}; + float phase{0}; + float omega{0}; + b2RevoluteJoint* joint; + std::unordered_set tiles; + + std::unique_ptr skid_start; + std::shared_ptr skid_particle; +}; + +class Car { + public: + Car(std::shared_ptr world, float init_angle, float init_x, + float init_y); + void Gas(float g); + void Brake(float b); + void Steer(float s); + void Step(float dt); + void Draw(const cv::Mat& surf, float zoom, + const std::array& translation, float angle, + bool draw_particles = true); + void Destroy(); + [[nodiscard]] float GetFuelSpent() const; + std::vector GetGas(); + std::vector GetSteer(); + std::vector GetBrake(); + + protected: + std::deque> particles_; + std::vector drawlist_; + std::shared_ptr world_; + b2Body* hull_; + std::vector wheels_; + float fuel_spent_; + + std::shared_ptr CreateParticle(b2Vec2 point1, b2Vec2 point2, + bool grass); + + friend class CarRacingBox2dEnv; +}; + +} // namespace box2d + +#endif // ENVPOOL_BOX2D_CAR_DYNAMICS_H_ diff --git a/envpool/box2d/car_racing.h b/envpool/box2d/car_racing.h new file mode 100644 index 00000000..1bf1a013 --- /dev/null +++ b/envpool/box2d/car_racing.h @@ -0,0 +1,97 @@ +/* + * Copyright 2021 Garena Online Private Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// https://github.com/openai/gym/blob/master/gym/envs/box2d/car_racing.py + +#ifndef ENVPOOL_BOX2D_CAR_RACING_H_ +#define ENVPOOL_BOX2D_CAR_RACING_H_ + +#include "car_racing_env.h" +#include "envpool/core/async_envpool.h" +#include "envpool/core/env.h" + +namespace box2d { + +class CarRacingEnvFns { + public: + static decltype(auto) DefaultConfig() { + return MakeDict("reward_threshold"_.Bind(900.0), + "lap_complete_percent"_.Bind(0.95f)); + } + template + static decltype(auto) StateSpec(const Config& conf) { +#ifdef ENVPOOL_TEST + return MakeDict("obs"_.Bind(Spec({96, 96, 3}, {0, 255})), + "info:tile_visited_count"_.Bind(Spec({-1})), + "info:car_fuel_spent"_.Bind(Spec({-1})), + "info:car_gas"_.Bind(Spec({-1, 2})), + "info:car_steer"_.Bind(Spec({-1, 2})), + "info:car_brake"_.Bind(Spec({-1, 4}))); +#else + return MakeDict("obs"_.Bind(Spec({96, 96, 3}, {0, 255}))); +#endif + } + template + static decltype(auto) ActionSpec(const Config& conf) { + return MakeDict("action"_.Bind(Spec({3}, {{-1, 0, 0}, {1, 1, 1}}))); + } +}; + +using CarRacingEnvSpec = EnvSpec; + +class CarRacingEnv : public Env, public CarRacingBox2dEnv { + public: + CarRacingEnv(const Spec& spec, int env_id) + : Env(spec, env_id), + CarRacingBox2dEnv(spec.config["max_episode_steps"_], + spec.config["lap_complete_percent"_]) {} + + bool IsDone() override { return done_; } + + void Reset() override { + CarRacingReset(&gen_); + WriteState(); + } + + void Step(const Action& action) override { + CarRacingStep(&gen_, action["action"_][0], action["action"_][1], + action["action"_][2]); + WriteState(); + } + + private: + void WriteState() { + State state = Allocate(); + state["reward"_] = step_reward_; + CreateImageArray(); + state["obs"_].Assign(img_array_.data, 96 * 96 * 3); +#ifdef ENVPOOL_TEST + state["info:tile_visited_count"_] = tile_visited_count_; + state["info:car_fuel_spent"_] = car_->GetFuelSpent(); + auto car_gas = car_->GetGas(); + auto car_steer = car_->GetSteer(); + auto car_brake = car_->GetBrake(); + state["info:car_gas"_].Assign(car_gas.data(), car_gas.size()); + state["info:car_steer"_].Assign(car_steer.data(), car_steer.size()); + state["info:car_brake"_].Assign(car_brake.data(), car_brake.size()); +#endif + } +}; + +using CarRacingEnvPool = AsyncEnvPool; + +} // namespace box2d + +#endif // ENVPOOL_BOX2D_CAR_RACING_H_ diff --git a/envpool/box2d/car_racing_env.cc b/envpool/box2d/car_racing_env.cc new file mode 100644 index 00000000..c0e10fdf --- /dev/null +++ b/envpool/box2d/car_racing_env.cc @@ -0,0 +1,593 @@ +/* + * Copyright 2021 Garena Online Private Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// https://github.com/openai/gym/blob/master/gym/envs/box2d/car_racing.py + +#include "car_racing_env.h" + +#include +#include +#include +#include +#include +#include + +namespace box2d { + +CarRacingFrictionDetector::CarRacingFrictionDetector(CarRacingBox2dEnv* env, + float lap_complete_percent) + : env_(env), lap_complete_percent_(lap_complete_percent) {} + +void CarRacingFrictionDetector::Contact(b2Contact* contact, bool begin) { + Tile* tile = nullptr; + Wheel* obj = nullptr; + + auto* u1 = reinterpret_cast( + contact->GetFixtureA()->GetBody()->GetUserData().pointer); + auto* u2 = reinterpret_cast( + contact->GetFixtureB()->GetBody()->GetUserData().pointer); + + if (u1 == nullptr || u2 == nullptr) { + return; + } + + if ((u1->type != WHEEL_TYPE && u1->type != TILE_TYPE) || + (u2->type != WHEEL_TYPE && u2->type != TILE_TYPE)) { + return; + } + + if (u1->type == TILE_TYPE) { + tile = reinterpret_cast(u1); + obj = reinterpret_cast(u2); + } else { + // u2->type == TILE_TYPE + tile = reinterpret_cast(u2); + obj = reinterpret_cast(u1); + } + + if (tile->type != TILE_TYPE || obj->type != WHEEL_TYPE) { + return; + } + + tile->road_color = kRoadColor; + if (begin) { + obj->tiles.insert(tile); + if (!tile->tile_road_visited) { + tile->tile_road_visited = true; + env_->reward_ += 1000.0f / env_->track_.size(); + env_->tile_visited_count_ += 1; + // Lap is considered completed if enough % of the track was covered + if (tile->idx == 0 && static_cast(env_->tile_visited_count_) > + env_->track_.size() * lap_complete_percent_) { + env_->new_lap_ = true; + } + } + } else { + obj->tiles.erase(tile); + } +} + +CarRacingBox2dEnv::CarRacingBox2dEnv(int max_episode_steps, + float lap_complete_percent) + : lap_complete_percent_(lap_complete_percent), + max_episode_steps_(max_episode_steps), + elapsed_step_(max_episode_steps + 1), + done_(true), + world_(new b2World(b2Vec2(0.0, 0.0))) { + b2PolygonShape shape; + std::array vertices = {b2Vec2(0, 0), b2Vec2(1, 0), b2Vec2(1, -1), + b2Vec2(0, -1)}; + shape.Set(vertices.data(), vertices.size()); + fd_tile_.shape = &shape; +} + +bool CarRacingBox2dEnv::CreateTrack(std::mt19937* gen) { + // Create checkpoints + std::vector> checkpoints; + for (int c = 0; c < kCheckPoint; ++c) { + auto noise = RandUniform(0, 2.0 * M_PI / kCheckPoint)(*gen); + auto alpha = 2.0 * M_PI * c / kCheckPoint + noise; + auto rad = RandUniform(kTrackRad / 3, kTrackRad)(*gen); + + if (c == 0) { + alpha = 0; + rad = 1.5 * kTrackRad; + } else if (c == kCheckPoint - 1) { + alpha = 2 * M_PI * c / kCheckPoint; + start_alpha_ = static_cast(-M_PI / kCheckPoint); + rad = 1.5 * kTrackRad; + } + std::array cp = {static_cast(alpha), + static_cast(rad * std::cos(alpha)), + static_cast(rad * std::sin(alpha))}; + checkpoints.emplace_back(cp); + } + // according to the implementation logic, + // `roads_` is empty for sure. + // keep this line for consistency with gym's version. + roads_.clear(); + // Go from one checkpoint to another to create track + float x = 1.5f * kTrackRad; + float y = 0; + float beta = 0; + int dest_i = 0; + int laps = 0; + std::vector> current_track; + int no_freeze = 2500; + bool visited_other_side = false; + while (true) { + float alpha = std::atan2(y, x); + if (visited_other_side && alpha > 0) { + laps++; + visited_other_side = false; + } + if (alpha < 0) { + visited_other_side = true; + alpha += 2 * M_PI; + } + float dest_alpha; + float dest_x; + float dest_y; + while (true) { // Find destination from checkpoints + bool failed = true; + while (true) { + dest_alpha = checkpoints[dest_i % checkpoints.size()][0]; + dest_x = checkpoints[dest_i % checkpoints.size()][1]; + dest_y = checkpoints[dest_i % checkpoints.size()][2]; + if (alpha <= dest_alpha) { + failed = false; + break; + } + dest_i++; + if (dest_i % checkpoints.size() == 0) { + break; + } + } + if (!failed) { + break; + } + alpha -= 2 * M_PI; + } + float r1x = std::cos(beta); + float r1y = std::sin(beta); + float p1x = -r1y; + float p1y = r1x; + float dest_dx = dest_x - x; // vector towards destination + float dest_dy = dest_y - y; + // destination vector projected on rad: + float proj = r1x * dest_dx + r1y * dest_dy; + while (beta - alpha > 1.5 * M_PI) { + beta -= 2 * M_PI; + } + while (beta - alpha < -1.5 * M_PI) { + beta += 2 * M_PI; + } + float prev_beta = beta; + proj *= kScale; + if (proj > 0.3) { + beta -= std::min(kTrackTurnRate, abs(0.001f * proj)); + } + if (proj < -0.3) { + beta += std::min(kTrackTurnRate, abs(0.001f * proj)); + } + x += p1x * kTrackDetailStep; + y += p1y * kTrackDetailStep; + std::array track = {alpha, prev_beta * 0.5f + beta * 0.5f, x, y}; + current_track.emplace_back(track); + if (laps > 4) { + break; + } + no_freeze--; + if (no_freeze == 0) { + break; + } + } + + // Find closed loop range i1..i2, first loop should be ignored, second is OK + int i1 = -1; + int i2 = -1; + for (int i = static_cast(current_track.size()) - 1;; --i) { + if (i == 0) { + return false; // failed + } + bool pass_through_start = current_track[i][0] > start_alpha_ && + current_track[i - 1][0] <= start_alpha_; + if (pass_through_start && i2 == -1) { + i2 = i; + } else if (pass_through_start && i1 == -1) { + i1 = i; + break; + } + } + + assert(i1 != -1 && i2 != -1); + + current_track = std::vector>( + current_track.begin() + i1, current_track.begin() + i2 - 1); + auto first_beta = current_track[0][1]; + auto first_perp_x = std::cos(first_beta); + auto first_perp_y = std::sin(first_beta); + // Length of perpendicular jump to put together head and tail + auto well_glued_together = std::sqrt( + std::pow(first_perp_x * (current_track[0][2] - current_track.back()[2]), + 2) + + std::pow(first_perp_y * (current_track[0][3] - current_track.back()[3]), + 2)); + if (well_glued_together > kTrackDetailStep) { + return false; + } + + // Red-white border on hard turns + int track_size = static_cast(current_track.size()); + std::vector border(track_size, false); + for (int i = 0; i < track_size; i++) { + bool good = true; + int oneside = 0; + for (int neg = 0; neg < kBorderMinCount; neg++) { + int idx1 = (i - neg - 0) >= 0 ? i - neg : i - neg + track_size; + int idx2 = (i - neg - 1) >= 0 ? i - neg - 1 : i - neg - 1 + track_size; + auto beta1 = current_track[idx1][1]; + auto beta2 = current_track[idx2][1]; + good &= (abs(beta1 - beta2) > (kTrackTurnRate * 0.2f)); + oneside += static_cast(Sign(beta1 - beta2)); + } + good &= (abs(oneside) == kBorderMinCount); + border[i] = good; + } + for (int i = 0; i < track_size; i++) { + for (int neg = 0; neg < kBorderMinCount; neg++) { + int idx = (i - neg >= 0) ? i - neg : i - neg + track_size; + border[idx] = border[idx] || border[i]; + } + } + + // Create tiles + for (int i = 0; i < track_size; i++) { + auto [alpha1, beta1, x1, y1] = current_track[i]; + int last_i = i - 1; + if (last_i < 0) { + last_i = static_cast(current_track.size()) - 1; + } + auto [alpha2, beta2, x2, y2] = current_track[last_i]; + b2Vec2 road1_l = {static_cast(x1 - kTrackWidth * std::cos(beta1)), + static_cast(y1 - kTrackWidth * std::sin(beta1))}; + b2Vec2 road1_r = {static_cast(x1 + kTrackWidth * std::cos(beta1)), + static_cast(y1 + kTrackWidth * std::sin(beta1))}; + b2Vec2 road2_l = {static_cast(x2 - kTrackWidth * std::cos(beta2)), + static_cast(y2 - kTrackWidth * std::sin(beta2))}; + b2Vec2 road2_r = {static_cast(x2 + kTrackWidth * std::cos(beta2)), + static_cast(y2 + kTrackWidth * std::sin(beta2))}; + std::array roads_vertices = {road1_l, road1_r, road2_r, road2_l}; + b2PolygonShape shape; + shape.Set(roads_vertices.data(), roads_vertices.size()); + fd_tile_.shape = &shape; + + b2BodyDef bd; + bd.type = b2_staticBody; + + auto* t = new Tile(); + t->body = world_->CreateBody(&bd); + t->body->CreateFixture(&fd_tile_); + + // t->body->SetUserData(t); // recently removed from 2.4.1 + t->body->GetUserData().pointer = reinterpret_cast(t); + + float c = 2.55f * static_cast(i % 3); + t->road_color = {kRoadColor[0] + c, kRoadColor[1] + c, kRoadColor[2] + c}; + + t->type = TILE_TYPE; + t->tile_road_visited = false; + t->road_friction = 1.0; + t->idx = i; + t->body->GetFixtureList()[0].SetSensor(true); + roads_.push_back(t); + roads_poly_.emplace_back(std::make_pair(roads_vertices, t->road_color)); + + if (border[i]) { + auto side = Sign(beta2 - beta1); + b2Vec2 b1_l{ + static_cast(x1 + side * kTrackWidth * std::cos(beta1)), + static_cast(y1 + side * kTrackWidth * std::sin(beta1))}; + b2Vec2 b1_r{static_cast(x1 + side * (kTrackWidth + kBorder) * + std::cos(beta1)), + static_cast(y1 + side * (kTrackWidth + kBorder) * + std::sin(beta1))}; + b2Vec2 b2_l{ + static_cast(x2 + side * kTrackWidth * std::cos(beta2)), + static_cast(y2 + side * kTrackWidth * std::sin(beta2))}; + b2Vec2 b2_r{static_cast(x2 + side * (kTrackWidth + kBorder) * + std::cos(beta2)), + static_cast(y2 + side * (kTrackWidth + kBorder) * + std::sin(beta2))}; + std::array border_vertices = {b1_l, b1_r, b2_r, b2_l}; + cv::Scalar border_color = + (i % 2 == 0) ? cv::Scalar(255, 255, 255) : cv::Scalar(0, 0, 255); + roads_poly_.emplace_back(std::make_pair(border_vertices, border_color)); + } + } + track_ = current_track; + return true; +} + +void CarRacingBox2dEnv::CarRacingReset(std::mt19937* gen) { + elapsed_step_ = 0; + done_ = false; + ResetBox2d(gen); + StepBox2d(gen, 0.0, 0.0, 0.0, false); +} + +void CarRacingBox2dEnv::ResetBox2d(std::mt19937* gen) { + // clean all body in world + if (!roads_.empty()) { + world_->SetContactListener(nullptr); + for (auto& t : roads_) { + world_->DestroyBody(t->body); + delete t; + t = nullptr; + } + roads_.clear(); + assert(car_ != nullptr); + car_->Destroy(); + } + listener_ = + std::make_unique(this, lap_complete_percent_); + world_->SetContactListener(listener_.get()); + reward_ = 0; + prev_reward_ = 0; + tile_visited_count_ = 0; + new_lap_ = false; + t_ = 0; + roads_poly_.clear(); + + bool success = false; + while (!success) { + success = CreateTrack(gen); + } + car_ = + std::make_unique(world_, track_[0][1], track_[0][2], track_[0][3]); +} + +void CarRacingBox2dEnv::CarRacingStep(std::mt19937* gen, float action0, + float action1, float action2) { + ++elapsed_step_; + StepBox2d(gen, action0, action1, action2, true); +} + +void CarRacingBox2dEnv::StepBox2d(std::mt19937* gen, float action0, + float action1, float action2, bool isAction) { + assert(car_ != nullptr); + assert(-1 <= action0 && action0 <= 1); + assert(0 <= action1 && action1 <= 1); + assert(0 <= action2 && action2 <= 1); + if (isAction) { + car_->Steer(-action0); + car_->Gas(action1); + car_->Brake(action2); + } + + car_->Step(1.0f / kFps); + world_->Step(1.0f / kFps, 6 * 30, 2 * 30); + t_ += 1.0f / kFps; + + step_reward_ = 0; + done_ = false; + // First step without action, called from reset() + if (isAction) { + reward_ -= 0.1; + // We actually don't want to count fuel spent, we want car to be faster. + car_->fuel_spent_ = 0.0; + step_reward_ = reward_ - prev_reward_; + prev_reward_ = reward_; + if (tile_visited_count_ == static_cast(track_.size()) || new_lap_) { + // Truncation due to finishing lap + // This should not be treated as a failure + // but like a timeout + // truncated = true; + done_ = true; + } + float x = car_->hull_->GetPosition().x; + float y = car_->hull_->GetPosition().y; + + if (abs(x) > kPlayfiled || abs(y) > kPlayfiled) { + // terminated = true; + done_ = true; + step_reward_ = -100; + } + + if (elapsed_step_ >= max_episode_steps_) { + done_ = true; + } + } + Render(); +} + +void CarRacingBox2dEnv::CreateImageArray() { + // cv::resize(surf_, img_array_, cv::Size(kStateW, kStateH), 0, 0, + // cv::INTER_AREA); + cv::resize(surf_, img_array_, cv::Size(kStateW, kStateH)); + cv::cvtColor(img_array_, img_array_, cv::COLOR_BGR2RGB); +} + +void CarRacingBox2dEnv::DrawColoredPolygon( + const std::array, 4>& field, const cv::Scalar& color, + float zoom, const std::array& translation, float angle, + bool clip) { + // This checks if the polygon is out of bounds of the screen, and we skip + // drawing if so. Instead of calculating exactly if the polygon and screen + // overlap, we simply check if the polygon is in a larger bounding box whose + // dimension is greater than the screen by MAX_SHAPE_DIM, which is the maximum + // diagonal length of an environment object + + bool exist = false; + std::vector poly; + for (const auto& f : field) { + auto f_roated = RotateRad(f, angle); + f_roated = {f_roated[0] * zoom + translation[0], + f_roated[1] * zoom + translation[1]}; + poly.emplace_back(cv::Point(f_roated[0], f_roated[1])); + if (-kMaxShapeDim <= f_roated[0] && + f_roated[0] <= static_cast(kWindowW) + kMaxShapeDim && + -kMaxShapeDim <= f_roated[1] && + f_roated[1] <= static_cast(kWindowH) + kMaxShapeDim) { + exist = true; + } + } + + if (!clip || exist) { + cv::fillPoly(surf_, poly, color); + } +} + +void CarRacingBox2dEnv::RenderRoad(float zoom, + const std::array& translation, + float angle) { + std::array, 4> field; + field[0] = {kPlayfiled, kPlayfiled}; + field[1] = {kPlayfiled, -kPlayfiled}; + field[2] = {-kPlayfiled, -kPlayfiled}; + field[3] = {-kPlayfiled, kPlayfiled}; + + // draw background + DrawColoredPolygon(field, kBgColor, zoom, translation, angle, false); + + // draw grass patches + std::vector, 4>> grass; + for (int x = -20; x < 20; x += 2) { + auto fx = static_cast(x); + for (int y = -20; y < 20; y += 2) { + auto fy = static_cast(y); + std::array, 4> grass; + grass[0] = {kGrassDim * fx + kGrassDim, kGrassDim * fy}; + grass[1] = {kGrassDim * fx, kGrassDim * fy}; + grass[2] = {kGrassDim * fx, kGrassDim * fy + kGrassDim}; + grass[3] = {kGrassDim * fx + kGrassDim, kGrassDim * fy + kGrassDim}; + DrawColoredPolygon(grass, kGrassColor, zoom, translation, angle); + } + } + + // draw road + for (auto& [poly, color] : roads_poly_) { + std::array, 4> field; + field[0] = {poly[0].x, poly[0].y}; + field[1] = {poly[1].x, poly[1].y}; + field[2] = {poly[2].x, poly[2].y}; + field[3] = {poly[3].x, poly[3].y}; + + DrawColoredPolygon(field, color, zoom, translation, angle); + } +} + +std::vector CarRacingBox2dEnv::VerticalInd(int place, int s, int h, + float val) const { + auto wh = static_cast(kWindowH); + auto fh = static_cast(h); + return { + cv::Point(place * s, wh - (fh + fh * val)), + cv::Point((place + 1) * s, wh - (fh + fh * val)), + cv::Point((place + 1) * s, wh - fh), + cv::Point(place * s, wh - fh), + }; +} + +std::vector CarRacingBox2dEnv::HorizInd(int place, int s, int h, + float val) const { + auto new_ind = (static_cast(place) + val) * static_cast(s); + return { + cv::Point(place * s, kWindowH - 4 * h), + cv::Point(new_ind, kWindowH - 4 * h), + cv::Point(new_ind, kWindowH - 2 * h), + cv::Point(place * s, kWindowH - 2 * h), + }; +} + +void CarRacingBox2dEnv::RenderIfMin(float value, + const std::vector& points, + const cv::Scalar& color) { + if (abs(value) > 1e-4) { + cv::fillPoly(surf_, points, color); + } +} + +void CarRacingBox2dEnv::RenderIndicators() { + int h = kWindowH / 40; + int s = kWindowW / 40; + std::vector poly = { + cv::Point(kWindowW, kWindowH), cv::Point(kWindowW, kWindowH - 5 * h), + cv::Point(0, kWindowH - 5 * h), cv::Point(0, kWindowH)}; + cv::fillPoly(surf_, poly, cv::Scalar(0, 0, 0)); + + assert(car_ != nullptr); + + auto true_speed = static_cast( + std::sqrt(std::pow(car_->hull_->GetLinearVelocity().x, 2) + + std::pow(car_->hull_->GetLinearVelocity().y, 2))); + + RenderIfMin(true_speed, VerticalInd(5, s, h, 0.02f * true_speed), + cv::Scalar(255, 255, 255)); + // ABS sensors + RenderIfMin(car_->wheels_[0]->omega, + VerticalInd(7, s, h, 0.01f * car_->wheels_[0]->omega), + cv::Scalar(255, 0, 0)); + RenderIfMin(car_->wheels_[1]->omega, + VerticalInd(8, s, h, 0.01f * car_->wheels_[1]->omega), + cv::Scalar(255, 0, 0)); + RenderIfMin(car_->wheels_[2]->omega, + VerticalInd(9, s, h, 0.01f * car_->wheels_[2]->omega), + cv::Scalar(255, 0, 51)); + RenderIfMin(car_->wheels_[3]->omega, + VerticalInd(10, s, h, 0.01f * car_->wheels_[3]->omega), + cv::Scalar(255, 0, 51)); + + RenderIfMin( + car_->wheels_[0]->joint->GetJointAngle(), + HorizInd(20, s, h, -10.0f * car_->wheels_[0]->joint->GetJointAngle()), + cv::Scalar(0, 255, 0)); + RenderIfMin(car_->hull_->GetAngularVelocity(), + HorizInd(30, s, h, -0.8f * car_->hull_->GetAngularVelocity()), + cv::Scalar(0, 0, 255)); +} + +void CarRacingBox2dEnv::Render() { + // render mode == "state_pixels" + cv::Scalar black(0, 0, 0); + surf_ = cv::Mat(kWindowH, kWindowW, CV_8UC3, black); + + assert(car_ != nullptr); + // computing transformations + float angle = -car_->hull_->GetAngle(); + // Animating first second zoom. + float zoom = 0.1f * kScale * std::max(1 - t_, 0.f) + + kZoom * kScale * std::min(t_, 1.f); + float scroll_x = -car_->hull_->GetPosition().x * zoom; + float scroll_y = -car_->hull_->GetPosition().y * zoom; + + std::array scroll = {scroll_x, scroll_y}; + std::array trans = RotateRad(scroll, angle); + trans = {static_cast(kWindowW) / 2.0f + trans[0], + static_cast(kWindowH) / 4.0f + trans[1]}; + + RenderRoad(zoom, trans, angle); + car_->Draw(surf_, zoom, trans, angle); + + cv::flip(surf_, surf_, 0); + RenderIndicators(); + + auto reward = static_cast(reward_); + cv::putText(surf_, cv::format("%04d", reward), + cv::Point(20, kWindowH - kWindowH * 2 / 40.0), + cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255), 2, 0); +} + +} // namespace box2d diff --git a/envpool/box2d/car_racing_env.h b/envpool/box2d/car_racing_env.h new file mode 100644 index 00000000..97fa2b94 --- /dev/null +++ b/envpool/box2d/car_racing_env.h @@ -0,0 +1,132 @@ +/* + * Copyright 2021 Garena Online Private Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// https://github.com/openai/gym/blob/master/gym/envs/box2d/car_racing.py + +#ifndef ENVPOOL_BOX2D_CAR_RACING_ENV_H_ +#define ENVPOOL_BOX2D_CAR_RACING_ENV_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "car_dynamics.h" +#include "utils.h" + +namespace box2d { + +class CarRacingBox2dEnv; + +class CarRacingFrictionDetector : public b2ContactListener { + public: + explicit CarRacingFrictionDetector(CarRacingBox2dEnv* _env, + float lap_complete_percent); + void BeginContact(b2Contact* contact) override { Contact(contact, true); } + void EndContact(b2Contact* contact) override { Contact(contact, false); } + + protected: + CarRacingBox2dEnv* env_; + float lap_complete_percent_; + void Contact(b2Contact* contact, bool begin); +}; + +class CarRacingBox2dEnv { + const int kStateW = 96; + const int kStateH = 96; + const int kWindowW = 1000; + const int kWindowH = 800; + const float kScale = 6.0; // Track scale + const float kFps = 50; // Frames per second + const float kZoom = 2.7; + const float kTrackRad = + 900 / kScale; // Track is heavily morphed circle with this radius + const float kPlayfiled = 2000 / kScale; // Game over boundary + const float kTrackTurnRate = 0.31; + const float kTrackDetailStep = 21 / kScale; + const float kTrackWidth = 40 / kScale; + + const float kBorder = 8.f / kScale; + const int kBorderMinCount = 4; + + const float kGrassDim = kPlayfiled / 20; + const float kMaxShapeDim = + std::max(kGrassDim, std::max(kTrackWidth, kTrackDetailStep)) * sqrt(2.f) * + kZoom * kScale; + const int kCheckPoint = 12; + + friend class CarRacingFrictionDetector; + + protected: + float lap_complete_percent_; + int max_episode_steps_, elapsed_step_{0}; + float reward_{0}; + float prev_reward_{0}; + float step_reward_{0}; + bool done_; + + cv::Mat surf_; + cv::Mat img_array_; + + std::unique_ptr listener_; + std::shared_ptr world_; + std::unique_ptr car_; + int tile_visited_count_{0}; + float start_alpha_{0}; + float t_{0}; + bool new_lap_{false}; + b2FixtureDef fd_tile_; + std::vector> track_; + std::vector roads_; + // pair of position and color + std::vector, cv::Scalar>> roads_poly_; + + public: + CarRacingBox2dEnv(int max_episode_steps, float lap_complete_percent); + void Render(); + + void RenderRoad(float zoom, const std::array& translation, + float angle); + void RenderIndicators(); + void DrawColoredPolygon(const std::array, 4>& field, + const cv::Scalar& color, float zoom, + const std::array& translation, float angle, + bool clip = true); + void CarRacingReset(std::mt19937* gen); + void CarRacingStep(std::mt19937* gen, float action0, float action1, + float action2); + void CreateImageArray(); + + private: + [[nodiscard]] std::vector VerticalInd(int place, int s, int h, + float val) const; + [[nodiscard]] std::vector HorizInd(int place, int s, int h, + float val) const; + void RenderIfMin(float value, const std::vector& points, + const cv::Scalar& color); + bool CreateTrack(std::mt19937* gen); + void ResetBox2d(std::mt19937* gen); + void StepBox2d(std::mt19937* gen, float action0, float action1, float action2, + bool isAction); +}; + +} // namespace box2d + +#endif // ENVPOOL_BOX2D_CAR_RACING_ENV_H_ diff --git a/envpool/box2d/registration.py b/envpool/box2d/registration.py index b54fc7c4..1252f21a 100644 --- a/envpool/box2d/registration.py +++ b/envpool/box2d/registration.py @@ -15,6 +15,15 @@ from envpool.registration import register +register( + task_id="CarRacing-v2", + import_path="envpool.box2d", + spec_cls="CarRacingEnvSpec", + dm_cls="CarRacingDMEnvPool", + gym_cls="CarRacingGymEnvPool", + max_episode_steps=1000, +) + register( task_id="BipedalWalker-v3", import_path="envpool.box2d", diff --git a/envpool/box2d/utils.cc b/envpool/box2d/utils.cc index 0b049878..8298365d 100644 --- a/envpool/box2d/utils.cc +++ b/envpool/box2d/utils.cc @@ -30,4 +30,20 @@ float Sign(double val, double eps) { return 0; } +std::array RotateRad(const std::array& vec, float angle) { + return {std::cos(angle) * vec[0] - std::sin(angle) * vec[1], + std::sin(angle) * vec[0] + std::cos(angle) * vec[1]}; +} + +b2Vec2 RotateRad(const b2Vec2& v, float angle) { + return {std::cos(angle) * v.x - std::sin(angle) * v.y, + std::sin(angle) * v.x + std::cos(angle) * v.y}; +} + +b2Vec2 Multiply(const b2Transform& trans, const b2Vec2& v) { + float x = (trans.q.c * v.x - trans.q.s * v.y) + trans.p.x; + float y = (trans.q.s * v.x + trans.q.c * v.y) + trans.p.y; + return b2Vec2(x, y); +} + } // namespace box2d diff --git a/envpool/box2d/utils.h b/envpool/box2d/utils.h index b2fafc03..bd558b95 100644 --- a/envpool/box2d/utils.h +++ b/envpool/box2d/utils.h @@ -19,6 +19,7 @@ #include +#include #include namespace box2d { @@ -31,6 +32,12 @@ b2Vec2 Vec2(double x, double y); float Sign(double val, double eps = 1e-8); +std::array RotateRad(const std::array& vec, float angle); + +b2Vec2 RotateRad(const b2Vec2& v, float angle); + +b2Vec2 Multiply(const b2Transform& trans, const b2Vec2& v); + } // namespace box2d #endif // ENVPOOL_BOX2D_UTILS_H_ diff --git a/envpool/classic_control/BUILD b/envpool/classic_control/BUILD index 5af9fb0c..087df3a1 100644 --- a/envpool/classic_control/BUILD +++ b/envpool/classic_control/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") diff --git a/envpool/core/BUILD b/envpool/core/BUILD index fd57eee4..79cb9bdd 100644 --- a/envpool/core/BUILD +++ b/envpool/core/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pybind11_bazel//:build_defs.bzl", "pybind_library") package(default_visibility = ["//visibility:public"]) diff --git a/envpool/dummy/BUILD b/envpool/dummy/BUILD index 3b19ed1e..8c3b8914 100644 --- a/envpool/dummy/BUILD +++ b/envpool/dummy/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") diff --git a/envpool/make_test.py b/envpool/make_test.py index ff9287d6..e349ffe2 100644 --- a/envpool/make_test.py +++ b/envpool/make_test.py @@ -98,6 +98,7 @@ def test_make_toytext(self) -> None: def test_make_box2d(self) -> None: self.check_step( [ + "CarRacing-v2", "BipedalWalker-v3", "BipedalWalkerHardcore-v3", "LunarLander-v2", diff --git a/envpool/mujoco/BUILD b/envpool/mujoco/BUILD index b738acf5..fe92bd6c 100644 --- a/envpool/mujoco/BUILD +++ b/envpool/mujoco/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") diff --git a/envpool/python/BUILD b/envpool/python/BUILD index 992b4adf..ef0ec376 100644 --- a/envpool/python/BUILD +++ b/envpool/python/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) diff --git a/envpool/toy_text/BUILD b/envpool/toy_text/BUILD index 6ec39350..46612b2f 100644 --- a/envpool/toy_text/BUILD +++ b/envpool/toy_text/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") diff --git a/envpool/utils/BUILD b/envpool/utils/BUILD index 285f666c..74117433 100644 --- a/envpool/utils/BUILD +++ b/envpool/utils/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + package(default_visibility = ["//visibility:public"]) cc_library( diff --git a/envpool/vizdoom/BUILD b/envpool/vizdoom/BUILD index 1c1e679d..76ccffa7 100644 --- a/envpool/vizdoom/BUILD +++ b/envpool/vizdoom/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@pip_requirements//:requirements.bzl", "requirement") load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") diff --git a/envpool/vizdoom/bin/BUILD b/envpool/vizdoom/bin/BUILD index 73e3bfca..2c369cba 100644 --- a/envpool/vizdoom/bin/BUILD +++ b/envpool/vizdoom/bin/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + package(default_visibility = ["//visibility:public"]) genrule( diff --git a/third_party/BUILD b/third_party/BUILD index e69de29b..12106201 100644 --- a/third_party/BUILD +++ b/third_party/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/ale/BUILD b/third_party/ale/BUILD index e69de29b..12106201 100644 --- a/third_party/ale/BUILD +++ b/third_party/ale/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/atari_roms/BUILD b/third_party/atari_roms/BUILD index e69de29b..12106201 100644 --- a/third_party/atari_roms/BUILD +++ b/third_party/atari_roms/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/box2d/BUILD b/third_party/box2d/BUILD index e69de29b..12106201 100644 --- a/third_party/box2d/BUILD +++ b/third_party/box2d/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/concurrentqueue/BUILD b/third_party/concurrentqueue/BUILD index e69de29b..12106201 100644 --- a/third_party/concurrentqueue/BUILD +++ b/third_party/concurrentqueue/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/cuda/BUILD b/third_party/cuda/BUILD index e69de29b..12106201 100644 --- a/third_party/cuda/BUILD +++ b/third_party/cuda/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/freedoom/BUILD b/third_party/freedoom/BUILD index e69de29b..12106201 100644 --- a/third_party/freedoom/BUILD +++ b/third_party/freedoom/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/glibc_version_header/BUILD b/third_party/glibc_version_header/BUILD index e69de29b..12106201 100644 --- a/third_party/glibc_version_header/BUILD +++ b/third_party/glibc_version_header/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/jpeg/BUILD b/third_party/jpeg/BUILD index e69de29b..12106201 100644 --- a/third_party/jpeg/BUILD +++ b/third_party/jpeg/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/mujoco/BUILD b/third_party/mujoco/BUILD index e69de29b..12106201 100644 --- a/third_party/mujoco/BUILD +++ b/third_party/mujoco/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/mujoco_dmc_xml/BUILD b/third_party/mujoco_dmc_xml/BUILD index e69de29b..12106201 100644 --- a/third_party/mujoco_dmc_xml/BUILD +++ b/third_party/mujoco_dmc_xml/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/mujoco_gym_xml/BUILD b/third_party/mujoco_gym_xml/BUILD index e69de29b..12106201 100644 --- a/third_party/mujoco_gym_xml/BUILD +++ b/third_party/mujoco_gym_xml/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/nasm/BUILD b/third_party/nasm/BUILD index e69de29b..12106201 100644 --- a/third_party/nasm/BUILD +++ b/third_party/nasm/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/opencv/BUILD b/third_party/opencv/BUILD index e69de29b..12106201 100644 --- a/third_party/opencv/BUILD +++ b/third_party/opencv/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/pip_requirements/BUILD b/third_party/pip_requirements/BUILD index eb91a953..3c506229 100644 --- a/third_party/pip_requirements/BUILD +++ b/third_party/pip_requirements/BUILD @@ -1,3 +1,17 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + package(default_visibility = ["//visibility:public"]) exports_files(["requirements.txt"]) diff --git a/third_party/pretrain_weight/BUILD b/third_party/pretrain_weight/BUILD index e69de29b..12106201 100644 --- a/third_party/pretrain_weight/BUILD +++ b/third_party/pretrain_weight/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/pugixml/BUILD b/third_party/pugixml/BUILD index e69de29b..12106201 100644 --- a/third_party/pugixml/BUILD +++ b/third_party/pugixml/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/sdl2/BUILD b/third_party/sdl2/BUILD index e69de29b..12106201 100644 --- a/third_party/sdl2/BUILD +++ b/third_party/sdl2/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/threadpool/BUILD b/third_party/threadpool/BUILD index e69de29b..12106201 100644 --- a/third_party/threadpool/BUILD +++ b/third_party/threadpool/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/vizdoom/BUILD b/third_party/vizdoom/BUILD index e69de29b..12106201 100644 --- a/third_party/vizdoom/BUILD +++ b/third_party/vizdoom/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/vizdoom_extra_maps/BUILD b/third_party/vizdoom_extra_maps/BUILD index e69de29b..12106201 100644 --- a/third_party/vizdoom_extra_maps/BUILD +++ b/third_party/vizdoom_extra_maps/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/vizdoom_lib/BUILD b/third_party/vizdoom_lib/BUILD index e69de29b..12106201 100644 --- a/third_party/vizdoom_lib/BUILD +++ b/third_party/vizdoom_lib/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/third_party/zlib/BUILD b/third_party/zlib/BUILD index e69de29b..12106201 100644 --- a/third_party/zlib/BUILD +++ b/third_party/zlib/BUILD @@ -0,0 +1,13 @@ +# Copyright 2022 Garena Online Private Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License.