@johnmorrisdotca/houseki 0.4.0 → 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 (72) hide show
  1. package/CHANGELOG.md +29 -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 +65 -7
  35. package/dist/gem-swap/practice.js +2 -2
  36. package/dist/gem-swap/types.d.ts +336 -3
  37. package/dist/magnetic-blocks/content-types.d.ts +56 -0
  38. package/dist/magnetic-blocks/content.d.ts +91 -5
  39. package/dist/magnetic-blocks/content.js +91 -5
  40. package/dist/magnetic-blocks/engine.d.ts +63 -5
  41. package/dist/magnetic-blocks/engine.js +63 -5
  42. package/dist/magnetic-blocks/persistence.d.ts +36 -3
  43. package/dist/magnetic-blocks/persistence.js +36 -3
  44. package/dist/magnetic-blocks/physics.d.ts +176 -6
  45. package/dist/magnetic-blocks/physics.js +176 -6
  46. package/dist/magnetic-blocks/types.d.ts +185 -1
  47. package/dist/magnetic-blocks/validation.d.ts +12 -0
  48. package/dist/magnetic-blocks/validation.js +12 -0
  49. package/dist/nature/attraction.d.ts +24 -2
  50. package/dist/nature/attraction.js +24 -2
  51. package/dist/nature/environment.d.ts +200 -7
  52. package/dist/nature/environment.js +88 -7
  53. package/dist/nature/marking.d.ts +13 -1
  54. package/dist/nature/marking.js +13 -1
  55. package/dist/nature/rebound.d.ts +30 -2
  56. package/dist/nature/rebound.js +30 -2
  57. package/dist/nature/state.d.ts +11 -0
  58. package/dist/nature/state.js +11 -0
  59. package/dist/nature/types.d.ts +194 -1
  60. package/dist/nature/validation.d.ts +12 -0
  61. package/dist/nature/validation.js +12 -0
  62. package/dist/stone-collapse/content-types.d.ts +69 -0
  63. package/dist/stone-collapse/content.d.ts +60 -3
  64. package/dist/stone-collapse/content.js +60 -3
  65. package/dist/stone-collapse/engine.d.ts +99 -8
  66. package/dist/stone-collapse/engine.js +99 -8
  67. package/dist/stone-collapse/persistence.d.ts +36 -3
  68. package/dist/stone-collapse/persistence.js +36 -3
  69. package/dist/stone-collapse/types.d.ts +220 -3
  70. package/dist/stone-collapse/validation.d.ts +12 -1
  71. package/dist/stone-collapse/validation.js +12 -1
  72. package/package.json +34 -26
@@ -1,8 +1,45 @@
1
1
  export const HIDDEN_ROWS = 0;
2
- export const MARK_TICKS = 7, REMOVE_TICKS = 6, GRAVITY_TICKS = 9;
2
+ /** The fixed 60 Hz ticks showing the magnetic-block match mark phase.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { MARK_TICKS } from "@johnmorrisdotca/houseki/magnetic-blocks";
7
+ *
8
+ * console.log(MARK_TICKS);
9
+ * ```
10
+ */
11
+ export const MARK_TICKS = 7;
12
+ /** Fixed 60 Hz ticks in the magnetic-block removal phase.
13
+ * @example
14
+ * ```ts
15
+ * import { REMOVE_TICKS } from "@johnmorrisdotca/houseki/magnetic-blocks";
16
+ * console.log(REMOVE_TICKS);
17
+ * ```
18
+ */
19
+ export const REMOVE_TICKS = 6;
20
+ /** Fixed 60 Hz ticks in the magnetic-block gravity phase.
21
+ * @example
22
+ * ```ts
23
+ * import { GRAVITY_TICKS } from "@johnmorrisdotca/houseki/magnetic-blocks";
24
+ * console.log(GRAVITY_TICKS);
25
+ * ```
26
+ */
27
+ export const GRAVITY_TICKS = 9;
3
28
  export const cell = (x, y, width) => y * width + x;
