@omnitronix/bonnys-fortune-game-engine 1.0.0-beta.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 (118) hide show
  1. package/README.md +680 -0
  2. package/dist/__tests__/bonnys-fortune-v1.game-engine.test.js +71 -0
  3. package/dist/__tests__/bonnys-fortune-v1.game-engine.test.js.map +1 -0
  4. package/dist/__tests__/comprehensive.test.js +741 -0
  5. package/dist/__tests__/comprehensive.test.js.map +1 -0
  6. package/dist/bonnys-fortune-v1.game-engine.js +184 -0
  7. package/dist/bonnys-fortune-v1.game-engine.js.map +1 -0
  8. package/dist/config/game-logic-config/file-system.game-logic-config-loader.js +57 -0
  9. package/dist/config/game-logic-config/file-system.game-logic-config-loader.js.map +1 -0
  10. package/dist/config/game-logic-config/game-logic-config-loader.js +3 -0
  11. package/dist/config/game-logic-config/game-logic-config-loader.js.map +1 -0
  12. package/dist/config/game-logic-config/game-logic-config.js +382 -0
  13. package/dist/config/game-logic-config/game-logic-config.js.map +1 -0
  14. package/dist/config/reel-strips-config/file-system.reel-strips-config-loader.js +34 -0
  15. package/dist/config/reel-strips-config/file-system.reel-strips-config-loader.js.map +1 -0
  16. package/dist/config/reel-strips-config/reel-strip-option.dto.js +27 -0
  17. package/dist/config/reel-strips-config/reel-strip-option.dto.js.map +1 -0
  18. package/dist/config/reel-strips-config/reel-strips-config-loader.js +3 -0
  19. package/dist/config/reel-strips-config/reel-strips-config-loader.js.map +1 -0
  20. package/dist/config/reel-strips-config/reel-strips-config.dto.js +29 -0
  21. package/dist/config/reel-strips-config/reel-strips-config.dto.js.map +1 -0
  22. package/dist/config/reel-strips-config/reel.dto.js +29 -0
  23. package/dist/config/reel-strips-config/reel.dto.js.map +1 -0
  24. package/dist/config/reel-strips-config/reels-BASE.csv +52 -0
  25. package/dist/config/reel-strips-config/reels-BONUS.csv +52 -0
  26. package/dist/domain/game-round.types.js +9 -0
  27. package/dist/domain/game-round.types.js.map +1 -0
  28. package/dist/domain/mappers/reel-strips-config.mapper.js +17 -0
  29. package/dist/domain/mappers/reel-strips-config.mapper.js.map +1 -0
  30. package/dist/domain/reel-strip-option.js +19 -0
  31. package/dist/domain/reel-strip-option.js.map +1 -0
  32. package/dist/domain/reel-strips-config.js +22 -0
  33. package/dist/domain/reel-strips-config.js.map +1 -0
  34. package/dist/domain/reel.js +19 -0
  35. package/dist/domain/reel.js.map +1 -0
  36. package/dist/domain/types/debug-trigger-bonus-command.js +3 -0
  37. package/dist/domain/types/debug-trigger-bonus-command.js.map +1 -0
  38. package/dist/domain/types/debug-trigger-bonus-request.dto.js +3 -0
  39. package/dist/domain/types/debug-trigger-bonus-request.dto.js.map +1 -0
  40. package/dist/domain/types/debug-update-bonus-meter-progress-command.js +3 -0
  41. package/dist/domain/types/debug-update-bonus-meter-progress-command.js.map +1 -0
  42. package/dist/domain/types/debug-update-bonus-meter-progress-request.dto.js +3 -0
  43. package/dist/domain/types/debug-update-bonus-meter-progress-request.dto.js.map +1 -0
  44. package/dist/domain/types/game-spin-command.js +3 -0
  45. package/dist/domain/types/game-spin-command.js.map +1 -0
  46. package/dist/domain/types/game-spin-input.dto.js +3 -0
  47. package/dist/domain/types/game-spin-input.dto.js.map +1 -0
  48. package/dist/domain/types/game-spin-output.dto.js +17 -0
  49. package/dist/domain/types/game-spin-output.dto.js.map +1 -0
  50. package/dist/domain/types/game-symbols.response.dto.js +7 -0
  51. package/dist/domain/types/game-symbols.response.dto.js.map +1 -0
  52. package/dist/domain/types/reel-strip-option.dto.js +27 -0
  53. package/dist/domain/types/reel-strip-option.dto.js.map +1 -0
  54. package/dist/domain/types/reel-strips-config.dto.js +28 -0
  55. package/dist/domain/types/reel-strips-config.dto.js.map +1 -0
  56. package/dist/domain/types/reel.dto.js +28 -0
  57. package/dist/domain/types/reel.dto.js.map +1 -0
  58. package/dist/domain/types/start-bonus-round-command.js +3 -0
  59. package/dist/domain/types/start-bonus-round-command.js.map +1 -0
  60. package/dist/domain/types/start-bonus-round-input.dto.js +3 -0
  61. package/dist/domain/types/start-bonus-round-input.dto.js.map +1 -0
  62. package/dist/game-engine.interface.js +3 -0
  63. package/dist/game-engine.interface.js.map +1 -0
  64. package/dist/helpers/generate-hash.js +9 -0
  65. package/dist/helpers/generate-hash.js.map +1 -0
  66. package/dist/helpers/number-helper.js +43 -0
  67. package/dist/helpers/number-helper.js.map +1 -0
  68. package/dist/helpers/optional-boolean-mapper.js +9 -0
  69. package/dist/helpers/optional-boolean-mapper.js.map +1 -0
  70. package/dist/helpers/uuid-helper.js +11 -0
  71. package/dist/helpers/uuid-helper.js.map +1 -0
  72. package/dist/helpers/validation-helper.js +25 -0
  73. package/dist/helpers/validation-helper.js.map +1 -0
  74. package/dist/index.js +7 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/logger/logger.js +140 -0
  77. package/dist/logger/logger.js.map +1 -0
  78. package/dist/logic/bonnys-fortune.game-logic.js +252 -0
  79. package/dist/logic/bonnys-fortune.game-logic.js.map +1 -0
  80. package/dist/logic/bonnys-fortune.spin-generator.js +101 -0
  81. package/dist/logic/bonnys-fortune.spin-generator.js.map +1 -0
  82. package/dist/logic/bonnys-fortune.types.js +17 -0
  83. package/dist/logic/bonnys-fortune.types.js.map +1 -0
  84. package/dist/logic/bonnys-fortune.win-calculator.js +130 -0
  85. package/dist/logic/bonnys-fortune.win-calculator.js.map +1 -0
  86. package/dist/logic/game-logic-config.interface.js +3 -0
  87. package/dist/logic/game-logic-config.interface.js.map +1 -0
  88. package/dist/logic/handlers/base-game.handler.js +113 -0
  89. package/dist/logic/handlers/base-game.handler.js.map +1 -0
  90. package/dist/logic/handlers/collect-feature-bonus.handler.js +152 -0
  91. package/dist/logic/handlers/collect-feature-bonus.handler.js.map +1 -0
  92. package/dist/logic/handlers/free-spins-bonus.handler.js +69 -0
  93. package/dist/logic/handlers/free-spins-bonus.handler.js.map +1 -0
  94. package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js +75 -0
  95. package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js.map +1 -0
  96. package/dist/logic/handlers/treasure-hunt-bonus.handler.js +131 -0
  97. package/dist/logic/handlers/treasure-hunt-bonus.handler.js.map +1 -0
  98. package/dist/rng/DummyRngClient.js +86 -0
  99. package/dist/rng/DummyRngClient.js.map +1 -0
  100. package/dist/rng/rng-client.factory.js +23 -0
  101. package/dist/rng/rng-client.factory.js.map +1 -0
  102. package/dist/rng/rng-client.interface.js +3 -0
  103. package/dist/rng/rng-client.interface.js.map +1 -0
  104. package/dist/rng/rng-service.js +78 -0
  105. package/dist/rng/rng-service.js.map +1 -0
  106. package/dist/validation/abstract-game-logic-config.validator.js +18 -0
  107. package/dist/validation/abstract-game-logic-config.validator.js.map +1 -0
  108. package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.js +454 -0
  109. package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.js.map +1 -0
  110. package/dist/validation/bonnys-fortune/bonnys-fortune-config.validator.js +12 -0
  111. package/dist/validation/bonnys-fortune/bonnys-fortune-config.validator.js.map +1 -0
  112. package/dist/validation/custom-decorators/IsNestedIntArray.js +26 -0
  113. package/dist/validation/custom-decorators/IsNestedIntArray.js.map +1 -0
  114. package/dist/validation/game-logic-config-validation.service.js +20 -0
  115. package/dist/validation/game-logic-config-validation.service.js.map +1 -0
  116. package/dist/validation/reel-strips-config-validation.service.js +26 -0
  117. package/dist/validation/reel-strips-config-validation.service.js.map +1 -0
  118. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,680 @@
