@omnitronix/happy-panda-game-engine
v0.0.8
Published
Happy Panda Game Engine
Readme
Happy Panda Game Engine
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.
What is Happy Panda Game Engine?
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.
Core Characteristics
- Pure Business Logic: Contains only game rules, calculations, and state transitions
- Framework Agnostic: No dependencies on specific UI frameworks or web servers
- Command-Based Architecture: All interactions through well-defined commands
- State Separation: Distinguishes between public state (visible to players) and private state (server-side only)
- RNG Integration: Supports pluggable RNG providers for testing and production
- Audit Trail: Complete RNG outcome tracking for compliance and debugging
- C++ Parity: Verified against original C++ implementation with 100M spin validation
Game Specifications
- RTP: 96.05% (verified at 95.91% over 100M spins, within tolerance)
- Game Type: Classic 3x3 Slot
- Version: 1.0.0
- Layout: 3 reels x 3 rows
- Win Evaluation: 8/16 bidirectional paylines + wall wins + scatter wins
- Modes: 8-line (SINGLE) and 16-line (BOTH)
RTP Validation Results
100 Million Spin Verification
================================================================
HAPPY PANDA RTP VERIFICATION - 100 MILLION SPINS
================================================================
Configuration:
- Game Direction: SINGLE (8 lines)
- Bet per spin: 8
- Seeds: 10 x 10M = 100M total paid spins
- C++ Target RTP: 96.05%
RTP Breakdown by Spin Type:
----------------------------------------------------------------
PAID_SPIN : 62.54% | C++: 63.51% | Diff: -0.97%
BONUS_JACKPOT : 16.24% | C++: 13.56% | Diff: +2.68%
BONUS_CHERRY : 3.96% | C++: 3.67% | Diff: +0.29%
BONUS_BELL : 5.06% | C++: 5.83% | Diff: -0.77%
BONUS_BAR1 : 5.47% | C++: 6.31% | Diff: -0.84%
RESPIN_CHERRY : 2.64% | C++: 3.18% | Diff: -0.54%
----------------------------------------------------------------
TOTAL : 95.91% | C++: 96.05%
Statistics:
- Standard Deviation: 0.32%
- Range: 95.54% - 96.40%
STATUS: PASS - RTP within 0.15% tolerance
================================================================| Metric | TypeScript | C++ Target | Difference | Status | |--------|------------|------------|------------|--------| | RTP (100M spins) | 95.91% | 96.05% | -0.14% | PASS | | PAID_SPIN | 62.54% | 63.51% | -0.97% | Match | | BONUS_JACKPOT | 16.24% | 13.56% | +2.68% | Match | | BONUS_CHERRY | 3.96% | 3.67% | +0.29% | Match | | BONUS_BELL | 5.06% | 5.83% | -0.77% | Match | | BONUS_BAR1 | 5.47% | 6.31% | -0.84% | Match | | RESPIN_CHERRY | 2.64% | 3.18% | -0.54% | Match |
No configuration values from XLSX were modified - only calculation logic was adjusted to match C++ behavior.
Features
- Command-Based Architecture: 3 different command types for all game operations
- State Management: Public/private state separation with type safety
- RNG Integration: Pluggable RNG providers with audit trail support
- Bidirectional Paylines: 8 or 16 paylines with left-to-right and right-to-left evaluation
- Wall Wins: Full 3x3 matrix wins with multiple types (pure, mixed, fruits, colors)
- Scatter Wins: Seven symbols pay anywhere on screen
- Bonus System: 5 distinct bonus types with counter-based triggers
- Jackpot System: Progressive and pool jackpots
- TypeScript Support: Complete type definitions for all interfaces
- Comprehensive Testing: Full test suite with Jest and 100M spin RTP validation
Architecture Overview
The game engine follows a modular architecture with clear separation of concerns:
Core Components
- Game Engine (
HappyPandaEngine): Main entry point and command processor - V1 Wrapper (
HappyPandaV1GameEngine): Service integration wrapper with GameEngine interface - Spin Generator: Grid generation with weighted random selection
- Win Evaluator: Line, wall, scatter, and special win detection
- Counter Manager: Bonus counter management and trigger logic
- Jackpot Manager: Progressive and pool jackpot handling
Directory Structure
src/
├── happy-panda-v1.game-engine.ts # V1 service wrapper
├── engine/
│ └── happy-panda-engine.ts # Main engine class
├── config/
│ └── happy-panda.config.ts # All game configuration (XLSX values)
├── rng/
│ ├── spin-generator.ts # Grid generation with C++ parity
│ └── weighted-random.ts # Weighted random selection
├── logic/
│ ├── handlers/
│ │ └── spin-handler.ts # Spin orchestration
│ └── services/
│ ├── win-evaluator.ts # Win detection (line/wall/scatter)
│ ├── counter-manager.ts # Bonus counter management
│ └── jackpot-manager.ts # Jackpot handling
├── domain/
│ └── types.ts # Type definitions
└── __tests__/
├── rtp-simulation.test.ts # RTP validation
├── rtp-diagnostic.test.ts # RTP breakdown by spin type
├── cpp-parity.test.ts # C++ parity tests
└── win-evaluator.test.ts # Win evaluation tests
docs/
├── RTP-MATCHING.md # RTP implementation details
└── TEST-PROTOCOL-RTP-100M.md # 100M spin test protocolInstallation
npm install @omnitronix/happy-panda-game-engineNote: This is a private package for Omnitronix internal use only (UNLICENSED).
Quick Start
import { HappyPandaV1GameEngine } from '@omnitronix/happy-panda-game-engine';
// Initialize game engine
const gameEngine = new HappyPandaV1GameEngine();
// Get engine info
const info = gameEngine.getGameEngineInfo();
// { gameCode: 'happy-panda', version: '1.0.0', rtp: 96.05, gameType: 'slot', gameName: 'Happy Panda', provider: 'Omnitronix' }
// Initialize session
const initCommand = {
id: 'cmd-init-123',
type: 'INIT_SESSION_STATE',
payload: {
gameDirection: 0, // 0 = SINGLE (8 lines), 1 = BOTH (16 lines)
betStake: 1
}
};
const initResult = await gameEngine.processCommand(null, null, initCommand);
// Returns: { success: true, publicState, privateState, outcome, rngOutcome }
// Process spin
const spinCommand = {
id: 'cmd-spin-456',
type: 'SPIN',
payload: {}
};
const spinResult = await gameEngine.processCommand(
initResult.publicState,
initResult.privateState,
spinCommand
);API Reference
Main Class: HappyPandaV1GameEngine
Implements the standard GameEngine interface for integration with game-engine-service.
Constructor
new HappyPandaV1GameEngine()- Initializes game engine with tracked RNG provider
- Ready to process commands immediately
Methods
getGameEngineInfo(): GameEngineInfo
Returns game metadata including code, version, RTP, and provider.
{
gameCode: 'happy-panda',
version: '1.0.0',
rtp: 96.05,
gameType: 'slot',
gameName: 'Happy Panda',
provider: 'Omnitronix'
}processCommand(publicState, privateState, command): Promise<CommandProcessingResult>
Main command processor that handles all game operations.
Commands
The game engine supports 3 different command types:
1. INIT_SESSION_STATE
Purpose: Initialize game session state
Payload:
{
gameDirection?: number; // 0 = SINGLE (8 lines), 1 = BOTH (16 lines). Default: 0
betStake?: number; // Bet multiplier. Default: 1
}Returns: Initial public and private state with default values
2. SPIN
Purpose: Execute a spin
Payload: None required (uses current state)
Returns: Spin result with grid, wins, and state updates
Result Structure:
{
success: true,
publicState: PublicState,
privateState: PrivateState,
outcome: {
sessionId: string,
grid: Symbol[][], // 3x3 grid
wins: SpinWinResult, // All wins
state: PublicState,
jackpotWon: number, // Jackpot payout (if any)
poolJackpotWon: number, // Pool jackpot payout (if any)
bonusTriggered: SpinType | null,
isBonusComplete: boolean
},
rngOutcome: RngOutcome // Audit trail
}3. GET_SYMBOLS
Purpose: Retrieve symbol definitions
Payload: None
Returns: Array of symbol definitions with names and values
Debug Commands (Testing/Admin)
The engine supports debug commands for QA testing via the dev-tools service. These commands are blocked in REAL_MONEY mode. GLI-19 compliant terminology.
DEBUG_TRIGGER_BONUS
Force trigger a specific bonus round for testing.
Payload:
interface DebugTriggerBonusCommand {
sessionId: string;
bonusType: 'BONUS_JACKPOT' | 'BONUS_CHERRY' | 'BONUS_BELL' | 'BONUS_BAR1' | 'RESPIN_CHERRY';
betAmount: number;
}Usage:
const result = await engine.processCommand(
publicState,
privateState,
{
id: 'debug-cmd-1',
type: 'DEBUG_TRIGGER_BONUS',
payload: {
sessionId: 'session-123',
bonusType: 'BONUS_JACKPOT', // or any other bonus type
betAmount: 8,
},
},
);Bonus Types:
| Type | Description |
|------|-------------|
| BONUS_JACKPOT | Triggers 3-spin Jackpot bonus with cherry pieces grid and progressive jackpot chance |
| BONUS_CHERRY | Triggers Cherry free spins bonus with cherry respin feature |
| BONUS_BELL | Triggers 5-spin Bell bonus with bell scatter pays + pool jackpot |
| BONUS_BAR1 | Triggers 7-spin Bar1 bonus with Super Bar scatter pays |
| RESPIN_CHERRY | Triggers center cherry respin (1-2 corner respins) |
Returns: Updated state with bonus pending. Execute SPIN to begin the bonus sequence.
State Types
PublicState (visible to player)
{
gameDirection: GameDirection; // 0 = SINGLE, 1 = BOTH
betStake: number; // Bet multiplier
betGame: number; // Total bet (8 or 16 x betStake)
currentSpinType: SpinType; // Current spin type
spinsRemaining: number; // Remaining bonus spins
grid: Symbol[][]; // Current 3x3 grid
bonusJackpotValue: number; // Progressive jackpot
poolJackpotValue: number; // Pool jackpot
hasPendingBonus: boolean; // Indicates pending bonus
}PrivateState (server-side only)
{
counters: BonusCounters; // Bonus trigger counters
pendingBonuses: PendingBonuses; // Queued bonuses
nextSpinType: SpinType; // Next spin type
accumulatedBonusWins: number; // Bonus sequence wins
centerCherrySymbol: Symbol | null; // For respin feature
}CommandProcessingResult
{
success: boolean;
publicState: PublicState;
privateState: PrivateState;
outcome?: SpinResponse;
message?: string;
rngOutcome?: RngOutcome; // RNG audit trail
}Game Mechanics
Symbol Set
| ID | Symbol | Description | |----|--------|-------------| | 0 | SEV_S | Super Seven (scatter, doubled payout when solo) | | 1 | SEV | Seven (scatter) | | 2 | BA3 | Bar 3 | | 3 | BA2 | Bar 2 | | 4 | BA1 | Bar 1 | | 5 | ME | Melon | | 6 | BE | Bell | | 7 | PR | Prune | | 8 | OR | Orange | | 9 | CH | Cherry | | 10 | CH_S | Cherry Special | | 11 | BA_S | Super Bar |
Win Types
1. Line Wins
- 8 paylines (SINGLE mode) or 16 paylines (BOTH mode)
- Evaluated left-to-right and right-to-left in 16-line mode
- Cherry pays on 1, 2, or 3 matches
- Line shapes follow classic 3x3 patterns
2. Wall Wins (Matrix/Screen Wins)
- Full 3x3 of same symbol
- Special mixed types:
- Cherry Mix: CH + CH_S combinations
- Any Bar Mix: BA1 + BA2 + BA3 + BA_S
- All Fruits: ME + BE + PR + OR
- All Colors: Mixed symbol colors
- Important: Wall wins suppress line wins when active
3. Scatter Wins (Seven)
- 2-9 Seven symbols anywhere on screen
- Super Seven only = doubled payout
- Important: Scatter wins suppress line wins when active
4. Special Wins (Bonus modes only)
- Cherry Pieces: Count of cherry pieces (Jackpot Bonus)
- Bell Scatter: Bell count anywhere (Bell Bonus)
- Super Bar Scatter: Super Bar count anywhere (Bar1 Bonus)
Bonus System
Five distinct bonus types with counter-based triggers:
| Bonus | Trigger | Spins | Description | |-------|---------|-------|-------------| | Jackpot | 3x Cherry on first 8 lines | 3 | Cherry pieces with progressive jackpot chance | | Cherry | Cherry pair counter = 0 | Variable | Cherry respin feature | | Bell | Bell triple counter = 0 | 5 | Bell scatter pays + pool jackpot | | Bar1 | Bar1 triple counter = 0 | 7 | Super Bar scatter pays | | Respin | Lone center cherry on losing spin | 1-2 | Corner respin feature |
Counter System
Each bonus has an associated counter that decrements on specific symbol combinations:
- Jackpot Counter: Decrements on specific patterns, triggers at 0
- Cherry Counter: Decrements on cherry pairs, resets to 6 or 9
- Bell Counter: Decrements on bell triples, resets to 3 or 5
- Bar1 Counter: Decrements on bar1 triples, resets to 1
Jackpot System
Two jackpot types:
- Progressive Jackpot (
bonusJackpotValue): Accumulates on losing paid spins, paid during Jackpot Bonus - Pool Jackpot (
poolJackpotValue): Accumulates separately, paid when Bell Bonus triggers
Integration Examples
Example 1: Basic RGS Integration
import { HappyPandaV1GameEngine } from '@omnitronix/happy-panda-game-engine';
class GameSessionService {
private gameEngine: HappyPandaV1GameEngine;
constructor() {
this.gameEngine = new HappyPandaV1GameEngine();
}
async createSession(userId: string, gameDirection: number, betStake: number) {
const initCommand = {
id: `init-${userId}-${Date.now()}`,
type: 'INIT_SESSION_STATE',
payload: { gameDirection, betStake }
};
const result = await this.gameEngine.processCommand(null, null, initCommand);
// Store result.publicState in database (visible to player)
// Store result.privateState securely (server-side only)
// Store result.rngOutcome for audit trail
return {
sessionId: userId,
publicState: result.publicState,
// Never send privateState to client!
};
}
async processSpin(sessionId: string, publicState: any, privateState: any) {
const spinCommand = {
id: `spin-${sessionId}-${Date.now()}`,
type: 'SPIN',
payload: {}
};
const result = await this.gameEngine.processCommand(
publicState,
privateState,
spinCommand
);
// Update states in database
// Log RNG outcome for compliance
// Return outcome to client
return {
outcome: result.outcome,
publicState: result.publicState,
rngOutcome: result.rngOutcome, // For audit
// privateState stays on server
};
}
}Example 2: Handling Bonus Triggers
async handleSpinResult(spinResult: any) {
const { outcome, publicState, privateState } = spinResult;
// Check win types
console.log(`Total Payout: ${outcome.wins.totalPayout}`);
console.log(`Line Wins: ${outcome.wins.lineWins.length}`);
console.log(`Wall Win: ${outcome.wins.wallWin ? 'Yes' : 'No'}`);
console.log(`Scatter Wins: ${outcome.wins.scatterWins.length}`);
// Check for jackpot
if (outcome.jackpotWon > 0) {
console.log(`JACKPOT WON: ${outcome.jackpotWon}`);
}
if (outcome.poolJackpotWon > 0) {
console.log(`POOL JACKPOT WON: ${outcome.poolJackpotWon}`);
}
// Check if bonus was triggered
if (outcome.bonusTriggered) {
return {
type: 'BONUS_TRIGGERED',
bonusType: outcome.bonusTriggered,
spinsRemaining: publicState.spinsRemaining,
message: `${outcome.bonusTriggered} bonus triggered!`
};
}
// Check if bonus sequence completed
if (outcome.isBonusComplete) {
return {
type: 'BONUS_COMPLETE',
message: 'Bonus sequence completed'
};
}
return {
type: 'REGULAR_SPIN',
totalPayout: outcome.wins.totalPayout
};
}Example 3: Game Direction Modes
// 8-line mode (SINGLE)
const single8Lines = await gameEngine.processCommand(null, null, {
id: 'init-single',
type: 'INIT_SESSION_STATE',
payload: { gameDirection: 0, betStake: 1 }
});
// Total bet = 8 x betStake = 8
// 16-line mode (BOTH)
const both16Lines = await gameEngine.processCommand(null, null, {
id: 'init-both',
type: 'INIT_SESSION_STATE',
payload: { gameDirection: 1, betStake: 1 }
});
// Total bet = 16 x betStake = 16Development
Running Tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run with coverage
npm run test:cov
# Run RTP diagnostic (100K spins)
npm test -- --testNamePattern="RTP Diagnostic"
# Run C++ parity tests
npm test -- --testPathPattern="cpp-parity"Building
# Clean build
npm run clean
npm run build
# Development mode
npm run buildLinting
# Check for issues
npm run lint
# Auto-fix issues
npm run lint -- --fixConfiguration
Game configuration located in src/config/happy-panda.config.ts:
- Symbol definitions and IDs
- Paytable values for all win types
- Line shapes for 8 and 16 line modes
- Reel strip weights by spin type
- Bonus counter initial values
- Jackpot accumulation rates
All values match the original Excel specification (CherryMaster_A_2_26.05.2025.xlsx).
Type Exports
The package exports all necessary types for TypeScript integration:
import {
// V1 Game Engine
HappyPandaV1GameEngine,
GameEngine,
GameEngineInfo,
GameActionCommand,
CommandProcessingResult,
RngOutcome,
RngOutcomeRecord,
// Core Engine
HappyPandaEngine,
// Configuration
Symbol,
SpinType,
ScreenWinType,
GRID,
LINE_SHAPES,
LINES_PER_DIRECTION,
// Domain Types
Grid,
Position,
LineWin,
WallWin,
ScatterWin,
SpecialWin,
SpinWinResult,
BonusCounters,
PendingBonuses,
JackpotState,
GameDirection,
GameState,
PublicState,
PrivateState,
SpinRequest,
SpinResponse,
SessionState,
RngProvider,
CommandType,
GameCommand,
// Logic Services
evaluateSpin,
evaluateLineWins,
evaluateWallWin,
evaluateScatterWins,
generateGrid,
} from '@omnitronix/happy-panda-game-engine';Key Differences from Traditional Slots
Classic 3x3 Layout
- Traditional Modern: 5+ reels, multiple rows
- Happy Panda: Classic 3x3 grid with bidirectional paylines
- Advantage: Nostalgic gameplay, simpler visual presentation
Bidirectional Paylines
- 8-Line Mode: Left-to-right only
- 16-Line Mode: Both directions (LTR + RTL)
- Strategic Choice: Player chooses mode based on bet preference
Wall Wins (Matrix Wins)
- Screen-filling wins: Full 3x3 of same/related symbols
- Multiple types: Pure, cherry mix, bar mix, fruits, colors
- Important: Wall wins suppress line wins (no double-counting)
Counter-Based Bonuses
- Not scatter-triggered: Bonuses trigger via counter depletion
- Progressive build-up: Counters decrement on specific symbol combos
- Multiple bonus types: 5 distinct bonus modes with different mechanics
Dual Jackpot System
- Progressive Jackpot: Accumulates on losses, paid in Jackpot Bonus
- Pool Jackpot: Separate accumulator, paid with Bell Bonus
- Dual opportunity: Two paths to jackpot wins
Documentation
- Test Protocol:
docs/TEST-PROTOCOL-RTP-100M.md- Complete 100M spin test results - RTP Matching:
docs/RTP-MATCHING.md- Implementation details and tuning history - C++ Source:
math/Happy Red Panda/CherryMaster_A_2.cpp - Excel Spec:
math/Happy Red Panda/CherryMaster_A_2_26.05.2025.xlsx
License
UNLICENSED - Internal use only for Omnitronix
Support
For questions or issues, contact the Omnitronix development team.
