@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,21 @@
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 handlers exports
19
+ */
20
+ __exportStar(require("./spin-handler"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logic/handlers/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,iDAA+B"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Spin Handler
3
+ *
4
+ * Coordinates the entire spin flow:
5
+ * 1. Determine spin type
6
+ * 2. Generate grid
7
+ * 3. Evaluate wins
8
+ * 4. Update counters
9
+ * 5. Update jackpots
10
+ * 6. Determine next spin type
11
+ * 7. Build response
12
+ */
13
+ import { SpinType } from '../../config/happy-panda.config';
14
+ import { Grid, GameDirection, GameState, SpinWinResult, RngProvider } from '../../domain/types';
15
+ export interface SpinResult {
16
+ /** Resulting screen (3x3 grid) */
17
+ screen: Grid;
18
+ /** Win evaluation results */
19
+ wins: SpinWinResult;
20
+ /** Updated game state */
21
+ state: GameState;
22
+ /** The spin type that was actually executed this spin */
23
+ executedSpinType: SpinType;
24
+ /** Progressive jackpot won (if any) */
25
+ jackpotWon: number;
26
+ /** Pool jackpot won (if any) */
27
+ poolJackpotWon: number;
28
+ /** Bonus type triggered this spin */
29
+ bonusTriggered: SpinType | null;
30
+ /** Is this the final spin of bonus sequence */
31
+ isBonusComplete: boolean;
32
+ /** Total player winning (required by RGS for balance crediting) */
33
+ playerWinning: number;
34
+ }
35
+ /**
36
+ * Execute a single spin.
37
+ */
38
+ export declare function executeSpin(state: GameState, rng: RngProvider): Promise<SpinResult>;
39
+ /** Minimum bet stake */
40
+ export declare const MIN_BET_STAKE = 1;
41
+ /** Maximum bet stake (reasonable upper bound) */
42
+ export declare const MAX_BET_STAKE = 100;
43
+ /**
44
+ * Validate bet parameters.
45
+ * @throws Error if parameters are invalid
46
+ */
47
+ export declare function validateBetParams(gameDirection: GameDirection, betStake: number): void;
48
+ /**
49
+ * Create initial game state.
50
+ * @throws Error if parameters are invalid
51
+ */
52
+ export declare function createInitialState(gameDirection: GameDirection, betStake: number): GameState;
53
+ /**
54
+ * Update state for new bet.
55
+ * @throws Error if parameters are invalid
56
+ */
57
+ export declare function updateBet(state: GameState, gameDirection: GameDirection, betStake: number): GameState;
58
+ //# sourceMappingURL=spin-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spin-handler.d.ts","sourceRoot":"","sources":["../../../src/logic/handlers/spin-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAEL,QAAQ,EAET,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,IAAI,EACJ,aAAa,EACb,SAAS,EACT,aAAa,EACb,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAoB5B,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,MAAM,EAAE,IAAI,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,aAAa,CAAC;IACpB,yBAAyB;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,yDAAyD;IACzD,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,cAAc,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,+CAA+C;IAC/C,eAAe,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;CACvB;AAqBD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,UAAU,CAAC,CAsLrB;AAMD,wBAAwB;AACxB,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,iDAAiD;AACjD,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAUtF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GACf,SAAS,CAsCX;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,GACf,SAAS,CAiBX"}
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ /**
3
+ * Spin Handler
4
+ *
5
+ * Coordinates the entire spin flow:
6
+ * 1. Determine spin type
7
+ * 2. Generate grid
8
+ * 3. Evaluate wins
9
+ * 4. Update counters
10
+ * 5. Update jackpots
11
+ * 6. Determine next spin type
12
+ * 7. Build response
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MAX_BET_STAKE = exports.MIN_BET_STAKE = void 0;
16
+ exports.executeSpin = executeSpin;
17
+ exports.validateBetParams = validateBetParams;
18
+ exports.createInitialState = createInitialState;
19
+ exports.updateBet = updateBet;
20
+ const happy_panda_config_1 = require("../../config/happy-panda.config");
21
+ const types_1 = require("../../domain/types");
22
+ const spin_generator_1 = require("../../rng/spin-generator");
23
+ const win_evaluator_1 = require("../services/win-evaluator");
24
+ const counter_manager_1 = require("../services/counter-manager");
25
+ const jackpot_manager_1 = require("../services/jackpot-manager");
26
+ // =============================================================================
27
+ // SPIN TYPE TO BONUS TYPE MAPPING
28
+ // =============================================================================
29
+ function bonusTypeToSpinType(bonusType) {
30
+ switch (bonusType) {
31
+ case 'bambooTriplets': return happy_panda_config_1.SpinType.BONUS_BAMBOO_TRIPLETS;
32
+ case 'bambooTwins': return happy_panda_config_1.SpinType.BONUS_BAMBOO_TWINS;
33
+ case 'camera': return happy_panda_config_1.SpinType.BONUS_CAMERA;
34
+ case 'fireworks': return happy_panda_config_1.SpinType.BONUS_FIREWORKS;
35
+ case 'centerBamboo': return happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO;
36
+ default: return happy_panda_config_1.SpinType.BASE_GAME_SPIN;
37
+ }
38
+ }
39
+ // =============================================================================
40
+ // MAIN SPIN EXECUTION
41
+ // =============================================================================
42
+ /**
43
+ * Execute a single spin.
44
+ */
45
+ async function executeSpin(state, rng) {
46
+ const { gameDirection, betStake, betGame, currentSpinType, spinsRemaining, counters, pendingBonuses, jackpots, centerBambooSymbol, } = state;
47
+ // Step 1: Generate grid based on spin type
48
+ // For RESPIN_CENTER_BAMBOO, pass the previous grid (state.screen) so edge positions
49
+ // can be preserved from the triggering paid_spin, matching C++ ScreenRespinCherry() behavior
50
+ const isRespin = currentSpinType === happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO;
51
+ const grid = await (0, spin_generator_1.generateGrid)(currentSpinType, gameDirection, rng, isRespin ? centerBambooSymbol ?? undefined : undefined, isRespin ? state.screen : undefined);
52
+ // Step 2: Evaluate wins
53
+ const wins = (0, win_evaluator_1.evaluateSpin)(grid, gameDirection, betStake, betGame, currentSpinType);
54
+ // Step 3: Handle special jackpot bonus logic
55
+ let jackpotWon = 0;
56
+ let poolJackpotWon = 0;
57
+ let updatedJackpots = { ...jackpots };
58
+ if (currentSpinType === happy_panda_config_1.SpinType.BONUS_BAMBOO_TRIPLETS) {
59
+ // Every bamboo triplets bonus spin gets the ADD_WIN bonus (C++ Main Logic line 59)
60
+ // "please note scenario: each screen gets such winning, independent on pieces panda wins"
61
+ jackpotWon = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.ADD_WIN * betGame;
62
+ // Full panda piece = ALSO triggers progressive jackpot payout
63
+ if ((0, spin_generator_1.isCompletePandaPiece)(grid)) {
64
+ const { payout, newValue } = (0, jackpot_manager_1.payProgressiveJackpot)(jackpots.bonusJackpotValue, betGame);
65
+ jackpotWon += payout;
66
+ updatedJackpots.bonusJackpotValue = newValue;
67
+ }
68
+ }
69
+ // Step 4: Update counters (only on paid spin and respin)
70
+ let updatedCounters = { ...counters };
71
+ let updatedPending = { ...pendingBonuses };
72
+ let bonusTriggered = null;
73
+ if (currentSpinType === happy_panda_config_1.SpinType.BASE_GAME_SPIN || currentSpinType === happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO) {
74
+ const counterResult = await (0, counter_manager_1.updateCounters)(counters, pendingBonuses, wins.lineWins, grid, gameDirection, currentSpinType, wins.totalPayout, rng);
75
+ updatedCounters = counterResult.counters;
76
+ updatedPending = counterResult.pendingBonuses;
77
+ // Determine triggered bonus (for animation/display)
78
+ if (counterResult.triggeredBambooTriplets) {
79
+ bonusTriggered = happy_panda_config_1.SpinType.BONUS_BAMBOO_TRIPLETS;
80
+ }
81
+ else if (counterResult.triggeredBambooTwins) {
82
+ bonusTriggered = happy_panda_config_1.SpinType.BONUS_BAMBOO_TWINS;
83
+ }
84
+ else if (counterResult.triggeredCamera) {
85
+ bonusTriggered = happy_panda_config_1.SpinType.BONUS_CAMERA;
86
+ // Camera bonus also pays pool jackpot
87
+ const poolResult = (0, jackpot_manager_1.payPoolJackpot)(jackpots.poolJackpotValue, betGame);
88
+ poolJackpotWon = poolResult.payout;
89
+ updatedJackpots.poolJackpotValue = poolResult.newValue;
90
+ }
91
+ else if (counterResult.triggeredFireworks) {
92
+ bonusTriggered = happy_panda_config_1.SpinType.BONUS_FIREWORKS;
93
+ }
94
+ else if (counterResult.triggeredRespin > 0) {
95
+ bonusTriggered = happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO;
96
+ }
97
+ }
98
+ // Step 5: Update jackpots (progressive and pool on paid spins)
99
+ if (currentSpinType === happy_panda_config_1.SpinType.BASE_GAME_SPIN) {
100
+ const jackpotUpdate = await (0, jackpot_manager_1.updateJackpots)(updatedJackpots, grid, gameDirection, betGame, betStake, currentSpinType, wins.totalPayout, rng);
101
+ updatedJackpots = jackpotUpdate.jackpots;
102
+ }
103
+ // Step 6: Determine next spin type and remaining spins
104
+ let nextSpinType = happy_panda_config_1.SpinType.BASE_GAME_SPIN;
105
+ let nextSpinsRemaining = 0;
106
+ let newAccumulatedBonusWins = state.accumulatedBonusWins;
107
+ let newCenterBambooSymbol = state.centerBambooSymbol;
108
+ let isBonusComplete = false;
109
+ // Handle ongoing bonus spins
110
+ if (currentSpinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN && spinsRemaining > 1) {
111
+ // Continue current bonus
112
+ nextSpinType = currentSpinType;
113
+ nextSpinsRemaining = spinsRemaining - 1;
114
+ newAccumulatedBonusWins += wins.totalPayout + jackpotWon + poolJackpotWon;
115
+ }
116
+ else if (currentSpinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN && spinsRemaining === 1) {
117
+ // Bonus sequence ending, check for more pending bonuses
118
+ newAccumulatedBonusWins += wins.totalPayout + jackpotWon + poolJackpotWon;
119
+ const nextBonusType = (0, counter_manager_1.getNextBonusType)(updatedPending);
120
+ if (nextBonusType) {
121
+ // Start next bonus
122
+ const { pending: newPending, spins } = (0, counter_manager_1.consumeBonus)(updatedPending, nextBonusType);
123
+ updatedPending = newPending;
124
+ nextSpinType = bonusTypeToSpinType(nextBonusType);
125
+ nextSpinsRemaining = spins;
126
+ // Set center bamboo symbol if starting respin
127
+ if (nextBonusType === 'centerBamboo') {
128
+ newCenterBambooSymbol = grid[1][1];
129
+ }
130
+ }
131
+ else {
132
+ // All bonuses complete, return to paid spin
133
+ nextSpinType = happy_panda_config_1.SpinType.BASE_GAME_SPIN;
134
+ nextSpinsRemaining = 0;
135
+ isBonusComplete = true;
136
+ newAccumulatedBonusWins = 0; // Reset for next bonus sequence
137
+ newCenterBambooSymbol = null;
138
+ // Reset bambooTriplets counter to ready state (#16)
139
+ updatedCounters.bambooTriplets = happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.COUNTER_INIT;
140
+ }
141
+ }
142
+ else if (currentSpinType === happy_panda_config_1.SpinType.BASE_GAME_SPIN) {
143
+ // Check if any bonuses were triggered
144
+ const nextBonusType = (0, counter_manager_1.getNextBonusType)(updatedPending);
145
+ if (nextBonusType) {
146
+ const { pending: newPending, spins } = (0, counter_manager_1.consumeBonus)(updatedPending, nextBonusType);
147
+ updatedPending = newPending;
148
+ nextSpinType = bonusTypeToSpinType(nextBonusType);
149
+ nextSpinsRemaining = spins;
150
+ // Set center bamboo symbol if starting respin
151
+ if (nextBonusType === 'centerBamboo') {
152
+ newCenterBambooSymbol = grid[1][1];
153
+ }
154
+ }
155
+ }
156
+ // Build updated state
157
+ const updatedState = {
158
+ ...state,
159
+ currentSpinType: nextSpinType,
160
+ nextSpinType,
161
+ spinsRemaining: nextSpinsRemaining,
162
+ counters: updatedCounters,
163
+ pendingBonuses: updatedPending,
164
+ jackpots: updatedJackpots,
165
+ screen: grid,
166
+ accumulatedBonusWins: newAccumulatedBonusWins,
167
+ centerBambooSymbol: newCenterBambooSymbol,
168
+ };
169
+ // Calculate total player winning for RGS compatibility
170
+ const playerWinning = wins.totalPayout + jackpotWon + poolJackpotWon;
171
+ return {
172
+ screen: grid,
173
+ wins,
174
+ state: updatedState,
175
+ executedSpinType: currentSpinType,
176
+ jackpotWon,
177
+ poolJackpotWon,
178
+ bonusTriggered,
179
+ isBonusComplete,
180
+ playerWinning, // Required by RGS for win crediting
181
+ };
182
+ }
183
+ // =============================================================================
184
+ // STATE INITIALIZATION
185
+ // =============================================================================
186
+ /** Minimum bet stake */
187
+ exports.MIN_BET_STAKE = 1;
188
+ /** Maximum bet stake (reasonable upper bound) */
189
+ exports.MAX_BET_STAKE = 100;
190
+ /**
191
+ * Validate bet parameters.
192
+ * @throws Error if parameters are invalid
193
+ */
194
+ function validateBetParams(gameDirection, betStake) {
195
+ if (gameDirection !== types_1.GameDirection.SINGLE && gameDirection !== types_1.GameDirection.BOTH) {
196
+ throw new Error(`Invalid game direction: ${gameDirection}. Must be 0 (8 lines) or 1 (16 lines)`);
197
+ }
198
+ if (!Number.isInteger(betStake)) {
199
+ throw new Error(`Bet stake must be an integer, got: ${betStake}`);
200
+ }
201
+ if (betStake < exports.MIN_BET_STAKE || betStake > exports.MAX_BET_STAKE) {
202
+ throw new Error(`Bet stake must be between ${exports.MIN_BET_STAKE} and ${exports.MAX_BET_STAKE}, got: ${betStake}`);
203
+ }
204
+ }
205
+ /**
206
+ * Create initial game state.
207
+ * @throws Error if parameters are invalid
208
+ */
209
+ function createInitialState(gameDirection, betStake) {
210
+ validateBetParams(gameDirection, betStake);
211
+ const baseBet = gameDirection === types_1.GameDirection.SINGLE ? 8 : 16;
212
+ const betGame = baseBet * betStake;
213
+ return {
214
+ gameDirection,
215
+ betStake,
216
+ betGame,
217
+ currentSpinType: happy_panda_config_1.SpinType.BASE_GAME_SPIN,
218
+ nextSpinType: happy_panda_config_1.SpinType.BASE_GAME_SPIN,
219
+ spinsRemaining: 0,
220
+ counters: {
221
+ bambooTriplets: 1,
222
+ bambooTwins: 9, // Will be randomized on first trigger
223
+ camera: 5, // Will be randomized on first trigger
224
+ fireworks: 1,
225
+ },
226
+ pendingBonuses: {
227
+ bambooTriplets: 0,
228
+ bambooTwins: 0,
229
+ camera: 0,
230
+ fireworks: 0,
231
+ centerBamboo: 0,
232
+ },
233
+ jackpots: {
234
+ bonusJackpotValue: 100 * betGame,
235
+ poolJackpotValue: 0,
236
+ },
237
+ screen: [
238
+ [happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
239
+ [happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
240
+ [happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
241
+ ],
242
+ accumulatedBonusWins: 0,
243
+ centerBambooSymbol: null,
244
+ };
245
+ }
246
+ /**
247
+ * Update state for new bet.
248
+ * @throws Error if parameters are invalid
249
+ */
250
+ function updateBet(state, gameDirection, betStake) {
251
+ validateBetParams(gameDirection, betStake);
252
+ // Can only change bet during paid spin with no pending bonuses
253
+ if (state.currentSpinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN || (0, counter_manager_1.hasPendingBonus)(state.pendingBonuses)) {
254
+ throw new Error('Cannot change bet during bonus or with pending bonuses');
255
+ }
256
+ const baseBet = gameDirection === types_1.GameDirection.SINGLE ? 8 : 16;
257
+ const betGame = baseBet * betStake;
258
+ return {
259
+ ...state,
260
+ gameDirection,
261
+ betStake,
262
+ betGame,
263
+ };
264
+ }
265
+ //# sourceMappingURL=spin-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spin-handler.js","sourceRoot":"","sources":["../../../src/logic/handlers/spin-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Logic module exports
3
+ */
4
+ export * from './services';
5
+ export * from './handlers';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logic/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,22 @@
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 module exports
19
+ */
20
+ __exportStar(require("./services"), exports);
21
+ __exportStar(require("./handlers"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/logic/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,6CAA2B;AAC3B,6CAA2B"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Counter Manager Service
3
+ *
4
+ * Manages bonus counters and trigger detection.
5
+ * Each bonus type has a counter that decrements on specific events.
6
+ * When a counter reaches 0, the bonus is triggered.
7
+ */
8
+ import { SpinType } from '../../config/happy-panda.config';
9
+ import { Grid, GameDirection, BonusCounters, PendingBonuses, LineWin, RngProvider } from '../../domain/types';
10
+ /**
11
+ * Create initial bonus counters.
12
+ */
13
+ export declare function createInitialCounters(): BonusCounters;
14
+ /**
15
+ * Create empty pending bonuses.
16
+ */
17
+ export declare function createEmptyPendingBonuses(): PendingBonuses;
18
+ /**
19
+ * Randomize Bamboo Twins counter initial value (6 or 9).
20
+ */
21
+ export declare function randomizeBambooTwinsCounter(gameDirection: GameDirection, rng: RngProvider): Promise<number>;
22
+ /**
23
+ * Randomize Camera counter initial value (3 or 5).
24
+ */
25
+ export declare function randomizeCameraCounter(gameDirection: GameDirection, rng: RngProvider): Promise<number>;
26
+ /** Result of counter update */
27
+ export interface CounterUpdateResult {
28
+ counters: BonusCounters;
29
+ pendingBonuses: PendingBonuses;
30
+ triggeredBambooTriplets: boolean;
31
+ triggeredBambooTwins: boolean;
32
+ triggeredCamera: boolean;
33
+ triggeredFireworks: boolean;
34
+ triggeredRespin: number;
35
+ }
36
+ /**
37
+ * Update counters based on spin results.
38
+ * Only applies during paid_spin and respin_cherry.
39
+ */
40
+ export declare function updateCounters(currentCounters: BonusCounters, currentPending: PendingBonuses, lineWins: LineWin[], grid: Grid, gameDirection: GameDirection, spinType: SpinType, totalWin: number, rng: RngProvider): Promise<CounterUpdateResult>;
41
+ /** Bonus type to consume */
42
+ export type BonusType = 'bambooTriplets' | 'bambooTwins' | 'camera' | 'fireworks' | 'centerBamboo';
43
+ /**
44
+ * Get the next bonus type to execute (priority order).
45
+ * Returns null if no bonuses pending.
46
+ */
47
+ export declare function getNextBonusType(pending: PendingBonuses): BonusType | null;
48
+ /**
49
+ * Consume one bonus from pending.
50
+ * Returns updated pending and number of spins for that bonus.
51
+ */
52
+ export declare function consumeBonus(pending: PendingBonuses, bonusType: BonusType): {
53
+ pending: PendingBonuses;
54
+ spins: number;
55
+ };
56
+ /**
57
+ * Check if any bonus is pending.
58
+ */
59
+ export declare function hasPendingBonus(pending: PendingBonuses): boolean;
60
+ //# sourceMappingURL=counter-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter-manager.d.ts","sourceRoot":"","sources":["../../../src/logic/services/counter-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEL,QAAQ,EAOT,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,IAAI,EACJ,aAAa,EACb,aAAa,EACb,cAAc,EACd,OAAO,EACP,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAO5B;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,aAAa,CAOrD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,cAAc,CAQ1D;AAED;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,aAAa,EAC5B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,aAAa,EAC5B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,CAAC,CAIjB;AAMD,+BAA+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,uBAAuB,EAAE,OAAO,CAAC;IACjC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,aAAa,EAC9B,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,mBAAmB,CAAC,CA2I9B;AAMD,4BAA4B;AAC5B,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,cAAc,CAAC;AAEnG;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAQ1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,SAAS,GACnB;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAsC5C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAQhE"}