@reicek/neataptic-ts 0.1.21 → 0.1.22

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 (223) hide show
  1. package/.github/agents/boundary-mapper.agent.md +29 -0
  2. package/.github/agents/docs-scout.agent.md +29 -0
  3. package/.github/agents/plan-scout.agent.md +29 -0
  4. package/.github/agents/solid-split.agent.md +138 -0
  5. package/.github/copilot-instructions.md +103 -0
  6. package/package.json +6 -3
  7. package/plans/ES2023 migration +13 -8
  8. package/plans/Evolution_Training_Interoperability_Contracts.md +1 -1
  9. package/plans/Interactive_Examples_and_Learning_Path.md +10 -2
  10. package/plans/Memory_Optimization.md +3 -3
  11. package/plans/README.md +63 -0
  12. package/plans/Roadmap.md +15 -3
  13. package/plans/asciiMaze_SOLID_split.done.md +130 -0
  14. package/plans/flappy_bird_SOLID_split.done.md +67 -0
  15. package/scripts/assets/theme.css +221 -34
  16. package/scripts/copy-examples.mjs +9 -5
  17. package/scripts/export-onnx.mjs +3 -3
  18. package/scripts/generate-bench-tables.mjs +10 -10
  19. package/scripts/generate-bench-tables.ts +10 -10
  20. package/scripts/generate-docs.ts +1415 -449
  21. package/scripts/render-docs-html.ts +15 -8
  22. package/src/README.md +101 -223
  23. package/src/architecture/README.md +57 -185
  24. package/src/architecture/layer/README.md +38 -38
  25. package/src/architecture/network/README.md +33 -31
  26. package/src/architecture/network/activate/README.md +77 -77
  27. package/src/architecture/network/connect/README.md +15 -13
  28. package/src/architecture/network/deterministic/README.md +7 -7
  29. package/src/architecture/network/evolve/README.md +44 -44
  30. package/src/architecture/network/gating/README.md +20 -20
  31. package/src/architecture/network/genetic/README.md +51 -51
  32. package/src/architecture/network/mutate/README.md +97 -97
  33. package/src/architecture/network/onnx/README.md +264 -264
  34. package/src/architecture/network/prune/README.md +39 -39
  35. package/src/architecture/network/remove/README.md +26 -26
  36. package/src/architecture/network/serialize/README.md +56 -56
  37. package/src/architecture/network/slab/README.md +61 -61
  38. package/src/architecture/network/standalone/README.md +24 -24
  39. package/src/architecture/network/stats/README.md +9 -9
  40. package/src/architecture/network/topology/README.md +46 -46
  41. package/src/architecture/network/training/README.md +21 -21
  42. package/src/methods/README.md +9 -87
  43. package/src/multithreading/README.md +8 -77
  44. package/src/multithreading/workers/README.md +2 -2
  45. package/src/multithreading/workers/browser/README.md +0 -6
  46. package/src/multithreading/workers/node/README.md +0 -3
  47. package/src/neat/README.md +562 -568
  48. package/src/utils/README.md +18 -18
  49. package/test/examples/asciiMaze/README.md +59 -59
  50. package/test/examples/asciiMaze/asciiMaze.e2e.test.ts +14 -9
  51. package/test/examples/asciiMaze/browser-entry/README.md +196 -0
  52. package/test/examples/asciiMaze/browser-entry/browser-entry.abort.services.ts +95 -0
  53. package/test/examples/asciiMaze/browser-entry/browser-entry.constants.ts +23 -0
  54. package/test/examples/asciiMaze/browser-entry/browser-entry.curriculum.services.ts +115 -0
  55. package/test/examples/asciiMaze/browser-entry/browser-entry.globals.services.ts +106 -0
  56. package/test/examples/asciiMaze/browser-entry/browser-entry.host.services.ts +157 -0
  57. package/test/examples/asciiMaze/browser-entry/browser-entry.services.ts +14 -0
  58. package/test/examples/asciiMaze/browser-entry/browser-entry.ts +129 -0
  59. package/test/examples/asciiMaze/browser-entry/browser-entry.types.ts +120 -0
  60. package/test/examples/asciiMaze/browser-entry/browser-entry.utils.ts +98 -0
  61. package/test/examples/asciiMaze/browser-entry.ts +10 -576
  62. package/test/examples/asciiMaze/dashboardManager/README.md +276 -0
  63. package/test/examples/asciiMaze/dashboardManager/archive/README.md +16 -0
  64. package/test/examples/asciiMaze/dashboardManager/archive/dashboardManager.archive.services.ts +267 -0
  65. package/test/examples/asciiMaze/dashboardManager/dashboardManager.constants.ts +35 -0
  66. package/test/examples/asciiMaze/dashboardManager/dashboardManager.services.ts +103 -0
  67. package/test/examples/asciiMaze/dashboardManager/dashboardManager.ts +181 -0
  68. package/test/examples/asciiMaze/dashboardManager/dashboardManager.types.ts +267 -0
  69. package/test/examples/asciiMaze/dashboardManager/dashboardManager.utils.ts +254 -0
  70. package/test/examples/asciiMaze/dashboardManager/live/README.md +14 -0
  71. package/test/examples/asciiMaze/dashboardManager/live/dashboardManager.live.services.ts +264 -0
  72. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +47 -0
  73. package/test/examples/asciiMaze/dashboardManager/telemetry/dashboardManager.telemetry.services.ts +513 -0
  74. package/test/examples/asciiMaze/dashboardManager.ts +13 -2335
  75. package/test/examples/asciiMaze/evolutionEngine/README.md +1058 -0
  76. package/test/examples/asciiMaze/evolutionEngine/curriculumPhase.ts +90 -0
  77. package/test/examples/asciiMaze/evolutionEngine/engineState.constants.ts +36 -0
  78. package/test/examples/asciiMaze/evolutionEngine/engineState.ts +58 -513
  79. package/test/examples/asciiMaze/evolutionEngine/engineState.types.ts +212 -0
  80. package/test/examples/asciiMaze/evolutionEngine/engineState.utils.ts +301 -0
  81. package/test/examples/asciiMaze/evolutionEngine/evolutionEngine.types.ts +445 -0
  82. package/test/examples/asciiMaze/evolutionEngine/evolutionLoop.ts +81 -50
  83. package/test/examples/asciiMaze/evolutionEngine/optionsAndSetup.ts +2 -4
  84. package/test/examples/asciiMaze/evolutionEngine/populationDynamics.ts +17 -33
  85. package/test/examples/asciiMaze/evolutionEngine/populationPruning.ts +1 -1
  86. package/test/examples/asciiMaze/evolutionEngine/rngAndTiming.ts +1 -2
  87. package/test/examples/asciiMaze/evolutionEngine/sampling.ts +1 -1
  88. package/test/examples/asciiMaze/evolutionEngine/scratchPools.ts +2 -5
  89. package/test/examples/asciiMaze/evolutionEngine/setupHelpers.ts +30 -37
  90. package/test/examples/asciiMaze/evolutionEngine/telemetryMetrics.ts +16 -58
  91. package/test/examples/asciiMaze/evolutionEngine/trainingWarmStart.ts +2 -2
  92. package/test/examples/asciiMaze/evolutionEngine.ts +55 -55
  93. package/test/examples/asciiMaze/fitness.ts +2 -2
  94. package/test/examples/asciiMaze/fitness.types.ts +65 -0
  95. package/test/examples/asciiMaze/interfaces.ts +64 -1352
  96. package/test/examples/asciiMaze/mazeMovement/README.md +356 -0
  97. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +49 -0
  98. package/test/examples/asciiMaze/mazeMovement/finalization/mazeMovement.finalization.ts +138 -0
  99. package/test/examples/asciiMaze/mazeMovement/mazeMovement.constants.ts +101 -0
  100. package/test/examples/asciiMaze/mazeMovement/mazeMovement.services.ts +230 -0
  101. package/test/examples/asciiMaze/mazeMovement/mazeMovement.ts +299 -0
  102. package/test/examples/asciiMaze/mazeMovement/mazeMovement.types.ts +185 -0
  103. package/test/examples/asciiMaze/mazeMovement/mazeMovement.utils.ts +153 -0
  104. package/test/examples/asciiMaze/mazeMovement/policy/README.md +91 -0
  105. package/test/examples/asciiMaze/mazeMovement/policy/mazeMovement.policy.ts +467 -0
  106. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +95 -0
  107. package/test/examples/asciiMaze/mazeMovement/runtime/mazeMovement.runtime.ts +354 -0
  108. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +124 -0
  109. package/test/examples/asciiMaze/mazeMovement/shaping/mazeMovement.shaping.ts +459 -0
  110. package/test/examples/asciiMaze/mazeMovement.ts +12 -2978
  111. package/test/examples/flappy_bird/Trace-20260309T191949.json +24124 -0
  112. package/test/examples/flappy_bird/browser-entry/README.md +1129 -0
  113. package/test/examples/flappy_bird/browser-entry/browser-entry.host.utils.ts +4 -324
  114. package/test/examples/flappy_bird/browser-entry/browser-entry.network-view.utils.ts +6 -399
  115. package/test/examples/flappy_bird/browser-entry/browser-entry.playback.utils.ts +1 -717
  116. package/test/examples/flappy_bird/browser-entry/browser-entry.spawn.utils.ts +11 -31
  117. package/test/examples/flappy_bird/browser-entry/browser-entry.visualization.utils.ts +15 -893
  118. package/test/examples/flappy_bird/browser-entry/host/README.md +307 -0
  119. package/test/examples/flappy_bird/browser-entry/host/host.resize.service.ts +1 -295
  120. package/test/examples/flappy_bird/browser-entry/host/host.ts +562 -6
  121. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +274 -0
  122. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.constants.ts +31 -0
  123. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.services.ts +360 -0
  124. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.ts +117 -0
  125. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.types.ts +63 -0
  126. package/test/examples/flappy_bird/browser-entry/host/resize/host.resize.service.utils.ts +250 -0
  127. package/test/examples/flappy_bird/browser-entry/network-view/README.md +399 -0
  128. package/test/examples/flappy_bird/browser-entry/network-view/network-view.topology.utils.ts +255 -0
  129. package/test/examples/flappy_bird/browser-entry/network-view/network-view.ts +802 -7
  130. package/test/examples/flappy_bird/browser-entry/playback/README.md +684 -0
  131. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +277 -0
  132. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +770 -0
  133. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.cache.services.ts +178 -0
  134. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.constants.ts +107 -0
  135. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.geometry.utils.ts +518 -0
  136. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.math.utils.ts +117 -0
  137. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.pulse.utils.ts +233 -0
  138. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.services.ts +211 -0
  139. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.ts +48 -0
  140. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.types.ts +212 -0
  141. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/playback.background.ground-grid.utils.ts +81 -0
  142. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.cache.services.ts +96 -0
  143. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.constants.ts +62 -0
  144. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.services.ts +244 -0
  145. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.ts +53 -0
  146. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.types.ts +68 -0
  147. package/test/examples/flappy_bird/browser-entry/playback/background/playback.background.utils.ts +100 -0
  148. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +310 -0
  149. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.service.ts +92 -0
  150. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.services.ts +272 -0
  151. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.types.ts +39 -0
  152. package/test/examples/flappy_bird/browser-entry/playback/frame-render/playback.frame-render.utils.ts +493 -0
  153. package/test/examples/flappy_bird/browser-entry/playback/playback.constants.ts +1 -1
  154. package/test/examples/flappy_bird/browser-entry/playback/playback.frame-render.service.ts +4 -0
  155. package/test/examples/flappy_bird/browser-entry/playback/playback.snapshot.utils.ts +44 -0
  156. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.service.ts +39 -122
  157. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.services.ts +272 -0
  158. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.types.ts +62 -0
  159. package/test/examples/flappy_bird/browser-entry/playback/playback.starfield.utils.ts +11 -4
  160. package/test/examples/flappy_bird/browser-entry/playback/playback.ts +409 -8
  161. package/test/examples/flappy_bird/browser-entry/playback/playback.types.ts +4 -12
  162. package/test/examples/flappy_bird/browser-entry/runtime/README.md +235 -0
  163. package/test/examples/flappy_bird/browser-entry/runtime/runtime.evolution-launch.service.ts +45 -0
  164. package/test/examples/flappy_bird/browser-entry/runtime/runtime.lifecycle.service.ts +81 -0
  165. package/test/examples/flappy_bird/browser-entry/runtime/runtime.startup.service.ts +74 -0
  166. package/test/examples/flappy_bird/browser-entry/runtime/runtime.ts +31 -121
  167. package/test/examples/flappy_bird/browser-entry/runtime/runtime.types.ts +36 -0
  168. package/test/examples/flappy_bird/browser-entry/visualization/README.md +557 -0
  169. package/test/examples/flappy_bird/browser-entry/visualization/visualization.constants.ts +110 -0
  170. package/test/examples/flappy_bird/browser-entry/visualization/visualization.draw.service.ts +957 -19
  171. package/test/examples/flappy_bird/browser-entry/visualization/visualization.legend.utils.ts +138 -3
  172. package/test/examples/flappy_bird/browser-entry/visualization/visualization.topology.utils.ts +3 -27
  173. package/test/examples/flappy_bird/browser-entry/visualization/visualization.ts +1 -23
  174. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +156 -0
  175. package/test/examples/flappy_bird/constants/README.md +1179 -0
  176. package/test/examples/flappy_bird/constants/constants.network-view.ts +24 -0
  177. package/test/examples/flappy_bird/constants/constants.palette.ts +7 -0
  178. package/test/examples/flappy_bird/constants/constants.starfield.ts +78 -3
  179. package/test/examples/flappy_bird/environment/README.md +143 -0
  180. package/test/examples/flappy_bird/environment/environment.observation.utils.ts +1 -19
  181. package/test/examples/flappy_bird/environment/environment.step.service.ts +3 -66
  182. package/test/examples/flappy_bird/evaluation/README.md +130 -0
  183. package/test/examples/flappy_bird/evaluation/evaluation.fitness.utils.ts +1 -1
  184. package/test/examples/flappy_bird/evaluation/evaluation.rollout.service.ts +5 -375
  185. package/test/examples/flappy_bird/evaluation/rollout/README.md +291 -0
  186. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.constants.ts +30 -0
  187. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.service.ts +58 -0
  188. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.services.ts +310 -0
  189. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.types.ts +56 -0
  190. package/test/examples/flappy_bird/evaluation/rollout/evaluation.rollout.utils.ts +368 -0
  191. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +618 -0
  192. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.playback.service.ts +7 -7
  193. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.frame.service.ts +364 -0
  194. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.types.ts +14 -0
  195. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.simulation.utils.ts +4 -201
  196. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.ts +184 -345
  197. package/test/examples/flappy_bird/flappy-evolution-worker/flappy-evolution-worker.warm-start.service.ts +291 -0
  198. package/test/examples/flappy_bird/flappy.simulation.shared.utils.ts +5 -0
  199. package/test/examples/flappy_bird/simulation-shared/README.md +417 -0
  200. package/test/examples/flappy_bird/simulation-shared/observation/README.md +183 -0
  201. package/test/examples/flappy_bird/simulation-shared/observation/observation.features.utils.ts +301 -0
  202. package/test/examples/flappy_bird/simulation-shared/observation/observation.ts +9 -0
  203. package/test/examples/flappy_bird/simulation-shared/observation/observation.vector.utils.ts +59 -0
  204. package/test/examples/flappy_bird/simulation-shared/simulation-shared.observation.utils.ts +5 -403
  205. package/test/examples/flappy_bird/simulation-shared/simulation-shared.spawn.utils.ts +20 -6
  206. package/test/examples/flappy_bird/{evaluation/evaluation.statistics.utils.ts → simulation-shared/simulation-shared.statistics.utils.ts} +23 -8
  207. package/test/examples/flappy_bird/trainer/README.md +563 -0
  208. package/test/examples/flappy_bird/trainer/evaluation/README.md +199 -0
  209. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.constants.ts +9 -0
  210. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.services.ts +73 -0
  211. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.ts +165 -0
  212. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.types.ts +25 -0
  213. package/test/examples/flappy_bird/trainer/evaluation/trainer.evaluation.service.utils.ts +161 -0
  214. package/test/examples/flappy_bird/trainer/trainer.evaluation.service.ts +13 -0
  215. package/test/examples/flappy_bird/trainer/trainer.report.service.services.ts +181 -0
  216. package/test/examples/flappy_bird/trainer/trainer.report.service.ts +126 -0
  217. package/test/examples/flappy_bird/trainer/trainer.selection.utils.ts +89 -0
  218. package/test/examples/flappy_bird/trainer/trainer.ts +11 -553
  219. package/test/examples/flappy_bird/browser-entry/browser-entry.utils.ts +0 -12
  220. package/test/examples/flappy_bird/environment/environment.ts +0 -7
  221. package/test/examples/flappy_bird/evaluation/evaluation.ts +0 -7
  222. package/test/examples/flappy_bird/simulation-shared/simulation-shared.ts +0 -15
  223. package/test/examples/flappy_bird/trainer/trainer.statistics.utils.ts +0 -78
