game-balance-and-bots
Tunes game difficulty with seeded headless bots: sim reports, balance bands, tuning knobs, a fun-in-seconds test.
Skill by zakariaf
Chalk Labs
Need help deploying game-balance-and-bots?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/zakariaf-game-balance-and-bots
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: testing
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/game-balance-and-bots
What's inside
- Skill name
game-balance-and-bots- Description
- Tunes game difficulty with seeded headless bots: sim reports, balance bands, tuning knobs, a fun-in-seconds test. Use when balancing, tuning pacing or adding bots or sims. Not for solvers or par (level-generation-and-solvers).
- License
- MIT
- Instructions
- 205 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (66)
- SKILL.md
- shared.json · assets/
- template-placeholders.json · assets/
- arena-chase.json · examples/arena-chase/reports/sim/
- arena-chase-sim.test.ts · examples/arena-chase/src/games/arena-chase/sim/
- arena-chase-sim.ts · examples/arena-chase/src/games/arena-chase/sim/
- arena-chase-tuning.ts · examples/arena-chase/src/games/arena-chase/sim/
- arena-chase-bot.test.ts · examples/arena-chase/src/games/arena-chase/testing/
- arena-chase-bot.ts · examples/arena-chase/src/games/arena-chase/testing/
- balance-bands.json · examples/arena-chase/test/sims/arena-chase/
- balance.sim.test.ts · examples/arena-chase/test/sims/arena-chase/
- pig.json · examples/pig/reports/sim/
- pig-tuning.ts · examples/pig/src/games/pig/rules/
- pig-bot.test.ts · examples/pig/src/games/pig/testing/
- pig-bot.ts · examples/pig/src/games/pig/testing/
- balance-bands.json · examples/pig/test/sims/pig/
- balance.sim.test.ts · examples/pig/test/sims/pig/
- balance-contract.md · references/
- bots-and-sims.md · references/
- checker.md · references/
- fun-and-kill-test.md · references/
- tuning-playbook.md · references/
- check-balance.mjs · scripts/
- check-lib.mjs · scripts/
- balance.mjs · scripts/lib/
- selftest.mjs · scripts/
- jest.sim.config.js · templates/
- balance-bands.test.ts · templates/src/core/game-balance/
- balance-bands.ts · templates/src/core/game-balance/
- bot-policies.test.ts · templates/src/core/game-balance/
- bot-policies.ts · templates/src/core/game-balance/
- parse-balance-bands.test.ts · templates/src/core/game-balance/
- parse-balance-bands.ts · templates/src/core/game-balance/
- run-sim-bot.test.ts · templates/src/core/game-balance/
- run-sim-bot.ts · templates/src/core/game-balance/
- sim-stats.test.ts · templates/src/core/game-balance/
- sim-stats.ts · templates/src/core/game-balance/
- trace-bot.test.ts · templates/src/core/game-balance/
- trace-bot.ts · templates/src/core/game-balance/
- difficulty.test.ts · templates/src/core/game/
- difficulty.ts · templates/src/core/game/
- game-engine.ts · templates/src/core/game/
- play-bot.test.ts · templates/src/core/game/testing/
- play-bot.ts · templates/src/core/game/testing/
- __GAME_ID__-tuning.test.ts · templates/src/games/__GAME_ID__/rules/
- __GAME_ID__-tuning.ts · templates/src/games/__GAME_ID__/rules/
- __GAME_ID__-types.ts · templates/src/games/__GAME_ID__/rules/
- apply-move.ts · templates/src/games/__GAME_ID__/rules/
- create.ts · templates/src/games/__GAME_ID__/rules/
- list-moves.ts · templates/src/games/__GAME_ID__/rules/
- outcome.ts · templates/src/games/__GAME_ID__/rules/
- toggle-cells.ts · templates/src/games/__GAME_ID__/rules/
- __GAME_ID__-bot.test.ts · templates/src/games/__GAME_ID__/testing/
- __GAME_ID__-bot.ts · templates/src/games/__GAME_ID__/testing/
- balance-bands.json · templates/test/sims/__GAME_ID__/
- balance.sim.test.ts · templates/test/sims/__GAME_ID__/
- write-sim-report.test.ts · templates/test/sims/
- write-sim-report.ts · templates/test/sims/
- jest.config.js · tests/bases/app/
- package.json · tests/bases/app/
- …and 6 more
SKILL.md (first lines)
# Game balance and bots
Every game is played thousands of times headless by seeded bots before a person plays it. The sims
measure the difficulty curve, whether skill matters, how soon the first win moment comes and whether
the twist happens; a bands file says what "balanced" means; every balance number lives in one tuning
file. This skill builds the harness once (`src/core/game-balance/`, `test/sims/write-sim-report.ts`),
each game's bots, sims and bands, and the check that judges the numbers.
## Rules that must hold
1. **Bots play only through the pure engine contract** (`create`, `listMoves`, `applyMove`,
`outcome`), headless in plain Node via `jest.sim.config.js`, never through the UI. Why: the same
rules the app runs are measured, in seconds, with no React Native mock.
2. **Every run is deterministic:** seeds `1…seedsPerCell`, the bot's own seeded RNG, no
`Math.random`, `Date`, `performance.now`, retries, skips or focus; the report has no timestamps.
Why: a rerun gives identical bytes, so a diff of the report shows exactly what a change did, and
chosen seeds could shop for numbers.
3. **Every run has a hard move cap, and no run may reach it** (`capHits` = 0). Why: a stuck or
endless game is a rules bug that a bot finds long before a player does.
4. **Use the three standard players:** `random` (the baseline), a reasonable player (the testing
spec's `bot`: `greedyPolicy` by default) and `lookahead` (the upper bound; it sees the seeded
future). In a game that draws during play (dice, cards, spawns), the reasonable player is a rule a
person could follow and never peeks at the next draw: `greedyPolicy` would. Skill must matter:
random < reasonable < lookahead by a clear step. Why: if naive play does as well as sensible play,
the choices are fake; a peeking player measures nothing a person meets.
5. **A sim asserts properties of at least 100 seeds per cell, never a single outcome,** and writes
`reports/sim/<game-id>.json` before its assertions. Why: one game proves nothing about balance, and
a failing run must still leave numbers to read.
6. **Every balance number lives in `src/games/<game-id>/rules/<game-id>-tuning.ts`** (real-time games:
`sim/<game-id>-tuning.ts`, a `'worklet'` module): documented knobs plus a per-difficulty table read
through `knobsFor`; the rules contain no other balance numbers. Why: one place to tune, and a diff
shows every balance change.
7. **Difficulty is one scale, whole numbers 0..100, never a table index.** `knobsFor(d)` returns the
endless row at 100 (`ENDLESS_DIFFICULTY`, no goal, never won) and otherwise the row
`rowFor(d, rows)` picks, with a `reduce` over the table; the grid samples the lower bound of each
row (`[0, 25, 50, 75]` for four rows), and an endless mode gets its own `endless` block at 100,
never a step of the curve. Why: levels (0..99), a daily and saved runs all use this scale; a 0..3
index makes every level above 3 play the hardest row.
8. **Balance is a contract in `test/sims/<game-id>/balance-bands.json`:** the bands hold, the curve
moves monotonically by `minStep`, the skill gap holds, the first payoff comes early, the twist
happens, and the endless run is never won and stays inside its own bands. Never widen a band, cut
or change seeds, or tune the bot to go green. Why: bands are the design record; moving them hides
the problem instead of fixing the game.
9. **The fun-within-seconds kill test:** on the easiest level the reasonable player reaches the first
payoff within 1-3 moves (2 for single-tap moves; seconds for real-time games: `runSimBot` with
`payoffStepTicks: 120`) in at least 90 % of runs, and the twist fires at least once per run. If
even the lookahead bot cannot, stop and report to the developer. Why: prototype bots showed ideas
fail here structurally (a loop-closing snake caught nothing in 13 foreseen moves; a crash puzzle's
twist came 0.25 times per game).
10. **A report counts only for the code it measured:** its fingerprint covers the game's `rules/`,
`levels/` (without the generated `pack-*.json`), `sim/`, `geom/` and `testing/` folders, the sim
file and the `src/core` files they import (the sim's import closure), and game logic imports
nothing outside them; rerun `npm run test:sim` after any change there. Why: stale numbers approve
code that no longer exists.
11. **Change the game, not the player:** improve a bot's evaluation only to match reasonable human
play, never to reach a band; decide the payoff and twist tags before the first run. Why: a bot or
a tag tuned to the bands measures nothing.
12. **A person's play-test is the final judge, and it never blocks:** bands stay `"proposed"` until a
person has played on an iPhone and an Android phone and agreed, then become `"approved"` with a
date. Until then every report lists it under "Human checks (not blocking)", and
`check-balance.mjs --release` prints a `HUMAN CHECK (not blocking)` line, not a problem. Why: bots
measure pace, not joy; the person's feel wins whenever it comes, and nothing waits for it.
13. **Bot numbers hold on both platforms only through pure, seeded rules and fixed ticks.** Sims run in
Node (V8), the app on Hermes on iOS and Android; a real-time sim counts fixed ticks, never display
frames (60 Hz Android phones, 120 Hz iPhones). Why: a balance claim that depends on the engine,
the frame rate or the device is not a claim about the game.
## Workflow
1. **Pick the job.** First harness setup: step 2. A game's bots and sims: steps 3-8. Retuning: steps
6-8. Judging a new idea early: read [references/fun-and-kill-test.md](references/fun-and-kill-test.md),
then steps 3-5 at the easiest difficulty only. Always finish with steps 9-10.
2. **Harness (once per app).** Read [references/bots-and-sims.md](references/bots-and-sims.md). The
game kit (`src/core/game/`, `src/core/rng/sfc32.ts`) comes from game-rules-engine. Run
`node ${CLAUDE_SKILL_DIR}/scripts/check-balance.mjs .`; for every `harness-missing`,
`harness-outdated` or `sim-config` line copy the file from `templates/` verbatim
(`src/core/game-balance/*` with tests, `test/sims/write-sim-report.ts` with its test,
`jest.sim.config.js`), keep `"test:sim": "jest --ci --config jest.sim.config.js"` in
`package.json`, and make the unit project of `jest.config.js` ignore `\\.sim\\.test\\.` (both are
in the canonical configs). Run `npx jest src/core/game-balance test/sims/write-sim-report.test.ts --ci --selectProjects unit`
(paths first: `--selectProjects` swallows every following word).
3. **Tuning file.** Read [references/tuning-playbook.md](references/tuning-playbook.md) first. The
game already has `rules/<game-id>-tuning.ts` from game-rules-engine's template. Name the knobs for
the game (document each per-difficulty knob once, on the knobs type), move every balance number of
the rules into it and read them through `TUNING` and `knobsFor(difficulty)`, keeping the `rowFor` +
`reduce` lookup. A game with an endless mode adds the endless row and returns it first for
`isEndlessDifficulty(difficulty)`: [examples/pig/src/games/pig/rules/pig-tuning.ts](examples/pig/src/games/pig/rules/pig-tuning.ts).
4. **Bot hooks, test-first.** Copy `templates/src/games/__GAME_ID__/testing/__GAME_ID__-bot.ts` and
its test (Lights Out's hooks; replace `__GAME_ID__`, `__GAME_PASCAL__` and `__GAME_CAMEL__`) and
rewrite them for the game: `evaluate<Game>`, `scoreOf<Game>` (the number the HUD shows),
`tag<Game>Event` (payoff and twist, decided now) and the reasonable player; then set it as `bot` in
`testing/<game-id>-testing.ts`. A game that draws during play writes the reasonable player as a rule
([examples/pig/src/games/pig/testing/pig-bot.ts](examples/pig/src/games/pig/testing/pig-bot.ts)).
When the levels are proven by the reasonable player (a witness), put the evaluation in
`rules/<game-id>-evaluate.ts` and re-export it: `levels/` may not import `testing/`. A real-time
game exports its `SimBotGame` hooks and the `idle`, `wander` and `dodge` command policies
([examples/arena-chase/src/games/arena-chase/testing/arena-chase-bot.ts](examples/arena-chase/src/games/arena-chase/testing/arena-chase-bot.ts)).
5. **Sim and bands.** Copy `templates/test/sims/__GAME_ID__/` to `test/sims/<game-id>/`, replace the
placeholders, set the grid to the lower bound of each tuning row (the template's `[0, 34, 67]` are
Lights Out's three), name the policies, and for an endless mode replace `"endless": null` with its
block ([references/balance-contract.md](references/balance-contract.md), "Endless mode"). Run
`npx eslint --fix` and `npx prettier --write` on it, then
`npx jest test/sims/<game-id> --ci --config jest.sim.config.js`. The first run writes the report
even if the bands fail. Real-time games play through `runSimBot` instead of `traceBot`
([examples/arena-chase/test/sims/arena-chase/balance.sim.test.ts](examples/arena-chase/test/sims/arena-chase/balance.sim.test.ts)).
6. **Read and tune.** Fix what is broken first (cap hits, replay differences, no early payoff, no
twist), then tune one knob at a time with the tuning loop and the symptom table, rerunning the sim
after each change and reading `lossReasons`.
7. **Write the bands** around the tuned numbers with a margin and a `why` per band, and the story in
`notes` in plain words; keep `"status": "proposed"`.
8. **Kill test verdict.** If the first payoff or the twist cannot be reached after reasonable tuning,
stop and tell the developer (fun-and-kill-test.md, "When a game fails"). Do not polish a game that
fails it.
9. **Run the check** (run, fix, rerun until `RESULT: PASS`): `npm run test:sim`, then
`node ${CLAUDE_SKILL_DIR}/scripts/check-balance.mjs .` (add `--game <game-id>` for one game).
Options, config keys and troubleshooting: [references/checker.md](references/checker.md).
10. **Report and hand over.** Put the plain-language "Bots" line per difficulty (numbers from the
report) in the evidence report and "Play-test <game> on an iPhone and an Android phone" under
"Human checks (not blocking)" until a person has done it; when they agree, setFrom zakariaf/ReactNative-Skills, MIT.
More from zakariaf/ReactNative-Skills
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
Signals
- Stars
- 11
- Forks
- 2
- Open issues
- 0
- Last commit (this folder)
- Oct 17, 2026 (14 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get game-balance-and-bots running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
game-balance-and-bots skill. Agent Mart. https://agentmart.dev/skills/zakariaf-game-balance-and-bots (last updated 2026-10-24)