Skip to content

Rendering

rle.reusable.rendering provides Pygame-compatible helpers backed by the pygame-ce distribution. They can be copied into a new paper or project repo without copying RL Engine's concrete environments.

What It Provides

helper purpose
PygameCheatcodeController slash-led /reset, /stats, and /controls commands, with clickable panel close markers
PygameLaunchManager restartable background pygame loops for Marimo play notebooks
RenderEnvMixin / RendererMixin small hooks for env-owned stats and Pygame renderers
blit_frame / draw_text_overlay notebook play-loop helpers for rgb_array frames
rle.reusable.rendering.gridworld procedural grid tiles, agent markers, and object glyphs
rle.reusable.rendering.matrix payoff matrices, score cards, badges, panels, and meters

The procedural helpers are the stable visual style for RL Engine's current multi-agent matrix and gridworld environments. Gridworld tiles use a flat, MASA-inspired beige board palette with thin grid lines, inset object tiles, and RL Engine's existing player markers. They intentionally avoid generated sprites, binary assets, and project-specific imports, so human and rgb_array render modes can share the same copy-pasteable scene drawing code.

Typical Use

from rle.reusable.rendering import RenderEnvMixin, RendererMixin
from rle.reusable.rendering.gridworld import draw_agent_marker, draw_walkable_tile

Custom environments should keep their own state, action mappings, and environment-specific stats in the project package. The reusable layer owns only the boring rendering infrastructure: command parsing, panel text, frame blits, and generic drawing primitives from the pygame import module provided by pygame-ce.

Active /stats and /controls panels include a small X marker in the panel's top-right corner. Render loops should pass mouse events through the same PygameCheatcodeController used for drawing so that marker can close the panel without retyping the slash command.

Marimo play notebooks that open a pygame-ce window should create one PygameLaunchManager, auto-launch a default play loop with pygame_launcher.restart(...), and call restart again from the notebook's Launch pygame button after controls change. The play loop should accept stop_event=None and exit when that event is set so the manager can replace the old window before starting a new environment instance.

Adapting To Your Project

For a new repo, copy src/rle/reusable/rendering and add pygame-ce to the project dependencies. Update imports from rle.reusable.rendering to the new package path, then keep environment state, reward logic, action labels, and project-specific stats outside the reusable package.

The first project-specific block is the renderer boundary. Let each environment own its state and expose only the small hooks the reusable helpers need:

class MyEnv(RenderEnvMixin):
    metadata = {"render_modes": ["human", "rgb_array"], "render_fps": 8}

    def render_stats(self) -> dict[str, object]:
        return {"episode": self.episode_id, "score": self.score}

The second block is visual language. Reuse the gridworld and matrix helpers when they match the domain, but add project drawing functions in the project package when the environment needs domain-specific objects, labels, or panels. Only move primitives back into reusable.rendering when they are generic enough to copy into another paper repo unchanged.

The blocks most likely to change are:

  • the project renderer class that translates environment state into draw calls;
  • the stats returned through RenderEnvMixin for /stats panels;
  • the controls text returned for /controls panels;
  • any project-local glyphs, tile types, badges, or payoff annotations.

Keep both human and rgb_array modes on the same drawing path when possible. That keeps notebook previews, video recording, and interactive debugging from drifting into separate visual implementations.