@johnmorrisdotca/houseki 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/README.md +77 -95
- package/dist/colour-chains/challenge.d.ts +12 -1
- package/dist/colour-chains/challenge.js +12 -1
- package/dist/colour-chains/content-types.d.ts +112 -0
- package/dist/colour-chains/content.d.ts +140 -3
- package/dist/colour-chains/content.js +140 -3
- package/dist/colour-chains/engine.d.ts +85 -6
- package/dist/colour-chains/engine.js +85 -6
- package/dist/colour-chains/input.d.ts +97 -7
- package/dist/colour-chains/input.js +97 -7
- package/dist/colour-chains/persistence.d.ts +36 -3
- package/dist/colour-chains/persistence.js +36 -3
- package/dist/colour-chains/types.d.ts +291 -17
- package/dist/colour-chains/validation.d.ts +12 -1
- package/dist/colour-chains/validation.js +12 -1
- package/dist/falling-triplets/content-types.d.ts +69 -0
- package/dist/falling-triplets/content.d.ts +60 -3
- package/dist/falling-triplets/content.js +60 -3
- package/dist/falling-triplets/engine.d.ts +122 -10
- package/dist/falling-triplets/engine.js +122 -10
- package/dist/falling-triplets/input.d.ts +61 -5
- package/dist/falling-triplets/input.js +61 -5
- package/dist/falling-triplets/match.d.ts +26 -2
- package/dist/falling-triplets/match.js +26 -2
- package/dist/falling-triplets/random.d.ts +34 -3
- package/dist/falling-triplets/random.js +34 -3
- package/dist/falling-triplets/types.d.ts +227 -3
- package/dist/gem-swap/content.d.ts +116 -0
- package/dist/gem-swap/content.js +72 -0
- package/dist/gem-swap/engine.d.ts +124 -9
- package/dist/gem-swap/engine.js +124 -9
- package/dist/gem-swap/persistence.d.ts +60 -4
- package/dist/gem-swap/persistence.js +60 -4
- package/dist/gem-swap/types.d.ts +336 -3
- package/dist/magnetic-blocks/content-types.d.ts +56 -0
- package/dist/magnetic-blocks/content.d.ts +91 -5
- package/dist/magnetic-blocks/content.js +91 -5
- package/dist/magnetic-blocks/engine.d.ts +63 -5
- package/dist/magnetic-blocks/engine.js +63 -5
- package/dist/magnetic-blocks/persistence.d.ts +36 -3
- package/dist/magnetic-blocks/persistence.js +36 -3
- package/dist/magnetic-blocks/physics.d.ts +176 -6
- package/dist/magnetic-blocks/physics.js +176 -6
- package/dist/magnetic-blocks/types.d.ts +185 -1
- package/dist/magnetic-blocks/validation.d.ts +12 -0
- package/dist/magnetic-blocks/validation.js +12 -0
- package/dist/nature/attraction.d.ts +24 -2
- package/dist/nature/attraction.js +24 -2
- package/dist/nature/environment.d.ts +200 -7
- package/dist/nature/environment.js +88 -7
- package/dist/nature/marking.d.ts +13 -1
- package/dist/nature/marking.js +13 -1
- package/dist/nature/rebound.d.ts +30 -2
- package/dist/nature/rebound.js +30 -2
- package/dist/nature/state.d.ts +11 -0
- package/dist/nature/state.js +11 -0
- package/dist/nature/types.d.ts +194 -1
- package/dist/nature/validation.d.ts +12 -0
- package/dist/nature/validation.js +12 -0
- package/dist/stone-collapse/content-types.d.ts +69 -0
- package/dist/stone-collapse/content.d.ts +60 -3
- package/dist/stone-collapse/content.js +60 -3
- package/dist/stone-collapse/engine.d.ts +99 -8
- package/dist/stone-collapse/engine.js +99 -8
- package/dist/stone-collapse/persistence.d.ts +36 -3
- package/dist/stone-collapse/persistence.js +36 -3
- package/dist/stone-collapse/types.d.ts +220 -3
- package/dist/stone-collapse/validation.d.ts +12 -1
- package/dist/stone-collapse/validation.js +12 -1
- package/package.json +34 -26
|
@@ -1,11 +1,68 @@
|
|
|
1
1
|
import type { CampaignLevel, CampaignManifest, TutorialDefinition } from './content-types.js';
|
|
2
2
|
import type { GameState } from './types.js';
|
|
3
|
+
/** The frozen native campaign manifest with its ordering, revisions, checksum and measured levels.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { campaignManifest } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
8
|
+
*
|
|
9
|
+
* console.log(campaignManifest.count);
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
3
12
|
export declare const campaignManifest: CampaignManifest;
|
|
13
|
+
/** The frozen ordered native levels; array indices start at zero and display numbers start at one.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { levelManifest } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
18
|
+
*
|
|
19
|
+
* console.log(levelManifest.length);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
4
22
|
export declare const levelManifest: readonly CampaignLevel[];
|
|
23
|
+
/** The frozen native bilingual lessons and their authored teaching steps.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { tutorialManifest } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
28
|
+
*
|
|
29
|
+
* console.log(tutorialManifest.length);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
5
32
|
export declare const tutorialManifest: readonly TutorialDefinition[];
|
|
6
|
-
/** Find a level by stable content ID or its current one-based display number.
|
|
33
|
+
/** Find a level by stable content ID or its current one-based display number.
|
|
34
|
+
*
|
|
35
|
+
* @param id - A stable content id, or the one-based display number when numbers are accepted.
|
|
36
|
+
* @returns Find a level by stable content ID or its current one-based display number. Returns undefined when the id or display number is absent.
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { getLevel } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
40
|
+
*
|
|
41
|
+
* console.log(getLevel(1)?.id);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
7
44
|
export declare function getLevel(id: string | number): CampaignLevel | undefined;
|
|
8
|
-
/** Construct a fresh validated challenge state for a stable level ID or display number.
|
|
45
|
+
/** Construct a fresh validated challenge state for a stable level ID or display number.
|
|
46
|
+
*
|
|
47
|
+
* @param id - A stable content id, or the one-based display number when numbers are accepted.
|
|
48
|
+
* @returns Construct a fresh validated challenge state for a stable level ID or display number.
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { createLevel } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
52
|
+
*
|
|
53
|
+
* console.log(createLevel(1).phase);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
9
56
|
export declare function createLevel(id: string | number): GameState;
|
|
10
|
-
/** Find a lesson by its persistent ID.
|
|
57
|
+
/** Find a lesson by its persistent ID.
|
|
58
|
+
*
|
|
59
|
+
* @param id - A stable content id, or the one-based display number when numbers are accepted.
|
|
60
|
+
* @returns Find a lesson by its persistent ID. Returns undefined when the id or display number is absent.
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { getTutorial, tutorialManifest } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
64
|
+
*
|
|
65
|
+
* console.log(getTutorial(tutorialManifest[0].id)?.id);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
11
68
|
export declare function getTutorial(id: string): TutorialDefinition | undefined;
|
|
@@ -8,16 +8,63 @@ 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/falling-triplets";
|
|
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/falling-triplets";
|
|
26
|
+
*
|
|
27
|
+
* console.log(levelManifest.length);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
12
30
|
export const levelManifest = campaignManifest.levels;
|
|
31
|
+
/** The frozen native bilingual lessons and their authored teaching steps.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { tutorialManifest } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
36
|
+
*
|
|
37
|
+
* console.log(tutorialManifest.length);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
13
40
|
export const tutorialManifest = deepFreeze(contentData.tutorials);
|
|
14
|
-
/** Find a level by stable content ID or its current one-based display number.
|
|
41
|
+
/** Find a level by stable content ID or its current one-based display number.
|
|
42
|
+
*
|
|
43
|
+
* @param id - A stable content id, or the one-based display number when numbers are accepted.
|
|
44
|
+
* @returns Find a level by stable content ID or its current one-based display number. Returns undefined when the id or display number is absent.
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { getLevel } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
48
|
+
*
|
|
49
|
+
* console.log(getLevel(1)?.id);
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
15
52
|
export function getLevel(id) {
|
|
16
53
|
if (typeof id === 'number')
|
|
17
54
|
return Number.isInteger(id) ? levelManifest.find(level => level.number === id) : undefined;
|
|
18
55
|
return levelManifest.find(level => level.id === id);
|
|
19
56
|
}
|
|
20
|
-
/** Construct a fresh validated challenge state for a stable level ID or display number.
|
|
57
|
+
/** Construct a fresh validated challenge state for a stable level ID or display number.
|
|
58
|
+
*
|
|
59
|
+
* @param id - A stable content id, or the one-based display number when numbers are accepted.
|
|
60
|
+
* @returns Construct a fresh validated challenge state for a stable level ID or display number.
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { createLevel } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
64
|
+
*
|
|
65
|
+
* console.log(createLevel(1).phase);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
21
68
|
export function createLevel(id) {
|
|
22
69
|
const level = getLevel(id);
|
|
23
70
|
if (!level)
|
|
@@ -33,7 +80,17 @@ export function createLevel(id) {
|
|
|
33
80
|
witness: level.witness
|
|
34
81
|
});
|
|
35
82
|
}
|
|
36
|
-
/** Find a lesson by its persistent ID.
|
|
83
|
+
/** Find a lesson by its persistent ID.
|
|
84
|
+
*
|
|
85
|
+
* @param id - A stable content id, or the one-based display number when numbers are accepted.
|
|
86
|
+
* @returns Find a lesson by its persistent ID. Returns undefined when the id or display number is absent.
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { getTutorial, tutorialManifest } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
90
|
+
*
|
|
91
|
+
* console.log(getTutorial(tutorialManifest[0].id)?.id);
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
37
94
|
export function getTutorial(id) {
|
|
38
95
|
return tutorialManifest.find(tutorial => tutorial.id === id);
|
|
39
96
|
}
|
|
@@ -1,26 +1,138 @@
|
|
|
1
1
|
import type { Action, ChallengeOptions, CreateOptions, GameState, Transition } from './types.js';
|
|
2
|
-
/** Returns the exact landing row for the current rigid triplet.
|
|
2
|
+
/** Returns the exact landing row for the current rigid triplet.
|
|
3
|
+
*
|
|
4
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
5
|
+
* @returns Returns the exact landing row for the current rigid triplet. Returns null when there is no active piece or applicable preview.
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createGame, landingY } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
9
|
+
*
|
|
10
|
+
* const state=createGame({seed:"docs"});
|
|
11
|
+
* console.log(landingY(state));
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
3
14
|
export declare function landingY(state: GameState): number | null;
|
|
4
|
-
/** Creates a deterministic empty game with an active piece and three-piece preview.
|
|
15
|
+
/** Creates a deterministic empty game with an active piece and three-piece preview.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
18
|
+
* @returns Creates a deterministic empty game with an active piece and three-piece preview.
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
22
|
+
*
|
|
23
|
+
* console.log(createGame({seed:"docs"}).phase);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
5
26
|
export declare function createGame(options?: CreateOptions): GameState;
|
|
6
|
-
/** Creates a challenge after validating its authored board, finite queue, goal, and witness.
|
|
27
|
+
/** Creates a challenge after validating its authored board, finite queue, goal, and witness.
|
|
28
|
+
*
|
|
29
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
30
|
+
* @returns Creates a challenge after validating its authored board, finite queue, goal, and witness.
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { createChallenge } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
34
|
+
*
|
|
35
|
+
* console.log(createChallenge({width:6,height:12,board:Array(72).fill(null),queue:[["red","green","blue"],["blue","red","green"]],goal:{type:"empty"}}).phase);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
7
38
|
export declare function createChallenge(options: ChallengeOptions): GameState;
|
|
8
|
-
/** Applies and records one immutable action; rejected actions preserve state identity.
|
|
39
|
+
/** Applies and records one immutable action; rejected actions preserve state identity.
|
|
40
|
+
*
|
|
41
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
42
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
43
|
+
* @returns Applies and records one immutable action; rejected actions preserve state identity.
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { applyAction, createGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
47
|
+
* import type { Action } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
48
|
+
*
|
|
49
|
+
* const state=createGame({seed:"docs"});
|
|
50
|
+
* const action: Action={kind:"left"};
|
|
51
|
+
* console.log(applyAction(state,action).accepted);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
9
54
|
export declare function applyAction(state: GameState, action: Action): Transition;
|
|
10
|
-
/** Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
|
|
55
|
+
/** Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
|
|
56
|
+
*
|
|
57
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
58
|
+
* @param ticks - The nonnegative number of fixed 60 Hz logical ticks to advance.
|
|
59
|
+
* @returns Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { advanceTicks, createGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
63
|
+
*
|
|
64
|
+
* const state=createGame({seed:"docs"});
|
|
65
|
+
* console.log(advanceTicks(state,1).state.phase);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
11
68
|
export declare function advanceTicks(state: GameState, ticks: number): Transition;
|
|
12
|
-
/** Lists the core actions currently available to the player.
|
|
69
|
+
/** Lists the core actions currently available to the player.
|
|
70
|
+
*
|
|
71
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
72
|
+
* @returns Lists the core actions currently available to the player.
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* import { createGame, legalActions } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
76
|
+
*
|
|
77
|
+
* const state=createGame({seed:"docs"});
|
|
78
|
+
* console.log(legalActions(state).length);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
13
81
|
export declare function legalActions(state: GameState): readonly Action[];
|
|
14
|
-
/** Returns a plain status summary suitable for UI labels.
|
|
82
|
+
/** Returns a plain status summary suitable for UI labels.
|
|
83
|
+
*
|
|
84
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
85
|
+
* @returns Returns a plain status summary suitable for UI labels.
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* import { createGame, statusOf } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
89
|
+
*
|
|
90
|
+
* const state=createGame({seed:"docs"});
|
|
91
|
+
* console.log(statusOf(state).phase);
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
15
94
|
export declare function statusOf(state: GameState): Readonly<{
|
|
16
95
|
phase: GameState['phase'];
|
|
17
96
|
score: number;
|
|
18
97
|
maxChain: number;
|
|
19
98
|
reason?: string;
|
|
20
99
|
}>;
|
|
21
|
-
/** Recreates the same authored mode, seed, and challenge configuration from its original setup.
|
|
100
|
+
/** Recreates the same authored mode, seed, and challenge configuration from its original setup.
|
|
101
|
+
*
|
|
102
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
103
|
+
* @returns Recreates the same authored mode, seed, and challenge configuration from its original setup.
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* import { createGame, restartGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
107
|
+
*
|
|
108
|
+
* const state=createGame({seed:"docs"});
|
|
109
|
+
* console.log(restartGame(state).phase);
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
22
112
|
export declare function restartGame(state: GameState): GameState;
|
|
23
|
-
/** Encodes the authoritative settings, action recording, and derived checkpoint.
|
|
113
|
+
/** Encodes the authoritative settings, action recording, and derived checkpoint.
|
|
114
|
+
*
|
|
115
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
116
|
+
* @returns Encodes the authoritative settings, action recording, and derived checkpoint.
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* import { createGame, encodeGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
120
|
+
*
|
|
121
|
+
* const state=createGame({seed:"docs"});
|
|
122
|
+
* console.log(encodeGame(state).length);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
24
125
|
export declare function encodeGame(state: GameState): string;
|
|
25
|
-
/** Replays and validates a bounded save, then verifies its checkpoint against the derived state.
|
|
126
|
+
/** Replays and validates a bounded save, then verifies its checkpoint against the derived state.
|
|
127
|
+
*
|
|
128
|
+
* @param text - A saved JSON recording from this entry’s encodeGame; malformed or inconsistent saves throw.
|
|
129
|
+
* @returns Replays and validates a bounded save, then verifies its checkpoint against the derived state.
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* import { createGame, decodeGame, encodeGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
133
|
+
*
|
|
134
|
+
* const state=createGame({seed:"docs"});
|
|
135
|
+
* console.log(decodeGame(encodeGame(state)).phase);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
26
138
|
export declare function decodeGame(text: string): GameState;
|
|
@@ -28,7 +28,18 @@ function activeFromColours(state, colours, y = -3) {
|
|
|
28
28
|
function occupied(state, piece, x = piece.x, y = piece.y) {
|
|
29
29
|
return piece.gems.some((_, i) => { const cy = y + i; return x < 0 || x >= state.settings.width || cy < -HIDDEN || cy >= state.settings.height || state.board[addr(x, cy, state.settings.width)] !== null; });
|
|
30
30
|
}
|
|
31
|
-
/** Returns the exact landing row for the current rigid triplet.
|
|
31
|
+
/** Returns the exact landing row for the current rigid triplet.
|
|
32
|
+
*
|
|
33
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
34
|
+
* @returns Returns the exact landing row for the current rigid triplet. Returns null when there is no active piece or applicable preview.
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { createGame, landingY } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
38
|
+
*
|
|
39
|
+
* const state=createGame({seed:"docs"});
|
|
40
|
+
* console.log(landingY(state));
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
32
43
|
export function landingY(state) {
|
|
33
44
|
if (!state.active)
|
|
34
45
|
return null;
|
|
@@ -48,7 +59,17 @@ function advanceBag(state) {
|
|
|
48
59
|
}
|
|
49
60
|
return [bag, randomState, next];
|
|
50
61
|
}
|
|
51
|
-
/** Creates a deterministic empty game with an active piece and three-piece preview.
|
|
62
|
+
/** Creates a deterministic empty game with an active piece and three-piece preview.
|
|
63
|
+
*
|
|
64
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
65
|
+
* @returns Creates a deterministic empty game with an active piece and three-piece preview.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { createGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
69
|
+
*
|
|
70
|
+
* console.log(createGame({seed:"docs"}).phase);
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
52
73
|
export function createGame(options = {}) {
|
|
53
74
|
if (options.preset !== undefined && !Object.hasOwn(PRESETS, options.preset))
|
|
54
75
|
throw new RangeError('Unsupported board preset');
|
|
@@ -88,7 +109,17 @@ export function createGame(options = {}) {
|
|
|
88
109
|
return state;
|
|
89
110
|
}
|
|
90
111
|
const VALID_COLOURS = new Set(['red', 'blue', 'green', 'gold', 'purple', 'teal']);
|
|
91
|
-
/** Creates a challenge after validating its authored board, finite queue, goal, and witness.
|
|
112
|
+
/** Creates a challenge after validating its authored board, finite queue, goal, and witness.
|
|
113
|
+
*
|
|
114
|
+
* @param options - Validated native creation, authored challenge, or environment options described by this signature.
|
|
115
|
+
* @returns Creates a challenge after validating its authored board, finite queue, goal, and witness.
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* import { createChallenge } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
119
|
+
*
|
|
120
|
+
* console.log(createChallenge({width:6,height:12,board:Array(72).fill(null),queue:[["red","green","blue"],["blue","red","green"]],goal:{type:"empty"}}).phase);
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
92
123
|
export function createChallenge(options) {
|
|
93
124
|
const width = options.width ?? 6;
|
|
94
125
|
const height = options.height ?? 13;
|
|
@@ -386,7 +417,21 @@ function applyActionCore(state, action) {
|
|
|
386
417
|
}
|
|
387
418
|
return emptyTransition(state, 'unsupported-action');
|
|
388
419
|
}
|
|
389
|
-
/** Applies and records one immutable action; rejected actions preserve state identity.
|
|
420
|
+
/** Applies and records one immutable action; rejected actions preserve state identity.
|
|
421
|
+
*
|
|
422
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
423
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
424
|
+
* @returns Applies and records one immutable action; rejected actions preserve state identity.
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* import { applyAction, createGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
428
|
+
* import type { Action } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
429
|
+
*
|
|
430
|
+
* const state=createGame({seed:"docs"});
|
|
431
|
+
* const action: Action={kind:"left"};
|
|
432
|
+
* console.log(applyAction(state,action).accepted);
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
390
435
|
export function applyAction(state, action) {
|
|
391
436
|
const result = applyActionCore(state, action);
|
|
392
437
|
if (!result.accepted || action.kind === 'tick')
|
|
@@ -395,7 +440,19 @@ export function applyAction(state, action) {
|
|
|
395
440
|
const ordinal = last?.tick === state.elapsedTicks ? last.ordinal + 1 : 0;
|
|
396
441
|
return { ...result, state: { ...result.state, recording: [...state.recording, { tick: state.elapsedTicks, ordinal, action }] } };
|
|
397
442
|
}
|
|
398
|
-
/** Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
|
|
443
|
+
/** Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
|
|
444
|
+
*
|
|
445
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
446
|
+
* @param ticks - The nonnegative number of fixed 60 Hz logical ticks to advance.
|
|
447
|
+
* @returns Advances fixed 60 Hz game ticks. Arcade timing is applied to the active piece.
|
|
448
|
+
* @example
|
|
449
|
+
* ```ts
|
|
450
|
+
* import { advanceTicks, createGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
451
|
+
*
|
|
452
|
+
* const state=createGame({seed:"docs"});
|
|
453
|
+
* console.log(advanceTicks(state,1).state.phase);
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
399
456
|
export function advanceTicks(state, ticks) {
|
|
400
457
|
if (!Number.isInteger(ticks) || ticks < 0 || ticks > 3600)
|
|
401
458
|
throw new RangeError('ticks must be an integer from 0 to 3600');
|
|
@@ -437,7 +494,18 @@ export function advanceTicks(state, ticks) {
|
|
|
437
494
|
}
|
|
438
495
|
return { state: current, events, accepted: true };
|
|
439
496
|
}
|
|
440
|
-
/** Lists the core actions currently available to the player.
|
|
497
|
+
/** Lists the core actions currently available to the player.
|
|
498
|
+
*
|
|
499
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
500
|
+
* @returns Lists the core actions currently available to the player.
|
|
501
|
+
* @example
|
|
502
|
+
* ```ts
|
|
503
|
+
* import { createGame, legalActions } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
504
|
+
*
|
|
505
|
+
* const state=createGame({seed:"docs"});
|
|
506
|
+
* console.log(legalActions(state).length);
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
441
509
|
export function legalActions(state) {
|
|
442
510
|
if (state.phase === 'paused')
|
|
443
511
|
return [{ kind: 'resume' }];
|
|
@@ -461,9 +529,31 @@ export function legalActions(state) {
|
|
|
461
529
|
actions.push({ kind: 'pause' });
|
|
462
530
|
return actions;
|
|
463
531
|
}
|
|
464
|
-
/** Returns a plain status summary suitable for UI labels.
|
|
532
|
+
/** Returns a plain status summary suitable for UI labels.
|
|
533
|
+
*
|
|
534
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
535
|
+
* @returns Returns a plain status summary suitable for UI labels.
|
|
536
|
+
* @example
|
|
537
|
+
* ```ts
|
|
538
|
+
* import { createGame, statusOf } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
539
|
+
*
|
|
540
|
+
* const state=createGame({seed:"docs"});
|
|
541
|
+
* console.log(statusOf(state).phase);
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
465
544
|
export function statusOf(state) { return { phase: state.phase, score: state.score, maxChain: state.maxChain, ...(state.reason ? { reason: state.reason } : {}) }; }
|
|
466
|
-
/** Recreates the same authored mode, seed, and challenge configuration from its original setup.
|
|
545
|
+
/** Recreates the same authored mode, seed, and challenge configuration from its original setup.
|
|
546
|
+
*
|
|
547
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
548
|
+
* @returns Recreates the same authored mode, seed, and challenge configuration from its original setup.
|
|
549
|
+
* @example
|
|
550
|
+
* ```ts
|
|
551
|
+
* import { createGame, restartGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
552
|
+
*
|
|
553
|
+
* const state=createGame({seed:"docs"});
|
|
554
|
+
* console.log(restartGame(state).phase);
|
|
555
|
+
* ```
|
|
556
|
+
*/
|
|
467
557
|
export function restartGame(state) {
|
|
468
558
|
if (state.settings.mode === 'challenge') {
|
|
469
559
|
const settings = state.settings;
|
|
@@ -472,7 +562,18 @@ export function restartGame(state) {
|
|
|
472
562
|
}
|
|
473
563
|
return createGame({ mode: state.settings.mode, seed: state.settings.seed, width: state.settings.width, height: state.settings.height, colourCount: state.settings.colourCount, ...(state.settings.pieceLimit === undefined ? {} : { pieceLimit: state.settings.pieceLimit }) });
|
|
474
564
|
}
|
|
475
|
-
/** Encodes the authoritative settings, action recording, and derived checkpoint.
|
|
565
|
+
/** Encodes the authoritative settings, action recording, and derived checkpoint.
|
|
566
|
+
*
|
|
567
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
568
|
+
* @returns Encodes the authoritative settings, action recording, and derived checkpoint.
|
|
569
|
+
* @example
|
|
570
|
+
* ```ts
|
|
571
|
+
* import { createGame, encodeGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
572
|
+
*
|
|
573
|
+
* const state=createGame({seed:"docs"});
|
|
574
|
+
* console.log(encodeGame(state).length);
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
476
577
|
export function encodeGame(state) { return JSON.stringify({ format: 1, game: state.game, rules: state.rules, settings: state.settings, actions: state.recording, elapsedTicks: state.elapsedTicks, checkpoint: state }); }
|
|
477
578
|
function stable(value) {
|
|
478
579
|
if (Array.isArray(value))
|
|
@@ -481,7 +582,18 @@ function stable(value) {
|
|
|
481
582
|
return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`).join(',')}}`;
|
|
482
583
|
return JSON.stringify(value) ?? 'null';
|
|
483
584
|
}
|
|
484
|
-
/** Replays and validates a bounded save, then verifies its checkpoint against the derived state.
|
|
585
|
+
/** Replays and validates a bounded save, then verifies its checkpoint against the derived state.
|
|
586
|
+
*
|
|
587
|
+
* @param text - A saved JSON recording from this entry’s encodeGame; malformed or inconsistent saves throw.
|
|
588
|
+
* @returns Replays and validates a bounded save, then verifies its checkpoint against the derived state.
|
|
589
|
+
* @example
|
|
590
|
+
* ```ts
|
|
591
|
+
* import { createGame, decodeGame, encodeGame } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
592
|
+
*
|
|
593
|
+
* const state=createGame({seed:"docs"});
|
|
594
|
+
* console.log(decodeGame(encodeGame(state)).phase);
|
|
595
|
+
* ```
|
|
596
|
+
*/
|
|
485
597
|
export function decodeGame(text) {
|
|
486
598
|
if (text.length > 2 * 1024 * 1024)
|
|
487
599
|
throw new RangeError('Save exceeds 2 MiB');
|
|
@@ -5,14 +5,70 @@ export interface InputSchedulerState {
|
|
|
5
5
|
readonly held: Readonly<Partial<Record<HoldAction, number>>>;
|
|
6
6
|
readonly queued: readonly Action[];
|
|
7
7
|
}
|
|
8
|
-
/** Creates an empty fixed-tick input scheduler state.
|
|
8
|
+
/** Creates an empty fixed-tick input scheduler state.
|
|
9
|
+
*
|
|
10
|
+
* @returns Creates an empty fixed-tick input scheduler state.
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createInputScheduler } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
14
|
+
*
|
|
15
|
+
* console.log(Object.keys(createInputScheduler()));
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
9
18
|
export declare function createInputScheduler(): InputSchedulerState;
|
|
10
|
-
/** Starts or releases one held action; opposite lateral holds cancel during scheduling.
|
|
19
|
+
/** Starts or releases one held action; opposite lateral holds cancel during scheduling.
|
|
20
|
+
*
|
|
21
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
22
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
23
|
+
* @param held - Whether this input is currently held.
|
|
24
|
+
* @returns Starts or releases one held action; opposite lateral holds cancel during scheduling.
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { createInputScheduler, setHeldAction } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
28
|
+
*
|
|
29
|
+
* const input=createInputScheduler();
|
|
30
|
+
* console.log(setHeldAction(input,"left",true));
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
11
33
|
export declare function setHeldAction(state: InputSchedulerState, action: HoldAction, held: boolean): InputSchedulerState;
|
|
12
|
-
/** Queues a one-shot cycle, drop, or pause edge for the next logical tick.
|
|
34
|
+
/** Queues a one-shot cycle, drop, or pause edge for the next logical tick.
|
|
35
|
+
*
|
|
36
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
37
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
38
|
+
* @returns Queues a one-shot cycle, drop, or pause edge for the next logical tick.
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { createInputScheduler, queueInputEdge } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
42
|
+
*
|
|
43
|
+
* const input=createInputScheduler();
|
|
44
|
+
* console.log(queueInputEdge(input,{kind:"hard-drop"}));
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
13
47
|
export declare function queueInputEdge(state: InputSchedulerState, action: Action): InputSchedulerState;
|
|
14
|
-
/** Resolves canonical actions in the design's per-tick order.
|
|
48
|
+
/** Resolves canonical actions in the design's per-tick order.
|
|
49
|
+
*
|
|
50
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
51
|
+
* @returns Resolves canonical actions in the design's per-tick order.
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { actionsForTick, createInputScheduler } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
55
|
+
*
|
|
56
|
+
* const input=createInputScheduler();
|
|
57
|
+
* console.log(actionsForTick(input)[0]);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
15
60
|
export declare function actionsForTick(state: InputSchedulerState): readonly [readonly Action[], InputSchedulerState];
|
|
16
|
-
/** Releases every held action after blur, pause, or pointer cancellation.
|
|
61
|
+
/** Releases every held action after blur, pause, or pointer cancellation.
|
|
62
|
+
*
|
|
63
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
64
|
+
* @returns Releases every held action after blur, pause, or pointer cancellation.
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* import { createInputScheduler, releaseAllActions } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
68
|
+
*
|
|
69
|
+
* const input=createInputScheduler();
|
|
70
|
+
* console.log(releaseAllActions(input));
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
17
73
|
export declare function releaseAllActions(state: InputSchedulerState): InputSchedulerState;
|
|
18
74
|
export {};
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
/** Creates an empty fixed-tick input scheduler state.
|
|
1
|
+
/** Creates an empty fixed-tick input scheduler state.
|
|
2
|
+
*
|
|
3
|
+
* @returns Creates an empty fixed-tick input scheduler state.
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { createInputScheduler } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
7
|
+
*
|
|
8
|
+
* console.log(Object.keys(createInputScheduler()));
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
2
11
|
export function createInputScheduler() { return { held: {}, queued: [] }; }
|
|
3
|
-
/** Starts or releases one held action; opposite lateral holds cancel during scheduling.
|
|
12
|
+
/** Starts or releases one held action; opposite lateral holds cancel during scheduling.
|
|
13
|
+
*
|
|
14
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
15
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
16
|
+
* @param held - Whether this input is currently held.
|
|
17
|
+
* @returns Starts or releases one held action; opposite lateral holds cancel during scheduling.
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { createInputScheduler, setHeldAction } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
21
|
+
*
|
|
22
|
+
* const input=createInputScheduler();
|
|
23
|
+
* console.log(setHeldAction(input,"left",true));
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
4
26
|
export function setHeldAction(state, action, held) {
|
|
5
27
|
const next = { ...state.held };
|
|
6
28
|
if (held) {
|
|
@@ -11,13 +33,36 @@ export function setHeldAction(state, action, held) {
|
|
|
11
33
|
delete next[action];
|
|
12
34
|
return { ...state, held: next };
|
|
13
35
|
}
|
|
14
|
-
/** Queues a one-shot cycle, drop, or pause edge for the next logical tick.
|
|
36
|
+
/** Queues a one-shot cycle, drop, or pause edge for the next logical tick.
|
|
37
|
+
*
|
|
38
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
39
|
+
* @param action - One native action for this entry point; inspect accepted and reason on the result.
|
|
40
|
+
* @returns Queues a one-shot cycle, drop, or pause edge for the next logical tick.
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { createInputScheduler, queueInputEdge } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
44
|
+
*
|
|
45
|
+
* const input=createInputScheduler();
|
|
46
|
+
* console.log(queueInputEdge(input,{kind:"hard-drop"}));
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
15
49
|
export function queueInputEdge(state, action) {
|
|
16
50
|
if (!['cycle-forward', 'cycle-backward', 'hard-drop', 'place', 'pause', 'resume'].includes(action.kind))
|
|
17
51
|
return state;
|
|
18
52
|
return { ...state, queued: [...state.queued, action] };
|
|
19
53
|
}
|
|
20
|
-
/** Resolves canonical actions in the design's per-tick order.
|
|
54
|
+
/** Resolves canonical actions in the design's per-tick order.
|
|
55
|
+
*
|
|
56
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
57
|
+
* @returns Resolves canonical actions in the design's per-tick order.
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { actionsForTick, createInputScheduler } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
61
|
+
*
|
|
62
|
+
* const input=createInputScheduler();
|
|
63
|
+
* console.log(actionsForTick(input)[0]);
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
21
66
|
export function actionsForTick(state) {
|
|
22
67
|
const actions = [];
|
|
23
68
|
const held = { ...state.held };
|
|
@@ -37,6 +82,17 @@ export function actionsForTick(state) {
|
|
|
37
82
|
held[key] = (held[key] ?? 0) + 1;
|
|
38
83
|
return [actions, { held, queued: [] }];
|
|
39
84
|
}
|
|
40
|
-
/** Releases every held action after blur, pause, or pointer cancellation.
|
|
85
|
+
/** Releases every held action after blur, pause, or pointer cancellation.
|
|
86
|
+
*
|
|
87
|
+
* @param state - The immutable native state to read or transition; the input is retained.
|
|
88
|
+
* @returns Releases every held action after blur, pause, or pointer cancellation.
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { createInputScheduler, releaseAllActions } from "@johnmorrisdotca/houseki/falling-triplets";
|
|
92
|
+
*
|
|
93
|
+
* const input=createInputScheduler();
|
|
94
|
+
* console.log(releaseAllActions(input));
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
41
97
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- the public signature takes the state a caller holds, though nothing of it is kept
|
|
42
98
|
export function releaseAllActions(state) { return { held: {}, queued: [] }; }
|