@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.
Files changed (71) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README.md +77 -95
  3. package/dist/colour-chains/challenge.d.ts +12 -1
  4. package/dist/colour-chains/challenge.js +12 -1
  5. package/dist/colour-chains/content-types.d.ts +112 -0
  6. package/dist/colour-chains/content.d.ts +140 -3
  7. package/dist/colour-chains/content.js +140 -3
  8. package/dist/colour-chains/engine.d.ts +85 -6
  9. package/dist/colour-chains/engine.js +85 -6
  10. package/dist/colour-chains/input.d.ts +97 -7
  11. package/dist/colour-chains/input.js +97 -7
  12. package/dist/colour-chains/persistence.d.ts +36 -3
  13. package/dist/colour-chains/persistence.js +36 -3
  14. package/dist/colour-chains/types.d.ts +291 -17
  15. package/dist/colour-chains/validation.d.ts +12 -1
  16. package/dist/colour-chains/validation.js +12 -1
  17. package/dist/falling-triplets/content-types.d.ts +69 -0
  18. package/dist/falling-triplets/content.d.ts +60 -3
  19. package/dist/falling-triplets/content.js +60 -3
  20. package/dist/falling-triplets/engine.d.ts +122 -10
  21. package/dist/falling-triplets/engine.js +122 -10
  22. package/dist/falling-triplets/input.d.ts +61 -5
  23. package/dist/falling-triplets/input.js +61 -5
  24. package/dist/falling-triplets/match.d.ts +26 -2
  25. package/dist/falling-triplets/match.js +26 -2
  26. package/dist/falling-triplets/random.d.ts +34 -3
  27. package/dist/falling-triplets/random.js +34 -3
  28. package/dist/falling-triplets/types.d.ts +227 -3
  29. package/dist/gem-swap/content.d.ts +116 -0
  30. package/dist/gem-swap/content.js +72 -0
  31. package/dist/gem-swap/engine.d.ts +124 -9
  32. package/dist/gem-swap/engine.js +124 -9
  33. package/dist/gem-swap/persistence.d.ts +60 -4
  34. package/dist/gem-swap/persistence.js +60 -4
  35. package/dist/gem-swap/types.d.ts +336 -3
  36. package/dist/magnetic-blocks/content-types.d.ts +56 -0
  37. package/dist/magnetic-blocks/content.d.ts +91 -5
  38. package/dist/magnetic-blocks/content.js +91 -5
  39. package/dist/magnetic-blocks/engine.d.ts +63 -5
  40. package/dist/magnetic-blocks/engine.js +63 -5
  41. package/dist/magnetic-blocks/persistence.d.ts +36 -3
  42. package/dist/magnetic-blocks/persistence.js +36 -3
  43. package/dist/magnetic-blocks/physics.d.ts +176 -6
  44. package/dist/magnetic-blocks/physics.js +176 -6
  45. package/dist/magnetic-blocks/types.d.ts +185 -1
  46. package/dist/magnetic-blocks/validation.d.ts +12 -0
  47. package/dist/magnetic-blocks/validation.js +12 -0
  48. package/dist/nature/attraction.d.ts +24 -2
  49. package/dist/nature/attraction.js +24 -2
  50. package/dist/nature/environment.d.ts +200 -7
  51. package/dist/nature/environment.js +88 -7
  52. package/dist/nature/marking.d.ts +13 -1
  53. package/dist/nature/marking.js +13 -1
  54. package/dist/nature/rebound.d.ts +30 -2
  55. package/dist/nature/rebound.js +30 -2
  56. package/dist/nature/state.d.ts +11 -0
  57. package/dist/nature/state.js +11 -0
  58. package/dist/nature/types.d.ts +194 -1
  59. package/dist/nature/validation.d.ts +12 -0
  60. package/dist/nature/validation.js +12 -0
  61. package/dist/stone-collapse/content-types.d.ts +69 -0
  62. package/dist/stone-collapse/content.d.ts +60 -3
  63. package/dist/stone-collapse/content.js +60 -3
  64. package/dist/stone-collapse/engine.d.ts +99 -8
  65. package/dist/stone-collapse/engine.js +99 -8
  66. package/dist/stone-collapse/persistence.d.ts +36 -3
  67. package/dist/stone-collapse/persistence.js +36 -3
  68. package/dist/stone-collapse/types.d.ts +220 -3
  69. package/dist/stone-collapse/validation.d.ts +12 -1
  70. package/dist/stone-collapse/validation.js +12 -1
  71. package/package.json +34 -26
