@omnitronix/happy-panda-game-engine 0.0.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 (71) hide show
  1. package/README.md +212 -0
  2. package/dist/__tests__/bonus-sequence.test.js +337 -0
  3. package/dist/__tests__/bonus-sequence.test.js.map +1 -0
  4. package/dist/__tests__/cherry-frequency.test.js +128 -0
  5. package/dist/__tests__/cherry-frequency.test.js.map +1 -0
  6. package/dist/__tests__/counter-manager.test.js +316 -0
  7. package/dist/__tests__/counter-manager.test.js.map +1 -0
  8. package/dist/__tests__/cpp-parity.test.js +368 -0
  9. package/dist/__tests__/cpp-parity.test.js.map +1 -0
  10. package/dist/__tests__/fixtures/cpp-parity-vectors.json +438 -0
  11. package/dist/__tests__/happy-panda-engine.test.js +367 -0
  12. package/dist/__tests__/happy-panda-engine.test.js.map +1 -0
  13. package/dist/__tests__/jackpot-manager.test.js +313 -0
  14. package/dist/__tests__/jackpot-manager.test.js.map +1 -0
  15. package/dist/__tests__/jackpot-trigger-trace.test.js +146 -0
  16. package/dist/__tests__/jackpot-trigger-trace.test.js.map +1 -0
  17. package/dist/__tests__/rtp-1million.test.js +156 -0
  18. package/dist/__tests__/rtp-1million.test.js.map +1 -0
  19. package/dist/__tests__/rtp-analysis.test.js +138 -0
  20. package/dist/__tests__/rtp-analysis.test.js.map +1 -0
  21. package/dist/__tests__/rtp-diagnostic.test.js +126 -0
  22. package/dist/__tests__/rtp-diagnostic.test.js.map +1 -0
  23. package/dist/__tests__/rtp-simulation.test.js +409 -0
  24. package/dist/__tests__/rtp-simulation.test.js.map +1 -0
  25. package/dist/__tests__/special-wins.test.js +179 -0
  26. package/dist/__tests__/special-wins.test.js.map +1 -0
  27. package/dist/__tests__/spin-generator.test.js +250 -0
  28. package/dist/__tests__/spin-generator.test.js.map +1 -0
  29. package/dist/__tests__/spin-handler.test.js +210 -0
  30. package/dist/__tests__/spin-handler.test.js.map +1 -0
  31. package/dist/__tests__/symbol-distribution.test.js +119 -0
  32. package/dist/__tests__/symbol-distribution.test.js.map +1 -0
  33. package/dist/__tests__/weighted-random.test.js +165 -0
  34. package/dist/__tests__/weighted-random.test.js.map +1 -0
  35. package/dist/__tests__/win-evaluator.test.js +254 -0
  36. package/dist/__tests__/win-evaluator.test.js.map +1 -0
  37. package/dist/config/happy-panda.config.js +714 -0
  38. package/dist/config/happy-panda.config.js.map +1 -0
  39. package/dist/config/index.js +21 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/domain/index.js +21 -0
  42. package/dist/domain/index.js.map +1 -0
  43. package/dist/domain/types.js +28 -0
  44. package/dist/domain/types.js.map +1 -0
  45. package/dist/engine/happy-panda-engine.js +197 -0
  46. package/dist/engine/happy-panda-engine.js.map +1 -0
  47. package/dist/engine/index.js +21 -0
  48. package/dist/engine/index.js.map +1 -0
  49. package/dist/index.js +34 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/logic/handlers/index.js +21 -0
  52. package/dist/logic/handlers/index.js.map +1 -0
  53. package/dist/logic/handlers/spin-handler.js +256 -0
  54. package/dist/logic/handlers/spin-handler.js.map +1 -0
  55. package/dist/logic/index.js +22 -0
  56. package/dist/logic/index.js.map +1 -0
  57. package/dist/logic/services/counter-manager.js +265 -0
  58. package/dist/logic/services/counter-manager.js.map +1 -0
  59. package/dist/logic/services/index.js +23 -0
  60. package/dist/logic/services/index.js.map +1 -0
  61. package/dist/logic/services/jackpot-manager.js +142 -0
  62. package/dist/logic/services/jackpot-manager.js.map +1 -0
  63. package/dist/logic/services/win-evaluator.js +470 -0
  64. package/dist/logic/services/win-evaluator.js.map +1 -0
  65. package/dist/rng/index.js +22 -0
  66. package/dist/rng/index.js.map +1 -0
  67. package/dist/rng/spin-generator.js +341 -0
  68. package/dist/rng/spin-generator.js.map +1 -0
  69. package/dist/rng/weighted-random.js +58 -0
  70. package/dist/rng/weighted-random.js.map +1 -0
  71. package/package.json +49 -0
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ /**
3
+ * High-Volume RTP Simulation (100k - 1M Spins)
4
+ *
5
+ * This test validates RTP accuracy with statistical significance.
6
+ * With 1M spins, we expect RTP variance of ±0.5% or less.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const happy_panda_engine_1 = require("../engine/happy-panda-engine");
10
+ const happy_panda_config_1 = require("../config/happy-panda.config");
11
+ const types_1 = require("../domain/types");
12
+ // =============================================================================
13
+ // RNG IMPLEMENTATION
14
+ // =============================================================================
15
+ function createSimulationRng(seed = 12345) {
16
+ let state = seed;
17
+ function nextInt() {
18
+ state = (state * 1664525 + 1013904223) >>> 0;
19
+ return state;
20
+ }
21
+ return {
22
+ async random(max) {
23
+ return nextInt() % max;
24
+ },
25
+ async randomBatch(count, max) {
26
+ const results = [];
27
+ for (let i = 0; i < count; i++) {
28
+ results.push(nextInt() % max);
29
+ }
30
+ return results;
31
+ },
32
+ };
33
+ }
34
+ async function runHighVolumeSimulation(spinCount, gameDirection, seed = 12345) {
35
+ const rng = createSimulationRng(seed);
36
+ const engine = new happy_panda_engine_1.HappyPandaEngine(rng);
37
+ const initRequest = {
38
+ command: types_1.CommandType.INIT_SESSION_STATE,
39
+ sessionId: `highvol-${Date.now()}`,
40
+ gameDirection,
41
+ betStake: 1,
42
+ };
43
+ let { state } = await engine.handleCommand(initRequest);
44
+ const betGame = state.publicState.betGame;
45
+ const result = {
46
+ totalWagered: 0,
47
+ totalWon: 0,
48
+ rtp: 0,
49
+ paidSpins: 0,
50
+ bonusSpins: 0,
51
+ lineWinTotal: 0,
52
+ scatterWinTotal: 0,
53
+ wallWinTotal: 0,
54
+ specialWinTotal: 0,
55
+ jackpotTotal: 0,
56
+ };
57
+ const spinRequest = {
58
+ command: types_1.CommandType.SPIN,
59
+ sessionId: initRequest.sessionId,
60
+ };
61
+ const startTime = Date.now();
62
+ let lastReport = 0;
63
+ while (result.paidSpins < spinCount) {
64
+ const isPaidSpin = state.publicState.currentSpinType === happy_panda_config_1.SpinType.PAID_SPIN;
65
+ if (isPaidSpin) {
66
+ result.totalWagered += betGame;
67
+ result.paidSpins++;
68
+ }
69
+ else {
70
+ result.bonusSpins++;
71
+ }
72
+ const spinResult = await engine.handleCommand(spinRequest, state);
73
+ state = spinResult.state;
74
+ const response = spinResult.response;
75
+ result.totalWon += response.wins.totalPayout;
76
+ result.totalWon += response.jackpotWon;
77
+ result.totalWon += response.poolJackpotWon;
78
+ result.lineWinTotal += response.wins.lineWins.reduce((s, w) => s + w.payout, 0);
79
+ result.scatterWinTotal += response.wins.scatterWins.reduce((s, w) => s + w.payout, 0);
80
+ if (response.wins.wallWin)
81
+ result.wallWinTotal += response.wins.wallWin.payout;
82
+ result.specialWinTotal += response.wins.specialWins.reduce((s, w) => s + w.payout, 0);
83
+ result.jackpotTotal += response.jackpotWon + response.poolJackpotWon;
84
+ // Progress report every 100k
85
+ if (result.paidSpins - lastReport >= 100000) {
86
+ lastReport = result.paidSpins;
87
+ const currentRtp = (result.totalWon / result.totalWagered) * 100;
88
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
89
+ console.log(` ${(result.paidSpins / 1000).toFixed(0)}k spins: RTP=${currentRtp.toFixed(2)}% (${elapsed}s)`);
90
+ }
91
+ }
92
+ result.rtp = (result.totalWon / result.totalWagered) * 100;
93
+ return result;
94
+ }
95
+ // =============================================================================
96
+ // TESTS
97
+ // =============================================================================
98
+ // 1 Million spin test
99
+ describe('RTP Validation (1M spins)', () => {
100
+ it('should achieve ~96% RTP over 1 million 8-line spins', async () => {
101
+ console.log('\nStarting 1 million spin simulation...');
102
+ const result = await runHighVolumeSimulation(1000000, types_1.GameDirection.SINGLE, 12345);
103
+ console.log(`\n========================================`);
104
+ console.log(`1 MILLION SPIN RTP SIMULATION`);
105
+ console.log(`========================================`);
106
+ console.log(`Mode: 8-Line | Bet: 8`);
107
+ console.log(`Paid Spins: ${result.paidSpins.toLocaleString()}`);
108
+ console.log(`Bonus Spins: ${result.bonusSpins.toLocaleString()}`);
109
+ console.log(`Total Wagered: ${result.totalWagered.toLocaleString()}`);
110
+ console.log(`Total Won: ${result.totalWon.toLocaleString()}`);
111
+ console.log(`----------------------------------------`);
112
+ console.log(`*** RTP: ${result.rtp.toFixed(4)}% ***`);
113
+ console.log(`----------------------------------------`);
114
+ console.log(`Win Breakdown:`);
115
+ console.log(` Lines: ${result.lineWinTotal.toLocaleString()} (${((result.lineWinTotal / result.totalWon) * 100).toFixed(1)}%)`);
116
+ console.log(` Scatter: ${result.scatterWinTotal.toLocaleString()} (${((result.scatterWinTotal / result.totalWon) * 100).toFixed(1)}%)`);
117
+ console.log(` Wall: ${result.wallWinTotal.toLocaleString()} (${((result.wallWinTotal / result.totalWon) * 100).toFixed(1)}%)`);
118
+ console.log(` Special: ${result.specialWinTotal.toLocaleString()} (${((result.specialWinTotal / result.totalWon) * 100).toFixed(1)}%)`);
119
+ console.log(` Jackpots: ${result.jackpotTotal.toLocaleString()} (${((result.jackpotTotal / result.totalWon) * 100).toFixed(1)}%)`);
120
+ console.log(`========================================\n`);
121
+ // 1M spins: expect 94-98% range (tighter variance at this sample size)
122
+ expect(result.rtp).toBeGreaterThan(94);
123
+ expect(result.rtp).toBeLessThan(98);
124
+ }, 600000);
125
+ });
126
+ // 100M spin test - C++ parity validation
127
+ describe('High-Volume RTP (100M spins)', () => {
128
+ it('should achieve ~96% RTP over 100 million 8-line spins', async () => {
129
+ console.log('\nStarting 100 million spin simulation...');
130
+ console.log('Estimated time: ~30 minutes');
131
+ const result = await runHighVolumeSimulation(100000000, types_1.GameDirection.SINGLE, 99999);
132
+ console.log(`\n========================================`);
133
+ console.log(`100 MILLION SPIN RTP SIMULATION`);
134
+ console.log(`========================================`);
135
+ console.log(`Mode: 8-Line | Bet: 8`);
136
+ console.log(`Paid Spins: ${result.paidSpins.toLocaleString()}`);
137
+ console.log(`Bonus Spins: ${result.bonusSpins.toLocaleString()}`);
138
+ console.log(`Total Wagered: ${result.totalWagered.toLocaleString()}`);
139
+ console.log(`Total Won: ${result.totalWon.toLocaleString()}`);
140
+ console.log(`----------------------------------------`);
141
+ console.log(`*** RTP: ${result.rtp.toFixed(4)}% ***`);
142
+ console.log(`----------------------------------------`);
143
+ console.log(`Win Breakdown:`);
144
+ console.log(` Lines: ${((result.lineWinTotal / result.totalWon) * 100).toFixed(1)}%`);
145
+ console.log(` Scatter: ${((result.scatterWinTotal / result.totalWon) * 100).toFixed(1)}%`);
146
+ console.log(` Wall: ${((result.wallWinTotal / result.totalWon) * 100).toFixed(1)}%`);
147
+ console.log(` Special: ${((result.specialWinTotal / result.totalWon) * 100).toFixed(1)}%`);
148
+ console.log(` Jackpots: ${((result.jackpotTotal / result.totalWon) * 100).toFixed(1)}%`);
149
+ console.log(`========================================\n`);
150
+ // C++ parity - verify we're within 1% of 96% target
151
+ console.log('\nC++ PARITY MODE - Target: 96.05% (C++ 1B spin result)');
152
+ expect(result.rtp).toBeGreaterThan(95);
153
+ expect(result.rtp).toBeLessThan(97);
154
+ }, 3600000); // 1 hour timeout for 100M spins
155
+ });
156
+ //# sourceMappingURL=rtp-1million.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtp-1million.test.js","sourceRoot":"","sources":["../../src/__tests__/rtp-1million.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * RTP Analysis Tests
4
+ *
5
+ * Analytical calculation to understand expected RTP from weights.
6
+ * Used to debug the 432% RTP issue.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const happy_panda_config_1 = require("../config/happy-panda.config");
10
+ const types_1 = require("../domain/types");
11
+ describe('RTP Analysis', () => {
12
+ describe('3x3 Wall Win Probabilities', () => {
13
+ function calculate3x3Rate(weights) {
14
+ const [yesWeight, noWeight] = weights[types_1.GameDirection.SINGLE];
15
+ return yesWeight / (yesWeight + noWeight);
16
+ }
17
+ it('should show 3x3 probabilities for each spin type', () => {
18
+ const paidRate = calculate3x3Rate(happy_panda_config_1.PAID_SPIN_3X3_WEIGHTS);
19
+ const jackpotRate = calculate3x3Rate(happy_panda_config_1.JACKPOT_BONUS_3X3_WEIGHTS);
20
+ const cherryRate = calculate3x3Rate(happy_panda_config_1.CHERRY_BONUS_3X3_WEIGHTS);
21
+ const bellRate = calculate3x3Rate(happy_panda_config_1.BELL_BONUS_3X3_WEIGHTS);
22
+ const bar1Rate = calculate3x3Rate(happy_panda_config_1.BAR1_BONUS_3X3_WEIGHTS);
23
+ console.log('3x3 Wall Win Probabilities:');
24
+ console.log(` Paid spin: ${(paidRate * 100).toFixed(2)}% (${happy_panda_config_1.PAID_SPIN_3X3_WEIGHTS[0].join('/')})`);
25
+ console.log(` Jackpot bonus: ${(jackpotRate * 100).toFixed(2)}% (${happy_panda_config_1.JACKPOT_BONUS_3X3_WEIGHTS[0].join('/')})`);
26
+ console.log(` Cherry bonus: ${(cherryRate * 100).toFixed(2)}% (${happy_panda_config_1.CHERRY_BONUS_3X3_WEIGHTS[0].join('/')})`);
27
+ console.log(` Bell bonus: ${(bellRate * 100).toFixed(2)}% (${happy_panda_config_1.BELL_BONUS_3X3_WEIGHTS[0].join('/')})`);
28
+ console.log(` Bar1 bonus: ${(bar1Rate * 100).toFixed(2)}% (${happy_panda_config_1.BAR1_BONUS_3X3_WEIGHTS[0].join('/')})`);
29
+ console.log('\nRatio vs Paid spin:');
30
+ console.log(` Jackpot: ${(jackpotRate / paidRate).toFixed(1)}x higher`);
31
+ console.log(` Cherry: ${(cherryRate / paidRate).toFixed(1)}x higher`);
32
+ console.log(` Bell: ${(bellRate / paidRate).toFixed(1)}x higher`);
33
+ console.log(` Bar1: ${(bar1Rate / paidRate).toFixed(1)}x higher`);
34
+ // After fix: All rates should be similar to paid spin (~1-2%)
35
+ // Previously these were 10-25% causing 432% RTP
36
+ expect(jackpotRate).toBeLessThan(0.03); // Fixed: Now ~1.5%
37
+ expect(bellRate).toBeLessThan(0.03); // Fixed: Now ~1%
38
+ expect(cherryRate).toBeLessThan(0.02); // Fixed: Now ~0.5%
39
+ expect(bar1Rate).toBeLessThan(0.02); // Fixed: Now ~0.5%
40
+ });
41
+ it('should calculate expected wall win payout per spin type', () => {
42
+ function calculateExpectedWallPayout(weights3x3, symbolWeights) {
43
+ const [yesWeight, noWeight] = weights3x3[types_1.GameDirection.SINGLE];
44
+ const rate3x3 = yesWeight / (yesWeight + noWeight);
45
+ // Calculate weighted average wall multiplier
46
+ const totalSymbolWeight = symbolWeights.reduce((a, b) => a + b, 0);
47
+ let expectedMultiplier = 0;
48
+ // Wall multipliers: BAR3=500, BAR2=400, BAR1=300, etc
49
+ // Symbol order varies by spin type, but we can calculate weighted average
50
+ for (let i = 0; i < symbolWeights.length && i < happy_panda_config_1.WALL_MULTIPLIERS.length; i++) {
51
+ if (symbolWeights[i] > 0 && totalSymbolWeight > 0) {
52
+ expectedMultiplier += (symbolWeights[i] / totalSymbolWeight) * happy_panda_config_1.WALL_MULTIPLIERS[i];
53
+ }
54
+ }
55
+ return rate3x3 * expectedMultiplier;
56
+ }
57
+ const paidWall = calculateExpectedWallPayout(happy_panda_config_1.PAID_SPIN_3X3_WEIGHTS, happy_panda_config_1.PAID_SPIN_3X3_SYMBOL_WEIGHTS);
58
+ const jackpotWall = calculateExpectedWallPayout(happy_panda_config_1.JACKPOT_BONUS_3X3_WEIGHTS, happy_panda_config_1.JACKPOT_BONUS_3X3_SYMBOL_WEIGHTS);
59
+ const cherryWall = calculateExpectedWallPayout(happy_panda_config_1.CHERRY_BONUS_3X3_WEIGHTS, happy_panda_config_1.CHERRY_BONUS_3X3_SYMBOL_WEIGHTS);
60
+ const bellWall = calculateExpectedWallPayout(happy_panda_config_1.BELL_BONUS_3X3_WEIGHTS, happy_panda_config_1.BELL_BONUS_3X3_SYMBOL_WEIGHTS);
61
+ const bar1Wall = calculateExpectedWallPayout(happy_panda_config_1.BAR1_BONUS_3X3_WEIGHTS, happy_panda_config_1.BAR1_BONUS_3X3_SYMBOL_WEIGHTS);
62
+ console.log('Expected Wall Win Payout (multiplier × betGame) per spin:');
63
+ console.log(` Paid spin: ${paidWall.toFixed(2)}x`);
64
+ console.log(` Jackpot bonus: ${jackpotWall.toFixed(2)}x`);
65
+ console.log(` Cherry bonus: ${cherryWall.toFixed(2)}x`);
66
+ console.log(` Bell bonus: ${bellWall.toFixed(2)}x`);
67
+ console.log(` Bar1 bonus: ${bar1Wall.toFixed(2)}x`);
68
+ });
69
+ it('should estimate RTP contribution from bonus wall wins', () => {
70
+ // Rough estimation of bonus frequency and wall win impact
71
+ // Per 10k paid spins, we see approximately:
72
+ // - 30 jackpot bonuses × 1 spin each = 30 bonus spins
73
+ // - 45 cherry bonuses × 1 spin each = 45 bonus spins
74
+ // - 8 bell bonuses × 5 spins each = 40 bonus spins
75
+ // - 9 bar1 bonuses × 7 spins each = 63 bonus spins
76
+ // - 300 respins = 300 bonus spins
77
+ // Total: ~478 bonus spins per 10k paid spins
78
+ const paidSpins = 10000;
79
+ const betGame = 8;
80
+ // Estimate bonus spin counts (based on simulation data)
81
+ const jackpotBonusSpins = 30;
82
+ const cherryBonusSpins = 45;
83
+ const bellBonusSpins = 40;
84
+ const bar1BonusSpins = 63;
85
+ // Calculate wall win contribution for each
86
+ const calculate3x3Rate = (weights) => {
87
+ const [yes, no] = weights[0];
88
+ return yes / (yes + no);
89
+ };
90
+ const avgWallMultiplier = 150; // Rough average across symbol weights
91
+ const paidWallRtp = calculate3x3Rate(happy_panda_config_1.PAID_SPIN_3X3_WEIGHTS) * avgWallMultiplier * paidSpins / (paidSpins * betGame);
92
+ const jackpotWallRtp = calculate3x3Rate(happy_panda_config_1.JACKPOT_BONUS_3X3_WEIGHTS) * avgWallMultiplier * jackpotBonusSpins / (paidSpins * betGame);
93
+ const cherryWallRtp = calculate3x3Rate(happy_panda_config_1.CHERRY_BONUS_3X3_WEIGHTS) * avgWallMultiplier * cherryBonusSpins / (paidSpins * betGame);
94
+ const bellWallRtp = calculate3x3Rate(happy_panda_config_1.BELL_BONUS_3X3_WEIGHTS) * avgWallMultiplier * bellBonusSpins / (paidSpins * betGame);
95
+ const bar1WallRtp = calculate3x3Rate(happy_panda_config_1.BAR1_BONUS_3X3_WEIGHTS) * avgWallMultiplier * bar1BonusSpins / (paidSpins * betGame);
96
+ console.log('Wall Win RTP Contribution:');
97
+ console.log(` Paid spins: ${(paidWallRtp * 100).toFixed(1)}%`);
98
+ console.log(` Jackpot bonus: ${(jackpotWallRtp * 100).toFixed(1)}%`);
99
+ console.log(` Cherry bonus: ${(cherryWallRtp * 100).toFixed(1)}%`);
100
+ console.log(` Bell bonus: ${(bellWallRtp * 100).toFixed(1)}%`);
101
+ console.log(` Bar1 bonus: ${(bar1WallRtp * 100).toFixed(1)}%`);
102
+ console.log(` Total wall RTP: ${((paidWallRtp + jackpotWallRtp + cherryWallRtp + bellWallRtp + bar1WallRtp) * 100).toFixed(1)}%`);
103
+ });
104
+ });
105
+ describe('Proposed Fix: Reduce Bonus 3x3 Rates', () => {
106
+ it('should calculate impact of fixing bonus 3x3 weights', () => {
107
+ // Current weights
108
+ const current = {
109
+ jackpot: happy_panda_config_1.JACKPOT_BONUS_3X3_WEIGHTS[0],
110
+ cherry: happy_panda_config_1.CHERRY_BONUS_3X3_WEIGHTS[0],
111
+ bell: happy_panda_config_1.BELL_BONUS_3X3_WEIGHTS[0],
112
+ bar1: happy_panda_config_1.BAR1_BONUS_3X3_WEIGHTS[0],
113
+ };
114
+ // Proposed fix: Use similar ratio to paid spin (3:193 ≈ 1:64)
115
+ // But maybe slightly higher for bonus feel (1:30 = 3.2%)
116
+ const proposed = {
117
+ jackpot: [3, 97], // 3% instead of 25%
118
+ cherry: [1, 99], // 1% instead of 10%
119
+ bell: [2, 98], // 2% instead of 18%
120
+ bar1: [1, 99], // 1% instead of 10%
121
+ };
122
+ console.log('Proposed 3x3 Weight Changes:');
123
+ Object.entries(current).forEach(([name, weights]) => {
124
+ const currentRate = weights[0] / (weights[0] + weights[1]);
125
+ const proposedWeights = proposed[name];
126
+ const proposedRate = proposedWeights[0] / (proposedWeights[0] + proposedWeights[1]);
127
+ const reduction = currentRate / proposedRate;
128
+ console.log(` ${name}: [${weights.join(', ')}] → [${proposedWeights.join(', ')}]`);
129
+ console.log(` ${(currentRate * 100).toFixed(1)}% → ${(proposedRate * 100).toFixed(1)}% (${reduction.toFixed(1)}x reduction)`);
130
+ });
131
+ console.log('\nExpected RTP impact:');
132
+ console.log(' Wall RTP should drop from ~265% to ~30-40%');
133
+ console.log(' Total RTP should drop from ~432% to ~130-150%');
134
+ console.log(' Further tuning needed for 96% target');
135
+ });
136
+ });
137
+ });
138
+ //# sourceMappingURL=rtp-analysis.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtp-analysis.test.js","sourceRoot":"","sources":["../../src/__tests__/rtp-analysis.test.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAEH,qEAgBsC;AACtC,2CAAgD;AAEhD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,SAAS,gBAAgB,CAAC,OAAmB;YAC3C,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,qBAAa,CAAC,MAAM,CAAC,CAAC;YAC5D,OAAO,SAAS,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;QAC5C,CAAC;QAED,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,0CAAqB,CAAC,CAAC;YACzD,MAAM,WAAW,GAAG,gBAAgB,CAAC,8CAAyB,CAAC,CAAC;YAChE,MAAM,UAAU,GAAG,gBAAgB,CAAC,6CAAwB,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,2CAAsB,CAAC,CAAC;YAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,2CAAsB,CAAC,CAAC;YAE1D,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,0CAAqB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,8CAAyB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/G,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,6CAAwB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5G,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,2CAAsB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,2CAAsB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAEtG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,UAAU,GAAG,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YAEnE,8DAA8D;YAC9D,gDAAgD;YAChD,MAAM,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB;YAC3D,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAI,iBAAiB;YACzD,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB;YAC1D,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAG,mBAAmB;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,SAAS,2BAA2B,CAClC,UAAsB,EACtB,aAAuB;gBAEvB,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,qBAAa,CAAC,MAAM,CAAC,CAAC;gBAC/D,MAAM,OAAO,GAAG,SAAS,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;gBAEnD,6CAA6C;gBAC7C,MAAM,iBAAiB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBACnE,IAAI,kBAAkB,GAAG,CAAC,CAAC;gBAE3B,sDAAsD;gBACtD,0EAA0E;gBAC1E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,IAAI,CAAC,GAAG,qCAAgB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC7E,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;wBAClD,kBAAkB,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,GAAG,qCAAgB,CAAC,CAAC,CAAC,CAAC;oBACrF,CAAC;gBACH,CAAC;gBAED,OAAO,OAAO,GAAG,kBAAkB,CAAC;YACtC,CAAC;YAED,MAAM,QAAQ,GAAG,2BAA2B,CAAC,0CAAqB,EAAE,iDAA4B,CAAC,CAAC;YAClG,MAAM,WAAW,GAAG,2BAA2B,CAAC,8CAAyB,EAAE,qDAAgC,CAAC,CAAC;YAC7G,MAAM,UAAU,GAAG,2BAA2B,CAAC,6CAAwB,EAAE,oDAA+B,CAAC,CAAC;YAC1G,MAAM,QAAQ,GAAG,2BAA2B,CAAC,2CAAsB,EAAE,kDAA6B,CAAC,CAAC;YACpG,MAAM,QAAQ,GAAG,2BAA2B,CAAC,2CAAsB,EAAE,kDAA6B,CAAC,CAAC;YAEpG,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,mBAAmB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;YAC/D,0DAA0D;YAC1D,4CAA4C;YAC5C,sDAAsD;YACtD,qDAAqD;YACrD,mDAAmD;YACnD,mDAAmD;YACnD,kCAAkC;YAClC,6CAA6C;YAE7C,MAAM,SAAS,GAAG,KAAK,CAAC;YACxB,MAAM,OAAO,GAAG,CAAC,CAAC;YAElB,wDAAwD;YACxD,MAAM,iBAAiB,GAAG,EAAE,CAAC;YAC7B,MAAM,gBAAgB,GAAG,EAAE,CAAC;YAC5B,MAAM,cAAc,GAAG,EAAE,CAAC;YAC1B,MAAM,cAAc,GAAG,EAAE,CAAC;YAE1B,2CAA2C;YAC3C,MAAM,gBAAgB,GAAG,CAAC,OAAmB,EAAE,EAAE;gBAC/C,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC7B,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;YAC1B,CAAC,CAAC;YAEF,MAAM,iBAAiB,GAAG,GAAG,CAAC,CAAC,sCAAsC;YAErE,MAAM,WAAW,GAAG,gBAAgB,CAAC,0CAAqB,CAAC,GAAG,iBAAiB,GAAG,SAAS,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;YACpH,MAAM,cAAc,GAAG,gBAAgB,CAAC,8CAAyB,CAAC,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;YACnI,MAAM,aAAa,GAAG,gBAAgB,CAAC,6CAAwB,CAAC,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;YAChI,MAAM,WAAW,GAAG,gBAAgB,CAAC,2CAAsB,CAAC,GAAG,iBAAiB,GAAG,cAAc,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;YAC1H,MAAM,WAAW,GAAG,gBAAgB,CAAC,2CAAsB,CAAC,GAAG,iBAAiB,GAAG,cAAc,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;YAE1H,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,cAAc,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACtE,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,WAAW,GAAG,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACrI,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;QACpD,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;YAC7D,kBAAkB;YAClB,MAAM,OAAO,GAAG;gBACd,OAAO,EAAE,8CAAyB,CAAC,CAAC,CAAC;gBACrC,MAAM,EAAE,6CAAwB,CAAC,CAAC,CAAC;gBACnC,IAAI,EAAE,2CAAsB,CAAC,CAAC,CAAC;gBAC/B,IAAI,EAAE,2CAAsB,CAAC,CAAC,CAAC;aAChC,CAAC;YAEF,8DAA8D;YAC9D,yDAAyD;YACzD,MAAM,QAAQ,GAAG;gBACf,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,oBAAoB;gBACtC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAG,oBAAoB;gBACtC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAK,oBAAoB;gBACtC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAK,oBAAoB;aACvC,CAAC;YAEF,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE;gBAClD,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3D,MAAM,eAAe,GAAG,QAAQ,CAAC,IAA6B,CAAC,CAAC;gBAChE,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpF,MAAM,SAAS,GAAG,WAAW,GAAG,YAAY,CAAC;gBAE7C,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACpF,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;YACxI,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;YAC5D,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * RTP Diagnostic Test
4
+ * Compares TypeScript engine RTP breakdown with C++ expected values.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const happy_panda_engine_1 = require("../engine/happy-panda-engine");
8
+ const happy_panda_config_1 = require("../config/happy-panda.config");
9
+ const types_1 = require("../domain/types");
10
+ function createSimulationRng(seed = 12345) {
11
+ let state = seed;
12
+ function nextInt() {
13
+ state = (state * 1664525 + 1013904223) >>> 0;
14
+ return state;
15
+ }
16
+ return {
17
+ async random(max) {
18
+ return nextInt() % max;
19
+ },
20
+ async randomBatch(count, max) {
21
+ const results = [];
22
+ for (let i = 0; i < count; i++) {
23
+ results.push(nextInt() % max);
24
+ }
25
+ return results;
26
+ },
27
+ };
28
+ }
29
+ describe('RTP Diagnostic - Component Breakdown', () => {
30
+ it('should break down RTP by spin type and compare with C++ stats', async () => {
31
+ const rng = createSimulationRng(12345);
32
+ const engine = new happy_panda_engine_1.HappyPandaEngine(rng);
33
+ const targetSpins = 100000;
34
+ const betGame = 8;
35
+ const initRequest = {
36
+ command: types_1.CommandType.INIT_SESSION_STATE,
37
+ sessionId: `diag-${Date.now()}`,
38
+ gameDirection: types_1.GameDirection.SINGLE,
39
+ betStake: 1,
40
+ };
41
+ let { state } = await engine.handleCommand(initRequest);
42
+ // Track by spin type
43
+ const stats = {};
44
+ for (let type = happy_panda_config_1.SpinType.PAID_SPIN; type <= happy_panda_config_1.SpinType.RESPIN_CHERRY; type++) {
45
+ stats[type] = { spins: 0, totalWon: 0, lineWins: 0, scatterWins: 0, wallWins: 0, specialWins: 0, jackpotWins: 0 };
46
+ }
47
+ let totalWagered = 0;
48
+ let paidSpins = 0;
49
+ const spinRequest = {
50
+ command: types_1.CommandType.SPIN,
51
+ sessionId: initRequest.sessionId,
52
+ };
53
+ while (paidSpins < targetSpins) {
54
+ const currentSpinType = state.publicState.currentSpinType;
55
+ const isPaidSpin = currentSpinType === happy_panda_config_1.SpinType.PAID_SPIN;
56
+ if (isPaidSpin) {
57
+ totalWagered += betGame;
58
+ paidSpins++;
59
+ }
60
+ const spinResult = await engine.handleCommand(spinRequest, state);
61
+ state = spinResult.state;
62
+ const response = spinResult.response;
63
+ const stat = stats[currentSpinType];
64
+ stat.spins++;
65
+ stat.totalWon += response.wins.totalPayout + response.jackpotWon + response.poolJackpotWon;
66
+ stat.lineWins += response.wins.lineWins.reduce((s, w) => s + w.payout, 0);
67
+ stat.scatterWins += response.wins.scatterWins.reduce((s, w) => s + w.payout, 0);
68
+ if (response.wins.wallWin)
69
+ stat.wallWins += response.wins.wallWin.payout;
70
+ stat.specialWins += response.wins.specialWins.reduce((s, w) => s + w.payout, 0);
71
+ stat.jackpotWins += response.jackpotWon + response.poolJackpotWon;
72
+ // Progress
73
+ if (paidSpins % 25000 === 0) {
74
+ const currentRtp = (Object.values(stats).reduce((s, st) => s + st.totalWon, 0) / totalWagered) * 100;
75
+ console.log(` ${(paidSpins / 1000).toFixed(0)}k spins: RTP=${currentRtp.toFixed(2)}%`);
76
+ }
77
+ }
78
+ const totalWon = Object.values(stats).reduce((s, st) => s + st.totalWon, 0);
79
+ const rtp = (totalWon / totalWagered) * 100;
80
+ console.log('\n========================================');
81
+ console.log('RTP DIAGNOSTIC BY SPIN TYPE');
82
+ console.log('========================================\n');
83
+ console.log('C++ Stats_8lines Reference:');
84
+ console.log(' paid_spin: 63.51% RTP contribution');
85
+ console.log(' bonus_jackpot: 13.56% RTP contribution');
86
+ console.log(' bonus_cherry: 3.67% RTP contribution');
87
+ console.log(' bonus_bell: 5.83% RTP contribution');
88
+ console.log(' bonus_bar1: 6.31% RTP contribution');
89
+ console.log(' respin_ch: 3.18% RTP contribution');
90
+ console.log(' TOTAL: 96.05%\n');
91
+ console.log('TypeScript Engine Results:');
92
+ const spinTypeNames = ['PAID_SPIN', 'BONUS_JACKPOT', 'BONUS_CHERRY', 'BONUS_BELL', 'BONUS_BAR1', 'RESPIN_CHERRY'];
93
+ const cppExpected = [63.51, 13.56, 3.67, 5.83, 6.31, 3.18];
94
+ for (let type = happy_panda_config_1.SpinType.PAID_SPIN; type <= happy_panda_config_1.SpinType.RESPIN_CHERRY; type++) {
95
+ const stat = stats[type];
96
+ const rtpContribution = (stat.totalWon / totalWagered) * 100;
97
+ const expected = cppExpected[type];
98
+ const diff = rtpContribution - expected;
99
+ const diffStr = diff >= 0 ? `+${diff.toFixed(2)}` : diff.toFixed(2);
100
+ console.log(`\n ${spinTypeNames[type]}:`);
101
+ console.log(` Spins: ${stat.spins.toLocaleString()}`);
102
+ console.log(` RTP Contribution: ${rtpContribution.toFixed(2)}% (C++: ${expected.toFixed(2)}%, diff: ${diffStr}%)`);
103
+ console.log(` Breakdown - Lines: ${stat.lineWins} | Scatter: ${stat.scatterWins} | Wall: ${stat.wallWins} | Special: ${stat.specialWins} | Jackpot: ${stat.jackpotWins}`);
104
+ }
105
+ console.log('\n========================================');
106
+ console.log(`TOTAL RTP: ${rtp.toFixed(4)}% (C++ target: 96.05%)`);
107
+ console.log(`DIFFERENCE: ${(rtp - 96.05).toFixed(2)}%`);
108
+ console.log('========================================\n');
109
+ // Show where the extra RTP is coming from
110
+ console.log('EXCESS RTP BY COMPONENT:');
111
+ let totalExcess = 0;
112
+ for (let type = happy_panda_config_1.SpinType.PAID_SPIN; type <= happy_panda_config_1.SpinType.RESPIN_CHERRY; type++) {
113
+ const stat = stats[type];
114
+ const rtpContribution = (stat.totalWon / totalWagered) * 100;
115
+ const expected = cppExpected[type];
116
+ const diff = rtpContribution - expected;
117
+ totalExcess += diff;
118
+ if (Math.abs(diff) > 0.5) {
119
+ console.log(` ${spinTypeNames[type]}: ${diff >= 0 ? '+' : ''}${diff.toFixed(2)}% excess`);
120
+ }
121
+ }
122
+ console.log(` TOTAL EXCESS: ${totalExcess.toFixed(2)}%`);
123
+ expect(true).toBe(true);
124
+ }, 300000);
125
+ });
126
+ //# sourceMappingURL=rtp-diagnostic.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtp-diagnostic.test.js","sourceRoot":"","sources":["../../src/__tests__/rtp-diagnostic.test.ts"],"names":[],"mappings":"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"}