@joystyk/arcade 0.0.0 → 0.2.0

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/panel.js ADDED
@@ -0,0 +1,356 @@
1
+ import { command, define, defineDriver, subscription } from "@gyral/core";
2
+ import { compiled } from "@gyral/core/compiled";
3
+ //#region src/panel.ts
4
+ var _gyral$t0 = {
5
+ "id": "ji8bt56epz",
6
+ "html": "<header><h2></h2><p class=\"status\"></p><p>Best (<!---->): <strong></strong></p></header><div class=\"actions\"><button type=\"button\"></button> <button type=\"button\">Race your best</button></div><fieldset><legend>Run settings</legend><label>Pace <select><option value=\"standard\">Standard</option><option value=\"gentle\">Gentle</option></select></label></fieldset><details><summary>Controls and graphics</summary><label>Graphics <select><option value=\"auto\">Auto</option><option value=\"low\">Low</option><option value=\"medium\">Medium</option><option value=\"high\">High</option></select></label> <label><input type=\"checkbox\"> Show control pad</label><label><input type=\"checkbox\"> Alignment tone</label><label><input type=\"checkbox\"> Automatic fire</label></details>",
7
+ "parts": [
8
+ [1, [0, 0]],
9
+ [1, [0, 1]],
10
+ [
11
+ 2,
12
+ [0, 2],
13
+ 1
14
+ ],
15
+ [1, [
16
+ 0,
17
+ 2,
18
+ 3
19
+ ]],
20
+ [
21
+ 3,
22
+ [1, 0],
23
+ "data-intent"
24
+ ],
25
+ [
26
+ 5,
27
+ [1, 0],
28
+ "disabled"
29
+ ],
30
+ [1, [1, 0]],
31
+ [
32
+ 3,
33
+ [1, 2],
34
+ "data-intent"
35
+ ],
36
+ [
37
+ 5,
38
+ [1, 2],
39
+ "hidden"
40
+ ],
41
+ [
42
+ 5,
43
+ [2],
44
+ "disabled"
45
+ ],
46
+ [
47
+ 3,
48
+ [
49
+ 2,
50
+ 1,
51
+ 1
52
+ ],
53
+ "data-intent"
54
+ ],
55
+ [
56
+ 3,
57
+ [
58
+ 2,
59
+ 1,
60
+ 1
61
+ ],
62
+ "value"
63
+ ],
64
+ [
65
+ 3,
66
+ [
67
+ 3,
68
+ 1,
69
+ 1
70
+ ],
71
+ "data-intent"
72
+ ],
73
+ [
74
+ 3,
75
+ [
76
+ 3,
77
+ 1,
78
+ 1
79
+ ],
80
+ "value"
81
+ ],
82
+ [
83
+ 5,
84
+ [
85
+ 3,
86
+ 3,
87
+ 0
88
+ ],
89
+ "checked"
90
+ ],
91
+ [
92
+ 3,
93
+ [
94
+ 3,
95
+ 3,
96
+ 0
97
+ ],
98
+ "data-intent"
99
+ ],
100
+ [
101
+ 5,
102
+ [3, 4],
103
+ "hidden"
104
+ ],
105
+ [
106
+ 5,
107
+ [
108
+ 3,
109
+ 4,
110
+ 0
111
+ ],
112
+ "checked"
113
+ ],
114
+ [
115
+ 3,
116
+ [
117
+ 3,
118
+ 4,
119
+ 0
120
+ ],
121
+ "data-intent"
122
+ ],
123
+ [
124
+ 5,
125
+ [3, 5],
126
+ "hidden"
127
+ ],
128
+ [
129
+ 5,
130
+ [
131
+ 3,
132
+ 5,
133
+ 0
134
+ ],
135
+ "checked"
136
+ ],
137
+ [
138
+ 3,
139
+ [
140
+ 3,
141
+ 5,
142
+ 0
143
+ ],
144
+ "data-intent"
145
+ ]
146
+ ],
147
+ "segments": [
148
+ "<header><h2>",
149
+ {
150
+ "k": "child",
151
+ "in": "h2"
152
+ },
153
+ "</h2><p class=\"status\">",
154
+ {
155
+ "k": "child",
156
+ "in": "p"
157
+ },
158
+ "</p><p>Best (",
159
+ {
160
+ "k": "child",
161
+ "in": "p"
162
+ },
163
+ "<!---->): <strong>",
164
+ {
165
+ "k": "child",
166
+ "in": "strong"
167
+ },
168
+ "</strong></p></header><div class=\"actions\"><button type=\"button\"",
169
+ {
170
+ "k": "attr",
171
+ "name": "data-intent"
172
+ },
173
+ {
174
+ "k": "bool",
175
+ "name": "disabled"
176
+ },
177
+ ">",
178
+ {
179
+ "k": "child",
180
+ "in": "button"
181
+ },
182
+ "</button> <button type=\"button\"",
183
+ {
184
+ "k": "attr",
185
+ "name": "data-intent"
186
+ },
187
+ {
188
+ "k": "bool",
189
+ "name": "hidden"
190
+ },
191
+ ">Race your best</button></div><fieldset",
192
+ {
193
+ "k": "bool",
194
+ "name": "disabled"
195
+ },
196
+ "><legend>Run settings</legend><label>Pace <select",
197
+ {
198
+ "k": "attr",
199
+ "name": "data-intent"
200
+ },
201
+ {
202
+ "k": "attr",
203
+ "name": "value"
204
+ },
205
+ "><option value=\"standard\">Standard</option><option value=\"gentle\">Gentle</option></select></label></fieldset><details><summary>Controls and graphics</summary><label>Graphics <select",
206
+ {
207
+ "k": "attr",
208
+ "name": "data-intent"
209
+ },
210
+ {
211
+ "k": "attr",
212
+ "name": "value"
213
+ },
214
+ "><option value=\"auto\">Auto</option><option value=\"low\">Low</option><option value=\"medium\">Medium</option><option value=\"high\">High</option></select></label> <label><input type=\"checkbox\"",
215
+ {
216
+ "k": "bool",
217
+ "name": "checked"
218
+ },
219
+ {
220
+ "k": "attr",
221
+ "name": "data-intent"
222
+ },
223
+ "> Show control pad</label><label",
224
+ {
225
+ "k": "bool",
226
+ "name": "hidden"
227
+ },
228
+ "><input type=\"checkbox\"",
229
+ {
230
+ "k": "bool",
231
+ "name": "checked"
232
+ },
233
+ {
234
+ "k": "attr",
235
+ "name": "data-intent"
236
+ },
237
+ "> Alignment tone</label><label",
238
+ {
239
+ "k": "bool",
240
+ "name": "hidden"
241
+ },
242
+ "><input type=\"checkbox\"",
243
+ {
244
+ "k": "bool",
245
+ "name": "checked"
246
+ },
247
+ {
248
+ "k": "attr",
249
+ "name": "data-intent"
250
+ },
251
+ "> Automatic fire</label></details>"
252
+ ]
253
+ };
254
+ var feed = defineDriver("arcade-hud");
255
+ var action = defineDriver("arcade-action");
256
+ var initial = {
257
+ title: "",
258
+ status: "Loading settings…",
259
+ phase: "ready",
260
+ paused: false,
261
+ resuming: false,
262
+ pace: "standard",
263
+ daily: false,
264
+ best: 0,
265
+ race: false,
266
+ pad: false,
267
+ tone: false,
268
+ autoFire: false,
269
+ graphics: "auto",
270
+ loaded: false,
271
+ alignment: false,
272
+ shooter: false
273
+ };
274
+ var ArcadePanel = define()("jy-arcade-panel", {
275
+ shadow: false,
276
+ init: () => ({ hud: initial }),
277
+ subscriptions: () => [command(feed, void 0, { onSuccess: (hud) => ({
278
+ _tag: "Changed",
279
+ hud
280
+ }) })],
281
+ intent: {
282
+ Primary: () => ({
283
+ _tag: "Action",
284
+ action: { type: "primary" }
285
+ }),
286
+ Race: () => ({
287
+ _tag: "Action",
288
+ action: { type: "race" }
289
+ }),
290
+ Pad: () => ({
291
+ _tag: "Action",
292
+ action: { type: "pad" }
293
+ }),
294
+ Tone: () => ({
295
+ _tag: "Action",
296
+ action: { type: "tone" }
297
+ }),
298
+ Fire: () => ({
299
+ _tag: "Action",
300
+ action: { type: "fire" }
301
+ }),
302
+ Pace: ({ value }) => value === "standard" || value === "gentle" ? {
303
+ _tag: "Action",
304
+ action: {
305
+ type: "pace",
306
+ pace: value
307
+ }
308
+ } : void 0,
309
+ Graphics: ({ value }) => value === "auto" || value === "low" || value === "medium" || value === "high" ? {
310
+ _tag: "Action",
311
+ action: {
312
+ type: "graphics",
313
+ value
314
+ }
315
+ } : void 0
316
+ },
317
+ update: {
318
+ Changed: (_s, m) => ({ hud: m.hud }),
319
+ Action: (s, m) => [s, [command(action, m.action, { concurrency: "queue" })]]
320
+ },
321
+ view: ({ hud: h }, i) => compiled(_gyral$t0, [
322
+ h.title,
323
+ h.status,
324
+ h.pace,
325
+ h.best,
326
+ i.Primary,
327
+ !h.loaded || h.resuming || h.phase === "over",
328
+ h.phase === "ready" ? "Start" : h.resuming ? "Resuming…" : h.paused ? "Resume" : "Pause",
329
+ i.Race,
330
+ !h.race || h.daily || h.phase !== "ready",
331
+ h.phase !== "ready" || h.daily || !h.loaded,
332
+ i.Pace,
333
+ h.pace,
334
+ i.Graphics,
335
+ h.graphics,
336
+ h.pad,
337
+ i.Pad,
338
+ !h.alignment,
339
+ h.tone,
340
+ i.Tone,
341
+ !h.shooter,
342
+ h.autoFire,
343
+ i.Fire
344
+ ])
345
+ });
346
+ var panelDrivers = (listen, perform) => ({
347
+ "arcade-hud": subscription("arcade-hud", listen),
348
+ "arcade-action": defineDriver({
349
+ name: "arcade-action",
350
+ run: perform
351
+ })
352
+ });
353
+ //#endregion
354
+ export { ArcadePanel, panelDrivers };
355
+
356
+ //# sourceMappingURL=panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.js","names":["_gyral$compiled"],"sources":["../src/panel.ts"],"sourcesContent":["import { command, define, defineDriver, html, subscription, type DriverOverrides } from '@gyral/core';\nimport type { Pace } from './records.ts';\nexport type Hud = {\n title: string;\n status: string;\n phase: string;\n paused: boolean;\n resuming: boolean;\n pace: Pace;\n daily: boolean;\n best: number;\n race: boolean;\n pad: boolean;\n tone: boolean;\n autoFire: boolean;\n graphics: string;\n loaded: boolean;\n alignment: boolean;\n shooter: boolean;\n};\nexport type PanelAction =\n | {\n type: 'primary' | 'race' | 'pad' | 'tone' | 'fire';\n }\n | {\n type: 'pace';\n pace: Pace;\n }\n | {\n type: 'graphics';\n value: 'auto' | 'low' | 'medium' | 'high';\n };\nconst feed = defineDriver<void, Hud, never>('arcade-hud'),\n action = defineDriver<PanelAction, void>('arcade-action');\ntype Msg =\n | {\n _tag: 'Changed';\n hud: Hud;\n }\n | {\n _tag: 'Action';\n action: PanelAction;\n };\nconst initial: Hud = {\n title: '',\n status: 'Loading settings…',\n phase: 'ready',\n paused: false,\n resuming: false,\n pace: 'standard',\n daily: false,\n best: 0,\n race: false,\n pad: false,\n tone: false,\n autoFire: false,\n graphics: 'auto',\n loaded: false,\n alignment: false,\n shooter: false,\n};\nexport const ArcadePanel = define<\n {\n hud: Hud;\n },\n Msg\n>()('jy-arcade-panel', {\n shadow: false,\n init: () => ({ hud: initial }),\n subscriptions: () => [command(feed, undefined, { onSuccess: (hud) => ({ _tag: 'Changed', hud }) })],\n intent: {\n Primary: () => ({ _tag: 'Action', action: { type: 'primary' } }),\n Race: () => ({ _tag: 'Action', action: { type: 'race' } }),\n Pad: () => ({ _tag: 'Action', action: { type: 'pad' } }),\n Tone: () => ({ _tag: 'Action', action: { type: 'tone' } }),\n Fire: () => ({ _tag: 'Action', action: { type: 'fire' } }),\n Pace: ({ value }) =>\n value === 'standard' || value === 'gentle'\n ? { _tag: 'Action', action: { type: 'pace', pace: value } }\n : undefined,\n Graphics: ({ value }) =>\n value === 'auto' || value === 'low' || value === 'medium' || value === 'high'\n ? { _tag: 'Action', action: { type: 'graphics', value } }\n : undefined,\n },\n update: {\n Changed: (_s, m) => ({ hud: m.hud }),\n Action: (s, m) => [s, [command(action, m.action, { concurrency: 'queue' })]],\n },\n view: (\n { hud: h },\n i,\n ) => html`<header><h2>${h.title}</h2><p class=\"status\">${h.status}</p><p>Best (${h.pace}): <strong>${h.best}</strong></p></header>\n <div class=\"actions\"><button type=\"button\" data-intent=${i.Primary} ?disabled=${!h.loaded || h.resuming || h.phase === 'over'}>${h.phase === 'ready' ? 'Start' : h.resuming ? 'Resuming…' : h.paused ? 'Resume' : 'Pause'}</button>\n <button type=\"button\" data-intent=${i.Race} ?hidden=${!h.race || h.daily || h.phase !== 'ready'}>Race your best</button></div>\n <fieldset ?disabled=${h.phase !== 'ready' || h.daily || !h.loaded}><legend>Run settings</legend><label>Pace <select data-intent=${i.Pace} value=${h.pace}><option value=\"standard\">Standard</option><option value=\"gentle\">Gentle</option></select></label></fieldset>\n <details><summary>Controls and graphics</summary><label>Graphics <select data-intent=${i.Graphics} value=${h.graphics}><option value=\"auto\">Auto</option><option value=\"low\">Low</option><option value=\"medium\">Medium</option><option value=\"high\">High</option></select></label>\n <label><input type=\"checkbox\" ?checked=${h.pad} data-intent=${i.Pad}> Show control pad</label><label ?hidden=${!h.alignment}><input type=\"checkbox\" ?checked=${h.tone} data-intent=${i.Tone}> Alignment tone</label><label ?hidden=${!h.shooter}><input type=\"checkbox\" ?checked=${h.autoFire} data-intent=${i.Fire}> Automatic fire</label></details>`,\n});\nexport const panelDrivers = (\n listen: (emit: (hud: Hud) => void) => () => void,\n perform: (action: PanelAction) => void,\n): DriverOverrides => ({\n 'arcade-hud': subscription<Hud>('arcade-hud', listen),\n 'arcade-action': defineDriver<PanelAction, void>({ name: 'arcade-action', run: perform }),\n});\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type PauseOptions = {
2
+ pause(): void;
3
+ resume(): void;
4
+ announce(text: string): void;
5
+ changed?(resuming: boolean): void;
6
+ after?: (ms: number, done: () => void) => () => void;
7
+ };
8
+ /** Resume is explicit and cancellable; hiding during the countdown cancels it. */
9
+ export declare function bindPause(o: PauseOptions): {
10
+ pause: () => void;
11
+ resume(): void;
12
+ dispose(): void;
13
+ };
package/dist/pause.js ADDED
@@ -0,0 +1,36 @@
1
+ import { bindPagePause, resumeAfter } from "./legacy.js";
2
+ //#region src/pause.ts
3
+ /** Resume is explicit and cancellable; hiding during the countdown cancels it. */
4
+ function bindPause(o) {
5
+ const abort = new AbortController(), { signal } = abort;
6
+ let cancel = null;
7
+ const pause = () => {
8
+ cancel?.();
9
+ cancel = null;
10
+ o.changed?.(false);
11
+ o.pause();
12
+ };
13
+ bindPagePause(pause, signal);
14
+ return {
15
+ pause,
16
+ resume() {
17
+ if (cancel !== null || document.hidden) return;
18
+ o.announce("Resuming in one second.");
19
+ o.changed?.(true);
20
+ cancel = (o.after ?? resumeAfter)(1e3, () => {
21
+ cancel = null;
22
+ o.changed?.(false);
23
+ if (!document.hidden) o.resume();
24
+ });
25
+ },
26
+ dispose() {
27
+ abort.abort();
28
+ cancel?.();
29
+ cancel = null;
30
+ }
31
+ };
32
+ }
33
+ //#endregion
34
+ export { bindPause };
35
+
36
+ //# sourceMappingURL=pause.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pause.js","names":[],"sources":["../src/pause.ts"],"sourcesContent":["import { bindPagePause, resumeAfter } from './legacy.ts';\nexport type PauseOptions = {\n pause(): void;\n resume(): void;\n announce(text: string): void;\n changed?(resuming: boolean): void;\n after?: (ms: number, done: () => void) => () => void;\n};\n/** Resume is explicit and cancellable; hiding during the countdown cancels it. */\nexport function bindPause(o: PauseOptions) {\n const abort = new AbortController(),\n { signal } = abort;\n let cancel: (() => void) | null = null;\n const pause = (): void => {\n cancel?.();\n cancel = null;\n o.changed?.(false);\n o.pause();\n };\n bindPagePause(pause, signal);\n return {\n pause,\n resume(): void {\n if (cancel !== null || document.hidden) return;\n o.announce('Resuming in one second.');\n o.changed?.(true);\n const after = o.after ?? resumeAfter;\n cancel = after(1000, () => {\n cancel = null;\n o.changed?.(false);\n if (!document.hidden) o.resume();\n });\n },\n dispose(): void {\n abort.abort();\n cancel?.();\n cancel = null;\n },\n };\n}\n"],"mappings":";;;AASA,SAAgB,UAAU,GAAiB;CACzC,MAAM,QAAQ,IAAI,gBAAgB,GAChC,EAAE,WAAW;CACf,IAAI,SAA8B;CAClC,MAAM,cAAoB;EACxB,SAAS;EACT,SAAS;EACT,EAAE,UAAU,KAAK;EACjB,EAAE,MAAM;CACV;CACA,cAAc,OAAO,MAAM;CAC3B,OAAO;EACL;EACA,SAAe;GACb,IAAI,WAAW,QAAQ,SAAS,QAAQ;GACxC,EAAE,SAAS,yBAAyB;GACpC,EAAE,UAAU,IAAI;GAEhB,UADc,EAAE,SAAS,YAAA,CACV,WAAY;IACzB,SAAS;IACT,EAAE,UAAU,KAAK;IACjB,IAAI,CAAC,SAAS,QAAQ,EAAE,OAAO;GACjC,CAAC;EACH;EACA,UAAgB;GACd,MAAM,MAAM;GACZ,SAAS;GACT,SAAS;EACX;CACF;AACF"}
@@ -0,0 +1,19 @@
1
+ import type { AnyFixedStepSimulation, GameStorage, SimulationReplay } from '@joystyk/contracts';
2
+ import { z } from 'zod';
3
+ export declare const PACES: readonly ["standard", "gentle"];
4
+ export type Pace = (typeof PACES)[number];
5
+ export type BestRun = {
6
+ readonly score: number;
7
+ readonly replay: SimulationReplay | null;
8
+ };
9
+ export declare function createRecords(storage: GameStorage, gameId: string, simulation: AnyFixedStepSimulation): {
10
+ best: (pace: Pace) => Promise<BestRun>;
11
+ pace(): Promise<Pace>;
12
+ remember(pace: Pace): Promise<void>;
13
+ record(pace: Pace, score: number, replay: SimulationReplay | null): Promise<void>;
14
+ };
15
+ /** Independent simulation state and RNG: the ghost never dispatches to the live runner. */
16
+ export declare function createGhost(simulation: AnyFixedStepSimulation, record: unknown): {
17
+ config: z.core.util.JSONType;
18
+ advance(to: number): unknown;
19
+ } | null;
@@ -0,0 +1,105 @@
1
+ import { restoreRng } from "@joystyk/kit/rng";
2
+ import { replaySimulation } from "@joystyk/session";
3
+ import { z } from "zod";
4
+ //#region src/records.ts
5
+ var PACES = ["standard", "gentle"];
6
+ var rng = z.object({
7
+ algorithm: z.string(),
8
+ words: z.array(z.number().int())
9
+ });
10
+ var replaySchema = z.object({
11
+ layer: z.literal(3),
12
+ gameId: z.string(),
13
+ rulesVersion: z.number().int(),
14
+ config: z.json(),
15
+ seed: rng,
16
+ inputs: z.array(z.object({
17
+ tick: z.number().int().nonnegative(),
18
+ input: z.json()
19
+ })).max(2e5),
20
+ endTick: z.number().int().nonnegative().max(216e4)
21
+ });
22
+ var recordSchema = z.object({
23
+ score: z.number().nonnegative(),
24
+ replay: replaySchema.nullable()
25
+ });
26
+ function createRecords(storage, gameId, simulation) {
27
+ let chain = Promise.resolve();
28
+ const best = async (pace) => {
29
+ const parsed = recordSchema.safeParse(await storage.get(`arcade.best.${pace}`).catch(() => null));
30
+ if (!parsed.success) return {
31
+ score: 0,
32
+ replay: null
33
+ };
34
+ const value = parsed.data;
35
+ if (value.replay !== null && (value.replay.gameId !== gameId || value.replay.rulesVersion !== simulation.rulesVersion)) {
36
+ await storage.set(`arcade.best.${pace}`, {
37
+ score: value.score,
38
+ replay: null
39
+ }).catch(() => {});
40
+ return {
41
+ score: value.score,
42
+ replay: null
43
+ };
44
+ }
45
+ return value;
46
+ };
47
+ return {
48
+ best,
49
+ async pace() {
50
+ const value = await storage.get("arcade.pace").catch(() => null);
51
+ return value === "gentle" ? value : "standard";
52
+ },
53
+ remember(pace) {
54
+ return storage.set("arcade.pace", pace);
55
+ },
56
+ record(pace, score, replay) {
57
+ chain = chain.catch(() => {}).then(async () => {
58
+ const old = await best(pace);
59
+ if (score > old.score || score === old.score && old.replay === null) await storage.set(`arcade.best.${pace}`, {
60
+ score,
61
+ replay
62
+ });
63
+ });
64
+ return chain;
65
+ }
66
+ };
67
+ }
68
+ /** Independent simulation state and RNG: the ghost never dispatches to the live runner. */
69
+ function createGhost(simulation, record) {
70
+ const parsed = replaySchema.safeParse(record);
71
+ if (!parsed.success || parsed.data.rulesVersion !== simulation.rulesVersion) return null;
72
+ const replay = parsed.data;
73
+ const config = simulation.schemas.config["~standard"].validate(replay.config);
74
+ if (config instanceof Promise || config.issues !== void 0) return null;
75
+ let previous = -1;
76
+ for (const entry of replay.inputs) {
77
+ const checked = simulation.schemas.input["~standard"].validate(entry.input);
78
+ if (checked instanceof Promise || checked.issues !== void 0 || entry.tick < previous || entry.tick >= replay.endTick) return null;
79
+ previous = entry.tick;
80
+ }
81
+ try {
82
+ if (!replaySimulation(simulation, replay, restoreRng).ok) return null;
83
+ const rng = restoreRng(replay.seed), initial = simulation.init(replay.config, rng);
84
+ if (!initial.ok) return null;
85
+ let state = initial.value, tick = 0, index = 0;
86
+ return {
87
+ config: replay.config,
88
+ advance(to) {
89
+ while (tick < Math.min(to, replay.endTick)) {
90
+ const inputs = [];
91
+ while (replay.inputs[index]?.tick === tick) inputs.push(replay.inputs[index++].input);
92
+ state = simulation.step(state, inputs, rng).state;
93
+ tick++;
94
+ }
95
+ return simulation.view(state);
96
+ }
97
+ };
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+ //#endregion
103
+ export { PACES, createGhost, createRecords };
104
+
105
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","names":[],"sources":["../src/records.ts"],"sourcesContent":["import type { AnyFixedStepSimulation, GameStorage, Json, SimulationReplay } from '@joystyk/contracts';\nimport { restoreRng } from '@joystyk/kit/rng';\nimport { replaySimulation } from '@joystyk/session';\nimport { z } from 'zod';\nexport const PACES = ['standard', 'gentle'] as const;\nexport type Pace = (typeof PACES)[number];\nconst rng = z.object({ algorithm: z.string(), words: z.array(z.number().int()) });\nconst replaySchema = z.object({\n layer: z.literal(3),\n gameId: z.string(),\n rulesVersion: z.number().int(),\n config: z.json(),\n seed: rng,\n inputs: z.array(z.object({ tick: z.number().int().nonnegative(), input: z.json() })).max(200000),\n endTick: z.number().int().nonnegative().max(2160000),\n});\nconst recordSchema = z.object({ score: z.number().nonnegative(), replay: replaySchema.nullable() });\nexport type BestRun = {\n readonly score: number;\n readonly replay: SimulationReplay | null;\n};\nexport function createRecords(storage: GameStorage, gameId: string, simulation: AnyFixedStepSimulation) {\n let chain = Promise.resolve();\n const best = async (pace: Pace): Promise<BestRun> => {\n const parsed = recordSchema.safeParse(await storage.get(`arcade.best.${pace}`).catch(() => null));\n if (!parsed.success) return { score: 0, replay: null };\n const value = parsed.data;\n if (\n value.replay !== null &&\n (value.replay.gameId !== gameId || value.replay.rulesVersion !== simulation.rulesVersion)\n ) {\n await storage.set(`arcade.best.${pace}`, { score: value.score, replay: null }).catch(() => {});\n return { score: value.score, replay: null };\n }\n return value;\n };\n return {\n best,\n async pace(): Promise<Pace> {\n const value = await storage.get('arcade.pace').catch(() => null);\n return value === 'gentle' ? value : 'standard';\n },\n remember(pace: Pace): Promise<void> {\n return storage.set('arcade.pace', pace);\n },\n record(pace: Pace, score: number, replay: SimulationReplay | null): Promise<void> {\n chain = chain\n .catch(() => {})\n .then(async () => {\n const old = await best(pace);\n if (score > old.score || (score === old.score && old.replay === null))\n await storage.set(`arcade.best.${pace}`, { score, replay });\n });\n return chain;\n },\n };\n}\n/** Independent simulation state and RNG: the ghost never dispatches to the live runner. */\nexport function createGhost(simulation: AnyFixedStepSimulation, record: unknown) {\n const parsed = replaySchema.safeParse(record);\n if (!parsed.success || parsed.data.rulesVersion !== simulation.rulesVersion) return null;\n const replay = parsed.data;\n const config = simulation.schemas.config['~standard'].validate(replay.config);\n if (config instanceof Promise || config.issues !== undefined) return null;\n let previous = -1;\n for (const entry of replay.inputs) {\n const checked = simulation.schemas.input['~standard'].validate(entry.input);\n if (\n checked instanceof Promise ||\n checked.issues !== undefined ||\n entry.tick < previous ||\n entry.tick >= replay.endTick\n )\n return null;\n previous = entry.tick;\n }\n try {\n const checked = replaySimulation(simulation, replay, restoreRng);\n if (!checked.ok) return null;\n const rng = restoreRng(replay.seed),\n initial = simulation.init(replay.config, rng);\n if (!initial.ok) return null;\n let state: Json = initial.value,\n tick = 0,\n index = 0;\n return {\n config: replay.config,\n advance(to: number): unknown {\n while (tick < Math.min(to, replay.endTick)) {\n const inputs: Json[] = [];\n while (replay.inputs[index]?.tick === tick) inputs.push(replay.inputs[index++]!.input);\n state = simulation.step(state, inputs, rng).state;\n tick++;\n }\n return simulation.view(state);\n },\n };\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;AAIA,IAAa,QAAQ,CAAC,YAAY,QAAQ;AAE1C,IAAM,MAAM,EAAE,OAAO;CAAE,WAAW,EAAE,OAAO;CAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AAAE,CAAC;AAChF,IAAM,eAAe,EAAE,OAAO;CAC5B,OAAO,EAAE,QAAQ,CAAC;CAClB,QAAQ,EAAE,OAAO;CACjB,cAAc,EAAE,OAAO,CAAC,CAAC,IAAI;CAC7B,QAAQ,EAAE,KAAK;CACf,MAAM;CACN,QAAQ,EAAE,MAAM,EAAE,OAAO;EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY;EAAG,OAAO,EAAE,KAAK;CAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAM;CAC/F,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,KAAO;AACrD,CAAC;AACD,IAAM,eAAe,EAAE,OAAO;CAAE,OAAO,EAAE,OAAO,CAAC,CAAC,YAAY;CAAG,QAAQ,aAAa,SAAS;AAAE,CAAC;AAKlG,SAAgB,cAAc,SAAsB,QAAgB,YAAoC;CACtG,IAAI,QAAQ,QAAQ,QAAQ;CAC5B,MAAM,OAAO,OAAO,SAAiC;EACnD,MAAM,SAAS,aAAa,UAAU,MAAM,QAAQ,IAAI,eAAe,MAAM,CAAC,CAAC,YAAY,IAAI,CAAC;EAChG,IAAI,CAAC,OAAO,SAAS,OAAO;GAAE,OAAO;GAAG,QAAQ;EAAK;EACrD,MAAM,QAAQ,OAAO;EACrB,IACE,MAAM,WAAW,SAChB,MAAM,OAAO,WAAW,UAAU,MAAM,OAAO,iBAAiB,WAAW,eAC5E;GACA,MAAM,QAAQ,IAAI,eAAe,QAAQ;IAAE,OAAO,MAAM;IAAO,QAAQ;GAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GAC7F,OAAO;IAAE,OAAO,MAAM;IAAO,QAAQ;GAAK;EAC5C;EACA,OAAO;CACT;CACA,OAAO;EACL;EACA,MAAM,OAAsB;GAC1B,MAAM,QAAQ,MAAM,QAAQ,IAAI,aAAa,CAAC,CAAC,YAAY,IAAI;GAC/D,OAAO,UAAU,WAAW,QAAQ;EACtC;EACA,SAAS,MAA2B;GAClC,OAAO,QAAQ,IAAI,eAAe,IAAI;EACxC;EACA,OAAO,MAAY,OAAe,QAAgD;GAChF,QAAQ,MACL,YAAY,CAAC,CAAC,CAAC,CACf,KAAK,YAAY;IAChB,MAAM,MAAM,MAAM,KAAK,IAAI;IAC3B,IAAI,QAAQ,IAAI,SAAU,UAAU,IAAI,SAAS,IAAI,WAAW,MAC9D,MAAM,QAAQ,IAAI,eAAe,QAAQ;KAAE;KAAO;IAAO,CAAC;GAC9D,CAAC;GACH,OAAO;EACT;CACF;AACF;;AAEA,SAAgB,YAAY,YAAoC,QAAiB;CAC/E,MAAM,SAAS,aAAa,UAAU,MAAM;CAC5C,IAAI,CAAC,OAAO,WAAW,OAAO,KAAK,iBAAiB,WAAW,cAAc,OAAO;CACpF,MAAM,SAAS,OAAO;CACtB,MAAM,SAAS,WAAW,QAAQ,OAAO,YAAY,CAAC,SAAS,OAAO,MAAM;CAC5E,IAAI,kBAAkB,WAAW,OAAO,WAAW,KAAA,GAAW,OAAO;CACrE,IAAI,WAAW;CACf,KAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,UAAU,WAAW,QAAQ,MAAM,YAAY,CAAC,SAAS,MAAM,KAAK;EAC1E,IACE,mBAAmB,WACnB,QAAQ,WAAW,KAAA,KACnB,MAAM,OAAO,YACb,MAAM,QAAQ,OAAO,SAErB,OAAO;EACT,WAAW,MAAM;CACnB;CACA,IAAI;EAEF,IAAI,CADY,iBAAiB,YAAY,QAAQ,UAChD,CAAA,CAAQ,IAAI,OAAO;EACxB,MAAM,MAAM,WAAW,OAAO,IAAI,GAChC,UAAU,WAAW,KAAK,OAAO,QAAQ,GAAG;EAC9C,IAAI,CAAC,QAAQ,IAAI,OAAO;EACxB,IAAI,QAAc,QAAQ,OACxB,OAAO,GACP,QAAQ;EACV,OAAO;GACL,QAAQ,OAAO;GACf,QAAQ,IAAqB;IAC3B,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,GAAG;KAC1C,MAAM,SAAiB,CAAC;KACxB,OAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,OAAO,KAAK,OAAO,OAAO,QAAQ,CAAE,KAAK;KACrF,QAAQ,WAAW,KAAK,OAAO,QAAQ,GAAG,CAAC,CAAC;KAC5C;IACF;IACA,OAAO,WAAW,KAAK,KAAK;GAC9B;EACF;CACF,QAAQ;EACN,OAAO;CACT;AACF"}
package/package.json CHANGED
@@ -1,20 +1,55 @@
1
1
  {
2
2
  "name": "@joystyk/arcade",
3
- "version": "0.0.0",
4
- "description": "Reserved for the first Joystyk library release.",
3
+ "version": "0.2.0",
4
+ "description": "Shared real-time input, pause, paces and ghost races.",
5
+ "private": false,
6
+ "type": "module",
7
+ "sideEffects": [
8
+ "**/panel.[jt]s"
9
+ ],
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "dependencies": {
17
+ "zod": "4.6.5",
18
+ "@gyral/core": "0.3.1",
19
+ "@joystyk/kit": "0.2.0",
20
+ "@joystyk/session": "0.2.0",
21
+ "@joystyk/contracts": "0.2.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "5.9.3",
25
+ "vite": "8.3.3",
26
+ "vitest": "4.1.11"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE",
35
+ "NOTICE",
36
+ "CHANGELOG.md"
37
+ ],
38
+ "engines": {
39
+ "node": ">=24.15.0"
40
+ },
5
41
  "license": "MIT",
6
42
  "repository": {
7
43
  "type": "git",
8
44
  "url": "git+https://github.com/joystyk-games/game-libraries.git",
9
45
  "directory": "packages/arcade"
10
46
  },
11
- "homepage": "https://github.com/joystyk-games/game-libraries",
12
- "files": [
13
- "README.md",
14
- "LICENSE",
15
- "NOTICE"
16
- ],
17
- "publishConfig": {
18
- "access": "public"
47
+ "homepage": "https://github.com/joystyk-games/game-libraries/tree/main/packages/arcade#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/joystyk-games/game-libraries/issues"
50
+ },
51
+ "scripts": {
52
+ "typecheck": "tsc --noEmit",
53
+ "build": "node ../../scripts/build-package.ts"
19
54
  }
20
- }
55
+ }