@@ -1,13 +1,14 @@
1
1
  import {
2
- FLAPPY_STARFIELD_CYAN_FILL_STYLE,
3
- FLAPPY_STARFIELD_FAR_SCROLL_RATIO,
4
- FLAPPY_STARFIELD_MID_SCROLL_RATIO,
5
- FLAPPY_STARFIELD_NEAR_SCROLL_RATIO,
2
+ FLAPPY_STARFIELD_LAYER_SPECS,
3
+ FLAPPY_STARFIELD_MIN_DIMENSION_PX,
6
4
  FLAPPY_STARFIELD_TILE_WIDTH_PX,
7
5
  } from '../../constants/constants';
8
6
  import { cachedStarfieldTilesByHeight } from './playback.constants';
9
- import { createSeededRandom } from './playback.starfield.utils';
10
- import type { StarTile } from './playback.types';
7
+ import { createStarTileCanvas } from './playback.starfield.services';
8
+ import type {
9
+ PlaybackStarfieldLayerSpec,
10
+ StarTile,
11
+ } from './playback.starfield.types';
11
12
 
12
13
  /**
13
14
  * Resolves (and lazily creates) cached starfield tile layers for the viewport.
@@ -18,132 +19,48 @@ import type { StarTile } from './playback.types';
18
19
  export function resolveStarfieldTiles(
19
20
  visibleWorldHeightPx: number,
20
21
  ): readonly StarTile[] {
21
- const tileHeightPx = Math.max(1, Math.round(visibleWorldHeightPx));
22
+ const tileHeightPx = Math.max(
23
+ FLAPPY_STARFIELD_MIN_DIMENSION_PX,
24
+ Math.round(visibleWorldHeightPx),
25
+ );
22
26
  const cachedTilesForHeight = cachedStarfieldTilesByHeight.get(tileHeightPx);
23
27
  if (cachedTilesForHeight) {
24
28
  return cachedTilesForHeight;
25
29
  }
26
30
 
27
- const farTile: StarTile = {
28
- image: createStarTileCanvas({
29
- seed: 1_337,
30
- tileWidthPx: FLAPPY_STARFIELD_TILE_WIDTH_PX,
31
- tileHeightPx,
32
- starCount: 35,
33
- minSizePx: 1,
34
- maxSizePx: 2,
35
- minAlpha: 0.08,
36
- maxAlpha: 0.22,
37
- blurPx: 4,
38
- }),
39
- tileWidthPx: FLAPPY_STARFIELD_TILE_WIDTH_PX,
40
- tileHeightPx,
41
- scrollRatio: FLAPPY_STARFIELD_FAR_SCROLL_RATIO,
42
- };
43
- const midTile: StarTile = {
44
- image: createStarTileCanvas({
45
- seed: 2_777,
46
- tileWidthPx: FLAPPY_STARFIELD_TILE_WIDTH_PX,
47
- tileHeightPx,
48
- starCount: 28,
49
- minSizePx: 1,
50
- maxSizePx: 3,
51
- minAlpha: 0.1,
52
- maxAlpha: 0.28,
53
- blurPx: 6,
54
- }),
55
- tileWidthPx: FLAPPY_STARFIELD_TILE_WIDTH_PX,
56
- tileHeightPx,
57
- scrollRatio: FLAPPY_STARFIELD_MID_SCROLL_RATIO,
58
- };
59
- const nearTile: StarTile = {
31
+ const resolvedTiles = FLAPPY_STARFIELD_LAYER_SPECS.map((layerSpec) =>
32
+ createStarTile(layerSpec, tileHeightPx),
33
+ );
34
+ cachedStarfieldTilesByHeight.set(tileHeightPx, resolvedTiles);
35
+ return resolvedTiles;
36
+ }
37
+
38
+ /**
39
+ * Creates one cached tile layer from a declarative layer specification.
40
+ *
41
+ * @param layerSpec - Density and motion contract for a starfield layer.
42
+ * @param tileHeightPx - Height of the visible sky band in pixels.
43
+ * @returns Cached tile metadata for parallax drawing.
44
+ */
45
+ function createStarTile(
46
+ layerSpec: PlaybackStarfieldLayerSpec,
47
+ tileHeightPx: number,
48
+ ): StarTile {
49
+ // Step 1: Pre-render one deterministic tile for the requested layer.
50
+ return {
60
51
  image: createStarTileCanvas({
61
- seed: 4_242,
52
+ seed: layerSpec.seed,
62
53
  tileWidthPx: FLAPPY_STARFIELD_TILE_WIDTH_PX,
63
54
  tileHeightPx,
64
- starCount: 23,
65
- minSizePx: 2,
66
- maxSizePx: 4,
67
- minAlpha: 0.12,
68
- maxAlpha: 0.34,
69
- blurPx: 8,
55
+ starCount: layerSpec.starCount,
56
+ minSizePx: layerSpec.minSizePx,
57
+ maxSizePx: layerSpec.maxSizePx,
58
+ minAlpha: layerSpec.minAlpha,
59
+ maxAlpha: layerSpec.maxAlpha,
60
+ blurPx: layerSpec.blurPx,
70
61
  }),
71
62
  tileWidthPx: FLAPPY_STARFIELD_TILE_WIDTH_PX,
72
63
  tileHeightPx,
73
- scrollRatio: FLAPPY_STARFIELD_NEAR_SCROLL_RATIO,
64
+ scrollRatio: layerSpec.scrollRatio,
74
65
  };
75
-
76
- const resolvedTiles = [farTile, midTile, nearTile] as const;
77
- cachedStarfieldTilesByHeight.set(tileHeightPx, resolvedTiles);
78
- return resolvedTiles;
79
- }
80
-
81
- function createStarTileCanvas(options: {
82
- seed: number;
83
- tileWidthPx: number;
84
- tileHeightPx: number;
85
- starCount: number;
86
- minSizePx: number;
87
- maxSizePx: number;
88
- minAlpha: number;
89
- maxAlpha: number;
90
- blurPx: number;
91
- }): CanvasImageSource {
92
- const canvas = createCompatibleCanvas(
93
- options.tileWidthPx,
94
- options.tileHeightPx,
95
- );
96
- const tileContext = canvas.getContext('2d');
97
- if (!tileContext) {
98
- return canvas;
99
- }
100
-
101
- const seededRandom = createSeededRandom(options.seed);
102
-
103
- tileContext.clearRect(0, 0, canvas.width, canvas.height);
104
- tileContext.globalCompositeOperation = 'source-over';
105
- tileContext.shadowColor = FLAPPY_STARFIELD_CYAN_FILL_STYLE;
106
- tileContext.shadowBlur = options.blurPx;
107
-
108
- for (let starIndex = 0; starIndex < options.starCount; starIndex += 1) {
109
- const xPx = Math.floor(seededRandom() * options.tileWidthPx);
110
- const yPx = Math.floor(seededRandom() * options.tileHeightPx);
111
- const sizePx =
112
- options.minSizePx +
113
- Math.floor(seededRandom() * (options.maxSizePx - options.minSizePx + 1));
114
- const alpha =
115
- options.minAlpha + seededRandom() * (options.maxAlpha - options.minAlpha);
116
-
117
- tileContext.globalAlpha = alpha;
118
- tileContext.fillStyle = FLAPPY_STARFIELD_CYAN_FILL_STYLE;
119
- tileContext.fillRect(xPx, yPx, sizePx, sizePx);
120
- }
121
-
122
- tileContext.shadowBlur = 0;
123
- tileContext.shadowColor = 'transparent';
124
- tileContext.globalAlpha = 1;
125
- return canvas;
126
- }
127
-
128
- function createCompatibleCanvas(
129
- widthPx: number,
130
- heightPx: number,
131
- ): HTMLCanvasElement | OffscreenCanvas {
132
- const width = Math.max(1, Math.round(widthPx));
133
- const height = Math.max(1, Math.round(heightPx));
134
-
135
- if (typeof OffscreenCanvas !== 'undefined') {
136
- return new OffscreenCanvas(width, height);
137
- }
138
-
139
- if (typeof document !== 'undefined') {
140
- const canvas = document.createElement('canvas');
141
- canvas.width = width;
142
- canvas.height = height;
143
- return canvas;
144
- }
145
-
146
- // Fallback: non-browser environments won't render, but should not crash.
147
- const canvas = { width, height } as unknown as HTMLCanvasElement;
148
- return canvas;
149
66
  }
