@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,9 +1,40 @@
1
1
  import type { Colour } from './types.js';
2
- /** Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state. */
2
+ /** Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state.
3
+ *
4
+ * @param seed - A string seed hashed into a nonzero deterministic random state.
5
+ * @returns Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state.
6
+ * @example
7
+ * ```ts
8
+ * import { seedState } from "@johnmorrisdotca/houseki/falling-triplets";
9
+ *
10
+ * console.log(seedState("docs"));
11
+ * ```
12
+ */
3
13
  export declare function seedState(seed: string): number;
4
- /** Advances the reference xorshift32 source. */
14
+ /** Advances the reference xorshift32 source.
15
+ *
16
+ * @param state - The immutable native state to read or transition; the input is retained.
17
+ * @returns Advances the reference xorshift32 source.
18
+ * @example
19
+ * ```ts
20
+ * import { nextUint32, seedState } from "@johnmorrisdotca/houseki/falling-triplets";
21
+ *
22
+ * console.log(nextUint32(seedState("docs")));
23
+ * ```
24
+ */
5
25
  export declare function nextUint32(state: number): readonly [number, number];
6
- /** Draws an unbiased integer and updated state using rejection sampling. */
26
+ /** Draws an unbiased integer and updated state using rejection sampling.
27
+ *
28
+ * @param state - The immutable native state to read or transition; the input is retained.
29
+ * @param n - The positive upper bound of the integer draw; the result is below this bound.
30
+ * @returns Draws an unbiased integer and updated state using rejection sampling.
31
+ * @example
32
+ * ```ts
33
+ * import { nextInt, seedState } from "@johnmorrisdotca/houseki/falling-triplets";
34
+ *
35
+ * console.log(nextInt(seedState("docs"),6));
36
+ * ```
37
+ */
7
38
  export declare function nextInt(state: number, n: number): readonly [number, number];
8
39
  /** Creates a shuffled bag with three of each available colour. */
9
40
  export declare function drawBag(bag: readonly Colour[], state: number, count: 4 | 5 | 6): readonly [readonly Colour[], number];
@@ -1,12 +1,32 @@
1
1
  const COLOURS = ['red', 'blue', 'green', 'gold', 'purple', 'teal'];
2
- /** Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state. */
2
+ /** Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state.
3
+ *
4
+ * @param seed - A string seed hashed into a nonzero deterministic random state.
5
+ * @returns Hashes a string seed with UTF-8 FNV-1a and returns a nonzero uint32 state.
6
+ * @example
7
+ * ```ts
8
+ * import { seedState } from "@johnmorrisdotca/houseki/falling-triplets";
9
+ *
10
+ * console.log(seedState("docs"));
11
+ * ```
12
+ */
3
13
  export function seedState(seed) {
4
14
  let hash = 0x811c9dc5;
5
15
  for (const byte of new TextEncoder().encode(seed))
6
16
  hash = Math.imul(hash ^ byte, 0x01000193) >>> 0;
7
17
  return hash || 0x6d2b79f5;
8
18
  }
