thedivergentai/godot-monte-carlo-balancer
Use when auditing or recalibrating game balance: build a source-driven Monte Carlo balance lab (Rust + rayon) that extracts live game data, simulates human playstyles (AFK→pro), emits win-rate/economy verdicts with confidence intervals, and bruteforce-tunes parameters. Trigger on unfair levels, unreachable shops, farm exploits, interest-curve cliffs, post-content recalibration, or CI balance JSON diffs. Keywords: balance lab, Monte Carlo, win rate, difficulty curve, economy career, playstyle simulation, Resource extraction, GDScript parser, bruteforce tuning.
npx skills add https://github.com/thedivergentai/GD-Agentic-Skills --skill godot-monte-carlo-balancer
.tres / Resources as the extract truth layer (Godot 4.x Resource pipelines).Build a bespoke, source-driven Monte Carlo balance lab for one game — not a prebuilt tool. Reference architecture: Rust CLI + rayon that extracts GameData, simulates imperfect humans, verdicts with CIs, tunes by simulation, and calibrates against headless Godot.
Always Phase 0 first. Lane-defense field names are not the default model.
godot-resource-data-patterns → godot-economy-system →
(godot-combat-system | godot-rpg-stats | godot-game-loop-waves) →
godot-monte-carlo-balancer → godot-testing-patterns → godot-builder
No hand-copied numbers in the sim. Parse Resources / source at startup so the next run reflects designer edits.
| Abstraction | Meaning | If absent |
|-------------|---------|-----------|
| Session | Bounded attempt | — |
| Threat | Pressure toward fail | delete |
| Defense / agency | Player levers | delete |
| Faults | Attention taxes | delete |
| Resources | Consumable flow | delete |
| In-run economy | Session spend | delete |
| Meta economy | Shop / unlocks / prestige | delete |
| Grade | Stars / rank / time / score | delete |
Write BALANCE_PLAN.md. Simulate only mapped rows.
> MANDATORY: Read the linked reference before implementing that phase.
>
> Do NOT Load:
> - example-lane-defense.md — unless Phase 0 maps to lane-defense / shift TD
> - 06-genre-adaptation.md — unless genre ≠ default PvE win%-band session
> - 07-godot-calibration.md — only when starting calibration or Phase 0 did not waive physics/AI (waiver = fully formulaic math-only game, documented in BALANCE_PLAN.md)
Genre, win/fail, modes, catalog, influence graph, economy, styles + primary metric, extraction plan. Confirm with designer.
Resource-first decision tree; inspect before any simulate.
Behavioral PlayStyle × InputModel (mouse/touch/gamepad), SessionModel for mobile, seeded SmallRng, rayon over independent jobs.
Wilson/bootstrap CI verdicts; secondary agency checks; stable JSON.
Careers, farms, interest curve, reward-cadence checkpoints.
Band-scored bruteforce; emit .tres when the project is Resource-first.
Metric overrides + Domain Skill chains.
3–5 golden cells vs headless Godot before full-matrix sign-off (unless waived).
Canonical layout after copy:
tools/
balance_lab.ps1 # from launcher.ps1
balance_lab.sh # from launcher.sh
balance_lab/
Cargo.toml
src/main.rs # clap stubs — expand per Phase 0
Copy Cargo.toml + src/ into tools/balance_lab/. Place launchers as tools/balance_lab.ps1 / tools/balance_lab.sh (siblings of the crate dir).
CI-aware snapshot diff.
Stable --json field contract.
balance-lab inspect
balance-lab simulate --level 3 --style average --runs 1000
balance-lab career --style casual --runs 200
balance-lab mode <key> --runs 500
balance-lab bruteforce --level 4 ...
balance-lab gen-level ...
balance-lab calibrate --cells golden.json
balance-lab --json <any command>
balance-lab --seed 42 <any command>
Bands are defined per style × input_model cell. Default input model is mouse.
| Style | Input | Win-rate target | Below → | Above → |
|---------|-------|-----------------|---------|---------|
| afk | mouse | 5% – 55% | TOO HARD | TOO EASY |
| casual | mouse | 55% – 90% | TOO HARD | TOO EASY |
| average | mouse | 70% – 95% | TOO HARD | TOO EASY |
| pro | mouse | 90% – 100% | TOO HARD | — |
| afk | touch | 5% – 55% | TOO HARD | TOO EASY |
| casual | touch | 55% – 90% | TOO HARD | TOO EASY |
| average | touch | 65% – 92% | TOO HARD | TOO EASY |
| pro | touch | 85% – 100% | TOO HARD | — |
A level is only OK when every simulated style × input_model cell lands inside its band. Difficulty must come from the level curve, not from punishing input speed alone.
> Platform Rule: If the game ships on mobile, the matrix MUST include touch input models. A level that is OK on mouse but TOO HARD on touch is TOO HARD.
| Mode | Runs/cell | OK rule |
|------|-----------|-----------|
| Search / working | 100–300 | 95% CI overlaps band; else TOO_HARD / TOO_EASY / INCONCLUSIVE |
| Sign-off / DoD / snapshot | ≥1000 | 95% CI fully ⊆ band for every style × shipped input model |
Fighting / educational / idle often replace win% — set primary metric in Phase 0.
.tres — hand copies rot into false conclusions; regex farms on Resource projects fight the data layer. Resource-first; regex only for inline formula coefficients. Flag every (default!) in inspect before the first simulate.HashMap-hashed seed paths — default hasher is process-randomized → false CI diffs across machines/rayon schedules; use seed_for + stable hash; unit-test determinism.RunState/RNG across rayon jobs — cross-talk masquerades as balance noise and breaks reproducibility..gd factories into a Resource-first project — emit .tres / Resource shape.GameData across game-source edits.--json + game_data_hash.BALANCE_PLAN.md + designer lock on bands/metrics.inspect → if unexpected (default!), stop and fix extract (example-lane-defense.md smell).inspect verified; no unexpected (default!).> Progressive disclosure: open Official Documentation links only when researching a specific API;
> load Related Skills when routing work to a peer domain — do not preload the whole lattice.
.tres over regex farms).Take thedivergentai/godot-monte-carlo-balancer from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.