@dreamboard-games/cli 0.1.30-alpha.1 → 0.1.30-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -22
- package/dist/agent-verifier/agent-workspace-verifier.mjs +30 -30
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-JH22JNYD.mjs → chunk-3UKQVWLV.mjs} +82 -19
- package/dist/agent-verifier/chunk-3UKQVWLV.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-4WD3YU2E.mjs → chunk-776W3UGV.mjs} +4 -3
- package/dist/agent-verifier/chunk-776W3UGV.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-CJEEA6NJ.mjs → chunk-7WWGFAAU.mjs} +9 -10
- package/dist/agent-verifier/chunk-7WWGFAAU.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-2SZHMP6F.mjs → chunk-A64ZZUZV.mjs} +6 -9
- package/dist/agent-verifier/chunk-A64ZZUZV.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-6A5HRJMQ.mjs → chunk-E7SSWJXJ.mjs} +62 -99
- package/dist/agent-verifier/chunk-E7SSWJXJ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-2GBBP27W.mjs → chunk-F2DIOJJZ.mjs} +1 -0
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-CFU5EWIC.mjs → chunk-G42BGGG2.mjs} +7 -6
- package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-SHUMAVAP.mjs → chunk-H76MT5UR.mjs} +7 -9
- package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-7E65UQLY.mjs → chunk-HGMUAL33.mjs} +3 -2
- package/dist/agent-verifier/chunk-HGMUAL33.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-LM3OZLZG.mjs → chunk-IAYRNVUC.mjs} +1 -0
- package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-VYJTHSYR.mjs → chunk-JGT4P4UD.mjs} +2 -1
- package/dist/agent-verifier/chunk-JGT4P4UD.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-CEDUHGNH.mjs → chunk-LUZ7KE6H.mjs} +8 -3
- package/dist/agent-verifier/chunk-LUZ7KE6H.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-2E5P5NWG.mjs → chunk-NAK77WXW.mjs} +58 -126
- package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-SYPLYRGB.mjs → chunk-O4YCPU7C.mjs} +116 -15
- package/dist/agent-verifier/chunk-O4YCPU7C.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-BVVNBJM4.mjs → chunk-S34FRJHS.mjs} +2 -1
- package/dist/agent-verifier/chunk-S34FRJHS.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-HJFQDSTU.mjs → chunk-SH5JKYOB.mjs} +6 -5
- package/dist/agent-verifier/chunk-SH5JKYOB.mjs.map +1 -0
- package/dist/agent-verifier/chunk-SKI2ESE5.mjs +44 -0
- package/dist/agent-verifier/{chunk-MINCYHXN.mjs → chunk-TAEQKBJB.mjs} +1 -0
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-CEQ2VJWN.mjs → chunk-UIOLGH4A.mjs} +2 -1
- package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +1 -0
- package/dist/agent-verifier/chunk-UIZNWRM6.mjs +2432 -0
- package/dist/agent-verifier/chunk-UIZNWRM6.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-2QMNAVV4.mjs → chunk-VS573ERH.mjs} +2 -1
- package/dist/agent-verifier/chunk-VS573ERH.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-EOQIV6PS.mjs → chunk-W3N3QJ4V.mjs} +75 -100
- package/dist/agent-verifier/chunk-W3N3QJ4V.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-EIQWDQWJ.mjs → chunk-XGWCY624.mjs} +11 -12
- package/dist/agent-verifier/chunk-XGWCY624.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-7653FPGJ.mjs → chunk-XQXDOBYB.mjs} +3 -2
- package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-MRCUP5SW.mjs → chunk-YE7UAO3T.mjs} +1 -0
- package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-RBDDIIPM.mjs → chunk-ZEELHSY3.mjs} +1 -0
- package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
- package/dist/agent-verifier/{compile-5QSPIOUT.mjs → compile-TEQVA46V.mjs} +24 -25
- package/dist/agent-verifier/compile-TEQVA46V.mjs.map +1 -0
- package/dist/agent-verifier/{global-config-WX3ZZIVU.mjs → global-config-Y2NTSK4R.mjs} +6 -5
- package/dist/{keychain-backend-JHTXAKWC.js → agent-verifier/keychain-backend-SPQWGKZN.mjs} +2 -2
- package/dist/agent-verifier/keychain-backend-SPQWGKZN.mjs.map +1 -0
- package/dist/agent-verifier/{local-files-MTPLP62S.mjs → local-files-JFOQQZDL.mjs} +10 -11
- package/dist/agent-verifier/local-files-JFOQQZDL.mjs.map +1 -0
- package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs +10 -0
- package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs.map +1 -0
- package/dist/agent-verifier/{materialize-workspace-FKALAE2T.mjs → materialize-workspace-ZAVGQQSF.mjs} +17 -18
- package/dist/agent-verifier/materialize-workspace-ZAVGQQSF.mjs.map +1 -0
- package/dist/agent-verifier/{project-state-7GR6BQTQ.mjs → project-state-K576C2TE.mjs} +3 -2
- package/dist/agent-verifier/project-state-K576C2TE.mjs.map +1 -0
- package/dist/{prompt-GMZABCJC.js → agent-verifier/prompt-MJRJMOGQ.mjs} +2 -2
- package/dist/agent-verifier/prompt-MJRJMOGQ.mjs.map +1 -0
- package/dist/agent-verifier/{reducer-bundle-preflight-C73LEXI2.mjs → reducer-bundle-preflight-LXNJUBKL.mjs} +6 -9
- package/dist/agent-verifier/reducer-bundle-preflight-LXNJUBKL.mjs.map +1 -0
- package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs +11 -0
- package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs.map +1 -0
- package/dist/agent-verifier/{reducer-native-test-harness-GMWBUISX.mjs → reducer-native-test-harness-CHX5MBL5.mjs} +14 -17
- package/dist/agent-verifier/reducer-native-test-harness-CHX5MBL5.mjs.map +1 -0
- package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs +27 -0
- package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs.map +1 -0
- package/dist/agent-verifier/{sync-3DUQH32H.mjs → sync-THAI546U.mjs} +31 -37
- package/dist/agent-verifier/sync-THAI546U.mjs.map +1 -0
- package/dist/agent-verifier/{test-P4U5INTD.mjs → test-AFAQFKOB.mjs} +28 -31
- package/dist/agent-verifier/test-AFAQFKOB.mjs.map +1 -0
- package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs +10 -0
- package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs.map +1 -0
- package/dist/agent-verifier/{workspace-dependencies-HZ6VVS4G.mjs → workspace-dependencies-NOOQBK6I.mjs} +5 -4
- package/dist/agent-verifier/workspace-dependencies-NOOQBK6I.mjs.map +1 -0
- package/dist/{chunk-C6UAT6EH.js → chunk-N7XPNNUI.js} +9 -12
- package/dist/chunk-N7XPNNUI.js.map +1 -0
- package/dist/chunk-SEGVTWSK.js +44 -0
- package/dist/chunk-SEGVTWSK.js.map +1 -0
- package/dist/{chunk-RS7UXJZV.js → chunk-TAQKH67O.js} +21300 -35881
- package/dist/chunk-TAQKH67O.js.map +1 -0
- package/dist/{global-config-AGFBDFYD.js → global-config-S4ZIPECE.js} +3 -3
- package/dist/global-config-S4ZIPECE.js.map +1 -0
- package/dist/index.js +415 -37
- package/dist/index.js.map +1 -1
- package/dist/internal.js +3 -4
- package/dist/{agent-verifier/keychain-backend-TNOPQV3Z.mjs → keychain-backend-HDF4TZDL.js} +2 -1
- package/dist/{agent-verifier/prompt-3BAINGAQ.mjs → prompt-NDV3AE5L.js} +2 -1
- package/package.json +8 -7
- package/skills/dreamboard/references/building-your-first-game.md +510 -0
- package/skills/dreamboard/references/cli.md +104 -0
- package/skills/dreamboard/references/game-interface.md +548 -0
- package/skills/dreamboard/references/manifest-authoring.md +597 -0
- package/skills/dreamboard/references/quickstart.md +66 -0
- package/skills/dreamboard/references/reducer.md +864 -0
- package/skills/dreamboard/references/rule-authoring.md +147 -0
- package/skills/dreamboard/references/testing.md +249 -0
- package/skills/dreamboard/scripts/events-extract.mjs +218 -0
- package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
- package/dist/agent-verifier/chunk-6UUJEYDV.mjs +0 -213
- package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
- package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
- package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
- package/dist/agent-verifier/chunk-XYDL7GY6.mjs +0 -10
- package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
- package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
- package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
- package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
- package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
- package/dist/chunk-2H7UOFLK.js +0 -11
- package/dist/chunk-7FOO4AJI.js +0 -50
- package/dist/chunk-7FOO4AJI.js.map +0 -1
- package/dist/chunk-C6UAT6EH.js.map +0 -1
- package/dist/chunk-RS7UXJZV.js.map +0 -1
- package/dist/internal.d.ts +0 -311
- package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
- package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
- package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
- package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
- package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
- package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
- package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
- package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
- package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
- package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
- package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
- package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
- package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
- package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
- package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
- package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
- package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
- package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
- package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
- package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
- package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
- package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
- package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
- package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
- package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
- package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
- package/dist/testing-KLSV6CPJ.js +0 -674
- package/dist/testing-KLSV6CPJ.js.map +0 -1
- /package/dist/{chunk-2H7UOFLK.js.map → agent-verifier/chunk-SKI2ESE5.mjs.map} +0 -0
- /package/dist/{global-config-AGFBDFYD.js.map → agent-verifier/global-config-Y2NTSK4R.mjs.map} +0 -0
- /package/dist/{keychain-backend-JHTXAKWC.js.map → keychain-backend-HDF4TZDL.js.map} +0 -0
- /package/dist/{prompt-GMZABCJC.js.map → prompt-NDV3AE5L.js.map} +0 -0
package/dist/internal.js
CHANGED
|
@@ -16,16 +16,15 @@ import {
|
|
|
16
16
|
shortHash,
|
|
17
17
|
updateProjectState,
|
|
18
18
|
writeSnapshot
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TAQKH67O.js";
|
|
20
20
|
import {
|
|
21
21
|
ENVIRONMENT_CONFIGS,
|
|
22
22
|
getStoredSession,
|
|
23
23
|
loadGlobalConfig,
|
|
24
24
|
readJsonFile,
|
|
25
25
|
writeJsonFile
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-2H7UOFLK.js";
|
|
26
|
+
} from "./chunk-N7XPNNUI.js";
|
|
27
|
+
import "./chunk-SEGVTWSK.js";
|
|
29
28
|
export {
|
|
30
29
|
CONFIG_FLAG_ARGS,
|
|
31
30
|
ENVIRONMENT_CONFIGS,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-SEGVTWSK.js";
|
|
3
3
|
|
|
4
4
|
// src/config/keychain-backend.ts
|
|
5
5
|
var KEYCHAIN_SERVICE = "dreamboard-cli";
|
|
@@ -132,3 +132,4 @@ export {
|
|
|
132
132
|
_setKeyringModuleForTests,
|
|
133
133
|
tryKeychainBackend
|
|
134
134
|
};
|
|
135
|
+
//# sourceMappingURL=keychain-backend-HDF4TZDL.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-SEGVTWSK.js";
|
|
3
3
|
|
|
4
4
|
// ../../node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/chunks/prompt.mjs
|
|
5
5
|
import "util";
|
|
@@ -753,3 +753,4 @@ export {
|
|
|
753
753
|
kCancel,
|
|
754
754
|
prompt
|
|
755
755
|
};
|
|
756
|
+
//# sourceMappingURL=prompt-NDV3AE5L.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreamboard-games/cli",
|
|
3
|
-
"version": "0.1.30-alpha.
|
|
3
|
+
"version": "0.1.30-alpha.3",
|
|
4
4
|
"description": "Design board games with AI and turn ideas into playable digital prototypes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"types": "./dist/internal.d.ts",
|
|
13
13
|
"default": "./dist/internal.js"
|
|
14
14
|
},
|
|
15
|
-
"./
|
|
16
|
-
"./
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
"./agent-workspace-verifier": "./dist/agent-verifier/agent-workspace-verifier.mjs"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@dreamboard-games/api-client": "0.3.0-alpha.3",
|
|
38
|
+
"@dreamboard-games/gameplay-authority-client": "0.1.0-alpha.1",
|
|
39
|
+
"@dreamboard-games/sdk": "0.4.0-alpha.0",
|
|
40
|
+
"@dreamboard-games/ui-host-runtime": "0.1.0-alpha.2",
|
|
37
41
|
"@tailwindcss/postcss": "^4.3.0",
|
|
38
42
|
"@vitejs/plugin-react": "^4.7.0",
|
|
39
43
|
"citty": "^0.2.2",
|
|
@@ -52,10 +56,7 @@
|
|
|
52
56
|
"vaul": "^1.1.2",
|
|
53
57
|
"vite": "^5.4.21",
|
|
54
58
|
"zod": "4.4.3",
|
|
55
|
-
"zustand": "^5.0.14"
|
|
56
|
-
"@dreamboard-games/api-client": "0.3.0-alpha.3",
|
|
57
|
-
"@dreamboard-games/sdk": "0.4.0-alpha.0",
|
|
58
|
-
"playwright": "^1.60.0"
|
|
59
|
+
"zustand": "^5.0.14"
|
|
59
60
|
},
|
|
60
61
|
"optionalDependencies": {
|
|
61
62
|
"@napi-rs/keyring": "^1.3.0"
|
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
<!-- Generated by apps/dreamboard-cli/scripts/sync-skill-docs.ts. -->
|
|
2
|
+
<!-- Source: docs/tutorials/building-your-first-game.mdx -->
|
|
3
|
+
|
|
4
|
+
# Building your first game
|
|
5
|
+
|
|
6
|
+
Tutorial: build a complete Dreamboard game from rule to tests.
|
|
7
|
+
|
|
8
|
+
This tutorial walks through a deliberately small Dreamboard game so you can see
|
|
9
|
+
the whole authoring loop once: rules, manifest, reducer, UI, and tests.
|
|
10
|
+
|
|
11
|
+
The example game is `Race to Ten`.
|
|
12
|
+
|
|
13
|
+
- 2 players
|
|
14
|
+
- one shared d6
|
|
15
|
+
- each turn, the active player rolls the die and adds the result to their score
|
|
16
|
+
- the first player to reach 10 points wins
|
|
17
|
+
|
|
18
|
+
For tutorial stability, the main walkthrough uses a deterministic die sequence
|
|
19
|
+
`1, 2, 3, 4, 5, 6, ...` instead of true randomness. The important part is how a
|
|
20
|
+
die component, a `rollDie` action, reducer state, UI, and tests fit together.
|
|
21
|
+
This is also consistent with the reducer model: reducers stay pure, and
|
|
22
|
+
runtime-owned effects handle live randomness.
|
|
23
|
+
|
|
24
|
+
## What you will build
|
|
25
|
+
|
|
26
|
+
By the end of the tutorial you will have:
|
|
27
|
+
|
|
28
|
+
- a `rule.md` that explains the game
|
|
29
|
+
- a `manifest.json` with a shared die
|
|
30
|
+
- reducer code in `app/`
|
|
31
|
+
- a playable `ui/App.tsx`
|
|
32
|
+
- a base and scenario under `test/`
|
|
33
|
+
|
|
34
|
+
## Prerequisites
|
|
35
|
+
|
|
36
|
+
- Dreamboard CLI installed: `npm install -g dreamboard`
|
|
37
|
+
- authenticated with `dreamboard login`
|
|
38
|
+
|
|
39
|
+
## 1. Create the workspace
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
dreamboard new race-to-ten --description "A tiny scoring game with one shared die"
|
|
43
|
+
cd race-to-ten
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The scaffold gives you the files you will edit next:
|
|
47
|
+
|
|
48
|
+
- `rule.md`
|
|
49
|
+
- `manifest.json`
|
|
50
|
+
- `app/game-contract.ts`
|
|
51
|
+
- `app/game.ts`
|
|
52
|
+
- `app/phases/*`
|
|
53
|
+
- `ui/App.tsx`
|
|
54
|
+
- `test/bases/*`
|
|
55
|
+
- `test/scenarios/*`
|
|
56
|
+
|
|
57
|
+
## 2. Write `rule.md`
|
|
58
|
+
|
|
59
|
+
```md
|
|
60
|
+
# Race to Ten
|
|
61
|
+
|
|
62
|
+
## Overview
|
|
63
|
+
|
|
64
|
+
- Players: 2
|
|
65
|
+
- Objective: be the first player to reach 10 points
|
|
66
|
+
- Duration: 3 to 5 minutes
|
|
67
|
+
|
|
68
|
+
## Components
|
|
69
|
+
|
|
70
|
+
- 1 shared six-sided die
|
|
71
|
+
- visible score totals for each player
|
|
72
|
+
- no hidden information
|
|
73
|
+
|
|
74
|
+
## Setup
|
|
75
|
+
|
|
76
|
+
1. Seat two players.
|
|
77
|
+
2. Set both scores to 0.
|
|
78
|
+
3. Clear the die value.
|
|
79
|
+
4. Player 1 takes the first turn.
|
|
80
|
+
|
|
81
|
+
## Gameplay
|
|
82
|
+
|
|
83
|
+
### Phase 1: takeTurn
|
|
84
|
+
|
|
85
|
+
- Acting player: the current player only
|
|
86
|
+
- Allowed actions: `rollDie`
|
|
87
|
+
- Validation: only the active player may act, and no actions are legal after a winner exists
|
|
88
|
+
- Completion:
|
|
89
|
+
- `rollDie` sets the shared die to a new value
|
|
90
|
+
- the acting player adds that value to their score
|
|
91
|
+
- if the acting player reaches 10 points, the game ends immediately
|
|
92
|
+
- otherwise the turn passes to the other player
|
|
93
|
+
|
|
94
|
+
## Scoring and progression
|
|
95
|
+
|
|
96
|
+
- `rollDie` increases the acting player's score by the rolled value
|
|
97
|
+
|
|
98
|
+
## Winning conditions
|
|
99
|
+
|
|
100
|
+
- End trigger: a player reaches 10 points
|
|
101
|
+
- Winner determination: the player who reached 10 points wins
|
|
102
|
+
- Tie-breaker: not applicable because turns resolve one at a time
|
|
103
|
+
|
|
104
|
+
## Special rules and edge cases
|
|
105
|
+
|
|
106
|
+
- Actions after game end are illegal
|
|
107
|
+
- Out-of-turn actions are illegal
|
|
108
|
+
- For this tutorial implementation, the die value cycles deterministically from 1 to 6 so the example stays reproducible
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For a fuller reference, see [Rule authoring](./rule-authoring.md).
|
|
112
|
+
|
|
113
|
+
## 3. Write `manifest.json`
|
|
114
|
+
|
|
115
|
+
This game needs player-count metadata and one shared die.
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"players": {
|
|
120
|
+
"minPlayers": 2,
|
|
121
|
+
"maxPlayers": 2,
|
|
122
|
+
"optimalPlayers": 2
|
|
123
|
+
},
|
|
124
|
+
"dieTypes": [
|
|
125
|
+
{
|
|
126
|
+
"id": "standard-d6",
|
|
127
|
+
"name": "Standard d6",
|
|
128
|
+
"sides": 6
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"dieSeeds": [
|
|
132
|
+
{
|
|
133
|
+
"id": "turn-die",
|
|
134
|
+
"name": "Turn die",
|
|
135
|
+
"typeId": "standard-d6"
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Run:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
dreamboard sync
|
|
145
|
+
dreamboard compile
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`dreamboard sync` refreshes generated contracts from authored files.
|
|
149
|
+
`dreamboard compile` builds the current authored head.
|
|
150
|
+
|
|
151
|
+
## 4. Define the reducer contract
|
|
152
|
+
|
|
153
|
+
Open `app/game-contract.ts` and replace the empty schemas with the state the
|
|
154
|
+
rules require.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { z } from "zod";
|
|
158
|
+
import * as manifestContract from "../shared/manifest-contract";
|
|
159
|
+
import { defineGameContract, type GameStateOf } from "@dreamboard/app-sdk/reducer";
|
|
160
|
+
|
|
161
|
+
const playerId = manifestContract.ids.playerId;
|
|
162
|
+
|
|
163
|
+
const publicStateSchema = z.object({
|
|
164
|
+
currentPlayerId: playerId,
|
|
165
|
+
winnerPlayerId: playerId.nullable(),
|
|
166
|
+
lastRoll: z.number().int().min(1).max(6).nullable(),
|
|
167
|
+
scores: z.record(playerId, z.number().int().nonnegative()),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const privateStateSchema = z.object({});
|
|
171
|
+
|
|
172
|
+
const hiddenStateSchema = z.object({
|
|
173
|
+
rollCount: z.number().int().nonnegative(),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
export const gameContract = defineGameContract({
|
|
177
|
+
manifest: manifestContract.manifestContract,
|
|
178
|
+
state: {
|
|
179
|
+
public: publicStateSchema,
|
|
180
|
+
private: privateStateSchema,
|
|
181
|
+
hidden: hiddenStateSchema,
|
|
182
|
+
initial: {
|
|
183
|
+
public: ({ playerIds }) => ({
|
|
184
|
+
currentPlayerId: playerIds[0],
|
|
185
|
+
winnerPlayerId: null,
|
|
186
|
+
lastRoll: null,
|
|
187
|
+
scores: Object.fromEntries(playerIds.map((playerId) => [playerId, 0])),
|
|
188
|
+
}),
|
|
189
|
+
private: () => ({}),
|
|
190
|
+
hidden: () => ({
|
|
191
|
+
rollCount: 0,
|
|
192
|
+
}),
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
export type GameContract = typeof gameContract;
|
|
198
|
+
export type GameState = GameStateOf<GameContract>;
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The public state holds player-visible game progress. The hidden state tracks the
|
|
202
|
+
deterministic roll index so the test can stay reproducible.
|
|
203
|
+
|
|
204
|
+
## 5. Add a player view
|
|
205
|
+
|
|
206
|
+
Create `app/player-view.ts`:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { z } from "zod";
|
|
210
|
+
import { defineView } from "@dreamboard/app-sdk/reducer";
|
|
211
|
+
import type { GameContract } from "./game-contract";
|
|
212
|
+
|
|
213
|
+
export const playerView = defineView<GameContract>()({
|
|
214
|
+
schema: z.object({
|
|
215
|
+
currentPlayerId: z.string(),
|
|
216
|
+
winnerPlayerId: z.string().nullable(),
|
|
217
|
+
lastRoll: z.number().nullable(),
|
|
218
|
+
scores: z.record(z.string(), z.number()),
|
|
219
|
+
isMyTurn: z.boolean(),
|
|
220
|
+
targetScore: z.number(),
|
|
221
|
+
turnDieId: z.string(),
|
|
222
|
+
}),
|
|
223
|
+
project({ state, playerId }) {
|
|
224
|
+
return {
|
|
225
|
+
currentPlayerId: state.publicState.currentPlayerId,
|
|
226
|
+
winnerPlayerId: state.publicState.winnerPlayerId,
|
|
227
|
+
lastRoll: state.publicState.lastRoll,
|
|
228
|
+
scores: state.publicState.scores,
|
|
229
|
+
isMyTurn: state.publicState.currentPlayerId === playerId,
|
|
230
|
+
targetScore: 10,
|
|
231
|
+
turnDieId: "turn-die",
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The UI will read this view instead of reconstructing game logic in React.
|
|
238
|
+
|
|
239
|
+
## 6. Implement the phase and action
|
|
240
|
+
|
|
241
|
+
Create `app/phases/take-turn.ts`:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
import { z } from "zod";
|
|
245
|
+
import { defineAction, definePhase, setActivePlayers } from "@dreamboard/app-sdk/reducer";
|
|
246
|
+
import type { GameContract } from "../game-contract";
|
|
247
|
+
|
|
248
|
+
const TARGET_SCORE = 10;
|
|
249
|
+
const TURN_DIE_ID = "turn-die" as const;
|
|
250
|
+
|
|
251
|
+
const rollDie = defineAction<GameContract>()({
|
|
252
|
+
params: z.object({}),
|
|
253
|
+
validate({ state, input }) {
|
|
254
|
+
if (state.publicState.winnerPlayerId) {
|
|
255
|
+
return {
|
|
256
|
+
errorCode: "GAME_ALREADY_ENDED",
|
|
257
|
+
message: "The game has already ended.",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (input.playerId !== state.publicState.currentPlayerId) {
|
|
262
|
+
return {
|
|
263
|
+
errorCode: "NOT_YOUR_TURN",
|
|
264
|
+
message: "It is not your turn.",
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return null;
|
|
269
|
+
},
|
|
270
|
+
reduce({ state, input, accept }) {
|
|
271
|
+
const nextRoll = (state.hiddenState.rollCount % 6) + 1;
|
|
272
|
+
const nextScore = state.publicState.scores[input.playerId] + nextRoll;
|
|
273
|
+
const nextScores = {
|
|
274
|
+
...state.publicState.scores,
|
|
275
|
+
[input.playerId]: nextScore,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const nextPlayerId =
|
|
279
|
+
input.playerId === "player-1" ? "player-2" : "player-1";
|
|
280
|
+
|
|
281
|
+
const nextTable = {
|
|
282
|
+
...state.table,
|
|
283
|
+
dice: {
|
|
284
|
+
...state.table.dice,
|
|
285
|
+
[TURN_DIE_ID]: {
|
|
286
|
+
...state.table.dice[TURN_DIE_ID],
|
|
287
|
+
value: nextRoll,
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
return accept({
|
|
293
|
+
...state,
|
|
294
|
+
table: nextTable,
|
|
295
|
+
publicState: {
|
|
296
|
+
...state.publicState,
|
|
297
|
+
currentPlayerId: nextScore >= TARGET_SCORE ? input.playerId : nextPlayerId,
|
|
298
|
+
winnerPlayerId: nextScore >= TARGET_SCORE ? input.playerId : null,
|
|
299
|
+
lastRoll: nextRoll,
|
|
300
|
+
scores: nextScores,
|
|
301
|
+
},
|
|
302
|
+
hiddenState: {
|
|
303
|
+
...state.hiddenState,
|
|
304
|
+
rollCount: state.hiddenState.rollCount + 1,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
export const takeTurn = definePhase<GameContract>()({
|
|
311
|
+
kind: "player",
|
|
312
|
+
state: z.object({}),
|
|
313
|
+
initialState: () => ({}),
|
|
314
|
+
enter({ state, accept }) {
|
|
315
|
+
if (state.publicState.winnerPlayerId) {
|
|
316
|
+
return accept(state);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return accept(
|
|
320
|
+
setActivePlayers(state, [state.publicState.currentPlayerId]),
|
|
321
|
+
);
|
|
322
|
+
},
|
|
323
|
+
actions: {
|
|
324
|
+
rollDie,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Then update `app/phases/index.ts`:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import { takeTurn } from "./take-turn";
|
|
333
|
+
|
|
334
|
+
export const phases = {
|
|
335
|
+
takeTurn,
|
|
336
|
+
};
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## 7. Register the game
|
|
340
|
+
|
|
341
|
+
Update `app/game.ts`:
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
import { defineGame } from "@dreamboard/app-sdk/reducer";
|
|
345
|
+
import { gameContract } from "./game-contract";
|
|
346
|
+
import { phases } from "./phases";
|
|
347
|
+
import { playerView } from "./player-view";
|
|
348
|
+
|
|
349
|
+
export default defineGame({
|
|
350
|
+
contract: gameContract,
|
|
351
|
+
initialPhase: "takeTurn",
|
|
352
|
+
phases,
|
|
353
|
+
views: {
|
|
354
|
+
player: playerView,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
At this point the rules exist, but the UI is still the scaffold placeholder.
|
|
360
|
+
|
|
361
|
+
## 8. Build the UI
|
|
362
|
+
|
|
363
|
+
Update `ui/App.tsx`:
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
import { DiceRoller, useActions, useDice, useGameView } from "@dreamboard/ui-sdk";
|
|
367
|
+
|
|
368
|
+
export default function App() {
|
|
369
|
+
const view = useGameView();
|
|
370
|
+
const phase = useActions();
|
|
371
|
+
const dice = useDice(["turn-die"]);
|
|
372
|
+
|
|
373
|
+
const rollDie = async () => {
|
|
374
|
+
if (phase.phase !== "takeTurn") {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
await phase.dispatch(phase.commands.rollDie());
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
return (
|
|
382
|
+
<main style={{ padding: 24, fontFamily: "system-ui, sans-serif" }}>
|
|
383
|
+
<h1>Race to Ten</h1>
|
|
384
|
+
<p>First to {view.targetScore} points wins.</p>
|
|
385
|
+
|
|
386
|
+
<DiceRoller
|
|
387
|
+
values={dice.values}
|
|
388
|
+
diceCount={1}
|
|
389
|
+
render={({ values }) => (
|
|
390
|
+
<div style={{ fontSize: 32, marginBottom: 16 }}>
|
|
391
|
+
Die: {values?.[0] ?? "?"}
|
|
392
|
+
</div>
|
|
393
|
+
)}
|
|
394
|
+
/>
|
|
395
|
+
|
|
396
|
+
<ul>
|
|
397
|
+
{Object.entries(view.scores).map(([playerId, score]) => (
|
|
398
|
+
<li key={playerId}>
|
|
399
|
+
{playerId}: {score}
|
|
400
|
+
{view.currentPlayerId === playerId ? " <- current player" : ""}
|
|
401
|
+
</li>
|
|
402
|
+
))}
|
|
403
|
+
</ul>
|
|
404
|
+
|
|
405
|
+
<p>Last roll: {view.lastRoll ?? "not rolled yet"}</p>
|
|
406
|
+
|
|
407
|
+
{view.winnerPlayerId ? (
|
|
408
|
+
<p>Winner: {view.winnerPlayerId}</p>
|
|
409
|
+
) : (
|
|
410
|
+
<button onClick={() => void rollDie()} disabled={!view.isMyTurn}>
|
|
411
|
+
Roll die
|
|
412
|
+
</button>
|
|
413
|
+
)}
|
|
414
|
+
</main>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The important part is that:
|
|
420
|
+
|
|
421
|
+
- the die exists in authored game structure
|
|
422
|
+
- the reducer updates the die value and the score together
|
|
423
|
+
- the UI reads projected view data and the die state from the runtime
|
|
424
|
+
- actions still go through `useActions()`
|
|
425
|
+
|
|
426
|
+
## 9. Add a base and a scenario
|
|
427
|
+
|
|
428
|
+
Replace `test/bases/initial-turn.base.ts`:
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
import { defineBase } from "../testing-types";
|
|
432
|
+
|
|
433
|
+
export default defineBase({
|
|
434
|
+
id: "initial-turn",
|
|
435
|
+
seed: 1337,
|
|
436
|
+
players: 2,
|
|
437
|
+
setup: async ({ game }) => {
|
|
438
|
+
await game.start();
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Create `test/scenarios/player-two-wins.scenario.ts`:
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
import { defineScenario } from "../testing-types";
|
|
447
|
+
|
|
448
|
+
export default defineScenario({
|
|
449
|
+
id: "player-two-wins",
|
|
450
|
+
description: "The deterministic roll sequence lets player 2 reach ten first",
|
|
451
|
+
from: "initial-turn",
|
|
452
|
+
when: async ({ game }) => {
|
|
453
|
+
await game.action("player-1", "rollDie", {});
|
|
454
|
+
await game.action("player-2", "rollDie", {});
|
|
455
|
+
await game.action("player-1", "rollDie", {});
|
|
456
|
+
await game.action("player-2", "rollDie", {});
|
|
457
|
+
await game.action("player-1", "rollDie", {});
|
|
458
|
+
await game.action("player-2", "rollDie", {});
|
|
459
|
+
},
|
|
460
|
+
then: ({ publicState, view, expect, history, phase }) => {
|
|
461
|
+
const state = publicState();
|
|
462
|
+
|
|
463
|
+
expect(phase()).toBe("takeTurn");
|
|
464
|
+
expect(state.lastRoll).toBe(6);
|
|
465
|
+
expect(state.scores["player-1"]).toBe(9);
|
|
466
|
+
expect(state.scores["player-2"]).toBe(12);
|
|
467
|
+
expect(state.winnerPlayerId).toBe("player-2");
|
|
468
|
+
expect(view("player-2").winnerPlayerId).toBe("player-2");
|
|
469
|
+
expect(history().accepted().length).toBe(6);
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Generate artifacts and run the test suite:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
dreamboard test generate
|
|
478
|
+
dreamboard test run
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## 10. Run the game locally
|
|
482
|
+
|
|
483
|
+
Use the local runtime to verify the same flow manually:
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
dreamboard run
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
If you edit `rule.md` or `manifest.json`, run `dreamboard sync` again before
|
|
490
|
+
continuing.
|
|
491
|
+
|
|
492
|
+
## Where to go next
|
|
493
|
+
|
|
494
|
+
This tutorial uses the smallest die-based loop that still touches authored
|
|
495
|
+
components, reducer state, UI, and tests. The next layer of depth is:
|
|
496
|
+
|
|
497
|
+
- add setup profiles or setup options
|
|
498
|
+
- add richer reducer state and more than one phase
|
|
499
|
+
- replace the plain button UI with grouped action panels and richer views
|
|
500
|
+
- add rejection-path tests such as out-of-turn actions
|
|
501
|
+
|
|
502
|
+
If you need live randomness in authored reducer logic, do not call
|
|
503
|
+
`Math.random()` inside reducers. Use runtime-owned effects instead:
|
|
504
|
+
|
|
505
|
+
- `effects.rollDie(...)` when the runtime only needs to update an authored die
|
|
506
|
+
- `effects.randomInt(...)` when reducer logic needs the sampled value back
|
|
507
|
+
|
|
508
|
+
The main walkthrough stays deterministic so the rule, reducer, UI, and test
|
|
509
|
+
snippets all line up exactly. See [Reducer](./reducer.md) for the
|
|
510
|
+
runtime-random versions.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<!-- Generated by apps/dreamboard-cli/scripts/sync-skill-docs.ts. -->
|
|
2
|
+
<!-- Source: docs/reference/cli.mdx -->
|
|
3
|
+
|
|
4
|
+
# CLI
|
|
5
|
+
|
|
6
|
+
Reference for Dreamboard CLI workflows and commands.
|
|
7
|
+
|
|
8
|
+
Dreamboard CLI manages the authored workspace loop: authenticate, create or
|
|
9
|
+
clone a game, sync authored changes, compile, run, and test.
|
|
10
|
+
|
|
11
|
+
## Responsibility
|
|
12
|
+
|
|
13
|
+
Use the CLI for:
|
|
14
|
+
|
|
15
|
+
- authenticating the current machine
|
|
16
|
+
- creating or cloning game workspaces
|
|
17
|
+
- syncing authored files to the remote authoring head
|
|
18
|
+
- compiling the current authored head
|
|
19
|
+
- inspecting local versus remote state
|
|
20
|
+
- pulling remote authored changes into the workspace
|
|
21
|
+
- starting or observing playable sessions
|
|
22
|
+
- generating and running reducer-native tests
|
|
23
|
+
|
|
24
|
+
## Install targets
|
|
25
|
+
|
|
26
|
+
Use the published package for normal game work:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g dreamboard
|
|
30
|
+
dreamboard login
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Command flow
|
|
34
|
+
|
|
35
|
+
Use this sequence for the normal authored loop:
|
|
36
|
+
|
|
37
|
+
1. `dreamboard login`
|
|
38
|
+
2. `dreamboard new ...` or `dreamboard clone ...`
|
|
39
|
+
3. edit `rule.md`, `manifest.json`, and authored source files
|
|
40
|
+
4. `dreamboard sync`
|
|
41
|
+
5. `dreamboard compile`
|
|
42
|
+
6. `dreamboard run` or `dreamboard start`
|
|
43
|
+
7. `dreamboard test generate`
|
|
44
|
+
8. `dreamboard test run`
|
|
45
|
+
|
|
46
|
+
When the remote authored head moves first, run `dreamboard pull` before the
|
|
47
|
+
next sync or compile. Use `dreamboard status` to decide whether the workspace is
|
|
48
|
+
ahead, behind, or diverged.
|
|
49
|
+
|
|
50
|
+
## Workspace commands
|
|
51
|
+
|
|
52
|
+
| Command | Use it for |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `dreamboard login` | Open browser login and store a refreshable session |
|
|
55
|
+
| `dreamboard new <slug> --description "..."` | Create a new game and scaffold a local workspace |
|
|
56
|
+
| `dreamboard clone <slug>` | Clone an existing game into a new local workspace |
|
|
57
|
+
| `dreamboard sync` | Upload authored changes and refresh scaffolded files |
|
|
58
|
+
| `dreamboard compile` | Compile the current remote authored head |
|
|
59
|
+
| `dreamboard status` | Compare local authored and compile state with remote |
|
|
60
|
+
| `dreamboard pull` | Reconcile remote authored changes into the workspace |
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
dreamboard new race-to-ten --description "A tiny scoring game"
|
|
64
|
+
dreamboard sync
|
|
65
|
+
dreamboard compile
|
|
66
|
+
dreamboard status --json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Session commands
|
|
70
|
+
|
|
71
|
+
Use session commands after you have a successful compile for the current
|
|
72
|
+
authored head.
|
|
73
|
+
|
|
74
|
+
| Command | Use it for |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `dreamboard run` | Start or resume a playable session and observe it from the CLI |
|
|
77
|
+
| `dreamboard start` | Start a fresh session and open it in the browser |
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
dreamboard run
|
|
81
|
+
dreamboard run --players 4 --seed 1337
|
|
82
|
+
dreamboard run --scenario path/to/scenario.json
|
|
83
|
+
dreamboard start --players 2
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`dreamboard run` reuses the previous active session by default. Use
|
|
87
|
+
`--new-session` when you want a fresh session instead.
|
|
88
|
+
|
|
89
|
+
## Test commands
|
|
90
|
+
|
|
91
|
+
Use the scaffolded reducer-native test workspace for repeatable game assertions.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
dreamboard test generate
|
|
95
|
+
dreamboard test run
|
|
96
|
+
dreamboard test run --scenario test/scenarios/player-two-wins.scenario.ts
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Start local server
|
|
100
|
+
```bash
|
|
101
|
+
dreamboard dev
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
See [Testing](./testing.md) for the scenario and base-file format.
|