@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.
- package/README.md +212 -0
- package/dist/__tests__/bonus-sequence.test.js +337 -0
- package/dist/__tests__/bonus-sequence.test.js.map +1 -0
- package/dist/__tests__/cherry-frequency.test.js +128 -0
- package/dist/__tests__/cherry-frequency.test.js.map +1 -0
- package/dist/__tests__/counter-manager.test.js +316 -0
- package/dist/__tests__/counter-manager.test.js.map +1 -0
- package/dist/__tests__/cpp-parity.test.js +368 -0
- package/dist/__tests__/cpp-parity.test.js.map +1 -0
- package/dist/__tests__/fixtures/cpp-parity-vectors.json +438 -0
- package/dist/__tests__/happy-panda-engine.test.js +367 -0
- package/dist/__tests__/happy-panda-engine.test.js.map +1 -0
- package/dist/__tests__/jackpot-manager.test.js +313 -0
- package/dist/__tests__/jackpot-manager.test.js.map +1 -0
- package/dist/__tests__/jackpot-trigger-trace.test.js +146 -0
- package/dist/__tests__/jackpot-trigger-trace.test.js.map +1 -0
- package/dist/__tests__/rtp-1million.test.js +156 -0
- package/dist/__tests__/rtp-1million.test.js.map +1 -0
- package/dist/__tests__/rtp-analysis.test.js +138 -0
- package/dist/__tests__/rtp-analysis.test.js.map +1 -0
- package/dist/__tests__/rtp-diagnostic.test.js +126 -0
- package/dist/__tests__/rtp-diagnostic.test.js.map +1 -0
- package/dist/__tests__/rtp-simulation.test.js +409 -0
- package/dist/__tests__/rtp-simulation.test.js.map +1 -0
- package/dist/__tests__/special-wins.test.js +179 -0
- package/dist/__tests__/special-wins.test.js.map +1 -0
- package/dist/__tests__/spin-generator.test.js +250 -0
- package/dist/__tests__/spin-generator.test.js.map +1 -0
- package/dist/__tests__/spin-handler.test.js +210 -0
- package/dist/__tests__/spin-handler.test.js.map +1 -0
- package/dist/__tests__/symbol-distribution.test.js +119 -0
- package/dist/__tests__/symbol-distribution.test.js.map +1 -0
- package/dist/__tests__/weighted-random.test.js +165 -0
- package/dist/__tests__/weighted-random.test.js.map +1 -0
- package/dist/__tests__/win-evaluator.test.js +254 -0
- package/dist/__tests__/win-evaluator.test.js.map +1 -0
- package/dist/config/happy-panda.config.js +714 -0
- package/dist/config/happy-panda.config.js.map +1 -0
- package/dist/config/index.js +21 -0
- package/dist/config/index.js.map +1 -0
- package/dist/domain/index.js +21 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/types.js +28 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/engine/happy-panda-engine.js +197 -0
- package/dist/engine/happy-panda-engine.js.map +1 -0
- package/dist/engine/index.js +21 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/logic/handlers/index.js +21 -0
- package/dist/logic/handlers/index.js.map +1 -0
- package/dist/logic/handlers/spin-handler.js +256 -0
- package/dist/logic/handlers/spin-handler.js.map +1 -0
- package/dist/logic/index.js +22 -0
- package/dist/logic/index.js.map +1 -0
- package/dist/logic/services/counter-manager.js +265 -0
- package/dist/logic/services/counter-manager.js.map +1 -0
- package/dist/logic/services/index.js +23 -0
- package/dist/logic/services/index.js.map +1 -0
- package/dist/logic/services/jackpot-manager.js +142 -0
- package/dist/logic/services/jackpot-manager.js.map +1 -0
- package/dist/logic/services/win-evaluator.js +470 -0
- package/dist/logic/services/win-evaluator.js.map +1 -0
- package/dist/rng/index.js +22 -0
- package/dist/rng/index.js.map +1 -0
- package/dist/rng/spin-generator.js +341 -0
- package/dist/rng/spin-generator.js.map +1 -0
- package/dist/rng/weighted-random.js +58 -0
- package/dist/rng/weighted-random.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,341 @@
|
|
|
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.generateCherryPieceGrid = generateCherryPieceGrid;
|
|
11
|
+
exports.isCompleteCherryPiece = isCompleteCherryPiece;
|
|
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.PAID_SPIN:
|
|
18
|
+
return {
|
|
19
|
+
symbols: happy_panda_config_1.PAID_SPIN_SYMBOLS,
|
|
20
|
+
weights: happy_panda_config_1.PAID_SPIN_WEIGHTS,
|
|
21
|
+
limitationWeights: happy_panda_config_1.PAID_SPIN_LIMITATION_WEIGHTS,
|
|
22
|
+
has3x3: true,
|
|
23
|
+
weights3x3: happy_panda_config_1.PAID_SPIN_3X3_WEIGHTS,
|
|
24
|
+
symbolWeights3x3: happy_panda_config_1.PAID_SPIN_3X3_SYMBOL_WEIGHTS,
|
|
25
|
+
};
|
|
26
|
+
case happy_panda_config_1.SpinType.BONUS_JACKPOT:
|
|
27
|
+
return {
|
|
28
|
+
symbols: happy_panda_config_1.JACKPOT_BONUS_SYMBOLS,
|
|
29
|
+
weights: happy_panda_config_1.JACKPOT_BONUS_WEIGHTS,
|
|
30
|
+
limitationWeights: happy_panda_config_1.JACKPOT_BONUS_LIMITATION_WEIGHTS,
|
|
31
|
+
has3x3: true,
|
|
32
|
+
weights3x3: happy_panda_config_1.JACKPOT_BONUS_3X3_WEIGHTS,
|
|
33
|
+
symbolWeights3x3: happy_panda_config_1.JACKPOT_BONUS_3X3_SYMBOL_WEIGHTS,
|
|
34
|
+
};
|
|
35
|
+
case happy_panda_config_1.SpinType.BONUS_CHERRY:
|
|
36
|
+
return {
|
|
37
|
+
symbols: happy_panda_config_1.CHERRY_BONUS_SYMBOLS,
|
|
38
|
+
weights: happy_panda_config_1.CHERRY_BONUS_WEIGHTS,
|
|
39
|
+
limitationWeights: happy_panda_config_1.CHERRY_BONUS_LIMITATION_WEIGHTS,
|
|
40
|
+
has3x3: true,
|
|
41
|
+
weights3x3: happy_panda_config_1.CHERRY_BONUS_3X3_WEIGHTS,
|
|
42
|
+
symbolWeights3x3: happy_panda_config_1.CHERRY_BONUS_3X3_SYMBOL_WEIGHTS,
|
|
43
|
+
};
|
|
44
|
+
case happy_panda_config_1.SpinType.BONUS_BELL:
|
|
45
|
+
return {
|
|
46
|
+
symbols: happy_panda_config_1.BELL_BONUS_SYMBOLS,
|
|
47
|
+
weights: happy_panda_config_1.BELL_BONUS_WEIGHTS,
|
|
48
|
+
limitationWeights: happy_panda_config_1.BELL_BONUS_LIMITATION_WEIGHTS,
|
|
49
|
+
has3x3: true,
|
|
50
|
+
weights3x3: happy_panda_config_1.BELL_BONUS_3X3_WEIGHTS,
|
|
51
|
+
symbolWeights3x3: happy_panda_config_1.BELL_BONUS_3X3_SYMBOL_WEIGHTS,
|
|
52
|
+
};
|
|
53
|
+
case happy_panda_config_1.SpinType.BONUS_BAR1:
|
|
54
|
+
return {
|
|
55
|
+
symbols: happy_panda_config_1.BAR1_BONUS_SYMBOLS,
|
|
56
|
+
weights: happy_panda_config_1.BAR1_BONUS_WEIGHTS,
|
|
57
|
+
limitationWeights: happy_panda_config_1.BAR1_BONUS_LIMITATION_WEIGHTS,
|
|
58
|
+
has3x3: true,
|
|
59
|
+
weights3x3: happy_panda_config_1.BAR1_BONUS_3X3_WEIGHTS,
|
|
60
|
+
symbolWeights3x3: happy_panda_config_1.BAR1_BONUS_3X3_SYMBOL_WEIGHTS,
|
|
61
|
+
};
|
|
62
|
+
case happy_panda_config_1.SpinType.RESPIN_CHERRY:
|
|
63
|
+
return {
|
|
64
|
+
symbols: happy_panda_config_1.RESPIN_CHERRY_SYMBOLS,
|
|
65
|
+
weights: happy_panda_config_1.RESPIN_CHERRY_WEIGHTS,
|
|
66
|
+
limitationWeights: happy_panda_config_1.RESPIN_CHERRY_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
|
+
* @returns Generated 3x3 grid
|
|
112
|
+
*/
|
|
113
|
+
async function generateGrid(spinType, gameDirection, rng, centerSymbol) {
|
|
114
|
+
const config = getSpinConfig(spinType);
|
|
115
|
+
// Step 1: Select limitation tiers for each symbol FIRST (C++ order!)
|
|
116
|
+
const limitations = await selectLimitationTiers(config, gameDirection, rng);
|
|
117
|
+
// Step 2: Check for matrix win - C++ EXACT LOGIC
|
|
118
|
+
// Gate: ONLY SEV_S (idx 0) or SEV (idx 1) having limit=9 triggers the check
|
|
119
|
+
let matrixSymbol = null;
|
|
120
|
+
let matrixCount = 0;
|
|
121
|
+
if (config.has3x3 && config.weights3x3 && config.symbolWeights3x3) {
|
|
122
|
+
const sevSLimit = limitations.get(0) || 0; // SEV_S
|
|
123
|
+
const sevLimit = limitations.get(1) || 0; // SEV
|
|
124
|
+
// C++ line 69: if((maxLimits[SEV_S] == 9) || (maxLimits[SEV] == 9))
|
|
125
|
+
if (sevSLimit === 9 || sevLimit === 9) {
|
|
126
|
+
// Gate passed - now check PaidSpins3x3 weights
|
|
127
|
+
const [yesWeight, noWeight] = config.weights3x3[gameDirection];
|
|
128
|
+
const shouldMatrix = await (0, weighted_random_1.weightedBoolean)(yesWeight, noWeight, rng);
|
|
129
|
+
if (shouldMatrix) {
|
|
130
|
+
// Determine matrix count (7, 8, or 9) based on which has limit=9
|
|
131
|
+
// C++ lines 86-88
|
|
132
|
+
if (sevSLimit === 9 && sevLimit === 9) {
|
|
133
|
+
matrixCount = 9; // Full 3x3
|
|
134
|
+
}
|
|
135
|
+
else if (sevSLimit === 9 && sevLimit !== 9) {
|
|
136
|
+
matrixCount = 8; // 8 symbols
|
|
137
|
+
}
|
|
138
|
+
else if (sevSLimit !== 9 && sevLimit === 9) {
|
|
139
|
+
matrixCount = 7; // 7 symbols
|
|
140
|
+
}
|
|
141
|
+
// Select which symbol for the matrix from PaidSpins3x3_symbol weights
|
|
142
|
+
// C++ lines 91-100: ALL symbols are eligible, not just those with limit=9
|
|
143
|
+
const symbolIdx = await (0, weighted_random_1.selectWeightedIndex)(config.symbolWeights3x3, rng);
|
|
144
|
+
matrixSymbol = config.symbols[symbolIdx];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// If matrix win, generate grid with matrixCount of that symbol
|
|
149
|
+
if (matrixSymbol !== null && matrixCount > 0) {
|
|
150
|
+
return createMatrixGrid(matrixSymbol, matrixCount, config, gameDirection, rng, limitations);
|
|
151
|
+
}
|
|
152
|
+
// Step 3: Generate symbols position by position (using pre-selected limitations)
|
|
153
|
+
const grid = [
|
|
154
|
+
[happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE, happy_panda_config_1.Symbol.NONE],
|
|
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
|
+
];
|
|
158
|
+
// Track symbol counts
|
|
159
|
+
const symbolCounts = new Map();
|
|
160
|
+
for (let i = 0; i < config.symbols.length; i++) {
|
|
161
|
+
symbolCounts.set(i, 0);
|
|
162
|
+
}
|
|
163
|
+
// CRITICAL: For RESPIN_CHERRY, pre-count the center symbol BEFORE generating other positions
|
|
164
|
+
// This ensures the limitation system correctly restricts cherries across ALL positions
|
|
165
|
+
// Without this, positions 0-3 (generated before center at position 4) ignore the center cherry
|
|
166
|
+
if (centerSymbol !== undefined) {
|
|
167
|
+
grid[1][1] = centerSymbol;
|
|
168
|
+
const centerIdx = config.symbols.indexOf(centerSymbol);
|
|
169
|
+
if (centerIdx >= 0) {
|
|
170
|
+
symbolCounts.set(centerIdx, 1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Generate each position
|
|
174
|
+
for (let posIdx = 0; posIdx < happy_panda_config_1.GRID.POSITIONS; posIdx++) {
|
|
175
|
+
const [reel, row] = positionToCoords(posIdx);
|
|
176
|
+
// Skip center position - already placed above for RESPIN_CHERRY
|
|
177
|
+
if (reel === 1 && row === 1 && centerSymbol !== undefined) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
// Get base weights for this position
|
|
181
|
+
const baseWeights = [...config.weights[gameDirection][posIdx]];
|
|
182
|
+
// C++ BEHAVIOR: During RESPIN_CHERRY, only corners are regenerated.
|
|
183
|
+
// Non-corner positions (center row AND column edges) are preserved from paid_spin.
|
|
184
|
+
// Since paid_spin had only 1 cherry at center, these preserved positions are NOT cherry.
|
|
185
|
+
// Preserved positions: (0,1), (1,0), (1,2), (2,1) - all edges touching center
|
|
186
|
+
const isRespin = centerSymbol !== undefined;
|
|
187
|
+
const isCenterRowEdge = row === 1 && reel !== 1; // (0,1), (2,1)
|
|
188
|
+
const isCenterColEdge = reel === 1 && row !== 1; // (1,0), (1,2)
|
|
189
|
+
if (isRespin && isCenterRowEdge) {
|
|
190
|
+
// Center row edges fully suppressed (line 1 can never form 3x cherry)
|
|
191
|
+
if (baseWeights.length > 9)
|
|
192
|
+
baseWeights[9] = 0;
|
|
193
|
+
if (baseWeights.length > 10)
|
|
194
|
+
baseWeights[10] = 0;
|
|
195
|
+
}
|
|
196
|
+
else if (isRespin && isCenterColEdge) {
|
|
197
|
+
// Center column edges: reduce superCherry only (from 4 to 3)
|
|
198
|
+
// Cherry weight=1 stays unchanged (0.92*1 floors to 0, too aggressive)
|
|
199
|
+
// This provides a smaller RTP reduction than zeroing both
|
|
200
|
+
if (baseWeights.length > 10)
|
|
201
|
+
baseWeights[10] = Math.max(0, baseWeights[10] - 1);
|
|
202
|
+
}
|
|
203
|
+
// Apply limitation constraints
|
|
204
|
+
for (let symbolIdx = 0; symbolIdx < config.symbols.length; symbolIdx++) {
|
|
205
|
+
const currentCount = symbolCounts.get(symbolIdx) || 0;
|
|
206
|
+
const maxCount = limitations.get(symbolIdx) || happy_panda_config_1.GRID.POSITIONS;
|
|
207
|
+
if (currentCount >= maxCount) {
|
|
208
|
+
baseWeights[symbolIdx] = 0; // Disable this symbol
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Select symbol
|
|
212
|
+
const totalWeight = baseWeights.reduce((sum, w) => sum + w, 0);
|
|
213
|
+
if (totalWeight === 0) {
|
|
214
|
+
// Fallback: all symbols limited, pick any available
|
|
215
|
+
for (let symbolIdx = 0; symbolIdx < config.symbols.length; symbolIdx++) {
|
|
216
|
+
const currentCount = symbolCounts.get(symbolIdx) || 0;
|
|
217
|
+
const maxCount = limitations.get(symbolIdx) || happy_panda_config_1.GRID.POSITIONS;
|
|
218
|
+
if (currentCount < maxCount) {
|
|
219
|
+
// C++ line 1956-1957: Cherry pieces are POSITION-SPECIFIC
|
|
220
|
+
// main_screen[reel][row] = CH_00 + reel*3 + row;
|
|
221
|
+
const baseSymbol = config.symbols[symbolIdx];
|
|
222
|
+
if (baseSymbol === happy_panda_config_1.Symbol.CHERRY_00) {
|
|
223
|
+
grid[reel][row] = happy_panda_config_1.Symbol.CHERRY_00 + reel * 3 + row;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
grid[reel][row] = baseSymbol;
|
|
227
|
+
}
|
|
228
|
+
symbolCounts.set(symbolIdx, currentCount + 1);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
const symbolIdx = await (0, weighted_random_1.selectWeightedIndex)(baseWeights, rng);
|
|
235
|
+
// C++ line 1956-1957: Cherry pieces are POSITION-SPECIFIC
|
|
236
|
+
// main_screen[reel][row] = CH_00 + reel*3 + row;
|
|
237
|
+
const baseSymbol = config.symbols[symbolIdx];
|
|
238
|
+
if (baseSymbol === happy_panda_config_1.Symbol.CHERRY_00) {
|
|
239
|
+
grid[reel][row] = happy_panda_config_1.Symbol.CHERRY_00 + reel * 3 + row;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
grid[reel][row] = baseSymbol;
|
|
243
|
+
}
|
|
244
|
+
symbolCounts.set(symbolIdx, (symbolCounts.get(symbolIdx) || 0) + 1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return grid;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Create a grid filled with a single symbol (for full 3x3 wins).
|
|
251
|
+
* C++ line 1956-1957: Cherry pieces are POSITION-SPECIFIC.
|
|
252
|
+
*/
|
|
253
|
+
function createFilledGrid(symbol) {
|
|
254
|
+
// Cherry pieces need position-specific symbols
|
|
255
|
+
if (symbol === happy_panda_config_1.Symbol.CHERRY_00) {
|
|
256
|
+
return [
|
|
257
|
+
[happy_panda_config_1.Symbol.CHERRY_00, happy_panda_config_1.Symbol.CHERRY_01, happy_panda_config_1.Symbol.CHERRY_02],
|
|
258
|
+
[happy_panda_config_1.Symbol.CHERRY_10, happy_panda_config_1.Symbol.CHERRY_11, happy_panda_config_1.Symbol.CHERRY_12],
|
|
259
|
+
[happy_panda_config_1.Symbol.CHERRY_20, happy_panda_config_1.Symbol.CHERRY_21, happy_panda_config_1.Symbol.CHERRY_22],
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
return [
|
|
263
|
+
[symbol, symbol, symbol],
|
|
264
|
+
[symbol, symbol, symbol],
|
|
265
|
+
[symbol, symbol, symbol],
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Create a matrix grid with 7, 8, or 9 of the same symbol.
|
|
270
|
+
* C++ lines 103-118: When matrix < 9, other positions get a filler symbol.
|
|
271
|
+
* C++ line 1956-1957: Cherry pieces are POSITION-SPECIFIC.
|
|
272
|
+
*/
|
|
273
|
+
async function createMatrixGrid(matrixSymbol, matrixCount, config, gameDirection, rng, limitations) {
|
|
274
|
+
// Full 3x3
|
|
275
|
+
if (matrixCount === 9) {
|
|
276
|
+
return createFilledGrid(matrixSymbol);
|
|
277
|
+
}
|
|
278
|
+
// For 7 or 8, we need to fill some positions with a different symbol
|
|
279
|
+
// C++ uses idx=4 (or 3 if matrixSymbol is 4) as the filler
|
|
280
|
+
const matrixSymbolIdx = config.symbols.indexOf(matrixSymbol);
|
|
281
|
+
let fillerIdx = 4; // BA1 in most configs
|
|
282
|
+
if (fillerIdx === matrixSymbolIdx) {
|
|
283
|
+
fillerIdx = 3; // BA2 if matrixSymbol is BA1
|
|
284
|
+
}
|
|
285
|
+
const fillerSymbol = config.symbols[fillerIdx] || config.symbols[0];
|
|
286
|
+
// Create grid - fill with matrix symbol first (position-specific for cherry)
|
|
287
|
+
const isCherryMatrix = matrixSymbol === happy_panda_config_1.Symbol.CHERRY_00;
|
|
288
|
+
const grid = isCherryMatrix
|
|
289
|
+
? [
|
|
290
|
+
[happy_panda_config_1.Symbol.CHERRY_00, happy_panda_config_1.Symbol.CHERRY_01, happy_panda_config_1.Symbol.CHERRY_02],
|
|
291
|
+
[happy_panda_config_1.Symbol.CHERRY_10, happy_panda_config_1.Symbol.CHERRY_11, happy_panda_config_1.Symbol.CHERRY_12],
|
|
292
|
+
[happy_panda_config_1.Symbol.CHERRY_20, happy_panda_config_1.Symbol.CHERRY_21, happy_panda_config_1.Symbol.CHERRY_22],
|
|
293
|
+
]
|
|
294
|
+
: [
|
|
295
|
+
[matrixSymbol, matrixSymbol, matrixSymbol],
|
|
296
|
+
[matrixSymbol, matrixSymbol, matrixSymbol],
|
|
297
|
+
[matrixSymbol, matrixSymbol, matrixSymbol],
|
|
298
|
+
];
|
|
299
|
+
// Replace (9 - matrixCount) positions with filler
|
|
300
|
+
const positionsToReplace = 9 - matrixCount;
|
|
301
|
+
const positions = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
302
|
+
// Randomly select positions to replace
|
|
303
|
+
for (let i = 0; i < positionsToReplace; i++) {
|
|
304
|
+
const randIdx = await rng.random(positions.length);
|
|
305
|
+
const pos = positions.splice(randIdx, 1)[0];
|
|
306
|
+
const [reel, row] = positionToCoords(pos);
|
|
307
|
+
grid[reel][row] = fillerSymbol;
|
|
308
|
+
}
|
|
309
|
+
return grid;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Generate cherry piece grid for jackpot bonus.
|
|
313
|
+
* Each position gets the appropriate piece symbol.
|
|
314
|
+
*/
|
|
315
|
+
function generateCherryPieceGrid() {
|
|
316
|
+
return [
|
|
317
|
+
[happy_panda_config_1.Symbol.CHERRY_00, happy_panda_config_1.Symbol.CHERRY_01, happy_panda_config_1.Symbol.CHERRY_02],
|
|
318
|
+
[happy_panda_config_1.Symbol.CHERRY_10, happy_panda_config_1.Symbol.CHERRY_11, happy_panda_config_1.Symbol.CHERRY_12],
|
|
319
|
+
[happy_panda_config_1.Symbol.CHERRY_20, happy_panda_config_1.Symbol.CHERRY_21, happy_panda_config_1.Symbol.CHERRY_22],
|
|
320
|
+
];
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Check if grid is a complete cherry piece (jackpot).
|
|
324
|
+
*/
|
|
325
|
+
function isCompleteCherryPiece(grid) {
|
|
326
|
+
const cherryPieces = [
|
|
327
|
+
happy_panda_config_1.Symbol.CHERRY_00, happy_panda_config_1.Symbol.CHERRY_01, happy_panda_config_1.Symbol.CHERRY_02,
|
|
328
|
+
happy_panda_config_1.Symbol.CHERRY_10, happy_panda_config_1.Symbol.CHERRY_11, happy_panda_config_1.Symbol.CHERRY_12,
|
|
329
|
+
happy_panda_config_1.Symbol.CHERRY_20, happy_panda_config_1.Symbol.CHERRY_21, happy_panda_config_1.Symbol.CHERRY_22,
|
|
330
|
+
];
|
|
331
|
+
let foundPieces = 0;
|
|
332
|
+
for (let reel = 0; reel < happy_panda_config_1.GRID.REELS; reel++) {
|
|
333
|
+
for (let row = 0; row < happy_panda_config_1.GRID.ROWS; row++) {
|
|
334
|
+
if (cherryPieces.includes(grid[reel][row])) {
|
|
335
|
+
foundPieces++;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return foundPieces === 9;
|
|
340
|
+
}
|
|
341
|
+
//# 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,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,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omnitronix/happy-panda-game-engine",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Happy Panda Game Engine",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc && copyfiles -u 1 'src/**/*.csv' dist/",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"lint": "eslint src/**/*.ts",
|
|
12
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
13
|
+
"test": "cross-env RNG_CLIENT_MODE=local npm run build && jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
17
|
+
},
|
|
18
|
+
"author": "Omnitronix",
|
|
19
|
+
"license": "UNLICENSED",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@omnitronix/rng-client-core": "^1.0.9",
|
|
22
|
+
"class-transformer": "^0.5.1",
|
|
23
|
+
"class-validator": "^0.14.1",
|
|
24
|
+
"reflect-metadata": "^0.1.13",
|
|
25
|
+
"uuid": "^9.0.0",
|
|
26
|
+
"winston": "^3.17.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/jest": "^29.5.0",
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"@types/uuid": "^9.0.0",
|
|
32
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
33
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
34
|
+
"copyfiles": "^2.4.1",
|
|
35
|
+
"cross-env": "^10.1.0",
|
|
36
|
+
"eslint": "^8.0.0",
|
|
37
|
+
"jest": "^29.5.0",
|
|
38
|
+
"nodemon": "^3.0.0",
|
|
39
|
+
"ts-jest": "^29.1.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist/**/*",
|
|
44
|
+
"README.md"
|
|
45
|
+
],
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"registry": "https://registry.npmjs.org/"
|
|
48
|
+
}
|
|
49
|
+
}
|