@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,338 @@
1
+ "use strict";
2
+ /**
3
+ * Spin Generator with Symbol Limitation System
4
+ *
5
+ * Generates 3x3 grids with weighted symbol distribution and limitation constraints.
6
+ * Each symbol type has a randomly selected maximum count (2, 4, 7, or 9).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.generateGrid = generateGrid;
10
+ exports.generatePandaPieceGrid = generatePandaPieceGrid;
11
+ exports.isCompletePandaPiece = isCompletePandaPiece;
12
+ const happy_panda_config_1 = require("../config/happy-panda.config");
13
+ const weighted_random_1 = require("./weighted-random");
14
+ /** Get configuration for a spin type */
15
+ function getSpinConfig(spinType) {
16
+ switch (spinType) {
17
+ case happy_panda_config_1.SpinType.BASE_GAME_SPIN:
18
+ return {
19
+ symbols: happy_panda_config_1.BASE_GAME_SPIN_SYMBOLS,
20
+ weights: happy_panda_config_1.BASE_GAME_SPIN_WEIGHTS,
21
+ limitationWeights: happy_panda_config_1.BASE_GAME_SPIN_LIMITATION_WEIGHTS,
22
+ has3x3: true,
23
+ weights3x3: happy_panda_config_1.BASE_GAME_SPIN_3X3_WEIGHTS,
24
+ symbolWeights3x3: happy_panda_config_1.BASE_GAME_SPIN_3X3_SYMBOL_WEIGHTS,
25
+ };
26
+ case happy_panda_config_1.SpinType.BONUS_BAMBOO_TRIPLETS:
27
+ return {
28
+ symbols: happy_panda_config_1.BAMBOO_TRIPLETS_SYMBOLS,
29
+ weights: happy_panda_config_1.BAMBOO_TRIPLETS_WEIGHTS,
30
+ limitationWeights: happy_panda_config_1.BAMBOO_TRIPLETS_LIMITATION_WEIGHTS,
31
+ has3x3: true,
32
+ weights3x3: happy_panda_config_1.BAMBOO_TRIPLETS_3X3_WEIGHTS,
33
+ symbolWeights3x3: happy_panda_config_1.BAMBOO_TRIPLETS_3X3_SYMBOL_WEIGHTS,
34
+ };
35
+ case happy_panda_config_1.SpinType.BONUS_BAMBOO_TWINS:
36
+ return {
37
+ symbols: happy_panda_config_1.BAMBOO_TWINS_SYMBOLS,
38
+ weights: happy_panda_config_1.BAMBOO_TWINS_WEIGHTS,
39
+ limitationWeights: happy_panda_config_1.BAMBOO_TWINS_LIMITATION_WEIGHTS,
40
+ has3x3: true,
41
+ weights3x3: happy_panda_config_1.BAMBOO_TWINS_3X3_WEIGHTS,
42
+ symbolWeights3x3: happy_panda_config_1.BAMBOO_TWINS_3X3_SYMBOL_WEIGHTS,
43
+ };
44
+ case happy_panda_config_1.SpinType.BONUS_CAMERA:
45
+ return {
46
+ symbols: happy_panda_config_1.CAMERA_BONUS_SYMBOLS,
47
+ weights: happy_panda_config_1.CAMERA_BONUS_WEIGHTS,
48
+ limitationWeights: happy_panda_config_1.CAMERA_BONUS_LIMITATION_WEIGHTS,
49
+ has3x3: true,
50
+ weights3x3: happy_panda_config_1.CAMERA_BONUS_3X3_WEIGHTS,
51
+ symbolWeights3x3: happy_panda_config_1.CAMERA_BONUS_3X3_SYMBOL_WEIGHTS,
52
+ };
53
+ case happy_panda_config_1.SpinType.BONUS_FIREWORKS:
54
+ return {
55
+ symbols: happy_panda_config_1.FIREWORKS_BONUS_SYMBOLS,
56
+ weights: happy_panda_config_1.FIREWORKS_BONUS_WEIGHTS,
57
+ limitationWeights: happy_panda_config_1.FIREWORKS_BONUS_LIMITATION_WEIGHTS,
58
+ has3x3: true,
59
+ weights3x3: happy_panda_config_1.FIREWORKS_BONUS_3X3_WEIGHTS,
60
+ symbolWeights3x3: happy_panda_config_1.FIREWORKS_BONUS_3X3_SYMBOL_WEIGHTS,
61
+ };
62
+ case happy_panda_config_1.SpinType.RESPIN_CENTER_BAMBOO:
63
+ return {
64
+ symbols: happy_panda_config_1.CENTER_BAMBOO_RESPIN_SYMBOLS,
65
+ weights: happy_panda_config_1.CENTER_BAMBOO_RESPIN_WEIGHTS,
66
+ limitationWeights: happy_panda_config_1.CENTER_BAMBOO_RESPIN_LIMITATION_WEIGHTS,
67
+ has3x3: false, // No 3x3 override for respin
68
+ };
69
+ default:
70
+ throw new Error(`Unknown spin type: ${spinType}`);
71
+ }
72
+ }
73
+ /** Convert position index (0-8) to [reel, row] */
74
+ function positionToCoords(posIdx) {
75
+ const reel = Math.floor(posIdx / happy_panda_config_1.GRID.ROWS);
76
+ const row = posIdx % happy_panda_config_1.GRID.ROWS;
77
+ return [reel, row];
78
+ }
79
+ /**
80
+ * Select limitation tiers for all symbols.
81
+ * @param config Spin configuration
82
+ * @param gameDirection Game direction (0 = 8 lines, 1 = 16 lines)
83
+ * @param rng RNG provider
84
+ * @returns Map of symbol index to maximum count
85
+ */
86
+ async function selectLimitationTiers(config, gameDirection, rng) {
87
+ const limitations = new Map();
88
+ for (let symbolIdx = 0; symbolIdx < config.symbols.length; symbolIdx++) {
89
+ const weights = config.limitationWeights[gameDirection][symbolIdx];
90
+ // Skip if all weights are zero (symbol has no limitation)
91
+ const totalWeight = weights.reduce((sum, w) => sum + w, 0);
92
+ if (totalWeight === 0) {
93
+ limitations.set(symbolIdx, happy_panda_config_1.GRID.POSITIONS); // No limit
94
+ continue;
95
+ }
96
+ const tierIdx = await (0, weighted_random_1.selectWeightedIndex)(weights, rng);
97
+ limitations.set(symbolIdx, happy_panda_config_1.LIMITATION_TIERS[tierIdx]);
98
+ }
99
+ return limitations;
100
+ }
101
+ /**
102
+ * Generate a grid with weighted symbols and limitation constraints.
103
+ *
104
+ * IMPORTANT: C++ logic requires limitations to be selected FIRST, then
105
+ * 3x3 check only happens if a symbol has limit=9 (max symbols).
106
+ *
107
+ * @param spinType Type of spin
108
+ * @param gameDirection Game direction
109
+ * @param rng RNG provider
110
+ * @param centerSymbol Optional fixed center symbol (for respin)
111
+ * @param previousGrid Optional grid from triggering spin (for RESPIN_CENTER_BAMBOO edge preservation)
112
+ * @returns Generated 3x3 grid
113
+ */
114
+ async function generateGrid(spinType, gameDirection, rng, centerSymbol, previousGrid) {
115
+ const config = getSpinConfig(spinType);
116
+ // Step 1: Select limitation tiers for each symbol FIRST (C++ order!)
117
+ const limitations = await selectLimitationTiers(config, gameDirection, rng);
118
+ // Step 2: Check for matrix win - C++ EXACT LOGIC
119
+ // Gate: ONLY SEV_S (idx 0) or SEV (idx 1) having limit=9 triggers the check
120
+ let matrixSymbol = null;
121
+ let matrixCount = 0;
122
+ if (config.has3x3 && config.weights3x3 && config.symbolWeights3x3) {
123
+ const sevSLimit = limitations.get(0) || 0; // SEV_S
124
+ const sevLimit = limitations.get(1) || 0; // SEV
125
+ // C++ line 69: if((maxLimits[SEV_S] == 9) || (maxLimits[SEV] == 9))
126
+ if (sevSLimit === 9 || sevLimit === 9) {
127
+ // Gate passed - now check PaidSpins3x3 weights
128
+ const [yesWeight, noWeight] = config.weights3x3[gameDirection];
129
+ const shouldMatrix = await (0, weighted_random_1.weightedBoolean)(yesWeight, noWeight, rng);
130
+ if (shouldMatrix) {
131
+ // Determine matrix count (7, 8, or 9) based on which has limit=9
132
+ // C++ lines 86-88
133
+ if (sevSLimit === 9 && sevLimit === 9) {
134
+ matrixCount = 9; // Full 3x3
135
+ }
136
+ else if (sevSLimit === 9 && sevLimit !== 9) {
137
+ matrixCount = 8; // 8 symbols
138
+ }
139
+ else if (sevSLimit !== 9 && sevLimit === 9) {
140
+ matrixCount = 7; // 7 symbols
141
+ }
142
+ // Select which symbol for the matrix from PaidSpins3x3_symbol weights
143
+ // C++ lines 91-100: ALL symbols are eligible, not just those with limit=9
144
+ const symbolIdx = await (0, weighted_random_1.selectWeightedIndex)(config.symbolWeights3x3, rng);
145
+ matrixSymbol = config.symbols[symbolIdx];
146
+ }
147
+ }
148
+ }
149
+ // If matrix win, generate grid with matrixCount of that symbol
150
+ if (matrixSymbol !== null && matrixCount > 0) {
151
+ return createMatrixGrid(matrixSymbol, matrixCount, config, gameDirection, rng, limitations);
152
+ }
153
+ // Step 3: Generate symbols position by position (using pre-selected limitations)
154
+ const grid = [
155
+ [happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
156
+ [happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
157
+ [happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
158
+ ];
159
+ // Track symbol counts
160
+ const symbolCounts = new Map();
161
+ for (let i = 0; i < config.symbols.length; i++) {
162
+ symbolCounts.set(i, 0);
163
+ }
164
+ // CRITICAL: For RESPIN_CENTER_BAMBOO, pre-count the center symbol BEFORE generating other positions
165
+ // This ensures the limitation system correctly restricts cherries across ALL positions
166
+ // Without this, positions 0-3 (generated before center at position 4) ignore the center cherry
167
+ if (centerSymbol !== undefined) {
168
+ grid[1][1] = centerSymbol;
169
+ const centerIdx = config.symbols.indexOf(centerSymbol);
170
+ if (centerIdx >= 0) {
171
+ symbolCounts.set(centerIdx, 1);
172
+ }
173
+ }
174
+ // Generate each position
175
+ for (let posIdx = 0; posIdx < happy_panda_config_1.GRID.POSITIONS; posIdx++) {
176
+ const [reel, row] = positionToCoords(posIdx);
177
+ // Skip center position - already placed above for RESPIN_CENTER_BAMBOO
178
+ if (reel === 1 && row === 1 && centerSymbol !== undefined) {
179
+ continue;
180
+ }
181
+ // C++ ScreenRespinCherry() lines 2342/2373: only positions where (reel!=1)&&(row!=1)
182
+ // are cleared and regenerated. Edge positions touching center are PRESERVED from
183
+ // the triggering paid_spin grid entirely.
184
+ const isRespin = centerSymbol !== undefined;
185
+ const isEdge = (reel === 1 || row === 1) && !(reel === 1 && row === 1);
186
+ if (isRespin && isEdge) {
187
+ if (previousGrid) {
188
+ grid[reel][row] = previousGrid[reel][row];
189
+ // Track the preserved symbol in counts for limitation system
190
+ const preservedIdx = config.symbols.indexOf(previousGrid[reel][row]);
191
+ if (preservedIdx >= 0) {
192
+ symbolCounts.set(preservedIdx, (symbolCounts.get(preservedIdx) || 0) + 1);
193
+ }
194
+ }
195
+ // If no previousGrid, leave as NONE (shouldn't happen in practice)
196
+ continue;
197
+ }
198
+ // Get base weights for this position
199
+ const baseWeights = [...config.weights[gameDirection][posIdx]];
200
+ // Apply limitation constraints
201
+ for (let symbolIdx = 0; symbolIdx < config.symbols.length; symbolIdx++) {
202
+ const currentCount = symbolCounts.get(symbolIdx) || 0;
203
+ const maxCount = limitations.get(symbolIdx) || happy_panda_config_1.GRID.POSITIONS;
204
+ if (currentCount >= maxCount) {
205
+ baseWeights[symbolIdx] = 0; // Disable this symbol
206
+ }
207
+ }
208
+ // Select symbol
209
+ const totalWeight = baseWeights.reduce((sum, w) => sum + w, 0);
210
+ if (totalWeight === 0) {
211
+ // Fallback: all symbols limited, pick any available
212
+ for (let symbolIdx = 0; symbolIdx < config.symbols.length; symbolIdx++) {
213
+ const currentCount = symbolCounts.get(symbolIdx) || 0;
214
+ const maxCount = limitations.get(symbolIdx) || happy_panda_config_1.GRID.POSITIONS;
215
+ if (currentCount < maxCount) {
216
+ // C++ line 1956-1957: Panda pieces are POSITION-SPECIFIC
217
+ // main_screen[reel][row] = PANDA_PIECE_00 + reel*3 + row;
218
+ const baseSymbol = config.symbols[symbolIdx];
219
+ if (baseSymbol === happy_panda_config_1.Symbol.PANDA_PIECE_00) {
220
+ grid[reel][row] = happy_panda_config_1.Symbol.PANDA_PIECE_00 + reel * 3 + row;
221
+ }
222
+ else {
223
+ grid[reel][row] = baseSymbol;
224
+ }
225
+ symbolCounts.set(symbolIdx, currentCount + 1);
226
+ break;
227
+ }
228
+ }
229
+ }
230
+ else {
231
+ const symbolIdx = await (0, weighted_random_1.selectWeightedIndex)(baseWeights, rng);
232
+ // C++ line 1956-1957: Panda pieces are POSITION-SPECIFIC
233
+ // main_screen[reel][row] = PANDA_PIECE_00 + reel*3 + row;
234
+ const baseSymbol = config.symbols[symbolIdx];
235
+ if (baseSymbol === happy_panda_config_1.Symbol.PANDA_PIECE_00) {
236
+ grid[reel][row] = happy_panda_config_1.Symbol.PANDA_PIECE_00 + reel * 3 + row;
237
+ }
238
+ else {
239
+ grid[reel][row] = baseSymbol;
240
+ }
241
+ symbolCounts.set(symbolIdx, (symbolCounts.get(symbolIdx) || 0) + 1);
242
+ }
243
+ }
244
+ return grid;
245
+ }
246
+ /**
247
+ * Create a grid filled with a single symbol (for full 3x3 wins).
248
+ * C++ line 1956-1957: Panda pieces are POSITION-SPECIFIC.
249
+ */
250
+ function createFilledGrid(symbol) {
251
+ // Panda pieces need position-specific symbols
252
+ if (symbol === happy_panda_config_1.Symbol.PANDA_PIECE_00) {
253
+ return [
254
+ [happy_panda_config_1.Symbol.PANDA_PIECE_00, happy_panda_config_1.Symbol.PANDA_PIECE_01, happy_panda_config_1.Symbol.PANDA_PIECE_02],
255
+ [happy_panda_config_1.Symbol.PANDA_PIECE_10, happy_panda_config_1.Symbol.PANDA_PIECE_11, happy_panda_config_1.Symbol.PANDA_PIECE_12],
256
+ [happy_panda_config_1.Symbol.PANDA_PIECE_20, happy_panda_config_1.Symbol.PANDA_PIECE_21, happy_panda_config_1.Symbol.PANDA_PIECE_22],
257
+ ];
258
+ }
259
+ return [
260
+ [symbol, symbol, symbol],
261
+ [symbol, symbol, symbol],
262
+ [symbol, symbol, symbol],
263
+ ];
264
+ }
265
+ /**
266
+ * Create a matrix grid with 7, 8, or 9 of the same symbol.
267
+ * C++ lines 103-118: When matrix < 9, other positions get a filler symbol.
268
+ * C++ line 1956-1957: Panda pieces are POSITION-SPECIFIC.
269
+ */
270
+ async function createMatrixGrid(matrixSymbol, matrixCount, config, gameDirection, rng, limitations) {
271
+ // Full 3x3
272
+ if (matrixCount === 9) {
273
+ return createFilledGrid(matrixSymbol);
274
+ }
275
+ // For 7 or 8, we need to fill some positions with a different symbol
276
+ // C++ uses idx=4 (or 3 if matrixSymbol is 4) as the filler
277
+ const matrixSymbolIdx = config.symbols.indexOf(matrixSymbol);
278
+ let fillerIdx = 4; // Fireworks Rockets in most configs
279
+ if (fillerIdx === matrixSymbolIdx) {
280
+ fillerIdx = 3; // City if matrixSymbol is Fireworks Rockets
281
+ }
282
+ const fillerSymbol = config.symbols[fillerIdx] || config.symbols[0];
283
+ // Create grid - fill with matrix symbol first (position-specific for panda)
284
+ const isPandaMatrix = matrixSymbol === happy_panda_config_1.Symbol.PANDA_PIECE_00;
285
+ const grid = isPandaMatrix
286
+ ? [
287
+ [happy_panda_config_1.Symbol.PANDA_PIECE_00, happy_panda_config_1.Symbol.PANDA_PIECE_01, happy_panda_config_1.Symbol.PANDA_PIECE_02],
288
+ [happy_panda_config_1.Symbol.PANDA_PIECE_10, happy_panda_config_1.Symbol.PANDA_PIECE_11, happy_panda_config_1.Symbol.PANDA_PIECE_12],
289
+ [happy_panda_config_1.Symbol.PANDA_PIECE_20, happy_panda_config_1.Symbol.PANDA_PIECE_21, happy_panda_config_1.Symbol.PANDA_PIECE_22],
290
+ ]
291
+ : [
292
+ [matrixSymbol, matrixSymbol, matrixSymbol],
293
+ [matrixSymbol, matrixSymbol, matrixSymbol],
294
+ [matrixSymbol, matrixSymbol, matrixSymbol],
295
+ ];
296
+ // Replace (9 - matrixCount) positions with filler
297
+ const positionsToReplace = 9 - matrixCount;
298
+ const positions = [0, 1, 2, 3, 4, 5, 6, 7, 8];
299
+ // Randomly select positions to replace
300
+ for (let i = 0; i < positionsToReplace; i++) {
301
+ const randIdx = await rng.random(positions.length);
302
+ const pos = positions.splice(randIdx, 1)[0];
303
+ const [reel, row] = positionToCoords(pos);
304
+ grid[reel][row] = fillerSymbol;
305
+ }
306
+ return grid;
307
+ }
308
+ /**
309
+ * Generate panda piece grid for Bamboo Triplets bonus (Dancing Panda picture).
310
+ * Each position gets the appropriate piece symbol.
311
+ */
312
+ function generatePandaPieceGrid() {
313
+ return [
314
+ [happy_panda_config_1.Symbol.PANDA_PIECE_00, happy_panda_config_1.Symbol.PANDA_PIECE_01, happy_panda_config_1.Symbol.PANDA_PIECE_02],
315
+ [happy_panda_config_1.Symbol.PANDA_PIECE_10, happy_panda_config_1.Symbol.PANDA_PIECE_11, happy_panda_config_1.Symbol.PANDA_PIECE_12],
316
+ [happy_panda_config_1.Symbol.PANDA_PIECE_20, happy_panda_config_1.Symbol.PANDA_PIECE_21, happy_panda_config_1.Symbol.PANDA_PIECE_22],
317
+ ];
318
+ }
319
+ /**
320
+ * Check if grid is a complete panda piece (Dancing Panda picture for jackpot).
321
+ */
322
+ function isCompletePandaPiece(grid) {
323
+ const pandaPieces = [
324
+ happy_panda_config_1.Symbol.PANDA_PIECE_00, happy_panda_config_1.Symbol.PANDA_PIECE_01, happy_panda_config_1.Symbol.PANDA_PIECE_02,
325
+ happy_panda_config_1.Symbol.PANDA_PIECE_10, happy_panda_config_1.Symbol.PANDA_PIECE_11, happy_panda_config_1.Symbol.PANDA_PIECE_12,
326
+ happy_panda_config_1.Symbol.PANDA_PIECE_20, happy_panda_config_1.Symbol.PANDA_PIECE_21, happy_panda_config_1.Symbol.PANDA_PIECE_22,
327
+ ];
328
+ let foundPieces = 0;
329
+ for (let reel = 0; reel < happy_panda_config_1.GRID.REELS; reel++) {
330
+ for (let row = 0; row < happy_panda_config_1.GRID.ROWS; row++) {
331
+ if (pandaPieces.includes(grid[reel][row])) {
332
+ foundPieces++;
333
+ }
334
+ }
335
+ }
336
+ return foundPieces === 9;
337
+ }
338
+ //# sourceMappingURL=spin-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spin-generator.js","sourceRoot":"","sources":["../../src/rng/spin-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Weighted Random Selection Utilities
3
+ *
4
+ * Helper functions for weighted random selection using the RNG provider.
5
+ */
6
+ import { RngProvider } from '../domain/types';
7
+ /**
8
+ * Select an index from an array of weights.
9
+ * @param weights Array of weights (must be positive integers)
10
+ * @param rng RNG provider
11
+ * @returns Selected index
12
+ */
13
+ export declare function selectWeightedIndex(weights: readonly number[], rng: RngProvider): Promise<number>;
14
+ /**
15
+ * Check if a weighted random selection succeeds.
16
+ * @param yesWeight Weight for "yes" outcome
17
+ * @param noWeight Weight for "no" outcome
18
+ * @param rng RNG provider
19
+ * @returns true if "yes" was selected
20
+ */
21
+ export declare function weightedBoolean(yesWeight: number, noWeight: number, rng: RngProvider): Promise<boolean>;
22
+ /**
23
+ * Select multiple weighted indices (with replacement).
24
+ * @param weights Array of weights
25
+ * @param count Number of selections
26
+ * @param rng RNG provider
27
+ * @returns Array of selected indices
28
+ */
29
+ export declare function selectMultipleWeighted(weights: readonly number[], count: number, rng: RngProvider): Promise<number[]>;
30
+ //# sourceMappingURL=weighted-random.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weighted-random.d.ts","sourceRoot":"","sources":["../../src/rng/weighted-random.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,EAAE,CAAC,CAMnB"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * Weighted Random Selection Utilities
4
+ *
5
+ * Helper functions for weighted random selection using the RNG provider.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.selectWeightedIndex = selectWeightedIndex;
9
+ exports.weightedBoolean = weightedBoolean;
10
+ exports.selectMultipleWeighted = selectMultipleWeighted;
11
+ /**
12
+ * Select an index from an array of weights.
13
+ * @param weights Array of weights (must be positive integers)
14
+ * @param rng RNG provider
15
+ * @returns Selected index
16
+ */
17
+ async function selectWeightedIndex(weights, rng) {
18
+ const totalWeight = weights.reduce((sum, w) => sum + w, 0);
19
+ if (totalWeight === 0) {
20
+ throw new Error('All weights are zero');
21
+ }
22
+ const roll = await rng.random(totalWeight);
23
+ let cumulative = 0;
24
+ for (let i = 0; i < weights.length; i++) {
25
+ cumulative += weights[i];
26
+ if (roll < cumulative) {
27
+ return i;
28
+ }
29
+ }
30
+ // Fallback (shouldn't happen with valid weights)
31
+ return weights.length - 1;
32
+ }
33
+ /**
34
+ * Check if a weighted random selection succeeds.
35
+ * @param yesWeight Weight for "yes" outcome
36
+ * @param noWeight Weight for "no" outcome
37
+ * @param rng RNG provider
38
+ * @returns true if "yes" was selected
39
+ */
40
+ async function weightedBoolean(yesWeight, noWeight, rng) {
41
+ const index = await selectWeightedIndex([yesWeight, noWeight], rng);
42
+ return index === 0;
43
+ }
44
+ /**
45
+ * Select multiple weighted indices (with replacement).
46
+ * @param weights Array of weights
47
+ * @param count Number of selections
48
+ * @param rng RNG provider
49
+ * @returns Array of selected indices
50
+ */
51
+ async function selectMultipleWeighted(weights, count, rng) {
52
+ const results = [];
53
+ for (let i = 0; i < count; i++) {
54
+ results.push(await selectWeightedIndex(weights, rng));
55
+ }
56
+ return results;
57
+ }
58
+ //# sourceMappingURL=weighted-random.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weighted-random.js","sourceRoot":"","sources":["../../src/rng/weighted-random.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAUH,kDAqBC;AASD,0CAOC;AASD,wDAUC;AA9DD;;;;;GAKG;AACI,KAAK,UAAU,mBAAmB,CACvC,OAA0B,EAC1B,GAAgB;IAEhB,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,GAAG,UAAU,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,QAAgB,EAChB,GAAgB;IAEhB,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;IACpE,OAAO,KAAK,KAAK,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,sBAAsB,CAC1C,OAA0B,EAC1B,KAAa,EACb,GAAgB;IAEhB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC,MAAM,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@omnitronix/happy-panda-game-engine",
3
+ "version": "0.5.1",
4
+ "publishConfig": {
5
+ "access": "restricted"
6
+ },
7
+ "description": "Happy Panda Game Engine",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsc && copyfiles -u 1 'src/**/*.csv' dist/",
12
+ "dev": "tsc --watch",
13
+ "clean": "rm -rf dist",
14
+ "lint": "eslint src/**/*.ts",
15
+ "lint:fix": "eslint src/**/*.ts --fix",
16
+ "test": "cross-env RNG_CLIENT_MODE=local npm run build && jest",
17
+ "test:watch": "jest --watch",
18
+ "test:coverage": "jest --coverage",
19
+ "prepublishOnly": "npm run clean && npm run build"
20
+ },
21
+ "author": "Omnitronix",
22
+ "license": "UNLICENSED",
23
+ "dependencies": {
24
+ "@omnitronix/game-engine-contract": "^1.4.0",
25
+ "@omnitronix/game-engine-sdk": "^2.5.0",
26
+ "@omnitronix/rng-client-core": "^1.0.9",
27
+ "class-transformer": "^0.5.1",
28
+ "class-validator": "^0.14.1",
29
+ "reflect-metadata": "^0.2.2",
30
+ "socket.io-client": "^4.8.3",
31
+ "uuid": "^9.0.0",
32
+ "winston": "^3.17.0"
33
+ },
34
+ "devDependencies": {
35
+ "@aws-sdk/client-secrets-manager": "^3.990.0",
36
+ "@nestjs/common": "^11.1.13",
37
+ "@nestjs/config": "^4.0.3",
38
+ "@nestjs/core": "^11.1.13",
39
+ "@nestjs/platform-express": "^11.1.13",
40
+ "@nestjs/swagger": "^11.2.6",
41
+ "@types/jest": "^29.5.0",
42
+ "@types/node": "^20.0.0",
43
+ "@types/uuid": "^9.0.0",
44
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
45
+ "@typescript-eslint/parser": "^6.0.0",
46
+ "copyfiles": "^2.4.1",
47
+ "cross-env": "^10.1.0",
48
+ "eslint": "^8.0.0",
49
+ "jest": "^29.5.0",
50
+ "nodemon": "^3.0.0",
51
+ "rxjs": "^7.8.2",
52
+ "ts-jest": "^29.1.0",
53
+ "typescript": "^5.0.0"
54
+ },
55
+ "files": [
56
+ "dist/**/*",
57
+ "README.md"
58
+ ]
59
+ }