@johnmorrisdotca/houseki 0.1.1 → 0.2.1
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 +33 -2
- package/README.md +506 -18
- package/dist/colour-chains/challenge.js +18 -4
- package/dist/colour-chains/content-data.js +19582 -2
- package/dist/colour-chains/content-types.d.ts +21 -0
- package/dist/colour-chains/content.d.ts +9 -1
- package/dist/colour-chains/content.js +13 -0
- package/dist/colour-chains/engine.js +8 -4
- package/dist/colour-chains/persistence.js +1 -1
- package/dist/colour-chains/types.d.ts +6 -2
- package/dist/colour-chains.d.ts +2 -2
- package/dist/colour-chains.js +1 -1
- package/dist/magnetic-blocks/content-data.d.ts +3 -0
- package/dist/magnetic-blocks/content-data.js +11781 -0
- package/dist/magnetic-blocks/content-types.d.ts +66 -0
- package/dist/magnetic-blocks/content-types.js +1 -0
- package/dist/magnetic-blocks/content.d.ts +16 -0
- package/dist/magnetic-blocks/content.js +38 -0
- package/dist/magnetic-blocks/engine.js +4 -2
- package/dist/magnetic-blocks.d.ts +2 -0
- package/dist/magnetic-blocks.js +1 -0
- package/package.json +8 -3
|
@@ -61,7 +61,7 @@ function solveWitness(start, witness) {
|
|
|
61
61
|
export function createChallenge(options) {
|
|
62
62
|
if (!options || typeof options !== 'object' || Array.isArray(options))
|
|
63
63
|
throw new StoneChainsOptionsError('invalid-challenge', 'Challenge options must be an object');
|
|
64
|
-
const allowed = ['id', 'width', 'height', 'colourCount', 'seed', 'board', 'queue', 'goal', 'witness'];
|
|
64
|
+
const allowed = ['id', 'width', 'height', 'colourCount', 'seed', 'board', 'queue', 'magneticQueue', 'nature', 'weather', 'goal', 'witness'];
|
|
65
65
|
if (Object.keys(options).some(key => !allowed.includes(key)))
|
|
66
66
|
throw new StoneChainsOptionsError('unknown-challenge-option', 'Challenge options contain unsupported fields');
|
|
67
67
|
if (typeof options.id !== 'string' || !/^[a-z0-9][a-z0-9._-]{0,63}$/.test(options.id))
|
|
@@ -75,6 +75,14 @@ export function createChallenge(options) {
|
|
|
75
75
|
const seed = options.seed ?? `challenge:${options.id}`;
|
|
76
76
|
if (typeof seed !== 'string' || seed.length < 1 || seed.length > 200)
|
|
77
77
|
throw new StoneChainsOptionsError('invalid-seed', 'Seed must contain 1–200 characters');
|
|
78
|
+
if (options.nature !== undefined && typeof options.nature !== 'boolean')
|
|
79
|
+
throw new StoneChainsOptionsError('invalid-nature-option', 'nature must be a boolean');
|
|
80
|
+
if (options.weather !== undefined && options.weather !== 'frequent' && options.weather !== 'rare')
|
|
81
|
+
throw new StoneChainsOptionsError('invalid-weather-option', 'weather must be frequent or rare');
|
|
82
|
+
if (options.weather !== undefined && options.nature !== true)
|
|
83
|
+
throw new StoneChainsOptionsError('weather-requires-nature', 'Arashi weather requires nature:true');
|
|
84
|
+
if (options.magneticQueue !== undefined && options.nature !== true)
|
|
85
|
+
throw new StoneChainsOptionsError('magnetic-queue-requires-nature', 'Magnetic queue flags require nature:true');
|
|
78
86
|
if (!Array.isArray(options.board) || options.board.length !== width * height)
|
|
79
87
|
throw new StoneChainsOptionsError('invalid-challenge-board', 'Challenge board must contain width × height visible cells');
|
|
80
88
|
if (!Array.isArray(options.queue) || options.queue.length < 2 || options.queue.length > 12)
|
|
@@ -84,10 +92,10 @@ export function createChallenge(options) {
|
|
|
84
92
|
const board = options.board.map((gem, index) => {
|
|
85
93
|
if (gem === null)
|
|
86
94
|
return null;
|
|
87
|
-
if (!gem || typeof gem !== 'object' || Array.isArray(gem) || Object.keys(gem).some(key => key !== 'id' && key !== 'colour') || !Number.isSafeInteger(gem.id) || gem.id < 1 || gem.id > 0x7fff_ffff || ids.has(gem.id) || !allowedColours.includes(gem.colour))
|
|
95
|
+
if (!gem || typeof gem !== 'object' || Array.isArray(gem) || Object.keys(gem).some(key => key !== 'id' && key !== 'colour' && key !== 'magnetic') || !Number.isSafeInteger(gem.id) || gem.id < 1 || gem.id > 0x7fff_ffff || ids.has(gem.id) || !allowedColours.includes(gem.colour) || gem.magnetic !== undefined && (gem.magnetic !== true || options.nature !== true))
|
|
88
96
|
throw new StoneChainsOptionsError('invalid-challenge-gem', `Invalid challenge gem at cell ${index}`);
|
|
89
97
|
ids.add(gem.id);
|
|
90
|
-
return Object.freeze({ id: gem.id, colour: gem.colour });
|
|
98
|
+
return Object.freeze({ id: gem.id, colour: gem.colour, ...(gem.magnetic ? { magnetic: true } : {}) });
|
|
91
99
|
});
|
|
92
100
|
for (let x = 0; x < width; x++) {
|
|
93
101
|
let gap = false;
|
|
@@ -103,6 +111,12 @@ export function createChallenge(options) {
|
|
|
103
111
|
throw new StoneChainsOptionsError('invalid-challenge-pair', `Invalid queue pair ${index + 1}`);
|
|
104
112
|
return Object.freeze([pair[0], pair[1]]);
|
|
105
113
|
});
|
|
114
|
+
let magneticQueue;
|
|
115
|
+
if (options.magneticQueue !== undefined) {
|
|
116
|
+
if (!Array.isArray(options.magneticQueue) || options.magneticQueue.length !== queue.length || options.magneticQueue.some(pair => !Array.isArray(pair) || pair.length !== 2 || pair.some(flag => typeof flag !== 'boolean')))
|
|
117
|
+
throw new StoneChainsOptionsError('invalid-magnetic-queue', 'Magnetic queue must contain one pair of boolean flags per finite queue pair');
|
|
118
|
+
magneticQueue = Object.freeze(options.magneticQueue.map(pair => Object.freeze([pair[0], pair[1]])));
|
|
119
|
+
}
|
|
106
120
|
const goal = validateChallengeGoal(options.goal, ids);
|
|
107
121
|
if (!Array.isArray(options.witness) || options.witness.length < 1 || options.witness.length > queue.length)
|
|
108
122
|
throw new StoneChainsOptionsError('invalid-witness', 'A challenge requires a complete witness of 1–12 pair placements');
|
|
@@ -111,7 +125,7 @@ export function createChallenge(options) {
|
|
|
111
125
|
throw new StoneChainsOptionsError('invalid-witness-step', `Invalid witness placement ${index + 1}`);
|
|
112
126
|
return Object.freeze({ pivotX: step.pivotX, orientation: step.orientation });
|
|
113
127
|
});
|
|
114
|
-
const settings = Object.freeze({ mode: 'challenge', width, height, colourCount, seed, challengeId: options.id, goal, initialBoard: Object.freeze(board), queue: Object.freeze(queue), witness: Object.freeze(witness) });
|
|
128
|
+
const settings = Object.freeze({ mode: 'challenge', width, height, colourCount, seed, challengeId: options.id, goal, initialBoard: Object.freeze(board), queue: Object.freeze(queue), ...(magneticQueue ? { magneticQueue } : {}), ...(options.nature ? { nature: true } : {}), ...(options.weather ? { weather: options.weather } : {}), witness: Object.freeze(witness) });
|
|
115
129
|
const state = initialChallengeState(settings, settings.initialBoard, settings.queue);
|
|
116
130
|
const solved = solveWitness(state, witness);
|
|
117
131
|
if (solved.phase !== 'won' || stable(solved.settings) !== stable(settings))
|