@omnitronix/happy-panda-game-engine 0.0.5 → 0.0.6
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 +722 -676
- package/dist/__tests__/cheat-trigger-bonus.test.d.ts +8 -0
- package/dist/__tests__/cheat-trigger-bonus.test.d.ts.map +1 -0
- package/dist/__tests__/cheat-trigger-bonus.test.js +336 -0
- package/dist/__tests__/cheat-trigger-bonus.test.js.map +1 -0
- package/dist/domain/cheat-trigger-bonus-command.d.ts +17 -0
- package/dist/domain/cheat-trigger-bonus-command.d.ts.map +1 -0
- package/dist/domain/cheat-trigger-bonus-command.js +3 -0
- package/dist/domain/cheat-trigger-bonus-command.js.map +1 -0
- package/dist/domain/types.d.ts +2 -1
- package/dist/domain/types.d.ts.map +1 -1
- package/dist/domain/types.js +1 -0
- package/dist/domain/types.js.map +1 -1
- package/dist/happy-panda-v1.game-engine.d.ts.map +1 -1
- package/dist/happy-panda-v1.game-engine.js +75 -0
- package/dist/happy-panda-v1.game-engine.js.map +1 -1
- package/package.json +49 -49
package/README.md
CHANGED
|
@@ -1,676 +1,722 @@
|
|
|
1
|
-
# Happy Panda Game Engine
|
|
2
|
-
|
|
3
|
-

|
|
4
|
-

