@johnmorrisdotca/houseki 0.4.1 → 0.4.2
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/CHANGELOG.md +19 -1
- package/README.md +77 -95
- package/dist/colour-chains/challenge.d.ts +12 -1
- package/dist/colour-chains/challenge.js +12 -1
- package/dist/colour-chains/content-types.d.ts +112 -0
- package/dist/colour-chains/content.d.ts +140 -3
- package/dist/colour-chains/content.js +140 -3
- package/dist/colour-chains/engine.d.ts +85 -6
- package/dist/colour-chains/engine.js +85 -6
- package/dist/colour-chains/input.d.ts +97 -7
- package/dist/colour-chains/input.js +97 -7
- package/dist/colour-chains/persistence.d.ts +36 -3
- package/dist/colour-chains/persistence.js +36 -3
- package/dist/colour-chains/types.d.ts +291 -17
- package/dist/colour-chains/validation.d.ts +12 -1
- package/dist/colour-chains/validation.js +12 -1
- package/dist/falling-triplets/content-types.d.ts +69 -0
- package/dist/falling-triplets/content.d.ts +60 -3
- package/dist/falling-triplets/content.js +60 -3
- package/dist/falling-triplets/engine.d.ts +122 -10
- package/dist/falling-triplets/engine.js +122 -10
- package/dist/falling-triplets/input.d.ts +61 -5
- package/dist/falling-triplets/input.js +61 -5
- package/dist/falling-triplets/match.d.ts +26 -2
- package/dist/falling-triplets/match.js +26 -2
- package/dist/falling-triplets/random.d.ts +34 -3
- package/dist/falling-triplets/random.js +34 -3
- package/dist/falling-triplets/types.d.ts +227 -3
- package/dist/gem-swap/content.d.ts +116 -0
- package/dist/gem-swap/content.js +72 -0
- package/dist/gem-swap/engine.d.ts +124 -9
- package/dist/gem-swap/engine.js +124 -9
- package/dist/gem-swap/persistence.d.ts +60 -4
- package/dist/gem-swap/persistence.js +60 -4
- package/dist/gem-swap/types.d.ts +336 -3
- package/dist/magnetic-blocks/content-types.d.ts +56 -0
- package/dist/magnetic-blocks/content.d.ts +91 -5
- package/dist/magnetic-blocks/content.js +91 -5
- package/dist/magnetic-blocks/engine.d.ts +63 -5
- package/dist/magnetic-blocks/engine.js +63 -5
- package/dist/magnetic-blocks/persistence.d.ts +36 -3
- package/dist/magnetic-blocks/persistence.js +36 -3
- package/dist/magnetic-blocks/physics.d.ts +176 -6
- package/dist/magnetic-blocks/physics.js +176 -6
- package/dist/magnetic-blocks/types.d.ts +185 -1
- package/dist/magnetic-blocks/validation.d.ts +12 -0
- package/dist/magnetic-blocks/validation.js +12 -0
- package/dist/nature/attraction.d.ts +24 -2
- package/dist/nature/attraction.js +24 -2
- package/dist/nature/environment.d.ts +200 -7
- package/dist/nature/environment.js +88 -7
- package/dist/nature/marking.d.ts +13 -1
- package/dist/nature/marking.js +13 -1
- package/dist/nature/rebound.d.ts +30 -2
- package/dist/nature/rebound.js +30 -2
- package/dist/nature/state.d.ts +11 -0
- package/dist/nature/state.js +11 -0
- package/dist/nature/types.d.ts +194 -1
- package/dist/nature/validation.d.ts +12 -0
- package/dist/nature/validation.js +12 -0
- package/dist/stone-collapse/content-types.d.ts +69 -0
- package/dist/stone-collapse/content.d.ts +60 -3
- package/dist/stone-collapse/content.js +60 -3
- package/dist/stone-collapse/engine.d.ts +99 -8
- package/dist/stone-collapse/engine.js +99 -8
- package/dist/stone-collapse/persistence.d.ts +36 -3
- package/dist/stone-collapse/persistence.js +36 -3
- package/dist/stone-collapse/types.d.ts +220 -3
- package/dist/stone-collapse/validation.d.ts +12 -1
- package/dist/stone-collapse/validation.js +12 -1
- package/package.json +34 -26
|
@@ -1,19 +1,110 @@
|
|
|
1
1
|
import type { Action, ChallengeOptions, CreateOptions, GameState, GameStatus, Stone, Transition } from './types.js';
|
|
2
2
|
/** @internal Constructs the generator's bounded valid-pair fallback for direct invariant checks. */
|
|
3
3
|
export declare function fallbackBoard(mask: readonly boolean[], width: number, height: number, colourCount: number): readonly (Stone | null)[];
|
|
4
|
-
/** Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
|
|
4
|
+
/** Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
|
|
5
|
+
*
|
|
6
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
7
|
+
* @returns Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
11
|
+
*
|
|
12
|
+
* console.log(createGame({seed:"docs"}).phase);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
5
15
|
export declare function createGame(options?: CreateOptions): GameState;
|
|
6
|
-
/** Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
|
|
16
|
+
/** Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
|
|
17
|
+
*
|
|
18
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
19
|
+
* @returns Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { createChallenge, levelManifest } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
23
|
+
*
|
|
24
|
+
* const level=levelManifest[0];
|
|
25
|
+
* console.log(createChallenge({id:level.id,seed:level.seed,width:level.width,height:level.height,colourCount:level.colourCount,initialBoard:level.board,goal:level.goal,moveLimit:level.moveLimit,witness:level.witness}).phase);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
7
28
|
export declare function createChallenge(options: ChallengeOptions): GameState;
|
|
8
|
-
/** Applies a selection, confirmation, or cancellation without mutating its input.
|
|
29
|
+
/** Applies a selection, confirmation, or cancellation without mutating its input.
|
|
30
|
+
*
|
|
31
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
32
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
33
|
+
* @returns Applies a selection, confirmation, or cancellation without mutating its input.
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { applyAction, createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
37
|
+
* import type { Action } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
38
|
+
*
|
|
39
|
+
* const state=createGame({seed:"docs"});
|
|
40
|
+
* const action: Action={kind:"hint"};
|
|
41
|
+
* console.log(applyAction(state,action).accepted);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
9
44
|
export declare function applyAction(state: GameState, action: Action): Transition;
|
|
10
|
-
/** Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
|
|
45
|
+
/** Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
|
|
46
|
+
*
|
|
47
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
48
|
+
* @returns Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { createGame, requestHint } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
52
|
+
*
|
|
53
|
+
* const state=createGame({seed:"docs"});
|
|
54
|
+
* console.log(requestHint(state).accepted);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
11
57
|
export declare function requestHint(state: GameState): Transition;
|
|
12
|
-
/** Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
|
|
58
|
+
/** Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
|
|
59
|
+
*
|
|
60
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
61
|
+
* @returns Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { createGame, undo } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
65
|
+
*
|
|
66
|
+
* const state=createGame({seed:"docs"});
|
|
67
|
+
* console.log(undo(state).accepted);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
13
70
|
export declare function undo(state: GameState): Transition;
|
|
14
|
-
/** Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
|
|
71
|
+
/** Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
|
|
72
|
+
*
|
|
73
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
74
|
+
* @param ticks - The nonnegative number of fixed 60 Hz logical ticks to advance.
|
|
75
|
+
* @returns Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { advanceTicks, createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
79
|
+
*
|
|
80
|
+
* const state=createGame({seed:"docs"});
|
|
81
|
+
* console.log(advanceTicks(state,1).state.phase);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
15
84
|
export declare function advanceTicks(state: GameState, ticks: number): Transition;
|
|
16
|
-
/** Lists every selection and confirmation currently accepted by the rules.
|
|
85
|
+
/** Lists every selection and confirmation currently accepted by the rules.
|
|
86
|
+
*
|
|
87
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
88
|
+
* @returns Lists every selection and confirmation currently accepted by the rules.
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { createGame, legalActions } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
92
|
+
*
|
|
93
|
+
* const state=createGame({seed:"docs"});
|
|
94
|
+
* console.log(legalActions(state).length);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
17
97
|
export declare function legalActions(state: GameState): readonly Action[];
|
|
18
|
-
/** Returns the stable public game status and the remaining-stone count.
|
|
98
|
+
/** Returns the stable public game status and the remaining-stone count.
|
|
99
|
+
*
|
|
100
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
101
|
+
* @returns Returns the stable public game status and the remaining-stone count.
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { createGame, statusOf } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
105
|
+
*
|
|
106
|
+
* const state=createGame({seed:"docs"});
|
|
107
|
+
* console.log(statusOf(state).phase);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
19
110
|
export declare function statusOf(state: GameState): GameStatus;
|
|
@@ -105,7 +105,17 @@ function baseState(settings, board, randomState, challenge, usedFallback = false
|
|
|
105
105
|
selectedTool: null, toolTarget: null, toolPreview: Object.freeze([]),
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
|
-
/** Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
|
|
108
|
+
/** Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
|
|
109
|
+
*
|
|
110
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
111
|
+
* @returns Creates a seeded full board; an all-singleton draw retries before using a checked adjacent pair.
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
115
|
+
*
|
|
116
|
+
* console.log(createGame({seed:"docs"}).phase);
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
109
119
|
export function createGame(options = {}) {
|
|
110
120
|
const valid = optionsFor(options); // All settings are checked before drawing any random values.
|
|
111
121
|
let randomState = hashSeed(`stone-collapse|${RULES}|${String(valid.seed)}`);
|
|
@@ -140,7 +150,18 @@ export function createGame(options = {}) {
|
|
|
140
150
|
const state = baseState(settings, board, randomState, null, usedFallback);
|
|
141
151
|
return { ...state, nextId };
|
|
142
152
|
}
|
|
143
|
-
/** Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
|
|
153
|
+
/** Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
|
|
154
|
+
*
|
|
155
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
156
|
+
* @returns Creates a fixed witnessed challenge after validating its board, objective, move budget, and every witness group.
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* import { createChallenge, levelManifest } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
160
|
+
*
|
|
161
|
+
* const level=levelManifest[0];
|
|
162
|
+
* console.log(createChallenge({id:level.id,seed:level.seed,width:level.width,height:level.height,colourCount:level.colourCount,initialBoard:level.board,goal:level.goal,moveLimit:level.moveLimit,witness:level.witness}).phase);
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
144
165
|
export function createChallenge(options) {
|
|
145
166
|
if (!options || typeof options !== 'object' || Array.isArray(options))
|
|
146
167
|
throw new StoneCollapseOptionsError('invalid-challenge', 'Challenge options must be an object');
|
|
@@ -466,13 +487,61 @@ function applyCore(state, action) {
|
|
|
466
487
|
}
|
|
467
488
|
return confirmSelected(state);
|
|
468
489
|
}
|
|
469
|
-
/** Applies a selection, confirmation, or cancellation without mutating its input.
|
|
490
|
+
/** Applies a selection, confirmation, or cancellation without mutating its input.
|
|
491
|
+
*
|
|
492
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
493
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
494
|
+
* @returns Applies a selection, confirmation, or cancellation without mutating its input.
|
|
495
|
+
* @example
|
|
496
|
+
* ```ts
|
|
497
|
+
* import { applyAction, createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
498
|
+
* import type { Action } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
499
|
+
*
|
|
500
|
+
* const state=createGame({seed:"docs"});
|
|
501
|
+
* const action: Action={kind:"hint"};
|
|
502
|
+
* console.log(applyAction(state,action).accepted);
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
470
505
|
export function applyAction(state, action) { return applyCore(state, action); }
|
|
471
|
-
/** Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
|
|
506
|
+
/** Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
|
|
507
|
+
*
|
|
508
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
509
|
+
* @returns Selects the next supplied witness group only while the run still matches its path; accepted hints mark the run assisted.
|
|
510
|
+
* @example
|
|
511
|
+
* ```ts
|
|
512
|
+
* import { createGame, requestHint } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
513
|
+
*
|
|
514
|
+
* const state=createGame({seed:"docs"});
|
|
515
|
+
* console.log(requestHint(state).accepted);
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
472
518
|
export function requestHint(state) { return applyCore(state, { kind: 'hint' }); }
|
|
473
|
-
/** Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
|
|
519
|
+
/** Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
|
|
520
|
+
*
|
|
521
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
522
|
+
* @returns Restores the full pre-move state in Relaxed or Challenge practice and permanently marks the attempt assisted.
|
|
523
|
+
* @example
|
|
524
|
+
* ```ts
|
|
525
|
+
* import { createGame, undo } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
526
|
+
*
|
|
527
|
+
* const state=createGame({seed:"docs"});
|
|
528
|
+
* console.log(undo(state).accepted);
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
474
531
|
export function undo(state) { return undoCore(state); }
|
|
475
|
-
/** Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
|
|
532
|
+
/** Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
|
|
533
|
+
*
|
|
534
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
535
|
+
* @param ticks - The nonnegative number of fixed 60 Hz logical ticks to advance.
|
|
536
|
+
* @returns Advances the observable 7/6/9 tick removal and gravity stages, bounded to 3600 ticks.
|
|
537
|
+
* @example
|
|
538
|
+
* ```ts
|
|
539
|
+
* import { advanceTicks, createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
540
|
+
*
|
|
541
|
+
* const state=createGame({seed:"docs"});
|
|
542
|
+
* console.log(advanceTicks(state,1).state.phase);
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
476
545
|
export function advanceTicks(state, ticks) {
|
|
477
546
|
if (!Number.isInteger(ticks) || ticks < 0 || ticks > 3600)
|
|
478
547
|
throw new RangeError('ticks must be an integer from 0 to 3600');
|
|
@@ -521,7 +590,18 @@ export function advanceTicks(state, ticks) {
|
|
|
521
590
|
}
|
|
522
591
|
return { state: current, events: Object.freeze(events), accepted: true };
|
|
523
592
|
}
|
|
524
|
-
/** Lists every selection and confirmation currently accepted by the rules.
|
|
593
|
+
/** Lists every selection and confirmation currently accepted by the rules.
|
|
594
|
+
*
|
|
595
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
596
|
+
* @returns Lists every selection and confirmation currently accepted by the rules.
|
|
597
|
+
* @example
|
|
598
|
+
* ```ts
|
|
599
|
+
* import { createGame, legalActions } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
600
|
+
*
|
|
601
|
+
* const state=createGame({seed:"docs"});
|
|
602
|
+
* console.log(legalActions(state).length);
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
525
605
|
export function legalActions(state) {
|
|
526
606
|
if (state.phase === 'won' || state.phase === 'finished' || state.phase === 'lost')
|
|
527
607
|
return canUndo(state) ? [{ kind: 'undo' }] : [];
|
|
@@ -564,7 +644,18 @@ export function legalActions(state) {
|
|
|
564
644
|
actions.push({ kind: 'undo' });
|
|
565
645
|
return actions;
|
|
566
646
|
}
|
|
567
|
-
/** Returns the stable public game status and the remaining-stone count.
|
|
647
|
+
/** Returns the stable public game status and the remaining-stone count.
|
|
648
|
+
*
|
|
649
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
650
|
+
* @returns Returns the stable public game status and the remaining-stone count.
|
|
651
|
+
* @example
|
|
652
|
+
* ```ts
|
|
653
|
+
* import { createGame, statusOf } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
654
|
+
*
|
|
655
|
+
* const state=createGame({seed:"docs"});
|
|
656
|
+
* console.log(statusOf(state).phase);
|
|
657
|
+
* ```
|
|
658
|
+
*/
|
|
568
659
|
export function statusOf(state) {
|
|
569
660
|
return { mode: state.settings.mode, phase: state.phase, score: state.score, moves: state.moves, removed: state.removed, remaining: state.board.filter(Boolean).length, previewScore: state.previewScore, usedFallback: state.usedFallback, assisted: state.assisted, inventory: state.inventory, toolProgress: state.toolProgress, toolAwardCursor: state.toolAwardCursor, selectedTool: state.selectedTool, toolTarget: state.toolTarget, toolPreview: state.toolPreview, ...(state.settings.goal ? { goal: state.settings.goal } : {}), ...(state.reason ? { reason: state.reason } : {}) };
|
|
570
661
|
}
|
|
@@ -1,7 +1,40 @@
|
|
|
1
1
|
import type { GameState } from './types.js';
|
|
2
|
-
/** Starts a fresh attempt from the identical seeded board or preserved challenge definition.
|
|
2
|
+
/** Starts a fresh attempt from the identical seeded board or preserved challenge definition.
|
|
3
|
+
*
|
|
4
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
5
|
+
* @returns Starts a fresh attempt from the identical seeded board or preserved challenge definition.
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createGame, restartGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
9
|
+
*
|
|
10
|
+
* const state=createGame({seed:"docs"});
|
|
11
|
+
* console.log(restartGame(state).phase);
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
3
14
|
export declare function restartGame(state: GameState): GameState;
|
|
4
|
-
/** Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
|
|
15
|
+
/** Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
|
|
16
|
+
*
|
|
17
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
18
|
+
* @returns Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { createGame, encodeGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
22
|
+
*
|
|
23
|
+
* const state=createGame({seed:"docs"});
|
|
24
|
+
* console.log(encodeGame(state).length);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
5
27
|
export declare function encodeGame(state: GameState): string;
|
|
6
|
-
/** Reconstructs and validates a bounded replay before accepting its checkpoint.
|
|
28
|
+
/** Reconstructs and validates a bounded replay before accepting its checkpoint.
|
|
29
|
+
*
|
|
30
|
+
* @param text - A saved JSON recording from this entry’s encodeGame; malformed or inconsistent saves throw.
|
|
31
|
+
* @returns Reconstructs and validates a bounded replay before accepting its checkpoint.
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { createGame, decodeGame, encodeGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
35
|
+
*
|
|
36
|
+
* const state=createGame({seed:"docs"});
|
|
37
|
+
* console.log(decodeGame(encodeGame(state)).phase);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
7
40
|
export declare function decodeGame(text: string): GameState;
|
|
@@ -35,7 +35,18 @@ function groupAt(board, settings, start) {
|
|
|
35
35
|
}
|
|
36
36
|
return [...found].sort((a, b) => a - b);
|
|
37
37
|
}
|
|
38
|
-
/** Starts a fresh attempt from the identical seeded board or preserved challenge definition.
|
|
38
|
+
/** Starts a fresh attempt from the identical seeded board or preserved challenge definition.
|
|
39
|
+
*
|
|
40
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
41
|
+
* @returns Starts a fresh attempt from the identical seeded board or preserved challenge definition.
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { createGame, restartGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
45
|
+
*
|
|
46
|
+
* const state=createGame({seed:"docs"});
|
|
47
|
+
* console.log(restartGame(state).phase);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
39
50
|
export function restartGame(state) {
|
|
40
51
|
if (state.settings.mode === 'challenge')
|
|
41
52
|
return createChallenge(state.challenge);
|
|
@@ -45,7 +56,18 @@ export function restartGame(state) {
|
|
|
45
56
|
const mode = settings.mode === 'arcade' ? 'arcade' : 'relaxed';
|
|
46
57
|
return createGame({ mode, colourCount: settings.colourCount, seed: settings.seed, tools: settings.tools, ...(settings.shape ? { shape: settings.shape } : { width: settings.width, height: settings.height, ...(settings.mask.every(Boolean) ? {} : { mask: settings.mask }) }) });
|
|
47
58
|
}
|
|
48
|
-
/** Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
|
|
59
|
+
/** Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
|
|
60
|
+
*
|
|
61
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
62
|
+
* @returns Encodes the canonical initial configuration, accepted replay actions, and a derived checkpoint.
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { createGame, encodeGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
66
|
+
*
|
|
67
|
+
* const state=createGame({seed:"docs"});
|
|
68
|
+
* console.log(encodeGame(state).length);
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
49
71
|
export function encodeGame(state) {
|
|
50
72
|
// Undo frames are derived by replaying committed actions; storing their full board snapshots scales poorly on deep boards.
|
|
51
73
|
const { history: _history, ...checkpoint } = state;
|
|
@@ -99,7 +121,18 @@ function stateFromInitial(settings, challenge) {
|
|
|
99
121
|
throw new TypeError('Initial settings are not canonical');
|
|
100
122
|
return state;
|
|
101
123
|
}
|
|
102
|
-
/** Reconstructs and validates a bounded replay before accepting its checkpoint.
|
|
124
|
+
/** Reconstructs and validates a bounded replay before accepting its checkpoint.
|
|
125
|
+
*
|
|
126
|
+
* @param text - A saved JSON recording from this entry’s encodeGame; malformed or inconsistent saves throw.
|
|
127
|
+
* @returns Reconstructs and validates a bounded replay before accepting its checkpoint.
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { createGame, decodeGame, encodeGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
131
|
+
*
|
|
132
|
+
* const state=createGame({seed:"docs"});
|
|
133
|
+
* console.log(decodeGame(encodeGame(state)).phase);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
103
136
|
export function decodeGame(text) {
|
|
104
137
|
if (typeof text !== 'string' || new TextEncoder().encode(text).length > 2 * 1024 * 1024)
|
|
105
138
|
throw new RangeError('Save must be text no larger than 2 MiB');
|
|
@@ -1,19 +1,111 @@
|
|
|
1
|
-
/** A colour used by a stone.
|
|
1
|
+
/** A colour used by a stone.
|
|
2
|
+
*
|
|
3
|
+
* @example
|
|
4
|
+
* ```ts
|
|
5
|
+
* import type { StoneColour } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
6
|
+
*
|
|
7
|
+
* const value: StoneColour = "red";
|
|
8
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
2
11
|
export type StoneColour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
|
|
3
|
-
/** A stone keeps its run-local identity as it falls or changes column.
|
|
12
|
+
/** A stone keeps its run-local identity as it falls or changes column.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import type { Stone } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
17
|
+
*
|
|
18
|
+
* const value: Stone = {id:1,colour:"red"};
|
|
19
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
4
22
|
export interface Stone {
|
|
5
23
|
readonly id: number;
|
|
6
24
|
readonly colour: StoneColour;
|
|
7
25
|
}
|
|
26
|
+
/** A named native board geometry; custom dimensions remain a separate option.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import type { BoardPreset } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
31
|
+
*
|
|
32
|
+
* const value: BoardPreset = "compact";
|
|
33
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
8
36
|
export type BoardPreset = 'compact' | 'standard' | 'wide' | 'tall' | 'extraWide' | 'deep' | 'large';
|
|
37
|
+
/** A native shaped active-cell layout applied to the row-major board.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import type { BoardShape } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
42
|
+
*
|
|
43
|
+
* const value: BoardShape = "heart";
|
|
44
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
9
47
|
export type BoardShape = 'heart' | 'star' | 'hexagon';
|
|
48
|
+
/** The native free, timed, daily or authored-challenge game mode.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import type { GameMode } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
53
|
+
*
|
|
54
|
+
* const value: GameMode = "relaxed";
|
|
55
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
10
58
|
export type GameMode = 'relaxed' | 'arcade' | 'daily' | 'challenge';
|
|
59
|
+
/** A stored Stone Collapse tool selected from the native Bomb and Pick tray.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* import type { StoredTool } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
64
|
+
*
|
|
65
|
+
* const value: StoredTool = "bomb";
|
|
66
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
11
69
|
export type StoredTool = 'bomb' | 'pick';
|
|
70
|
+
/** The native remaining counts of stored tools.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
75
|
+
* import type { ToolInventory } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
76
|
+
*
|
|
77
|
+
* const state=createGame({seed:"docs"});
|
|
78
|
+
* const value: ToolInventory = state.inventory;
|
|
79
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
12
82
|
export interface ToolInventory {
|
|
13
83
|
readonly bomb: number;
|
|
14
84
|
readonly pick: number;
|
|
15
85
|
}
|
|
86
|
+
/** The native ready, resolution and ending phases.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
91
|
+
* import type { GamePhase } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
92
|
+
*
|
|
93
|
+
* const state=createGame({seed:"docs"});
|
|
94
|
+
* const value: GamePhase = state.phase;
|
|
95
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
16
98
|
export type GamePhase = 'ready' | 'clear-mark' | 'clear-remove' | 'gravity' | 'won' | 'lost' | 'finished';
|
|
99
|
+
/** A native authored challenge objective; its discriminant selects target, chain, empty-board or score requirements.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* import type { ChallengeGoal } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
104
|
+
*
|
|
105
|
+
* const value: ChallengeGoal = {kind:"clear-all"};
|
|
106
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
17
109
|
export type ChallengeGoal = {
|
|
18
110
|
readonly kind: 'clear-all';
|
|
19
111
|
} | {
|
|
@@ -23,6 +115,18 @@ export type ChallengeGoal = {
|
|
|
23
115
|
readonly kind: 'score-target';
|
|
24
116
|
readonly minimumScore: number;
|
|
25
117
|
};
|
|
118
|
+
/** Validated settings fixed at native game creation.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
123
|
+
* import type { Settings } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
124
|
+
*
|
|
125
|
+
* const state=createGame({seed:"docs"});
|
|
126
|
+
* const value: Settings = state.settings;
|
|
127
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
26
130
|
export interface Settings {
|
|
27
131
|
readonly mode: GameMode;
|
|
28
132
|
readonly width: number;
|
|
@@ -37,6 +141,18 @@ export interface Settings {
|
|
|
37
141
|
readonly moveLimit?: number;
|
|
38
142
|
readonly tools: boolean;
|
|
39
143
|
}
|
|
144
|
+
/** A validated Stone Collapse authored setup and stable-id group witness.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
149
|
+
* import type { ChallengeDefinition } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
150
|
+
*
|
|
151
|
+
* const state=createGame({seed:"docs"});
|
|
152
|
+
* const value: ChallengeDefinition = {id:"docs",width:state.settings.width,height:state.settings.height,colourCount:4,seed:"docs",mask:state.settings.mask,initialBoard:state.board,goal:{kind:"clear-all"},witness:[]};
|
|
153
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
40
156
|
export interface ChallengeDefinition {
|
|
41
157
|
readonly id: string;
|
|
42
158
|
readonly width: number;
|
|
@@ -50,7 +166,18 @@ export interface ChallengeDefinition {
|
|
|
50
166
|
/** Each entry is the complete stable-ID set of the next witnessed group. */
|
|
51
167
|
readonly witness: readonly (readonly number[])[];
|
|
52
168
|
}
|
|
53
|
-
/** Data sufficient to restore the complete game immediately before a committed move.
|
|
169
|
+
/** Data sufficient to restore the complete game immediately before a committed move.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
174
|
+
* import type { UndoFrame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
175
|
+
*
|
|
176
|
+
* const state=createGame({seed:"docs"});
|
|
177
|
+
* const value: UndoFrame = {board:state.board,score:0,moves:0,removed:0,randomState:state.randomState,nextId:state.nextId,usedFallback:false,finishAdjustmentApplied:false,selectedId:null,selectedIds:[],previewScore:0,witnessIndex:0,inventory:state.inventory,toolProgress:0,toolAwardCursor:0,selectedTool:null,toolTarget:null,toolPreview:[]};
|
|
178
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
54
181
|
export interface UndoFrame {
|
|
55
182
|
readonly board: readonly (Stone | null)[];
|
|
56
183
|
readonly score: number;
|
|
@@ -71,6 +198,16 @@ export interface UndoFrame {
|
|
|
71
198
|
readonly toolTarget: number | null;
|
|
72
199
|
readonly toolPreview: readonly number[];
|
|
73
200
|
}
|
|
201
|
+
/** A native action recording with the logical position needed for deterministic replay.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* import type { RecordedAction } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
206
|
+
*
|
|
207
|
+
* const value: RecordedAction = {kind:"ticks",count:1};
|
|
208
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
74
211
|
export type RecordedAction = {
|
|
75
212
|
readonly kind: 'remove';
|
|
76
213
|
readonly move: number;
|
|
@@ -96,6 +233,18 @@ export type RecordedAction = {
|
|
|
96
233
|
readonly kind: 'ticks';
|
|
97
234
|
readonly count: number;
|
|
98
235
|
};
|
|
236
|
+
/** The immutable authoritative native game state, including its board, random state and recording.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
241
|
+
* import type { GameState } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
242
|
+
*
|
|
243
|
+
* const state=createGame({seed:"docs"});
|
|
244
|
+
* const value: GameState = state;
|
|
245
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
99
248
|
export interface GameState {
|
|
100
249
|
readonly game: 'stone-collapse';
|
|
101
250
|
readonly rules: 'collapse-2';
|
|
@@ -136,6 +285,17 @@ export interface GameState {
|
|
|
136
285
|
/** Exact row-major cells affected by the current target. */
|
|
137
286
|
readonly toolPreview: readonly number[];
|
|
138
287
|
}
|
|
288
|
+
/** A native player action accepted or rejected according to the current phase and rules.
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```ts
|
|
292
|
+
* import type { Action } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
293
|
+
*
|
|
294
|
+
* const action: Action={kind:"hint"};
|
|
295
|
+
* const value: Action = action;
|
|
296
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
139
299
|
export type Action = {
|
|
140
300
|
readonly kind: 'select';
|
|
141
301
|
readonly stoneId: number;
|
|
@@ -158,16 +318,49 @@ export type Action = {
|
|
|
158
318
|
} | {
|
|
159
319
|
readonly kind: 'cancel-tool';
|
|
160
320
|
};
|
|
321
|
+
/** An event emitted by one native transition; type selects the event’s additional fields.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* import type { GameEvent } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
326
|
+
*
|
|
327
|
+
* const value: GameEvent = {type:"host-example"};
|
|
328
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
161
331
|
export interface GameEvent {
|
|
162
332
|
readonly type: string;
|
|
163
333
|
readonly [key: string]: unknown;
|
|
164
334
|
}
|
|
335
|
+
/** The resulting native state, emitted events and acceptance decision; rejected actions preserve the input.
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```ts
|
|
339
|
+
* import { applyAction, createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
340
|
+
* import type { Action, Transition } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
341
|
+
*
|
|
342
|
+
* const state=createGame({seed:"docs"});
|
|
343
|
+
* const action: Action={kind:"hint"};
|
|
344
|
+
* const value: Transition = applyAction(state,action);
|
|
345
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
165
348
|
export interface Transition {
|
|
166
349
|
readonly state: GameState;
|
|
167
350
|
readonly events: readonly GameEvent[];
|
|
168
351
|
readonly accepted: boolean;
|
|
169
352
|
readonly reason?: string;
|
|
170
353
|
}
|
|
354
|
+
/** Creation settings for this native engine; dimensions, mode, seed and optional mechanics are validated together.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* import type { CreateOptions } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
359
|
+
*
|
|
360
|
+
* const value: CreateOptions = {seed:"docs"};
|
|
361
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
171
364
|
export interface CreateOptions {
|
|
172
365
|
readonly mode?: Exclude<GameMode, 'challenge'>;
|
|
173
366
|
readonly preset?: BoardPreset;
|
|
@@ -183,6 +376,18 @@ export interface CreateOptions {
|
|
|
183
376
|
/** Optional Bomb and Pick tray; available only in Relaxed and Arcade. */
|
|
184
377
|
readonly tools?: boolean;
|
|
185
378
|
}
|
|
379
|
+
/** Authored challenge inputs including the starting board, finite goal and supplied witness.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* import { createGame } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
384
|
+
* import type { ChallengeOptions } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
385
|
+
*
|
|
386
|
+
* const state=createGame({seed:"docs"});
|
|
387
|
+
* const value: ChallengeOptions = {id:"docs",width:6,height:6,colourCount:4,initialBoard:state.board,goal:{kind:"clear-all"},witness:[]};
|
|
388
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
186
391
|
export interface ChallengeOptions {
|
|
187
392
|
readonly id: string;
|
|
188
393
|
readonly width: number;
|
|
@@ -195,6 +400,18 @@ export interface ChallengeOptions {
|
|
|
195
400
|
readonly moveLimit?: number;
|
|
196
401
|
readonly witness: readonly (readonly number[])[];
|
|
197
402
|
}
|
|
403
|
+
/** A compact native status derived from the authoritative state for a host’s labels.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```ts
|
|
407
|
+
* import { createGame, statusOf } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
408
|
+
* import type { GameStatus } from "@johnmorrisdotca/houseki/stone-collapse";
|
|
409
|
+
*
|
|
410
|
+
* const state=createGame({seed:"docs"});
|
|
411
|
+
* const value: GameStatus = statusOf(state);
|
|
412
|
+
* console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
198
415
|
export interface GameStatus {
|
|
199
416
|
readonly mode: GameMode;
|
|
200
417
|
readonly phase: GamePhase;
|