@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
@@ -0,0 +1,493 @@
1
+ import {
2
+ FLAPPY_BIRD_AURA_ALPHA,
3
+ FLAPPY_BIRD_AURA_BLUR_MULTIPLIER,
4
+ FLAPPY_BIRD_AURA_EXPAND_PX,
5
+ FLAPPY_BIRD_BODY_GLOW_BLUR_PX,
6
+ FLAPPY_BIRD_CHAMPION_EXTRA_GLOW_BLUR_PX,
7
+ FLAPPY_BIRD_CHAMPION_RED_GLOW_ALPHA,
8
+ FLAPPY_BIRD_CHAMPION_RED_GLOW_EXPAND_PX,
9
+ FLAPPY_BIRD_CHAMPION_SHINE_FILL_STYLE,
10
+ FLAPPY_BIRD_CHAMPION_SHINE_GLOW_COLOR,
11
+ FLAPPY_BIRD_RADIUS_PX,
12
+ FLAPPY_BIRD_SHINE_FILL_STYLE,
13
+ FLAPPY_BIRD_SHINE_INSET_RATIO,
14
+ FLAPPY_BIRD_SHINE_SIZE_RATIO,
15
+ FLAPPY_BIRD_WHITE_SHINE_GLOW_BLUR_PX,
16
+ FLAPPY_BIRD_WHITE_SHINE_GLOW_COLOR,
17
+ FLAPPY_BIRD_X_PX,
18
+ FLAPPY_LEADER_RING_GLOW_BLUR_PX,
19
+ FLAPPY_LEADER_RING_LINE_WIDTH_PX,
20
+ FLAPPY_LEADER_RING_RADIUS_OFFSET_PX,
21
+ FLAPPY_NEON_PALETTE,
22
+ FLAPPY_NON_CHAMPION_OPACITY,
23
+ FLAPPY_PIPE_SPEED_PX_PER_FRAME,
24
+ FLAPPY_TRAIL_LINE_WIDTH_PX,
25
+ FLAPPY_TRAIL_MIN_HORIZONTAL_SEGMENT_PX,
26
+ FLAPPY_TRAIL_MIN_VERTICAL_SEGMENT_PX,
27
+ FLAPPY_TRAIL_OPACITY_FACTOR,
28
+ } from '../../../constants/constants';
29
+ import type { TrailPoint } from '../../browser-entry.types';
30
+ import { resolveBirdRenderStyle } from '../playback.render.utils';
31
+ import {
32
+ resolveEdgeOpacityFactor,
33
+ resolveTrailLifetimeOpacityFactor,
34
+ } from '../playback.trail.utils';
35
+ import type { PlaybackEdgeBounds } from '../playback.types';
36
+ import type {
37
+ PlaybackBirdGeometry,
38
+ PlaybackTrailRenderStyle,
39
+ } from './playback.frame-render.types';
40
+
41
+ /**
42
+ * Draws one active bird body, glow, shine, and leader ring.
43
+ *
44
+ * @param context - Canvas 2D drawing context.
45
+ * @param birdYPx - Bird vertical position in world pixels.
46
+ * @param birdIndex - Index of the bird being rendered.
47
+ * @param championBirdIndex - Champion index for the current frame.
48
+ * @returns Nothing.
49
+ */
50
+ export function renderPlaybackBird(
51
+ context: CanvasRenderingContext2D,
52
+ birdYPx: number,
53
+ birdIndex: number,
54
+ championBirdIndex: number,
55
+ ): void {
56
+ // Step 1: Resolve geometry and style contracts for the current bird.
57
+ const birdGeometry = resolvePlaybackBirdGeometry(birdYPx);
58
+ const birdRenderStyle = resolveBirdRenderStyle(birdIndex, championBirdIndex);
59
+
60
+ // Step 2: Draw champion-only glow passes behind the bird body.
61
+ drawPlaybackBirdChampionAura(context, birdGeometry, birdRenderStyle);
62
+ drawPlaybackBirdChampionGlowPlate(context, birdGeometry, birdRenderStyle);
63
+
64
+ // Step 3: Draw the bird body, shine highlight, and champion ring.
65
+ drawPlaybackBirdBody(context, birdGeometry, birdRenderStyle);
66
+ drawPlaybackBirdShine(context, birdGeometry, birdRenderStyle.isChampionBird);
67
+ drawPlaybackBirdLeaderRing(
68
+ context,
69
+ birdGeometry,
70
+ birdRenderStyle.isChampionBird,
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Resolves the trail style used for one bird's stepped trail.
76
+ *
77
+ * @param birdIndex - Index of the bird being rendered.
78
+ * @param championBirdIndex - Champion index for the current frame.
79
+ * @returns Base opacity and color for the bird trail.
80
+ */
81
+ export function resolvePlaybackTrailStyle(
82
+ birdIndex: number,
83
+ championBirdIndex: number,
84
+ ): PlaybackTrailRenderStyle {
85
+ // Step 1: Resolve champion-aware opacity and trail color.
86
+ const isChampionBird = birdIndex === championBirdIndex;
87
+ return {
88
+ baseOpacity:
89
+ (isChampionBird ? 1 : FLAPPY_NON_CHAMPION_OPACITY) *
90
+ FLAPPY_TRAIL_OPACITY_FACTOR,
91
+ trailColor: isChampionBird
92
+ ? FLAPPY_NEON_PALETTE.trail
93
+ : FLAPPY_NEON_PALETTE.nonChampionBird,
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Draws the stepped trail history for one active bird.
99
+ *
100
+ * @param context - Canvas 2D drawing context.
101
+ * @param trailPoints - Cached per-frame trail points for one bird.
102
+ * @param color - Stroke color for the trail.
103
+ * @param anchorX - Bird anchor x-position in world space.
104
+ * @param baseOpacity - Base opacity before edge and lifetime fading.
105
+ * @param edgeBounds - Visible world bounds used for edge fading.
106
+ * @returns Nothing.
107
+ */
108
+ export function drawTrail(
109
+ context: CanvasRenderingContext2D,
110
+ trailPoints: TrailPoint[],
111
+ color: string,
112
+ anchorX: number,
113
+ baseOpacity: number,
114
+ edgeBounds: PlaybackEdgeBounds,
115
+ ): void {
116
+ // Step 1: Guard empty trails.
117
+ if (trailPoints.length === 0) {
118
+ return;
119
+ }
120
+
121
+ const latestTrailFrameIndex = trailPoints.at(-1)?.frameIndex ?? 0;
122
+
123
+ const firstTrailPoint = trailPoints[0];
124
+ const firstTrailFrameOffset = Math.max(
125
+ 0,
126
+ latestTrailFrameIndex - firstTrailPoint.frameIndex,
127
+ );
128
+ let previousXPosition =
129
+ anchorX - firstTrailFrameOffset * FLAPPY_PIPE_SPEED_PX_PER_FRAME;
130
+ let previousYPosition = firstTrailPoint.yPx;
131
+ let previousFrameOffset = firstTrailFrameOffset;
132
+ const maximumTrailFrameOffset = Math.max(1, firstTrailFrameOffset);
133
+
134
+ // Step 2: Configure trail stroke style.
135
+ const previousGlobalAlpha = context.globalAlpha;
136
+ context.strokeStyle = color;
137
+ context.lineWidth = FLAPPY_TRAIL_LINE_WIDTH_PX;
138
+
139
+ // Step 3: Render stepped segments with edge-proximity alpha fading.
140
+ trailPoints.slice(1).forEach((trailPoint) => {
141
+ const frameOffset = Math.max(
142
+ 0,
143
+ latestTrailFrameIndex - trailPoint.frameIndex,
144
+ );
145
+ const nextXPosition =
146
+ anchorX - frameOffset * FLAPPY_PIPE_SPEED_PX_PER_FRAME;
147
+ const nextYPosition = trailPoint.yPx;
148
+
149
+ const horizontalDeltaPx = nextXPosition - previousXPosition;
150
+ const horizontalDirection = Math.sign(horizontalDeltaPx) || 1;
151
+ const steppedHorizontalLengthPx = Math.max(
152
+ Math.abs(horizontalDeltaPx),
153
+ FLAPPY_TRAIL_MIN_HORIZONTAL_SEGMENT_PX,
154
+ );
155
+ const steppedHorizontalXPosition =
156
+ previousXPosition + horizontalDirection * steppedHorizontalLengthPx;
157
+ drawTrailSegmentWithEdgeFade(
158
+ context,
159
+ previousXPosition,
160
+ previousYPosition,
161
+ steppedHorizontalXPosition,
162
+ previousYPosition,
163
+ baseOpacity,
164
+ edgeBounds,
165
+ previousFrameOffset,
166
+ frameOffset,
167
+ maximumTrailFrameOffset,
168
+ );
169
+ previousXPosition = steppedHorizontalXPosition;
170
+
171
+ const verticalDeltaPx = nextYPosition - previousYPosition;
172
+ if (verticalDeltaPx !== 0) {
173
+ const verticalDirection = Math.sign(verticalDeltaPx);
174
+ const steppedVerticalLengthPx = Math.max(
175
+ Math.abs(verticalDeltaPx),
176
+ FLAPPY_TRAIL_MIN_VERTICAL_SEGMENT_PX,
177
+ );
178
+ const steppedVerticalYPosition =
179
+ previousYPosition + verticalDirection * steppedVerticalLengthPx;
180
+ drawTrailSegmentWithEdgeFade(
181
+ context,
182
+ previousXPosition,
183
+ previousYPosition,
184
+ steppedHorizontalXPosition,
185
+ steppedVerticalYPosition,
186
+ baseOpacity,
187
+ edgeBounds,
188
+ previousFrameOffset,
189
+ frameOffset,
190
+ maximumTrailFrameOffset,
191
+ );
192
+ previousYPosition = steppedVerticalYPosition;
193
+ }
194
+
195
+ drawTrailSegmentWithEdgeFade(
196
+ context,
197
+ previousXPosition,
198
+ previousYPosition,
199
+ steppedHorizontalXPosition,
200
+ nextYPosition,
201
+ baseOpacity,
202
+ edgeBounds,
203
+ previousFrameOffset,
204
+ frameOffset,
205
+ maximumTrailFrameOffset,
206
+ );
207
+ previousYPosition = nextYPosition;
208
+
209
+ drawTrailSegmentWithEdgeFade(
210
+ context,
211
+ previousXPosition,
212
+ previousYPosition,
213
+ nextXPosition,
214
+ nextYPosition,
215
+ baseOpacity,
216
+ edgeBounds,
217
+ previousFrameOffset,
218
+ frameOffset,
219
+ maximumTrailFrameOffset,
220
+ );
221
+ previousXPosition = nextXPosition;
222
+ previousYPosition = nextYPosition;
223
+ previousFrameOffset = frameOffset;
224
+ });
225
+
226
+ // Step 4: Restore caller alpha state.
227
+ context.globalAlpha = previousGlobalAlpha;
228
+ }
229
+
230
+ /**
231
+ * Resolves the fixed bird geometry used by all body rendering passes.
232
+ *
233
+ * @param birdYPx - Bird vertical position in world pixels.
234
+ * @returns Pixel-aligned square geometry for the bird body.
235
+ */
236
+ function resolvePlaybackBirdGeometry(birdYPx: number): PlaybackBirdGeometry {
237
+ // Step 1: Resolve a fixed square body size from the collision radius.
238
+ const birdSideLengthPx = Math.max(1, Math.round(FLAPPY_BIRD_RADIUS_PX * 2));
239
+
240
+ // Step 2: Return pixel-aligned body bounds for the current y-position.
241
+ return {
242
+ birdSideLengthPx,
243
+ birdLeftPx: Math.round(FLAPPY_BIRD_X_PX - FLAPPY_BIRD_RADIUS_PX),
244
+ birdTopPx: Math.round(birdYPx - FLAPPY_BIRD_RADIUS_PX),
245
+ };
246
+ }
247
+
248
+ /**
249
+ * Draws the soft champion aura plate behind the bird body.
250
+ *
251
+ * @param context - Canvas 2D drawing context.
252
+ * @param birdGeometry - Pixel-aligned bird geometry.
253
+ * @param birdRenderStyle - Resolved bird style payload.
254
+ * @returns Nothing.
255
+ */
256
+ function drawPlaybackBirdChampionAura(
257
+ context: CanvasRenderingContext2D,
258
+ birdGeometry: PlaybackBirdGeometry,
259
+ birdRenderStyle: ReturnType<typeof resolveBirdRenderStyle>,
260
+ ): void {
261
+ // Step 1: Skip the aura pass for non-champion birds.
262
+ if (!birdRenderStyle.isChampionBird) {
263
+ return;
264
+ }
265
+
266
+ // Step 2: Draw the additive aura plate behind the champion body.
267
+ const auraExpandPx = Math.round(FLAPPY_BIRD_AURA_EXPAND_PX);
268
+ const previousCompositeOperation = context.globalCompositeOperation;
269
+ context.globalCompositeOperation = 'lighter';
270
+ context.globalAlpha = birdRenderStyle.birdOpacity * FLAPPY_BIRD_AURA_ALPHA;
271
+ context.fillStyle = birdRenderStyle.birdRenderColor;
272
+ context.shadowColor = birdRenderStyle.birdRenderColor;
273
+ context.shadowBlur = Math.round(
274
+ FLAPPY_BIRD_BODY_GLOW_BLUR_PX * FLAPPY_BIRD_AURA_BLUR_MULTIPLIER,
275
+ );
276
+ context.fillRect(
277
+ birdGeometry.birdLeftPx - auraExpandPx,
278
+ birdGeometry.birdTopPx - auraExpandPx,
279
+ birdGeometry.birdSideLengthPx + auraExpandPx * 2,
280
+ birdGeometry.birdSideLengthPx + auraExpandPx * 2,
281
+ );
282
+ context.shadowBlur = 0;
283
+ context.shadowColor = 'transparent';
284
+ context.globalCompositeOperation = previousCompositeOperation;
285
+ }
286
+
287
+ /**
288
+ * Draws the champion-only red glow plate beneath the bird body.
289
+ *
290
+ * @param context - Canvas 2D drawing context.
291
+ * @param birdGeometry - Pixel-aligned bird geometry.
292
+ * @param birdRenderStyle - Resolved bird style payload.
293
+ * @returns Nothing.
294
+ */
295
+ function drawPlaybackBirdChampionGlowPlate(
296
+ context: CanvasRenderingContext2D,
297
+ birdGeometry: PlaybackBirdGeometry,
298
+ birdRenderStyle: ReturnType<typeof resolveBirdRenderStyle>,
299
+ ): void {
300
+ // Step 1: Skip the red glow plate for non-champion birds.
301
+ if (!birdRenderStyle.isChampionBird) {
302
+ return;
303
+ }
304
+
305
+ // Step 2: Draw the expanded red glow plate behind the champion body.
306
+ const expandedGlowInsetPx = Math.round(
307
+ FLAPPY_BIRD_CHAMPION_RED_GLOW_EXPAND_PX,
308
+ );
309
+ context.globalAlpha =
310
+ birdRenderStyle.birdOpacity * FLAPPY_BIRD_CHAMPION_RED_GLOW_ALPHA;
311
+ context.fillStyle = FLAPPY_NEON_PALETTE.championBird;
312
+ context.shadowColor = FLAPPY_NEON_PALETTE.championBird;
313
+ context.shadowBlur =
314
+ FLAPPY_BIRD_BODY_GLOW_BLUR_PX + FLAPPY_BIRD_CHAMPION_EXTRA_GLOW_BLUR_PX;
315
+ context.fillRect(
316
+ birdGeometry.birdLeftPx - expandedGlowInsetPx,
317
+ birdGeometry.birdTopPx - expandedGlowInsetPx,
318
+ birdGeometry.birdSideLengthPx + expandedGlowInsetPx * 2,
319
+ birdGeometry.birdSideLengthPx + expandedGlowInsetPx * 2,
320
+ );
321
+ }
322
+
323
+ /**
324
+ * Draws the square bird body with its base neon glow.
325
+ *
326
+ * @param context - Canvas 2D drawing context.
327
+ * @param birdGeometry - Pixel-aligned bird geometry.
328
+ * @param birdRenderStyle - Resolved bird style payload.
329
+ * @returns Nothing.
330
+ */
331
+ function drawPlaybackBirdBody(
332
+ context: CanvasRenderingContext2D,
333
+ birdGeometry: PlaybackBirdGeometry,
334
+ birdRenderStyle: ReturnType<typeof resolveBirdRenderStyle>,
335
+ ): void {
336
+ // Step 1: Draw the main square body using the resolved bird color.
337
+ context.globalAlpha = birdRenderStyle.birdOpacity;
338
+ context.fillStyle = birdRenderStyle.birdRenderColor;
339
+ context.shadowColor = birdRenderStyle.birdRenderColor;
340
+ context.shadowBlur =
341
+ FLAPPY_BIRD_BODY_GLOW_BLUR_PX +
342
+ (birdRenderStyle.isChampionBird
343
+ ? FLAPPY_BIRD_CHAMPION_EXTRA_GLOW_BLUR_PX
344
+ : 0);
345
+ context.fillRect(
346
+ birdGeometry.birdLeftPx,
347
+ birdGeometry.birdTopPx,
348
+ birdGeometry.birdSideLengthPx,
349
+ birdGeometry.birdSideLengthPx,
350
+ );
351
+ }
352
+
353
+ /**
354
+ * Draws the reflective shine highlight for one bird body.
355
+ *
356
+ * @param context - Canvas 2D drawing context.
357
+ * @param birdGeometry - Pixel-aligned bird geometry.
358
+ * @param isChampionBird - Whether the current bird is the champion.
359
+ * @returns Nothing.
360
+ */
361
+ function drawPlaybackBirdShine(
362
+ context: CanvasRenderingContext2D,
363
+ birdGeometry: PlaybackBirdGeometry,
364
+ isChampionBird: boolean,
365
+ ): void {
366
+ // Step 1: Resolve shine geometry inside the square bird body.
367
+ const shineInsetPx =
368
+ birdGeometry.birdSideLengthPx * FLAPPY_BIRD_SHINE_INSET_RATIO;
369
+ const shineSideLengthPx = Math.max(
370
+ 1,
371
+ birdGeometry.birdSideLengthPx * FLAPPY_BIRD_SHINE_SIZE_RATIO,
372
+ );
373
+
374
+ // Step 2: Draw the inner shine highlight using champion-aware colors.
375
+ context.fillStyle = isChampionBird
376
+ ? FLAPPY_BIRD_CHAMPION_SHINE_FILL_STYLE
377
+ : FLAPPY_BIRD_SHINE_FILL_STYLE;
378
+ context.shadowColor = isChampionBird
379
+ ? FLAPPY_BIRD_CHAMPION_SHINE_GLOW_COLOR
380
+ : FLAPPY_BIRD_WHITE_SHINE_GLOW_COLOR;
381
+ context.shadowBlur = FLAPPY_BIRD_WHITE_SHINE_GLOW_BLUR_PX;
382
+ context.fillRect(
383
+ Math.round(birdGeometry.birdLeftPx + shineInsetPx),
384
+ Math.round(birdGeometry.birdTopPx + shineInsetPx),
385
+ Math.round(shineSideLengthPx),
386
+ Math.round(shineSideLengthPx),
387
+ );
388
+ context.shadowBlur = 0;
389
+ context.shadowColor = 'transparent';
390
+ }
391
+
392
+ /**
393
+ * Draws the leader ring around the champion bird.
394
+ *
395
+ * @param context - Canvas 2D drawing context.
396
+ * @param birdGeometry - Pixel-aligned bird geometry.
397
+ * @param isChampionBird - Whether the current bird is the champion.
398
+ * @returns Nothing.
399
+ */
400
+ function drawPlaybackBirdLeaderRing(
401
+ context: CanvasRenderingContext2D,
402
+ birdGeometry: PlaybackBirdGeometry,
403
+ isChampionBird: boolean,
404
+ ): void {
405
+ // Step 1: Skip the leader ring for non-champion birds.
406
+ if (!isChampionBird) {
407
+ return;
408
+ }
409
+
410
+ // Step 2: Draw the glowing leader ring around the champion body.
411
+ const leaderRingInsetPx = Math.round(FLAPPY_LEADER_RING_RADIUS_OFFSET_PX);
412
+ context.strokeStyle = FLAPPY_NEON_PALETTE.leaderRing;
413
+ context.lineWidth = FLAPPY_LEADER_RING_LINE_WIDTH_PX;
414
+ context.shadowColor = FLAPPY_NEON_PALETTE.leaderRing;
415
+ context.shadowBlur = FLAPPY_LEADER_RING_GLOW_BLUR_PX;
416
+ context.strokeRect(
417
+ birdGeometry.birdLeftPx - leaderRingInsetPx,
418
+ birdGeometry.birdTopPx - leaderRingInsetPx,
419
+ birdGeometry.birdSideLengthPx + leaderRingInsetPx * 2,
420
+ birdGeometry.birdSideLengthPx + leaderRingInsetPx * 2,
421
+ );
422
+ context.shadowBlur = 0;
423
+ context.shadowColor = 'transparent';
424
+ }
425
+
426
+ /**
427
+ * Draws one trail segment with combined edge and lifetime fading.
428
+ *
429
+ * @param context - Canvas 2D drawing context.
430
+ * @param startXPx - Segment start x-position.
431
+ * @param startYPx - Segment start y-position.
432
+ * @param endXPx - Segment end x-position.
433
+ * @param endYPx - Segment end y-position.
434
+ * @param baseOpacity - Base opacity before fade factors.
435
+ * @param edgeBounds - Visible world bounds used for edge fading.
436
+ * @param startFrameOffset - Relative age of the segment start.
437
+ * @param endFrameOffset - Relative age of the segment end.
438
+ * @param maximumTrailFrameOffset - Oldest visible trail age.
439
+ * @returns Nothing.
440
+ */
441
+ function drawTrailSegmentWithEdgeFade(
442
+ context: CanvasRenderingContext2D,
443
+ startXPx: number,
444
+ startYPx: number,
445
+ endXPx: number,
446
+ endYPx: number,
447
+ baseOpacity: number,
448
+ edgeBounds: PlaybackEdgeBounds,
449
+ startFrameOffset: number,
450
+ endFrameOffset: number,
451
+ maximumTrailFrameOffset: number,
452
+ ): void {
453
+ const segmentLengthPx = Math.hypot(endXPx - startXPx, endYPx - startYPx);
454
+ if (segmentLengthPx === 0 || baseOpacity <= 0) {
455
+ return;
456
+ }
457
+
458
+ // Step 1: Resolve edge-fade factor from both segment endpoints.
459
+ const startOpacityFactor = resolveEdgeOpacityFactor(
460
+ startXPx,
461
+ startYPx,
462
+ edgeBounds,
463
+ );
464
+ const endOpacityFactor = resolveEdgeOpacityFactor(endXPx, endYPx, edgeBounds);
465
+ const edgeOpacityFactor = Math.min(startOpacityFactor, endOpacityFactor);
466
+
467
+ // Step 2: Resolve lifetime fade so older trail history fades near cutoff.
468
+ const startLifetimeOpacityFactor = resolveTrailLifetimeOpacityFactor(
469
+ startFrameOffset,
470
+ maximumTrailFrameOffset,
471
+ );
472
+ const endLifetimeOpacityFactor = resolveTrailLifetimeOpacityFactor(
473
+ endFrameOffset,
474
+ maximumTrailFrameOffset,
475
+ );
476
+ const lifetimeOpacityFactor = Math.min(
477
+ startLifetimeOpacityFactor,
478
+ endLifetimeOpacityFactor,
479
+ );
480
+
481
+ const segmentOpacity =
482
+ baseOpacity * edgeOpacityFactor * lifetimeOpacityFactor;
483
+ if (segmentOpacity <= 0) {
484
+ return;
485
+ }
486
+
487
+ // Step 3: Draw the segment with resolved opacity.
488
+ context.globalAlpha = segmentOpacity;
489
+ context.beginPath();
490
+ context.moveTo(startXPx, startYPx);
491
+ context.lineTo(endXPx, endYPx);
492
+ context.stroke();
493
+ }
@@ -1,4 +1,4 @@
1
- import type { StarTile } from './playback.types';
1
+ import type { StarTile } from './playback.starfield.types';
2
2
 
3
3
  /**
4
4
  * Shared in-memory cache for pre-rendered parallax starfield tiles.
@@ -0,0 +1,4 @@
1
+ export {
2
+ renderPopulationFrame,
3
+ updateTrailState,
4
+ } from './frame-render/playback.frame-render.service';
@@ -0,0 +1,44 @@
1
+ import type {
2
+ EvolutionPlaybackStepSnapshot,
3
+ PopulationRenderState,
4
+ } from '../browser-entry.types';
5
+
6
+ /**
7
+ * Applies worker snapshot data to the mutable playback render state.
8
+ *
9
+ * @param renderState - Mutable render state mirror used by the browser.
10
+ * @param snapshot - Worker playback snapshot for the current render tick.
11
+ * @returns Nothing.
12
+ */
13
+ export function applyPlaybackSnapshot(
14
+ renderState: PopulationRenderState,
15
+ snapshot: EvolutionPlaybackStepSnapshot,
16
+ ): void {
17
+ renderState.frameIndex = snapshot.frameIndex;
18
+ renderState.visibleWorldWidthPx = snapshot.visibleWorldWidthPx;
19
+ renderState.visibleWorldHeightPx = snapshot.visibleWorldHeightPx;
20
+ renderState.pipes = snapshot.pipes;
21
+ renderState.birds = snapshot.birds.map((birdSnapshot) => ({
22
+ color: birdSnapshot.color,
23
+ yPx: birdSnapshot.yPx,
24
+ pipesPassed: birdSnapshot.pipesPassed,
25
+ framesSurvived: birdSnapshot.framesSurvived,
26
+ done: birdSnapshot.done,
27
+ }));
28
+ }
29
+
30
+ /**
31
+ * Resolves the maximum survived-frame count in the current render state.
32
+ *
33
+ * @param renderState - Current render state.
34
+ * @returns Maximum frames survived by any bird.
35
+ */
36
+ export function resolveLeaderFramesSurvived(
37
+ renderState: PopulationRenderState,
38
+ ): number {
39
+ return renderState.birds.reduce(
40
+ (maximumFramesSurvived, bird) =>
41
+ Math.max(maximumFramesSurvived, bird.framesSurvived),
42
+ 0,
43
+ );
44
+ }