9
- /** Advances the reference xorshift32 source. */
19
+ /** Advances the reference xorshift32 source.
20
+ *
21
+ * @param state - The immutable native state to read or transition; the input is retained.
22
+ * @returns Advances the reference xorshift32 source.
23
+ * @example
24
+ * ```ts
25
+ * import { nextUint32, seedState } from "@johnmorrisdotca/houseki/falling-triplets";
26
+ *
27
+ * console.log(nextUint32(seedState("docs")));
28
+ * ```
29
+ */
10
30
  export function nextUint32(state) {
11
31
  let next = state >>> 0;
12
32
  next ^= next << 13;
@@ -15,7 +35,18 @@ export function nextUint32(state) {
15
35
  next >>>= 0;
16
36
  return [next, next];
17
37
  }
18
- /** Draws an unbiased integer and updated state using rejection sampling. */
38
+ /** Draws an unbiased integer and updated state using rejection sampling.
39
+ *
40
+ * @param state - The immutable native state to read or transition; the input is retained.
41
+ * @param n - The positive upper bound of the integer draw; the result is below this bound.
42
+ * @returns Draws an unbiased integer and updated state using rejection sampling.
43
+ * @example
44
+ * ```ts
45
+ * import { nextInt, seedState } from "@johnmorrisdotca/houseki/falling-triplets";
46
+ *
47
+ * console.log(nextInt(seedState("docs"),6));
48
+ * ```
49
+ */
19
50
  export function nextInt(state, n) {
20
51
  if (!Number.isInteger(n) || n < 1 || n > 0x100000000)
21
52
  throw new RangeError('n must be an integer from 1 to 2^32');
@@ -1,13 +1,65 @@
1
- /** A gem colour ID. */
1
+ /** A gem colour ID.
2
+ *
3
+ * @example
4
+ * ```ts
5
+ * import type { Colour } from "@johnmorrisdotca/houseki/falling-triplets";
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';
3
- /** A settled or active gem with a stable run-local identity. */
12
+ /** A settled or active gem with a stable run-local identity.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import type { Gem } from "@johnmorrisdotca/houseki/falling-triplets";
17
+ *
18
+ * const value: Gem = {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 Gem {
5
23
  readonly id: number;
6
24
  readonly colour: Colour;
7
25
  readonly target?: boolean;
8
26
  }
27
+ /** The native game mode; authored challenges have fixed setup and queue rules.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import type { Mode } from "@johnmorrisdotca/houseki/falling-triplets";
32
+ *
33
+ * const value: Mode = "relaxed";
34
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
35
+ * ```
36
+ */
9
37
  export type Mode = 'relaxed' | 'arcade' | 'daily' | 'challenge';
38
+ /** The native falling, resolution, pause and ending phases.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
43
+ * import type { Phase } from "@johnmorrisdotca/houseki/falling-triplets";
44
+ *
45
+ * const state=createGame({seed:"docs"});
46
+ * const value: Phase = state.phase;
47
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
48
+ * ```
49
+ */
10
50
  export type Phase = 'falling' | 'clear-mark' | 'clear-remove' | 'gravity' | 'paused' | 'won' | 'lost' | 'finished';
51
+ /** The native falling triplet with stable gem ids, orientation, descent and lock counters.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
56
+ * import type { Piece } from "@johnmorrisdotca/houseki/falling-triplets";
57
+ *
58
+ * const state=createGame({seed:"docs"});
59
+ * const value: Piece = state.active!;
60
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
61
+ * ```
62
+ */
11
63
  export interface Piece {
12
64
  readonly gems: readonly [Gem, Gem, Gem];
13
65
  readonly x: number;
@@ -19,6 +71,18 @@ export interface Piece {
19
71
  readonly lockResets: number;
20
72
  readonly lockStarted: boolean;
21
73
  }
74
+ /** Validated settings fixed at native game creation.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
79
+ * import type { Settings } from "@johnmorrisdotca/houseki/falling-triplets";
80
+ *
81
+ * const state=createGame({seed:"docs"});
82
+ * const value: Settings = state.settings;
83
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
84
+ * ```
85
+ */
22
86
  export interface Settings {
23
87
  readonly mode: Mode;
24
88
  readonly width: number;
@@ -33,35 +97,129 @@ export interface Settings {
33
97
  readonly challengeQueue?: readonly Triplet[];
34
98
  readonly witness?: readonly ChallengeHint[];
35
99
  }
100
+ /** Three ordered gem colours composing one falling triplet.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * import type { Triplet } from "@johnmorrisdotca/houseki/falling-triplets";
105
+ *
106
+ * const value: Triplet = ["red","green","blue"];
107
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
108
+ * ```
109
+ */
36
110
  export type Triplet = readonly [Colour, Colour, Colour];
111
+ /** A Falling Triplets objective requiring the supplied stable target ids to be cleared.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * import type { ChallengeGoalTargets } from "@johnmorrisdotca/houseki/falling-triplets";
116
+ *
117
+ * const value: ChallengeGoalTargets = {type:"targets",targetIds:[1]};
118
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
119
+ * ```
120
+ */
37
121
  export interface ChallengeGoalTargets {
38
122
  readonly type: 'targets';
39
123
  readonly targetIds: readonly number[];
40
124
  }
125
+ /** A Falling Triplets objective requiring at least the supplied chain depth.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * import type { ChallengeGoalChain } from "@johnmorrisdotca/houseki/falling-triplets";
130
+ *
131
+ * const value: ChallengeGoalChain = {type:"chain",minimumChain:2};
132
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
133
+ * ```
134
+ */
41
135
  export interface ChallengeGoalChain {
42
136
  readonly type: 'chain';
43
137
  readonly minimumChain: number;
44
138
  }
139
+ /** A Falling Triplets objective requiring an empty board.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * import type { ChallengeGoalEmpty } from "@johnmorrisdotca/houseki/falling-triplets";
144
+ *
145
+ * const value: ChallengeGoalEmpty = {type:"empty"};
146
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
147
+ * ```
148
+ */
45
149
  export interface ChallengeGoalEmpty {
46
150
  readonly type: 'empty';
47
151
  }
152
+ /** A native authored challenge objective; its discriminant selects target, chain, empty-board or score requirements.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * import type { ChallengeGoal } from "@johnmorrisdotca/houseki/falling-triplets";
157
+ *
158
+ * const value: ChallengeGoal = {type:"empty"};
159
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
160
+ * ```
161
+ */
48
162
  export type ChallengeGoal = ChallengeGoalTargets | ChallengeGoalChain | ChallengeGoalEmpty;
163
+ /** One witnessed Falling Triplets placement with a column and cycle orientation.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * import type { ChallengeHint } from "@johnmorrisdotca/houseki/falling-triplets";
168
+ *
169
+ * const value: ChallengeHint = {x:0,orientation:0};
170
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
171
+ * ```
172
+ */
49
173
  export interface ChallengeHint {
50
174
  readonly x: number;
51
175
  readonly orientation: 0 | 1 | 2;
52
176
  }
177
+ /** The fixed Falling Triplets challenge settings with the original board and finite triplet queue.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
182
+ * import type { ChallengeSettings } from "@johnmorrisdotca/houseki/falling-triplets";
183
+ *
184
+ * const state=createGame({seed:"docs"});
185
+ * const value: ChallengeSettings = {...state.settings,mode:"challenge",goal:"empty",challengeBoard:state.board,challengeQueue:[["red","green","blue"]]};
186
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
187
+ * ```
188
+ */
53
189
  export interface ChallengeSettings extends Settings {
54
190
  readonly mode: 'challenge';
55
191
  readonly goal: ChallengeGoal['type'];
56
192
  readonly challengeBoard: readonly (Gem | null)[];
57
193
  readonly challengeQueue: readonly Triplet[];
58
194
  }
195
+ /** One simultaneous native match wave, its cleared cells and chain score.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * import type { Wave } from "@johnmorrisdotca/houseki/falling-triplets";
200
+ *
201
+ * const value: Wave = {chain:1,cells:[],points:0,clearedIds:[]};
202
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
203
+ * ```
204
+ */
59
205
  export interface Wave {
60
206
  readonly clearedIds: readonly number[];
61
207
  readonly cells: readonly number[];
62
208
  readonly chain: number;
63
209
  readonly points: number;
64
210
  }
211
+ /** The immutable authoritative native game state, including its board, random state and recording.
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
216
+ * import type { GameState } from "@johnmorrisdotca/houseki/falling-triplets";
217
+ *
218
+ * const state=createGame({seed:"docs"});
219
+ * const value: GameState = state;
220
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
221
+ * ```
222
+ */
65
223
  export interface GameState {
66
224
  readonly game: 'falling-triplets';
67
225
  readonly rules: 'triplets-1';
@@ -92,16 +250,50 @@ export interface GameState {
92
250
  readonly recording: readonly RecordedAction[];
93
251
  readonly reason?: string;
94
252
  }
253
+ /** A native player action accepted or rejected according to the current phase and rules.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * import type { Action } from "@johnmorrisdotca/houseki/falling-triplets";
258
+ *
259
+ * const action: Action={kind:"left"};
260
+ * const value: Action = action;
261
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
262
+ * ```
263
+ */
95
264
  export type Action = {
96
265
  readonly kind: 'left' | 'right' | 'soft-drop' | 'hard-drop' | 'place' | 'cycle-forward' | 'cycle-backward' | 'pause' | 'resume' | 'hint' | 'tick';
97
266
  };
267
+ /** The resulting native state, emitted events and acceptance decision; rejected actions preserve the input.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * import { applyAction, createGame } from "@johnmorrisdotca/houseki/falling-triplets";
272
+ * import type { Action, Transition } from "@johnmorrisdotca/houseki/falling-triplets";
273
+ *
274
+ * const state=createGame({seed:"docs"});
275
+ * const action: Action={kind:"left"};
276
+ * const value: Transition = applyAction(state,action);
277
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
278
+ * ```
279
+ */
98
280
  export interface Transition {
99
281
  readonly state: GameState;
100
282
  readonly events: readonly GameEvent[];
101
283
  readonly accepted: boolean;
102
284
  readonly reason?: string;
103
285
  }
104
- /** One accepted canonical action and its logical tick position. */
286
+ /** One accepted canonical action and its logical tick position.
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * import type { Action, RecordedAction } from "@johnmorrisdotca/houseki/falling-triplets";
291
+ *
292
+ * const action: Action={kind:"left"};
293
+ * const value: RecordedAction = {tick:0,ordinal:0,action};
294
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
295
+ * ```
296
+ */
105
297
  export interface RecordedAction {
106
298
  readonly tick: number;
107
299
  readonly ordinal: number;
@@ -109,10 +301,30 @@ export interface RecordedAction {
109
301
  readonly kind: 'tick';
110
302
  }>;
111
303
  }
304
+ /** An event emitted by one native transition; type selects the event’s additional fields.
305
+ *
306
+ * @example
307
+ * ```ts
308
+ * import type { GameEvent } from "@johnmorrisdotca/houseki/falling-triplets";
309
+ *
310
+ * const value: GameEvent = {type:"host-example"};
311
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
312
+ * ```
313
+ */
112
314
  export interface GameEvent {
113
315
  readonly type: string;
114
316
  readonly [key: string]: unknown;
115
317
  }
318
+ /** Creation settings for this native engine; dimensions, mode, seed and optional mechanics are validated together.
319
+ *
320
+ * @example
321
+ * ```ts
322
+ * import type { CreateOptions } from "@johnmorrisdotca/houseki/falling-triplets";
323
+ *
324
+ * const value: CreateOptions = {seed:"docs"};
325
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
326
+ * ```
327
+ */
116
328
  export interface CreateOptions {
117
329
  readonly mode?: Exclude<Mode, 'challenge'>;
118
330
  readonly seed?: string;
@@ -122,6 +334,18 @@ export interface CreateOptions {
122
334
  readonly colourCount?: 4 | 5 | 6;
123
335
  readonly pieceLimit?: number;
124
336
  }
337
+ /** Authored challenge inputs including the starting board, finite goal and supplied witness.
338
+ *
339
+ * @example
340
+ * ```ts
341
+ * import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
342
+ * import type { ChallengeOptions } from "@johnmorrisdotca/houseki/falling-triplets";
343
+ *
344
+ * const state=createGame({seed:"docs"});
345
+ * const value: ChallengeOptions = {width:6,height:12,board:state.board,queue:[["red","green","blue"]],goal:{type:"empty"}};
346
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
347
+ * ```
348
+ */
125
349
  export interface ChallengeOptions {
126
350
  readonly seed?: string;
127
351
  readonly width?: number;
@@ -1,5 +1,27 @@
1
1
  import type { Action, CreateOptions, ReplayOperation } from './types.js';
2
+ /** The existing campaign’s human-review-pending status; an engine witness is not human review.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import type { GemSwapReviewStatus } from "@johnmorrisdotca/houseki/gem-swap";
7
+ *
8
+ * const value: GemSwapReviewStatus = "human-review-pending";
9
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
10
+ * ```
11
+ */
2
12
  export type GemSwapReviewStatus = 'human-review-pending';
13
+ /** One authored native challenge with its stable id, setup, winning witness, measurements and review status.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { GEM_SWAP_CAMPAIGN } from "@johnmorrisdotca/houseki/gem-swap";
18
+ * import type { GemSwapCampaignLevel } from "@johnmorrisdotca/houseki/gem-swap";
19
+ *
20
+ * const level=GEM_SWAP_CAMPAIGN[0];
21
+ * const value: GemSwapCampaignLevel = level;
22
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
23
+ * ```
24
+ */
3
25
  export interface GemSwapCampaignLevel {
4
26
  readonly id: string;
5
27
  readonly number: number;
@@ -23,6 +45,17 @@ export interface GemSwapCampaignLevel {
23
45
  readonly proofStatus: 'engine-witness-verified';
24
46
  readonly reviewStatus: GemSwapReviewStatus;
25
47
  }
48
+ /** One bilingual native lesson instruction and the corresponding action or tick step.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import type { GemSwapLessonStep } from "@johnmorrisdotca/houseki/gem-swap";
53
+ * import { GEM_SWAP_LESSONS } from "@johnmorrisdotca/houseki/gem-swap";
54
+ *
55
+ * const value: GemSwapLessonStep = GEM_SWAP_LESSONS[0].steps[0];
56
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
57
+ * ```
58
+ */
26
59
  export type GemSwapLessonStep = {
27
60
  readonly kind: 'action';
28
61
  readonly id: string;
@@ -47,6 +80,17 @@ export type GemSwapLessonStep = {
47
80
  readonly ja: string;
48
81
  };
49
82
  };
83
+ /** One bilingual native lesson with its authored setup, teaching steps and winning witness.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * import type { GemSwapLesson } from "@johnmorrisdotca/houseki/gem-swap";
88
+ * import { GEM_SWAP_LESSONS } from "@johnmorrisdotca/houseki/gem-swap";
89
+ *
90
+ * const value: GemSwapLesson = GEM_SWAP_LESSONS[0];
91
+ * console.log(value == null ? value : typeof value === "object" ? Object.keys(value).slice(0,6) : value);
92
+ * ```
93
+ */
50
94
  export interface GemSwapLesson {
51
95
  readonly id: string;
52
96
  readonly title: {
@@ -58,9 +102,45 @@ export interface GemSwapLesson {
58
102
  readonly witness: readonly ReplayOperation[];
59
103
  readonly reviewStatus: GemSwapReviewStatus;
60
104
  }
105
+ /** The stored Gem Swap campaign generation revision.
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * import { GENERATION_REVISION } from "@johnmorrisdotca/houseki/gem-swap";
110
+ *
111
+ * console.log(GENERATION_REVISION);
112
+ * ```
113
+ */
61
114
  export declare const GENERATION_REVISION = "gem-swap-campaign-1.3.0";
115
+ /** The measurement/normalization revision used to grade Gem Swap challenges.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * import { GRADING_VERSION } from "@johnmorrisdotca/houseki/gem-swap";
120
+ *
121
+ * console.log(GRADING_VERSION);
122
+ * ```
123
+ */
62
124
  export declare const GRADING_VERSION = "swap-choice-forgiveness-1";
125
+ /** The fixed number of seeded playout samples used in the stored Gem Swap grading.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * import { SAMPLE_BUDGET } from "@johnmorrisdotca/houseki/gem-swap";
130
+ *
131
+ * console.log(SAMPLE_BUDGET);
132
+ * ```
133
+ */
63
134
  export declare const SAMPLE_BUDGET = 24;
135
+ /** The bounded Gem Swap metric weights used in the recorded grading formula.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * import { GRADING_WEIGHTS } from "@johnmorrisdotca/houseki/gem-swap";
140
+ *
141
+ * console.log(Object.keys(GRADING_WEIGHTS));
142
+ * ```
143
+ */
64
144
  export declare const GRADING_WEIGHTS: {
65
145
  seededPlayoutDifficulty: number;
66
146
  choiceDifficulty: number;
@@ -69,7 +149,43 @@ export declare const GRADING_WEIGHTS: {
69
149
  witnessLength: number;
70
150
  specialCombination: number;
71
151
  };
152
+ /** The stored Gem Swap campaign content fingerprint.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * import { CAMPAIGN_CHECKSUM } from "@johnmorrisdotca/houseki/gem-swap";
157
+ *
158
+ * console.log(CAMPAIGN_CHECKSUM);
159
+ * ```
160
+ */
72
161
  export declare const CAMPAIGN_CHECKSUM = "eb7c54a583db428d596b8fbdab0d63a9ad39ad5b9e39948dc4ccab84ba1196aa";
162
+ /** The number of stored Gem Swap campaign challenges.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * import { CAMPAIGN_COUNT } from "@johnmorrisdotca/houseki/gem-swap";
167
+ *
168
+ * console.log(CAMPAIGN_COUNT);
169
+ * ```
170
+ */
73
171
  export declare const CAMPAIGN_COUNT = 50;
172
+ /** The frozen measured Gem Swap challenges with stable ids, setups, witnesses and review status.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * import { GEM_SWAP_CAMPAIGN } from "@johnmorrisdotca/houseki/gem-swap";
177
+ *
178
+ * console.log(GEM_SWAP_CAMPAIGN.length);
179
+ * ```
180
+ */
74
181
  export declare const GEM_SWAP_CAMPAIGN: readonly GemSwapCampaignLevel[];
182
+ /** The frozen bilingual Gem Swap lessons with setup, teaching steps and witnesses.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * import { GEM_SWAP_LESSONS } from "@johnmorrisdotca/houseki/gem-swap";
187
+ *
188
+ * console.log(GEM_SWAP_LESSONS.length);
189
+ * ```
190
+ */
75
191
  export declare const GEM_SWAP_LESSONS: readonly GemSwapLesson[];
@@ -3,12 +3,75 @@ function freezeContent(value) { if (value && typeof value === 'object' && !Objec
3
3
  for (const item of Object.values(value))
4
4
  freezeContent(item);
5
5
  } return value; }
6
+ /** The stored Gem Swap campaign generation revision.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { GENERATION_REVISION } from "@johnmorrisdotca/houseki/gem-swap";
11
+ *
12
+ * console.log(GENERATION_REVISION);
13
+ * ```
14
+ */
6
15
  export const GENERATION_REVISION = "gem-swap-campaign-1.3.0";
16
+ /** The measurement/normalization revision used to grade Gem Swap challenges.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { GRADING_VERSION } from "@johnmorrisdotca/houseki/gem-swap";
21
+ *
22
+ * console.log(GRADING_VERSION);
23
+ * ```
24
+ */
7
25
  export const GRADING_VERSION = "swap-choice-forgiveness-1";
26
+ /** The fixed number of seeded playout samples used in the stored Gem Swap grading.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { SAMPLE_BUDGET } from "@johnmorrisdotca/houseki/gem-swap";
31
+ *
32
+ * console.log(SAMPLE_BUDGET);
33
+ * ```
34
+ */
8
35
  export const SAMPLE_BUDGET = 24;
36
+ /** The bounded Gem Swap metric weights used in the recorded grading formula.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { GRADING_WEIGHTS } from "@johnmorrisdotca/houseki/gem-swap";
41
+ *
42
+ * console.log(Object.keys(GRADING_WEIGHTS));
43
+ * ```
44
+ */
9
45
  export const GRADING_WEIGHTS = freezeContent({ "seededPlayoutDifficulty": 30, "choiceDifficulty": 18, "objectiveCoordination": 12, "chainDepth": 10, "witnessLength": 25, "specialCombination": 5 });
46
+ /** The stored Gem Swap campaign content fingerprint.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { CAMPAIGN_CHECKSUM } from "@johnmorrisdotca/houseki/gem-swap";
51
+ *
52
+ * console.log(CAMPAIGN_CHECKSUM);
53
+ * ```
54
+ */
10
55
  export const CAMPAIGN_CHECKSUM = "eb7c54a583db428d596b8fbdab0d63a9ad39ad5b9e39948dc4ccab84ba1196aa";
56
+ /** The number of stored Gem Swap campaign challenges.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * import { CAMPAIGN_COUNT } from "@johnmorrisdotca/houseki/gem-swap";
61
+ *
62
+ * console.log(CAMPAIGN_COUNT);
63
+ * ```
64
+ */
11
65
  export const CAMPAIGN_COUNT = 50;
66
+ /** The frozen measured Gem Swap challenges with stable ids, setups, witnesses and review status.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { GEM_SWAP_CAMPAIGN } from "@johnmorrisdotca/houseki/gem-swap";
71
+ *
72
+ * console.log(GEM_SWAP_CAMPAIGN.length);
73
+ * ```
74
+ */
12
75
  export const GEM_SWAP_CAMPAIGN = freezeContent([
13
76
  { "id": "swap-03d5fd388b09", "number": 1, "title": { "en": "Clear Horizon", "ja": "晴れた地平" }, "options": { "mode": "challenge", "preset": "compact", "seed": 11, "challenge": { "moveLimit": 1, "goals": [{ "kind": "score", "target": 30 }] } }, "width": 6, "height": 6, "colourCount": 5, "seed": 11, "challenge": { "moveLimit": 1, "goals": [{ "kind": "score", "target": 30 }] }, "witness": [{ "kind": "action", "action": { "kind": "swap", "from": 26, "to": 27 } }, { "kind": "ticks", "count": 3600 }], "canonicalKeyHash": "25637ca705ea4e9aef83f2b58b5944c49cb8fa5dee8f3f3a3cc7b00bd45d5a1d", "tags": ["score"], "rawMetrics": { "legalSwapCount": 8, "initiallyProgressingSwaps": 8, "initialProgressingShare": 1, "seededPlayoutSamples": 24, "seededPlayoutSuccessRate": 1, "objectiveCount": 1, "requiredChainDepth": 1, "sealLayers": 0, "witnessMoves": 1, "planningLength": 1, "specialCreations": 0, "specialActivations": 0, "specialCombinations": 0, "difficultyScore": 0 }, "difficultyScore": 0, "difficultyMarks": 1, "gradingVersion": "swap-choice-forgiveness-1", "proofStatus": "engine-witness-verified", "reviewStatus": "human-review-pending" },
14
77
  { "id": "swap-a0f21f37725a", "number": 2, "title": { "en": "Quiet Current", "ja": "静かな流れ" }, "options": { "mode": "challenge", "preset": "compact", "seed": 0, "challenge": { "moveLimit": 1, "goals": [{ "kind": "score", "target": 30 }] } }, "width": 6, "height": 6, "colourCount": 5, "seed": 0, "challenge": { "moveLimit": 1, "goals": [{ "kind": "score", "target": 30 }] }, "witness": [{ "kind": "action", "action": { "kind": "swap", "from": 22, "to": 23 } }, { "kind": "ticks", "count": 3600 }], "canonicalKeyHash": "18c1e827e012b7e7b86959c46cab081fa0ceec239ddc35d40bc23463c83d17c1", "tags": ["score"], "rawMetrics": { "legalSwapCount": 10, "initiallyProgressingSwaps": 10, "initialProgressingShare": 1, "seededPlayoutSamples": 24, "seededPlayoutSuccessRate": 1, "objectiveCount": 1, "requiredChainDepth": 1, "sealLayers": 0, "witnessMoves": 1, "planningLength": 1, "specialCreations": 0, "specialActivations": 0, "specialCombinations": 0, "difficultyScore": 0 }, "difficultyScore": 0, "difficultyMarks": 1, "gradingVersion": "swap-choice-forgiveness-1", "proofStatus": "engine-witness-verified", "reviewStatus": "human-review-pending" },
@@ -61,6 +124,15 @@ export const GEM_SWAP_CAMPAIGN = freezeContent([
61
124
  { "id": "swap-454ad7e8d1bd", "number": 49, "title": { "en": "Final Echo", "ja": "最後の反響" }, "options": { "mode": "challenge", "preset": "compact", "seed": 57, "challenge": { "moveLimit": 6, "goals": [{ "kind": "chain", "target": 6 }] } }, "width": 6, "height": 6, "colourCount": 5, "seed": 57, "challenge": { "moveLimit": 6, "goals": [{ "kind": "chain", "target": 6 }] }, "witness": [{ "kind": "action", "action": { "kind": "swap", "from": 20, "to": 21 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 16, "to": 17 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 27, "to": 28 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 22, "to": 23 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 18, "to": 24 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 20, "to": 26 } }, { "kind": "ticks", "count": 3600 }], "canonicalKeyHash": "72cd52768321ddfa4ac2b40a347fd8597f157dba1c3e961c946e60a8ac2795e9", "tags": ["chain", "multi-move-planning", "cascade", "special-creation", "special-combination"], "rawMetrics": { "legalSwapCount": 8, "initiallyProgressingSwaps": 8, "initialProgressingShare": 1, "seededPlayoutSamples": 24, "seededPlayoutSuccessRate": 0.041667, "objectiveCount": 1, "requiredChainDepth": 6, "sealLayers": 0, "witnessMoves": 6, "planningLength": 6, "specialCreations": 4, "specialActivations": 1, "specialCombinations": 1, "difficultyScore": 68.75 }, "difficultyScore": 68.75, "difficultyMarks": 4, "gradingVersion": "swap-choice-forgiveness-1", "proofStatus": "engine-witness-verified", "reviewStatus": "human-review-pending" },
62
125
  { "id": "swap-4c6462480f21", "number": 50, "title": { "en": "Final Marks", "ja": "最後の印" }, "options": { "mode": "challenge", "preset": "compact", "seed": 18, "challenge": { "moveLimit": 5, "goals": [{ "kind": "seals" }], "seals": [{ "cell": 26, "layers": 1 }, { "cell": 32, "layers": 1 }, { "cell": 33, "layers": 1 }] } }, "width": 6, "height": 6, "colourCount": 5, "seed": 18, "challenge": { "moveLimit": 5, "goals": [{ "kind": "seals" }], "seals": [{ "cell": 26, "layers": 1 }, { "cell": 32, "layers": 1 }, { "cell": 33, "layers": 1 }] }, "witness": [{ "kind": "action", "action": { "kind": "swap", "from": 18, "to": 19 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 2, "to": 8 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 31, "to": 32 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 1, "to": 2 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 19, "to": 20 } }, { "kind": "ticks", "count": 3600 }], "canonicalKeyHash": "db177c8ae220211fc4624a140687c5f1dc1f4b128d2c958123de3d564ce98904", "tags": ["seals", "multi-move-planning", "cascade", "special-creation"], "rawMetrics": { "legalSwapCount": 11, "initiallyProgressingSwaps": 1, "initialProgressingShare": 0.09090909090909091, "seededPlayoutSamples": 24, "seededPlayoutSuccessRate": 0.041667, "objectiveCount": 1, "requiredChainDepth": 1, "sealLayers": 3, "witnessMoves": 5, "planningLength": 5, "specialCreations": 2, "specialActivations": 0, "specialCombinations": 0, "difficultyScore": 71.11 }, "difficultyScore": 71.11, "difficultyMarks": 4, "gradingVersion": "swap-choice-forgiveness-1", "proofStatus": "engine-witness-verified", "reviewStatus": "human-review-pending" }
63
126
  ]);
127
+ /** The frozen bilingual Gem Swap lessons with setup, teaching steps and witnesses.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * import { GEM_SWAP_LESSONS } from "@johnmorrisdotca/houseki/gem-swap";
132
+ *
133
+ * console.log(GEM_SWAP_LESSONS.length);
134
+ * ```
135
+ */
64
136
  export const GEM_SWAP_LESSONS = freezeContent([
65
137
  { "id": "gem-swap-lesson-basics", "title": { "en": "Make a match", "ja": "マッチを作る" }, "initial": { "preset": "compact", "seed": 8001 }, "steps": [{ "kind": "action", "id": "invalid-return", "action": { "kind": "swap", "from": 0, "to": 1 }, "accepted": false, "reason": "swap-makes-no-match", "text": { "en": "This pair makes no line of three, so it returns unchanged.", "ja": "3つ並ばないため、この交換は元に戻ります。" } }, { "kind": "action", "id": "first-match", "action": { "kind": "swap", "from": 1, "to": 7 }, "accepted": true, "event": "match-marked", "text": { "en": "Swap neighbouring gems to make a line of three.", "ja": "隣り合う宝石を交換して、3つ並べましょう。" } }, { "kind": "ticks", "id": "watch-refill", "count": 3600, "text": { "en": "Watch the cleared cells refill before the next turn.", "ja": "次の手の前に、消えた場所が補充される様子を見ましょう。" } }], "witness": [{ "kind": "action", "action": { "kind": "swap", "from": 1, "to": 7 } }, { "kind": "ticks", "count": 3600 }], "reviewStatus": "human-review-pending" },
66
138
  { "id": "gem-swap-lesson-beam", "title": { "en": "Create and use a beam", "ja": "ビームを作って使う" }, "initial": { "preset": "compact", "seed": 1 }, "steps": [{ "kind": "action", "id": "plan-beam", "action": { "kind": "swap", "from": 26, "to": 32 }, "accepted": true, "event": "special-planned", "text": { "en": "A line of four plans a beam at its marked anchor.", "ja": "4つの列がそろうと、印の位置にビームが作られます。" } }, { "kind": "ticks", "id": "create-beam", "count": 3600, "event": "special-created", "text": { "en": "Let the clear finish; the beam stays on the board.", "ja": "消去が終わると、ビームが盤面に残ります。" } }, { "kind": "action", "id": "activate-beam", "action": { "kind": "swap", "from": 15, "to": 21 }, "accepted": true, "event": "match-marked", "text": { "en": "Make a match that reaches the beam to clear its line.", "ja": "ビームに届くマッチを作り、その列を消しましょう。" } }, { "kind": "ticks", "id": "finish-beam", "count": 3600, "event": "special-activated", "text": { "en": "Watch the beam clear its line and the board refill.", "ja": "ビームが列を消し、盤面が補充される様子を見ましょう。" } }], "witness": [{ "kind": "action", "action": { "kind": "swap", "from": 26, "to": 32 } }, { "kind": "ticks", "count": 3600 }, { "kind": "action", "action": { "kind": "swap", "from": 15, "to": 21 } }, { "kind": "ticks", "count": 3600 }], "reviewStatus": "human-review-pending" },