@provable-games/dm-engine
v0.7.1
Published
Death Mountain game rules and commit-reveal protocol, ported from the Cairo contracts. Shared by the client, the indexer and the API.
Downloads
1,112
Readme
@provable-games/dm-engine
The Death Mountain game rules and commit-reveal protocol in TypeScript — a mirror of the Cairo
in contracts/src.
Consumed by three places that must agree exactly:
| | |
| --- | --- |
| death-mountain-client | the board the player acts on |
| indexer | off-chain replay of committed transcripts into the event tables |
| api | token-id decoding |
Why it has to be shared
Under commit-reveal the chain executes nothing. The client's simulation is not a guess that on-chain events correct a second later — it is the board, and nothing corrects it. If it diverges from the Cairo replay by one point of health, the next committed action is state-invalid, settlement rejects the transcript, and the run is permanently unplayable.
Two copies of the rules would drift. There is one.
Keeping it honest
The rules are hand-maintained, so nothing in Cairo forces them to stay in step. Three things do:
test/vectors.scheme.json—root,fold,action_entropyand every opcode encoding, printed by the real Cairo. Regenerate withsnforge test commit_reveal::test_vectorsincontracts/, then paste into the fixture.test/replay.entropy.test.ts— asserts each action is a pure function of(state, action, entropy)and draws zeroMath.random(). A missed roll is the bug class that bricks runs.GameSettledcross-check — in production the indexer diffs its replayed adventurer against the contract's packed result and writes areplay_divergencesrow on mismatch. That is the only genuinely independent check, since the client and the indexer share this code.
Publishing
indexer, api and the client all depend on the published package (^0.1.0), not a
workspace link. That is deliberate: both backends deploy via Dockerfiles that build from their own
subdirectory, where a workspace:* specifier cannot resolve.
The cost is that a rules change is not picked up by its consumers until it ships:
pnpm test && pnpm build # vectors must pass before anything goes out
npm version patch # or minor, if the API changed
npm publish # publishConfig already sets public accessthen bump the range in indexer, api and the client.
To iterate locally without publishing, point a consumer at the checkout temporarily
(pnpm add link:../packages/engine) and put it back before committing.
Layout
src/
protocol.ts domains, action codec, root / fold / actionEntropy
transcript.ts ActionCommitted decoding, re-folding, chain ordering
actions.ts GameAction <-> encoded felt
replay.ts applyAction(state, action, entropy)
packing.ts unpackAdventurer / unpackBag
rules/ combat, exploration, loot, market, beast, token idsapplyAction is the client's useGameCore body verbatim; only the wrapper differs. It never
themes a beast — the indexer stores raw ids and every reader restyles on read.