@@ -0,0 +1,272 @@
1
+ import {
2
+ FLAPPY_STARFIELD_CANVAS_CONTEXT_ID,
3
+ FLAPPY_STARFIELD_CYAN_FILL_STYLE,
4
+ FLAPPY_STARFIELD_COMPOSITE_SOURCE_OVER,
5
+ FLAPPY_STARFIELD_FULL_ALPHA,
6
+ FLAPPY_STARFIELD_INCLUSIVE_RANGE_OFFSET,
7
+ FLAPPY_STARFIELD_MIN_DIMENSION_PX,
8
+ FLAPPY_STARFIELD_NO_BLUR_PX,
9
+ FLAPPY_STARFIELD_ORIGIN_PX,
10
+ FLAPPY_STARFIELD_TRANSPARENT_SHADOW_COLOR,
11
+ } from '../../constants/constants';
12
+ import { createSeededRandom } from './playback.starfield.utils';
13
+ import type {
14
+ CreateStarTileCanvasOptions,
15
+ StarfieldCanvasDimensions,
16
+ StarPlacement,
17
+ } from './playback.starfield.types';
18
+
19
+ /**
20
+ * Pre-renders a deterministic tile that can be reused across animation frames.
21
+ *
22
+ * @param options - Declarative drawing recipe for one parallax layer.
23
+ * @returns Canvas image source containing the rendered star strip.
24
+ */
25
+ export function createStarTileCanvas(
26
+ options: CreateStarTileCanvasOptions,
27
+ ): CanvasImageSource {
28
+ // Step 1: Allocate a compatible canvas for the requested tile dimensions.
29
+ const canvas = createCompatibleCanvas(
30
+ options.tileWidthPx,
31
+ options.tileHeightPx,
32
+ );
33
+
34
+ // Step 2: Resolve a 2D context when the runtime can actually render.
35
+ const tileContext = resolveStarTileContext(canvas);
36
+ if (!tileContext) {
37
+ return canvas;
38
+ }
39
+
40
+ // Step 3: Initialize drawing state and render deterministic stars.
41
+ const seededRandom = createSeededRandom(options.seed);
42
+ initializeStarTileContext({
43
+ tileContext,
44
+ canvas,
45
+ blurPx: options.blurPx,
46
+ });
47
+ renderSeededStars({
48
+ tileContext,
49
+ seededRandom,
50
+ canvasOptions: options,
51
+ });
52
+
53
+ // Step 4: Restore neutral drawing state before handing the canvas back.
54
+ resetStarTileContext(tileContext);
55
+ return canvas;
56
+ }
57
+
58
+ /**
59
+ * Creates a browser-compatible canvas with clamped integer dimensions.
60
+ *
61
+ * @param widthPx - Requested tile width in pixels.
62
+ * @param heightPx - Requested tile height in pixels.
63
+ * @returns Offscreen canvas when supported, otherwise a DOM canvas fallback.
64
+ */
65
+ function createCompatibleCanvas(
66
+ widthPx: number,
67
+ heightPx: number,
68
+ ): HTMLCanvasElement | OffscreenCanvas {
69
+ const canvasDimensions = normalizeCanvasDimensions(widthPx, heightPx);
70
+ const offscreenCanvas = createOffscreenCanvasIfSupported(canvasDimensions);
71
+ if (offscreenCanvas) {
72
+ return offscreenCanvas;
73
+ }
74
+
75
+ const documentCanvas = createDocumentCanvasIfSupported(canvasDimensions);
76
+ if (documentCanvas) {
77
+ return documentCanvas;
78
+ }
79
+
80
+ return createCanvasSizeFallback(canvasDimensions);
81
+ }
82
+
83
+ /**
84
+ * Resolves the rendering context used for star tile pre-rendering.
85
+ *
86
+ * @param canvas - Compatible canvas returned by the runtime-specific factory.
87
+ * @returns A 2D drawing context when rendering is supported.
88
+ */
89
+ function resolveStarTileContext(
90
+ canvas: HTMLCanvasElement | OffscreenCanvas,
91
+ ): OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null {
92
+ return canvas.getContext(FLAPPY_STARFIELD_CANVAS_CONTEXT_ID);
93
+ }
94
+
95
+ /**
96
+ * Clears the canvas and applies the glow settings shared by all rendered stars.
97
+ *
98
+ * @param options - Context initialization dependencies.
99
+ * @returns Nothing. The provided context is mutated in place.
100
+ */
101
+ function initializeStarTileContext(options: {
102
+ tileContext: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
103
+ canvas: HTMLCanvasElement | OffscreenCanvas;
104
+ blurPx: number;
105
+ }): void {
106
+ options.tileContext.clearRect(
107
+ FLAPPY_STARFIELD_ORIGIN_PX,
108
+ FLAPPY_STARFIELD_ORIGIN_PX,
109
+ options.canvas.width,
110
+ options.canvas.height,
111
+ );
112
+ options.tileContext.globalCompositeOperation =
113
+ FLAPPY_STARFIELD_COMPOSITE_SOURCE_OVER;
114
+ options.tileContext.shadowColor = FLAPPY_STARFIELD_CYAN_FILL_STYLE;
115
+ options.tileContext.shadowBlur = options.blurPx;
116
+ }
117
+
118
+ /**
119
+ * Draws all stars for one tile using a seeded random source.
120
+ *
121
+ * @param options - Drawing context, seed source, and tile recipe.
122
+ * @returns Nothing. The provided context is mutated in place.
123
+ */
124
+ function renderSeededStars(options: {
125
+ tileContext: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
126
+ seededRandom: () => number;
127
+ canvasOptions: CreateStarTileCanvasOptions;
128
+ }): void {
129
+ for (
130
+ let starIndex = 0;
131
+ starIndex < options.canvasOptions.starCount;
132
+ starIndex += 1
133
+ ) {
134
+ const starPlacement = resolveStarPlacement({
135
+ seededRandom: options.seededRandom,
136
+ tileWidthPx: options.canvasOptions.tileWidthPx,
137
+ tileHeightPx: options.canvasOptions.tileHeightPx,
138
+ minSizePx: options.canvasOptions.minSizePx,
139
+ maxSizePx: options.canvasOptions.maxSizePx,
140
+ minAlpha: options.canvasOptions.minAlpha,
141
+ maxAlpha: options.canvasOptions.maxAlpha,
142
+ });
143
+
144
+ options.tileContext.globalAlpha = starPlacement.alpha;
145
+ options.tileContext.fillStyle = FLAPPY_STARFIELD_CYAN_FILL_STYLE;
146
+ options.tileContext.fillRect(
147
+ starPlacement.xPx,
148
+ starPlacement.yPx,
149
+ starPlacement.sizePx,
150
+ starPlacement.sizePx,
151
+ );
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Restores neutral drawing state so later canvas consumers start from defaults.
157
+ *
158
+ * @param tileContext - 2D context used to render the star tile.
159
+ * @returns Nothing. The provided context is mutated in place.
160
+ */
161
+ function resetStarTileContext(
162
+ tileContext: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,
163
+ ): void {
164
+ tileContext.shadowBlur = FLAPPY_STARFIELD_NO_BLUR_PX;
165
+ tileContext.shadowColor = FLAPPY_STARFIELD_TRANSPARENT_SHADOW_COLOR;
166
+ tileContext.globalAlpha = FLAPPY_STARFIELD_FULL_ALPHA;
167
+ }
168
+
169
+ /**
170
+ * Normalizes requested canvas dimensions into positive integer pixel sizes.
171
+ *
172
+ * @param widthPx - Requested width in pixels.
173
+ * @param heightPx - Requested height in pixels.
174
+ * @returns Clamped integer dimensions safe for canvas allocation.
175
+ */
176
+ function normalizeCanvasDimensions(
177
+ widthPx: number,
178
+ heightPx: number,
179
+ ): StarfieldCanvasDimensions {
180
+ return {
181
+ width: Math.max(FLAPPY_STARFIELD_MIN_DIMENSION_PX, Math.round(widthPx)),
182
+ height: Math.max(FLAPPY_STARFIELD_MIN_DIMENSION_PX, Math.round(heightPx)),
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Creates an offscreen canvas when the current runtime supports it.
188
+ *
189
+ * @param canvasDimensions - Already-normalized pixel dimensions.
190
+ * @returns Offscreen canvas instance or `null` when unavailable.
191
+ */
192
+ function createOffscreenCanvasIfSupported(
193
+ canvasDimensions: StarfieldCanvasDimensions,
194
+ ): OffscreenCanvas | null {
195
+ if (typeof OffscreenCanvas !== 'undefined') {
196
+ return new OffscreenCanvas(canvasDimensions.width, canvasDimensions.height);
197
+ }
198
+
199
+ return null;
200
+ }
201
+
202
+ /**
203
+ * Creates a DOM canvas when document APIs are available.
204
+ *
205
+ * @param canvasDimensions - Already-normalized pixel dimensions.
206
+ * @returns DOM canvas instance or `null` when unavailable.
207
+ */
208
+ function createDocumentCanvasIfSupported(
209
+ canvasDimensions: StarfieldCanvasDimensions,
210
+ ): HTMLCanvasElement | null {
211
+ if (typeof document !== 'undefined') {
212
+ const canvas = document.createElement('canvas');
213
+ canvas.width = canvasDimensions.width;
214
+ canvas.height = canvasDimensions.height;
215
+ return canvas;
216
+ }
217
+
218
+ return null;
219
+ }
220
+
221
+ /**
222
+ * Creates a size-only fallback so non-browser tests can skip rendering safely.
223
+ *
224
+ * @param canvasDimensions - Already-normalized pixel dimensions.
225
+ * @returns Minimal canvas-shaped object cast to the compatible return type.
226
+ */
227
+ function createCanvasSizeFallback(
228
+ canvasDimensions: StarfieldCanvasDimensions,
229
+ ): HTMLCanvasElement {
230
+ const canvas = {
231
+ width: canvasDimensions.width,
232
+ height: canvasDimensions.height,
233
+ } as Partial<HTMLCanvasElement> as HTMLCanvasElement;
234
+ return canvas;
235
+ }
236
+
237
+ /**
238
+ * Resolves one deterministic star placement and appearance from the seeded RNG.
239
+ *
240
+ * @param options - Random source and star placement bounds.
241
+ * @returns Pixel location, square size, and alpha for one rendered star.
242
+ */
243
+ function resolveStarPlacement(options: {
244
+ seededRandom: () => number;
245
+ tileWidthPx: number;
246
+ tileHeightPx: number;
247
+ minSizePx: number;
248
+ maxSizePx: number;
249
+ minAlpha: number;
250
+ maxAlpha: number;
251
+ }): StarPlacement {
252
+ const xPx = Math.floor(options.seededRandom() * options.tileWidthPx);
253
+ const yPx = Math.floor(options.seededRandom() * options.tileHeightPx);
254
+ const sizePx =
255
+ options.minSizePx +
256
+ Math.floor(
257
+ options.seededRandom() *
258
+ (options.maxSizePx -
259
+ options.minSizePx +
260
+ FLAPPY_STARFIELD_INCLUSIVE_RANGE_OFFSET),
261
+ );
262
+ const alpha =
263
+ options.minAlpha +
264
+ options.seededRandom() * (options.maxAlpha - options.minAlpha);
265
+
266
+ return {
267
+ xPx,
268
+ yPx,
269
+ sizePx,
270
+ alpha,
271
+ };
272
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Shared type contract for starfield tile rendering layers.
3
+ *
4
+ * A tile is pre-rendered and repeated horizontally to draw efficient
5
+ * parallax backgrounds during playback.
6
+ */
7
+ export type StarTile = {
8
+ image: CanvasImageSource;
9
+ tileWidthPx: number;
10
+ tileHeightPx: number;
11
+ scrollRatio: number;
12
+ };
13
+
14
+ /**
15
+ * Declarative recipe for building one cached starfield parallax layer.
16
+ */
17
+ export type PlaybackStarfieldLayerSpec = {
18
+ seed: number;
19
+ scrollRatio: number;
20
+ starCount: number;
21
+ minSizePx: number;
22
+ maxSizePx: number;
23
+ minAlpha: number;
24
+ maxAlpha: number;
25
+ blurPx: number;
26
+ };
27
+
28
+ /**
29
+ * Input contract for pre-rendering one deterministic starfield tile.
30
+ *
31
+ * The generated tile is cached and repeated horizontally during playback,
32
+ * so every field here affects both the visual look and the parallax cost.
33
+ */
34
+ export type CreateStarTileCanvasOptions = {
35
+ seed: number;
36
+ tileWidthPx: number;
37
+ tileHeightPx: number;
38
+ starCount: number;
39
+ minSizePx: number;
40
+ maxSizePx: number;
41
+ minAlpha: number;
42
+ maxAlpha: number;
43
+ blurPx: number;
44
+ };
45
+
46
+ /**
47
+ * Normalized canvas dimensions used by browser and offscreen tile creation.
48
+ */
49
+ export type StarfieldCanvasDimensions = {
50
+ width: number;
51
+ height: number;
52
+ };
53
+
54
+ /**
55
+ * Deterministic placement and appearance for one rendered star sprite.
56
+ */
57
+ export type StarPlacement = {
58
+ xPx: number;
59
+ yPx: number;
60
+ sizePx: number;
61
+ alpha: number;
62
+ };
@@ -1,3 +1,10 @@
1
+ import {
2
+ FLAPPY_STARFIELD_UNSIGNED_NORMALIZATION_DIVISOR,
3
+ FLAPPY_STARFIELD_XORSHIFT_LEFT_SHIFT_FINAL,
4
+ FLAPPY_STARFIELD_XORSHIFT_LEFT_SHIFT_PRIMARY,
5
+ FLAPPY_STARFIELD_XORSHIFT_RIGHT_SHIFT,
6
+ } from '../../constants/constants';
7
+
1
8
  /**
2
9
  * Creates a deterministic pseudo-random generator for starfield tile layouts.
3
10
  *
@@ -9,15 +16,15 @@ export function createSeededRandom(seed: number): () => number {
9
16
 
10
17
  return () => {
11
18
  // Step 1: Apply xorshift32 state transitions.
12
- randomState ^= randomState << 13;
19
+ randomState ^= randomState << FLAPPY_STARFIELD_XORSHIFT_LEFT_SHIFT_PRIMARY;
13
20
  randomState >>>= 0;
14
- randomState ^= randomState >> 17;
21
+ randomState ^= randomState >> FLAPPY_STARFIELD_XORSHIFT_RIGHT_SHIFT;
15
22
  randomState >>>= 0;
16
- randomState ^= randomState << 5;
23
+ randomState ^= randomState << FLAPPY_STARFIELD_XORSHIFT_LEFT_SHIFT_FINAL;
17
24
  randomState >>>= 0;
18
25
 
19
26
  // Step 2: Normalize 32-bit unsigned state into [0, 1).
20
- return randomState / 0x1_0000_0000;
27
+ return randomState / FLAPPY_STARFIELD_UNSIGNED_NORMALIZATION_DIVISOR;
21
28
  };
22
29
  }
23
30