@johnmorrisdotca/houseki 0.2.1 → 0.3.0

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 CHANGED
@@ -4,6 +4,23 @@ All notable changes to this package are written here, newest first, in the form
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.3.0] - 2026-10-06
8
+
9
+ The ten open fix tickets were each read against the code, and the ones that were real are fixed and held by new tests: Magnetic Blocks' settings and Space key, and the big-board region of Falling Triplets and Colour Chains. The other tickets were already fixed in 0.2.0, and the review tests that hold them now run in `pnpm test:demo`.
10
+
11
+ ### Added
12
+
13
+ - `powerDropPreview` of Colour Chains returns `settledCells` as well as `finalCells`: where each stone of the pair comes to rest in the board the hard drop commits, before any clear, with the stones' stable ids. `finalCells` is where the pair locks after the rebound (the cells of the `power-drop-landed` event), and stones above a gap then fall, so a ghost drawn from `finalCells` could sit a row above where the stone ends up. The new type is `ChainsPowerDropPreview`.
14
+ - `test/previews-match-outcomes.test.mjs` compares the previews with the boards the real actions leave, over thousands of played boards: the Shizen hard-drop preview, the gentle Place landing, Falling Triplets' landing row, and Magnetic Blocks' two actions. `e2e/review/boards-settings-and-keys.test.mjs` plays the demo's level settings, its Space key, its big-board keyboard navigation in both languages and the square cells of every size.
15
+
16
+ ### Fixed
17
+
18
+ - Magnetic Blocks: choosing a level or lesson shows its own board size, colours, floor and impact at once, as "Fixed · 8 × 12" ("固定"), instead of the free-play choice it would not use; the rules are locked while it is chosen, and choosing Free play, or a mode, gives the player's own settings back. Before this the controls changed only on New game, then kept the level's values for good, labelled "Custom size". The board in play is not relabelled before New, and the free choices are remembered between visits.
19
+ - Magnetic Blocks: in a campaign level or lesson the Space key did a hard drop while the ghost and the Place button showed a gentle land. The two leave different boards on about one in seven positions of the campaign. Space now does what the game's mode offers, as the ghost shows.
20
+ - Magnetic Blocks keeps its checkpoint within 350 ms of a change during continuous play; every move used to postpone the save until the player stopped.
21
+ - Falling Triplets: a board too big for the screen is a focusable, labelled region like the other two, with a position readout; its arrow keys scroll it and no longer move the triplet.
22
+ - Falling Triplets, Colour Chains and Magnetic Blocks: "Top", "Bottom" and the position readout are in Japanese when the page is, and the region is named for the board it holds, not for its scroll position. A board that fits is no longer a tab stop.
23
+
7
24
  ## [0.2.1] - 2026-10-06
8
25
 
9
26
  Nothing that was exported has changed. The README is the family's one layout, in full.
@@ -66,7 +83,9 @@ Nothing that was exported has changed. The README is the family's one layout, in
66
83
  - An experimental Magnetic Blocks engine with bonded squares, magnetic floor schedules, a Floor Switch and impact drops.
67
84
  - Typed entry points, a generated API reference, an MIT licence and reproducible package checks.
68
85
 
69
- [Unreleased]: https://github.com/johnmorrisdotca/houseki/compare/v0.2.0...HEAD
86
+ [Unreleased]: https://github.com/johnmorrisdotca/houseki/compare/v0.3.0...HEAD
87
+ [0.3.0]: https://github.com/johnmorrisdotca/houseki/compare/v0.2.1...v0.3.0
88
+ [0.2.1]: https://github.com/johnmorrisdotca/houseki/compare/v0.2.0...v0.2.1
70
89
  [0.2.0]: https://github.com/johnmorrisdotca/houseki/compare/v0.1.1...v0.2.0
71
90
  [0.1.1]: https://github.com/johnmorrisdotca/houseki/compare/v0.1.0...v0.1.1
72
91
  [0.1.0]: https://github.com/johnmorrisdotca/houseki/releases/tag/v0.1.0
