game-rules-engine
Builds game rules as a pure seeded engine: create, listMoves, applyMove, outcome, events, property tests.
Skill by zakariaf
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-rules-engine
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: writing
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-rules-engine
What's inside
- Skill name
game-rules-engine- Description
- Builds game rules as a pure seeded engine: create, listMoves, applyMove, outcome, events, property tests. Use when writing game rules, moves, scoring or in-game randomness. Not for levels (level-generation-and-solvers).
- License
- MIT
- Instructions
- 209 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (93)
- SKILL.md
- shared.json · assets/
- template-catalog-keys.json · assets/
- template-placeholders.json · assets/
- intent-to-move.ts · examples/lights-out-module/canvas/
- lights-out-game.test.ts · examples/lights-out-module/
- lights-out-game.ts · examples/lights-out-module/
- catalog-keys.json · examples/pig/
- apply-move.test.ts · examples/pig/rules/
- apply-move.ts · examples/pig/rules/
- continue-run.test.ts · examples/pig/rules/
- continue-run.ts · examples/pig/rules/
- create.test.ts · examples/pig/rules/
- create.ts · examples/pig/rules/
- list-moves.ts · examples/pig/rules/
- outcome.test.ts · examples/pig/rules/
- outcome.ts · examples/pig/rules/
- pig-engine.test.ts · examples/pig/rules/
- pig-engine.ts · examples/pig/rules/
- pig-hold-policy.ts · examples/pig/rules/
- pig-persistence.test.ts · examples/pig/rules/
- pig-persistence.ts · examples/pig/rules/
- pig-stats.ts · examples/pig/rules/
- pig-tuning.ts · examples/pig/rules/
- pig-types.ts · examples/pig/rules/
- roll-die.test.ts · examples/pig/rules/
- roll-die.ts · examples/pig/rules/
- pig-testing.test.ts · examples/pig/testing/
- pig-testing.ts · examples/pig/testing/
- checker.md · references/
- determinism-and-rng.md · references/
- engine-contract.md · references/
- testing-rules.md · references/
- writing-rules.md · references/
- check-lib.mjs · scripts/
- check-rules-engine.mjs · scripts/
- app-modules.mjs · scripts/lib/
- engine-run.mjs · scripts/lib/
- game-layout.mjs · scripts/lib/
- kit-checks.mjs · scripts/lib/
- rules-checks.mjs · scripts/lib/
- ts-scan.mjs · scripts/lib/
- selftest.mjs · scripts/
- difficulty.test.ts · templates/src/core/game/
- difficulty.ts · templates/src/core/game/
- game-engine.ts · templates/src/core/game/
- game-input.ts · templates/src/core/game/
- game-module.ts · templates/src/core/game/
- game-rules.ts · templates/src/core/game/
- levels.ts · templates/src/core/game/
- messages.ts · templates/src/core/game/
- persistence.ts · templates/src/core/game/
- stats.ts · templates/src/core/game/
- testing.ts · templates/src/core/game/
- contract-intents.test.ts · templates/src/core/game/testing/
- contract-intents.ts · templates/src/core/game/testing/
- engine-contract.test.ts · templates/src/core/game/testing/
- engine-contract.ts · templates/src/core/game/testing/
- json-shape.test.ts · templates/src/core/game/testing/
- json-shape.ts · templates/src/core/game/testing/
- …and 33 more
SKILL.md (first lines)
# Game rules engine
Makes every game's rules a pure, deterministic TypeScript engine behind one game contract: the same
seed and moves give the same states on iOS, Android, Jest and Node, so bots, solvers, saves, undo,
replays and the daily level can all trust it. The skill ships the kit (`src/core/game/`: the contract
types, the difficulty scale and the test helpers), a complete template game (the public-domain puzzle
Lights Out) and a checker that proves the kit, the seeded RNG against its golden values and each
game's rules, by running them.
## Rules that must hold
1. **The engine functions are pure and live in `src/games/<game-id>/rules/`, one per file:**
`create.ts`, `list-moves.ts`, `apply-move.ts`, `outcome.ts`, importing only `src/core` and the
game's own `rules/`, `levels/` and `geom/`. Why: bots, solvers, goldens and save replays call them
headless thousands of times; one React Native, store or app import breaks all of that.
2. **Rules follow the determinism policy:** only `+ - * /`, `%`, bitwise operators and
`Math.sqrt/imul/floor/round/abs/min/max/PI`; no `**`, no other `Math.*`, no `Math.random`, `Date`,
`performance.now`, `Intl`, `toLocale*` or `localeCompare` (tests: no clock, no randomness either).
Why: Hermes uses each platform's libm and locale data while Jest uses V8's, so replays and daily
levels would diverge between an iPhone, an Android phone and CI (seeded-determinism-and-golden-vectors
owns the policy).
3. **All randomness comes from `src/core/rng/sfc32.ts`, threaded as state.** A game that draws after
`create()` keeps `rng: RngState` in its state and every draw advances it; never re-seed from the move
count. Why: same seed, same level, same daily and same replay on every device, and a continue or an
undo cannot re-roll.
4. **`sfc32.ts` and its golden values never change:** `seedRng(1)` draws `1828152527, 3394835397,
2967886022, 2251045104, 4148684523` and `hashSeed('tasks:2026-09-26')` is `2847463752`. Why: they are
the compatibility contract of every generated level, daily and saved run; a different generator is
a new module under a new name, and a question for the developer.
5. **Copy the kit verbatim** from `templates/src/core/game/` and never rename a member (`create`,
`listMoves`, `applyMove`, `outcome`, `panMode`, `selectRegions`, `intentToMove`, `hud`, `undo`,
`hints`, `continueRun`; `identity` is `{ id, nameId }`). Why: the session host, the bots, the solver,
the canvas skills and the checker all call these exact names and shapes.
6. **The input is declared as data next to its intent function:** `panMode` (`'none'` taps only,
`'swipe'`, `'drag'`, `'aim'`) and `selectRegions` are literals in the module, and `intentToMove` (in
`src/games/<game-id>/canvas/`) returns only moves `listMoves` lists and only for the intents that
pan mode sends. Why: the screen enables exactly that gesture; a game expecting swipes on a `'none'`
surface is unplayable, and legality decided anywhere but `listMoves` lets bots and players disagree.
7. **Difficulty is one scale, whole numbers 0..100** (`difficulty.ts`): `create` clamps to it, tuning
rows come from `rowFor`, levels and the daily use 0..99, and 100 (`ENDLESS_DIFFICULTY`) is the
endless run of a game with an endless mode, which is never won. Why: the curve, the tuning, the daily
and the saved runs must mean the same numbers, and there is no mode argument to tell an endless run apart.
8. **A selection is UI state, never a move.** Tap-then-tap games list their selecting regions in
`selectRegions` (`[]` for most); `intentToMove` returns `null` for a tap there and reads
`intent.selected` on the next tap. Why: a `select` move would be seen by bots, the solver, par, undo
and the counters.
9. **State, moves and events are JSON-safe:** plain objects, arrays, finite numbers, strings, booleans,
`null`; no `Map`, `Set`, `Date`, classes, `undefined` or typed arrays. Why: runs are saved as JSON
after every move and replayed; anything JSON drops corrupts a save.
10. **`applyMove` never changes its input and throws `RangeError` for an illegal move.** Why: the undo
history keeps old states by reference, and a loud failure beats a silently corrupted save.
11. **`listMoves` is empty exactly when `outcome` is not `playing`, in a fixed order; each way of losing
has its own `<game-id>.lose.<reason>` key in every catalog of the app; win scores are whole numbers
>= 0.** Why: bots, solvers and the host's end-of-run logic rely on it, and every visible sentence is
a translated message.
12. **Events are past tense, kebab-case, and carry every id and from/to value** the animation and the
counters need. Why: the surface only receives the final state; the events alone explain how it got there.
13. **Every rules folder has example tests, fast-check properties (determinism, invariants, end state,
JSON round trip) and one pinned `GOLDEN_*` value, at 95/95/95/90 coverage.** Why: the tests are the
specification, and properties alone let real bugs survive.
14. **A saved run parses back to exactly itself.** `parseState` / `parseMove` rebuild a fresh typed
value with explicit checks (never `json as State`) and return `null` otherwise; `stateVersion` and
`migrateState` handle older shapes. Why: a damaged or old save must drop only the run, not crash the
app later.
15. **The testing spec plays fair:** the bot picks only offered moves and returns its own seeded stream,
and the `start`, `middle`, `win`, `lose` examples have exactly the outcome their names promise. Why:
balance runs, hints, screenshots and E2E setup are built on them.
16. **Once the canvas exists, the game is one typed `GameModule`** in `src/games/<game-id>/<game-id>-game.ts`
whose `identity.id` is the folder name, with a test that runs the contract with its input. Why: the
host binds games only through this value, and the id names the folder, the catalog namespace and
every saved run.
## Workflow
1. **Classify and plan.** Read [references/engine-contract.md](references/engine-contract.md). Classify
the game (turn-based, simulate-then-replay, real-time) and write down in plain words: moves, win, each
way of losing, score, undo, hints, continue, an endless mode (difficulty 100), the input (`panMode`,
tap-then-tap `selectRegions`). If the design does not say, ask the developer (defaults: unlimited
undo, or none when the game draws during play; no hints; no continue; no endless mode; taps only).
2. **Install the kit once per app.** Pin fast-check exactly in the root `devDependencies` (the kit's,
the RNG's and the rules' tests import it; without it `tsc` fails with TS2307 on every copied test):
`npm install -D -E fast-check@4.10.2` (verified on 2026-10-03; re-verify: `npm view fast-check
version`). Run
`node ${CLAUDE_SKILL_DIR}/scripts/check-rules-engine.mjs .`; for each `kit-file-missing` copy the
file from `templates/src/core/` to `src/core/` verbatim (with its test). `src/core/rng/sfc32.ts` is
canonical: if seeded-determinism-and-golden-vectors already put it there, keep that copy. A
`kit-member-missing` line names an older copy: replace it and fix what `tsc` reports. Read
[references/determinism-and-rng.md](references/determinism-and-rng.md) before anything random.
3. **Copy the rules templates.** `templates/src/games/__GAME_ID__/` to `src/games/<game-id>/`, renaming
files and replacing `__GAME_ID__` (kebab case), `__GAME_PASCAL__` and `__GAME_CONST__` (UPPER_SNAKE).
The template is a complete small game (Lights Out with a move budget); keep its structure and replace
its logic test by test. Every balance number lives in `<game-id>-tuning.ts`.
4. **Add the catalog keys** the templates name to every catalog of the app (`src/i18n/catalogs/<locale>.json`,
i18n-strings-and-catalogs): [assets/template-catalog-keys.json](assets/template-catalog-keys.json)
holds each key with an English and an RTL text (`.name`, `.hud.moves`, `.continue.more-moves`,
`.lose.out-of-moves`, `.stats.*`). A renamed or new key (a lose reason, a hud line, a counter) goes
into every catalog too, and translations go through the app's review queue.
5. **Write the rules test-first, in this order,** following [references/writing-rules.md](references/writing-rules.md)
and [references/testing-rules.md](references/testing-rules.md): types, tuning and `create` (with its
pinned golden), `outcome` and `listMoves`, `applyMove`, the rules members, persistence, counters, then
the bot and example states. For each behaviour: one failing test, read the failure (an assertion, not
an import error), minimum code, green. Imitate `examples/pig/` for a game that draws during play, has
an endless run or more than one way of losing.
6. **Assemble the engine** (`rules/<game-id>-engine.ts`: `<CONST>_ENGINE` and `<CONST>_RULES`) and keep
its contract test (`<game-id>-engine.test.ts`; add `endless` for a game with an endless mode).
7. **Assemble the module once the canvas exists.** When canvas-gestures-and-hit-testing has written
`src/games/<game-id>/canvas/intent-to-move.ts`, write `<game-id>-game.ts` and `<game-id>-game.test.ts`
from `examples/lights-out-module/`, with `panMode` and `selectRegions` from step 1 and the intents the
surface really sends. Until then the checker prints one SKIP for the module.
8. **Format, lint and type-check:** `npx eslint --fix src/games/<game-id> src/core/game`,
`npx prettier --write src/games/<game-id>` (import order depends on the game id), `npm run typecheck`.
9. **Run the tests with coverage:** `npx jest src/games/<game-id> --ci --selectProjects unit --coverage
--collectCoverageFrom='src/games/<game-id>/rules/**/*.ts' --coverageThreshold='{}'` (paths first;
`--coverageThreshold='{}'` because a subset run cannot meet the global numbers) and read the table:
rules stay at 95/95/95/90. When a module is finished, run Stryker on it (break threshold 75 %).
10. **Run the check** `node ${CLAUDE_SKILL_DIR}/scripts/check-rules-engine.mjs . --game <game-id>`; fix
every `FAIL` line (file, rule, fix) and rerun until `RESULT: PASS`. An `engine-contract` line names
the seed and difficulty: add that case as an example test first, then fix the engine. Rules,
options and troubleshooting: [references/checker.md](references/checker.md).From 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 (12 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
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
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
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 - 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
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
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
Cite this page
game-rules-engine skill. Agent Mart. https://agentmart.dev/skills/zakariaf-game-rules-engine (last updated 2026-10-24)