@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,7 @@
1
+ /**
2
+ * Bonus Sequence Integration Tests
3
+ *
4
+ * Tests for complete bonus flows from trigger to completion.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=bonus-sequence.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bonus-sequence.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bonus-sequence.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,381 @@
1
+ "use strict";
2
+ /**
3
+ * Bonus Sequence Integration Tests
4
+ *
5
+ * Tests for complete bonus flows from trigger to completion.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const happy_panda_engine_1 = require("../engine/happy-panda-engine");
9
+ const happy_panda_config_1 = require("../config/happy-panda.config");
10
+ const types_1 = require("../domain/types");
11
+ // Deterministic mock RNG
12
+ function createMockRng(values = []) {
13
+ let index = 0;
14
+ return {
15
+ async random(max) {
16
+ const val = values[index % Math.max(values.length, 1)] || 0;
17
+ index++;
18
+ return val % max;
19
+ },
20
+ async randomBatch(count, max) {
21
+ const results = [];
22
+ for (let i = 0; i < count; i++) {
23
+ const val = values[index % Math.max(values.length, 1)] || 0;
24
+ index++;
25
+ results.push(val % max);
26
+ }
27
+ return results;
28
+ },
29
+ };
30
+ }
31
+ describe('Bonus Sequences', () => {
32
+ describe('Bonus Spin Counting', () => {
33
+ it('should track spins remaining during bonus', async () => {
34
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
35
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
36
+ // Create initial session
37
+ const initRequest = {
38
+ command: types_1.CommandType.INIT_SESSION_STATE,
39
+ sessionId: 'bonus-test',
40
+ gameDirection: types_1.GameDirection.SINGLE,
41
+ betStake: 1,
42
+ };
43
+ let { state } = await engine.handleCommand(initRequest);
44
+ // Manually set up a bonus state to test spin counting
45
+ state.privateState.internalPendingBonuses.bambooTwins = 1;
46
+ state.publicState.hasPendingBonus = true;
47
+ const spinRequest = {
48
+ command: types_1.CommandType.SPIN,
49
+ sessionId: 'bonus-test',
50
+ };
51
+ // First spin should start the bonus
52
+ const result1 = await engine.handleCommand(spinRequest, state);
53
+ state = result1.state;
54
+ // After starting, spinsRemaining should be set (or decremented)
55
+ expect(state.publicState.currentSpinType).toBeDefined();
56
+ });
57
+ it('should decrement spins remaining each bonus spin', async () => {
58
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
59
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
60
+ const initRequest = {
61
+ command: types_1.CommandType.INIT_SESSION_STATE,
62
+ sessionId: 'decrement-test',
63
+ gameDirection: types_1.GameDirection.SINGLE,
64
+ betStake: 1,
65
+ };
66
+ let { state } = await engine.handleCommand(initRequest);
67
+ // Set up bell bonus (5 spins)
68
+ state.privateState.internalPendingBonuses.camera = 1;
69
+ state.publicState.hasPendingBonus = true;
70
+ const spinRequest = {
71
+ command: types_1.CommandType.SPIN,
72
+ sessionId: 'decrement-test',
73
+ };
74
+ // Track spin types through the sequence
75
+ const spinTypes = [];
76
+ for (let i = 0; i < 7; i++) {
77
+ const result = await engine.handleCommand(spinRequest, state);
78
+ state = result.state;
79
+ spinTypes.push(state.publicState.currentSpinType);
80
+ }
81
+ // Should see bonus spin type then return to paid spin
82
+ expect(spinTypes).toContain(happy_panda_config_1.SpinType.BASE_GAME_SPIN);
83
+ });
84
+ });
85
+ describe('Bonus Priority Order', () => {
86
+ it('should execute bonuses in priority order: Jackpot > Cherry > Bell > Bar1 > Respin', async () => {
87
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
88
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
89
+ const initRequest = {
90
+ command: types_1.CommandType.INIT_SESSION_STATE,
91
+ sessionId: 'priority-test',
92
+ gameDirection: types_1.GameDirection.SINGLE,
93
+ betStake: 1,
94
+ };
95
+ let { state } = await engine.handleCommand(initRequest);
96
+ // Queue multiple bonuses
97
+ state.privateState.internalPendingBonuses.bambooTwins = 1;
98
+ state.privateState.internalPendingBonuses.camera = 1;
99
+ state.publicState.hasPendingBonus = true;
100
+ const spinRequest = {
101
+ command: types_1.CommandType.SPIN,
102
+ sessionId: 'priority-test',
103
+ };
104
+ // First bonus should be cherry (higher priority than bell)
105
+ const result1 = await engine.handleCommand(spinRequest, state);
106
+ state = result1.state;
107
+ // Cherry bonus should start before bell
108
+ if (state.publicState.currentSpinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN) {
109
+ expect(state.publicState.currentSpinType).toBe(happy_panda_config_1.SpinType.BONUS_BAMBOO_TWINS);
110
+ }
111
+ });
112
+ });
113
+ describe('Bonus Completion', () => {
114
+ it('should set isBonusComplete when sequence ends', async () => {
115
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
116
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
117
+ const initRequest = {
118
+ command: types_1.CommandType.INIT_SESSION_STATE,
119
+ sessionId: 'complete-test',
120
+ gameDirection: types_1.GameDirection.SINGLE,
121
+ betStake: 1,
122
+ };
123
+ let { state } = await engine.handleCommand(initRequest);
124
+ // Add a single respin (1 spin)
125
+ state.privateState.internalPendingBonuses.centerBamboo = 1;
126
+ state.publicState.hasPendingBonus = true;
127
+ const spinRequest = {
128
+ command: types_1.CommandType.SPIN,
129
+ sessionId: 'complete-test',
130
+ };
131
+ // Execute spins until we see completion
132
+ let foundComplete = false;
133
+ for (let i = 0; i < 5; i++) {
134
+ const result = await engine.handleCommand(spinRequest, state);
135
+ const response = result.response;
136
+ state = result.state;
137
+ if (response.isBonusComplete) {
138
+ foundComplete = true;
139
+ break;
140
+ }
141
+ }
142
+ // Eventually should complete
143
+ expect(foundComplete || state.publicState.currentSpinType === happy_panda_config_1.SpinType.BASE_GAME_SPIN).toBe(true);
144
+ });
145
+ it('should return to BASE_GAME_SPIN after all bonuses complete', async () => {
146
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
147
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
148
+ const initRequest = {
149
+ command: types_1.CommandType.INIT_SESSION_STATE,
150
+ sessionId: 'return-test',
151
+ gameDirection: types_1.GameDirection.SINGLE,
152
+ betStake: 1,
153
+ };
154
+ let { state } = await engine.handleCommand(initRequest);
155
+ // Add a respin
156
+ state.privateState.internalPendingBonuses.centerBamboo = 1;
157
+ state.publicState.hasPendingBonus = true;
158
+ const spinRequest = {
159
+ command: types_1.CommandType.SPIN,
160
+ sessionId: 'return-test',
161
+ };
162
+ // Execute enough spins to complete the sequence
163
+ for (let i = 0; i < 10; i++) {
164
+ const result = await engine.handleCommand(spinRequest, state);
165
+ state = result.state;
166
+ }
167
+ // State should have a valid spin type (could be in any state depending on RNG)
168
+ expect(state.publicState.currentSpinType).toBeDefined();
169
+ expect(Object.values(happy_panda_config_1.SpinType)).toContain(state.publicState.currentSpinType);
170
+ });
171
+ });
172
+ describe('Accumulated Bonus Wins', () => {
173
+ it('should accumulate wins during bonus sequence', async () => {
174
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
175
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
176
+ const initRequest = {
177
+ command: types_1.CommandType.INIT_SESSION_STATE,
178
+ sessionId: 'accumulate-test',
179
+ gameDirection: types_1.GameDirection.SINGLE,
180
+ betStake: 1,
181
+ };
182
+ let { state } = await engine.handleCommand(initRequest);
183
+ // Initial accumulated should be 0
184
+ expect(state.privateState.accumulatedBonusWins).toBe(0);
185
+ });
186
+ it('should reset accumulated wins when sequence completes', async () => {
187
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
188
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
189
+ const initRequest = {
190
+ command: types_1.CommandType.INIT_SESSION_STATE,
191
+ sessionId: 'reset-test',
192
+ gameDirection: types_1.GameDirection.SINGLE,
193
+ betStake: 1,
194
+ };
195
+ let { state } = await engine.handleCommand(initRequest);
196
+ // After bonus completes and returns to paid spin, accumulated should be 0
197
+ expect(state.privateState.accumulatedBonusWins).toBe(0);
198
+ });
199
+ });
200
+ describe('Bonus Spin Counts', () => {
201
+ it('should have correct spin count for jackpot bonus', () => {
202
+ expect(happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.SPINS).toBe(3); // Per C++ model: 1 occurrence x 3 spins
203
+ });
204
+ it('should have correct spin count for cherry bonus', () => {
205
+ expect(happy_panda_config_1.BONUS_BAMBOO_TWINS.SPINS).toBe(1); // Per C++ model
206
+ });
207
+ it('should have correct spin count for bell bonus', () => {
208
+ expect(happy_panda_config_1.BONUS_CAMERA.SPINS).toBe(5);
209
+ });
210
+ it('should have correct spin count for bar1 bonus', () => {
211
+ expect(happy_panda_config_1.BONUS_FIREWORKS.SPINS).toBe(7);
212
+ });
213
+ });
214
+ describe('Center Cherry for Respin', () => {
215
+ it('should track center cherry symbol during respin', async () => {
216
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
217
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
218
+ const initRequest = {
219
+ command: types_1.CommandType.INIT_SESSION_STATE,
220
+ sessionId: 'cherry-center-test',
221
+ gameDirection: types_1.GameDirection.SINGLE,
222
+ betStake: 1,
223
+ };
224
+ const { state } = await engine.handleCommand(initRequest);
225
+ // Initially no center cherry
226
+ expect(state.privateState.centerBambooSymbol).toBeNull();
227
+ });
228
+ });
229
+ describe('Multiple Bonus Queue', () => {
230
+ it('should handle multiple pending bonuses', async () => {
231
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
232
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
233
+ const initRequest = {
234
+ command: types_1.CommandType.INIT_SESSION_STATE,
235
+ sessionId: 'multi-bonus-test',
236
+ gameDirection: types_1.GameDirection.SINGLE,
237
+ betStake: 1,
238
+ };
239
+ let { state } = await engine.handleCommand(initRequest);
240
+ // Queue multiple bonuses
241
+ state.privateState.internalPendingBonuses.bambooTriplets = 2;
242
+ state.privateState.internalPendingBonuses.bambooTwins = 1;
243
+ state.publicState.hasPendingBonus = true;
244
+ // Total pending should be 3
245
+ const totalPending = state.privateState.internalPendingBonuses.bambooTriplets +
246
+ state.privateState.internalPendingBonuses.bambooTwins +
247
+ state.privateState.internalPendingBonuses.camera +
248
+ state.privateState.internalPendingBonuses.fireworks +
249
+ state.privateState.internalPendingBonuses.centerBamboo;
250
+ expect(totalPending).toBe(3);
251
+ });
252
+ it('should decrement pending count when bonus consumed', async () => {
253
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
254
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
255
+ const initRequest = {
256
+ command: types_1.CommandType.INIT_SESSION_STATE,
257
+ sessionId: 'consume-test',
258
+ gameDirection: types_1.GameDirection.SINGLE,
259
+ betStake: 1,
260
+ };
261
+ let { state } = await engine.handleCommand(initRequest);
262
+ // Add pending bonus
263
+ const initialPending = 2;
264
+ state.privateState.internalPendingBonuses.centerBamboo = initialPending;
265
+ state.publicState.hasPendingBonus = true;
266
+ const spinRequest = {
267
+ command: types_1.CommandType.SPIN,
268
+ sessionId: 'consume-test',
269
+ };
270
+ // Execute spin to consume bonus
271
+ const result = await engine.handleCommand(spinRequest, state);
272
+ state = result.state;
273
+ // Pending should be decremented (respins are all consumed at once)
274
+ expect(state.privateState.internalPendingBonuses.centerBamboo).toBeLessThanOrEqual(initialPending);
275
+ });
276
+ });
277
+ describe('Bonus Win Tracking', () => {
278
+ it('should return jackpotWon in response', async () => {
279
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
280
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
281
+ const initRequest = {
282
+ command: types_1.CommandType.INIT_SESSION_STATE,
283
+ sessionId: 'jackpot-win-test',
284
+ gameDirection: types_1.GameDirection.SINGLE,
285
+ betStake: 1,
286
+ };
287
+ let { state } = await engine.handleCommand(initRequest);
288
+ const spinRequest = {
289
+ command: types_1.CommandType.SPIN,
290
+ sessionId: 'jackpot-win-test',
291
+ };
292
+ const result = await engine.handleCommand(spinRequest, state);
293
+ const response = result.response;
294
+ expect(typeof response.jackpotWon).toBe('number');
295
+ expect(response.jackpotWon).toBeGreaterThanOrEqual(0);
296
+ });
297
+ it('should return poolJackpotWon in response', async () => {
298
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
299
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
300
+ const initRequest = {
301
+ command: types_1.CommandType.INIT_SESSION_STATE,
302
+ sessionId: 'pool-win-test',
303
+ gameDirection: types_1.GameDirection.SINGLE,
304
+ betStake: 1,
305
+ };
306
+ let { state } = await engine.handleCommand(initRequest);
307
+ const spinRequest = {
308
+ command: types_1.CommandType.SPIN,
309
+ sessionId: 'pool-win-test',
310
+ };
311
+ const result = await engine.handleCommand(spinRequest, state);
312
+ const response = result.response;
313
+ expect(typeof response.poolJackpotWon).toBe('number');
314
+ expect(response.poolJackpotWon).toBeGreaterThanOrEqual(0);
315
+ });
316
+ it('should return triggeredBonus in response when bonus triggers', async () => {
317
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8]);
318
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
319
+ const initRequest = {
320
+ command: types_1.CommandType.INIT_SESSION_STATE,
321
+ sessionId: 'trigger-test',
322
+ gameDirection: types_1.GameDirection.SINGLE,
323
+ betStake: 1,
324
+ };
325
+ let { state } = await engine.handleCommand(initRequest);
326
+ const spinRequest = {
327
+ command: types_1.CommandType.SPIN,
328
+ sessionId: 'trigger-test',
329
+ };
330
+ const result = await engine.handleCommand(spinRequest, state);
331
+ const response = result.response;
332
+ // triggeredBonus should be undefined or a valid BonusTrigger object
333
+ if (response.triggeredBonus) {
334
+ expect(typeof response.triggeredBonus.bonusId).toBe('string');
335
+ expect(typeof response.triggeredBonus.bonusType).toBe('string');
336
+ expect(typeof response.triggeredBonus.betAmount).toBe('number');
337
+ }
338
+ });
339
+ it('should reset bambooTriplets counter to COUNTER_INIT after bonus completes (#16)', async () => {
340
+ const mockRng = createMockRng([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
341
+ const engine = new happy_panda_engine_1.HappyPandaEngine(mockRng);
342
+ const initRequest = {
343
+ command: types_1.CommandType.INIT_SESSION_STATE,
344
+ sessionId: 'counter-reset-test',
345
+ gameDirection: types_1.GameDirection.SINGLE,
346
+ betStake: 1,
347
+ };
348
+ let { state } = await engine.handleCommand(initRequest);
349
+ // Verify counter starts at COUNTER_INIT (1)
350
+ expect(state.privateState.counters.bambooTriplets).toBe(happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.COUNTER_INIT);
351
+ // Manually set up a pending BONUS_BAMBOO_TRIPLETS (1 spin queued)
352
+ state.privateState.internalPendingBonuses.bambooTriplets = 1;
353
+ state.privateState.counters.bambooTriplets = 0; // Counter was set to 0 when bonus triggered
354
+ state.publicState.hasPendingBonus = true;
355
+ state.publicState.additionalData.counters.bambooTriplets = 0;
356
+ const spinRequest = {
357
+ command: types_1.CommandType.SPIN,
358
+ sessionId: 'counter-reset-test',
359
+ };
360
+ // First spin starts the bonus
361
+ let result = await engine.handleCommand(spinRequest, state);
362
+ state = result.state;
363
+ // Verify bonus started
364
+ expect(state.publicState.currentSpinType).toBe(happy_panda_config_1.SpinType.BONUS_BAMBOO_TRIPLETS);
365
+ // Play through all bonus spins until complete
366
+ let response = result.response;
367
+ while (!response.isBonusComplete && state.publicState.currentSpinType !== happy_panda_config_1.SpinType.BASE_GAME_SPIN) {
368
+ result = await engine.handleCommand(spinRequest, state);
369
+ state = result.state;
370
+ response = result.response;
371
+ }
372
+ // Verify bonus completed
373
+ expect(response.isBonusComplete).toBe(true);
374
+ expect(response.nextSpinType).toBe(happy_panda_config_1.SpinType.BASE_GAME_SPIN);
375
+ // Verify counter was reset to COUNTER_INIT (1)
376
+ expect(result.state.privateState.counters.bambooTriplets).toBe(happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.COUNTER_INIT);
377
+ expect(result.state.publicState.additionalData.counters.bambooTriplets).toBe(happy_panda_config_1.BONUS_BAMBOO_TRIPLETS.COUNTER_INIT);
378
+ });
379
+ });
380
+ });
381
+ //# sourceMappingURL=bonus-sequence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bonus-sequence.test.js","sourceRoot":"","sources":["../../src/__tests__/bonus-sequence.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Cherry Frequency Analysis Test
3
+ *
4
+ * Counts how many BASE_GAME_SPIN grids have 3x cherry lines to verify
5
+ * grid generation produces expected cherry frequency.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=cherry-frequency.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cherry-frequency.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cherry-frequency.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * Cherry Frequency Analysis Test
4
+ *
5
+ * Counts how many BASE_GAME_SPIN grids have 3x cherry lines to verify
6
+ * grid generation produces expected cherry frequency.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const happy_panda_config_1 = require("../config/happy-panda.config");
10
+ const spin_generator_1 = require("../rng/spin-generator");
11
+ const types_1 = require("../domain/types");
12
+ function createSimulationRng(seed = 12345) {
13
+ let state = seed;
14
+ function nextInt() {
15
+ state = (state * 1664525 + 1013904223) >>> 0;
16
+ return state;
17
+ }
18
+ return {
19
+ async random(max) {
20
+ return nextInt() % max;
21
+ },
22
+ async randomBatch(count, max) {
23
+ const results = [];
24
+ for (let i = 0; i < count; i++) {
25
+ results.push(nextInt() % max);
26
+ }
27
+ return results;
28
+ },
29
+ };
30
+ }
31
+ function countCherrySymbols(grid) {
32
+ let count = 0;
33
+ for (let reel = 0; reel < 3; reel++) {
34
+ for (let row = 0; row < 3; row++) {
35
+ if (happy_panda_config_1.BAMBOO_SYMBOLS.includes(grid[reel][row])) {
36
+ count++;
37
+ }
38
+ }
39
+ }
40
+ return count;
41
+ }
42
+ function count3xCherryLines(grid) {
43
+ let count = 0;
44
+ // Only check first 8 lines
45
+ for (let lineIndex = 0; lineIndex < 8; lineIndex++) {
46
+ const lineShape = happy_panda_config_1.LINE_SHAPES[lineIndex];
47
+ const symbols = lineShape.map(([reel, row]) => grid[reel][row]);
48
+ // Check if all 3 symbols are cherries
49
+ const allCherry = symbols.every(s => happy_panda_config_1.BAMBOO_SYMBOLS.includes(s));
50
+ if (allCherry) {
51
+ count++;
52
+ }
53
+ }
54
+ return count;
55
+ }
56
+ describe('Cherry Frequency Analysis', () => {
57
+ it('should count cherry appearances in BASE_GAME_SPIN grids', async () => {
58
+ const rng = createSimulationRng(12345);
59
+ const grids = 100000;
60
+ let totalCherries = 0;
61
+ let gridsWithCherry = 0;
62
+ let gridsWithThreeCherry = 0;
63
+ let gridsWith3xCherryLine = 0;
64
+ let total3xCherryLines = 0;
65
+ const cherryLineDistribution = {};
66
+ // Debug: Print first few grids to verify generation
67
+ console.log('\nFirst 5 grids:');
68
+ for (let debug = 0; debug < 5; debug++) {
69
+ const debugGrid = await (0, spin_generator_1.generateGrid)(happy_panda_config_1.SpinType.BASE_GAME_SPIN, types_1.GameDirection.SINGLE, rng);
70
+ console.log(`Grid ${debug}: ${JSON.stringify(debugGrid)}`);
71
+ console.log(` Cherry count: ${countCherrySymbols(debugGrid)}, 3x lines: ${count3xCherryLines(debugGrid)}`);
72
+ }
73
+ console.log('');
74
+ // Reset RNG for actual test
75
+ const rng2 = createSimulationRng(12345);
76
+ for (let i = 0; i < grids; i++) {
77
+ const grid = await (0, spin_generator_1.generateGrid)(happy_panda_config_1.SpinType.BASE_GAME_SPIN, types_1.GameDirection.SINGLE, rng2);
78
+ const cherryCount = countCherrySymbols(grid);
79
+ totalCherries += cherryCount;
80
+ if (cherryCount > 0)
81
+ gridsWithCherry++;
82
+ if (cherryCount >= 3)
83
+ gridsWithThreeCherry++;
84
+ const cherryLines = count3xCherryLines(grid);
85
+ total3xCherryLines += cherryLines;
86
+ if (cherryLines > 0) {
87
+ gridsWith3xCherryLine++;
88
+ cherryLineDistribution[cherryLines] = (cherryLineDistribution[cherryLines] || 0) + 1;
89
+ }
90
+ if (i > 0 && i % 25000 === 0) {
91
+ console.log(` ${i / 1000}k grids: ${gridsWith3xCherryLine} with 3x cherry lines (${(gridsWith3xCherryLine / i * 100).toFixed(3)}%)`);
92
+ }
93
+ }
94
+ console.log('\n========================================');
95
+ console.log('CHERRY FREQUENCY ANALYSIS');
96
+ console.log('========================================\n');
97
+ console.log(`Total grids generated: ${grids.toLocaleString()}`);
98
+ console.log(`Total cherries placed: ${totalCherries.toLocaleString()}`);
99
+ console.log(`Average cherries per grid: ${(totalCherries / grids).toFixed(3)}`);
100
+ console.log();
101
+ console.log(`Grids with at least 1 cherry: ${gridsWithCherry.toLocaleString()} (${(gridsWithCherry / grids * 100).toFixed(2)}%)`);
102
+ console.log(`Grids with at least 3 cherries: ${gridsWithThreeCherry.toLocaleString()} (${(gridsWithThreeCherry / grids * 100).toFixed(2)}%)`);
103
+ console.log();
104
+ console.log(`Grids with 3x cherry line: ${gridsWith3xCherryLine.toLocaleString()} (${(gridsWith3xCherryLine / grids * 100).toFixed(4)}%)`);
105
+ console.log(`Total 3x cherry lines: ${total3xCherryLines.toLocaleString()}`);
106
+ console.log(`Average 3x cherry lines per trigger: ${gridsWith3xCherryLine > 0 ? (total3xCherryLines / gridsWith3xCherryLine).toFixed(2) : 'N/A'}`);
107
+ console.log();
108
+ console.log('Distribution of 3x cherry lines per grid:');
109
+ for (const [count, freq] of Object.entries(cherryLineDistribution).sort((a, b) => Number(a[0]) - Number(b[0]))) {
110
+ console.log(` ${count} line(s): ${freq} grids`);
111
+ }
112
+ console.log();
113
+ // C++ reference: ~2,490 jackpot triggers per 1M spins
114
+ // But that's bonus SPINS, not trigger events
115
+ // If avg 2.66 lines per trigger, then ~936 trigger events per 1M
116
+ // So expected trigger rate: 0.0936% = 936/1,000,000
117
+ const expectedRate = 0.00094; // ~0.094% trigger rate
118
+ const actualRate = gridsWith3xCherryLine / grids;
119
+ const ratio = actualRate / expectedRate;
120
+ console.log('========================================');
121
+ console.log(`Expected trigger rate (from C++ ~936/1M): ${(expectedRate * 100).toFixed(4)}%`);
122
+ console.log(`Actual trigger rate: ${(actualRate * 100).toFixed(4)}%`);
123
+ console.log(`Ratio (actual/expected): ${ratio.toFixed(2)}x`);
124
+ console.log('========================================\n');
125
+ expect(true).toBe(true);
126
+ }, 120000);
127
+ });
128
+ //# sourceMappingURL=cherry-frequency.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cherry-frequency.test.js","sourceRoot":"","sources":["../../src/__tests__/cherry-frequency.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Comprehensive Test Suite for Happy Panda Game Engine
3
+ *
4
+ * Coverage:
5
+ * - Session initialization
6
+ * - Base game spins
7
+ * - All bonus types (BONUS_BAMBOO_TRIPLETS, BONUS_BAMBOO_TWINS, BONUS_CAMERA, BONUS_FIREWORKS, RESPIN_CENTER_BAMBOO)
8
+ * - Debug triggers
9
+ * - Reality check flow
10
+ * - Connection loss/resume
11
+ * - Error handling
12
+ *
13
+ * Note: RTP and C++ parity tests are in separate files
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=comprehensive.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comprehensive.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/comprehensive.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}