4
29
  const BASE = [[0, 0], [1, 0], [1, 1], [0, 1]];
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 function blockCells(block, width) {
7
44
  return block.gems.map((gem, corner) => {
8
45
  let [x, y] = BASE[corner];
@@ -11,18 +48,64 @@ export function blockCells(block, width) {
11
48
  return { index: cell(block.x + x, block.y + y, width), gem };
12
49
  });
13
50
  }
51
+ /** Test whether the whole native block fits passable empty board cells.
52
+ *
53
+ * @param block - The native active 2×2 block and its stable gem ids.
54
+ * @param board - The row-major native board; null cells are empty or masked.
55
+ * @param width - The native board width in cells.
56
+ * @param height - The native board height in cells.
57
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
58
+ * @returns Test whether the whole native block fits passable empty board cells.
59
+ * @example
60
+ * ```ts
61
+ * import { canPlace, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
62
+ *
63
+ * const state=createGame({seed:"docs"});
64
+ * console.log(canPlace(state.active!,state.board,state.settings.width,state.settings.height,state.settings.mask));
65
+ * ```
66
+ */
14
67
  export function canPlace(block, board, width, height, mask) {
15
68
  if (block.x < 0 || block.x + 1 >= width || block.y < 0 || block.y + 1 >= height)
16
69
  return false;
17
70
  return blockCells(block, width).every(({ index }) => { const x = index % width, y = Math.floor(index / width); return x >= 0 && x < width && y >= 0 && y < height && mask[index] && !board[index]; });
18
71
  }
72
+ /** Find the last legal block row before downward collision.
73
+ *
74
+ * @param block - The native active 2×2 block and its stable gem ids.
75
+ * @param board - The row-major native board; null cells are empty or masked.
76
+ * @param width - The native board width in cells.
77
+ * @param height - The native board height in cells.
78
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
79
+ * @returns Find the last legal block row before downward collision.
80
+ * @example
81
+ * ```ts
82
+ * import { createGame, landingY } from "@johnmorrisdotca/houseki/magnetic-blocks";
83
+ *
84
+ * const state=createGame({seed:"docs"});
85
+ * console.log(landingY(state.active!,state.board,state.settings.width,state.settings.height,state.settings.mask));
86
+ * ```
87
+ */
19
88
  export function landingY(block, board, width, height, mask) {
20
89
  let y = block.y;
21
90
  while (canPlace({ ...block, y: y + 1 }, board, width, height, mask))
22
91
  y++;
23
92
  return y;
24
93
  }
25
- /** Computes deterministic orthogonal colour components of at least four gems. */
94
+ /** Computes deterministic orthogonal colour components of at least four gems.
95
+ *
96
+ * @param board - The row-major native board; null cells are empty or masked.
97
+ * @param width - The native board width in cells.
98
+ * @param height - The native board height in cells.
99
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
100
+ * @returns Computes deterministic orthogonal colour components of at least four gems.
101
+ * @example
102
+ * ```ts
103
+ * import { createGame, matchGroups } from "@johnmorrisdotca/houseki/magnetic-blocks";
104
+ *
105
+ * const state=createGame({seed:"docs"});
106
+ * console.log(matchGroups(state.board,state.settings.width,state.settings.height,state.settings.mask).length);
107
+ * ```
108
+ */
26
109
  export function matchGroups(board, width, height, mask) {
27
110
  const seen = new Set(), matches = [];
28
111
  for (let start = 0; start < board.length; start++) {
@@ -46,6 +129,20 @@ export function matchGroups(board, width, height, mask) {
46
129
  return matches;
47
130
  }
48
131
  export function allMatchedCells(groups) { return [...new Set(groups.flat())].sort((a, b) => a - b); }
132
+ /** Remove the selected native cell indices and discard bonds whose endpoint gems were removed.
133
+ *
134
+ * @param board - The row-major native board; null cells are empty or masked.
135
+ * @param bonds - Native stable-id bonds connecting surviving gems.
136
+ * @param indices - Row-major cell indices selected for removal.
137
+ * @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.
138
+ * @example
139
+ * ```ts
140
+ * import { createGame, removeIds } from "@johnmorrisdotca/houseki/magnetic-blocks";
141
+ *
142
+ * const state=createGame({seed:"docs"});
143
+ * console.log(removeIds(state.board,state.bonds,[]).ids.length);
144
+ * ```
145
+ */
49
146
  export function removeIds(board, bonds, indices) {
50
147
  const removed = new Set(indices), ids = indices.flatMap(index => board[index] ? [board[index].id] : []), remaining = board.map((gem, index) => removed.has(index) ? null : gem), alive = new Set(remaining.flatMap(gem => gem ? [gem.id] : []));
51
148
  return { board: Object.freeze(remaining), bonds: Object.freeze(bonds.filter(edge => alive.has(edge.a) && alive.has(edge.b))), ids: Object.freeze(ids) };
@@ -81,7 +178,22 @@ function connectedComponents(board, bonds) {
81
178
  }
82
179
  return groups;
83
180
  }
84
- /** Settles bonded connected components one cell per deterministic bottom/ID priority step. */
181
+ /** Settles bonded connected components one cell per deterministic bottom/ID priority step.
182
+ *
183
+ * @param initial - The row-major starting board for gravity settlement.
184
+ * @param bonds - Native stable-id bonds connecting surviving gems.
185
+ * @param width - The native board width in cells.
186
+ * @param height - The native board height in cells.
187
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
188
+ * @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.
189
+ * @example
190
+ * ```ts
191
+ * import { calmGravity, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
192
+ *
193
+ * const state=createGame({seed:"docs"});
194
+ * console.log(calmGravity(state.board,state.bonds,state.settings.width,state.settings.height,state.settings.mask).length);
195
+ * ```
196
+ */
85
197
  export function calmGravity(initial, bonds, width, height, mask) {
86
198
  let board = [...initial], moved = true, guard = width * height * height;
87
199
  while (moved && guard-- > 0) {
@@ -106,7 +218,21 @@ export function calmGravity(initial, bonds, width, height, mask) {
106
218
  throw new RangeError('Calm gravity did not converge within its board-derived step bound');
107
219
  return Object.freeze(board);
108
220
  }
109
- /** Magnetic floor removes all bonds and settles each column segment independently. */
221
+ /** Magnetic floor removes all bonds and settles each column segment independently.
222
+ *
223
+ * @param initial - The row-major starting board for gravity settlement.
224
+ * @param width - The native board width in cells.
225
+ * @param height - The native board height in cells.
226
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
227
+ * @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.
228
+ * @example
229
+ * ```ts
230
+ * import { createGame, magneticGravity } from "@johnmorrisdotca/houseki/magnetic-blocks";
231
+ *
232
+ * const state=createGame({seed:"docs"});
233
+ * console.log(magneticGravity(state.board,state.settings.width,state.settings.height,state.settings.mask).length);
234
+ * ```
235
+ */
110
236
  export function magneticGravity(initial, width, height, mask) {
111
237
  const board = [...initial];
112
238
  for (let x = 0; x < width; x++) {
@@ -131,10 +257,42 @@ export function magneticGravity(initial, width, height, mask) {
131
257
  }
132
258
  return Object.freeze(board);
133
259
  }
260
+ /** Settle surviving gems with the chosen floor rule and retain valid stable-id bonds.
261
+ *
262
+ * @param initial - The row-major starting board for gravity settlement.
263
+ * @param bonds - Native stable-id bonds connecting surviving gems.
264
+ * @param floor - The native calm or magnetic floor rule to apply.
265
+ * @param width - The native board width in cells.
266
+ * @param height - The native board height in cells.
267
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
268
+ * @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.
269
+ * @example
270
+ * ```ts
271
+ * import { createGame, settle } from "@johnmorrisdotca/houseki/magnetic-blocks";
272
+ *
273
+ * const state=createGame({seed:"docs"});
274
+ * console.log(settle(state.board,state.bonds,"calm",state.settings.width,state.settings.height,state.settings.mask).board.length);
275
+ * ```
276
+ */
134
277
  export function settle(initial, bonds, floor, width, height, mask) {
135
278
  return floor === 'magnetic' ? { board: magneticGravity(initial, width, height, mask), bonds: Object.freeze([]) } : { board: calmGravity(initial, bonds, width, height, mask), bonds };
136
279
  }
137
- /** Collects the occupied one-cell support layer under both block columns at first contact. */
280
+ /** Collects the occupied one-cell support layer under both block columns at first contact.
281
+ *
282
+ * @param block - The native active 2×2 block and its stable gem ids.
283
+ * @param board - The row-major native board; null cells are empty or masked.
284
+ * @param width - The native board width in cells.
285
+ * @param height - The native board height in cells.
286
+ * @param mask - The row-major active-cell mask; false cells cannot hold gems.
287
+ * @returns Collects the occupied one-cell support layer under both block columns at first contact.
288
+ * @example
289
+ * ```ts
290
+ * import { createGame, impactSupportIds } from "@johnmorrisdotca/houseki/magnetic-blocks";
291
+ *
292
+ * const state=createGame({seed:"docs"});
293
+ * console.log(impactSupportIds(state.active!,state.board,state.settings.width,state.settings.height,state.settings.mask).length);
294
+ * ```
295
+ */
138
296
  export function impactSupportIds(block, board, width, height, mask) {
139
297
  const byColumn = new Map();
140
298
  for (const { index } of blockCells(block, width)) {
@@ -152,4 +310,16 @@ export function impactSupportIds(block, board, width, height, mask) {
152
310
  }
153
311
  return Object.freeze([...ids].sort((a, b) => a - b));
154
312
  }
313
+ /** List the native stable-id bonds connecting adjacent gems in the active block.
314
+ *
315
+ * @param block - The native active 2×2 block and its stable gem ids.
316
+ * @returns List the native stable-id bonds connecting adjacent gems in the active block.
317
+ * @example
318
+ * ```ts
319
+ * import { blockBonds, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
320
+ *
321
+ * const state=createGame({seed:"docs"});
322
+ * console.log(blockBonds(state.active!).length);
323
+ * ```
324
+ */
155
325
  export function blockBonds(block) { return Object.freeze([[0, 1], [1, 2], [2, 3], [3, 0]].map(([a, b]) => { const ids = [block.gems[a].id, block.gems[b].id].sort((x, y) => x - y); return Object.freeze({ a: ids[0], b: ids[1] }); }).sort((a, b) => a.a - b.a || a.b - b.b)); }
@@ -1,6 +1,35 @@
1
- /** A standard gem colour used by a magnetic block. */
1
+ /** A standard gem colour used by a magnetic block.
2
+ *
3
+ * @example
4
+ * ```ts
5
+ * import type { Colour } from "@johnmorrisdotca/houseki/magnetic-blocks";
6
+ *
7
+ * const value: Colour = "red";
8
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
9
+ * ```
10
+ */
2
11
  export type Colour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
12
+ /** The native calm or magnetic floor used when settling blocks.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import type { Floor } from "@johnmorrisdotca/houseki/magnetic-blocks";
17
+ *
18
+ * const value: Floor = "calm";
19
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
20
+ * ```
21
+ */
3
22
  export type Floor = 'calm' | 'magnetic';
23
+ /** A fixed, periodic or authored schedule selecting the floor for committed block placements.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import type { FloorSchedule } from "@johnmorrisdotca/houseki/magnetic-blocks";
28
+ *
29
+ * const value: FloorSchedule = {kind:"fixed",floor:"calm"};
30
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
31
+ * ```
32
+ */
4
33
  export type FloorSchedule = {
5
34
  readonly kind: 'frequent';
6
35
  } | {
@@ -18,16 +47,70 @@ export type FloorSchedule = {
18
47
  readonly magneticPlacements: readonly number[];
19
48
  readonly defaultFloor?: Floor;
20
49
  };
50
+ /** The native game mode; authored challenges have fixed setup and queue rules.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import type { Mode } from "@johnmorrisdotca/houseki/magnetic-blocks";
55
+ *
56
+ * const value: Mode = "relaxed";
57
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
58
+ * ```
59
+ */
21
60
  export type Mode = 'relaxed' | 'arcade';
61
+ /** The native falling, resolution, pause and ending phases.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
66
+ * import type { Phase } from "@johnmorrisdotca/houseki/magnetic-blocks";
67
+ *
68
+ * const state=createGame({seed:"docs"});
69
+ * const value: Phase = state.phase;
70
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
71
+ * ```
72
+ */
22
73
  export type Phase = 'falling' | 'clear-mark' | 'clear-remove' | 'gravity' | 'paused' | 'won' | 'lost' | 'finished';
74
+ /** A native gem with a stable run-local id and colour.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * import type { Gem } from "@johnmorrisdotca/houseki/magnetic-blocks";
79
+ *
80
+ * const value: Gem = {id:1,colour:"red"};
81
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
82
+ * ```
83
+ */
23
84
  export interface Gem {
24
85
  readonly id: number;
25
86
  readonly colour: Colour;
26
87
  }
88
+ /** An undirected stable-id connection between two gems in a magnetic block.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * import type { Bond } from "@johnmorrisdotca/houseki/magnetic-blocks";
93
+ *
94
+ * const value: Bond = {a:1,b:2};
95
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
96
+ * ```
97
+ */
27
98
  export interface Bond {
28
99
  readonly a: number;
29
100
  readonly b: number;
30
101
  }
102
+ /** The native active 2×2 block, orientation and fixed-tick descent/lock counters.
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * import { createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
107
+ * import type { Block } from "@johnmorrisdotca/houseki/magnetic-blocks";
108
+ *
109
+ * const state=createGame({seed:"docs"});
110
+ * const value: Block = state.active!;
111
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
112
+ * ```
113
+ */
31
114
  export interface Block {
32
115
  readonly x: number;
33
116
  readonly y: number;
@@ -38,12 +121,32 @@ export interface Block {
38
121
  readonly lockResets: number;
39
122
  readonly lockStarted: boolean;
40
123
  }
124
+ /** The native authored goal; its discriminant selects the required collection or clearing condition.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * import type { Goal } from "@johnmorrisdotca/houseki/magnetic-blocks";
129
+ *
130
+ * const value: Goal = {kind:"clear-all"};
131
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
132
+ * ```
133
+ */
41
134
  export type Goal = {
42
135
  readonly kind: 'clear-all';
43
136
  } | {
44
137
  readonly kind: 'clear-targets';
45
138
  readonly targetIds: readonly number[];
46
139
  };
140
+ /** Creation settings for this native engine; dimensions, mode, seed and optional mechanics are validated together.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import type { CreateOptions } from "@johnmorrisdotca/houseki/magnetic-blocks";
145
+ *
146
+ * const value: CreateOptions = {seed:"docs"};
147
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
148
+ * ```
149
+ */
47
150
  export interface CreateOptions {
48
151
  readonly mode?: Mode;
49
152
  readonly width?: number;
@@ -60,6 +163,18 @@ export interface CreateOptions {
60
163
  readonly magneticImpact?: boolean;
61
164
  readonly goal?: Goal;
62
165
  }
166
+ /** Validated settings fixed at native game creation.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * import { createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
171
+ * import type { Settings } from "@johnmorrisdotca/houseki/magnetic-blocks";
172
+ *
173
+ * const state=createGame({seed:"docs"});
174
+ * const value: Settings = state.settings;
175
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
176
+ * ```
177
+ */
63
178
  export interface Settings {
64
179
  readonly mode: Mode;
65
180
  readonly width: number;
@@ -76,6 +191,18 @@ export interface Settings {
76
191
  readonly initialQueue?: readonly (readonly [Colour, Colour, Colour, Colour])[];
77
192
  readonly goal?: Goal;
78
193
  }
194
+ /** The immutable authoritative native game state, including its board, random state and recording.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * import { createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
199
+ * import type { GameState } from "@johnmorrisdotca/houseki/magnetic-blocks";
200
+ *
201
+ * const state=createGame({seed:"docs"});
202
+ * const value: GameState = state;
203
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
204
+ * ```
205
+ */
79
206
  export interface GameState {
80
207
  readonly game: 'magnetic-blocks';
81
208
  readonly rules: 'magnetic-blocks-1';
@@ -105,6 +232,17 @@ export interface GameState {
105
232
  readonly recording: readonly RecordedAction[];
106
233
  readonly reason?: string;
107
234
  }
235
+ /** A native player action accepted or rejected according to the current phase and rules.
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * import type { Action } from "@johnmorrisdotca/houseki/magnetic-blocks";
240
+ *
241
+ * const action: Action={kind:"left"};
242
+ * const value: Action = action;
243
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
244
+ * ```
245
+ */
108
246
  export type Action = {
109
247
  readonly kind: 'left';
110
248
  } | {
@@ -129,21 +267,67 @@ export type Action = {
129
267
  } | {
130
268
  readonly kind: 'cancel-floor-override';
131
269
  };
270
+ /** A native action recording with the logical position needed for deterministic replay.
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * import type { Action, RecordedAction } from "@johnmorrisdotca/houseki/magnetic-blocks";
275
+ *
276
+ * const action: Action={kind:"left"};
277
+ * const value: RecordedAction = {tick:0,ordinal:0,action};
278
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
279
+ * ```
280
+ */
132
281
  export interface RecordedAction {
133
282
  readonly tick: number;
134
283
  readonly ordinal: number;
135
284
  readonly action: Action;
136
285
  }
286
+ /** An event emitted by one native transition; type selects the event’s additional fields.
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * import type { GameEvent } from "@johnmorrisdotca/houseki/magnetic-blocks";
291
+ *
292
+ * const value: GameEvent = {type:"host-example"};
293
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
294
+ * ```
295
+ */
137
296
  export interface GameEvent {
138
297
  readonly type: string;
139
298
  readonly [key: string]: unknown;
140
299
  }
300
+ /** The resulting native state, emitted events and acceptance decision; rejected actions preserve the input.
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * import { applyAction, createGame } from "@johnmorrisdotca/houseki/magnetic-blocks";
305
+ * import type { Action, Transition } from "@johnmorrisdotca/houseki/magnetic-blocks";
306
+ *
307
+ * const state=createGame({seed:"docs"});
308
+ * const action: Action={kind:"left"};
309
+ * const value: Transition = applyAction(state,action);
310
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
311
+ * ```
312
+ */
141
313
  export interface Transition {
142
314
  readonly state: GameState;
143
315
  readonly events: readonly GameEvent[];
144
316
  readonly accepted: boolean;
145
317
  readonly reason?: string;
146
318
  }
319
+ /** A compact native status derived from the authoritative state for a host’s labels.
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * import { createGame, statusOf } from "@johnmorrisdotca/houseki/magnetic-blocks";
324
+ * import type { GameStatus } from "@johnmorrisdotca/houseki/magnetic-blocks";
325
+ *
326
+ * const state=createGame({seed:"docs"});
327
+ * const value: GameStatus = statusOf(state);
328
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
329
+ * ```
330
+ */
147
331
  export interface GameStatus {
148
332
  readonly phase: Phase;
149
333
  readonly score: number;
@@ -1,4 +1,16 @@
1
1
  import type { Bond, Colour, CreateOptions, FloorSchedule, Gem, Goal, Mode } from './types.js';
2
+ /** A typed native magnetic-block setup validation error.
3
+ *
4
+ * @param code - The native stable error code used by callers to distinguish setup or replay failures.
5
+ * @param message - The human-readable diagnostic accompanying the native code.
6
+ * @example
7
+ * ```ts
8
+ * import { MagneticBlocksOptionsError } from "@johnmorrisdotca/houseki/magnetic-blocks";
9
+ *
10
+ * const error=new MagneticBlocksOptionsError("docs-example","Invalid example setup");
11
+ * console.log(error.name,error.code,error.message);
12
+ * ```
13
+ */
2
14
  export declare class MagneticBlocksOptionsError extends RangeError {
3
15
  readonly code: string;
4
16
  constructor(code: string, message: string);
@@ -1,4 +1,16 @@
1
1
  import { calmGravity } from './physics.js';
2
+ /** A typed native magnetic-block setup validation error.
3
+ *
4
+ * @param code - The native stable error code used by callers to distinguish setup or replay failures.
5
+ * @param message - The human-readable diagnostic accompanying the native code.
6
+ * @example
7
+ * ```ts
8
+ * import { MagneticBlocksOptionsError } from "@johnmorrisdotca/houseki/magnetic-blocks";
9
+ *
10
+ * const error=new MagneticBlocksOptionsError("docs-example","Invalid example setup");
11
+ * console.log(error.name,error.code,error.message);
12
+ * ```
13
+ */
2
14
  export class MagneticBlocksOptionsError extends RangeError {
3
15
  code;
4
16
  constructor(code, message) { super(message); this.name = 'MagneticBlocksOptionsError'; this.code = code; }
@@ -1,5 +1,27 @@
1
1
  import type { MagneticPreview, NatureState, NatureTransition } from './types.js';
2
- /** Preview one bounded deterministic attraction pulse without mutating the state. */
2
+ /** Preview one bounded deterministic attraction pulse without mutating the state.
3
+ *
4
+ * @param state - The immutable native state to read or transition; the input is retained.
5
+ * @returns Preview one bounded deterministic attraction pulse without mutating the state.
6
+ * @example
7
+ * ```ts
8
+ * import { createNatureState, previewMagneticPulse } from "@johnmorrisdotca/houseki/nature";
9
+ *
10
+ * const state=createNatureState({width:4,height:4,seed:"docs"});
11
+ * console.log(previewMagneticPulse(state).moves.length);
12
+ * ```
13
+ */
3
14
  export declare function previewMagneticPulse(state: NatureState): MagneticPreview;
4
- /** Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state. */
15
+ /** Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state.
16
+ *
17
+ * @param state - The immutable native state to read or transition; the input is retained.
18
+ * @returns Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state.
19
+ * @example
20
+ * ```ts
21
+ * import { applyMagneticPulse, createNatureState } from "@johnmorrisdotca/houseki/nature";
22
+ *
23
+ * const state=createNatureState({width:4,height:4,seed:"docs"});
24
+ * console.log(applyMagneticPulse(state).accepted);
25
+ * ```
26
+ */
5
27
  export declare function applyMagneticPulse(state: NatureState): NatureTransition;
@@ -116,7 +116,18 @@ function movePair(board, state, pair) {
116
116
  const a = coordinates(aIndex, state.width), b = coordinates(bIndex, state.width);
117
117
  return deepFreeze({ ids, axis: pair.axis, emptyGap: pair.gap, midpoint: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }, ...(pair.a.anchored || pair.b.anchored ? { anchoredId: pair.a.anchored ? pair.a.id : pair.b.id } : {}), moves: localMoves });
118
118
  }
119
- /** Preview one bounded deterministic attraction pulse without mutating the state. */
119
+ /** Preview one bounded deterministic attraction pulse without mutating the state.
120
+ *
121
+ * @param state - The immutable native state to read or transition; the input is retained.
122
+ * @returns Preview one bounded deterministic attraction pulse without mutating the state.
123
+ * @example
124
+ * ```ts
125
+ * import { createNatureState, previewMagneticPulse } from "@johnmorrisdotca/houseki/nature";
126
+ *
127
+ * const state=createNatureState({width:4,height:4,seed:"docs"});
128
+ * console.log(previewMagneticPulse(state).moves.length);
129
+ * ```
130
+ */
120
131
  export function previewMagneticPulse(state) {
121
132
  validateState(state);
122
133
  if (!state.rules.magneticEnabled)
@@ -126,7 +137,18 @@ export function previewMagneticPulse(state) {
126
137
  const moves = pairs.flatMap(pair => pair.moves);
127
138
  return deepFreeze({ pairs, moves, cells: board });
128
139
  }
129
- /** Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state. */
140
+ /** Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state.
141
+ *
142
+ * @param state - The immutable native state to read or transition; the input is retained.
143
+ * @returns Apply one attraction pulse. Disabled nature rules preserve the exact state reference and consume no random state.
144
+ * @example
145
+ * ```ts
146
+ * import { applyMagneticPulse, createNatureState } from "@johnmorrisdotca/houseki/nature";
147
+ *
148
+ * const state=createNatureState({width:4,height:4,seed:"docs"});
149
+ * console.log(applyMagneticPulse(state).accepted);
150
+ * ```
151
+ */
130
152
  export function applyMagneticPulse(state) {
131
153
  const preview = previewMagneticPulse(state);
132
154
  if (!state.rules.magneticEnabled || preview.moves.length === 0)