@@ -8,28 +8,114 @@ function deepFreeze(value) {
8
8
  }
9
9
  return value;
10
10
  }
11
+ /** The frozen native campaign manifest with its ordering, revisions, checksum and measured levels.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { campaignManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
16
+ *
17
+ * console.log(campaignManifest.count);
18
+ * ```
19
+ */
11
20
  export const campaignManifest = deepFreeze(contentData.campaign);
21
+ /** The frozen ordered native levels; array indices start at zero and display numbers start at one.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { levelManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
26
+ *
27
+ * console.log(levelManifest.length);
28
+ * ```
29
+ */
12
30
  export const levelManifest = campaignManifest.levels;
13
- /** Alias kept alongside other Houseki campaign lesson APIs. */
31
+ /** Alias kept alongside other Houseki campaign lesson APIs.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * import { tutorialManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
36
+ *
37
+ * console.log(tutorialManifest.length);
38
+ * ```
39
+ */
14
40
  export const tutorialManifest = deepFreeze(contentData.lessons);
41
+ /** The frozen native bilingual lessons and their authored teaching steps.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { lessonManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
46
+ *
47
+ * console.log(lessonManifest.length);
48
+ * ```
49
+ */
15
50
  export const lessonManifest = tutorialManifest;
16
- /** Finds a Magnetic Blocks campaign level by stable ID or one-based display number. */
51
+ /** Finds a Magnetic Blocks campaign level by stable ID or one-based display number.
52
+ *
53
+ * @param id - A stable content id, or the one-based display number when numbers are accepted.
54
+ * @returns Finds a Magnetic Blocks campaign level by stable ID or one-based display number. Returns undefined when the id or display number is absent.
55
+ * @example
56
+ * ```ts
57
+ * import { getLevel } from "@johnmorrisdotca/houseki/magnetic-blocks";
58
+ *
59
+ * console.log(getLevel(1)?.id);
60
+ * ```
61
+ */
17
62
  export function getLevel(id) {
18
63
  if (typeof id === 'number')
19
64
  return Number.isInteger(id) ? levelManifest.find(level => level.number === id) : undefined;
20
65
  return levelManifest.find(level => level.id === id);
21
66
  }
22
- /** Creates a fresh game with the authored rules, board, queue, and finite move budget. */
67
+ /** Creates a fresh game with the authored rules, board, queue, and finite move budget.
68
+ *
69
+ * @param id - A stable content id, or the one-based display number when numbers are accepted.
70
+ * @returns Creates a fresh game with the authored rules, board, queue, and finite move budget.
71
+ * @example
72
+ * ```ts
73
+ * import { createLevel } from "@johnmorrisdotca/houseki/magnetic-blocks";
74
+ *
75
+ * console.log(createLevel(1).phase);
76
+ * ```
77
+ */
23
78
  export function createLevel(id) {
24
79
  const level = getLevel(id);
25
80
  if (!level)
26
81
  throw new RangeError(`Unknown Magnetic Blocks level: ${String(id)}`);
27
82
  return createGame(level.options);
28
83
  }
29
- /** Finds one of the three bilingual rules lessons by stable ID. */
84
+ /** Finds one of the three bilingual rules lessons by stable ID.
85
+ *
86
+ * @param id - A stable content id, or the one-based display number when numbers are accepted.
87
+ * @returns Finds one of the three bilingual rules lessons by stable ID. Returns undefined when the id or display number is absent.
88
+ * @example
89
+ * ```ts
90
+ * import { getLesson, lessonManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
91
+ *
92
+ * console.log(getLesson(lessonManifest[0].id)?.id);
93
+ * ```
94
+ */
30
95
  export function getLesson(id) { return tutorialManifest.find(lesson => lesson.id === id); }
