@datamoc/mw_games 0.7.3 → 0.7.6

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 (112) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/README.md +16 -3
  3. package/dist/board/FogOfWar.d.ts +6 -0
  4. package/dist/board/FogOfWar.d.ts.map +1 -1
  5. package/dist/board/FogOfWar.js +15 -0
  6. package/dist/board/FogOfWar.js.map +1 -1
  7. package/dist/core/ActionJournal.d.ts +36 -0
  8. package/dist/core/ActionJournal.d.ts.map +1 -0
  9. package/dist/core/ActionJournal.js +60 -0
  10. package/dist/core/ActionJournal.js.map +1 -0
  11. package/dist/core/Blob.d.ts +13 -2
  12. package/dist/core/Blob.d.ts.map +1 -1
  13. package/dist/core/Blob.js +16 -2
  14. package/dist/core/Blob.js.map +1 -1
  15. package/dist/core/Pathfinding.d.ts +41 -0
  16. package/dist/core/Pathfinding.d.ts.map +1 -0
  17. package/dist/core/Pathfinding.js +86 -0
  18. package/dist/core/Pathfinding.js.map +1 -0
  19. package/dist/core/State.d.ts +59 -0
  20. package/dist/core/State.d.ts.map +1 -0
  21. package/dist/core/State.js +100 -0
  22. package/dist/core/State.js.map +1 -0
  23. package/dist/core/index.d.ts +6 -0
  24. package/dist/core/index.d.ts.map +1 -1
  25. package/dist/core/index.js +3 -0
  26. package/dist/core/index.js.map +1 -1
  27. package/dist/mw_games.global.js +97 -97
  28. package/dist/mwl/hooks.d.ts +3 -2
  29. package/dist/mwl/hooks.d.ts.map +1 -1
  30. package/dist/mwl/hooks.js.map +1 -1
  31. package/dist/mwl/index.d.ts +3 -3
  32. package/dist/mwl/index.d.ts.map +1 -1
  33. package/dist/mwl/index.js +1 -1
  34. package/dist/mwl/index.js.map +1 -1
  35. package/dist/mwl/report.d.ts +17 -1
  36. package/dist/mwl/report.d.ts.map +1 -1
  37. package/dist/mwl/report.js +33 -0
  38. package/dist/mwl/report.js.map +1 -1
  39. package/dist/mwl/runtime.d.ts +46 -1
  40. package/dist/mwl/runtime.d.ts.map +1 -1
  41. package/dist/mwl/runtime.js +237 -19
  42. package/dist/mwl/runtime.js.map +1 -1
  43. package/dist/mwl/schema.d.ts.map +1 -1
  44. package/dist/mwl/schema.js +136 -1
  45. package/dist/mwl/schema.js.map +1 -1
  46. package/dist/roguelike/FieldOfView.d.ts +3 -2
  47. package/dist/roguelike/FieldOfView.d.ts.map +1 -1
  48. package/dist/roguelike/FieldOfView.js +3 -2
  49. package/dist/roguelike/FieldOfView.js.map +1 -1
  50. package/dist/roguelike/Level.d.ts +9 -1
  51. package/dist/roguelike/Level.d.ts.map +1 -1
  52. package/dist/roguelike/Level.js +6 -2
  53. package/dist/roguelike/Level.js.map +1 -1
  54. package/dist/roguelike/Scheduler.d.ts +5 -1
  55. package/dist/roguelike/Scheduler.d.ts.map +1 -1
  56. package/dist/roguelike/Scheduler.js +10 -3
  57. package/dist/roguelike/Scheduler.js.map +1 -1
  58. package/dist/roguelike/Targeting.d.ts +30 -0
  59. package/dist/roguelike/Targeting.d.ts.map +1 -1
  60. package/dist/roguelike/Targeting.js +37 -0
  61. package/dist/roguelike/Targeting.js.map +1 -1
  62. package/dist/roguelike/index.d.ts +2 -2
  63. package/dist/roguelike/index.d.ts.map +1 -1
  64. package/dist/roguelike/index.js +1 -1
  65. package/dist/roguelike/index.js.map +1 -1
  66. package/dist/simulation/Campaign.d.ts +60 -0
  67. package/dist/simulation/Campaign.d.ts.map +1 -0
  68. package/dist/simulation/Campaign.js +85 -0
  69. package/dist/simulation/Campaign.js.map +1 -0
  70. package/dist/simulation/Harness.d.ts +26 -0
  71. package/dist/simulation/Harness.d.ts.map +1 -0
  72. package/dist/simulation/Harness.js +25 -0
  73. package/dist/simulation/Harness.js.map +1 -0
  74. package/dist/simulation/index.d.ts +4 -0
  75. package/dist/simulation/index.d.ts.map +1 -1
  76. package/dist/simulation/index.js +2 -0
  77. package/dist/simulation/index.js.map +1 -1
  78. package/dist/two-d/render/ImageModifiers.d.ts +58 -0
  79. package/dist/two-d/render/ImageModifiers.d.ts.map +1 -0
  80. package/dist/two-d/render/ImageModifiers.js +145 -0
  81. package/dist/two-d/render/ImageModifiers.js.map +1 -0
  82. package/dist/two-d/render/Minimap.d.ts +8 -0
  83. package/dist/two-d/render/Minimap.d.ts.map +1 -1
  84. package/dist/two-d/render/Minimap.js +15 -10
  85. package/dist/two-d/render/Minimap.js.map +1 -1
  86. package/dist/two-d/render/Particles.d.ts +16 -0
  87. package/dist/two-d/render/Particles.d.ts.map +1 -1
  88. package/dist/two-d/render/Particles.js +15 -2
  89. package/dist/two-d/render/Particles.js.map +1 -1
  90. package/dist/two-d/render/index.d.ts +3 -1
  91. package/dist/two-d/render/index.d.ts.map +1 -1
  92. package/dist/two-d/render/index.js +1 -0
  93. package/dist/two-d/render/index.js.map +1 -1
  94. package/dist/two-d/ui/Bar.d.ts +21 -2
  95. package/dist/two-d/ui/Bar.d.ts.map +1 -1
  96. package/dist/two-d/ui/Bar.js +27 -5
  97. package/dist/two-d/ui/Bar.js.map +1 -1
  98. package/dist/two-d/ui/FloatingText.d.ts +42 -1
  99. package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
  100. package/dist/two-d/ui/FloatingText.js +52 -4
  101. package/dist/two-d/ui/FloatingText.js.map +1 -1
  102. package/dist/two-d/ui/FloatingTextStack.d.ts +77 -0
  103. package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -0
  104. package/dist/two-d/ui/FloatingTextStack.js +99 -0
  105. package/dist/two-d/ui/FloatingTextStack.js.map +1 -0
  106. package/dist/two-d/ui/index.d.ts +3 -1
  107. package/dist/two-d/ui/index.d.ts.map +1 -1
  108. package/dist/two-d/ui/index.js +2 -1
  109. package/dist/two-d/ui/index.js.map +1 -1
  110. package/dist/version.d.ts +1 -1
  111. package/dist/version.js +1 -1
  112. package/package.json +9 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,43 @@ All notable changes to `mwg` are documented here. Format follows
