A fast, deterministic Clash Royale battle simulator in C++ with Python bindings, a recurrent PPO agent, lookahead search, and a computer-vision bridge to the real game.
Python
1
441 commits
updated Sep 27, 2026
A fast, deterministic Clash Royale battle engine in C++, with Python bindings, a recurrent PPO agent, lookahead search, and a computer-vision bridge to the real game.
The replay viewer's Simulation View. The opponent (red) plans by simulation. Every second it scores each candidate play by running the match 10 seconds forward inside the engine. The left panel shows each candidate's placement now and the board it predicts afterwards, and the numbered rings on the arena mark the same candidates. Blue is the PPO agent.
The story in 87 seconds. Unmute for the voiceover.
https://github.com/user-attachments/assets/9508d054-0a50-489f-ba2a-3595480b8a20
Music: "Hiding Your Reality" by Kevin MacLeod (incompetech.com), licensed under CC BY 4.0. Edited: trimmed, faded and mixed under the voiceover.
You can't speed up the real game, and it has no API. Reinforcement learning needs millions of games. ClashRoyaleEnv rebuilds the battle from the ground up as a headless simulator. It plays a full match in about 10 ms on one laptop CPU core, roughly 20,000× real time. It can also fork any position in 7–20 µs to look ahead.
The engine is the core of the project. On top of it sit a complete RL training stack, a search-based opponent, and a perception pipeline. That pipeline reads a live match off the screen and replays it inside the engine.
Engine (include/, src/)
seed()
fixes the opening hand, and snapshot() forks a match for lookahead or
paired A/B tests.Learning (python_ai/)
Perception (perception/)
Tooling
web/viewer.html) with tower
health, hands, an event log, an entity inspector, and the agent's value
estimate. Its Simulation View shows the teacher's candidate plays and
the board it predicted for each. It works offline.tools/audit/) that measure engine behaviour
directly: pathing stalls, sight ranges, spawn speeds and collision
performance.The engine is standard C++, and its test suite has also been built with g++ under WSL. Linux and macOS builds of the Python extension are on the roadmap.
A single script installs Python 3.11 if it's missing, creates the virtual environments, configures CMake, builds the extension and the test suite, and runs both test suites:
git clone https://github.com/itzik123/ClashRoyaleAi.git
cd ClashRoyaleAi
powershell -ExecutionPolicy Bypass -File tools\setup_dev_env.ps1
This produces python_ai/clash_royale_env.pyd and
build_python/Release/ClashRoyaleTests.exe.
The script's header documents flags for skipping steps, such as -SkipTests.
import python_ai # puts the compiled engine on sys.path
import clash_royale_env as cr
deck = [15, 6, 25, 40, 24, 72, 33, 7] # 2.6 Hog Cycle
env = cr.ClashRoyaleEnv(deck, deck, max_ticks=3600)
env.seed(0)
print(env.get_hand()) # card ids in hand, e.g. [24, 7, 72, 40]
print(cr.get_card_info(15)) # {'name': 'Hog Rider', 'cost': 4.0, ...}
result = env.step(card_index=0, target_x=3.0, target_y=14.0) # play slot 0 at the left bridge
print(len(result.observation), result.reward, result.done)
branch = env.snapshot() # fork the match
for _ in range(20):
branch.step(4, 0.0, 0.0) # slot 4 = no-op; the live match is untouched
from python_ai.envs.gym_wrapper import MicroRoyaleEnv
env = MicroRoyaleEnv() # 2.6 Hog Cycle vs. the utility teacher
obs, info = env.reset(seed=0)
done = False
while not done:
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
done = terminated or truncated
$env:CLASH_DECK = "hog rider,musketeer,cannon,ice golem,skeletons,ice spirit,the log,fireball"
python_ai\venv\Scripts\python.exe -m python_ai.trainers.train
Training logs to TensorBoard under runs/ and writes replays to replays/.
To watch a replay, open web/viewer.html in a browser and drop a replay JSON
onto it. The full operator guide, including monitoring and the phase-2
handoff, is in docs/runbooks/FINAL_RUN_RUNBOOK.md.
.\build_python\Release\ClashRoyaleTests.exe # C++: 715 cases
python_ai\venv\Scripts\python.exe -m pytest python_ai\tests -q # training stack
perception\.venv\Scripts\python.exe -m pytest perception\tests -q # perception
In a healthy C++ run exactly one case fails "as expected". It pins a known open collision defect, and the runner still exits 0.
flowchart LR
subgraph Engine["C++ engine (header-only)"]
GM[GameManager<br/>board, towers, elixir] --> ENT[Entities<br/>troops, buildings, spells]
GM --> SNAP[snapshot / seed]
end
Engine -- pybind11 --> PY[clash_royale_env]
PY --> GYM[Gymnasium env]
GYM --> PPO[Recurrent PPO<br/>CNN + LSTM]
TEACH[Utility teacher<br/>simulation-ranked] --> GYM
LEAGUE[PFSP league<br/>snapshots + bots] --> GYM
PY --> SEARCH[Lookahead search]
SEARCH -. distill .-> PPO
CV[Perception<br/>screen → events] -- state setters --> PY
GYM --> REPLAY[(Replay JSON)] --> VIEW[web/viewer.html]
All numbers are measured, and each comes with the conditions it was measured
under. The complete record, including the reversals, is in
docs/DECISIONS.md.
| Experiment | Result |
|---|---|
| 1-ply lookahead search vs. the greedy policy (160 paired matches, built-in heuristic opponent at 1.5× elixir, Aug 2026, earlier engine version) | win rate 0.625 → 0.944, +0.319 (95% CI +0.24 to +0.40, p = 5.6e-12) |
| Distilling search back into the policy (value-distribution targets + DAgger, 1,600 paired matches, Aug 2026) | +0.045 win rate (95% CI +0.013 to +0.077, p = 0.007) |
| Engine speed (20 seeded random-play matches, one core of an i5-13420H laptop, Sep 2026) | ~5 µs per tick (3.3–8.5 µs as the laptop's clock varies), about 10 ms per full match; forking a match takes 7–20 µs |
This is an active research project. The engine and tooling are the mature part. The agent isn't strong yet and doesn't beat competent human players. The PPO agent is being retrained from scratch on a new deck, after a full audit of the reward function and curriculum. Perception reads the elixir bar and your own hand from live gameplay. Detecting the opponent's placements is blocked until more recordings are available.
Every release from v0.1.0 onward, with what changed and whether it affects
checkpoints, is listed in docs/CHANGELOG.md.
pip install) for Linux and macOSperception/| Path | Contents |
|---|---|
include/, src/ | The C++ engine and its pybind11 bindings |
tests/ | C++ Catch2 test suite |
python_ai/ | Training stack: envs/, models/, rl/, trainers/, opponents/, search/, rewards/, eval/ |
perception/ | Screen capture → game events → simulator state |
web/viewer.html | Replay viewer |
tools/ | Environment setup and C++ audit tools |
docs/ | Design specs, the decision log, runbooks and measurements. Start at docs/README.md |
Issues and pull requests are welcome. Read
CONTRIBUTING.md for setup, tests and the rules for engine
changes. A good place to start is the list of
good first issues,
and questions and ideas are welcome in
Discussions.
Released under the MIT License.
perception/clashroyalebuildabot/ is vendored from
Clash Royale Build-A-Bot
and keeps its own MIT license.
This content is not affiliated with, endorsed, sponsored, or specifically approved by Supercell and Supercell is not responsible for it. For more information see Supercell's Fan Content Policy.
ClashRoyaleEnv is a research simulator. Automating play on a real Clash Royale account breaks Supercell's Terms of Service.
Python
68.2%
C++
29.2%
HTML
2.1%
A fast, deterministic Clash Royale battle simulator in C++ with Python bindings, a recurrent PPO agent, lookahead search, and a computer-vision bridge to the real game.
Python
1
441 commits
updated Sep 27, 2026
A fast, deterministic Clash Royale battle engine in C++, with Python bindings, a recurrent PPO agent, lookahead search, and a computer-vision bridge to the real game.
The replay viewer's Simulation View. The opponent (red) plans by simulation. Every second it scores each candidate play by running the match 10 seconds forward inside the engine. The left panel shows each candidate's placement now and the board it predicts afterwards, and the numbered rings on the arena mark the same candidates. Blue is the PPO agent.
The story in 87 seconds. Unmute for the voiceover.
https://github.com/user-attachments/assets/9508d054-0a50-489f-ba2a-3595480b8a20
Music: "Hiding Your Reality" by Kevin MacLeod (incompetech.com), licensed under CC BY 4.0. Edited: trimmed, faded and mixed under the voiceover.
You can't speed up the real game, and it has no API. Reinforcement learning needs millions of games. ClashRoyaleEnv rebuilds the battle from the ground up as a headless simulator. It plays a full match in about 10 ms on one laptop CPU core, roughly 20,000× real time. It can also fork any position in 7–20 µs to look ahead.
The engine is the core of the project. On top of it sit a complete RL training stack, a search-based opponent, and a perception pipeline. That pipeline reads a live match off the screen and replays it inside the engine.
Engine (include/, src/)
seed()
fixes the opening hand, and snapshot() forks a match for lookahead or
paired A/B tests.Learning (python_ai/)
Perception (perception/)
Tooling
web/viewer.html) with tower
health, hands, an event log, an entity inspector, and the agent's value
estimate. Its Simulation View shows the teacher's candidate plays and
the board it predicted for each. It works offline.tools/audit/) that measure engine behaviour
directly: pathing stalls, sight ranges, spawn speeds and collision
performance.The engine is standard C++, and its test suite has also been built with g++ under WSL. Linux and macOS builds of the Python extension are on the roadmap.
A single script installs Python 3.11 if it's missing, creates the virtual environments, configures CMake, builds the extension and the test suite, and runs both test suites:
git clone https://github.com/itzik123/ClashRoyaleAi.git
cd ClashRoyaleAi
powershell -ExecutionPolicy Bypass -File tools\setup_dev_env.ps1
This produces python_ai/clash_royale_env.pyd and
build_python/Release/ClashRoyaleTests.exe.
The script's header documents flags for skipping steps, such as -SkipTests.
import python_ai # puts the compiled engine on sys.path
import clash_royale_env as cr
deck = [15, 6, 25, 40, 24, 72, 33, 7] # 2.6 Hog Cycle
env = cr.ClashRoyaleEnv(deck, deck, max_ticks=3600)
env.seed(0)
print(env.get_hand()) # card ids in hand, e.g. [24, 7, 72, 40]
print(cr.get_card_info(15)) # {'name': 'Hog Rider', 'cost': 4.0, ...}
result = env.step(card_index=0, target_x=3.0, target_y=14.0) # play slot 0 at the left bridge
print(len(result.observation), result.reward, result.done)
branch = env.snapshot() # fork the match
for _ in range(20):
branch.step(4, 0.0, 0.0) # slot 4 = no-op; the live match is untouched
from python_ai.envs.gym_wrapper import MicroRoyaleEnv
env = MicroRoyaleEnv() # 2.6 Hog Cycle vs. the utility teacher
obs, info = env.reset(seed=0)
done = False
while not done:
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
done = terminated or truncated
$env:CLASH_DECK = "hog rider,musketeer,cannon,ice golem,skeletons,ice spirit,the log,fireball"
python_ai\venv\Scripts\python.exe -m python_ai.trainers.train
Training logs to TensorBoard under runs/ and writes replays to replays/.
To watch a replay, open web/viewer.html in a browser and drop a replay JSON
onto it. The full operator guide, including monitoring and the phase-2
handoff, is in docs/runbooks/FINAL_RUN_RUNBOOK.md.
.\build_python\Release\ClashRoyaleTests.exe # C++: 715 cases
python_ai\venv\Scripts\python.exe -m pytest python_ai\tests -q # training stack
perception\.venv\Scripts\python.exe -m pytest perception\tests -q # perception
In a healthy C++ run exactly one case fails "as expected". It pins a known open collision defect, and the runner still exits 0.
flowchart LR
subgraph Engine["C++ engine (header-only)"]
GM[GameManager<br/>board, towers, elixir] --> ENT[Entities<br/>troops, buildings, spells]
GM --> SNAP[snapshot / seed]
end
Engine -- pybind11 --> PY[clash_royale_env]
PY --> GYM[Gymnasium env]
GYM --> PPO[Recurrent PPO<br/>CNN + LSTM]
TEACH[Utility teacher<br/>simulation-ranked] --> GYM
LEAGUE[PFSP league<br/>snapshots + bots] --> GYM
PY --> SEARCH[Lookahead search]
SEARCH -. distill .-> PPO
CV[Perception<br/>screen → events] -- state setters --> PY
GYM --> REPLAY[(Replay JSON)] --> VIEW[web/viewer.html]
All numbers are measured, and each comes with the conditions it was measured
under. The complete record, including the reversals, is in
docs/DECISIONS.md.
| Experiment | Result |
|---|---|
| 1-ply lookahead search vs. the greedy policy (160 paired matches, built-in heuristic opponent at 1.5× elixir, Aug 2026, earlier engine version) | win rate 0.625 → 0.944, +0.319 (95% CI +0.24 to +0.40, p = 5.6e-12) |
| Distilling search back into the policy (value-distribution targets + DAgger, 1,600 paired matches, Aug 2026) | +0.045 win rate (95% CI +0.013 to +0.077, p = 0.007) |
| Engine speed (20 seeded random-play matches, one core of an i5-13420H laptop, Sep 2026) | ~5 µs per tick (3.3–8.5 µs as the laptop's clock varies), about 10 ms per full match; forking a match takes 7–20 µs |
This is an active research project. The engine and tooling are the mature part. The agent isn't strong yet and doesn't beat competent human players. The PPO agent is being retrained from scratch on a new deck, after a full audit of the reward function and curriculum. Perception reads the elixir bar and your own hand from live gameplay. Detecting the opponent's placements is blocked until more recordings are available.
Every release from v0.1.0 onward, with what changed and whether it affects
checkpoints, is listed in docs/CHANGELOG.md.
pip install) for Linux and macOSperception/| Path | Contents |
|---|---|
include/, src/ | The C++ engine and its pybind11 bindings |
tests/ | C++ Catch2 test suite |
python_ai/ | Training stack: envs/, models/, rl/, trainers/, opponents/, search/, rewards/, eval/ |
perception/ | Screen capture → game events → simulator state |
web/viewer.html | Replay viewer |
tools/ | Environment setup and C++ audit tools |
docs/ | Design specs, the decision log, runbooks and measurements. Start at docs/README.md |
Issues and pull requests are welcome. Read
CONTRIBUTING.md for setup, tests and the rules for engine
changes. A good place to start is the list of
good first issues,
and questions and ideas are welcome in
Discussions.
Released under the MIT License.
perception/clashroyalebuildabot/ is vendored from
Clash Royale Build-A-Bot
and keeps its own MIT license.
This content is not affiliated with, endorsed, sponsored, or specifically approved by Supercell and Supercell is not responsible for it. For more information see Supercell's Fan Content Policy.
ClashRoyaleEnv is a research simulator. Automating play on a real Clash Royale account breaks Supercell's Terms of Service.
Python
68.2%
C++
29.2%
HTML
2.1%