Reinforcement Learning Sim-to-Real Python · PyTorch C++ · Geode

Teaching an AI to play the real Geometry Dash

Not a clone — the actual retail game on Steam. I train agents in a headless simulator running >2,000× real time, then deploy them onto Geometry Dash 2.2 through a custom C++ mod. Along the way: a controlled DQN/PPO/GA comparison, sim-to-real transfer, and a full official level cleared.

An agent flying the ship section of Stereo Madness on the retail game
An agent clearing the ship section of the official Stereo Madness on retail GD 2.2.

What's in the project

Five results — and exactly what each one means

I care more about being precise than impressive. Every claim below says what it actually is, including where it stops.

DQN · PPO · GA

A controlled algorithm comparison

Same observation, reward, and levels; five seeds each. All three solve it — but with its default exploration, PPO traps in a local optimum on 4/5 seeds where DQN and GA don't.

~11% → 35%

Sim-to-real transfer

A policy trained only in the sim drives the retail game via the mod (~11%); a checkpoint search on the real game clears the full cube section (~35%).

100%

Full-level clear

The complete official level — via learning from demonstration: a recorded human run replayed deterministically on the real game. Not autonomous RL, and I say so.

90.7%

Behavior cloning

A network trained on that demonstration predicts the human's jumps at 90.7% validation accuracy (jump F1 0.82) — a genuinely learned imitation policy.

~1.7×

Domain randomization

Training across randomized physics widens the policy's tolerance to the sim-to-real gap by ~1.7× vs a point estimate — the textbook fix, measured with a number.

Result #1

PPO isn't always the easy answer

DQN vs PPO vs GA comparison chart
Algorithmboth levelsseedsout of the box?
DQN (double)100%5/5yes
Genetic Algorithm100%5/5yes
PPO100%5/5needs tuning
Given each method its own hyperparameters, all three solve both levels on all 5 seeds. The catch is what PPO needed to get there — and DQN and GA didn't.

With its default exploration (entropy 0.01), on-policy PPO converges to "take the free progress, don't risk the jump" and stalls at exactly 21.6% on 4 of 5 seeds — through the entire 3M-step budget. A 5× entropy increase (0.01 → 0.05) is the whole difference between 1/5 and 5/5 solved. Value-based and evolutionary methods never fall in. This is the kind of thing a single-algorithm project can't surface.

PPO entropy ablation: default exploration traps at 21.6% on 4/5 seeds

Result #2

Closing the sim-to-real gap on purpose

A policy trained on one exact set of physics overfits the sim — which is why the sim-trained agent dies ~11% into the real game. The standard defense is domain randomization: jitter the physics every episode so the policy learns to survive a range, not a single point estimate. I trained DQN with and without it (5 seeds) and measured the tolerance to held-out physics error.

Robustness curves: domain randomization vs point estimate
Domain randomization widens the ≥90%-progress tolerance band ~1.7× on both jump strength and speed, and degrades gracefully where the point-estimate policy cliffs. It's not magic — beyond ±15% both fail at a timing-critical obstacle — but that's the honest ceiling, stated.

Results #3–5

Watch it run

The sim renders shareable clips, and the mod records footage straight off the retail game. A few of them:

Architecture

How it works

The retail game is the evaluation target, not the training loop — that's the whole idea. Training happens in a simulator thousands of times faster than real time.

01
Headless sim7 gamemodes, slopes, portals. >2,000× real time on one CPU core.
02
RL agentsDQN (double), PPO (GAE, vectorized), and a genetic algorithm.
03
Python bridgeShared observation builder — the sim and real game look identical to the policy.
04
Geode C++ modHooks the game loop, streams state and injects input over a binary socket.
05
Retail gameGeometry Dash 2.2 on Steam — the real evaluation target.

The part I won't hide

Honest scope & limitations

These are the most credible thing in the whole project, so they go up front, not in a footnote.

  • The guaranteed full clear is deterministic replay of a human demonstration, not an autonomous RL agent solving the level. The behavior-cloning policy learns the input mapping (90.7%) but drifts on a single frame-perfect level — so replay is used for the exact clear.
  • Sim-to-real transfer dies at ~11%. I've since calibrated the cube jump against logged real trajectories (the old constants undershot the real apex by ~8%), but closing the gap fully needs more than the jump arc alone.
  • Non-cube physics are approximate — chosen for plausible motion, pending calibration against logged trajectories; robot and spider are first-pass.
  • The mod targets macOS + GD 2.2081 (Geode's supported build); uncertain field reads are marked VERIFY in the source.

Built with

Tech stack

Python PyTorch Gymnasium NumPy C++20 Geode SDK POSIX sockets custom binary protocol matplotlib · ffmpeg 48 tests