5
5
  [Semantic Versioning](https://semver.org/) as of this first release - a 0.y.z version means
6
6
  the public API may still change between minor versions.
7
7
 
8
+ ## [0.7.6] - 2026-09-11
9
+
10
+ ### Added
11
+
12
+ - `Blob.spread` returns the cells it just emptied, in reading order, so a game can turn the ground
13
+ under a fire to embers at the moment the flames leave it instead of diffing `cellsAbove` between
14
+ two steps to notice. Cells emptied before the step (a doused one) are not reported.
15
+
16
+ ### Changed
17
+
18
+ - `pixi.js` moved from `dependencies` to an optional peer dependency, the treatment
19
+ `@babylonjs/core` already had (item 175's 1.0 decision). The install line now names it:
20
+ `npm install @datamoc/mw_games pixi.js vite`. A game that only uses the renderer-free modules
21
+ never downloads it. Verified from an empty directory on all three documented paths: the
22
+ registry install, the `npm pack` tarball installed by path, and the no-install
23
+ `mw_games.global.js` script tag.
24
+
25
+ ## [0.7.4] - 2026-09-11
26
+
27
+ ### Added
28
+
29
+ - `FloatingTextStack` owns the pop-ups a game spawns over world points: one `push` per number, one
30
+ `update(dt)` to drive them all, and pop-ups sharing a key and an origin stack apart instead of
31
+ overprinting. `FloatingText` gained a `hold` option for the classic hold-then-fade curve, and its
32
+ rise now moves an inner layer rather than the container itself - it used to overwrite the
33
+ container's own `y`, so a pop-up positioned at a world point jumped to `y = 0` on its first
34
+ update. The fade curve and the stacking rule are exported as pure functions, so both are tested
35
+ without a DOM.
36
+ - `ParticleEmitter` accepts a `frames` texture sequence, walked per particle across its own life -
37
+ the four frames of a flame, the puff of smoke - in place of the single `texture`. The current
38
+ frame is exposed as `Particle.frame` for the same reason the rest of the particle state is.
39
+ - `Bar` recolours its fill at runtime with `setColor(color)` and takes a `background` colour for its
40
+ track; both are readable back through `color`/`background`, and a runtime colour counts as
41
+ explicit so a later theme change cannot throw it away. These were the two blockers for a consumer
42
+ whose bars change colour with state (a health bar going red as it empties) and whose track is
43
+ black rather than the theme's panel fill.
44
+
8
45
  ## [0.7.3] - 2026-09-11
9
46
 
10
47
  ### Added
@@ -35,8 +72,6 @@ the public API may still change between minor versions.
35
72
  - The standalone bundle baseline, API report, documentation, and roadmap were updated.
36
73
  - Release checks now isolate npm audit and package-smoke caches from user-level npm settings.
37
74
 
38
- ## [Unreleased]
39
-
40
75
  ## [0.7.1] - 2026-09-10
41
76
 
42
77
  ### Added
package/README.md CHANGED
@@ -15,7 +15,7 @@ playable in the browser with no download, plus the generated API reference.
15
15
  planning or porting a demanding game with mwg, with links to the [mwg reference](REFERENCE.md)
16
16
  and the [mwg Pixel Dungeon study](https://datamoc.github.io/mwg-pixel-dungeon/).
17
17
 
18
- > **Status: pre-alpha (v0.7.3).** Every module in the shared floor below, plus optional
18
+ > **Status: pre-alpha (v0.7.4).** Every module in the shared floor below, plus optional
19
19
  > 3D, mobile (Capacitor) and desktop (WebView2) packaging, is built and tested - see
20
20
  > [ROADMAP.md](ROADMAP.md) for the full, numbered history.
21
21
  >
@@ -28,6 +28,14 @@ and the [mwg Pixel Dungeon study](https://datamoc.github.io/mwg-pixel-dungeon/).
28
28
  > Each future major version is meant to carry a name, one per letter of the alphabet: 1.0 takes
29
29
  > a name beginning with A, 2.0 one beginning with B, and so on. None of the names are chosen
30
30
  > yet, and nothing depends on them until they are.
31
+ >
32
+ > **API stability:** everything documented in [REFERENCE.md](REFERENCE.md) and
33
+ > [API_REPORT.md](API_REPORT.md) is meant to be stable from 1.0 on. Anything that is not carries
34
+ > an `@experimental` tag in its own doc comment, and is called out as experimental in the release
35
+ > notes that introduce it, so it can still change or disappear without a major version. A rename
36
+ > or a removal is deprecated first: the old name keeps working, its doc comment carries
37
+ > `@deprecated` naming the replacement, and the release notes list it. 1.0 is the last release
38
+ > where either can move silently.
31
39
 
32
40
  ## What it is for
33
41
 
@@ -86,19 +94,24 @@ for free: a finished game is still a folder you double-click.
86
94
  **With a toolchain**, if you already use a bundler:
87
95
 
88
96
  ```
89
- npm install @datamoc/mw_games
97
+ npm install @datamoc/mw_games pixi.js
90
98
  ```
91
99
  ```ts
92
100
  import { Game, Scene } from '@datamoc/mw_games/core';
93
101
  import { Sprite } from '@datamoc/mw_games/two-d/render';
94
102
  ```
95
103
 
104
+ `pixi.js` is the 2D renderer, and it is an optional peer dependency rather than an installed
105
+ one: a game that only uses `mwg/core`, `mwg/roguelike`, `mwg/actors`, `mwg/i18n` or the other
106
+ logic modules never downloads it. Name it on the install line when a game draws, exactly as
107
+ `@babylonjs/core` is named for 3D.
108
+
96
109
  No npm registry access (an offline machine, a pinned artifact for reproducible builds)?
97
110
  `npm pack @datamoc/mw_games` downloads the exact published tarball; install it by path
98
111
  instead of by name:
99
112
 
100
113
  ```
101
- npm install ./datamoc-mw_games-0.1.1.tgz
114
+ npm install ./datamoc-mw_games-<version>.tgz pixi.js
102
115
  ```
103
116
 
104
117
  **Without either.** Drop one file next to your page and open it: this path exists because
@@ -33,6 +33,12 @@ export declare class FactionFog {
33
33
  private explored;
34
34
  constructor(width: number, height: number);
35
35
  sync(faction: string, sources: readonly VisionCell[], cells: (source: VisionCell) => Iterable<VisionCell>): void;
36
+ /**
37
+ * Mark cells explored without changing what is currently visible: the
38
+ * shroud-clearing half of `sync`, for effects (a revealed map, a scouted
39
+ * region) that lift the shroud where no unit stands watch.
40
+ */
41
+ reveal(faction: string, cells: Iterable<VisionCell>): void;
36
42
  isVisible(faction: string, x: number, y: number): boolean;
37
43
  isExplored(faction: string, x: number, y: number): boolean;
38
44
  visibleCells(faction: string): readonly number[];
@@ -1 +1 @@
1
- {"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,UAAU;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAkC;IAElD,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAW/G;IAED,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzD;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAE/C;IACD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAEhD;IAED,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,KAAK;CAGb"}
1
+ {"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,UAAU;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAkC;IAElD,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAW/G;IAED;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAOzD;IAED,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzD;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAE/C;IACD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAEhD;IAED,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,KAAK;CAGb"}
@@ -46,6 +46,21 @@ export class FactionFog {
46
46
  memory.add(cell);
47
47
  this.explored.set(faction, memory);
48
48
  }
49
+ /**
50
+ * Mark cells explored without changing what is currently visible: the
51
+ * shroud-clearing half of `sync`, for effects (a revealed map, a scouted
52
+ * region) that lift the shroud where no unit stands watch.
53
+ */
54
+ reveal(faction, cells) {
55
+ if (!faction)
56
+ throw new Error('fog needs a faction id');
57
+ const memory = this.explored.get(faction) ?? new Set();
58
+ for (const cell of cells) {
59
+ if (this.inside(cell.x, cell.y))
60
+ memory.add(this.index(cell.x, cell.y));
61
+ }
62
+ this.explored.set(faction, memory);
63
+ }
49
64
  isVisible(faction, x, y) {
50
65
  return this.visible.get(faction)?.has(this.index(x, y)) ?? false;
51
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FogOfWar.js","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,UAAU;IAMtB,YAAY,KAAa,EAAE,MAAc;QAHjC,YAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;QACzC,aAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;QAGjD,IAAI,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC9E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,OAA8B,EAAE,KAAmD;QACxG,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,MAAM,IAAI,OAAO;YAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;oBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAC/D,KAAK,MAAM,IAAI,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,CAAC,OAAe,EAAE,CAAS,EAAE,CAAS;QAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,OAAe,EAAE,CAAS,EAAE,CAAS;QAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IACnE,CAAC;IAED,YAAY,CAAC,OAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,aAAa,CAAC,OAAe;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAEO,MAAM,CAAC,CAAS,EAAE,CAAS;QAClC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9D,CAAC;IACO,KAAK,CAAC,CAAS,EAAE,CAAS;QACjC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;CACD"}
1
+ {"version":3,"file":"FogOfWar.js","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /** One deterministic action and the framework/game events it produced. */
2
+ export interface ActionJournalEntry<Action, Event> {
3
+ readonly sequence: number;
4
+ readonly action: Action;
5
+ readonly events: readonly Event[];
6
+ }
7
+ /**
8
+ * A serializable, append-only action log for replay, undo checkpoints, synchronization and
9
+ * debugging. It does not interpret actions or events: the game decides how to reapply an
10
+ * action, while the journal preserves the exact order and resulting event batch.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { ActionJournal } from '@datamoc/mw_games/core';
15
+ *
16
+ * const journal = new ActionJournal<string, { type: string }>();
17
+ * journal.append('open-door', [{ type: 'door-opened' }]);
18
+ * const checkpoint = journal.mark();
19
+ * // An undo system can restore its state and replay journal.since(checkpoint).
20
+ * ```
21
+ */
22
+ export declare class ActionJournal<Action, Event> {
23
+ private entries;
24
+ private nextSequence;
25
+ append(action: Action, events?: readonly Event[]): ActionJournalEntry<Action, Event>;
26
+ get size(): number;
27
+ get all(): readonly ActionJournalEntry<Action, Event>[];
28
+ /** Returns a sequence number that can be used as an undo or sync checkpoint. */
29
+ mark(): number;
30
+ since(sequence: number): ActionJournalEntry<Action, Event>[];
31
+ /** Drops entries at and after a checkpoint, as an undo operation would. */
32
+ truncate(sequence: number): void;
33
+ toJSON(): ActionJournalEntry<Action, Event>[];
34
+ static fromJSON<Action, Event>(entries: readonly ActionJournalEntry<Action, Event>[]): ActionJournal<Action, Event>;
35
+ }
36
+ //# sourceMappingURL=ActionJournal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionJournal.d.ts","sourceRoot":"","sources":["../../src/core/ActionJournal.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB,CAAC,MAAM,EAAE,KAAK;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,aAAa,CAAC,MAAM,EAAE,KAAK;IACvC,OAAO,CAAC,OAAO,CAA2C;IAC1D,OAAO,CAAC,YAAY,CAAK;IAEzB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,KAAK,EAAO,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAIvF;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,GAAG,IAAI,SAAS,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAEtD;IAED,gFAAgF;IAChF,IAAI,IAAI,MAAM,CAEb;IAED,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAE3D;IAED,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG/B;IAED,MAAM,IAAI,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAE5C;IAED,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAC5B,OAAO,EAAE,SAAS,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GACnD,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAa9B;CACD"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * A serializable, append-only action log for replay, undo checkpoints, synchronization and
3
+ * debugging. It does not interpret actions or events: the game decides how to reapply an
4
+ * action, while the journal preserves the exact order and resulting event batch.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { ActionJournal } from '@datamoc/mw_games/core';
9
+ *
10
+ * const journal = new ActionJournal<string, { type: string }>();
11
+ * journal.append('open-door', [{ type: 'door-opened' }]);
12
+ * const checkpoint = journal.mark();
13
+ * // An undo system can restore its state and replay journal.since(checkpoint).
14
+ * ```
15
+ */
16
+ export class ActionJournal {
17
+ constructor() {
18
+ this.entries = [];
19
+ this.nextSequence = 0;
20
+ }
21
+ append(action, events = []) {
22
+ const entry = { sequence: this.nextSequence++, action, events: structuredClone([...events]) };
23
+ this.entries.push(entry);
24
+ return structuredClone(entry);
25
+ }
26
+ get size() {
27
+ return this.entries.length;
28
+ }
29
+ get all() {
30
+ return structuredClone(this.entries);
31
+ }
32
+ /** Returns a sequence number that can be used as an undo or sync checkpoint. */
33
+ mark() {
34
+ return this.nextSequence;
35
+ }
36
+ since(sequence) {
37
+ return structuredClone(this.entries.filter((entry) => entry.sequence >= sequence));
38
+ }
39
+ /** Drops entries at and after a checkpoint, as an undo operation would. */
40
+ truncate(sequence) {
41
+ this.entries = this.entries.filter((entry) => entry.sequence < sequence);
42
+ this.nextSequence = sequence;
43
+ }
44
+ toJSON() {
45
+ return this.all;
46
+ }
47
+ static fromJSON(entries) {
48
+ const journal = new ActionJournal();
49
+ let expected = 0;
50
+ for (const entry of entries) {
51
+ if (!Number.isSafeInteger(entry.sequence) || entry.sequence !== expected)
52
+ throw new Error('action journal sequences must be contiguous from zero');
53
+ journal.entries.push(structuredClone({ sequence: entry.sequence, action: entry.action, events: [...entry.events] }));
54
+ expected++;
55
+ }
56
+ journal.nextSequence = expected;
57
+ return journal;
58
+ }
59
+ }
60
+ //# sourceMappingURL=ActionJournal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionJournal.js","sourceRoot":"","sources":["../../src/core/ActionJournal.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,aAAa;IAA1B;QACS,YAAO,GAAwC,EAAE,CAAC;QAClD,iBAAY,GAAG,CAAC,CAAC;IAmD1B,CAAC;IAjDA,MAAM,CAAC,MAAc,EAAE,MAAM,GAAqB,EAAE;QACnD,MAAM,KAAK,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;QAC9F,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,IAAI,GAAG;QACN,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,gFAAgF;IAChF,IAAI;QACH,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAgB;QACrB,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,2EAA2E;IAC3E,QAAQ,CAAC,QAAgB;QACxB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;QACzE,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;IAC9B,CAAC;IAED,MAAM;QACL,OAAO,IAAI,CAAC,GAA0C,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,QAAQ,CACd,OAAqD;QAErD,MAAM,OAAO,GAAG,IAAI,aAAa,EAAiB,CAAC;QACnD,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ;gBACvE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC1E,OAAO,CAAC,OAAO,CAAC,IAAI,CACnB,eAAe,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAC9F,CAAC;YACF,QAAQ,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC;QAChC,OAAO,OAAO,CAAC;IAChB,CAAC;CACD"}
@@ -19,7 +19,11 @@
19
19
  * const fire = new Blob(20, 20);
20
20
  * fire.seed(5, 5, 10); // a fireball lands
21
21
  *
22
- * fire.spread((x, y) => x >= 0 && y >= 0 && x < 20 && y < 20, 0.25, 0.9); // one turn of spreading
22
+ * // one turn of spreading; it hands back whatever it just burned out, so the ground under
23
+ * // the fire can turn to embers at the moment the flames leave it
24
+ * const burntOut = fire.spread((x, y) => x >= 0 && y >= 0 && x < 20 && y < 20, 0.25, 0.9);
25
+ * for (const cell of burntOut) console.log('burned out at', cell.x, cell.y);
26
+ *
23
27
  * for (const cell of fire.cellsAbove(0.5)) console.log('burning at', cell.x, cell.y);
24
28
  *
25
29
  * fire.clear(5, 5); // someone douses that one tile
@@ -46,8 +50,15 @@ export declare class Blob {
46
50
  * `decay` of what it ends up with. `decay: 1` conserves volume and only moves it around;
47
51
  * anything lower thins the effect out over time. `open(x, y)` decides whether the effect
48
52
  * may cross into a cell, so the caller chooses what blocks it.
53
+ *
54
+ * Returns the cells this step emptied, in reading order: those holding a volume before the
55
+ * step and none after it. That is the moment a game turns the grass under a fire to embers,
56
+ * and reporting it here saves the game diffing `cellsAbove` between two steps to notice.
49
57
  */
50
- spread(open: (x: number, y: number) => boolean, spread?: number, decay?: number): void;
58
+ spread(open: (x: number, y: number) => boolean, spread?: number, decay?: number): Array<{
59
+ x: number;
60
+ y: number;
61
+ }>;
51
62
  /** every cell holding at least `minimum` - the cells a game applies its effect on */
52
63
  cellsAbove(minimum: number): Array<{
53
64
  x: number;
@@ -1 +1 @@
1
- {"version":3,"file":"Blob.d.ts","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAUH,qBAAa,IAAI;IAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,OAAO,CAAC,MAAM,CAAe;IAE7B,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,mEAAmE;IACnE,OAAO,CAAC,KAAK;IAKb,4DAA4D;IAC5D,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrC;IAED,iFAAiF;IACjF,KAAK,IAAI,MAAM,CAId;IAED,2EAA2E;IAC3E,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAG/C;IAED,yFAAyF;IACzF,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGhC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,SAAO,EAAE,KAAK,SAAM,GAAG,IAAI,CAgChF;IAED,qFAAqF;IACrF,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAS3E;IAED,MAAM,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAE5D;IAED,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAI/E;CACD"}
1
+ {"version":3,"file":"Blob.d.ts","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAUH,qBAAa,IAAI;IAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,OAAO,CAAC,MAAM,CAAe;IAE7B,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,mEAAmE;IACnE,OAAO,CAAC,KAAK;IAKb,4DAA4D;IAC5D,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrC;IAED,iFAAiF;IACjF,KAAK,IAAI,MAAM,CAId;IAED,2EAA2E;IAC3E,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAG/C;IAED,yFAAyF;IACzF,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGhC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,SAAO,EAAE,KAAK,SAAM,GAAG,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAsC3G;IAED,qFAAqF;IACrF,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAS3E;IAED,MAAM,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAE5D;IAED,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAI/E;CACD"}
package/dist/core/Blob.js CHANGED
@@ -19,7 +19,11 @@
19
19
  * const fire = new Blob(20, 20);
20
20
  * fire.seed(5, 5, 10); // a fireball lands
21
21
  *
22
- * fire.spread((x, y) => x >= 0 && y >= 0 && x < 20 && y < 20, 0.25, 0.9); // one turn of spreading
22
+ * // one turn of spreading; it hands back whatever it just burned out, so the ground under
23
+ * // the fire can turn to embers at the moment the flames leave it
24
+ * const burntOut = fire.spread((x, y) => x >= 0 && y >= 0 && x < 20 && y < 20, 0.25, 0.9);
25
+ * for (const cell of burntOut) console.log('burned out at', cell.x, cell.y);
26
+ *
23
27
  * for (const cell of fire.cellsAbove(0.5)) console.log('burning at', cell.x, cell.y);
24
28
  *
25
29
  * fire.clear(5, 5); // someone douses that one tile
@@ -74,6 +78,10 @@ export class Blob {
74
78
  * `decay` of what it ends up with. `decay: 1` conserves volume and only moves it around;
75
79
  * anything lower thins the effect out over time. `open(x, y)` decides whether the effect
76
80
  * may cross into a cell, so the caller chooses what blocks it.
81
+ *
82
+ * Returns the cells this step emptied, in reading order: those holding a volume before the
83
+ * step and none after it. That is the moment a game turns the grass under a fire to embers,
84
+ * and reporting it here saves the game diffing `cellsAbove` between two steps to notice.
77
85
  */
78
86
  spread(open, spread = 0.25, decay = 0.9) {
79
87
  const next = new Float32Array(this.volume.length);
@@ -102,11 +110,17 @@ export class Blob {
102
110
  }
103
111
  }
104
112
  }
113
+ const emptied = [];
105
114
  for (let i = 0; i < next.length; i++) {
106
115
  const kept = next[i] * decay;
107
116
  //snap float dust to zero so a burned-out effect actually reads as gone
108
- this.volume[i] = kept < 0.001 ? 0 : kept;
117
+ const volume = kept < 0.001 ? 0 : kept;
118
+ if (volume === 0 && this.volume[i] > 0) {
119
+ emptied.push({ x: i % this.width, y: Math.floor(i / this.width) });
120
+ }
121
+ this.volume[i] = volume;
109
122
  }
123
+ return emptied;
110
124
  }
111
125
  /** every cell holding at least `minimum` - the cells a game applies its effect on */
112
126
  cellsAbove(minimum) {
@@ -1 +1 @@
1
- {"version":3,"file":"Blob.js","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Blob.js","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /** A cell reached by a weighted flood. */
2
+ export interface WeightedCell<T> {
3
+ readonly cell: T;
4
+ readonly cost: number;
5
+ }
6
+ export interface WeightedFloodOptions<T, K> {
7
+ /** Stable identity for a cell; it may be a string, number, or object key. */
8
+ key: (cell: T) => K;
9
+ /** Adjacent cells, in whatever topology the game uses. */
10
+ neighbors: (cell: T) => Iterable<T>;
11
+ /** Cost of entering `to` from `from`; non-finite or negative costs are ignored. */
12
+ cost: (from: T, to: T) => number;
13
+ /** Maximum accumulated cost, inclusive. */
14
+ maxCost: number;
15
+ /** Optional game rule that rejects a candidate before it is reached. */
16
+ canEnter?: (cell: T, from: T, cost: number) => boolean;
17
+ /** Optional game rule that keeps a reached cell from expanding further. */
18
+ stop?: (cell: T, cost: number) => boolean;
19
+ }
20
+ /**
21
+ * Dijkstra-style weighted flood fill, independent of a particular board or game rule.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { weightedFlood } from '@datamoc/mw_games/core';
26
+ *
27
+ * const cells = weightedFlood(0, {
28
+ * key: (cell) => cell,
29
+ * neighbors: (cell) => cell < 3 ? [cell + 1] : [],
30
+ * cost: () => 1,
31
+ * maxCost: 2,
32
+ * });
33
+ * console.log(cells.get(2)?.cost); // 2
34
+ * ```
35
+ *
36
+ * The returned map includes `start` at cost 0. A game can use `canEnter` for blockers and
37
+ * `stop` for rules such as a zone-of-control boundary; neither callback is interpreted here.
38
+ * Equal-cost revisits are discarded, and the input cells are never mutated.
39
+ */
40
+ export declare function weightedFlood<T, K>(start: T, options: WeightedFloodOptions<T, K>): Map<K, WeightedCell<T>>;
41
+ //# sourceMappingURL=Pathfinding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pathfinding.d.ts","sourceRoot":"","sources":["../../src/core/Pathfinding.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,MAAM,WAAW,YAAY,CAAC,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,EAAE,CAAC;IACzC,6EAA6E;IAC7E,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IACpB,0DAA0D;IAC1D,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpC,mFAAmF;IACnF,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACvD,2EAA2E;IAC3E,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CA6B1G"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Dijkstra-style weighted flood fill, independent of a particular board or game rule.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { weightedFlood } from '@datamoc/mw_games/core';
7
+ *
8
+ * const cells = weightedFlood(0, {
9
+ * key: (cell) => cell,
10
+ * neighbors: (cell) => cell < 3 ? [cell + 1] : [],
11
+ * cost: () => 1,
12
+ * maxCost: 2,
13
+ * });
14
+ * console.log(cells.get(2)?.cost); // 2
15
+ * ```
16
+ *
17
+ * The returned map includes `start` at cost 0. A game can use `canEnter` for blockers and
18
+ * `stop` for rules such as a zone-of-control boundary; neither callback is interpreted here.
19
+ * Equal-cost revisits are discarded, and the input cells are never mutated.
20
+ */
21
+ export function weightedFlood(start, options) {
22
+ if (!Number.isFinite(options.maxCost) || options.maxCost < 0)
23
+ return new Map();
24
+ const startKey = options.key(start);
25
+ const reached = new Map([[startKey, { cell: start, cost: 0 }]]);
26
+ const queue = [{ cell: start, key: startKey, cost: 0 }];
27
+ while (queue.length > 0) {
28
+ const current = pop(queue);
29
+ const known = reached.get(current.key);
30
+ if (!known || current.cost !== known.cost)
31
+ continue;
32
+ if (options.stop?.(current.cell, current.cost))
33
+ continue;
34
+ for (const next of options.neighbors(current.cell)) {
35
+ const step = options.cost(current.cell, next);
36
+ if (!Number.isFinite(step) || step < 0)
37
+ continue;
38
+ const total = current.cost + step;
39
+ if (total > options.maxCost)
40
+ continue;
41
+ if (options.canEnter && !options.canEnter(next, current.cell, total))
42
+ continue;
43
+ const key = options.key(next);
44
+ if (total >= (reached.get(key)?.cost ?? Infinity))
45
+ continue;
46
+ const entry = { cell: next, key, cost: total };
47
+ reached.set(key, entry);
48
+ push(queue, entry);
49
+ }
50
+ }
51
+ return reached;
52
+ }
53
+ function push(queue, entry) {
54
+ queue.push(entry);
55
+ let index = queue.length - 1;
56
+ while (index > 0) {
57
+ const parent = Math.floor((index - 1) / 2);
58
+ if (queue[parent].cost <= queue[index].cost)
59
+ break;
60
+ [queue[parent], queue[index]] = [queue[index], queue[parent]];
61
+ index = parent;
62
+ }
63
+ }
64
+ function pop(queue) {
65
+ const first = queue[0];
66
+ const last = queue.pop();
67
+ if (!first || !last || queue.length === 0)
68
+ return first;
69
+ queue[0] = last;
70
+ let index = 0;
71
+ while (true) {
72
+ const left = index * 2 + 1;
73
+ const right = left + 1;
74
+ let smallest = index;
75
+ if (left < queue.length && queue[left].cost < queue[smallest].cost)
76
+ smallest = left;
77
+ if (right < queue.length && queue[right].cost < queue[smallest].cost)
78
+ smallest = right;
79
+ if (smallest === index)
80
+ break;
81
+ [queue[index], queue[smallest]] = [queue[smallest], queue[index]];
82
+ index = smallest;
83
+ }
84
+ return first;
85
+ }
86
+ //# sourceMappingURL=Pathfinding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pathfinding.js","sourceRoot":"","sources":["../../src/core/Pathfinding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /** A JSON-compatible value owned by a game or one of its framework extensions. */
2
+ export type StateValue = null | boolean | number | string | StateValue[] | {
3
+ readonly [key: string]: StateValue;
4
+ };
5
+ export interface StateExtension<T extends StateValue = StateValue> {
6
+ /** stable key written into a snapshot */
7
+ readonly id: string;
8
+ /** capture the extension's complete serializable state */
9
+ readonly capture: () => T;
10
+ /** restore a previously captured state atomically */
11
+ readonly restore: (state: T) => void;
12
+ /** Current schema version for this extension, independent of other save data. */
13
+ readonly version?: number;
14
+ /** Migrations are keyed by destination version and run in order. */
15
+ readonly migrations?: Readonly<Record<number, (state: StateValue) => T>>;
16
+ /** Optional reset used when a save predates this extension. */
17
+ readonly reset?: () => void;
18
+ /** Optional cleanup when the extension is deliberately absent from a restored save. */
19
+ readonly remove?: () => void;
20
+ }
21
+ export interface StateSnapshot {
22
+ readonly extensions: Readonly<Record<string, StateValue>>;
23
+ readonly versions?: Readonly<Record<string, number>>;
24
+ }
25
+ export interface StateRestoreDiagnostic {
26
+ readonly extension: string;
27
+ readonly from: number;
28
+ readonly to: number;
29
+ readonly status: 'migrated' | 'unchanged' | 'reset' | 'removed';
30
+ }
31
+ /**
32
+ * Coordinates game-owned save state without knowing its schema.
33
+ *
34
+ * Each extension owns its data and restore logic. `transaction` captures all registered
35
+ * extensions before running work and restores that snapshot if work throws, which prevents
36
+ * half-applied saves when one subsystem rejects an update.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { StateRegistry } from '@datamoc/mw_games/core';
41
+ *
42
+ * let inventory = { gold: 10 };
43
+ * const state = new StateRegistry();
44
+ * state.register({ id: 'inventory', capture: () => inventory, restore: (saved) => { inventory = saved; } });
45
+ * state.transaction(() => { inventory.gold -= 5; });
46
+ * const save = state.snapshot();
47
+ * ```
48
+ */
49
+ export declare class StateRegistry {
50
+ private extensions;
51
+ register<T extends StateValue>(extension: StateExtension<T>): () => void;
52
+ snapshot(): StateSnapshot;
53
+ restore(snapshot: StateSnapshot, options?: {
54
+ readonly missing?: 'keep' | 'reset' | 'remove';
55
+ readonly onDiagnostic?: (diagnostic: StateRestoreDiagnostic) => void;
56
+ }): readonly StateRestoreDiagnostic[];
57
+ transaction<T>(work: () => T): T;
58
+ }
59
+ //# sourceMappingURL=State.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"State.d.ts","sourceRoot":"","sources":["../../src/core/State.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAElH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAChE,yCAAyC;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACrC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC;IACzE,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;CAChE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,UAAU,CAAqC;IAEvD,QAAQ,CAAC,CAAC,SAAS,UAAU,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAcvE;IAED,QAAQ,IAAI,aAAa,CAMxB;IAED,OAAO,CACN,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE;QACR,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;QAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI,CAAC;KAChE,GACJ,SAAS,sBAAsB,EAAE,CAiCnC;IAED,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ/B;CACD"}