1
+ # Bonny's Fortune Game Engine
2
+
3
+ ![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)
4
+ ![License](https://img.shields.io/badge/license-UNLICENSED-red.svg)
5
+ ![Package](https://img.shields.io/badge/package-%40omnitronix%2Fbonnys--fortune--game--engine-green.svg)
6
+
7
+ Pure business logic library for the Bonny's Fortune slot game. A framework-agnostic, TypeScript-based game engine that handles all core game mechanics, state management, and bonus game logic.
8
+
9
+ ## What is Bonny's Fortune Game Engine?
10
+
11
+ The Bonny's Fortune Game Engine is an isolated, framework-agnostic library containing pure business logic for the Bonny's Fortune 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 both external RNG services and local dummy RNG for testing
20
+ - **Audit Trail**: Complete RNG outcome tracking for compliance and debugging
21
+
22
+ ### Game Specifications
23
+
24
+ - **RTP**: 96%
25
+ - **Game Type**: Slot
26
+ - **Version**: 1.0.0
27
+ - **Layout**: 5 reels × 3 rows
28
+ - **Win Evaluation**: 243 ways to win
29
+
30
+ ## Features
31
+
32
+ - **Command-Based Architecture**: 6 different command types for all game operations
33
+ - **State Management**: Public/private state separation with type safety
34
+ - **RNG Integration**: Pluggable RNG clients with audit trail support
35
+ - **Configuration Validation**: Uses class-validator for runtime type checking
36
+ - **Multiple Bonus Games**: 4 unique bonus game mechanics
37
+ - **TypeScript Support**: Complete type definitions for all interfaces
38
+ - **Comprehensive Testing**: Full test suite with Jest and coverage reporting
39
+
40
+ ## Architecture Overview
41
+
42
+ The game engine follows a modular architecture with clear separation of concerns:
43
+
44
+ ### Core Components
45
+
46
+ - **Game Engine** (`BonnysFortuneV1GameEngine`): Main entry point and command processor
47
+ - **Game Logic** (`BonnysFortuneGameLogic`): Core game rules and state transitions
48
+ - **Handlers**: Specialized handlers for different game modes:
49
+ - `BaseGameHandler`: Base game spin logic
50
+ - `TreasureHuntBonusHandler`: Pick-style bonus game
51
+ - `FreeSpinsBonusHandler`: Free spins with multipliers
52
+ - `SteeringToTheFortuneBonusHandler`: Wheel of fortune bonus
53
+ - `CollectFeatureBonusHandler`: Multi-spin collect feature
54
+ - **RNG Service**: Random number generation with pluggable clients
55
+ - **Win Calculator**: Payline/ways evaluation and win computation
56
+ - **Validators**: Configuration and input validation using class-validator
57
+ - **Config Loaders**: File system-based configuration loading
58
+
59
+ ### Directory Structure
60
+
61
+ ```
62
+ src/
63
+ ├── bonnys-fortune-v1.game-engine.ts # Main game engine
64
+ ├── logic/ # Game logic and handlers
65
+ ├── config/ # Configuration loaders
66
+ ├── validation/ # Config validators
67
+ ├── rng/ # RNG clients and service
68
+ ├── domain/ # Types and DTOs
69
+ └── helpers/ # Utility functions
70
+ ```
71
+
72
+ ## Installation
73
+
74
+ ```bash
75
+ npm install @omnitronix/bonnys-fortune-game-engine
76
+ ```
77
+
78
+ > **Note**: This is a private package for Omnitronix internal use only (UNLICENSED).
79
+
80
+ ## Environment Variables
81
+
82
+ The game engine supports various RNG configurations through environment variables:
83
+
84
+ | Variable | Description | Values | Default |
85
+ |----------|-------------|--------|---------|
86
+ | `RNG_CLIENT_MODE` | RNG client mode | `local` (DummyRngClient), `external` (HttpRngClient) | `external` |
87
+ | `RNG_CLIENT_URL` | External RNG service HTTP URL | URL string | - |
88
+ | `RNG_CLIENT_GRPC_URL` | External RNG service gRPC URL | URL string | - |
89
+ | `RNG_CLIENT_METHOD` | RNG communication method | `grpc`, `rest` | - |
90
+ | `RNG_CLIENT_POOL_SIZE` | RNG ring buffer pool size | Number | `2000` |
91
+
92
+ ### Environment Examples
93
+
94
+ **Local Development** (uses dummy RNG):
95
+ ```bash
96
+ RNG_CLIENT_MODE=local
97
+ ```
98
+
99
+ **Production Environment** (external RNG via gRPC):
100
+ ```bash
101
+ RNG_CLIENT_MODE=external
102
+ RNG_CLIENT_METHOD=grpc
103
+ RNG_CLIENT_GRPC_URL=grpc://rng-prod.omnitronix.com:50051
104
+ RNG_CLIENT_POOL_SIZE=5000
105
+ ```
106
+
107
+ ## Quick Start
108
+
109
+ ```typescript
110
+ import { BonnysFortuneV1GameEngine } from '@omnitronix/bonnys-fortune-game-engine';
111
+
112
+ // Initialize game engine
113
+ const gameEngine = new BonnysFortuneV1GameEngine();
114
+
115
+ // Get game info
116
+ const info = gameEngine.getGameEngineInfo();
117
+ // { gameCode: 'bonnys-fortune', version: '1.0.0', rtp: 96, gameType: 'slot' }
118
+
119
+ // Initialize session
120
+ const initCommand = {
121
+ id: 'cmd-init-123',
122
+ type: 'INIT_SESSION_STATE',
123
+ payload: {
124
+ betAmountThresholds: [10, 25, 50, 100],
125
+ defaultBetAmount: 10
126
+ }
127
+ };
128
+
129
+ const initResult = await gameEngine.processCommand({}, {}, initCommand);
130
+ // Returns: { success, publicState, privateState, outcome, rngOutcome }
131
+
132
+ // Process spin
133
+ const spinCommand = {
134
+ id: 'cmd-spin-456',
135
+ type: 'SPIN',
136
+ payload: {
137
+ sessionId: 'session-123',
138
+ betAmount: 10,
139
+ gameCode: 'bonnys-fortune',
140
+ gameVersion: '1.0.0',
141
+ lines: 10,
142
+ creditsPerLine: 1
143
+ }
144
+ };
145
+
146
+ const spinResult = await gameEngine.processCommand(
147
+ initResult.publicState,
148
+ initResult.privateState,
149
+ spinCommand
150
+ );
151
+ ```
152
+
153
+ ## API Reference
154
+
155
+ ### Main Class: `BonnysFortuneV1GameEngine`
156
+
157
+ #### Constructor
158
+ ```typescript
159
+ new BonnysFortuneV1GameEngine()
160
+ ```
161
+ - Initializes game engine with configuration
162
+ - Auto-loads game configuration and reel strips from file system
163
+ - Sets up RNG client based on environment variables
164
+
165
+ #### Methods
166
+
167
+ **`getGameEngineInfo(): GameEngineInfo`**
168
+ Returns game metadata including code, version, RTP, and game type.
169
+
170
+ **`processCommand(publicState, privateState, command): Promise<CommandProcessingResult>`**
171
+ Main command processor that handles all game operations.
172
+
173
+ ### Commands
174
+
175
+ The game engine supports 6 different command types:
176
+
177
+ #### 1. INIT_SESSION_STATE
178
+
179
+ **Purpose**: Initialize game session state with bonus meters
180
+
181
+ **Payload**:
182
+ ```typescript
183
+ {
184
+ betAmountThresholds: number[];
185
+ defaultBetAmount: number;
186
+ }
187
+ ```
188
+
189
+ **Returns**: Initial public and private state
190
+
191
+ #### 2. SPIN
192
+
193
+ **Purpose**: Execute base game spin
194
+
195
+ **Payload**:
196
+ ```typescript
197
+ {
198
+ sessionId: string;
199
+ betAmount: number;
200
+ gameCode: string;
201
+ gameVersion: string;
202
+ lines?: number;
203
+ creditsPerLine?: number;
204
+ }
205
+ ```
206
+
207
+ **Returns**: Spin result with screen, wins, and potential bonus triggers
208
+
209
+ #### 3. START_BONUS_ROUND
210
+
211
+ **Purpose**: Start triggered bonus round
212
+
213
+ **Payload**:
214
+ ```typescript
215
+ {
216
+ sessionId: string;
217
+ bonusType: string;
218
+ betAmount: number;
219
+ gameCode: string;
220
+ gameVersion: string;
221
+ lines?: number;
222
+ creditsPerLine?: number;
223
+ bonusId: string;
224
+ }
225
+ ```
226
+
227
+ **Returns**: Complete bonus round results
228
+
229
+ #### 4. UPDATE_SESSION_STATE
230
+
231
+ **Purpose**: Update session state (e.g., redraw bonus meters after bonus completion)
232
+
233
+ **Payload**:
234
+ ```typescript
235
+ {
236
+ bonusMeters: {
237
+ redraw: string[];
238
+ };
239
+ }
240
+ ```
241
+
242
+ **Returns**: Updated state
243
+
244
+ #### 5. DEBUG_TRIGGER_BONUS (Testing/Admin)
245
+
246
+ **Purpose**: Force trigger specific bonus game
247
+
248
+ **Payload**: `DebugTriggerBonusCommand`
249
+
250
+ **Returns**: State with pending bonus
251
+
252
+ #### 6. DEBUG_UPDATE_BONUS_METER_PROGRESS (Testing/Admin)
253
+
254
+ **Purpose**: Manually set bonus meter progress
255
+
256
+ **Payload**: `DebugUpdateBonusMeterProgressCommand`
257
+
258
+ **Returns**: Updated meter state
259
+
260
+ ### State Types
261
+
262
+ #### PublicState (visible to player)
263
+ ```typescript
264
+ {
265
+ currentBetAmount: number;
266
+ bonusMeters: {
267
+ [bonusGameId: string]: {
268
+ symbolName: string;
269
+ symbolId: number;
270
+ threshold: number;
271
+ progress: number;
272
+ level: number;
273
+ }
274
+ }
275
+ }
276
+ ```
277
+
278
+ #### PrivateState (server-side only)
279
+ ```typescript
280
+ {
281
+ screen?: number[][];
282
+ nextSpinType: SpinType;
283
+ pendingBonuses: BonusTrigger[];
284
+ activeBonus?: BonusTrigger;
285
+ bonusMetersByAmount: Record<number, MetersState>;
286
+ }
287
+ ```
288
+
289
+ #### CommandProcessingResult
290
+ ```typescript
291
+ {
292
+ success: boolean;
293
+ publicState: BonnysFortunePublicState;
294
+ privateState: BonnysFortunePrivateState;
295
+ outcome?: any; // Spin/bonus result
296
+ message?: string;
297
+ rngOutcome?: RngOutcome; // RNG audit trail
298
+ }
299
+ ```
300
+
301
+ ## Game Mechanics
302
+
303
+ ### Base Game
304
+
305
+ - **Layout**: 5 reels × 3 rows
306
+ - **Win Evaluation**: 243 ways to win
307
+ - **Symbols**: 10 base symbols (H1-H3 high, M1-M3 medium, L1-L4 low) + Wild + Scatter
308
+ - **Symbol Multipliers**: Range from 2x (L4) to 500x (H1) for 5-of-a-kind
309
+ - **Bet Levels**: [10, 25, 50, 100, 500, 1000, 2000]
310
+ - **RTP**: 96%
311
+
312
+ ### Bonus Meters System
313
+
314
+ Progressive meters track collection of specific high-value symbols (H1, H2, H3):
315
+
316
+ - Each meter has dynamic threshold (min-max range)
317
+ - Progress increases when symbols land on reels
318
+ - 5 progress levels (0-25%, 25-65%, 65-80%, 80-100%, 100%+)
319
+ - Separate meters per bet amount
320
+ - Triggers corresponding bonus game when threshold reached
321
+
322
+ ### Bonus Game 1: Treasure Hunt
323
+
324
+ **Type**: Pick-style bonus
325
+
326
+ **Mechanics**:
327
+ - 25 symbols arranged in grid
328
+ - 6 steps/picks to reveal prizes
329
+ - 4 jackpot levels: Mini (20x), Minor (50x), Major (200x), Grand (2000x)
330
+ - Each pick reveals multiplier or jackpot
331
+ - Jackpot triggered when specific count revealed (Mini: 10, Minor: 7, Major: 5, Grand: 3)
332
+ - Starting counts configurable per jackpot level
333
+
334
+ **Trigger**: Bonus Meter 1 (H1 symbol collection) reaches threshold
335
+
336
+ **Weighted Selection**: Different probabilities for each prize tier
337
+
338
+ ### Bonus Game 2: Free Spins
339
+
340
+ **Type**: Free spins with multipliers
341
+
342
+ **Mechanics**:
343
+ - 6 spin type variants with different configurations
344
+ - Variable layouts: 5×3, 5×4, or 5×5 reels
345
+ - Variable spin counts: 4 or 8 spins
346
+ - Win multipliers: 1x to 20x
347
+ - Each variant has weight for random selection
348
+ - All wins multiplied by selected multiplier
349
+ - Uses alternative reel strips (bonus reels)
350
+
351
+ **Trigger**: Bonus Meter 2 (H2 symbol collection) reaches threshold
352
+
353
+ **Reel Options**: 4 reel strip options with configurable weights [1, 1, 4, 4]
354
+
355
+ ### Bonus Game 3: Steering to the Fortune (Wheel)
356
+
357
+ **Type**: Wheel of fortune / prize wheel
358
+
359
+ **Mechanics**:
360
+ - Spin wheel once
361
+ - 10 possible outcomes with weighted selection
362
+ - Prize types:
363
+ - **MULTIPLIER**: Direct win multipliers (5x, 8x, 10x, 15x, 25x, 40x, 80x)
364
+ - **BONUS**: Triggers another bonus game (collectFeature, bonusGame1, bonusGame2)
365
+ - Multiplier applied to bet amount for wins
366
+ - Bonus prizes trigger immediate transition to other bonus games
367
+
368
+ **Trigger**: Bonus Meter 3 (H3 symbol collection) reaches threshold
369
+
370
+ **Weights**: Lower multipliers more common, higher multipliers rare, bonus triggers rare
371
+
372
+ ### Collect Feature (Scatter Bonus)
373
+
374
+ **Type**: Multi-spin collect feature
375
+
376
+ **Mechanics**:
377
+ - Triggered by 3+ scatter symbols in base game
378
+ - 6 spins total
379
+ - Each spin reveals 1-3 special symbols
380
+ - Two symbol types:
381
+ - **Multiplier symbols** (CM1: 1x, CM2: 2x, CM3: 3x) - multiply total value
382
+ - **Value symbols** (CV1: 1, CV2: 2, CV3: 5) - add to total value
383
+ - Symbols can be "slashed" (disabled) based on weighted probability
384
+ - Checkpoints at specific spins require minimum multipliers/values (Spin 3: 0 multi, 1 value; Spin 2: 1 multi, 2 values)
385
+ - Final win = total values × total multipliers × bet stake
386
+ - Spins continue until all 6 complete
387
+
388
+ **Trigger**: 3 or more scatter symbols land anywhere on reels during base game
389
+
390
+ **Symbol Weights**: Values more common than multipliers, higher values rarer
391
+
392
+ ### Special Features
393
+
394
+ - **Wild Symbol**: Substitutes for all symbols except scatter
395
+ - **Progressive Meters**: Meter progress persists across spins until triggered
396
+ - **Bonus Chaining**: Steering to Fortune can trigger other bonuses
397
+ - **State Transitions**: Game tracks next expected spin type (base game vs bonus)
398
+
399
+ ## Integration Examples
400
+
401
+ ### Example 1: Basic RGS Integration
402
+
403
+ ```typescript
404
+ import 'reflect-metadata';
405
+ import { BonnysFortuneV1GameEngine } from '@omnitronix/bonnys-fortune-game-engine';
406
+
407
+ class GameSessionService {
408
+ private gameEngine: BonnysFortuneV1GameEngine;
409
+
410
+ constructor() {
411
+ this.gameEngine = new BonnysFortuneV1GameEngine();
412
+ }
413
+
414
+ async createSession(userId: string, betAmount: number) {
415
+ const initCommand = {
416
+ id: `init-${userId}-${Date.now()}`,
417
+ type: 'INIT_SESSION_STATE',
418
+ payload: {
419
+ betAmountThresholds: [10, 25, 50, 100, 500, 1000, 2000],
420
+ defaultBetAmount: betAmount
421
+ }
422
+ };
423
+
424
+ const result = await this.gameEngine.processCommand({}, {}, initCommand);
425
+
426
+ // Store result.publicState in database (visible to player)
427
+ // Store result.privateState securely (server-side only)
428
+ // Store result.rngOutcome for audit trail
429
+
430
+ return {
431
+ sessionId: userId,
432
+ publicState: result.publicState,
433
+ // Never send privateState to client!
434
+ };
435
+ }
436
+
437
+ async processSpin(sessionId: string, publicState: any, privateState: any, betAmount: number) {
438
+ const spinCommand = {
439
+ id: `spin-${sessionId}-${Date.now()}`,
440
+ type: 'SPIN',
441
+ payload: {
442
+ sessionId,
443
+ betAmount,
444
+ gameCode: 'bonnys-fortune',
445
+ gameVersion: '1.0.0',
446
+ lines: 10,
447
+ creditsPerLine: betAmount / 10
448
+ }
449
+ };
450
+
451
+ const result = await this.gameEngine.processCommand(
452
+ publicState,
453
+ privateState,
454
+ spinCommand
455
+ );
456
+
457
+ // Update states in database
458
+ // Log RNG outcome for compliance
459
+ // Return outcome to client
460
+
461
+ return {
462
+ outcome: result.outcome,
463
+ publicState: result.publicState,
464
+ rngOutcome: result.rngOutcome, // For audit
465
+ // privateState stays on server
466
+ };
467
+ }
468
+ }
469
+ ```
470
+
471
+ ### Example 2: Handling Bonus Triggers
472
+
473
+ ```typescript
474
+ async handleSpinResult(spinResult: any) {
475
+ const { outcome, publicState, privateState } = spinResult;
476
+
477
+ // Check if bonus was triggered
478
+ if (outcome.result.triggeredBonus) {
479
+ const bonus = outcome.result.triggeredBonus;
480
+
481
+ // Store pending bonus in session
482
+ // Notify player
483
+ // Wait for player to start bonus
484
+
485
+ return {
486
+ type: 'BONUS_TRIGGERED',
487
+ bonusType: bonus.bonusType,
488
+ bonusId: bonus.bonusId,
489
+ message: 'Bonus game triggered! Click to start.',
490
+ };
491
+ }
492
+
493
+ // Check if we should start pending bonus
494
+ if (privateState.pendingBonuses?.length > 0) {
495
+ const pendingBonus = privateState.pendingBonuses[0];
496
+
497
+ const bonusCommand = {
498
+ id: `bonus-${Date.now()}`,
499
+ type: 'START_BONUS_ROUND',
500
+ payload: {
501
+ sessionId: spinResult.sessionId,
502
+ bonusType: pendingBonus.bonusType,
503
+ betAmount: pendingBonus.betAmount,
504
+ gameCode: 'bonnys-fortune',
505
+ gameVersion: '1.0.0',
506
+ bonusId: pendingBonus.bonusId
507
+ }
508
+ };
509
+
510
+ const bonusResult = await this.gameEngine.processCommand(
511
+ publicState,
512
+ privateState,
513
+ bonusCommand
514
+ );
515
+
516
+ return {
517
+ type: 'BONUS_COMPLETED',
518
+ results: bonusResult.outcome.results, // All bonus spins
519
+ totalWin: bonusResult.outcome.results.reduce(
520
+ (sum, r) => sum + r.result.playerWinning, 0
521
+ ),
522
+ publicState: bonusResult.publicState,
523
+ };
524
+ }
525
+
526
+ return {
527
+ type: 'REGULAR_SPIN',
528
+ ...outcome
529
+ };
530
+ }
531
+ ```
532
+
533
+ ### Example 3: RNG Configuration for Different Environments
534
+
535
+ ```typescript
536
+ // Local development - use dummy RNG
537
+ // .env.development
538
+ RNG_CLIENT_MODE=local
539
+
540
+ // Staging - use external RNG service
541
+ // .env.staging
542
+ RNG_CLIENT_MODE=external
543
+ RNG_CLIENT_METHOD=rest
544
+ RNG_CLIENT_URL=https://rng-staging.omnitronix.com
545
+ RNG_CLIENT_POOL_SIZE=2000
546
+
547
+ // Production - use external RNG with gRPC
548
+ // .env.production
549
+ RNG_CLIENT_MODE=external
550
+ RNG_CLIENT_METHOD=grpc
551
+ RNG_CLIENT_GRPC_URL=grpc://rng-prod.omnitronix.com:50051
552
+ RNG_CLIENT_POOL_SIZE=5000
553
+ ```
554
+
555
+ ### Example 4: Testing with Debug Commands
556
+
557
+ ```typescript
558
+ async function testBonusGame() {
559
+ const gameEngine = new BonnysFortuneV1GameEngine();
560
+
561
+ // Initialize session
562
+ const initResult = await gameEngine.processCommand({}, {}, {
563
+ id: 'test-init',
564
+ type: 'INIT_SESSION_STATE',
565
+ payload: { betAmountThresholds: [10], defaultBetAmount: 10 }
566
+ });
567
+
568
+ // Force trigger treasure hunt bonus
569
+ const debugResult = await gameEngine.processCommand(
570
+ initResult.publicState,
571
+ initResult.privateState,
572
+ {
573
+ id: 'test-debug',
574
+ type: 'DEBUG_TRIGGER_BONUS',
575
+ payload: {
576
+ sessionId: 'test-session',
577
+ bonusType: 'bonusGame1',
578
+ betAmount: 10
579
+ }
580
+ }
581
+ );
582
+
583
+ // Now start the bonus
584
+ const bonusResult = await gameEngine.processCommand(
585
+ debugResult.publicState,
586
+ debugResult.privateState,
587
+ {
588
+ id: 'test-bonus-start',
589
+ type: 'START_BONUS_ROUND',
590
+ payload: {
591
+ sessionId: 'test-session',
592
+ bonusType: 'bonusGame1',
593
+ betAmount: 10,
594
+ gameCode: 'bonnys-fortune',
595
+ gameVersion: '1.0.0',
596
+ bonusId: debugResult.privateState.pendingBonuses[0].bonusId
597
+ }
598
+ }
599
+ );
600
+
601
+ console.log('Bonus results:', bonusResult.outcome);
602
+ }
603
+ ```
604
+
605
+ ## Development
606
+
607
+ ### Running Tests
608
+
609
+ ```bash
610
+ # Run all tests
611
+ npm test
612
+
613
+ # Run tests in watch mode
614
+ npm run test:watch
615
+
616
+ # Run with coverage
617
+ npm run test:coverage
618
+ ```
619
+
620
+ Tests use local RNG (`RNG_CLIENT_MODE=local`) automatically via `jest.setup.js`.
621
+
622
+ ### Building
623
+
624
+ ```bash
625
+ # Clean build
626
+ npm run clean
627
+ npm run build
628
+
629
+ # Development mode (watch)
630
+ npm run dev
631
+ ```
632
+
633
+ ### Linting
634
+
635
+ ```bash
636
+ # Check for issues
637
+ npm run lint
638
+
639
+ # Auto-fix issues
640
+ npm run lint:fix
641
+ ```
642
+
643
+ ## Configuration
644
+
645
+ Game configuration located in `src/config/game-logic-config/game-logic-config.ts`:
646
+ - Symbol definitions and multipliers
647
+ - Bonus game settings
648
+ - Meter thresholds
649
+ - Bet levels
650
+ - RTP configuration
651
+
652
+ Reel strips in `src/config/reel-strips-config/`:
653
+ - `reels-BASE.csv` - Base game reel strips
654
+ - `reels-BONUS.csv` - Bonus game 2 (free spins) reel strips
655
+
656
+ Configuration validated on startup using class-validator.
657
+
658
+ ## Type Exports
659
+
660
+ The package exports all necessary types for TypeScript integration:
661
+
662
+ ```typescript
663
+ import {
664
+ BonnysFortuneV1GameEngine,
665
+ GameEngineInfo,
666
+ CommandProcessingResult,
667
+ GameActionCommand,
668
+ BonnysFortunePublicState,
669
+ BonnysFortunePrivateState,
670
+ // ... additional types as needed
671
+ } from '@omnitronix/bonnys-fortune-game-engine';
672
+ ```
673
+
674
+ ## License
675
+
676
+ UNLICENSED - Internal use only for Omnitronix
677
+
678
+ ## Support
679
+
680
+ For questions or issues, contact the Omnitronix development team.