|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
Pure business logic library for the Happy Panda (Cherry Master) slot game. A framework-agnostic, TypeScript-based game engine that handles all core game mechanics, bonus systems, and jackpot logic. Achieves C++ parity with the original `CherryMaster_A_2.cpp` implementation.
|
|
8
|
-
|
|
9
|
-
## What is Happy Panda Game Engine?
|
|
10
|
-
|
|
11
|
-
The Happy Panda Game Engine is an isolated, framework-agnostic library containing pure business logic for the Happy Panda slot game. It implements a command-based architecture that separates game logic from presentation and infrastructure concerns.
|
|
12
|
-
|
|
13
|
-
### Core Characteristics
|
|
14
|
-
|
|
15
|
-
- **Pure Business Logic**: Contains only game rules, calculations, and state transitions
|
|
16
|
-
- **Framework Agnostic**: No dependencies on specific UI frameworks or web servers
|
|
17
|
-
- **Command-Based Architecture**: All interactions through well-defined commands
|
|
18
|
-
- **State Separation**: Distinguishes between public state (visible to players) and private state (server-side only)
|
|
19
|
-
- **RNG Integration**: Supports pluggable RNG providers for testing and production
|
|
20
|
-
- **Audit Trail**: Complete RNG outcome tracking for compliance and debugging
|
|
21
|
-
- **C++ Parity**: Verified against original C++ implementation with 100M spin validation
|
|
22
|
-
|
|
23
|
-
### Game Specifications
|
|
24
|
-
|
|
25
|
-
- **RTP**: 96.05% (verified at 95.91% over 100M spins, within tolerance)
|
|
26
|
-
- **Game Type**: Classic 3x3 Slot
|
|
27
|
-
- **Version**: 1.0.0
|
|
28
|
-
- **Layout**: 3 reels x 3 rows
|
|
29
|
-
- **Win Evaluation**: 8/16 bidirectional paylines + wall wins + scatter wins
|
|
30
|
-
- **Modes**: 8-line (SINGLE) and 16-line (BOTH)
|
|
31
|
-
|
|
32
|
-
## RTP Validation Results
|
|
33
|
-
|
|
34
|
-
### 100 Million Spin Verification
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
================================================================
|
|
38
|
-
HAPPY PANDA RTP VERIFICATION - 100 MILLION SPINS
|
|
39
|
-
================================================================
|
|
40
|
-
|
|
41
|
-
Configuration:
|
|
42
|
-
- Game Direction: SINGLE (8 lines)
|
|
43
|
-
- Bet per spin: 8
|
|
44
|
-
- Seeds: 10 x 10M = 100M total paid spins
|
|
45
|
-
- C++ Target RTP: 96.05%
|
|
46
|
-
|
|
47
|
-
RTP Breakdown by Spin Type:
|
|
48
|
-
----------------------------------------------------------------
|
|
49
|
-
PAID_SPIN : 62.54% | C++: 63.51% | Diff: -0.97%
|
|
50
|
-
BONUS_JACKPOT : 16.24% | C++: 13.56% | Diff: +2.68%
|
|
51
|
-
BONUS_CHERRY : 3.96% | C++: 3.67% | Diff: +0.29%
|
|
52
|
-
BONUS_BELL : 5.06% | C++: 5.83% | Diff: -0.77%
|
|
53
|
-
BONUS_BAR1 : 5.47% | C++: 6.31% | Diff: -0.84%
|
|
54
|
-
RESPIN_CHERRY : 2.64% | C++: 3.18% | Diff: -0.54%
|
|
55
|
-
----------------------------------------------------------------
|
|
56
|
-
TOTAL : 95.91% | C++: 96.05%
|
|
57
|
-
|
|
58
|
-
Statistics:
|
|
59
|
-
- Standard Deviation: 0.32%
|
|
60
|
-
- Range: 95.54% - 96.40%
|
|
61
|
-
|
|
62
|
-
STATUS: PASS - RTP within 0.15% tolerance
|
|
63
|
-
================================================================
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
| Metric | TypeScript | C++ Target | Difference | Status |
|
|
67
|
-
|--------|------------|------------|------------|--------|
|
|
68
|
-
| **RTP (100M spins)** | 95.91% | 96.05% | -0.14% | **PASS** |
|
|
69
|
-
| PAID_SPIN | 62.54% | 63.51% | -0.97% | Match |
|
|
70
|
-
| BONUS_JACKPOT | 16.24% | 13.56% | +2.68% | Match |
|
|
71
|
-
| BONUS_CHERRY | 3.96% | 3.67% | +0.29% | Match |
|
|
72
|
-
| BONUS_BELL | 5.06% | 5.83% | -0.77% | Match |
|
|
73
|
-
| BONUS_BAR1 | 5.47% | 6.31% | -0.84% | Match |
|
|
74
|
-
| RESPIN_CHERRY | 2.64% | 3.18% | -0.54% | Match |
|
|
75
|
-
|
|
76
|
-
**No configuration values from XLSX were modified** - only calculation logic was adjusted to match C++ behavior.
|
|
77
|
-
|
|
78
|
-
## Features
|
|
79
|
-
|
|
80
|
-
- **Command-Based Architecture**: 3 different command types for all game operations
|
|
81
|
-
- **State Management**: Public/private state separation with type safety
|
|
82
|
-
- **RNG Integration**: Pluggable RNG providers with audit trail support
|
|
83
|
-
- **Bidirectional Paylines**: 8 or 16 paylines with left-to-right and right-to-left evaluation
|
|
84
|
-
- **Wall Wins**: Full 3x3 matrix wins with multiple types (pure, mixed, fruits, colors)
|
|
85
|
-
- **Scatter Wins**: Seven symbols pay anywhere on screen
|
|
86
|
-
- **Bonus System**: 5 distinct bonus types with counter-based triggers
|
|
87
|
-
- **Jackpot System**: Progressive and pool jackpots
|
|
88
|
-
- **TypeScript Support**: Complete type definitions for all interfaces
|
|
89
|
-
- **Comprehensive Testing**: Full test suite with Jest and 100M spin RTP validation
|
|
90
|
-
|
|
91
|
-
## Architecture Overview
|
|
92
|
-
|
|
93
|
-
The game engine follows a modular architecture with clear separation of concerns:
|
|
94
|
-
|
|
95
|
-
### Core Components
|
|
96
|
-
|
|
97
|
-
- **Game Engine** (`HappyPandaEngine`): Main entry point and command processor
|
|
98
|
-
- **V1 Wrapper** (`HappyPandaV1GameEngine`): Service integration wrapper with GameEngine interface
|
|
99
|
-
- **Spin Generator**: Grid generation with weighted random selection
|
|
100
|
-
- **Win Evaluator**: Line, wall, scatter, and special win detection
|
|
101
|
-
- **Counter Manager**: Bonus counter management and trigger logic
|
|
102
|
-
- **Jackpot Manager**: Progressive and pool jackpot handling
|
|
103
|
-
|
|
104
|
-
### Directory Structure
|
|
105
|
-
|
|
106
|
-
```
|
|
107
|
-
src/
|
|
108
|
-
├── happy-panda-v1.game-engine.ts # V1 service wrapper
|
|
109
|
-
├── engine/
|
|
110
|
-
│ └── happy-panda-engine.ts # Main engine class
|
|
111
|
-
├── config/
|
|
112
|
-
│ └── happy-panda.config.ts # All game configuration (XLSX values)
|
|
113
|
-
├── rng/
|
|
114
|
-
│ ├── spin-generator.ts # Grid generation with C++ parity
|
|
115
|
-
│ └── weighted-random.ts # Weighted random selection
|
|
116
|
-
├── logic/
|
|
117
|
-
│ ├── handlers/
|
|
118
|
-
│ │ └── spin-handler.ts # Spin orchestration
|
|
119
|
-
│ └── services/
|
|
120
|
-
│ ├── win-evaluator.ts # Win detection (line/wall/scatter)
|
|
121
|
-
│ ├── counter-manager.ts # Bonus counter management
|
|
122
|
-
│ └── jackpot-manager.ts # Jackpot handling
|
|
123
|
-
├── domain/
|
|
124
|
-
│ └── types.ts # Type definitions
|
|
125
|
-
└── __tests__/
|
|
126
|
-
├── rtp-simulation.test.ts # RTP validation
|
|
127
|
-
├── rtp-diagnostic.test.ts # RTP breakdown by spin type
|
|
128
|
-
├── cpp-parity.test.ts # C++ parity tests
|
|
129
|
-
└── win-evaluator.test.ts # Win evaluation tests
|
|
130
|
-
|
|
131
|
-
docs/
|
|
132
|
-
├── RTP-MATCHING.md # RTP implementation details
|
|
133
|
-
└── TEST-PROTOCOL-RTP-100M.md # 100M spin test protocol
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Installation
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
npm install @omnitronix/happy-panda-game-engine
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
> **Note**: This is a private package for Omnitronix internal use only (UNLICENSED).
|
|
143
|
-
|
|
144
|
-
## Quick Start
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
import { HappyPandaV1GameEngine } from '@omnitronix/happy-panda-game-engine';
|
|
148
|
-
|
|
149
|
-
// Initialize game engine
|
|
150
|
-
const gameEngine = new HappyPandaV1GameEngine();
|
|
151
|
-
|
|
152
|
-
// Get engine info
|
|
153
|
-
const info = gameEngine.getGameEngineInfo();
|
|
154
|
-
// { gameCode: 'happy-panda', version: '1.0.0', rtp: 96.05, gameType: 'slot', gameName: 'Happy Panda', provider: 'Omnitronix' }
|
|
155
|
-
|
|
156
|
-
// Initialize session
|
|
157
|
-
const initCommand = {
|
|
158
|
-
id: 'cmd-init-123',
|
|
159
|
-
type: 'INIT_SESSION_STATE',
|
|
160
|
-
payload: {
|
|
161
|
-
gameDirection: 0, // 0 = SINGLE (8 lines), 1 = BOTH (16 lines)
|
|
162
|
-
betStake: 1
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const initResult = await gameEngine.processCommand(null, null, initCommand);
|
|
167
|
-
// Returns: { success: true, publicState, privateState, outcome, rngOutcome }
|
|
168
|
-
|
|
169
|
-
// Process spin
|
|
170
|
-
const spinCommand = {
|
|
171
|
-
id: 'cmd-spin-456',
|
|
172
|
-
type: 'SPIN',
|
|
173
|
-
payload: {}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const spinResult = await gameEngine.processCommand(
|
|
177
|
-
initResult.publicState,
|
|
178
|
-
initResult.privateState,
|
|
179
|
-
spinCommand
|
|
180
|
-
);
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## API Reference
|
|
184
|
-
|
|
185
|
-
### Main Class: `HappyPandaV1GameEngine`
|
|
186
|
-
|
|
187
|
-
Implements the standard `GameEngine` interface for integration with game-engine-service.
|
|
188
|
-
|
|
189
|
-
#### Constructor
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
new HappyPandaV1GameEngine()
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
- Initializes game engine with tracked RNG provider
|
|
196
|
-
- Ready to process commands immediately
|
|
197
|
-
|
|
198
|
-
#### Methods
|
|
199
|
-
|
|
200
|
-
**`getGameEngineInfo(): GameEngineInfo`**
|
|
201
|
-
Returns game metadata including code, version, RTP, and provider.
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
{
|
|
205
|
-
gameCode: 'happy-panda',
|
|
206
|
-
version: '1.0.0',
|
|
207
|
-
rtp: 96.05,
|
|
208
|
-
gameType: 'slot',
|
|
209
|
-
gameName: 'Happy Panda',
|
|
210
|
-
provider: 'Omnitronix'
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**`processCommand(publicState, privateState, command): Promise<CommandProcessingResult>`**
|
|
215
|
-
Main command processor that handles all game operations.
|
|
216
|
-
|
|
217
|
-
### Commands
|
|
218
|
-
|
|
219
|
-
The game engine supports 3 different command types:
|
|
220
|
-
|
|
221
|
-
#### 1. INIT_SESSION_STATE
|
|
222
|
-
|
|
223
|
-
**Purpose**: Initialize game session state
|
|
224
|
-
|
|
225
|
-
**Payload**:
|
|
226
|
-
```typescript
|
|
227
|
-
{
|
|
228
|
-
gameDirection?: number; // 0 = SINGLE (8 lines), 1 = BOTH (16 lines). Default: 0
|
|
229
|
-
betStake?: number; // Bet multiplier. Default: 1
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
**Returns**: Initial public and private state with default values
|
|
234
|
-
|
|
235
|
-
#### 2. SPIN
|
|
236
|
-
|
|
237
|
-
**Purpose**: Execute a spin
|
|
238
|
-
|
|
239
|
-
**Payload**: None required (uses current state)
|
|
240
|
-
|
|
241
|
-
**Returns**: Spin result with grid, wins, and state updates
|
|
242
|
-
|
|
243
|
-
**Result Structure**:
|
|
244
|
-
```typescript
|
|
245
|
-
{
|
|
246
|
-
success: true,
|
|
247
|
-
publicState: PublicState,
|
|
248
|
-
privateState: PrivateState,
|
|
249
|
-
outcome: {
|
|
250
|
-
sessionId: string,
|
|
251
|
-
grid: Symbol[][], // 3x3 grid
|
|
252
|
-
wins: SpinWinResult, // All wins
|
|
253
|
-
state: PublicState,
|
|
254
|
-
jackpotWon: number, // Jackpot payout (if any)
|
|
255
|
-
poolJackpotWon: number, // Pool jackpot payout (if any)
|
|
256
|
-
bonusTriggered: SpinType | null,
|
|
257
|
-
isBonusComplete: boolean
|
|
258
|
-
},
|
|
259
|
-
rngOutcome: RngOutcome // Audit trail
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
#### 3. GET_SYMBOLS
|
|
264
|
-
|
|
265
|
-
**Purpose**: Retrieve symbol definitions
|
|
266
|
-
|
|
267
|
-
**Payload**: None
|
|
268
|
-
|
|
269
|
-
**Returns**: Array of symbol definitions with names and values
|
|
270
|
-
|
|
271
|
-
###
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
####
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
|
369
|
-
|
|
370
|
-
|
|
|
371
|
-
|
|
|
372
|
-
|
|
|
373
|
-
|
|
|
374
|
-
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Example
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1
|
+
# Happy Panda Game Engine
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Pure business logic library for the Happy Panda (Cherry Master) slot game. A framework-agnostic, TypeScript-based game engine that handles all core game mechanics, bonus systems, and jackpot logic. Achieves C++ parity with the original `CherryMaster_A_2.cpp` implementation.
|
|
8
|
+
|
|
9
|
+
## What is Happy Panda Game Engine?
|
|
10
|
+
|
|
11
|
+
The Happy Panda Game Engine is an isolated, framework-agnostic library containing pure business logic for the Happy Panda slot game. It implements a command-based architecture that separates game logic from presentation and infrastructure concerns.
|
|
12
|
+
|
|
13
|
+
### Core Characteristics
|
|
14
|
+
|
|
15
|
+
- **Pure Business Logic**: Contains only game rules, calculations, and state transitions
|
|
16
|
+
- **Framework Agnostic**: No dependencies on specific UI frameworks or web servers
|
|
17
|
+
- **Command-Based Architecture**: All interactions through well-defined commands
|
|
18
|
+
- **State Separation**: Distinguishes between public state (visible to players) and private state (server-side only)
|
|
19
|
+
- **RNG Integration**: Supports pluggable RNG providers for testing and production
|
|
20
|
+
- **Audit Trail**: Complete RNG outcome tracking for compliance and debugging
|
|
21
|
+
- **C++ Parity**: Verified against original C++ implementation with 100M spin validation
|
|
22
|
+
|
|
23
|
+
### Game Specifications
|
|
24
|
+
|
|
25
|
+
- **RTP**: 96.05% (verified at 95.91% over 100M spins, within tolerance)
|
|
26
|
+
- **Game Type**: Classic 3x3 Slot
|
|
27
|
+
- **Version**: 1.0.0
|
|
28
|
+
- **Layout**: 3 reels x 3 rows
|
|
29
|
+
- **Win Evaluation**: 8/16 bidirectional paylines + wall wins + scatter wins
|
|
30
|
+
- **Modes**: 8-line (SINGLE) and 16-line (BOTH)
|
|
31
|
+
|
|
32
|
+
## RTP Validation Results
|
|
33
|
+
|
|
34
|
+
### 100 Million Spin Verification
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
================================================================
|
|
38
|
+
HAPPY PANDA RTP VERIFICATION - 100 MILLION SPINS
|
|
39
|
+
================================================================
|
|
40
|
+
|
|
41
|
+
Configuration:
|
|
42
|
+
- Game Direction: SINGLE (8 lines)
|
|
43
|
+
- Bet per spin: 8
|
|
44
|
+
- Seeds: 10 x 10M = 100M total paid spins
|
|
45
|
+
- C++ Target RTP: 96.05%
|
|
46
|
+
|
|
47
|
+
RTP Breakdown by Spin Type:
|
|
48
|
+
----------------------------------------------------------------
|
|
49
|
+
PAID_SPIN : 62.54% | C++: 63.51% | Diff: -0.97%
|
|
50
|
+
BONUS_JACKPOT : 16.24% | C++: 13.56% | Diff: +2.68%
|
|
51
|
+
BONUS_CHERRY : 3.96% | C++: 3.67% | Diff: +0.29%
|
|
52
|
+
BONUS_BELL : 5.06% | C++: 5.83% | Diff: -0.77%
|
|
53
|
+
BONUS_BAR1 : 5.47% | C++: 6.31% | Diff: -0.84%
|
|
54
|
+
RESPIN_CHERRY : 2.64% | C++: 3.18% | Diff: -0.54%
|
|
55
|
+
----------------------------------------------------------------
|
|
56
|
+
TOTAL : 95.91% | C++: 96.05%
|
|
57
|
+
|
|
58
|
+
Statistics:
|
|
59
|
+
- Standard Deviation: 0.32%
|
|
60
|
+
- Range: 95.54% - 96.40%
|
|
61
|
+
|
|
62
|
+
STATUS: PASS - RTP within 0.15% tolerance
|
|
63
|
+
================================================================
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Metric | TypeScript | C++ Target | Difference | Status |
|
|
67
|
+
|--------|------------|------------|------------|--------|
|
|
68
|
+
| **RTP (100M spins)** | 95.91% | 96.05% | -0.14% | **PASS** |
|
|
69
|
+
| PAID_SPIN | 62.54% | 63.51% | -0.97% | Match |
|
|
70
|
+
| BONUS_JACKPOT | 16.24% | 13.56% | +2.68% | Match |
|
|
71
|
+
| BONUS_CHERRY | 3.96% | 3.67% | +0.29% | Match |
|
|
72
|
+
| BONUS_BELL | 5.06% | 5.83% | -0.77% | Match |
|
|
73
|
+
| BONUS_BAR1 | 5.47% | 6.31% | -0.84% | Match |
|
|
74
|
+
| RESPIN_CHERRY | 2.64% | 3.18% | -0.54% | Match |
|
|
75
|
+
|
|
76
|
+
**No configuration values from XLSX were modified** - only calculation logic was adjusted to match C++ behavior.
|
|
77
|
+
|
|
78
|
+
## Features
|
|
79
|
+
|
|
80
|
+
- **Command-Based Architecture**: 3 different command types for all game operations
|
|
81
|
+
- **State Management**: Public/private state separation with type safety
|
|
82
|
+
- **RNG Integration**: Pluggable RNG providers with audit trail support
|
|
83
|
+
- **Bidirectional Paylines**: 8 or 16 paylines with left-to-right and right-to-left evaluation
|
|
84
|
+
- **Wall Wins**: Full 3x3 matrix wins with multiple types (pure, mixed, fruits, colors)
|
|
85
|
+
- **Scatter Wins**: Seven symbols pay anywhere on screen
|
|
86
|
+
- **Bonus System**: 5 distinct bonus types with counter-based triggers
|
|
87
|
+
- **Jackpot System**: Progressive and pool jackpots
|
|
88
|
+
- **TypeScript Support**: Complete type definitions for all interfaces
|
|
89
|
+
- **Comprehensive Testing**: Full test suite with Jest and 100M spin RTP validation
|
|
90
|
+
|
|
91
|
+
## Architecture Overview
|
|
92
|
+
|
|
93
|
+
The game engine follows a modular architecture with clear separation of concerns:
|
|
94
|
+
|
|
95
|
+
### Core Components
|
|
96
|
+
|
|
97
|
+
- **Game Engine** (`HappyPandaEngine`): Main entry point and command processor
|
|
98
|
+
- **V1 Wrapper** (`HappyPandaV1GameEngine`): Service integration wrapper with GameEngine interface
|
|
99
|
+
- **Spin Generator**: Grid generation with weighted random selection
|
|
100
|
+
- **Win Evaluator**: Line, wall, scatter, and special win detection
|
|
101
|
+
- **Counter Manager**: Bonus counter management and trigger logic
|
|
102
|
+
- **Jackpot Manager**: Progressive and pool jackpot handling
|
|
103
|
+
|
|
104
|
+
### Directory Structure
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
src/
|
|
108
|
+
├── happy-panda-v1.game-engine.ts # V1 service wrapper
|
|
109
|
+
├── engine/
|
|
110
|
+
│ └── happy-panda-engine.ts # Main engine class
|
|
111
|
+
├── config/
|
|
112
|
+
│ └── happy-panda.config.ts # All game configuration (XLSX values)
|
|
113
|
+
├── rng/
|
|
114
|
+
│ ├── spin-generator.ts # Grid generation with C++ parity
|
|
115
|
+
│ └── weighted-random.ts # Weighted random selection
|
|
116
|
+
├── logic/
|
|
117
|
+
│ ├── handlers/
|
|
118
|
+
│ │ └── spin-handler.ts # Spin orchestration
|
|
119
|
+
│ └── services/
|
|
120
|
+
│ ├── win-evaluator.ts # Win detection (line/wall/scatter)
|
|
121
|
+
│ ├── counter-manager.ts # Bonus counter management
|
|
122
|
+
│ └── jackpot-manager.ts # Jackpot handling
|
|
123
|
+
├── domain/
|
|
124
|
+
│ └── types.ts # Type definitions
|
|
125
|
+
└── __tests__/
|
|
126
|
+
├── rtp-simulation.test.ts # RTP validation
|
|
127
|
+
├── rtp-diagnostic.test.ts # RTP breakdown by spin type
|
|
128
|
+
├── cpp-parity.test.ts # C++ parity tests
|
|
129
|
+
└── win-evaluator.test.ts # Win evaluation tests
|
|
130
|
+
|
|
131
|
+
docs/
|
|
132
|
+
├── RTP-MATCHING.md # RTP implementation details
|
|
133
|
+
└── TEST-PROTOCOL-RTP-100M.md # 100M spin test protocol
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Installation
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm install @omnitronix/happy-panda-game-engine
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> **Note**: This is a private package for Omnitronix internal use only (UNLICENSED).
|
|
143
|
+
|
|
144
|
+
## Quick Start
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { HappyPandaV1GameEngine } from '@omnitronix/happy-panda-game-engine';
|
|
148
|
+
|
|
149
|
+
// Initialize game engine
|
|
150
|
+
const gameEngine = new HappyPandaV1GameEngine();
|
|
151
|
+
|
|
152
|
+
// Get engine info
|
|
153
|
+
const info = gameEngine.getGameEngineInfo();
|
|
154
|
+
// { gameCode: 'happy-panda', version: '1.0.0', rtp: 96.05, gameType: 'slot', gameName: 'Happy Panda', provider: 'Omnitronix' }
|
|
155
|
+
|
|
156
|
+
// Initialize session
|
|
157
|
+
const initCommand = {
|
|
158
|
+
id: 'cmd-init-123',
|
|
159
|
+
type: 'INIT_SESSION_STATE',
|
|
160
|
+
payload: {
|
|
161
|
+
gameDirection: 0, // 0 = SINGLE (8 lines), 1 = BOTH (16 lines)
|
|
162
|
+
betStake: 1
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const initResult = await gameEngine.processCommand(null, null, initCommand);
|
|
167
|
+
// Returns: { success: true, publicState, privateState, outcome, rngOutcome }
|
|
168
|
+
|
|
169
|
+
// Process spin
|
|
170
|
+
const spinCommand = {
|
|
171
|
+
id: 'cmd-spin-456',
|
|
172
|
+
type: 'SPIN',
|
|
173
|
+
payload: {}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const spinResult = await gameEngine.processCommand(
|
|
177
|
+
initResult.publicState,
|
|
178
|
+
initResult.privateState,
|
|
179
|
+
spinCommand
|
|
180
|
+
);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## API Reference
|
|
184
|
+
|
|
185
|
+
### Main Class: `HappyPandaV1GameEngine`
|
|
186
|
+
|
|
187
|
+
Implements the standard `GameEngine` interface for integration with game-engine-service.
|
|
188
|
+
|
|
189
|
+
#### Constructor
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
new HappyPandaV1GameEngine()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- Initializes game engine with tracked RNG provider
|
|
196
|
+
- Ready to process commands immediately
|
|
197
|
+
|
|
198
|
+
#### Methods
|
|
199
|
+
|
|
200
|
+
**`getGameEngineInfo(): GameEngineInfo`**
|
|
201
|
+
Returns game metadata including code, version, RTP, and provider.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
{
|
|
205
|
+
gameCode: 'happy-panda',
|
|
206
|
+
version: '1.0.0',
|
|
207
|
+
rtp: 96.05,
|
|
208
|
+
gameType: 'slot',
|
|
209
|
+
gameName: 'Happy Panda',
|
|
210
|
+
provider: 'Omnitronix'
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**`processCommand(publicState, privateState, command): Promise<CommandProcessingResult>`**
|
|
215
|
+
Main command processor that handles all game operations.
|
|
216
|
+
|
|
217
|
+
### Commands
|
|
218
|
+
|
|
219
|
+
The game engine supports 3 different command types:
|
|
220
|
+
|
|
221
|
+
#### 1. INIT_SESSION_STATE
|
|
222
|
+
|
|
223
|
+
**Purpose**: Initialize game session state
|
|
224
|
+
|
|
225
|
+
**Payload**:
|
|
226
|
+
```typescript
|
|
227
|
+
{
|
|
228
|
+
gameDirection?: number; // 0 = SINGLE (8 lines), 1 = BOTH (16 lines). Default: 0
|
|
229
|
+
betStake?: number; // Bet multiplier. Default: 1
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Returns**: Initial public and private state with default values
|
|
234
|
+
|
|
235
|
+
#### 2. SPIN
|
|
236
|
+
|
|
237
|
+
**Purpose**: Execute a spin
|
|
238
|
+
|
|
239
|
+
**Payload**: None required (uses current state)
|
|
240
|
+
|
|
241
|
+
**Returns**: Spin result with grid, wins, and state updates
|
|
242
|
+
|
|
243
|
+
**Result Structure**:
|
|
244
|
+
```typescript
|
|
245
|
+
{
|
|
246
|
+
success: true,
|
|
247
|
+
publicState: PublicState,
|
|
248
|
+
privateState: PrivateState,
|
|
249
|
+
outcome: {
|
|
250
|
+
sessionId: string,
|
|
251
|
+
grid: Symbol[][], // 3x3 grid
|
|
252
|
+
wins: SpinWinResult, // All wins
|
|
253
|
+
state: PublicState,
|
|
254
|
+
jackpotWon: number, // Jackpot payout (if any)
|
|
255
|
+
poolJackpotWon: number, // Pool jackpot payout (if any)
|
|
256
|
+
bonusTriggered: SpinType | null,
|
|
257
|
+
isBonusComplete: boolean
|
|
258
|
+
},
|
|
259
|
+
rngOutcome: RngOutcome // Audit trail
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### 3. GET_SYMBOLS
|
|
264
|
+
|
|
265
|
+
**Purpose**: Retrieve symbol definitions
|
|
266
|
+
|
|
267
|
+
**Payload**: None
|
|
268
|
+
|
|
269
|
+
**Returns**: Array of symbol definitions with names and values
|
|
270
|
+
|
|
271
|
+
### Debug/Cheat Commands (Testing/Admin)
|
|
272
|
+
|
|
273
|
+
The engine supports debug commands for QA testing via the dev-tools service. These commands are **blocked in REAL_MONEY mode**.
|
|
274
|
+
|
|
275
|
+
#### CHEAT_TRIGGER_BONUS
|
|
276
|
+
|
|
277
|
+
Force trigger a specific bonus round for testing.
|
|
278
|
+
|
|
279
|
+
**Payload**:
|
|
280
|
+
```typescript
|
|
281
|
+
interface CheatTriggerBonusCommand {
|
|
282
|
+
sessionId: string;
|
|
283
|
+
bonusType: 'BONUS_JACKPOT' | 'BONUS_CHERRY' | 'BONUS_BELL' | 'BONUS_BAR1' | 'RESPIN_CHERRY';
|
|
284
|
+
betAmount: number;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Usage**:
|
|
289
|
+
```typescript
|
|
290
|
+
const result = await engine.processCommand(
|
|
291
|
+
publicState,
|
|
292
|
+
privateState,
|
|
293
|
+
{
|
|
294
|
+
id: 'cheat-cmd-1',
|
|
295
|
+
type: 'CHEAT_TRIGGER_BONUS',
|
|
296
|
+
payload: {
|
|
297
|
+
sessionId: 'session-123',
|
|
298
|
+
bonusType: 'BONUS_JACKPOT', // or any other bonus type
|
|
299
|
+
betAmount: 8,
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Bonus Types**:
|
|
306
|
+
|
|
307
|
+
| Type | Description |
|
|
308
|
+
|------|-------------|
|
|
309
|
+
| `BONUS_JACKPOT` | Triggers 3-spin Jackpot bonus with cherry pieces grid and progressive jackpot chance |
|
|
310
|
+
| `BONUS_CHERRY` | Triggers Cherry free spins bonus with cherry respin feature |
|
|
311
|
+
| `BONUS_BELL` | Triggers 5-spin Bell bonus with bell scatter pays + pool jackpot |
|
|
312
|
+
| `BONUS_BAR1` | Triggers 7-spin Bar1 bonus with Super Bar scatter pays |
|
|
313
|
+
| `RESPIN_CHERRY` | Triggers center cherry respin (1-2 corner respins) |
|
|
314
|
+
|
|
315
|
+
**Returns**: Updated state with bonus pending. Execute `SPIN` to begin the bonus sequence.
|
|
316
|
+
|
|
317
|
+
### State Types
|
|
318
|
+
|
|
319
|
+
#### PublicState (visible to player)
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
{
|
|
323
|
+
gameDirection: GameDirection; // 0 = SINGLE, 1 = BOTH
|
|
324
|
+
betStake: number; // Bet multiplier
|
|
325
|
+
betGame: number; // Total bet (8 or 16 x betStake)
|
|
326
|
+
currentSpinType: SpinType; // Current spin type
|
|
327
|
+
spinsRemaining: number; // Remaining bonus spins
|
|
328
|
+
grid: Symbol[][]; // Current 3x3 grid
|
|
329
|
+
bonusJackpotValue: number; // Progressive jackpot
|
|
330
|
+
poolJackpotValue: number; // Pool jackpot
|
|
331
|
+
hasPendingBonus: boolean; // Indicates pending bonus
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### PrivateState (server-side only)
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
{
|
|
339
|
+
counters: BonusCounters; // Bonus trigger counters
|
|
340
|
+
pendingBonuses: PendingBonuses; // Queued bonuses
|
|
341
|
+
nextSpinType: SpinType; // Next spin type
|
|
342
|
+
accumulatedBonusWins: number; // Bonus sequence wins
|
|
343
|
+
centerCherrySymbol: Symbol | null; // For respin feature
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### CommandProcessingResult
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
{
|
|
351
|
+
success: boolean;
|
|
352
|
+
publicState: PublicState;
|
|
353
|
+
privateState: PrivateState;
|
|
354
|
+
outcome?: SpinResponse;
|
|
355
|
+
message?: string;
|
|
356
|
+
rngOutcome?: RngOutcome; // RNG audit trail
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Game Mechanics
|
|
361
|
+
|
|
362
|
+
### Symbol Set
|
|
363
|
+
|
|
364
|
+
| ID | Symbol | Description |
|
|
365
|
+
|----|--------|-------------|
|
|
366
|
+
| 0 | SEV_S | Super Seven (scatter, doubled payout when solo) |
|
|
367
|
+
| 1 | SEV | Seven (scatter) |
|
|
368
|
+
| 2 | BA3 | Bar 3 |
|
|
369
|
+
| 3 | BA2 | Bar 2 |
|
|
370
|
+
| 4 | BA1 | Bar 1 |
|
|
371
|
+
| 5 | ME | Melon |
|
|
372
|
+
| 6 | BE | Bell |
|
|
373
|
+
| 7 | PR | Prune |
|
|
374
|
+
| 8 | OR | Orange |
|
|
375
|
+
| 9 | CH | Cherry |
|
|
376
|
+
| 10 | CH_S | Cherry Special |
|
|
377
|
+
| 11 | BA_S | Super Bar |
|
|
378
|
+
|
|
379
|
+
### Win Types
|
|
380
|
+
|
|
381
|
+
#### 1. Line Wins
|
|
382
|
+
|
|
383
|
+
- 8 paylines (SINGLE mode) or 16 paylines (BOTH mode)
|
|
384
|
+
- Evaluated left-to-right and right-to-left in 16-line mode
|
|
385
|
+
- Cherry pays on 1, 2, or 3 matches
|
|
386
|
+
- Line shapes follow classic 3x3 patterns
|
|
387
|
+
|
|
388
|
+
#### 2. Wall Wins (Matrix/Screen Wins)
|
|
389
|
+
|
|
390
|
+
- Full 3x3 of same symbol
|
|
391
|
+
- Special mixed types:
|
|
392
|
+
- **Cherry Mix**: CH + CH_S combinations
|
|
393
|
+
- **Any Bar Mix**: BA1 + BA2 + BA3 + BA_S
|
|
394
|
+
- **All Fruits**: ME + BE + PR + OR
|
|
395
|
+
- **All Colors**: Mixed symbol colors
|
|
396
|
+
- **Important**: Wall wins suppress line wins when active
|
|
397
|
+
|
|
398
|
+
#### 3. Scatter Wins (Seven)
|
|
399
|
+
|
|
400
|
+
- 2-9 Seven symbols anywhere on screen
|
|
401
|
+
- Super Seven only = doubled payout
|
|
402
|
+
- **Important**: Scatter wins suppress line wins when active
|
|
403
|
+
|
|
404
|
+
#### 4. Special Wins (Bonus modes only)
|
|
405
|
+
|
|
406
|
+
- **Cherry Pieces**: Count of cherry pieces (Jackpot Bonus)
|
|
407
|
+
- **Bell Scatter**: Bell count anywhere (Bell Bonus)
|
|
408
|
+
- **Super Bar Scatter**: Super Bar count anywhere (Bar1 Bonus)
|
|
409
|
+
|
|
410
|
+
### Bonus System
|
|
411
|
+
|
|
412
|
+
Five distinct bonus types with counter-based triggers:
|
|
413
|
+
|
|
414
|
+
| Bonus | Trigger | Spins | Description |
|
|
415
|
+
|-------|---------|-------|-------------|
|
|
416
|
+
| **Jackpot** | 3x Cherry on first 8 lines | 3 | Cherry pieces with progressive jackpot chance |
|
|
417
|
+
| **Cherry** | Cherry pair counter = 0 | Variable | Cherry respin feature |
|
|
418
|
+
| **Bell** | Bell triple counter = 0 | 5 | Bell scatter pays + pool jackpot |
|
|
419
|
+
| **Bar1** | Bar1 triple counter = 0 | 7 | Super Bar scatter pays |
|
|
420
|
+
| **Respin** | Lone center cherry on losing spin | 1-2 | Corner respin feature |
|
|
421
|
+
|
|
422
|
+
### Counter System
|
|
423
|
+
|
|
424
|
+
Each bonus has an associated counter that decrements on specific symbol combinations:
|
|
425
|
+
|
|
426
|
+
- **Jackpot Counter**: Decrements on specific patterns, triggers at 0
|
|
427
|
+
- **Cherry Counter**: Decrements on cherry pairs, resets to 6 or 9
|
|
428
|
+
- **Bell Counter**: Decrements on bell triples, resets to 3 or 5
|
|
429
|
+
- **Bar1 Counter**: Decrements on bar1 triples, resets to 1
|
|
430
|
+
|
|
431
|
+
### Jackpot System
|
|
432
|
+
|
|
433
|
+
Two jackpot types:
|
|
434
|
+
|
|
435
|
+
- **Progressive Jackpot** (`bonusJackpotValue`): Accumulates on losing paid spins, paid during Jackpot Bonus
|
|
436
|
+
- **Pool Jackpot** (`poolJackpotValue`): Accumulates separately, paid when Bell Bonus triggers
|
|
437
|
+
|
|
438
|
+
## Integration Examples
|
|
439
|
+
|
|
440
|
+
### Example 1: Basic RGS Integration
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import { HappyPandaV1GameEngine } from '@omnitronix/happy-panda-game-engine';
|
|
444
|
+
|
|
445
|
+
class GameSessionService {
|
|
446
|
+
private gameEngine: HappyPandaV1GameEngine;
|
|
447
|
+
|
|
448
|
+
constructor() {
|
|
449
|
+
this.gameEngine = new HappyPandaV1GameEngine();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async createSession(userId: string, gameDirection: number, betStake: number) {
|
|
453
|
+
const initCommand = {
|
|
454
|
+
id: `init-${userId}-${Date.now()}`,
|
|
455
|
+
type: 'INIT_SESSION_STATE',
|
|
456
|
+
payload: { gameDirection, betStake }
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const result = await this.gameEngine.processCommand(null, null, initCommand);
|
|
460
|
+
|
|
461
|
+
// Store result.publicState in database (visible to player)
|
|
462
|
+
// Store result.privateState securely (server-side only)
|
|
463
|
+
// Store result.rngOutcome for audit trail
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
sessionId: userId,
|
|
467
|
+
publicState: result.publicState,
|
|
468
|
+
// Never send privateState to client!
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async processSpin(sessionId: string, publicState: any, privateState: any) {
|
|
473
|
+
const spinCommand = {
|
|
474
|
+
id: `spin-${sessionId}-${Date.now()}`,
|
|
475
|
+
type: 'SPIN',
|
|
476
|
+
payload: {}
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const result = await this.gameEngine.processCommand(
|
|
480
|
+
publicState,
|
|
481
|
+
privateState,
|
|
482
|
+
spinCommand
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
// Update states in database
|
|
486
|
+
// Log RNG outcome for compliance
|
|
487
|
+
// Return outcome to client
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
outcome: result.outcome,
|
|
491
|
+
publicState: result.publicState,
|
|
492
|
+
rngOutcome: result.rngOutcome, // For audit
|
|
493
|
+
// privateState stays on server
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Example 2: Handling Bonus Triggers
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
async handleSpinResult(spinResult: any) {
|
|
503
|
+
const { outcome, publicState, privateState } = spinResult;
|
|
504
|
+
|
|
505
|
+
// Check win types
|
|
506
|
+
console.log(`Total Payout: ${outcome.wins.totalPayout}`);
|
|
507
|
+
console.log(`Line Wins: ${outcome.wins.lineWins.length}`);
|
|
508
|
+
console.log(`Wall Win: ${outcome.wins.wallWin ? 'Yes' : 'No'}`);
|
|
509
|
+
console.log(`Scatter Wins: ${outcome.wins.scatterWins.length}`);
|
|
510
|
+
|
|
511
|
+
// Check for jackpot
|
|
512
|
+
if (outcome.jackpotWon > 0) {
|
|
513
|
+
console.log(`JACKPOT WON: ${outcome.jackpotWon}`);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (outcome.poolJackpotWon > 0) {
|
|
517
|
+
console.log(`POOL JACKPOT WON: ${outcome.poolJackpotWon}`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Check if bonus was triggered
|
|
521
|
+
if (outcome.bonusTriggered) {
|
|
522
|
+
return {
|
|
523
|
+
type: 'BONUS_TRIGGERED',
|
|
524
|
+
bonusType: outcome.bonusTriggered,
|
|
525
|
+
spinsRemaining: publicState.spinsRemaining,
|
|
526
|
+
message: `${outcome.bonusTriggered} bonus triggered!`
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Check if bonus sequence completed
|
|
531
|
+
if (outcome.isBonusComplete) {
|
|
532
|
+
return {
|
|
533
|
+
type: 'BONUS_COMPLETE',
|
|
534
|
+
message: 'Bonus sequence completed'
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return {
|
|
539
|
+
type: 'REGULAR_SPIN',
|
|
540
|
+
totalPayout: outcome.wins.totalPayout
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Example 3: Game Direction Modes
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
// 8-line mode (SINGLE)
|
|
549
|
+
const single8Lines = await gameEngine.processCommand(null, null, {
|
|
550
|
+
id: 'init-single',
|
|
551
|
+
type: 'INIT_SESSION_STATE',
|
|
552
|
+
payload: { gameDirection: 0, betStake: 1 }
|
|
553
|
+
});
|
|
554
|
+
// Total bet = 8 x betStake = 8
|
|
555
|
+
|
|
556
|
+
// 16-line mode (BOTH)
|
|
557
|
+
const both16Lines = await gameEngine.processCommand(null, null, {
|
|
558
|
+
id: 'init-both',
|
|
559
|
+
type: 'INIT_SESSION_STATE',
|
|
560
|
+
payload: { gameDirection: 1, betStake: 1 }
|
|
561
|
+
});
|
|
562
|
+
// Total bet = 16 x betStake = 16
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
## Development
|
|
566
|
+
|
|
567
|
+
### Running Tests
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
# Run all tests
|
|
571
|
+
npm test
|
|
572
|
+
|
|
573
|
+
# Run tests in watch mode
|
|
574
|
+
npm run test:watch
|
|
575
|
+
|
|
576
|
+
# Run with coverage
|
|
577
|
+
npm run test:cov
|
|
578
|
+
|
|
579
|
+
# Run RTP diagnostic (100K spins)
|
|
580
|
+
npm test -- --testNamePattern="RTP Diagnostic"
|
|
581
|
+
|
|
582
|
+
# Run C++ parity tests
|
|
583
|
+
npm test -- --testPathPattern="cpp-parity"
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Building
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
# Clean build
|
|
590
|
+
npm run clean
|
|
591
|
+
npm run build
|
|
592
|
+
|
|
593
|
+
# Development mode
|
|
594
|
+
npm run build
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Linting
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
# Check for issues
|
|
601
|
+
npm run lint
|
|
602
|
+
|
|
603
|
+
# Auto-fix issues
|
|
604
|
+
npm run lint -- --fix
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
## Configuration
|
|
608
|
+
|
|
609
|
+
Game configuration located in `src/config/happy-panda.config.ts`:
|
|
610
|
+
|
|
611
|
+
- Symbol definitions and IDs
|
|
612
|
+
- Paytable values for all win types
|
|
613
|
+
- Line shapes for 8 and 16 line modes
|
|
614
|
+
- Reel strip weights by spin type
|
|
615
|
+
- Bonus counter initial values
|
|
616
|
+
- Jackpot accumulation rates
|
|
617
|
+
|
|
618
|
+
All values match the original Excel specification (`CherryMaster_A_2_26.05.2025.xlsx`).
|
|
619
|
+
|
|
620
|
+
## Type Exports
|
|
621
|
+
|
|
622
|
+
The package exports all necessary types for TypeScript integration:
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
import {
|
|
626
|
+
// V1 Game Engine
|
|
627
|
+
HappyPandaV1GameEngine,
|
|
628
|
+
GameEngine,
|
|
629
|
+
GameEngineInfo,
|
|
630
|
+
GameActionCommand,
|
|
631
|
+
CommandProcessingResult,
|
|
632
|
+
RngOutcome,
|
|
633
|
+
RngOutcomeRecord,
|
|
634
|
+
|
|
635
|
+
// Core Engine
|
|
636
|
+
HappyPandaEngine,
|
|
637
|
+
|
|
638
|
+
// Configuration
|
|
639
|
+
Symbol,
|
|
640
|
+
SpinType,
|
|
641
|
+
ScreenWinType,
|
|
642
|
+
GRID,
|
|
643
|
+
LINE_SHAPES,
|
|
644
|
+
LINES_PER_DIRECTION,
|
|
645
|
+
|
|
646
|
+
// Domain Types
|
|
647
|
+
Grid,
|
|
648
|
+
Position,
|
|
649
|
+
LineWin,
|
|
650
|
+
WallWin,
|
|
651
|
+
ScatterWin,
|
|
652
|
+
SpecialWin,
|
|
653
|
+
SpinWinResult,
|
|
654
|
+
BonusCounters,
|
|
655
|
+
PendingBonuses,
|
|
656
|
+
JackpotState,
|
|
657
|
+
GameDirection,
|
|
658
|
+
GameState,
|
|
659
|
+
PublicState,
|
|
660
|
+
PrivateState,
|
|
661
|
+
SpinRequest,
|
|
662
|
+
SpinResponse,
|
|
663
|
+
SessionState,
|
|
664
|
+
RngProvider,
|
|
665
|
+
CommandType,
|
|
666
|
+
GameCommand,
|
|
667
|
+
|
|
668
|
+
// Logic Services
|
|
669
|
+
evaluateSpin,
|
|
670
|
+
evaluateLineWins,
|
|
671
|
+
evaluateWallWin,
|
|
672
|
+
evaluateScatterWins,
|
|
673
|
+
generateGrid,
|
|
674
|
+
} from '@omnitronix/happy-panda-game-engine';
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
## Key Differences from Traditional Slots
|
|
678
|
+
|
|
679
|
+
### Classic 3x3 Layout
|
|
680
|
+
|
|
681
|
+
- **Traditional Modern**: 5+ reels, multiple rows
|
|
682
|
+
- **Happy Panda**: Classic 3x3 grid with bidirectional paylines
|
|
683
|
+
- **Advantage**: Nostalgic gameplay, simpler visual presentation
|
|
684
|
+
|
|
685
|
+
### Bidirectional Paylines
|
|
686
|
+
|
|
687
|
+
- **8-Line Mode**: Left-to-right only
|
|
688
|
+
- **16-Line Mode**: Both directions (LTR + RTL)
|
|
689
|
+
- **Strategic Choice**: Player chooses mode based on bet preference
|
|
690
|
+
|
|
691
|
+
### Wall Wins (Matrix Wins)
|
|
692
|
+
|
|
693
|
+
- **Screen-filling wins**: Full 3x3 of same/related symbols
|
|
694
|
+
- **Multiple types**: Pure, cherry mix, bar mix, fruits, colors
|
|
695
|
+
- **Important**: Wall wins suppress line wins (no double-counting)
|
|
696
|
+
|
|
697
|
+
### Counter-Based Bonuses
|
|
698
|
+
|
|
699
|
+
- **Not scatter-triggered**: Bonuses trigger via counter depletion
|
|
700
|
+
- **Progressive build-up**: Counters decrement on specific symbol combos
|
|
701
|
+
- **Multiple bonus types**: 5 distinct bonus modes with different mechanics
|
|
702
|
+
|
|
703
|
+
### Dual Jackpot System
|
|
704
|
+
|
|
705
|
+
- **Progressive Jackpot**: Accumulates on losses, paid in Jackpot Bonus
|
|
706
|
+
- **Pool Jackpot**: Separate accumulator, paid with Bell Bonus
|
|
707
|
+
- **Dual opportunity**: Two paths to jackpot wins
|
|
708
|
+
|
|
709
|
+
## Documentation
|
|
710
|
+
|
|
711
|
+
- **Test Protocol**: `docs/TEST-PROTOCOL-RTP-100M.md` - Complete 100M spin test results
|
|
712
|
+
- **RTP Matching**: `docs/RTP-MATCHING.md` - Implementation details and tuning history
|
|
713
|
+
- **C++ Source**: `math/Happy Red Panda/CherryMaster_A_2.cpp`
|
|
714
|
+
- **Excel Spec**: `math/Happy Red Panda/CherryMaster_A_2_26.05.2025.xlsx`
|
|
715
|
+
|
|
716
|
+
## License
|
|
717
|
+
|
|
718
|
+
UNLICENSED - Internal use only for Omnitronix
|
|
719
|
+
|
|
720
|
+
## Support
|
|
721
|
+
|
|
722
|
+
For questions or issues, contact the Omnitronix development team.
|