package/README.md CHANGED
@@ -753,7 +753,7 @@ Ideas, bug reports and pull requests are welcome in the [issues](https://github.
753
753
 
754
754
  ## Changes
755
755
 
756
- Every release is written up in [CHANGELOG.md](./CHANGELOG.md). The latest release, 0.2.1, adds no code: it is this README in full, with pictures of every game, examples that are run on every change, examples for React, Vue, Svelte and Angular, and an Accessibility section.
756
+ Every release is written up in [CHANGELOG.md](./CHANGELOG.md). The latest release, 0.3.0, makes Magnetic Blocks show a level's fixed rules as soon as the level is chosen and give free play its own choices back, makes Space do what the ghost showed, lets a big board be scrolled from the keyboard on all three falling players, and adds `settledCells` to the Shizen hard-drop preview.
757
757
 
758
758
  ## Licence
759
759
 
@@ -1,5 +1,4 @@
1
- import type { PowerDropPreview } from '../nature/types.js';
2
- import type { Action, Colour, CreateOptions, GameState, GameStatus, Gem, Landing, Settings, Transition } from './types.js';
1
+ import type { Action, ChainsPowerDropPreview, Colour, CreateOptions, GameState, GameStatus, Gem, Landing, Settings, Transition } from './types.js';
3
2
  export { StoneChainsOptionsError } from './validation.js';
4
3
  /** Applies one immutable accepted action and records its logical-tick position. */
5
4
  export declare function applyAction(state: GameState, action: Action): Transition;
@@ -9,8 +8,15 @@ export declare function advanceTicks(state: GameState, ticks: number): Transitio
9
8
  export declare function legalActions(state: GameState): readonly Action[];
10
9
  /** Returns the exact independent landing cells for the current rigid pair without consuming random values. */
11
10
  export declare function landingCells(state: GameState): Landing | null;
12
- /** Returns the Shizen hard-drop landing and one-step rebound preview without changing state. */
13
- export declare function powerDropPreview(state: GameState): PowerDropPreview | null;
11
+ /**
12
+ * Returns the Shizen hard-drop landing and one-step rebound preview without changing state.
13
+ *
14
+ * `finalCells` is where the pair locks after any rebound, the cells of the `power-drop-landed` event. Stones above a
15
+ * gap then fall, so `settledCells` is where each stone of the pair comes to rest in the board the hard drop commits,
16
+ * before any clear: draw a ghost from it. Both carry the pair's stable stone ids, and both are checked against
17
+ * `applyAction` with a `hard-drop` over many played boards.
18
+ */
19
+ export declare function powerDropPreview(state: GameState): ChainsPowerDropPreview | null;
14
20
  /** Returns score, mode, chain and completion fields derived from state. */
15
21
  export declare function statusOf(state: GameState): GameStatus;
16
22
  /** Creates a base challenge state with a previously validated visible board and pair queue. @internal */
@@ -457,11 +457,26 @@ export function landingCells(state) {
457
457
  const settled = compactBoard(board, state.settings.width, state.settings.height + HIDDEN);
458
458
  return pair.gems.map(gem => { const index = settled.findIndex(cell => cell?.id === gem.id); return { x: index % state.settings.width, y: Math.floor(index / state.settings.width) - HIDDEN }; });
459
459
  }
460
- /** Returns the Shizen hard-drop landing and one-step rebound preview without changing state. */
460
+ /**
461
+ * Returns the Shizen hard-drop landing and one-step rebound preview without changing state.
462
+ *
463
+ * `finalCells` is where the pair locks after any rebound, the cells of the `power-drop-landed` event. Stones above a
464
+ * gap then fall, so `settledCells` is where each stone of the pair comes to rest in the board the hard drop commits,
465
+ * before any clear: draw a ghost from it. Both carry the pair's stable stone ids, and both are checked against
466
+ * `applyAction` with a `hard-drop` over many played boards.
467
+ */
461
468
  export function powerDropPreview(state) {
462
469
  if (!state.settings.nature || state.settings.mode === 'relaxed' || !state.active)
463
470
  return null;
464
- return rebound(state, state.active)?.preview ?? null;
471
+ const preview = rebound(state, state.active)?.preview;
472
+ if (!preview)
473
+ return null;
474
+ const dropped = applyAction(state, { kind: 'hard-drop' });
475
+ const board = dropped.accepted ? (dropped.state.gravityBoard ?? dropped.state.board) : null;
476
+ const settledCells = board
477
+ ? state.active.gems.flatMap(gem => { const index = board.findIndex(cell => cell?.id === gem.id); return index < 0 ? [] : [{ ...(preview.finalCells.find(cell => cell.id === gem.id) ?? { id: gem.id, kind: 'stone', colour: gem.colour }), x: index % state.settings.width, y: Math.floor(index / state.settings.width) - HIDDEN }]; })
478
+ : preview.finalCells;
479
+ return Object.freeze({ ...preview, settledCells: Object.freeze(settledCells) });
465
480
  }
466
481
  /** Returns score, mode, chain and completion fields derived from state. */
467
482
  export function statusOf(state) { return { mode: state.settings.mode, phase: state.phase, score: state.score, maxChain: state.maxChain, allClears: state.allClears, completedPairs: state.completedPairs, assisted: state.assisted, ...(state.reason ? { reason: state.reason } : {}) }; }
@@ -1,3 +1,4 @@
1
+ import type { NaturePieceCell, PowerDropPreview } from '../nature/types.js';
1
2
  /** A gem colour used by Colour Chains. */
2
3
  export type Colour = 'red' | 'blue' | 'green' | 'gold' | 'purple' | 'teal';
3
4
  /** A settled or active stone with a stable run-local identity. */
@@ -193,3 +194,7 @@ export interface GameStatus {
193
194
  }
194
195
  /** Destination cells for the two independently settled ghost stones. */
195
196
  export type Landing = readonly [Cell, Cell];
197
+ /** The Shizen hard-drop preview: the one-step rebound and where the pair locks (`finalCells`), and where each stone of the pair comes to rest once the committed drop's gravity has run (`settledCells`). */
198
+ export interface ChainsPowerDropPreview extends PowerDropPreview {
199
+ readonly settledCells: readonly NaturePieceCell[];
200
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/houseki",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Houseki 宝石: five original gem and stone puzzle engines, 450 graded challenges, bilingual lessons, deterministic saves, magnetic floors, Shizen and Arashi campaigns. TypeScript. Zero dependencies.",
5
5
  "keywords": [
6
6
  "puzzle",
@@ -108,10 +108,10 @@
108
108
  "check": "pnpm lint && pnpm typecheck && pnpm test",
109
109
  "prepublishOnly": "pnpm check && pnpm build",
110
110
  "site": "pnpm build && node scripts/site.mjs",
111
- "test:demo": "pnpm site && playwright test && node --test --test-concurrency=2 e2e/review/appearance-api.test.mjs e2e/review/endings.test.mjs e2e/review/full-board-recovery.test.mjs e2e/review/nature-previews.test.mjs e2e/review/responsive.test.mjs",
111
+ "test:demo": "pnpm site && playwright test && node --test --test-concurrency=2 e2e/review/appearance-api.test.mjs e2e/review/boards-settings-and-keys.test.mjs e2e/review/endings.test.mjs e2e/review/full-board-recovery.test.mjs e2e/review/nature-previews.test.mjs e2e/review/responsive.test.mjs",
112
112
  "screenshots:readme": "pnpm site && node scripts/readme-pictures.mjs",
113
113
  "test:readme": "pnpm build && node scripts/check-readme-examples.mjs",
114
- "test:review": "pnpm site && node --test --test-concurrency=2 e2e/review/appearance-api.test.mjs e2e/review/endings.test.mjs e2e/review/full-board-recovery.test.mjs e2e/review/nature-previews.test.mjs e2e/review/responsive.test.mjs"
114
+ "test:review": "pnpm site && node --test --test-concurrency=2 e2e/review/appearance-api.test.mjs e2e/review/boards-settings-and-keys.test.mjs e2e/review/endings.test.mjs e2e/review/full-board-recovery.test.mjs e2e/review/nature-previews.test.mjs e2e/review/responsive.test.mjs"
115
115
  },
116
116
  "devDependencies": {
117
117
  "@eslint/js": "^9.0.0",