dreamboard 0.1.21 → 0.1.23
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/dist/{chunk-EYYWGWTO.js → chunk-2RCUHMGL.js} +343 -1911
- package/dist/chunk-2RCUHMGL.js.map +1 -0
- package/dist/{chunk-MOVHYB6E.js → chunk-GN7232BY.js} +1025 -695
- package/dist/chunk-GN7232BY.js.map +1 -0
- package/dist/{chunk-BMYC6772.js → chunk-H3O43F5P.js} +9765 -3068
- package/dist/chunk-H3O43F5P.js.map +1 -0
- package/dist/dev-host/dev-api-proxy-plugin.ts +330 -0
- package/dist/dev-host/dev-diagnostics.ts +62 -0
- package/dist/dev-host/dev-fallback-stylesheet.ts +50 -0
- package/dist/dev-host/dev-host-controller.ts +686 -0
- package/dist/dev-host/dev-host-player-query.ts +17 -0
- package/dist/dev-host/dev-host-session-transport.ts +52 -0
- package/dist/dev-host/dev-host-storage.ts +56 -0
- package/dist/dev-host/dev-log-relay-plugin.ts +469 -0
- package/dist/dev-host/dev-runtime-config.ts +14 -0
- package/dist/dev-host/dev-runtime-platform.ts +419 -0
- package/dist/dev-host/dev-virtual-modules-plugin.ts +63 -0
- package/dist/dev-host/host-main.css +182 -0
- package/dist/dev-host/host-main.tsx +754 -0
- package/dist/dev-host/index.html +56 -0
- package/dist/dev-host/plugin-main.ts +55 -0
- package/dist/dev-host/plugin.html +24 -0
- package/dist/dev-host/start-dev-server.ts +138 -0
- package/dist/dev-host/virtual-modules.d.ts +27 -0
- package/dist/dist-FEPN3BDN.js +16543 -0
- package/dist/dist-FEPN3BDN.js.map +1 -0
- package/dist/{dist-WJRJNFLI.js → dist-FRURQI7Q.js} +4 -2
- package/dist/index.js +3 -3
- package/dist/internal.js +159 -124
- package/dist/internal.js.map +1 -1
- package/dist/runtime-packages/tailwind-config/shared-styles.css +146 -0
- package/dist/runtime-packages/ui/src/components/.gitkeep +0 -0
- package/dist/runtime-packages/ui/src/components/accordion.tsx +66 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/actions.tsx +65 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/artifact.tsx +147 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/branch.tsx +215 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/canvas.tsx +22 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/chain-of-thought.tsx +228 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/code-block.tsx +179 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/confirmation.tsx +158 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/connection.tsx +28 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/context.tsx +408 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/controls.tsx +18 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/conversation.tsx +97 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/edge.tsx +140 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/image.tsx +24 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/inline-citation.tsx +287 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/loader.tsx +96 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/message.tsx +463 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/node.tsx +71 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/open-in-chat.tsx +365 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/panel.tsx +15 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/plan.tsx +142 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/prompt-input.tsx +1380 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/queue.tsx +274 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/reasoning.tsx +182 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/response.tsx +22 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/shimmer.tsx +64 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/sources.tsx +77 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/suggestion.tsx +53 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/task.tsx +87 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/tool.tsx +165 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/toolbar.tsx +16 -0
- package/dist/runtime-packages/ui/src/components/ai-elements/web-preview.tsx +263 -0
- package/dist/runtime-packages/ui/src/components/alert-dialog.tsx +157 -0
- package/dist/runtime-packages/ui/src/components/alert.tsx +66 -0
- package/dist/runtime-packages/ui/src/components/avatar.tsx +53 -0
- package/dist/runtime-packages/ui/src/components/badge.tsx +46 -0
- package/dist/runtime-packages/ui/src/components/button-group.tsx +83 -0
- package/dist/runtime-packages/ui/src/components/button.tsx +65 -0
- package/dist/runtime-packages/ui/src/components/card.tsx +92 -0
- package/dist/runtime-packages/ui/src/components/carousel.tsx +241 -0
- package/dist/runtime-packages/ui/src/components/collapsible.tsx +33 -0
- package/dist/runtime-packages/ui/src/components/command.tsx +184 -0
- package/dist/runtime-packages/ui/src/components/context-menu.tsx +252 -0
- package/dist/runtime-packages/ui/src/components/counter.tsx +11 -0
- package/dist/runtime-packages/ui/src/components/dialog.tsx +146 -0
- package/dist/runtime-packages/ui/src/components/drawer.tsx +132 -0
- package/dist/runtime-packages/ui/src/components/dropdown-menu.tsx +257 -0
- package/dist/runtime-packages/ui/src/components/form.tsx +168 -0
- package/dist/runtime-packages/ui/src/components/header.tsx +13 -0
- package/dist/runtime-packages/ui/src/components/hover-card.tsx +44 -0
- package/dist/runtime-packages/ui/src/components/input-group.tsx +178 -0
- package/dist/runtime-packages/ui/src/components/input.tsx +21 -0
- package/dist/runtime-packages/ui/src/components/item.tsx +193 -0
- package/dist/runtime-packages/ui/src/components/label.tsx +24 -0
- package/dist/runtime-packages/ui/src/components/navigation-menu.tsx +168 -0
- package/dist/runtime-packages/ui/src/components/popover.tsx +55 -0
- package/dist/runtime-packages/ui/src/components/progress.tsx +31 -0
- package/dist/runtime-packages/ui/src/components/radio-group.tsx +45 -0
- package/dist/runtime-packages/ui/src/components/resizable.tsx +56 -0
- package/dist/runtime-packages/ui/src/components/scroll-area.tsx +58 -0
- package/dist/runtime-packages/ui/src/components/select.tsx +187 -0
- package/dist/runtime-packages/ui/src/components/separator.tsx +28 -0
- package/dist/runtime-packages/ui/src/components/skeleton.tsx +13 -0
- package/dist/runtime-packages/ui/src/components/sonner.tsx +25 -0
- package/dist/runtime-packages/ui/src/components/spinner.tsx +16 -0
- package/dist/runtime-packages/ui/src/components/switch.tsx +31 -0
- package/dist/runtime-packages/ui/src/components/table.tsx +137 -0
- package/dist/runtime-packages/ui/src/components/tabs.tsx +74 -0
- package/dist/runtime-packages/ui/src/components/textarea.tsx +23 -0
- package/dist/runtime-packages/ui/src/components/theme-provider.tsx +73 -0
- package/dist/runtime-packages/ui/src/components/tooltip.tsx +61 -0
- package/dist/runtime-packages/ui/src/components/ui/file-upload.tsx +195 -0
- package/dist/runtime-packages/ui/src/hooks/.gitkeep +0 -0
- package/dist/runtime-packages/ui/src/index.ts +68 -0
- package/dist/runtime-packages/ui/src/lib/utils.ts +6 -0
- package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +68 -0
- package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +359 -0
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +274 -0
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +214 -0
- package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +135 -0
- package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +5 -0
- package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +194 -0
- package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +1 -0
- package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +1 -0
- package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +162 -0
- package/dist/runtime-packages/ui-host-runtime/src/index.ts +2 -0
- package/dist/runtime-packages/ui-host-runtime/src/logger.ts +11 -0
- package/dist/runtime-packages/ui-host-runtime/src/perf.ts +253 -0
- package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +195 -0
- package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +138 -0
- package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +159 -0
- package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +524 -0
- package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +12 -0
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +122 -0
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +26 -0
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +477 -0
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +209 -0
- package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +112 -0
- package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +318 -0
- package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +211 -0
- package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +814 -0
- package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +334 -0
- package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +180 -0
- package/dist/scaffold/assets/static/app/tsconfig.framework.json +3 -1
- package/dist/scaffold/assets/static/ui/tsconfig.framework.json +7 -19
- package/package.json +52 -17
- package/skills/dreamboard/SKILL.md +41 -75
- package/LICENSE +0 -89
- package/NOTICE +0 -1
- package/dist/chunk-BMYC6772.js.map +0 -1
- package/dist/chunk-EYYWGWTO.js.map +0 -1
- package/dist/chunk-MOVHYB6E.js.map +0 -1
- package/dist/scaffold/assets/static/ui/App.tsx +0 -22
- package/dist/src-CUL7EGGG.js +0 -634
- package/dist/src-CUL7EGGG.js.map +0 -1
- package/skills/dreamboard/references/authoring-lifecycle.md +0 -102
- package/skills/dreamboard/references/board-surfaces.md +0 -36
- package/skills/dreamboard/references/board-topology.md +0 -443
- package/skills/dreamboard/references/boards-and-topology.md +0 -100
- package/skills/dreamboard/references/card-actions.md +0 -107
- package/skills/dreamboard/references/cli.md +0 -120
- package/skills/dreamboard/references/core-concepts.md +0 -514
- package/skills/dreamboard/references/custom-renderers.md +0 -26
- package/skills/dreamboard/references/derived-values.md +0 -55
- package/skills/dreamboard/references/effects.md +0 -111
- package/skills/dreamboard/references/game-contract.md +0 -89
- package/skills/dreamboard/references/game-definition.md +0 -89
- package/skills/dreamboard/references/game-shell.md +0 -80
- package/skills/dreamboard/references/hand-surfaces.md +0 -33
- package/skills/dreamboard/references/index.md +0 -112
- package/skills/dreamboard/references/inputs-and-targets.md +0 -160
- package/skills/dreamboard/references/interactions.md +0 -158
- package/skills/dreamboard/references/manifest-fields.md +0 -137
- package/skills/dreamboard/references/manifest.md +0 -81
- package/skills/dreamboard/references/package-surfaces.md +0 -69
- package/skills/dreamboard/references/phases.md +0 -145
- package/skills/dreamboard/references/prompts-and-choices.md +0 -55
- package/skills/dreamboard/references/quickstart.md +0 -71
- package/skills/dreamboard/references/rule-authoring.md +0 -144
- package/skills/dreamboard/references/setup-bootstrap.md +0 -78
- package/skills/dreamboard/references/stages-and-zones.md +0 -59
- package/skills/dreamboard/references/static-views.md +0 -67
- package/skills/dreamboard/references/table-queries-and-ops.md +0 -103
- package/skills/dreamboard/references/testing-bases.md +0 -147
- package/skills/dreamboard/references/testing-generated-contracts.md +0 -142
- package/skills/dreamboard/references/testing-runtime-assertions.md +0 -124
- package/skills/dreamboard/references/testing-scenarios.md +0 -148
- package/skills/dreamboard/references/testing-ui-tests.md +0 -174
- package/skills/dreamboard/references/testing.md +0 -161
- package/skills/dreamboard/references/ui-architecture.md +0 -137
- package/skills/dreamboard/references/ui-components.md +0 -34
- package/skills/dreamboard/references/views.md +0 -72
- package/skills/dreamboard/references/workspace-layout.md +0 -136
- /package/dist/{dist-WJRJNFLI.js.map → dist-FRURQI7Q.js.map} +0 -0
|
@@ -1,514 +0,0 @@
|
|
|
1
|
-
# Core concepts
|
|
2
|
-
|
|
3
|
-
Learn Dreamboard's core concepts through real reducer, UI, and test code.
|
|
4
|
-
|
|
5
|
-
Dreamboard games are authored as a small TypeScript program. The same authored
|
|
6
|
-
program feeds the reducer runtime, generated UI contract, generated testing
|
|
7
|
-
contract, and local CLI checks.
|
|
8
|
-
|
|
9
|
-
The snippets below use generic SDK-shaped code and scaffold patterns.
|
|
10
|
-
|
|
11
|
-
## Manifest
|
|
12
|
-
|
|
13
|
-
The manifest is the static table model. Author it in `manifest.ts` with
|
|
14
|
-
`defineTopologyManifest`.
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
import { defineTopologyManifest } from "@dreamboard/sdk-types";
|
|
18
|
-
|
|
19
|
-
export default defineTopologyManifest({
|
|
20
|
-
players: {
|
|
21
|
-
minPlayers: 3,
|
|
22
|
-
maxPlayers: 4,
|
|
23
|
-
optimalPlayers: 4,
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
zones: [
|
|
27
|
-
{
|
|
28
|
-
id: "action-deck",
|
|
29
|
-
name: "Action Deck",
|
|
30
|
-
scope: "shared",
|
|
31
|
-
allowedCardSetIds: ["action-cards"],
|
|
32
|
-
visibility: "hidden",
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "player-hand",
|
|
36
|
-
name: "Player Hand",
|
|
37
|
-
scope: "perPlayer",
|
|
38
|
-
allowedCardSetIds: ["action-cards"],
|
|
39
|
-
visibility: "ownerOnly",
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: "played-cards",
|
|
43
|
-
name: "Played Cards",
|
|
44
|
-
scope: "shared",
|
|
45
|
-
allowedCardSetIds: ["action-cards"],
|
|
46
|
-
visibility: "public",
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
|
|
50
|
-
boardTemplates: [
|
|
51
|
-
{
|
|
52
|
-
id: "shared-board-template",
|
|
53
|
-
name: "Shared board template",
|
|
54
|
-
layout: "hex",
|
|
55
|
-
orientation: "pointy-top",
|
|
56
|
-
spaces: [
|
|
57
|
-
{ id: "cell-01", q: 0, r: 0, typeId: "standard" },
|
|
58
|
-
{ id: "cell-02", q: 1, r: -1, typeId: "standard" },
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`dreamboard sync` turns the manifest into generated ID unions, ID schemas,
|
|
66
|
-
runtime table types, board helpers, setup helpers, and typed UI/test contracts.
|
|
67
|
-
|
|
68
|
-
## Game contract
|
|
69
|
-
|
|
70
|
-
The game contract binds the generated manifest contract to reducer state schemas
|
|
71
|
-
and phase names.
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
import { z } from "zod";
|
|
75
|
-
import { ids, manifestContract } from "../shared/manifest-contract";
|
|
76
|
-
import {
|
|
77
|
-
defineGameContract,
|
|
78
|
-
sparseCounts,
|
|
79
|
-
type GameStateOf,
|
|
80
|
-
} from "@dreamboard/app-sdk/reducer";
|
|
81
|
-
|
|
82
|
-
const playerTurnPhaseStateSchema = z.object({
|
|
83
|
-
step: z.enum(["roll", "resolve", "main", "cleanup"]),
|
|
84
|
-
diceRolled: z.boolean(),
|
|
85
|
-
diceValues: z
|
|
86
|
-
.tuple([z.number().int().min(1).max(6), z.number().int().min(1).max(6)])
|
|
87
|
-
.nullable(),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
export const gameContract = defineGameContract({
|
|
91
|
-
manifest: manifestContract,
|
|
92
|
-
state: {
|
|
93
|
-
public: z.object({
|
|
94
|
-
progressByPlayer: sparseCounts(ids.playerId),
|
|
95
|
-
}),
|
|
96
|
-
private: z.object({}),
|
|
97
|
-
hidden: z.object({}),
|
|
98
|
-
},
|
|
99
|
-
phaseNames: ["setup", "playerTurn"] as const,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
export type GameContract = typeof gameContract;
|
|
103
|
-
export type GameState = GameStateOf<GameContract>;
|
|
104
|
-
export type PlayerTurnPhaseState = z.infer<typeof playerTurnPhaseStateSchema>;
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
The contract is the reducer's type root. If a phase name, player id, card id,
|
|
108
|
-
zone id, board id, or state field changes, dependent reducer/UI/test code should
|
|
109
|
-
fail to compile.
|
|
110
|
-
|
|
111
|
-
## Game definition
|
|
112
|
-
|
|
113
|
-
`defineGame` assembles the contract into a runnable reducer definition. Initial
|
|
114
|
-
state belongs here, not in `defineGameContract`.
|
|
115
|
-
|
|
116
|
-
```ts
|
|
117
|
-
import {
|
|
118
|
-
defineGame,
|
|
119
|
-
type ReducerGameDefinition,
|
|
120
|
-
} from "@dreamboard/app-sdk/reducer";
|
|
121
|
-
import { gameContract, type GameContract } from "./game-contract";
|
|
122
|
-
import { phases } from "./phases";
|
|
123
|
-
import { playerView } from "./player-view";
|
|
124
|
-
import { boardStatic } from "./board-static";
|
|
125
|
-
import setupProfiles from "./setup-profiles";
|
|
126
|
-
|
|
127
|
-
const views = {
|
|
128
|
-
player: playerView,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const game: ReducerGameDefinition<GameContract, typeof phases, typeof views> =
|
|
132
|
-
defineGame({
|
|
133
|
-
contract: gameContract,
|
|
134
|
-
initial: {
|
|
135
|
-
public: ({ playerIds }) => {
|
|
136
|
-
const progressByPlayer: Record<string, number> = {};
|
|
137
|
-
for (const pid of playerIds) {
|
|
138
|
-
// Session-time player ids come from the manifest-backed setup.
|
|
139
|
-
progressByPlayer[pid] = 0;
|
|
140
|
-
}
|
|
141
|
-
return { progressByPlayer };
|
|
142
|
-
},
|
|
143
|
-
private: () => ({}),
|
|
144
|
-
hidden: () => ({}),
|
|
145
|
-
},
|
|
146
|
-
initialPhase: "setup",
|
|
147
|
-
setupProfiles,
|
|
148
|
-
phases,
|
|
149
|
-
views,
|
|
150
|
-
staticView: boardStatic,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
export default game;
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Generated UI and testing contracts read from this definition. Registered phases,
|
|
157
|
-
interactions, zones, and board targets become typed UI/test primitives
|
|
158
|
-
automatically.
|
|
159
|
-
|
|
160
|
-
## Phase
|
|
161
|
-
|
|
162
|
-
A phase owns a slice of game flow: phase-local state, actor selection, optional
|
|
163
|
-
entry behavior, interactions, card actions, stages, and zones.
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
import { definePhase, pipe } from "@dreamboard/app-sdk/reducer";
|
|
167
|
-
import { zones } from "../../../shared/manifest-contract";
|
|
168
|
-
import {
|
|
169
|
-
playerTurnPhaseStateSchema,
|
|
170
|
-
type GameContract,
|
|
171
|
-
} from "../../game-contract";
|
|
172
|
-
import { rollDice } from "./roll-dice";
|
|
173
|
-
import { playActionCard } from "./card-actions";
|
|
174
|
-
import { endTurn } from "./end-turn";
|
|
175
|
-
import { FRESH_TURN } from "./turn-state";
|
|
176
|
-
|
|
177
|
-
export const playerTurn = definePhase<GameContract>()({
|
|
178
|
-
kind: "player",
|
|
179
|
-
state: playerTurnPhaseStateSchema,
|
|
180
|
-
initialState: () => ({ ...FRESH_TURN }),
|
|
181
|
-
enter({ state, accept, ops, q }) {
|
|
182
|
-
const activePlayer = state.flow.activePlayers[0] ?? q.player.order()[0]!;
|
|
183
|
-
|
|
184
|
-
// Entering from setup seeds the first turn. Later turns are set by endTurn.
|
|
185
|
-
return accept(pipe(state, ops.setActivePlayers([activePlayer])));
|
|
186
|
-
},
|
|
187
|
-
actor: ({ state }) => state.flow.activePlayers,
|
|
188
|
-
zones: [zones.playerHand],
|
|
189
|
-
cardActions: {
|
|
190
|
-
playActionCard,
|
|
191
|
-
},
|
|
192
|
-
interactions: {
|
|
193
|
-
rollDice,
|
|
194
|
-
endTurn,
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
Use phases for real game-flow states: setup, turn, discard, resolve prompt,
|
|
200
|
-
round cleanup, and end game.
|
|
201
|
-
|
|
202
|
-
## Interaction
|
|
203
|
-
|
|
204
|
-
An interaction is a player-submitted command or prompt. The trusted runtime
|
|
205
|
-
authorizes the actor, validates input collectors, parses params, and only then
|
|
206
|
-
calls `reduce`.
|
|
207
|
-
|
|
208
|
-
```ts
|
|
209
|
-
import {
|
|
210
|
-
defineInteraction,
|
|
211
|
-
rngInput,
|
|
212
|
-
pipe,
|
|
213
|
-
} from "@dreamboard/app-sdk/reducer";
|
|
214
|
-
import {
|
|
215
|
-
playerTurnPhaseStateSchema,
|
|
216
|
-
type GameContract,
|
|
217
|
-
} from "../../game-contract";
|
|
218
|
-
|
|
219
|
-
export const rollDice = defineInteraction<
|
|
220
|
-
GameContract,
|
|
221
|
-
typeof playerTurnPhaseStateSchema
|
|
222
|
-
>()({
|
|
223
|
-
step: "roll",
|
|
224
|
-
visibility: "all",
|
|
225
|
-
inputs: {
|
|
226
|
-
// The client submits no dice values. The trusted reducer bundle samples
|
|
227
|
-
// both dice and passes the result to reduce as input.params.dice.values.
|
|
228
|
-
dice: rngInput.d6(2),
|
|
229
|
-
},
|
|
230
|
-
validate({ state }) {
|
|
231
|
-
if (state.phase.diceRolled) {
|
|
232
|
-
return {
|
|
233
|
-
errorCode: "ALREADY_ROLLED",
|
|
234
|
-
message: "Already rolled this turn.",
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
return null;
|
|
238
|
-
},
|
|
239
|
-
reduce({ state, input, accept, ops }) {
|
|
240
|
-
const [d1, d2] = input.params.dice.values;
|
|
241
|
-
return accept(
|
|
242
|
-
pipe(
|
|
243
|
-
state,
|
|
244
|
-
ops.patchPhaseState({
|
|
245
|
-
step: "main",
|
|
246
|
-
diceRolled: true,
|
|
247
|
-
diceValues: [d1!, d2!],
|
|
248
|
-
}),
|
|
249
|
-
),
|
|
250
|
-
);
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
Use `validate` for business rules. Do not repeat turn authorization in every
|
|
256
|
-
interaction when phase-level `actor` already owns it.
|
|
257
|
-
|
|
258
|
-
## Card action
|
|
259
|
-
|
|
260
|
-
A card action is a hand-oriented interaction. It is tied to a `cardType` and a
|
|
261
|
-
`playFrom` zone, and the runtime adds `cardId` to reducer params.
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
import { defineCardAction, pipe } from "@dreamboard/app-sdk/reducer";
|
|
265
|
-
import {
|
|
266
|
-
playerTurnPhaseStateSchema,
|
|
267
|
-
type GameContract,
|
|
268
|
-
} from "../../game-contract";
|
|
269
|
-
import { cardTypes, zones } from "../../../shared/manifest-contract";
|
|
270
|
-
|
|
271
|
-
export const playActionCard = defineCardAction<
|
|
272
|
-
GameContract,
|
|
273
|
-
typeof playerTurnPhaseStateSchema
|
|
274
|
-
>()({
|
|
275
|
-
cardType: cardTypes.bonus,
|
|
276
|
-
playFrom: zones.playerHand,
|
|
277
|
-
label: "Play action card",
|
|
278
|
-
step: "main",
|
|
279
|
-
reduce({ state, input, accept, ops }) {
|
|
280
|
-
return accept(
|
|
281
|
-
pipe(
|
|
282
|
-
state,
|
|
283
|
-
ops.moveCardFromPlayerZoneToSharedZone({
|
|
284
|
-
playerId: input.playerId,
|
|
285
|
-
fromZoneId: zones.playerHand,
|
|
286
|
-
toZoneId: zones.playedCards,
|
|
287
|
-
cardId: input.params.cardId,
|
|
288
|
-
playedBy: input.playerId,
|
|
289
|
-
}),
|
|
290
|
-
ops.patchPhaseState({ actionCardPlayedThisTurn: true }),
|
|
291
|
-
),
|
|
292
|
-
);
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Use `defineCardAction` for "play this card from hand" mechanics. Use
|
|
298
|
-
`defineInteraction` for actions that are not fundamentally card-instance plays.
|
|
299
|
-
|
|
300
|
-
## Input collector
|
|
301
|
-
|
|
302
|
-
An input collector describes one parameter for an interaction. Collectors are
|
|
303
|
-
the source of reducer parameter types and client-facing metadata.
|
|
304
|
-
|
|
305
|
-
```ts
|
|
306
|
-
import { boardInput, formInput } from "@dreamboard/app-sdk/reducer";
|
|
307
|
-
import type { GameState } from "../../game-contract";
|
|
308
|
-
import {
|
|
309
|
-
literals,
|
|
310
|
-
type ResourceId,
|
|
311
|
-
type SpaceId,
|
|
312
|
-
} from "../../../shared/manifest-contract";
|
|
313
|
-
import { tokenSpaceTarget } from "../../eligibility";
|
|
314
|
-
|
|
315
|
-
const moveTokenInputs = {
|
|
316
|
-
spaceId: boardInput.space<GameState, SpaceId>({
|
|
317
|
-
target: tokenSpaceTarget,
|
|
318
|
-
}),
|
|
319
|
-
receiveResource: formInput.choice<ResourceId>({
|
|
320
|
-
choices: literals.resourceIds.map((resourceId) => ({
|
|
321
|
-
value: resourceId,
|
|
322
|
-
label: resourceId,
|
|
323
|
-
})),
|
|
324
|
-
}),
|
|
325
|
-
};
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
Engine-sampled collectors such as `rngInput.*` appear in reducer params but are
|
|
329
|
-
omitted from client-submitted params.
|
|
330
|
-
|
|
331
|
-
## Target rule
|
|
332
|
-
|
|
333
|
-
A target rule is a finite eligibility rule built with `boardTarget`,
|
|
334
|
-
`cardTarget`, or `choiceTarget`.
|
|
335
|
-
|
|
336
|
-
```ts
|
|
337
|
-
import { boardTarget } from "@dreamboard/app-sdk/reducer";
|
|
338
|
-
import type { GameState } from "./game-contract";
|
|
339
|
-
import type { EdgeId } from "../shared/manifest-contract";
|
|
340
|
-
import { markerAt } from "./reducer-support";
|
|
341
|
-
|
|
342
|
-
export const buildMarkerTarget = boardTarget
|
|
343
|
-
.edge<GameState, EdgeId>("sector")
|
|
344
|
-
.where({
|
|
345
|
-
id: "empty",
|
|
346
|
-
errorCode: "EDGE_OCCUPIED",
|
|
347
|
-
message: "That edge already has a marker.",
|
|
348
|
-
test: ({ state, q, targetId }) => !markerAt(state, q, targetId),
|
|
349
|
-
})
|
|
350
|
-
.where({
|
|
351
|
-
id: "connected",
|
|
352
|
-
errorCode: "NOT_CONNECTED",
|
|
353
|
-
message: "Marker must connect to your network.",
|
|
354
|
-
// Local domain helpers can use the same state/q/playerId inputs as targets.
|
|
355
|
-
test: ({ state, q, targetId, playerId }) =>
|
|
356
|
-
isConnectedMarkerTarget(state, q, targetId, playerId),
|
|
357
|
-
})
|
|
358
|
-
.build();
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
The same rule should drive UI hints, submit validation, and tests. That keeps
|
|
362
|
-
"which space/card/choice is legal?" out of duplicated UI code.
|
|
363
|
-
|
|
364
|
-
## View
|
|
365
|
-
|
|
366
|
-
A dynamic view is the player-facing projection authored with `defineView`. UI
|
|
367
|
-
code should read views and generated hooks, not raw reducer state.
|
|
368
|
-
|
|
369
|
-
```ts
|
|
370
|
-
import { defineView } from "@dreamboard/app-sdk/reducer";
|
|
371
|
-
import type { GameContract, PlayerTurnPhaseState } from "./game-contract";
|
|
372
|
-
import { scoreLeader } from "./derived";
|
|
373
|
-
|
|
374
|
-
export const playerView = defineView<GameContract>()({
|
|
375
|
-
project({ state, playerId, q, derived }) {
|
|
376
|
-
const turn: PlayerTurnPhaseState | null =
|
|
377
|
-
state.flow.currentPhase === "playerTurn"
|
|
378
|
-
? (state.phase as PlayerTurnPhaseState)
|
|
379
|
-
: null;
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
diceRolled: turn?.diceRolled ?? false,
|
|
383
|
-
diceValues: turn?.diceValues ?? null,
|
|
384
|
-
myResources: q.player.resources(playerId),
|
|
385
|
-
leader: derived(scoreLeader),
|
|
386
|
-
};
|
|
387
|
-
},
|
|
388
|
-
});
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
Views receive state, `playerId`, runtime helpers, table queries, and derived
|
|
392
|
-
value access.
|
|
393
|
-
|
|
394
|
-
## Static view
|
|
395
|
-
|
|
396
|
-
A static view is session-scoped immutable data derived from the manifest and
|
|
397
|
-
static queries.
|
|
398
|
-
|
|
399
|
-
```ts
|
|
400
|
-
import { defineStaticView } from "@dreamboard/app-sdk/reducer";
|
|
401
|
-
import type { GameContract } from "./game-contract";
|
|
402
|
-
|
|
403
|
-
export const boardStatic = defineStaticView<GameContract>()({
|
|
404
|
-
project: ({ q }) => {
|
|
405
|
-
// Board topology does not change during play, so the UI can cache it.
|
|
406
|
-
return q.board.hex("sector");
|
|
407
|
-
},
|
|
408
|
-
});
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
Use static views for board topology, labels, and precomputed decoration derived
|
|
412
|
-
from the manifest.
|
|
413
|
-
|
|
414
|
-
## Derived value
|
|
415
|
-
|
|
416
|
-
A derived value is a pure reusable computation authored with `defineDerived`.
|
|
417
|
-
It is memoized for one state snapshot.
|
|
418
|
-
|
|
419
|
-
```ts
|
|
420
|
-
import { defineDerived } from "@dreamboard/app-sdk/reducer";
|
|
421
|
-
import type { GameContract } from "./game-contract";
|
|
422
|
-
import type { PlayerId } from "../shared/manifest-contract";
|
|
423
|
-
|
|
424
|
-
// Finds the current leader from stored per-player progress for this state.
|
|
425
|
-
export const scoreLeader = defineDerived<GameContract>()({
|
|
426
|
-
name: "scoreLeader",
|
|
427
|
-
compute: ({ state, q }): { playerId: PlayerId | null; score: number } => {
|
|
428
|
-
let playerId: PlayerId | null = null;
|
|
429
|
-
let score = 0;
|
|
430
|
-
|
|
431
|
-
for (const pid of q.player.order()) {
|
|
432
|
-
const playerScore = state.publicState.progressByPlayer[pid] ?? 0;
|
|
433
|
-
if (playerScore > score) {
|
|
434
|
-
playerId = pid;
|
|
435
|
-
score = playerScore;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return { playerId, score };
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
Keep source facts in state and compute aggregates from them.
|
|
445
|
-
|
|
446
|
-
## UI primitive
|
|
447
|
-
|
|
448
|
-
Generated UI primitives are typed React wrappers for the reducer contract. They
|
|
449
|
-
let UI code submit interactions, answer prompts, render zones, and target board
|
|
450
|
-
spaces without stringly typed input names.
|
|
451
|
-
|
|
452
|
-
```tsx
|
|
453
|
-
import {
|
|
454
|
-
Interaction,
|
|
455
|
-
PromptInbox,
|
|
456
|
-
Zone,
|
|
457
|
-
useInteractionByKey,
|
|
458
|
-
} from "@dreamboard/ui-contract";
|
|
459
|
-
|
|
460
|
-
export function PlayerTurnPanel() {
|
|
461
|
-
const rollDice = useInteractionByKey("playerTurn.rollDice");
|
|
462
|
-
|
|
463
|
-
return (
|
|
464
|
-
<section>
|
|
465
|
-
<PromptInbox.Root>
|
|
466
|
-
<PromptInbox.Empty>No prompts.</PromptInbox.Empty>
|
|
467
|
-
<PromptInbox.Items />
|
|
468
|
-
</PromptInbox.Root>
|
|
469
|
-
|
|
470
|
-
<Interaction.Root interaction="playerTurn.rollDice">
|
|
471
|
-
<Interaction.Submit disabled={!rollDice?.canSubmit}>
|
|
472
|
-
Roll dice
|
|
473
|
-
</Interaction.Submit>
|
|
474
|
-
</Interaction.Root>
|
|
475
|
-
|
|
476
|
-
<Zone.Root zone="playerHand">
|
|
477
|
-
<Zone.List />
|
|
478
|
-
</Zone.Root>
|
|
479
|
-
</section>
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
Use `@dreamboard/ui-contract` for game-specific typing and `@dreamboard/ui-sdk`
|
|
485
|
-
for generic components such as cards, boards, resources, dice, and player
|
|
486
|
-
chrome.
|
|
487
|
-
|
|
488
|
-
## Scenario
|
|
489
|
-
|
|
490
|
-
A scenario is a reducer-native test authored with `defineScenario`.
|
|
491
|
-
|
|
492
|
-
```ts
|
|
493
|
-
import { defineScenario } from "../testing-types";
|
|
494
|
-
|
|
495
|
-
export default defineScenario({
|
|
496
|
-
id: "player-turn-roll",
|
|
497
|
-
description: "Seat 0 rolls dice on their first turn after setup",
|
|
498
|
-
from: "after-setup",
|
|
499
|
-
when: async ({ game, seat }) => {
|
|
500
|
-
// rollDice uses rngInput.d6(2), so the client submits no params.
|
|
501
|
-
await game.submit(seat(0), "rollDice", {});
|
|
502
|
-
},
|
|
503
|
-
then: ({ expect, state, view, seat }) => {
|
|
504
|
-
expect(state()).toBe("playerTurn");
|
|
505
|
-
|
|
506
|
-
const p1View = view(seat(0));
|
|
507
|
-
expect(p1View.diceRolled).toBe(true);
|
|
508
|
-
expect(p1View.diceValues).not.toBeNull();
|
|
509
|
-
},
|
|
510
|
-
});
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
Scenarios verify the same reducer bundle that `dreamboard dev` runs. They are
|
|
514
|
-
the fastest way to prove reducer behavior without a browser.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Custom renderers
|
|
2
|
-
|
|
3
|
-
Use hooks as local escape hatches while keeping reducer behavior authoritative.
|
|
4
|
-
|
|
5
|
-
Most UI should be built from `Interaction`, `Prompt`, `PromptInbox`, and `Zone`
|
|
6
|
-
components. Use hooks when component composition cannot express the UI.
|
|
7
|
-
|
|
8
|
-
Good hook escape hatches:
|
|
9
|
-
|
|
10
|
-
- SVG or canvas board geometry
|
|
11
|
-
- gesture-heavy interaction layers
|
|
12
|
-
- styling a card based on draft state
|
|
13
|
-
- integrating a complex third-party renderer
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
const buildRoute = useInteractionByKey("playerTurn.buildRoute");
|
|
17
|
-
const board = useBoardInteractions();
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Do not use hooks to recreate a generic action panel or prompt inbox. Prefer
|
|
21
|
-
primitives and defaults for ordinary actions, prompts, card selection, and
|
|
22
|
-
zones.
|
|
23
|
-
|
|
24
|
-
When a custom renderer needs a descriptor, bind it through
|
|
25
|
-
`useInteractionByKey` or `useInteractionHandle`; do not call runtime transport
|
|
26
|
-
methods directly.
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Derived values
|
|
2
|
-
|
|
3
|
-
Use defineDerived for reusable, memoized pure computations over reducer state and table queries.
|
|
4
|
-
|
|
5
|
-
Derived values are pure computations over one state snapshot. They are useful for expensive or repeated calculations: longest route, area majority, winner selection, eligible scoring sets, or aggregate card/resource summaries.
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
export const winnerOf = defineDerived<GameContract>()({
|
|
9
|
-
name: "winnerOf",
|
|
10
|
-
compute: ({ state, q }) => {
|
|
11
|
-
const players = q.player.order();
|
|
12
|
-
const scores = players.map((playerId) => ({
|
|
13
|
-
playerId,
|
|
14
|
-
score: state.publicState.scores[playerId] ?? 0,
|
|
15
|
-
}));
|
|
16
|
-
return scores.toSorted((a, b) => b.score - a.score)[0]?.playerId ?? null;
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Read it from reducer callbacks or views through injected `derived`:
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
reduce({ state, derived, accept, ops }) {
|
|
25
|
-
const winnerPlayerId = derived(winnerOf);
|
|
26
|
-
return accept(pipe(state, ops.patchPublicState({ winnerPlayerId })));
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Cache lifetime
|
|
31
|
-
|
|
32
|
-
Derived values are memoized by definition identity for one state snapshot. Each engine tick and each view projection gets a fresh resolver.
|
|
33
|
-
|
|
34
|
-
That means:
|
|
35
|
-
|
|
36
|
-
- repeated reads of the same derived value in one callback are cheap
|
|
37
|
-
- derived values should be pure
|
|
38
|
-
- cache lifetime does not cross reducer steps
|
|
39
|
-
- derived values can depend on other derived values through `derived(...)`
|
|
40
|
-
|
|
41
|
-
Cyclic derived dependencies throw a readable error.
|
|
42
|
-
|
|
43
|
-
## Do not cache mirrors in publicState
|
|
44
|
-
|
|
45
|
-
Avoid this pattern:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
publicState: {
|
|
49
|
-
scoreByPlayerId: ...
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
if the value can be recomputed from board occupancy, card locations, resources, or other source facts. Store the source facts and derive the aggregate.
|
|
54
|
-
|
|
55
|
-
Use state for mutable facts. Use `defineDerived` for computed meaning.
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Effects
|
|
2
|
-
|
|
3
|
-
Use defineEffect and fx.effect for engine-side dice rolls and zone shuffles.
|
|
4
|
-
|
|
5
|
-
Effects are engine-side cues. They are for runtime work the pure reducer should not perform itself, such as rolling a die or shuffling a shared or player zone.
|
|
6
|
-
|
|
7
|
-
Effects are authored with `defineEffect`, registered on a phase, and dispatched with `fx.effect`.
|
|
8
|
-
|
|
9
|
-
## Shuffle a shared zone
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
const shuffleDeck = defineEffect<GameContract>()({
|
|
13
|
-
type: "shuffleSharedZone",
|
|
14
|
-
id: "shuffle-deck",
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export const shufflePhase = definePhase<GameContract>()({
|
|
18
|
-
kind: "auto",
|
|
19
|
-
state: z.object({}),
|
|
20
|
-
initialState: () => ({}),
|
|
21
|
-
effects: {
|
|
22
|
-
shuffleDeck,
|
|
23
|
-
},
|
|
24
|
-
stages: {
|
|
25
|
-
shuffle: definePhaseStage<GameContract, typeof phaseState>()({
|
|
26
|
-
allow: [],
|
|
27
|
-
onEnter({ state, accept, fx }) {
|
|
28
|
-
return accept(state, [
|
|
29
|
-
fx.effect(shuffleDeck, { zoneId: "draw" }),
|
|
30
|
-
fx.transition("deal"),
|
|
31
|
-
]);
|
|
32
|
-
},
|
|
33
|
-
}),
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
After the runtime shuffles the zone, later reducer code can deal deterministically from the top with `ops.dealCardsToPlayerZone`.
|
|
39
|
-
|
|
40
|
-
Use `shufflePlayerZone` when the runtime should shuffle a single player's zone:
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
const shuffleHand = defineEffect<GameContract>()({
|
|
44
|
-
type: "shufflePlayerZone",
|
|
45
|
-
id: "shuffle-hand",
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
fx.effect(shuffleHand, {
|
|
49
|
-
zoneId: "hand",
|
|
50
|
-
playerId: input.playerId,
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Roll a die with a continuation
|
|
55
|
-
|
|
56
|
-
Use `reduce` on an effect when the reducer needs the engine result.
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
const rollProduction = defineEffect<GameContract>()({
|
|
60
|
-
type: "rollDie",
|
|
61
|
-
id: "roll-production",
|
|
62
|
-
context: z.object({
|
|
63
|
-
reason: z.string(),
|
|
64
|
-
}),
|
|
65
|
-
reduce({ state, input, accept, ops }) {
|
|
66
|
-
return accept(
|
|
67
|
-
pipe(
|
|
68
|
-
state,
|
|
69
|
-
ops.patchPublicState({
|
|
70
|
-
lastRoll: input.response.value,
|
|
71
|
-
lastRollReason: input.data.reason,
|
|
72
|
-
}),
|
|
73
|
-
),
|
|
74
|
-
);
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Dispatch it from a reducer callback:
|
|
80
|
-
|
|
81
|
-
```ts
|
|
82
|
-
return accept(state, [
|
|
83
|
-
fx.effect(rollProduction, {
|
|
84
|
-
dieId: "production-die",
|
|
85
|
-
context: { reason: "turn-start" },
|
|
86
|
-
}),
|
|
87
|
-
]);
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
The `context` is author data captured when the effect is queued. `input.response` is the engine result.
|
|
91
|
-
|
|
92
|
-
## Effects are not prompts
|
|
93
|
-
|
|
94
|
-
Use effects for engine work:
|
|
95
|
-
|
|
96
|
-
- `rollDie`
|
|
97
|
-
- `shuffleSharedZone`
|
|
98
|
-
- `shufflePlayerZone`
|
|
99
|
-
|
|
100
|
-
Use [Interactions](./interactions.md) with `promptInput` for addressed player requests. Prompts are player submissions; effects are runtime instructions.
|
|
101
|
-
|
|
102
|
-
## Effects and rngInput
|
|
103
|
-
|
|
104
|
-
`rngInput.d6(2)` is different from `defineEffect({ type: "rollDie" })`.
|
|
105
|
-
|
|
106
|
-
| Primitive | Use when |
|
|
107
|
-
| --- | --- |
|
|
108
|
-
| `rngInput.*` | Randomness is sampled as part of one player interaction submit. |
|
|
109
|
-
| `defineEffect` + `fx.effect` | Randomness or shuffling should be queued as a runtime instruction, often from an auto phase or with a continuation. |
|
|
110
|
-
|
|
111
|
-
Prefer the simpler `rngInput.*` when the random value is only needed inside the same interaction's `reduce`.
|