@omnitronix/happy-panda-game-engine 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +732 -0
  2. package/dist/__tests__/bonus-sequence.test.d.ts +7 -0
  3. package/dist/__tests__/bonus-sequence.test.d.ts.map +1 -0
  4. package/dist/__tests__/bonus-sequence.test.js +381 -0
  5. package/dist/__tests__/bonus-sequence.test.js.map +1 -0
  6. package/dist/__tests__/cherry-frequency.test.d.ts +8 -0
  7. package/dist/__tests__/cherry-frequency.test.d.ts.map +1 -0
  8. package/dist/__tests__/cherry-frequency.test.js +128 -0
  9. package/dist/__tests__/cherry-frequency.test.js.map +1 -0
  10. package/dist/__tests__/comprehensive.test.d.ts +16 -0
  11. package/dist/__tests__/comprehensive.test.d.ts.map +1 -0
  12. package/dist/__tests__/comprehensive.test.js +491 -0
  13. package/dist/__tests__/comprehensive.test.js.map +1 -0
  14. package/dist/__tests__/counter-manager.test.d.ts +7 -0
  15. package/dist/__tests__/counter-manager.test.d.ts.map +1 -0
  16. package/dist/__tests__/counter-manager.test.js +317 -0
  17. package/dist/__tests__/counter-manager.test.js.map +1 -0
  18. package/dist/__tests__/coverage-boost.test.d.ts +13 -0
  19. package/dist/__tests__/coverage-boost.test.d.ts.map +1 -0
  20. package/dist/__tests__/coverage-boost.test.js +606 -0
  21. package/dist/__tests__/coverage-boost.test.js.map +1 -0
  22. package/dist/__tests__/cpp-parity.test.d.ts +39 -0
  23. package/dist/__tests__/cpp-parity.test.d.ts.map +1 -0
  24. package/dist/__tests__/cpp-parity.test.js +392 -0
  25. package/dist/__tests__/cpp-parity.test.js.map +1 -0
  26. package/dist/__tests__/debug-trigger-bonus.test.d.ts +8 -0
  27. package/dist/__tests__/debug-trigger-bonus.test.d.ts.map +1 -0
  28. package/dist/__tests__/debug-trigger-bonus.test.js +359 -0
  29. package/dist/__tests__/debug-trigger-bonus.test.js.map +1 -0
  30. package/dist/__tests__/fixtures/cpp-parity-vectors.json +457 -0
  31. package/dist/__tests__/gdd-validation-1000.test.d.ts +14 -0
  32. package/dist/__tests__/gdd-validation-1000.test.d.ts.map +1 -0
  33. package/dist/__tests__/gdd-validation-1000.test.js +418 -0
  34. package/dist/__tests__/gdd-validation-1000.test.js.map +1 -0
  35. package/dist/__tests__/happy-panda-engine.test.d.ts +7 -0
  36. package/dist/__tests__/happy-panda-engine.test.d.ts.map +1 -0
  37. package/dist/__tests__/happy-panda-engine.test.js +414 -0
  38. package/dist/__tests__/happy-panda-engine.test.js.map +1 -0
  39. package/dist/__tests__/jackpot-manager.test.d.ts +8 -0
  40. package/dist/__tests__/jackpot-manager.test.d.ts.map +1 -0
  41. package/dist/__tests__/jackpot-manager.test.js +313 -0
  42. package/dist/__tests__/jackpot-manager.test.js.map +1 -0
  43. package/dist/__tests__/jackpot-trigger-trace.test.d.ts +6 -0
  44. package/dist/__tests__/jackpot-trigger-trace.test.d.ts.map +1 -0
  45. package/dist/__tests__/jackpot-trigger-trace.test.js +146 -0
  46. package/dist/__tests__/jackpot-trigger-trace.test.js.map +1 -0
  47. package/dist/__tests__/rng-gli19-compliance.test.d.ts +21 -0
  48. package/dist/__tests__/rng-gli19-compliance.test.d.ts.map +1 -0
  49. package/dist/__tests__/rng-gli19-compliance.test.js +480 -0
  50. package/dist/__tests__/rng-gli19-compliance.test.js.map +1 -0
  51. package/dist/__tests__/rng-seed-security.test.d.ts +19 -0
  52. package/dist/__tests__/rng-seed-security.test.d.ts.map +1 -0
  53. package/dist/__tests__/rng-seed-security.test.js +518 -0
  54. package/dist/__tests__/rng-seed-security.test.js.map +1 -0
  55. package/dist/__tests__/rng-seed-type.test.d.ts +16 -0
  56. package/dist/__tests__/rng-seed-type.test.d.ts.map +1 -0
  57. package/dist/__tests__/rng-seed-type.test.js +285 -0
  58. package/dist/__tests__/rng-seed-type.test.js.map +1 -0
  59. package/dist/__tests__/rng-stress-boundary.test.d.ts +15 -0
  60. package/dist/__tests__/rng-stress-boundary.test.d.ts.map +1 -0
  61. package/dist/__tests__/rng-stress-boundary.test.js +590 -0
  62. package/dist/__tests__/rng-stress-boundary.test.js.map +1 -0
  63. package/dist/__tests__/rtp-1million.test.d.ts +8 -0
  64. package/dist/__tests__/rtp-1million.test.d.ts.map +1 -0
  65. package/dist/__tests__/rtp-1million.test.js +156 -0
  66. package/dist/__tests__/rtp-1million.test.js.map +1 -0
  67. package/dist/__tests__/rtp-analysis.test.d.ts +8 -0
  68. package/dist/__tests__/rtp-analysis.test.d.ts.map +1 -0
  69. package/dist/__tests__/rtp-analysis.test.js +138 -0
  70. package/dist/__tests__/rtp-analysis.test.js.map +1 -0
  71. package/dist/__tests__/rtp-diagnostic.test.d.ts +6 -0
  72. package/dist/__tests__/rtp-diagnostic.test.d.ts.map +1 -0
  73. package/dist/__tests__/rtp-diagnostic.test.js +141 -0
  74. package/dist/__tests__/rtp-diagnostic.test.js.map +1 -0
  75. package/dist/__tests__/rtp-simulation.test.d.ts +8 -0
  76. package/dist/__tests__/rtp-simulation.test.d.ts.map +1 -0
  77. package/dist/__tests__/rtp-simulation.test.js +411 -0
  78. package/dist/__tests__/rtp-simulation.test.js.map +1 -0
  79. package/dist/__tests__/special-wins.test.d.ts +7 -0
  80. package/dist/__tests__/special-wins.test.d.ts.map +1 -0
  81. package/dist/__tests__/special-wins.test.js +179 -0
  82. package/dist/__tests__/special-wins.test.js.map +1 -0
  83. package/dist/__tests__/spin-generator.test.d.ts +7 -0
  84. package/dist/__tests__/spin-generator.test.d.ts.map +1 -0
  85. package/dist/__tests__/spin-generator.test.js +297 -0
  86. package/dist/__tests__/spin-generator.test.js.map +1 -0
  87. package/dist/__tests__/spin-handler.test.d.ts +7 -0
  88. package/dist/__tests__/spin-handler.test.d.ts.map +1 -0
  89. package/dist/__tests__/spin-handler.test.js +210 -0
  90. package/dist/__tests__/spin-handler.test.js.map +1 -0
  91. package/dist/__tests__/symbol-distribution.test.d.ts +6 -0
  92. package/dist/__tests__/symbol-distribution.test.d.ts.map +1 -0
  93. package/dist/__tests__/symbol-distribution.test.js +119 -0
  94. package/dist/__tests__/symbol-distribution.test.js.map +1 -0
  95. package/dist/__tests__/test-protocol-10000-spin.test.d.ts +14 -0
  96. package/dist/__tests__/test-protocol-10000-spin.test.d.ts.map +1 -0
  97. package/dist/__tests__/test-protocol-10000-spin.test.js +359 -0
  98. package/dist/__tests__/test-protocol-10000-spin.test.js.map +1 -0
  99. package/dist/__tests__/weighted-random.test.d.ts +7 -0
  100. package/dist/__tests__/weighted-random.test.d.ts.map +1 -0
  101. package/dist/__tests__/weighted-random.test.js +165 -0
  102. package/dist/__tests__/weighted-random.test.js.map +1 -0
  103. package/dist/__tests__/win-evaluator.test.d.ts +7 -0
  104. package/dist/__tests__/win-evaluator.test.d.ts.map +1 -0
  105. package/dist/__tests__/win-evaluator.test.js +264 -0
  106. package/dist/__tests__/win-evaluator.test.js.map +1 -0
  107. package/dist/config/happy-panda.config.d.ts +217 -0
  108. package/dist/config/happy-panda.config.d.ts.map +1 -0
  109. package/dist/config/happy-panda.config.js +721 -0
  110. package/dist/config/happy-panda.config.js.map +1 -0
  111. package/dist/config/index.d.ts +5 -0
  112. package/dist/config/index.d.ts.map +1 -0
  113. package/dist/config/index.js +21 -0
  114. package/dist/config/index.js.map +1 -0
  115. package/dist/debug/debug-command-definitions.d.ts +38 -0
  116. package/dist/debug/debug-command-definitions.d.ts.map +1 -0
  117. package/dist/debug/debug-command-definitions.js +80 -0
  118. package/dist/debug/debug-command-definitions.js.map +1 -0
  119. package/dist/domain/debug-trigger-bonus-command.d.ts +17 -0
  120. package/dist/domain/debug-trigger-bonus-command.d.ts.map +1 -0
  121. package/dist/domain/debug-trigger-bonus-command.js +3 -0
  122. package/dist/domain/debug-trigger-bonus-command.js.map +1 -0
  123. package/dist/domain/index.d.ts +5 -0
  124. package/dist/domain/index.d.ts.map +1 -0
  125. package/dist/domain/index.js +21 -0
  126. package/dist/domain/index.js.map +1 -0
  127. package/dist/domain/types.d.ts +228 -0
  128. package/dist/domain/types.d.ts.map +1 -0
  129. package/dist/domain/types.js +30 -0
  130. package/dist/domain/types.js.map +1 -0
  131. package/dist/engine/happy-panda-engine.d.ts +64 -0
  132. package/dist/engine/happy-panda-engine.d.ts.map +1 -0
  133. package/dist/engine/happy-panda-engine.js +324 -0
  134. package/dist/engine/happy-panda-engine.js.map +1 -0
  135. package/dist/engine/index.d.ts +5 -0
  136. package/dist/engine/index.d.ts.map +1 -0
  137. package/dist/engine/index.js +21 -0
  138. package/dist/engine/index.js.map +1 -0
  139. package/dist/game-engine.interface.d.ts +19 -0
  140. package/dist/game-engine.interface.d.ts.map +1 -0
  141. package/dist/game-engine.interface.js +9 -0
  142. package/dist/game-engine.interface.js.map +1 -0
  143. package/dist/happy-panda-v1.game-engine.d.ts +60 -0
  144. package/dist/happy-panda-v1.game-engine.d.ts.map +1 -0
  145. package/dist/happy-panda-v1.game-engine.js +524 -0
  146. package/dist/happy-panda-v1.game-engine.js.map +1 -0
  147. package/dist/index.d.ts +22 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +59 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/logic/handlers/index.d.ts +5 -0
  152. package/dist/logic/handlers/index.d.ts.map +1 -0
  153. package/dist/logic/handlers/index.js +21 -0
  154. package/dist/logic/handlers/index.js.map +1 -0
  155. package/dist/logic/handlers/spin-handler.d.ts +58 -0
  156. package/dist/logic/handlers/spin-handler.d.ts.map +1 -0
  157. package/dist/logic/handlers/spin-handler.js +265 -0
  158. package/dist/logic/handlers/spin-handler.js.map +1 -0
  159. package/dist/logic/index.d.ts +6 -0
  160. package/dist/logic/index.d.ts.map +1 -0
  161. package/dist/logic/index.js +22 -0
  162. package/dist/logic/index.js.map +1 -0
  163. package/dist/logic/services/counter-manager.d.ts +60 -0
  164. package/dist/logic/services/counter-manager.d.ts.map +1 -0
  165. package/dist/logic/services/counter-manager.js +268 -0
  166. package/dist/logic/services/counter-manager.js.map +1 -0
  167. package/dist/logic/services/index.d.ts +7 -0
  168. package/dist/logic/services/index.d.ts.map +1 -0
  169. package/dist/logic/services/index.js +23 -0
  170. package/dist/logic/services/index.js.map +1 -0
  171. package/dist/logic/services/jackpot-manager.d.ts +59 -0
  172. package/dist/logic/services/jackpot-manager.d.ts.map +1 -0
  173. package/dist/logic/services/jackpot-manager.js +142 -0
  174. package/dist/logic/services/jackpot-manager.js.map +1 -0
  175. package/dist/logic/services/win-evaluator.d.ts +51 -0
  176. package/dist/logic/services/win-evaluator.d.ts.map +1 -0
  177. package/dist/logic/services/win-evaluator.js +463 -0
  178. package/dist/logic/services/win-evaluator.js.map +1 -0
  179. package/dist/rng/dummy-rng-client.d.ts +39 -0
  180. package/dist/rng/dummy-rng-client.d.ts.map +1 -0
  181. package/dist/rng/dummy-rng-client.js +77 -0
  182. package/dist/rng/dummy-rng-client.js.map +1 -0
  183. package/dist/rng/index.d.ts +10 -0
  184. package/dist/rng/index.d.ts.map +1 -0
  185. package/dist/rng/index.js +27 -0
  186. package/dist/rng/index.js.map +1 -0
  187. package/dist/rng/rng-client.factory.d.ts +33 -0
  188. package/dist/rng/rng-client.factory.d.ts.map +1 -0
  189. package/dist/rng/rng-client.factory.js +57 -0
  190. package/dist/rng/rng-client.factory.js.map +1 -0
  191. package/dist/rng/rng-client.interface.d.ts +51 -0
  192. package/dist/rng/rng-client.interface.d.ts.map +1 -0
  193. package/dist/rng/rng-client.interface.js +9 -0
  194. package/dist/rng/rng-client.interface.js.map +1 -0
  195. package/dist/rng/rng-service.d.ts +93 -0
  196. package/dist/rng/rng-service.d.ts.map +1 -0
  197. package/dist/rng/rng-service.js +124 -0
  198. package/dist/rng/rng-service.js.map +1 -0
  199. package/dist/rng/spin-generator.d.ts +32 -0
  200. package/dist/rng/spin-generator.d.ts.map +1 -0
  201. package/dist/rng/spin-generator.js +338 -0
  202. package/dist/rng/spin-generator.js.map +1 -0
  203. package/dist/rng/weighted-random.d.ts +30 -0
  204. package/dist/rng/weighted-random.d.ts.map +1 -0
  205. package/dist/rng/weighted-random.js +58 -0
  206. package/dist/rng/weighted-random.js.map +1 -0
  207. package/package.json +59 -0
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+ /**
3
+ * Counter Manager Service
4
+ *
5
+ * Manages bonus counters and trigger detection.
6
+ * Each bonus type has a counter that decrements on specific events.
7
+ * When a counter reaches 0, the bonus is triggered.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createInitialCounters = createInitialCounters;
11
+ exports.createEmptyPendingBonuses = createEmptyPendingBonuses;
12
+ exports.randomizeBambooTwinsCounter = randomizeBambooTwinsCounter;
13
+ exports.randomizeCameraCounter = randomizeCameraCounter;
14
+ exports.updateCounters = updateCounters;
15
+ exports.getNextBonusType = getNextBonusType;
16
+ exports.consumeBonus = consumeBonus;
17
+ exports.hasPendingBonus = hasPendingBonus;
18
+ const happy_panda_config_1 = require("../../config/happy-panda.config");
19
+ const weighted_random_1 = require("../../rng/weighted-random");
20
+ // =============================================================================
21
+ // COUNTER INITIALIZATION
22
+ // =============================================================================
23
+ /**
24
+ * Create initial bonus counters.
25
+ */
26
+ function createInitialCounters() {
27
+ return {
28
+ bambooTriplets: happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.COUNTER_INIT,
29
+ bambooTwins: happy_panda_config_1.BONUS_BAMBOO_TWINS.COUNTER_INIT[1], // Default to 9, will be randomized
30
+ camera: happy_panda_config_1.BONUS_CAMERA.COUNTER_INIT[1], // Default to 5, will be randomized
31
+ fireworks: happy_panda_config_1.BONUS_FIREWORKS.COUNTER_INIT,
32
+ };
33
+ }
34
+ /**
35
+ * Create empty pending bonuses.
36
+ */
37
+ function createEmptyPendingBonuses() {
38
+ return {
39
+ bambooTriplets: 0,
40
+ bambooTwins: 0,
41
+ camera: 0,
42
+ fireworks: 0,
43
+ centerBamboo: 0,
44
+ };
45
+ }
46
+ /**
47
+ * Randomize Bamboo Twins counter initial value (6 or 9).
48
+ */
49
+ async function randomizeBambooTwinsCounter(gameDirection, rng) {
50
+ const weights = happy_panda_config_1.BONUS_BAMBOO_TWINS.COUNTER_INIT_WEIGHTS[gameDirection];
51
+ const idx = await (0, weighted_random_1.selectWeightedIndex)(weights, rng);
52
+ return happy_panda_config_1.BONUS_BAMBOO_TWINS.COUNTER_INIT[idx];
53
+ }
54
+ /**
55
+ * Randomize Camera counter initial value (3 or 5).
56
+ */
57
+ async function randomizeCameraCounter(gameDirection, rng) {
58
+ const weights = happy_panda_config_1.BONUS_CAMERA.COUNTER_INIT_WEIGHTS[gameDirection];
59
+ const idx = await (0, weighted_random_1.selectWeightedIndex)(weights, rng);
60
+ return happy_panda_config_1.BONUS_CAMERA.COUNTER_INIT[idx];
61
+ }
62
+ /**
63
+ * Update counters based on spin results.
64
+ * Only applies during paid_spin and respin_cherry.
65
+ */
66
+ async function updateCounters(currentCounters, currentPending, lineWins, grid, gameDirection, spinType, totalWin, rng) {
67
+ // Only update counters during paid spins and respins
68
+ if (spinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN && spinType !== happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO) {
69
+ return {
70
+ counters: { ...currentCounters },
71
+ pendingBonuses: { ...currentPending },
72
+ triggeredBambooTriplets: false,
73
+ triggeredBambooTwins: false,
74
+ triggeredCamera: false,
75
+ triggeredFireworks: false,
76
+ triggeredRespin: 0,
77
+ };
78
+ }
79
+ const counters = { ...currentCounters };
80
+ const pendingBonuses = { ...currentPending };
81
+ let triggeredBambooTriplets = false;
82
+ let triggeredBambooTwins = false;
83
+ let triggeredCamera = false;
84
+ let triggeredFireworks = false;
85
+ let triggeredRespin = 0;
86
+ // Count specific line win patterns
87
+ let bambooTripleCount = 0; // 3x Bamboo wins (only first 8 lines)
88
+ let bambooPairCount = 0; // 2x Bamboo wins
89
+ let cameraTripleCount = 0; // 3x Camera wins
90
+ let fireworksTripleCount = 0; // 3x Fireworks wins
91
+ for (const win of lineWins) {
92
+ // Only first 8 lines count for Bamboo Triplets, Camera, and Fireworks triggers (per C++ math model)
93
+ const isFirst8Lines = win.lineIndex < 8;
94
+ if (win.symbol === happy_panda_config_1.Symbol.BAMBOO || win.symbol === happy_panda_config_1.Symbol.GOLDEN_BAMBOO) {
95
+ if (win.matchLength === 3 && isFirst8Lines) {
96
+ // C++ BEHAVIOR: During RESPIN_CENTER_BAMBOO, C++ only regenerates corners.
97
+ // Center row (line 1) edges (0,1) and (2,1) are preserved from paid_spin.
98
+ // Since the triggering paid_spin had only 1 bamboo at center, these edges are NOT bamboo.
99
+ // Line 1 can NEVER form 3x bamboo during respin. Other lines may still form 3x bamboo
100
+ // through corner positions which are regenerated.
101
+ const isCenterRow = win.lineIndex === 1;
102
+ if (spinType === happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO && isCenterRow) {
103
+ // Skip line 1 - center row edges are preserved non-bamboo in C++
104
+ }
105
+ else {
106
+ bambooTripleCount++;
107
+ }
108
+ }
109
+ else if (win.matchLength === 2) {
110
+ // Bamboo pairs count on ALL lines for Bamboo Twins bonus
111
+ bambooPairCount++;
112
+ }
113
+ }
114
+ else if (win.symbol === happy_panda_config_1.Symbol.CAMERA && win.matchLength === 3 && isFirst8Lines) {
115
+ // Camera bonus only counts first 8 lines
116
+ cameraTripleCount++;
117
+ }
118
+ else if (win.symbol === happy_panda_config_1.Symbol.FIREWORKS_ROCKETS && win.matchLength === 3 && isFirst8Lines) {
119
+ // Fireworks bonus only counts first 8 lines
120
+ fireworksTripleCount++;
121
+ }
122
+ }
123
+ // BAMBOO TRIPLETS BONUS: Triggered by 3x Bamboo on first 8 lines
124
+ // C++ BonusJackpotDecision: decrements counter_bonus_jackpot once per qualifying SPIN
125
+ // (not per line). When counter reaches 0, awards 1 bonus occurrence.
126
+ // Each occurrence = BONUS_BAMBOO_TRIPLETS.SPINS (3) free spins.
127
+ if (bambooTripleCount > 0) {
128
+ counters.bambooTriplets--;
129
+ if (counters.bambooTriplets <= 0) {
130
+ pendingBonuses.bambooTriplets += 1; // 1 bonus occurrence per trigger
131
+ triggeredBambooTriplets = true;
132
+ counters.bambooTriplets = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.COUNTER_INIT;
133
+ }
134
+ }
135
+ // BAMBOO TWINS BONUS: Triggered when counter reaches 0
136
+ // Decremented by 2x Bamboo pairs on any line
137
+ // Per C++ math model: process each pair individually, trigger can occur multiple times
138
+ for (let i = 0; i < bambooPairCount; i++) {
139
+ counters.bambooTwins--;
140
+ if (counters.bambooTwins <= 0) {
141
+ pendingBonuses.bambooTwins++;
142
+ triggeredBambooTwins = true;
143
+ // Reset counter with random value before processing next pair
144
+ counters.bambooTwins = await randomizeBambooTwinsCounter(gameDirection, rng);
145
+ }
146
+ }
147
+ // CAMERA BONUS: Triggered when counter reaches 0
148
+ // Decremented by 3x Camera wins - process each win individually
149
+ for (let i = 0; i < cameraTripleCount; i++) {
150
+ counters.camera--;
151
+ if (counters.camera <= 0) {
152
+ pendingBonuses.camera++;
153
+ triggeredCamera = true;
154
+ // Reset counter with random value before processing next win
155
+ counters.camera = await randomizeCameraCounter(gameDirection, rng);
156
+ }
157
+ }
158
+ // FIREWORKS BONUS: Triggered when counter reaches 0
159
+ // Decremented by 3x Fireworks wins - process each win individually
160
+ for (let i = 0; i < fireworksTripleCount; i++) {
161
+ counters.fireworks--;
162
+ if (counters.fireworks <= 0) {
163
+ pendingBonuses.fireworks++;
164
+ triggeredFireworks = true;
165
+ // Reset counter before processing next win
166
+ counters.fireworks = happy_panda_config_1.BONUS_FIREWORKS.COUNTER_INIT;
167
+ }
168
+ }
169
+ // CENTER BAMBOO RESPIN: Only on paid spins, no other wins
170
+ if (spinType === happy_panda_config_1.SpinType.BASE_GAME_SPIN && totalWin === 0) {
171
+ const centerSymbol = grid[1][1];
172
+ if (happy_panda_config_1.BAMBOO_SYMBOLS.includes(centerSymbol)) {
173
+ // Check if it's the ONLY bamboo on the screen
174
+ let bambooCount = 0;
175
+ for (let reel = 0; reel < 3; reel++) {
176
+ for (let row = 0; row < 3; row++) {
177
+ if (happy_panda_config_1.BAMBOO_SYMBOLS.includes(grid[reel][row])) {
178
+ bambooCount++;
179
+ }
180
+ }
181
+ }
182
+ if (bambooCount === 1) {
183
+ // Award respins: 1 for Bamboo, 2 for Golden Bamboo
184
+ triggeredRespin = centerSymbol === happy_panda_config_1.Symbol.GOLDEN_BAMBOO ? 2 : 1;
185
+ pendingBonuses.centerBamboo += triggeredRespin;
186
+ }
187
+ }
188
+ }
189
+ return {
190
+ counters,
191
+ pendingBonuses,
192
+ triggeredBambooTriplets,
193
+ triggeredBambooTwins,
194
+ triggeredCamera,
195
+ triggeredFireworks,
196
+ triggeredRespin,
197
+ };
198
+ }
199
+ /**
200
+ * Get the next bonus type to execute (priority order).
201
+ * Returns null if no bonuses pending.
202
+ */
203
+ function getNextBonusType(pending) {
204
+ // Priority: BambooTriplets > BambooTwins > Camera > Fireworks > CenterBamboo
205
+ if (pending.bambooTriplets > 0)
206
+ return 'bambooTriplets';
207
+ if (pending.bambooTwins > 0)
208
+ return 'bambooTwins';
209
+ if (pending.camera > 0)
210
+ return 'camera';
211
+ if (pending.fireworks > 0)
212
+ return 'fireworks';
213
+ if (pending.centerBamboo > 0)
214
+ return 'centerBamboo';
215
+ return null;
216
+ }
217
+ /**
218
+ * Consume one bonus from pending.
219
+ * Returns updated pending and number of spins for that bonus.
220
+ */
221
+ function consumeBonus(pending, bonusType) {
222
+ const updated = { ...pending };
223
+ let spins = 0;
224
+ switch (bonusType) {
225
+ case 'bambooTriplets':
226
+ if (updated.bambooTriplets > 0) {
227
+ updated.bambooTriplets--;
228
+ spins = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.SPINS;
229
+ }
230
+ break;
231
+ case 'bambooTwins':
232
+ if (updated.bambooTwins > 0) {
233
+ updated.bambooTwins--;
234
+ spins = happy_panda_config_1.BONUS_BAMBOO_TWINS.SPINS;
235
+ }
236
+ break;
237
+ case 'camera':
238
+ if (updated.camera > 0) {
239
+ updated.camera--;
240
+ spins = happy_panda_config_1.BONUS_CAMERA.SPINS;
241
+ }
242
+ break;
243
+ case 'fireworks':
244
+ if (updated.fireworks > 0) {
245
+ updated.fireworks--;
246
+ spins = happy_panda_config_1.BONUS_FIREWORKS.SPINS;
247
+ }
248
+ break;
249
+ case 'centerBamboo':
250
+ if (updated.centerBamboo > 0) {
251
+ spins = updated.centerBamboo;
252
+ updated.centerBamboo = 0;
253
+ }
254
+ break;
255
+ }
256
+ return { pending: updated, spins };
257
+ }
258
+ /**
259
+ * Check if any bonus is pending.
260
+ */
261
+ function hasPendingBonus(pending) {
262
+ return (pending.bambooTriplets > 0 ||
263
+ pending.bambooTwins > 0 ||
264
+ pending.camera > 0 ||
265
+ pending.fireworks > 0 ||
266
+ pending.centerBamboo > 0);
267
+ }
268
+ //# sourceMappingURL=counter-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter-manager.js","sourceRoot":"","sources":["../../../src/logic/services/counter-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Logic services exports
3
+ */
4
+ export * from './win-evaluator';
5
+ export * from './counter-manager';
6
+ export * from './jackpot-manager';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/logic/services/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Logic services exports
19
+ */
20
+ __exportStar(require("./win-evaluator"), exports);
21
+ __exportStar(require("./counter-manager"), exports);
22
+ __exportStar(require("./jackpot-manager"), exports);
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logic/services/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,kDAAgC;AAChC,oDAAkC;AAClC,oDAAkC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Jackpot Manager Service
3
+ *
4
+ * Manages progressive jackpot and pool jackpot values.
5
+ *
6
+ * Progressive Jackpot (Bonus Jackpot):
7
+ * - Starts at 100 x bet_game
8
+ * - On losing paid spins: 3/13 chance to increase by 1-2 x bet_stake
9
+ * - Max: 1000 x bet_game
10
+ * - Paid when Jackpot Bonus triggered
11
+ *
12
+ * Pool Jackpot:
13
+ * - Starts at 0
14
+ * - Increases when Red Panda/Golden Panda in center with no wins
15
+ * - Max: 250 x bet_game
16
+ * - Paid when Camera Bonus triggered
17
+ */
18
+ import { SpinType } from '../../config/happy-panda.config';
19
+ import { Grid, GameDirection, JackpotState, RngProvider } from '../../domain/types';
20
+ /**
21
+ * Create initial jackpot state.
22
+ */
23
+ export declare function createInitialJackpots(betGame: number): JackpotState;
24
+ /**
25
+ * Update progressive jackpot value on losing paid spin.
26
+ * 3/13 probability to increase.
27
+ */
28
+ export declare function updateProgressiveJackpot(currentValue: number, gameDirection: GameDirection, betGame: number, betStake: number, spinType: SpinType, totalWin: number, rng: RngProvider): Promise<number>;
29
+ /**
30
+ * Pay out progressive jackpot and reset.
31
+ */
32
+ export declare function payProgressiveJackpot(currentValue: number, betGame: number): {
33
+ payout: number;
34
+ newValue: number;
35
+ };
36
+ /**
37
+ * Update pool jackpot value.
38
+ * Increases when Red Panda/Golden Panda in center position with no wins.
39
+ * IMPORTANT: Must be the ONLY Panda on the entire screen (per C++ math model).
40
+ */
41
+ export declare function updatePoolJackpot(currentValue: number, grid: Grid, gameDirection: GameDirection, betGame: number, betStake: number, spinType: SpinType, totalWin: number): number;
42
+ /**
43
+ * Pay out pool jackpot and reset.
44
+ */
45
+ export declare function payPoolJackpot(currentValue: number, betGame: number): {
46
+ payout: number;
47
+ newValue: number;
48
+ };
49
+ /** Result of jackpot updates */
50
+ export interface JackpotUpdateResult {
51
+ jackpots: JackpotState;
52
+ progressiveJackpotIncreased: boolean;
53
+ poolJackpotIncreased: boolean;
54
+ }
55
+ /**
56
+ * Update both jackpot values after a spin.
57
+ */
58
+ export declare function updateJackpots(currentJackpots: JackpotState, grid: Grid, gameDirection: GameDirection, betGame: number, betStake: number, spinType: SpinType, totalWin: number, rng: RngProvider): Promise<JackpotUpdateResult>;
59
+ //# sourceMappingURL=jackpot-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jackpot-manager.d.ts","sourceRoot":"","sources":["../../../src/logic/services/jackpot-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,QAAQ,EAIT,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOpF;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAKnE;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,CAAC,CAyBjB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAItC;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CAsCR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAItC;AAMD,gCAAgC;AAChC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,YAAY,CAAC;IACvB,2BAA2B,EAAE,OAAO,CAAC;IACrC,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,YAAY,EAC7B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,mBAAmB,CAAC,CAgC9B"}
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * Jackpot Manager Service
4
+ *
5
+ * Manages progressive jackpot and pool jackpot values.
6
+ *
7
+ * Progressive Jackpot (Bonus Jackpot):
8
+ * - Starts at 100 x bet_game
9
+ * - On losing paid spins: 3/13 chance to increase by 1-2 x bet_stake
10
+ * - Max: 1000 x bet_game
11
+ * - Paid when Jackpot Bonus triggered
12
+ *
13
+ * Pool Jackpot:
14
+ * - Starts at 0
15
+ * - Increases when Red Panda/Golden Panda in center with no wins
16
+ * - Max: 250 x bet_game
17
+ * - Paid when Camera Bonus triggered
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createInitialJackpots = createInitialJackpots;
21
+ exports.updateProgressiveJackpot = updateProgressiveJackpot;
22
+ exports.payProgressiveJackpot = payProgressiveJackpot;
23
+ exports.updatePoolJackpot = updatePoolJackpot;
24
+ exports.payPoolJackpot = payPoolJackpot;
25
+ exports.updateJackpots = updateJackpots;
26
+ const happy_panda_config_1 = require("../../config/happy-panda.config");
27
+ const weighted_random_1 = require("../../rng/weighted-random");
28
+ // =============================================================================
29
+ // JACKPOT INITIALIZATION
30
+ // =============================================================================
31
+ /**
32
+ * Create initial jackpot state.
33
+ */
34
+ function createInitialJackpots(betGame) {
35
+ return {
36
+ bonusJackpotValue: happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.INIT * betGame,
37
+ poolJackpotValue: happy_panda_config_1.POOL_JACKPOT.INIT * betGame,
38
+ };
39
+ }
40
+ // =============================================================================
41
+ // PROGRESSIVE JACKPOT
42
+ // =============================================================================
43
+ /**
44
+ * Update progressive jackpot value on losing paid spin.
45
+ * 3/13 probability to increase.
46
+ */
47
+ async function updateProgressiveJackpot(currentValue, gameDirection, betGame, betStake, spinType, totalWin, rng) {
48
+ // Only on paid spins with no win
49
+ if (spinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN || totalWin > 0) {
50
+ return currentValue;
51
+ }
52
+ // Check if should increase (3/13 probability)
53
+ const [yesWeight, noWeight] = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.INCREMENT_WEIGHTS[gameDirection];
54
+ const shouldIncrease = await (0, weighted_random_1.weightedBoolean)(yesWeight, noWeight, rng);
55
+ if (!shouldIncrease) {
56
+ return currentValue;
57
+ }
58
+ // Increase by increment value
59
+ const increment = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.INCREMENT_VALUE[gameDirection] * betStake;
60
+ let newValue = currentValue + increment;
61
+ // Cap at maximum
62
+ const maxValue = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.MAX * betGame;
63
+ if (newValue > maxValue) {
64
+ newValue = maxValue;
65
+ }
66
+ return newValue;
67
+ }
68
+ /**
69
+ * Pay out progressive jackpot and reset.
70
+ */
71
+ function payProgressiveJackpot(currentValue, betGame) {
72
+ const payout = currentValue;
73
+ const newValue = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.INIT * betGame;
74
+ return { payout, newValue };
75
+ }
76
+ // =============================================================================
77
+ // POOL JACKPOT
78
+ // =============================================================================
79
+ /**
80
+ * Update pool jackpot value.
81
+ * Increases when Red Panda/Golden Panda in center position with no wins.
82
+ * IMPORTANT: Must be the ONLY Panda on the entire screen (per C++ math model).
83
+ */
84
+ function updatePoolJackpot(currentValue, grid, gameDirection, betGame, betStake, spinType, totalWin) {
85
+ // Only on paid spins with no win
86
+ if (spinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN || totalWin > 0) {
87
+ return currentValue;
88
+ }
89
+ // Check center position (1,1)
90
+ const centerSymbol = grid[1][1];
91
+ if (!happy_panda_config_1.PANDA_SYMBOLS.includes(centerSymbol)) {
92
+ return currentValue;
93
+ }
94
+ // Count ALL Pandas on the screen - must be exactly 1
95
+ let pandaCount = 0;
96
+ for (let reel = 0; reel < 3; reel++) {
97
+ for (let row = 0; row < 3; row++) {
98
+ if (happy_panda_config_1.PANDA_SYMBOLS.includes(grid[reel][row])) {
99
+ pandaCount++;
100
+ }
101
+ }
102
+ }
103
+ if (pandaCount !== 1) {
104
+ return currentValue; // Only single Panda can trigger pool increment
105
+ }
106
+ // Determine increment based on symbol
107
+ const incrementIdx = centerSymbol === happy_panda_config_1.Symbol.GOLDEN_PANDA ? 0 : 1;
108
+ const increment = happy_panda_config_1.POOL_JACKPOT.INCREMENT[gameDirection][incrementIdx] * betStake;
109
+ let newValue = currentValue + increment;
110
+ // Cap at maximum
111
+ const maxValue = happy_panda_config_1.POOL_JACKPOT.MAX * betGame;
112
+ if (newValue > maxValue) {
113
+ newValue = maxValue;
114
+ }
115
+ return newValue;
116
+ }
117
+ /**
118
+ * Pay out pool jackpot and reset.
119
+ */
120
+ function payPoolJackpot(currentValue, betGame) {
121
+ const payout = currentValue;
122
+ const newValue = happy_panda_config_1.POOL_JACKPOT.INIT * betGame;
123
+ return { payout, newValue };
124
+ }
125
+ /**
126
+ * Update both jackpot values after a spin.
127
+ */
128
+ async function updateJackpots(currentJackpots, grid, gameDirection, betGame, betStake, spinType, totalWin, rng) {
129
+ const oldProgressiveValue = currentJackpots.bonusJackpotValue;
130
+ const oldPoolValue = currentJackpots.poolJackpotValue;
131
+ const newProgressiveValue = await updateProgressiveJackpot(oldProgressiveValue, gameDirection, betGame, betStake, spinType, totalWin, rng);
132
+ const newPoolValue = updatePoolJackpot(oldPoolValue, grid, gameDirection, betGame, betStake, spinType, totalWin);
133
+ return {
134
+ jackpots: {
135
+ bonusJackpotValue: newProgressiveValue,
136
+ poolJackpotValue: newPoolValue,
137
+ },
138
+ progressiveJackpotIncreased: newProgressiveValue > oldProgressiveValue,
139
+ poolJackpotIncreased: newPoolValue > oldPoolValue,
140
+ };
141
+ }
142
+ //# sourceMappingURL=jackpot-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jackpot-manager.js","sourceRoot":"","sources":["../../../src/logic/services/jackpot-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Win Evaluator Service
3
+ *
4
+ * Evaluates wins on a 3x3 grid. Per C++ math model (lines 2731-2735):
5
+ * 1. Wall wins (3x3 screen fills)
6
+ * 2. Scatter wins (Red Panda/Golden Panda) - wall wins suppress scatter wins
7
+ * 3. Line wins - always evaluated independently (NEVER suppressed)
8
+ * 4. Special wins (bonus-specific)
9
+ *
10
+ * Wall and line wins are both added to player_current_win independently.
11
+ */
12
+ import { SpinType } from '../../config/happy-panda.config';
13
+ import { Grid, GameDirection, LineWin, WallWin, ScatterWin, SpecialWin, SpinWinResult } from '../../domain/types';
14
+ /**
15
+ * Evaluate wall (3x3) wins.
16
+ * Wall wins require all 9 positions to be the same symbol or valid mix.
17
+ */
18
+ export declare function evaluateWallWin(grid: Grid, betGame: number): WallWin | null;
19
+ /**
20
+ * Evaluate Red Panda/Golden Panda scatter wins.
21
+ * C++ can award TWO scatter wins:
22
+ * 1. Golden Panda only scatter (if GOLDEN_PANDA >= 2, doubled if no regular Pandas)
23
+ * 2. Total count scatter (if total >= 2 AND at least 1 regular Red Panda)
24
+ * Plus +bet_game bonus if exactly 1 Golden Panda
25
+ */
26
+ export declare function evaluateScatterWins(grid: Grid, betGame: number): ScatterWin[];
27
+ /**
28
+ * Evaluate all line wins.
29
+ */
30
+ export declare function evaluateLineWins(grid: Grid, gameDirection: GameDirection, betStake: number): LineWin[];
31
+ /**
32
+ * Evaluate panda piece line wins (Bamboo Triplets Bonus).
33
+ * Dancing Panda pieces on lines pay special multiplier.
34
+ */
35
+ export declare function evaluatePandaPieceWins(grid: Grid, gameDirection: GameDirection, betStake: number): SpecialWin[];
36
+ /**
37
+ * Evaluate camera scatter wins (Camera Bonus).
38
+ * Each camera pays as scatter during bonus.
39
+ */
40
+ export declare function evaluateCameraScatterWins(grid: Grid, gameDirection: GameDirection, betStake: number): SpecialWin | null;
41
+ /**
42
+ * Evaluate hat scatter wins (Fireworks Bonus).
43
+ * Each hat pays as scatter during bonus.
44
+ */
45
+ export declare function evaluateHatScatterWins(grid: Grid, gameDirection: GameDirection, betStake: number): SpecialWin | null;
46
+ /**
47
+ * Evaluate all wins for a spin.
48
+ * Implements correct priority: Wall > Scatter > Lines
49
+ */
50
+ export declare function evaluateSpin(grid: Grid, gameDirection: GameDirection, betStake: number, betGame: number, spinType: SpinType): SpinWinResult;
51
+ //# sourceMappingURL=win-evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"win-evaluator.d.ts","sourceRoot":"","sources":["../../../src/logic/services/win-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAEL,QAAQ,EAiBT,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,IAAI,EACJ,aAAa,EAEb,OAAO,EACP,OAAO,EACP,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,oBAAoB,CAAC;AAqE5B;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,GACd,OAAO,GAAG,IAAI,CAmEhB;AAMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,GACd,UAAU,EAAE,CAsDd;AAmGD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,EAAE,CAYX;AAMD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU,EAAE,CAmCd;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU,GAAG,IAAI,CAcnB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GACf,UAAU,GAAG,IAAI,CAcnB;AAMD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,GACjB,aAAa,CAgFf"}