tinysim 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. tinysim-0.0.1/PKG-INFO +56 -0
  2. tinysim-0.0.1/README.md +30 -0
  3. tinysim-0.0.1/pyproject.toml +50 -0
  4. tinysim-0.0.1/setup.cfg +4 -0
  5. tinysim-0.0.1/tinysim/__init__.py +11 -0
  6. tinysim-0.0.1/tinysim/_tk_base.py +54 -0
  7. tinysim-0.0.1/tinysim/flappy/__init__.py +103 -0
  8. tinysim-0.0.1/tinysim/flappy/sim.js +118 -0
  9. tinysim-0.0.1/tinysim/flappy/tk.py +94 -0
  10. tinysim-0.0.1/tinysim/flappy/widget.py +53 -0
  11. tinysim-0.0.1/tinysim/frogger/__init__.py +145 -0
  12. tinysim-0.0.1/tinysim/frogger/sim.js +110 -0
  13. tinysim-0.0.1/tinysim/frogger/tk.py +95 -0
  14. tinysim-0.0.1/tinysim/frogger/widget.py +60 -0
  15. tinysim-0.0.1/tinysim/mountain_car/__init__.py +56 -0
  16. tinysim-0.0.1/tinysim/mountain_car/sim.js +158 -0
  17. tinysim-0.0.1/tinysim/mountain_car/styles.css +5 -0
  18. tinysim-0.0.1/tinysim/mountain_car/tk.py +141 -0
  19. tinysim-0.0.1/tinysim/mountain_car/widget.py +56 -0
  20. tinysim-0.0.1/tinysim/simple_amr/__init__.py +0 -0
  21. tinysim-0.0.1/tinysim/simple_amr/example_maps.py +121 -0
  22. tinysim-0.0.1/tinysim/simple_amr/sim.js +430 -0
  23. tinysim-0.0.1/tinysim/simple_amr/styles.css +54 -0
  24. tinysim-0.0.1/tinysim/simple_amr/widget.py +73 -0
  25. tinysim-0.0.1/tinysim/topdown_driving/__init__.py +190 -0
  26. tinysim-0.0.1/tinysim/topdown_driving/sim.js +136 -0
  27. tinysim-0.0.1/tinysim/topdown_driving/tk.py +180 -0
  28. tinysim-0.0.1/tinysim/topdown_driving/track_0.json +753 -0
  29. tinysim-0.0.1/tinysim/topdown_driving/widget.py +60 -0
  30. tinysim-0.0.1/tinysim.egg-info/PKG-INFO +56 -0
  31. tinysim-0.0.1/tinysim.egg-info/SOURCES.txt +36 -0
  32. tinysim-0.0.1/tinysim.egg-info/dependency_links.txt +1 -0
  33. tinysim-0.0.1/tinysim.egg-info/requires.txt +6 -0
  34. tinysim-0.0.1/tinysim.egg-info/top_level.txt +2 -0
  35. tinysim-0.0.1/tinysim_warp/cart_pole/__init__.py +259 -0
  36. tinysim-0.0.1/tinysim_warp/quadruped/__init__.py +203 -0
  37. tinysim-0.0.1/tinysim_warp/simple_quadruped/__init__.py +168 -0
  38. tinysim-0.0.1/tinysim_warp/simple_quadruped/simple_quadruped.urdf +247 -0