96
+ /** Find a magnetic-block lesson by stable id; returns undefined when no lesson has that id.
97
+ *
98
+ * @param id - A stable content id, or the one-based display number when numbers are accepted.
99
+ * @returns Find a magnetic-block lesson by stable id; returns undefined when no lesson has that id. Returns undefined when the id or display number is absent.
100
+ * @example
101
+ * ```ts
102
+ * import { getTutorial, lessonManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
103
+ *
104
+ * console.log(getTutorial(lessonManifest[0].id)?.id);
105
+ * ```
106
+ */
31
107
  export const getTutorial = getLesson;
32
- /** Creates a fresh lesson game with its authored setup and queue. */
108
+ /** Creates a fresh lesson game with its authored setup and queue.
109
+ *
110
+ * @param id - A stable content id, or the one-based display number when numbers are accepted.
111
+ * @returns Creates a fresh lesson game with its authored setup and queue.
112
+ * @example
113
+ * ```ts
114
+ * import { createLesson, lessonManifest } from "@johnmorrisdotca/houseki/magnetic-blocks";
115
+ *
116
+ * console.log(createLesson(lessonManifest[0].id).phase);
117
+ * ```
118
+ */
33
119
  export function createLesson(id) {
34
120
  const lesson = getLesson(id);
35
121
  if (!lesson)
@@ -1,11 +1,69 @@
1
1
  import type { Action, GameState, GameStatus, Transition } from './types.js';
2
- /** Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule. */
2
+ /** Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule.
3
+ *
4
+ * @param options - Validated native creation, authored challenge, or environment options described by this signature.
5
+ * @returns Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule.
6
+ * @example
7
+ * ```ts
8
+ * import { createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
9
+ *
10
+ * console.log(createGame({seed:"docs"}).phase);
11
+ * ```
12
+ */
3
13
  export declare function createGame(options?: import('./types.js').CreateOptions): GameState;
4
- /** Applies one movement, rotation, floor-switch or placement action without mutation. */
14
+ /** Applies one movement, rotation, floor-switch or placement action without mutation.
15
+ *
16
+ * @param state - The immutable native state to read or transition; the input is retained.
17
+ * @param action - One native action for this entry point; inspect accepted and reason on the result.
18
+ * @returns Applies one movement, rotation, floor-switch or placement action without mutation.
19
+ * @example
20
+ * ```ts
21
+ * import { applyAction, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
22
+ * import type { Action } from "@johnmorrisdotca/houseki/magnetic-blocks";
23
+ *
24
+ * const state=createGame({seed:"docs"});
25
+ * const action: Action={kind:"left"};
26
+ * console.log(applyAction(state,action).accepted);
27
+ * ```
28
+ */
5
29
  export declare function applyAction(state: GameState, action: Action): Transition;
6
- /** Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade. */
30
+ /** Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade.
31
+ *
32
+ * @param state - The immutable native state to read or transition; the input is retained.
33
+ * @param ticks - The nonnegative number of fixed 60 Hz logical ticks to advance.
34
+ * @returns Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade.
35
+ * @example
36
+ * ```ts
37
+ * import { advanceTicks, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
38
+ *
39
+ * const state=createGame({seed:"docs"});
40
+ * console.log(advanceTicks(state,1).state.phase);
41
+ * ```
42
+ */
7
43
  export declare function advanceTicks(state: GameState, ticks: number): Transition;
8
- /** Lists accepted controls for the current falling or resolution phase. */
44
+ /** Lists accepted controls for the current falling or resolution phase.
45
+ *
46
+ * @param state - The immutable native state to read or transition; the input is retained.
47
+ * @returns Lists accepted controls for the current falling or resolution phase.
48
+ * @example
49
+ * ```ts
50
+ * import { createGame, legalActions } from "@johnmorrisdotca/houseki/magnetic-blocks";
51
+ *
52
+ * const state=createGame({seed:"docs"});
53
+ * console.log(legalActions(state).length);
54
+ * ```
55
+ */
9
56
  export declare function legalActions(state: GameState): readonly Action[];
10
- /** Returns a compact immutable status including current and upcoming floor state. */
57
+ /** Returns a compact immutable status including current and upcoming floor state.
58
+ *
59
+ * @param state - The immutable native state to read or transition; the input is retained.
60
+ * @returns Returns a compact immutable status including current and upcoming floor state.
61
+ * @example
62
+ * ```ts
63
+ * import { createGame, statusOf } from "@johnmorrisdotca/houseki/magnetic-blocks";
64
+ *
65
+ * const state=createGame({seed:"docs"});
66
+ * console.log(statusOf(state).phase);
67
+ * ```
68
+ */
11
69
  export declare function statusOf(state: GameState): GameStatus;
@@ -154,7 +154,17 @@ function core(state, action) {
154
154
  const reset = state.settings.mode === 'arcade' && grounded && active.lockResets < 8;
155
155
  return { state: { ...state, active: freezeBlock({ ...rotated, lockTicks: reset ? 0 : active.lockTicks, lockResets: active.lockResets + (reset ? 1 : 0), lockStarted: reset ? true : active.lockStarted }) }, events: [{ type: 'piece-rotated', orientation, ids: rotated.gems.map(gem => gem.id), cells: blockCells(rotated, width).map(item => item.index) }], accepted: true };
156
156
  }
157
- /** Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule. */
157
+ /** Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule.
158
+ *
159
+ * @param options - Validated native creation, authored challenge, or environment options described by this signature.
160
+ * @returns Creates an immutable seeded 2×2-block game with a fixed queue and explicit floor schedule.
161
+ * @example
162
+ * ```ts
163
+ * import { createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
164
+ *
165
+ * console.log(createGame({seed:"docs"}).phase);
166
+ * ```
167
+ */
158
168
  export function createGame(options = {}) {
159
169
  const valid = validateOptions(options);
160
170
  let randomState, queue;
@@ -175,7 +185,21 @@ export function createGame(options = {}) {
175
185
  const [started] = spawn(state);
176
186
  return freezeState(started);
177
187
  }
178
- /** Applies one movement, rotation, floor-switch or placement action without mutation. */
188
+ /** Applies one movement, rotation, floor-switch or placement action without mutation.
189
+ *
190
+ * @param state - The immutable native state to read or transition; the input is retained.
191
+ * @param action - One native action for this entry point; inspect accepted and reason on the result.
192
+ * @returns Applies one movement, rotation, floor-switch or placement action without mutation.
193
+ * @example
194
+ * ```ts
195
+ * import { applyAction, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
196
+ * import type { Action } from "@johnmorrisdotca/houseki/magnetic-blocks";
197
+ *
198
+ * const state=createGame({seed:"docs"});
199
+ * const action: Action={kind:"left"};
200
+ * console.log(applyAction(state,action).accepted);
201
+ * ```
202
+ */
179
203
  export function applyAction(state, action) {
180
204
  const result = core(state, action);
181
205
  if (!result.accepted)
@@ -186,7 +210,19 @@ export function applyAction(state, action) {
186
210
  const next = freezeState({ ...result.state, recording: Object.freeze([...state.recording, recorded]) });
187
211
  return { ...result, state: next };
188
212
  }
189
- /** Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade. */
213
+ /** Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade.
214
+ *
215
+ * @param state - The immutable native state to read or transition; the input is retained.
216
+ * @param ticks - The nonnegative number of fixed 60 Hz logical ticks to advance.
217
+ * @returns Advances fixed 60 Hz time, resolving phases in all modes and falling/locking only in Arcade.
218
+ * @example
219
+ * ```ts
220
+ * import { advanceTicks, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
221
+ *
222
+ * const state=createGame({seed:"docs"});
223
+ * console.log(advanceTicks(state,1).state.phase);
224
+ * ```
225
+ */
190
226
  export function advanceTicks(state, ticks) {
191
227
  if (!Number.isInteger(ticks) || ticks < 0 || ticks > 3600)
192
228
  throw new RangeError('ticks must be an integer from 0 to 3600');
@@ -245,7 +281,18 @@ export function advanceTicks(state, ticks) {
245
281
  }
246
282
  return { state: freezeState(current), events: Object.freeze(events), accepted: true };
247
283
  }
248
- /** Lists accepted controls for the current falling or resolution phase. */
284
+ /** Lists accepted controls for the current falling or resolution phase.
285
+ *
286
+ * @param state - The immutable native state to read or transition; the input is retained.
287
+ * @returns Lists accepted controls for the current falling or resolution phase.
288
+ * @example
289
+ * ```ts
290
+ * import { createGame, legalActions } from "@johnmorrisdotca/houseki/magnetic-blocks";
291
+ *
292
+ * const state=createGame({seed:"docs"});
293
+ * console.log(legalActions(state).length);
294
+ * ```
295
+ */
249
296
  export function legalActions(state) {
250
297
  if (state.phase === 'paused')
251
298
  return Object.freeze([{ kind: 'resume' }]);
@@ -261,5 +308,16 @@ export function legalActions(state) {
261
308
  ]);
262
309
  return Object.freeze([]);
263
310
  }
264
- /** Returns a compact immutable status including current and upcoming floor state. */
311
+ /** Returns a compact immutable status including current and upcoming floor state.
312
+ *
313
+ * @param state - The immutable native state to read or transition; the input is retained.
314
+ * @returns Returns a compact immutable status including current and upcoming floor state.
315
+ * @example
316
+ * ```ts
317
+ * import { createGame, statusOf } from "@johnmorrisdotca/houseki/magnetic-blocks";
318
+ *
319
+ * const state=createGame({seed:"docs"});
320
+ * console.log(statusOf(state).phase);
321
+ * ```
322
+ */
265
323
  export function statusOf(state) { return Object.freeze({ phase: state.phase, score: state.score, moves: state.moves, placements: state.placements, floor: state.pendingFloorOverride ?? state.floor, nextFloor: state.nextFloor, floorSwitchCharges: state.floorSwitchCharges, pendingFloorOverride: state.pendingFloorOverride, maxChain: state.maxChain, ...(state.reason ? { reason: state.reason } : {}) }); }
@@ -1,7 +1,40 @@
1
1
  import type { GameState } from './types.js';
2
- /** Restarts the same seed, custom starting board, fixed queue, rules and floor options. */
2
+ /** Restarts the same seed, custom starting board, fixed queue, rules and floor options.
3
+ *
4
+ * @param state - The immutable native state to read or transition; the input is retained.
5
+ * @returns Restarts the same seed, custom starting board, fixed queue, rules and floor options.
6
+ * @example
7
+ * ```ts
8
+ * import { createGame, restartGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
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 canonical settings, accepted tick-stamped actions and a verified replay checkpoint. */
15
+ /** Encodes canonical settings, accepted tick-stamped actions and a verified replay checkpoint.
16
+ *
17
+ * @param state - The immutable native state to read or transition; the input is retained.
18
+ * @returns Encodes canonical settings, accepted tick-stamped actions and a verified replay checkpoint.
19
+ * @example
20
+ * ```ts
21
+ * import { createGame, encodeGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
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
- /** Replays bounded canonical input and verifies every saved checkpoint field. */
28
+ /** Replays bounded canonical input and verifies every saved checkpoint field.
29
+ *
30
+ * @param text - A saved JSON recording from this entry’s encodeGame; malformed or inconsistent saves throw.
31
+ * @returns Replays bounded canonical input and verifies every saved checkpoint field.
32
+ * @example
33
+ * ```ts
34
+ * import { createGame, decodeGame, encodeGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
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;
@@ -68,9 +68,31 @@ function replay(settings, actions, elapsedTicks) {
68
68
  }
69
69
  return state;
70
70
  }
71
- /** Restarts the same seed, custom starting board, fixed queue, rules and floor options. */
71
+ /** Restarts the same seed, custom starting board, fixed queue, rules and floor options.
72
+ *
73
+ * @param state - The immutable native state to read or transition; the input is retained.
74
+ * @returns Restarts the same seed, custom starting board, fixed queue, rules and floor options.
75
+ * @example
76
+ * ```ts
77
+ * import { createGame, restartGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
78
+ *
79
+ * const state=createGame({seed:"docs"});
80
+ * console.log(restartGame(state).phase);
81
+ * ```
82
+ */
72
83
  export function restartGame(state) { return createFromSettings(state.settings); }
73
- /** Encodes canonical settings, accepted tick-stamped actions and a verified replay checkpoint. */
84
+ /** Encodes canonical settings, accepted tick-stamped actions and a verified replay checkpoint.
85
+ *
86
+ * @param state - The immutable native state to read or transition; the input is retained.
87
+ * @returns Encodes canonical settings, accepted tick-stamped actions and a verified replay checkpoint.
88
+ * @example
89
+ * ```ts
90
+ * import { createGame, encodeGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
91
+ *
92
+ * const state=createGame({seed:"docs"});
93
+ * console.log(encodeGame(state).length);
94
+ * ```
95
+ */
74
96
  export function encodeGame(state) {
75
97
  if (!Number.isSafeInteger(state.elapsedTicks) || state.elapsedTicks < 0 || state.elapsedTicks > MAX_TICKS || state.recording.length > MAX_ACTIONS)
76
98
  throw new RangeError('Run exceeds replay limits');
@@ -82,7 +104,18 @@ export function encodeGame(state) {
82
104
  throw new RangeError('Save exceeds 2 MiB');
83
105
  return text;
84
106
  }
85
- /** Replays bounded canonical input and verifies every saved checkpoint field. */
107
+ /** Replays bounded canonical input and verifies every saved checkpoint field.
108
+ *
109
+ * @param text - A saved JSON recording from this entry’s encodeGame; malformed or inconsistent saves throw.
110
+ * @returns Replays bounded canonical input and verifies every saved checkpoint field.
111
+ * @example
112
+ * ```ts
113
+ * import { createGame, decodeGame, encodeGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
114
+ *
115
+ * const state=createGame({seed:"docs"});
116
+ * console.log(decodeGame(encodeGame(state)).phase);
117
+ * ```
118
+ */
86
119
  export function decodeGame(text) {
87
120
  if (typeof text !== 'string' || new TextEncoder().encode(text).byteLength > MAX_SAVE_BYTES)
88
121
  throw new RangeError('Save must be text no larger than 2 MiB');
@@ -1,30 +1,200 @@
1
1
  import type { Block, Bond, Floor, Gem } from './types.js';
2
2
  export declare const HIDDEN_ROWS = 0;
3
- export declare const MARK_TICKS = 7, REMOVE_TICKS = 6, GRAVITY_TICKS = 9;
3
+ /** The fixed 60 Hz ticks showing the magnetic-block match mark phase.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { MARK_TICKS } from "@johnmorrisdotca/houseki/magnetic-blocks";
8
+ *
9
+ * console.log(MARK_TICKS);
10
+ * ```
11
+ */
12
+ export declare const MARK_TICKS = 7;
13
+ /** Fixed 60 Hz ticks in the magnetic-block removal phase.
14
+ * @example
15
+ * ```ts
16
+ * import { REMOVE_TICKS } from "@johnmorrisdotca/houseki/magnetic-blocks";
17
+ * console.log(REMOVE_TICKS);
18
+ * ```
19
+ */
20
+ export declare const REMOVE_TICKS = 6;
21
+ /** Fixed 60 Hz ticks in the magnetic-block gravity phase.
22
+ * @example
23
+ * ```ts
24
+ * import { GRAVITY_TICKS } from "@johnmorrisdotca/houseki/magnetic-blocks";
25
+ * console.log(GRAVITY_TICKS);
26
+ * ```
27
+ */
28
+ export declare const GRAVITY_TICKS = 9;
4
29
  export declare const cell: (x: number, y: number, width: number) => number;
5
- /** Returns stable-gem addresses after rotating the square block around its centre. */
30
+ /** Returns stable-gem addresses after rotating the square block around its centre.
31
+ *
32
+ * @param block - The native active 2×2 block and its stable gem ids.
33
+ * @param width - The native board width in cells.
34
+ * @returns Returns stable-gem addresses after rotating the square block around its centre.
35
+ * @example
36
+ * ```ts
37
+ * import { blockCells, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
38
+ *
39
+ * const state=createGame({seed:"docs"});
40
+ * console.log(blockCells(state.active!,state.settings.width).length);
41
+ * ```
42
+ */
6
43
  export declare function blockCells(block: Block, width: number): readonly {
7
44
  readonly index: number;
8
45
  readonly gem: Gem;
9
46
  }[];
47
+ /** Test whether the whole native block fits passable empty board cells.
48
+ *
49
+ * @param block - The native active 2×2 block and its stable gem ids.
50
+ * @param board - The row-major native board; null cells are empty or masked.
51
+ * @param width - The native board width in cells.
52
+ * @param height - The native board height in cells.
53
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
54
+ * @returns Test whether the whole native block fits passable empty board cells.
55
+ * @example
56
+ * ```ts
57
+ * import { canPlace, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
58
+ *
59
+ * const state=createGame({seed:"docs"});
60
+ * console.log(canPlace(state.active!,state.board,state.settings.width,state.settings.height,state.settings.mask));
61
+ * ```
62
+ */
10
63
  export declare function canPlace(block: Block, board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): boolean;
64
+ /** Find the last legal block row before downward collision.
65
+ *
66
+ * @param block - The native active 2×2 block and its stable gem ids.
67
+ * @param board - The row-major native board; null cells are empty or masked.
68
+ * @param width - The native board width in cells.
69
+ * @param height - The native board height in cells.
70
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
71
+ * @returns Find the last legal block row before downward collision.
72
+ * @example
73
+ * ```ts
74
+ * import { createGame, landingY } from "@johnmorrisdotca/houseki/magnetic-blocks";
75
+ *
76
+ * const state=createGame({seed:"docs"});
77
+ * console.log(landingY(state.active!,state.board,state.settings.width,state.settings.height,state.settings.mask));
78
+ * ```
79
+ */
11
80
  export declare function landingY(block: Block, board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): number;
12
- /** Computes deterministic orthogonal colour components of at least four gems. */
81
+ /** Computes deterministic orthogonal colour components of at least four gems.
82
+ *
83
+ * @param board - The row-major native board; null cells are empty or masked.
84
+ * @param width - The native board width in cells.
85
+ * @param height - The native board height in cells.
86
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
87
+ * @returns Computes deterministic orthogonal colour components of at least four gems.
88
+ * @example
89
+ * ```ts
90
+ * import { createGame, matchGroups } from "@johnmorrisdotca/houseki/magnetic-blocks";
91
+ *
92
+ * const state=createGame({seed:"docs"});
93
+ * console.log(matchGroups(state.board,state.settings.width,state.settings.height,state.settings.mask).length);
94
+ * ```
95
+ */
13
96
  export declare function matchGroups(board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): readonly (readonly number[])[];
14
97
  export declare function allMatchedCells(groups: readonly (readonly number[])[]): readonly number[];
98
+ /** Remove the selected native cell indices and discard bonds whose endpoint gems were removed.
99
+ *
100
+ * @param board - The row-major native board; null cells are empty or masked.
101
+ * @param bonds - Native stable-id bonds connecting surviving gems.
102
+ * @param indices - Row-major cell indices selected for removal.
103
+ * @returns Remove the selected native cell indices and discard bonds whose endpoint gems were removed. Nested null values denote empty board cells, absent active pieces, unselected tools or outcomes not yet reached; optional undefined fields are absent until applicable.
104
+ * @example
105
+ * ```ts
106
+ * import { createGame, removeIds } from "@johnmorrisdotca/houseki/magnetic-blocks";
107
+ *
108
+ * const state=createGame({seed:"docs"});
109
+ * console.log(removeIds(state.board,state.bonds,[]).ids.length);
110
+ * ```
111
+ */
15
112
  export declare function removeIds(board: readonly (Gem | null)[], bonds: readonly Bond[], indices: readonly number[]): {
16
113
  readonly board: readonly (Gem | null)[];
17
114
  readonly bonds: readonly Bond[];
18
115
  readonly ids: readonly number[];
19
116
  };
20
- /** Settles bonded connected components one cell per deterministic bottom/ID priority step. */
117
+ /** Settles bonded connected components one cell per deterministic bottom/ID priority step.
118
+ *
119
+ * @param initial - The row-major starting board for gravity settlement.
120
+ * @param bonds - Native stable-id bonds connecting surviving gems.
121
+ * @param width - The native board width in cells.
122
+ * @param height - The native board height in cells.
123
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
124
+ * @returns Settles bonded connected components one cell per deterministic bottom/ID priority step. Nested null values denote empty board cells, absent active pieces, unselected tools or outcomes not yet reached; optional undefined fields are absent until applicable.
125
+ * @example
126
+ * ```ts
127
+ * import { calmGravity, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
128
+ *
129
+ * const state=createGame({seed:"docs"});
130
+ * console.log(calmGravity(state.board,state.bonds,state.settings.width,state.settings.height,state.settings.mask).length);
131
+ * ```
132
+ */
21
133
  export declare function calmGravity(initial: readonly (Gem | null)[], bonds: readonly Bond[], width: number, height: number, mask: readonly boolean[]): readonly (Gem | null)[];
22
- /** Magnetic floor removes all bonds and settles each column segment independently. */
134
+ /** Magnetic floor removes all bonds and settles each column segment independently.
135
+ *
136
+ * @param initial - The row-major starting board for gravity settlement.
137
+ * @param width - The native board width in cells.
138
+ * @param height - The native board height in cells.
139
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
140
+ * @returns Magnetic floor removes all bonds and settles each column segment independently. Nested null values denote empty board cells, absent active pieces, unselected tools or outcomes not yet reached; optional undefined fields are absent until applicable.
141
+ * @example
142
+ * ```ts
143
+ * import { createGame, magneticGravity } from "@johnmorrisdotca/houseki/magnetic-blocks";
144
+ *
145
+ * const state=createGame({seed:"docs"});
146
+ * console.log(magneticGravity(state.board,state.settings.width,state.settings.height,state.settings.mask).length);
147
+ * ```
148
+ */
23
149
  export declare function magneticGravity(initial: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): readonly (Gem | null)[];
150
+ /** Settle surviving gems with the chosen floor rule and retain valid stable-id bonds.
151
+ *
152
+ * @param initial - The row-major starting board for gravity settlement.
153
+ * @param bonds - Native stable-id bonds connecting surviving gems.
154
+ * @param floor - The native calm or magnetic floor rule to apply.
155
+ * @param width - The native board width in cells.
156
+ * @param height - The native board height in cells.
157
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
158
+ * @returns Settle surviving gems with the chosen floor rule and retain valid stable-id bonds. Nested null values denote empty board cells, absent active pieces, unselected tools or outcomes not yet reached; optional undefined fields are absent until applicable.
159
+ * @example
160
+ * ```ts
161
+ * import { createGame, settle } from "@johnmorrisdotca/houseki/magnetic-blocks";
162
+ *
163
+ * const state=createGame({seed:"docs"});
164
+ * console.log(settle(state.board,state.bonds,"calm",state.settings.width,state.settings.height,state.settings.mask).board.length);
165
+ * ```
166
+ */
24
167
  export declare function settle(initial: readonly (Gem | null)[], bonds: readonly Bond[], floor: Floor, width: number, height: number, mask: readonly boolean[]): {
25
168
  readonly board: readonly (Gem | null)[];
26
169
  readonly bonds: readonly Bond[];
27
170
  };
28
- /** Collects the occupied one-cell support layer under both block columns at first contact. */
171
+ /** Collects the occupied one-cell support layer under both block columns at first contact.
172
+ *
173
+ * @param block - The native active 2×2 block and its stable gem ids.
174
+ * @param board - The row-major native board; null cells are empty or masked.
175
+ * @param width - The native board width in cells.
176
+ * @param height - The native board height in cells.
177
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
178
+ * @returns Collects the occupied one-cell support layer under both block columns at first contact.
179
+ * @example
180
+ * ```ts
181
+ * import { createGame, impactSupportIds } from "@johnmorrisdotca/houseki/magnetic-blocks";
182
+ *
183
+ * const state=createGame({seed:"docs"});
184
+ * console.log(impactSupportIds(state.active!,state.board,state.settings.width,state.settings.height,state.settings.mask).length);
185
+ * ```
186
+ */
29
187
  export declare function impactSupportIds(block: Block, board: readonly (Gem | null)[], width: number, height: number, mask: readonly boolean[]): readonly number[];
188
+ /** List the native stable-id bonds connecting adjacent gems in the active block.
189
+ *
190
+ * @param block - The native active 2×2 block and its stable gem ids.
191
+ * @returns List the native stable-id bonds connecting adjacent gems in the active block.
192
+ * @example
193
+ * ```ts
194
+ * import { blockBonds, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
195
+ *
196
+ * const state=createGame({seed:"docs"});
197
+ * console.log(blockBonds(state.active!).length);
198
+ * ```
199
+ */
30
200
  export declare function blockBonds(block: Block): readonly Bond[];