tinysim-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: tinysim
3
+ Version: 0.0.1
4
+ Summary: small modular simulation environments
5
+ Author-email: Matthew Taylor <matthew.taylor.andre@gmail.com>
6
+ Project-URL: Homepage, https://github.com/MatthewAndreTaylor/TinySim
7
+ Keywords: jupyter,simulation
8
+ Classifier: Framework :: Jupyter
9
+ Classifier: Framework :: Jupyter :: JupyterLab
10
+ Classifier: Framework :: Jupyter :: JupyterLab :: 4
11
+ Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: numpy
22
+ Requires-Dist: anywidget
23
+ Requires-Dist: jupyter-ui-poll
24
+ Provides-Extra: warp
25
+ Requires-Dist: warp-lang==1.8.1; extra == "warp"
26
+
27
+
28
+ # TinySim
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/coderbot-sim.svg)](https://pypi.org/project/coderbot-sim)
31
+ [![Jupyterlite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://matthewandretaylor.github.io/CoderBots/lab?path=example.ipynb)
32
+
33
+ The goal of this project is to create more minimal simulation environments for robotics and machine learning.
34
+
35
+ Existing solutions like [ROS 2](https://github.com/ros2) and [Gymnasium](https://gymnasium.farama.org/) come with many heavy dependencies.
36
+
37
+ Additionally they are usually difficult to get setup and may require domain specific knowledge to use.
38
+ Simulation environments should be able to run in any notebook environment and require fewer dependencies.
39
+
40
+
41
+ ## Get Started 🚀
42
+
43
+ To use the baseline Python only simulation environments.
44
+
45
+ ```bash
46
+ pip install tinysim
47
+ ```
48
+
49
+ To use the [warp](https://github.com/NVIDIA/warp) simulation environments.
50
+
51
+ ```bash
52
+ pip install tinysim[warp]
53
+ ```
54
+
55
+
56
+
@@ -0,0 +1,30 @@
1
+
2
+ # TinySim
3
+
4
+ [![PyPI](https://img.shields.io/pypi/v/coderbot-sim.svg)](https://pypi.org/project/coderbot-sim)
5
+ [![Jupyterlite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://matthewandretaylor.github.io/CoderBots/lab?path=example.ipynb)
6
+
7
+ The goal of this project is to create more minimal simulation environments for robotics and machine learning.
8
+
9
+ Existing solutions like [ROS 2](https://github.com/ros2) and [Gymnasium](https://gymnasium.farama.org/) come with many heavy dependencies.
10
+
11
+ Additionally they are usually difficult to get setup and may require domain specific knowledge to use.
12
+ Simulation environments should be able to run in any notebook environment and require fewer dependencies.
13
+
14
+
15
+ ## Get Started 🚀
16
+
17
+ To use the baseline Python only simulation environments.
18
+
19
+ ```bash
20
+ pip install tinysim
21
+ ```
22
+
23
+ To use the [warp](https://github.com/NVIDIA/warp) simulation environments.
24
+
25
+ ```bash
26
+ pip install tinysim[warp]
27
+ ```
28
+
29
+
30
+
@@ -0,0 +1,50 @@
1
+ [project]
2
+ name = "tinysim"
3
+ version = "0.0.1"
4
+ description = "small modular simulation environments"
5
+ authors = [
6
+ {name = "Matthew Taylor", email = "matthew.taylor.andre@gmail.com"},
7
+ ]
8
+ requires-python = ">=3.9"
9
+ classifiers = [
10
+ "Framework :: Jupyter",
11
+ "Framework :: Jupyter :: JupyterLab",
12
+ "Framework :: Jupyter :: JupyterLab :: 4",
13
+ "Framework :: Jupyter :: JupyterLab :: Extensions",
14
+ "Programming Language :: Python",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ ]
22
+ urls = {Homepage = "https://github.com/MatthewAndreTaylor/TinySim"}
23
+ keywords = ["jupyter", "simulation"]
24
+
25
+ dependencies = [
26
+ "numpy",
27
+ "anywidget",
28
+ "jupyter-ui-poll"
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ warp = ["warp-lang==1.8.1"]
33
+
34
+ [project.readme]
35
+ file = "README.md"
36
+ content-type = "text/markdown"
37
+
38
+ [build-system]
39
+ requires = ["setuptools>=61.0", "wheel"]
40
+ build-backend = "setuptools.build_meta"
41
+
42
+ [tool.setuptools]
43
+ packages = ["tinysim", "tinysim_warp"]
44
+ include-package-data = true
45
+
46
+ [tool.setuptools.package-data]
47
+ tinysim = ["**/*"]
48
+ tinysim_warp = ["**/*"]
49
+
50
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class SimEnvironment(ABC):
5
+ @abstractmethod
6
+ def step(self, action) -> dict:
7
+ pass
8
+
9
+ @abstractmethod
10
+ def reset(self) -> dict:
11
+ pass
@@ -0,0 +1,54 @@
1
+ import tkinter as tk
2
+ import threading
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class TkBaseFrontend(ABC):
7
+
8
+ def __init__(self):
9
+ self._root = None
10
+ self._canvas = None
11
+ self._thread = None
12
+
13
+ def render(self):
14
+ if self._thread is not None:
15
+ return
16
+ self._thread = threading.Thread(target=self._window_hook, daemon=True)
17
+ self._thread.start()
18
+
19
+ def _window_hook(self):
20
+ root = tk.Tk()
21
+ root.protocol("WM_DELETE_WINDOW", self._on_close)
22
+ self._create_window(root)
23
+
24
+ @abstractmethod
25
+ def _create_window(self, root):
26
+ pass
27
+
28
+ def bring_to_front(self, root):
29
+ root.lift()
30
+ root.attributes("-topmost", True)
31
+ root.after_idle(root.attributes, "-topmost", False)
32
+ root.focus_force()
33
+
34
+ def _on_close(self):
35
+ if self._root:
36
+ try:
37
+ self._root.destroy()
38
+ except tk.TclError:
39
+ pass
40
+ self._root = None
41
+ self._canvas = None
42
+
43
+ def _pump(self):
44
+ if not self._root:
45
+ return
46
+
47
+ try:
48
+ self._root.update_idletasks()
49
+ self._root.update()
50
+ except tk.TclError:
51
+ return
52
+
53
+ if self._root:
54
+ self._root.after(20, self._pump)
@@ -0,0 +1,103 @@
1
+ import numpy as np
2
+ from .. import SimEnvironment
3
+
4
+ WIDTH, HEIGHT = 800, 600
5
+ GRAVITY = 900.0
6
+ FLAP_STRENGTH = -300.0
7
+ PIPE_SPEED = -200.0
8
+ PIPE_WIDTH = 80
9
+ PIPE_GAP = 200
10
+ PIPE_INTERVAL = 1.6
11
+
12
+ BIRD_X = 200
13
+ BIRD_SIZE = 35
14
+
15
+
16
+ class FlappyEnv(SimEnvironment):
17
+ def __init__(self, num_envs: int = 1):
18
+ self.num_envs = num_envs
19
+ self.reset()
20
+
21
+ def reset(self):
22
+ # birds (vectorized)
23
+ self.bird_y = np.full(self.num_envs, HEIGHT / 2, dtype=np.float32)
24
+ self.bird_vel = np.zeros(self.num_envs, dtype=np.float32)
25
+ self.done = np.zeros(self.num_envs, dtype=bool)
26
+
27
+ # shared pipes
28
+ self.pipes_x = np.empty(0, dtype=np.float32)
29
+ self.pipes_y = np.empty(0, dtype=np.float32)
30
+ self.time_since_pipe = PIPE_INTERVAL
31
+ return self.step(np.zeros(self.num_envs, dtype=np.int32), dt=0.0)
32
+
33
+ def _step_physics(self, action, dt):
34
+ flap_mask = (action == 1) & (~self.done)
35
+ self.bird_vel[flap_mask] = FLAP_STRENGTH
36
+ self.bird_vel += GRAVITY * dt
37
+ self.bird_y += self.bird_vel * dt
38
+
39
+ def _spawn_pipe(self):
40
+ pipe_y = np.random.randint(120, HEIGHT - 120 - PIPE_GAP)
41
+ self.pipes_x = np.append(self.pipes_x, WIDTH)
42
+ self.pipes_y = np.append(self.pipes_y, pipe_y)
43
+
44
+ def _update_pipes(self, dt):
45
+ self.time_since_pipe += dt
46
+ if self.time_since_pipe > PIPE_INTERVAL:
47
+ self.time_since_pipe = 0.0
48
+ self._spawn_pipe()
49
+
50
+ self.pipes_x += PIPE_SPEED * dt
51
+
52
+ # keep pipes on screen
53
+ keep = self.pipes_x > -PIPE_WIDTH
54
+ self.pipes_x = self.pipes_x[keep]
55
+ self.pipes_y = self.pipes_y[keep]
56
+
57
+ def _check_collisions(self): # world bounds
58
+ hit_bounds = (self.bird_y < 0) | (self.bird_y + BIRD_SIZE > HEIGHT)
59
+ bx = BIRD_X
60
+ by = self.bird_y[:, None]
61
+ px = self.pipes_x[None, :]
62
+ upper_y = np.zeros_like(self.pipes_y)[None, :]
63
+ upper_h = self.pipes_y[None, :]
64
+ lower_y = (self.pipes_y + PIPE_GAP)[None, :]
65
+ lower_h = (HEIGHT - (self.pipes_y + PIPE_GAP))[None, :]
66
+ x_overlap = (bx < px + PIPE_WIDTH) & (bx + BIRD_SIZE > px)
67
+ upper_hit = x_overlap & (by < upper_y + upper_h) & (by + BIRD_SIZE > upper_y)
68
+ lower_hit = x_overlap & (by < lower_y + lower_h) & (by + BIRD_SIZE > lower_y)
69
+ hit_pipe = (upper_hit | lower_hit).any(axis=1)
70
+ self.done |= hit_bounds | hit_pipe
71
+
72
+ def step(self, action, dt=0.02):
73
+ if np.isscalar(action):
74
+ action = np.full(self.num_envs, action, dtype=np.float32)
75
+ else:
76
+ action = np.asarray(action, dtype=np.float32)
77
+ if action.shape[0] != self.num_envs:
78
+ raise ValueError(
79
+ f"Expected actions of shape ({self.num_envs},), got {action.shape}"
80
+ )
81
+
82
+ # mask done environments to have no action
83
+ action = action * (~self.done)
84
+ self._step_physics(action, dt)
85
+ self._update_pipes(dt)
86
+ self._check_collisions()
87
+
88
+ bird_y = self.bird_y.tolist()
89
+ bird_vel = self.bird_vel.tolist()
90
+ done = self.done.tolist()
91
+
92
+ if self.num_envs == 1:
93
+ bird_y = self.bird_y[0]
94
+ bird_vel = self.bird_vel[0]
95
+ done = bool(self.done[0])
96
+
97
+ return {
98
+ "bird_y": bird_y,
99
+ "bird_vel": bird_vel,
100
+ "pipes_x": self.pipes_x.tolist(),
101
+ "pipes_y": self.pipes_y.tolist(),
102
+ "done": done,
103
+ }
@@ -0,0 +1,118 @@
1
+ let simState = {};
2
+
3
+ export default {
4
+ initialize({ model }) {
5
+ model.on("change:sim_state", () => {
6
+ simState = model.get("sim_state") || {};
7
+ });
8
+ },
9
+
10
+ async render({ model, el }) {
11
+ const [width, height] = model.get("_viewport_size") || [800, 600];
12
+
13
+ const container = document.createElement("div");
14
+ container.style.position = "relative";
15
+ container.style.width = width + "px";
16
+ container.style.height = height + "px";
17
+ el.appendChild(container);
18
+
19
+ const canvas = document.createElement("canvas");
20
+ canvas.width = width;
21
+ canvas.height = height;
22
+ container.appendChild(canvas);
23
+ const ctx = canvas.getContext("2d");
24
+
25
+ const WORLD_WIDTH = 800;
26
+ const WORLD_HEIGHT = 600;
27
+ const BIRD_X = 200;
28
+ const BIRD_SIZE = 35;
29
+ const PIPE_WIDTH = 80;
30
+ const PIPE_GAP = 200;
31
+ const GROUND_HEIGHT = 80;
32
+
33
+ // Scale world to canvas (in case viewport differs from 800×600)
34
+ const scaleX = width / WORLD_WIDTH;
35
+ const scaleY = height / WORLD_HEIGHT;
36
+
37
+ function draw() {
38
+ const state = simState || {};
39
+ const birdY = state.bird_y ?? WORLD_HEIGHT / 2;
40
+ const pipes_x = state.pipes_x || [];
41
+ const pipes_y = state.pipes_y || [];
42
+ const done = state.done || false;
43
+
44
+ ctx.clearRect(0, 0, width, height);
45
+
46
+ // Background sky
47
+ ctx.fillStyle = "#70C5CE";
48
+ ctx.fillRect(0, 0, width, height);
49
+
50
+ // Ground
51
+ const groundH = GROUND_HEIGHT * scaleY;
52
+ ctx.fillStyle = "#DED895";
53
+ ctx.fillRect(0, height - groundH, width, groundH);
54
+
55
+ // Bird
56
+ const birdScreenX = BIRD_X * scaleX;
57
+ const birdScreenY = birdY * scaleY;
58
+ const birdSizeX = BIRD_SIZE * scaleX;
59
+ const birdSizeY = BIRD_SIZE * scaleY;
60
+
61
+ ctx.fillStyle = "#FFD700";
62
+ ctx.strokeStyle = "#000000";
63
+ ctx.lineWidth = 1;
64
+
65
+ if (!done) {
66
+ ctx.beginPath();
67
+ ctx.ellipse(
68
+ birdScreenX + birdSizeX / 2,
69
+ birdScreenY + birdSizeY / 2,
70
+ birdSizeX / 2,
71
+ birdSizeY / 2,
72
+ 0,
73
+ 0,
74
+ Math.PI * 2
75
+ );
76
+ ctx.fill();
77
+ ctx.stroke();
78
+ }
79
+
80
+ // Pipes
81
+ ctx.fillStyle = "#228B22";
82
+ ctx.strokeStyle = "#4a8d34";
83
+ ctx.lineWidth = 2 * ((scaleX + scaleY) / 2);
84
+
85
+ for (let i = 0; i < pipes_x.length; i++) {
86
+ const pipeX = pipes_x[i];
87
+ const pipeY = pipes_y[i];
88
+
89
+ const pw = PIPE_WIDTH * scaleX;
90
+ const ux = pipeX * scaleX;
91
+ const uy = 0; // Upper pipe starts at top of screen
92
+ const uh = pipeY * scaleY; // Upper pipe height extends down to pipeY
93
+ const lx = pipeX * scaleX;
94
+ const ly = (pipeY + PIPE_GAP) * scaleY; // Lower pipe starts after gap
95
+ const lh = (WORLD_HEIGHT - (pipeY + PIPE_GAP)) * scaleY; // Lower pipe extends to bottom
96
+
97
+ // Upper pipe
98
+ ctx.beginPath();
99
+ ctx.rect(ux, uy, pw, uh);
100
+ ctx.fill();
101
+ ctx.stroke();
102
+
103
+ // Lower pipe
104
+ ctx.beginPath();
105
+ ctx.rect(lx, ly, pw, lh);
106
+ ctx.fill();
107
+ ctx.stroke();
108
+ }
109
+
110
+ requestAnimationFrame(draw);
111
+ }
112
+
113
+ draw();
114
+
115
+ model.set("_view_ready", true);
116
+ model.save_changes();
117
+ }
118
+ };
@@ -0,0 +1,94 @@
1
+ import asyncio
2
+
3
+ try:
4
+ import tkinter as tk
5
+ from .. import _tk_base
6
+ except ImportError:
7
+ raise ImportError("tkinter is required for FlappyTkFrontend")
8
+
9
+ from . import (
10
+ FlappyEnv,
11
+ WIDTH,
12
+ HEIGHT,
13
+ PIPE_WIDTH,
14
+ BIRD_SIZE,
15
+ BIRD_X,
16
+ PIPE_GAP,
17
+ )
18
+
19
+
20
+ class FlappyTkFrontend(_tk_base.TkBaseFrontend):
21
+
22
+ def __init__(self, viewport_size=(800, 600), sim_env=None):
23
+ super().__init__()
24
+ if sim_env is None:
25
+ sim_env = FlappyEnv()
26
+
27
+ self.sim_env = sim_env
28
+ self._viewport_size = viewport_size
29
+
30
+ async def step(self, action, dt=0.02):
31
+ state = self.sim_env.step(action, dt=dt)
32
+ if self._root:
33
+ self._root.after(0, lambda: self._draw_state(state))
34
+
35
+ await asyncio.sleep(dt)
36
+ return state
37
+
38
+ async def reset(self):
39
+ state = self.sim_env.reset()
40
+ if self._root:
41
+ self._draw_state(state)
42
+ return state
43
+
44
+ def _create_window(self, root):
45
+ w, h = self._viewport_size
46
+ root.title("Flappy Bird")
47
+ canvas = tk.Canvas(root, width=w, height=h, bg="#1E1E1E")
48
+ canvas.pack(fill="both", expand=True)
49
+ self._root = root
50
+ self._canvas = canvas
51
+ self.bring_to_front(root)
52
+ self._draw_state(self.sim_env.reset())
53
+ self._pump()
54
+ root.mainloop()
55
+
56
+ def _draw_state(self, state):
57
+ if not self._canvas:
58
+ return
59
+
60
+ canvas = self._canvas
61
+ canvas.delete("all")
62
+ canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill="#70C5CE", outline="")
63
+ canvas.create_rectangle(
64
+ 0, HEIGHT - 80, WIDTH, HEIGHT, fill="#DED895", outline=""
65
+ )
66
+
67
+ # bird
68
+ by = state["bird_y"]
69
+
70
+ if not state.get("done", False):
71
+ canvas.create_oval(
72
+ BIRD_X,
73
+ by,
74
+ BIRD_X + BIRD_SIZE,
75
+ by + BIRD_SIZE,
76
+ fill="#FFD700",
77
+ outline="#000",
78
+ )
79
+
80
+ # pipes
81
+ for x, y in zip(state["pipes_x"], state["pipes_y"]):
82
+ # Upper pipe
83
+ canvas.create_rectangle(
84
+ x, 0, x + PIPE_WIDTH, y, fill="#228B22", outline="#4a8d34"
85
+ )
86
+ # Lower pipe
87
+ canvas.create_rectangle(
88
+ x,
89
+ y + PIPE_GAP,
90
+ x + PIPE_WIDTH,
91
+ HEIGHT,
92
+ fill="#228B22",
93
+ outline="#4a8d34",
94
+ )
@@ -0,0 +1,53 @@
1
+ import pathlib
2
+ import anywidget
3
+ import traitlets
4
+ import asyncio
5
+ from IPython.display import display
6
+ from jupyter_ui_poll import ui_events
7
+
8
+ from . import FlappyEnv
9
+
10
+
11
+ class FlappySim(anywidget.AnyWidget):
12
+ _esm = pathlib.Path(__file__).parent / "sim.js"
13
+
14
+ sim_state = traitlets.Dict(default_value={}).tag(sync=True)
15
+ _viewport_size = traitlets.Tuple(
16
+ traitlets.Int(), traitlets.Int(), default_value=(800, 600)
17
+ ).tag(sync=True)
18
+ _manual_control = traitlets.Bool(default_value=False).tag(sync=True)
19
+ _view_ready = traitlets.Bool(default_value=False).tag(sync=True)
20
+
21
+ def __init__(self, viewport_size=(800, 600), manual_control=False, sim_env=None):
22
+ super().__init__()
23
+ self._viewport_size = viewport_size
24
+ self._manual_control = manual_control
25
+ if sim_env is None:
26
+ sim_env = FlappyEnv()
27
+ if sim_env.num_envs != 1:
28
+ raise ValueError("FlappySim currently only supports single environment.")
29
+
30
+ self.sim_env = sim_env
31
+ self.sim_state = self.sim_env.reset()
32
+
33
+ def render(self):
34
+ display(self)
35
+
36
+ try:
37
+ with ui_events() as ui_poll:
38
+ while not self._view_ready:
39
+ ui_poll(100)
40
+ except Exception:
41
+ pass
42
+
43
+ async def step(self, action, dt=0.02):
44
+ state = self.sim_env.step(action, dt=dt)
45
+ self.sim_state = state
46
+ await asyncio.sleep(dt)
47
+ return state
48
+
49
+ async def reset(self):
50
+ state = self.sim_env.reset()
51
+ self.sim_state = state
52
+ await asyncio.sleep(0)
53
+ return state