@omnitronix/bonnys-fortune-game-engine 1.3.1 → 1.3.3

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 (146) hide show
  1. package/README.md +15 -15
  2. package/dist/__tests__/bonnys-fortune-v1.game-engine.test.d.ts +1 -0
  3. package/dist/bonnys-fortune-v1.game-engine.d.ts +36 -0
  4. package/dist/bonnys-fortune-v1.game-engine.js +23 -12
  5. package/dist/bonnys-fortune-v1.game-engine.js.map +1 -1
  6. package/dist/config/game-logic-config/file-system.game-logic-config-loader.d.ts +4 -0
  7. package/dist/config/game-logic-config/game-logic-config-loader.d.ts +3 -0
  8. package/dist/config/game-logic-config/game-logic-config.d.ts +2 -0
  9. package/dist/config/game-logic-config/game-logic-config.js +2 -0
  10. package/dist/config/game-logic-config/game-logic-config.js.map +1 -1
  11. package/dist/config/reel-strips-config/file-system.reel-strips-config-loader.d.ts +4 -0
  12. package/dist/config/reel-strips-config/reel-strip-option.dto.d.ts +4 -0
  13. package/dist/config/reel-strips-config/reel-strips-config-loader.d.ts +3 -0
  14. package/dist/config/reel-strips-config/reel-strips-config.dto.d.ts +5 -0
  15. package/dist/config/reel-strips-config/reel.dto.d.ts +5 -0
  16. package/dist/domain/game-round.types.d.ts +9 -0
  17. package/dist/domain/mappers/reel-strips-config.mapper.d.ts +4 -0
  18. package/dist/domain/reel-strip-option.d.ts +7 -0
  19. package/dist/domain/reel-strips-config.d.ts +9 -0
  20. package/dist/domain/reel.d.ts +8 -0
  21. package/dist/domain/types/cheat-trigger-bonus-command.d.ts +5 -0
  22. package/dist/domain/types/{debug-trigger-bonus-command.js → cheat-trigger-bonus-command.js} +1 -1
  23. package/dist/domain/types/cheat-trigger-bonus-command.js.map +1 -0
  24. package/dist/domain/types/cheat-trigger-bonus-request.dto.d.ts +5 -0
  25. package/dist/domain/types/{debug-trigger-bonus-request.dto.js → cheat-trigger-bonus-request.dto.js} +1 -1
  26. package/dist/domain/types/cheat-trigger-bonus-request.dto.js.map +1 -0
  27. package/dist/domain/types/cheat-update-bonus-meter-progress-command.d.ts +5 -0
  28. package/dist/domain/types/{debug-update-bonus-meter-progress-command.js → cheat-update-bonus-meter-progress-command.js} +1 -1
  29. package/dist/domain/types/cheat-update-bonus-meter-progress-command.js.map +1 -0
  30. package/dist/domain/types/cheat-update-bonus-meter-progress-request.dto.d.ts +5 -0
  31. package/dist/domain/types/{debug-update-bonus-meter-progress-request.dto.js → cheat-update-bonus-meter-progress-request.dto.js} +1 -1
  32. package/dist/domain/types/cheat-update-bonus-meter-progress-request.dto.js.map +1 -0
  33. package/dist/domain/types/game-spin-command.d.ts +8 -0
  34. package/dist/domain/types/game-spin-input.dto.d.ts +8 -0
  35. package/dist/domain/types/game-spin-output.dto.d.ts +119 -0
  36. package/dist/domain/types/game-symbols.response.dto.d.ts +10 -0
  37. package/dist/domain/types/reel-strip-option.dto.d.ts +4 -0
  38. package/dist/domain/types/reel-strips-config.dto.d.ts +5 -0
  39. package/dist/domain/types/reel.dto.d.ts +5 -0
  40. package/dist/domain/types/start-bonus-round-command.d.ts +8 -0
  41. package/dist/domain/types/start-bonus-round-input.dto.d.ts +10 -0
  42. package/dist/game-engine.interface.d.ts +41 -0
  43. package/dist/helpers/generate-hash.d.ts +1 -0
  44. package/dist/helpers/number-helper.d.ts +23 -0
  45. package/dist/helpers/optional-boolean-mapper.d.ts +1 -0
  46. package/dist/helpers/uuid-helper.d.ts +3 -0
  47. package/dist/helpers/validation-helper.d.ts +14 -0
  48. package/dist/index.d.ts +2 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/logger/logger.d.ts +22 -0
  51. package/dist/logger/logger.js +4 -5
  52. package/dist/logger/logger.js.map +1 -1
  53. package/dist/logic/bonnys-fortune.game-logic.d.ts +32 -0
  54. package/dist/logic/bonnys-fortune.game-logic.js +15 -11
  55. package/dist/logic/bonnys-fortune.game-logic.js.map +1 -1
  56. package/dist/logic/bonnys-fortune.spin-generator.d.ts +19 -0
  57. package/dist/logic/bonnys-fortune.types.d.ts +170 -0
  58. package/dist/logic/bonnys-fortune.win-calculator.d.ts +10 -0
  59. package/dist/logic/game-logic-config.interface.d.ts +180 -0
  60. package/dist/logic/handlers/base-game.handler.d.ts +15 -0
  61. package/dist/logic/handlers/base-game.handler.js +5 -1
  62. package/dist/logic/handlers/base-game.handler.js.map +1 -1
  63. package/dist/logic/handlers/collect-feature-bonus.handler.d.ts +13 -0
  64. package/dist/logic/handlers/free-spins-bonus.handler.d.ts +11 -0
  65. package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.d.ts +11 -0
  66. package/dist/logic/handlers/treasure-hunt-bonus.handler.d.ts +15 -0
  67. package/dist/rng/DummyRngClient.d.ts +18 -0
  68. package/dist/rng/DummyRngClient.js +1 -8
  69. package/dist/rng/DummyRngClient.js.map +1 -1
  70. package/dist/rng/rng-client.factory.d.ts +7 -0
  71. package/dist/rng/rng-client.factory.js.map +1 -1
  72. package/dist/rng/rng-client.interface.d.ts +16 -0
  73. package/dist/rng/rng-service.d.ts +26 -0
  74. package/dist/validation/abstract-game-logic-config.validator.d.ts +4 -0
  75. package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.d.ts +131 -0
  76. package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.js +9 -0
  77. package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.js.map +1 -1
  78. package/dist/validation/bonnys-fortune/bonnys-fortune-config.validator.d.ts +5 -0
  79. package/dist/validation/custom-decorators/IsNestedIntArray.d.ts +2 -0
  80. package/dist/validation/game-logic-config-validation.service.d.ts +8 -0
  81. package/dist/validation/reel-strips-config-validation.service.d.ts +5 -0
  82. package/package.json +4 -3
  83. package/src/__tests__/bonnys-fortune-v1.game-engine.test.ts +80 -0
  84. package/src/bonnys-fortune-v1.game-engine.ts +314 -0
  85. package/src/config/game-logic-config/file-system.game-logic-config-loader.ts +27 -0
  86. package/src/config/game-logic-config/game-logic-config-loader.ts +3 -0
  87. package/src/config/game-logic-config/game-logic-config.ts +382 -0
  88. package/src/config/reel-strips-config/file-system.reel-strips-config-loader.ts +43 -0
  89. package/src/config/reel-strips-config/reel-strip-option.dto.ts +13 -0
  90. package/src/config/reel-strips-config/reel-strips-config-loader.ts +9 -0
  91. package/src/config/reel-strips-config/reel-strips-config.dto.ts +16 -0
  92. package/src/config/reel-strips-config/reel.dto.ts +16 -0
  93. package/src/config/reel-strips-config/reels-BASE.csv +52 -0
  94. package/src/config/reel-strips-config/reels-BONUS.csv +52 -0
  95. package/src/domain/game-round.types.ts +10 -0
  96. package/src/domain/mappers/reel-strips-config.mapper.ts +16 -0
  97. package/src/domain/reel-strip-option.ts +15 -0
  98. package/src/domain/reel-strips-config.ts +21 -0
  99. package/src/domain/reel.ts +17 -0
  100. package/src/domain/types/cheat-trigger-bonus-command.ts +5 -0
  101. package/src/domain/types/cheat-trigger-bonus-request.dto.ts +5 -0
  102. package/src/domain/types/cheat-update-bonus-meter-progress-command.ts +6 -0
  103. package/src/domain/types/cheat-update-bonus-meter-progress-request.dto.ts +5 -0
  104. package/src/domain/types/game-spin-command.ts +8 -0
  105. package/src/domain/types/game-spin-input.dto.ts +8 -0
  106. package/src/domain/types/game-spin-output.dto.ts +142 -0
  107. package/src/domain/types/game-symbols.response.dto.ts +11 -0
  108. package/src/domain/types/reel-strip-option.dto.ts +13 -0
  109. package/src/domain/types/reel-strips-config.dto.ts +15 -0
  110. package/src/domain/types/reel.dto.ts +15 -0
  111. package/src/domain/types/start-bonus-round-command.ts +8 -0
  112. package/src/domain/types/start-bonus-round-input.dto.ts +10 -0
  113. package/src/game-engine.interface.ts +59 -0
  114. package/src/helpers/generate-hash.ts +5 -0
  115. package/src/helpers/number-helper.ts +41 -0
  116. package/src/helpers/optional-boolean-mapper.ts +5 -0
  117. package/src/helpers/uuid-helper.ts +7 -0
  118. package/src/helpers/validation-helper.ts +27 -0
  119. package/src/index.ts +3 -0
  120. package/src/logger/logger.ts +178 -0
  121. package/src/logic/bonnys-fortune.game-logic.ts +490 -0
  122. package/src/logic/bonnys-fortune.spin-generator.ts +277 -0
  123. package/src/logic/bonnys-fortune.types.ts +223 -0
  124. package/src/logic/bonnys-fortune.win-calculator.ts +210 -0
  125. package/src/logic/game-logic-config.interface.ts +176 -0
  126. package/src/logic/handlers/base-game.handler.ts +221 -0
  127. package/src/logic/handlers/collect-feature-bonus.handler.ts +301 -0
  128. package/src/logic/handlers/free-spins-bonus.handler.ts +119 -0
  129. package/src/logic/handlers/steering-to-the-fortune-bonus.handler.ts +118 -0
  130. package/src/logic/handlers/treasure-hunt-bonus.handler.ts +232 -0
  131. package/src/rng/DummyRngClient.ts +108 -0
  132. package/src/rng/rng-client.factory.ts +27 -0
  133. package/src/rng/rng-client.interface.ts +38 -0
  134. package/src/rng/rng-service.ts +130 -0
  135. package/src/validation/abstract-game-logic-config.validator.ts +20 -0
  136. package/src/validation/bonnys-fortune/bonnys-fortune-config.dto.ts +379 -0
  137. package/src/validation/bonnys-fortune/bonnys-fortune-config.validator.ts +8 -0
  138. package/src/validation/custom-decorators/IsNestedIntArray.ts +29 -0
  139. package/src/validation/game-logic-config-validation.service.ts +28 -0
  140. package/src/validation/reel-strips-config-validation.service.ts +29 -0
  141. package/dist/__tests__/comprehensive.test.js +0 -741
  142. package/dist/__tests__/comprehensive.test.js.map +0 -1
  143. package/dist/domain/types/debug-trigger-bonus-command.js.map +0 -1
  144. package/dist/domain/types/debug-trigger-bonus-request.dto.js.map +0 -1
  145. package/dist/domain/types/debug-update-bonus-meter-progress-command.js.map +0 -1
  146. package/dist/domain/types/debug-update-bonus-meter-progress-request.dto.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnitronix/bonnys-fortune-game-engine",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Bonnys Fortune Game Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "author": "Omnitronix",
20
20
  "license": "UNLICENSED",
21
21
  "dependencies": {
22
- "@omnitronix/rng-client-core": "^1.0.10",
22
+ "@omnitronix/rng-client-core": "^1.1.1",
23
23
  "class-transformer": "^0.5.1",
24
24
  "class-validator": "^0.14.1",
25
25
  "reflect-metadata": "^0.1.13",
@@ -42,7 +42,8 @@
42
42
  "typescript": "^5.0.0"
43
43
  },
44
44
  "files": [
45
- "dist/**/*",
45
+ "dist",
46
+ "src",
46
47
  "README.md"
47
48
  ],
48
49
  "publishConfig": {
@@ -0,0 +1,80 @@
1
+ import { BonnysFortuneV1GameEngine } from '../bonnys-fortune-v1.game-engine';
2
+ import { GameSpinCommand } from '../domain/types/game-spin-command';
3
+ import { SpinType } from '../domain/types/game-spin-output.dto';
4
+
5
+ describe('BonnysFortuneV1GameEngine', () => {
6
+ let gameEngine: BonnysFortuneV1GameEngine;
7
+
8
+ beforeEach(() => {
9
+ gameEngine = new BonnysFortuneV1GameEngine();
10
+ });
11
+
12
+ it('should initialize the game engine and return info', async () => {
13
+ const info = gameEngine.getGameEngineInfo();
14
+ expect(info.gameCode).toBe('bonnys-fortune');
15
+ expect(info.version).toBe('1.0.0');
16
+ expect(info.rtp).toBe(96);
17
+ expect(info.gameType).toBe('slot');
18
+ });
19
+
20
+ it('should process a spin command successfully', async () => {
21
+ const initialPublicState = {
22
+ currentBetAmount: 1,
23
+ bonusMeters: {},
24
+ };
25
+ const initialPrivateState = {
26
+ nextSpinType: SpinType.BASE_GAME_SPIN,
27
+ pendingBonuses: [],
28
+ bonusMetersByAmount: {
29
+ 1: {
30
+ bonusGame1: {
31
+ symbolName: 'H1',
32
+ symbolId: 0,
33
+ threshold: 25,
34
+ progress: 0,
35
+ level: 1,
36
+ },
37
+ bonusGame2: {
38
+ symbolName: 'H2',
39
+ symbolId: 1,
40
+ threshold: 25,
41
+ progress: 0,
42
+ level: 1,
43
+ },
44
+ bonusGame3: {
45
+ symbolName: 'H3',
46
+ symbolId: 2,
47
+ threshold: 25,
48
+ progress: 0,
49
+ level: 1,
50
+ },
51
+ },
52
+ },
53
+ };
54
+
55
+ const spinCommand = {
56
+ id: 'test-command-123',
57
+ type: 'SPIN',
58
+ payload: {
59
+ sessionId: 'test-session-123',
60
+ betAmount: 1,
61
+ gameCode: 'bonnys-fortune',
62
+ gameVersion: '1.0.0',
63
+ lines: 10,
64
+ creditsPerLine: 1,
65
+ } as GameSpinCommand,
66
+ };
67
+
68
+ const result = await gameEngine.processCommand(
69
+ initialPublicState,
70
+ initialPrivateState,
71
+ spinCommand,
72
+ );
73
+
74
+ expect(result).toBeDefined();
75
+ expect(result.success).toBe(true);
76
+ expect(result.outcome).toBeDefined();
77
+ expect(result.publicState).toBeDefined();
78
+ expect(result.privateState).toBeDefined();
79
+ });
80
+ });
@@ -0,0 +1,314 @@
1
+ import {
2
+ BonnysFortunePrivateState,
3
+ BonnysFortunePublicState,
4
+ } from './logic/bonnys-fortune.types';
5
+ import {
6
+ CommandProcessingResult,
7
+ GameActionCommand,
8
+ GameEngine,
9
+ GameEngineConfig,
10
+ GameEngineInfo,
11
+ } from './game-engine.interface';
12
+
13
+ import { BonnysFortuneGameLogic } from './logic/bonnys-fortune.game-logic';
14
+ import { BonnysFortuneGameLogicConfigInterface } from './logic/game-logic-config.interface';
15
+ import { BonnysFortuneGameLogicConfigValidator } from './validation/bonnys-fortune/bonnys-fortune-config.validator';
16
+ import { CheatTriggerBonusCommand } from './domain/types/cheat-trigger-bonus-command';
17
+ import { CheatUpdateBonusMeterProgressCommand } from './domain/types/cheat-update-bonus-meter-progress-command';
18
+ import { FileSystemGameLogicConfigLoader } from './config/game-logic-config/file-system.game-logic-config-loader';
19
+ import { FileSystemReelStripsConfigLoader } from './config/reel-strips-config/file-system.reel-strips-config-loader';
20
+ import { GameLogicConfigLoader } from './config/game-logic-config/game-logic-config-loader';
21
+ import { GameLogicConfigValidationService } from './validation/game-logic-config-validation.service';
22
+ import { GameRoundType } from './domain/game-round.types';
23
+ import { GameSpinCommand } from './domain/types/game-spin-command';
24
+ import { Logger } from './logger/logger';
25
+ import { ReelStripsConfig } from './domain/reel-strips-config';
26
+ import { ReelStripsConfigLoader } from './config/reel-strips-config/reel-strips-config-loader';
27
+ import { ReelStripsConfigMapper } from './domain/mappers/reel-strips-config.mapper';
28
+ import { ReelStripsConfigValidationService } from './validation/reel-strips-config-validation.service';
29
+ import { RngClientFactory } from './rng/rng-client.factory';
30
+ import { RngService } from './rng/rng-service';
31
+ import { StartBonusRoundCommand } from './domain/types/start-bonus-round-command';
32
+
33
+ export class BonnysFortuneV1GameEngine implements GameEngine<
34
+ BonnysFortunePublicState,
35
+ BonnysFortunePrivateState,
36
+ any
37
+ > {
38
+ private readonly gameCode = 'bonnys-fortune';
39
+ private readonly version = '1.0.0';
40
+ private readonly rtp = 96;
41
+ private readonly gameType = 'slot';
42
+ private readonly gameName = "Bonny's Fortune";
43
+ private readonly provider = 'Omnitronix';
44
+ private config!: BonnysFortuneGameLogicConfigInterface;
45
+ private reels!: Map<GameRoundType, ReelStripsConfig>;
46
+
47
+ readonly configOutPort: GameLogicConfigLoader;
48
+ readonly reelsOutPort: ReelStripsConfigLoader;
49
+ readonly configValidator: GameLogicConfigValidationService;
50
+ readonly reelsValidator: ReelStripsConfigValidationService;
51
+ readonly reelMapper: ReelStripsConfigMapper;
52
+ readonly gameLogic: BonnysFortuneGameLogic;
53
+ readonly rngService: RngService;
54
+ readonly logger: Logger;
55
+
56
+ constructor(config: GameEngineConfig = {}) {
57
+ this.logger = new Logger('BonnysFortuneV1Engine');
58
+ this.configOutPort = new FileSystemGameLogicConfigLoader();
59
+ this.reelsOutPort = new FileSystemReelStripsConfigLoader();
60
+
61
+ const rngClient = RngClientFactory.create({ metrics: config.metrics });
62
+ this.rngService = new RngService(rngClient);
63
+
64
+ this.configValidator = new GameLogicConfigValidationService();
65
+ this.reelsValidator = new ReelStripsConfigValidationService();
66
+ this.reelMapper = new ReelStripsConfigMapper();
67
+ this.gameLogic = new BonnysFortuneGameLogic(this.rngService);
68
+ this.preload();
69
+ }
70
+
71
+ public getGameEngineInfo(): GameEngineInfo {
72
+ return {
73
+ gameCode: this.gameCode,
74
+ version: this.version,
75
+ rtp: this.rtp,
76
+ gameType: this.gameType,
77
+ gameName: this.gameName,
78
+ provider: this.provider,
79
+ };
80
+ }
81
+
82
+ private async preload(): Promise<void> {
83
+ this.logger.log('Preloading Bonnys Fortune engine');
84
+ this.configValidator.register(
85
+ this.gameCode,
86
+ new BonnysFortuneGameLogicConfigValidator(),
87
+ );
88
+ const rawConfig =
89
+ await this.configOutPort.loadConfig<BonnysFortuneGameLogicConfigInterface>(
90
+ this.gameCode,
91
+ this.version,
92
+ );
93
+ if (!rawConfig) {
94
+ throw new Error('Bonnys Fortune game logic config not found');
95
+ }
96
+ await this.configValidator.validate(this.gameCode, rawConfig);
97
+
98
+ const reelsCount = rawConfig.baseGame.baseGameConfig.layout.reels;
99
+ const optionsCount =
100
+ rawConfig.baseGame.baseGameConfig.reelsOptions.numberOfOptions;
101
+
102
+ const rawBaseGameReels = await this.reelsOutPort.loadConfig(
103
+ this.gameCode,
104
+ this.version,
105
+ GameRoundType.BASE,
106
+ reelsCount,
107
+ optionsCount,
108
+ );
109
+ await this.reelsValidator.validate(rawBaseGameReels);
110
+
111
+ const rawBonusGame2Reels = await this.reelsOutPort.loadConfig(
112
+ this.gameCode,
113
+ this.version,
114
+ GameRoundType.BONUS,
115
+ reelsCount,
116
+ optionsCount,
117
+ );
118
+ await this.reelsValidator.validate(rawBonusGame2Reels);
119
+
120
+ this.config = rawConfig;
121
+ this.reels = new Map([
122
+ [GameRoundType.BASE, this.reelMapper.mapRawToDomain(rawBaseGameReels)],
123
+ [GameRoundType.BONUS, this.reelMapper.mapRawToDomain(rawBonusGame2Reels)],
124
+ ]);
125
+ }
126
+
127
+ public async processCommand(
128
+ publicState: BonnysFortunePublicState,
129
+ privateState: BonnysFortunePrivateState,
130
+ command: GameActionCommand,
131
+ ): Promise<
132
+ CommandProcessingResult<
133
+ BonnysFortunePublicState,
134
+ BonnysFortunePrivateState,
135
+ any
136
+ >
137
+ > {
138
+ this.logger.log(`Processing command ${command.type}`);
139
+ this.rngService.registerCommand(command.id);
140
+ switch (command.type) {
141
+ case 'INIT_SESSION_STATE': {
142
+ const baseReels = this.reels.get(GameRoundType.BASE);
143
+ if (!baseReels) {
144
+ throw new Error('Base game reels not found');
145
+ }
146
+ const gameConfig = this.resolveInitGameConfig(command.payload ?? {});
147
+ const result = await this.gameLogic.initSessionState(
148
+ this.config,
149
+ baseReels,
150
+ gameConfig,
151
+ command.id,
152
+ );
153
+ const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
154
+ this.rngService.removeCommand(command.id);
155
+ return {
156
+ success: true,
157
+ publicState: result.publicState,
158
+ privateState: result.privateState,
159
+ outcome: result,
160
+ message: 'Session state initialized',
161
+ rngOutcome,
162
+ };
163
+ }
164
+
165
+ case 'SPIN': {
166
+ const params = command.payload as GameSpinCommand;
167
+ const baseReels = this.reels.get(GameRoundType.BASE);
168
+ if (!baseReels) {
169
+ throw new Error('Base game reels not found');
170
+ }
171
+ const outcome = await this.gameLogic.spin(
172
+ params,
173
+ publicState,
174
+ privateState,
175
+ this.config,
176
+ baseReels,
177
+ command.id,
178
+ );
179
+ const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
180
+ this.rngService.removeCommand(command.id);
181
+ return {
182
+ success: true,
183
+ publicState: outcome.publicState,
184
+ privateState: outcome.privateState,
185
+ outcome,
186
+ message: 'Spin completed',
187
+ rngOutcome,
188
+ };
189
+ }
190
+
191
+ case 'START_BONUS_ROUND': {
192
+ const params = command.payload as StartBonusRoundCommand;
193
+ const bonusReels = this.reels.get(GameRoundType.BONUS);
194
+ if (!bonusReels) {
195
+ throw new Error('Bonus game reels not found');
196
+ }
197
+ const outcome = await this.gameLogic.startBonusRound(
198
+ params,
199
+ publicState,
200
+ privateState,
201
+ this.config,
202
+ bonusReels,
203
+ command.id,
204
+ );
205
+
206
+ if (!outcome) {
207
+ throw new Error('Bonus round outcome not found');
208
+ }
209
+
210
+ const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
211
+ this.rngService.removeCommand(command.id);
212
+ return {
213
+ success: true,
214
+ publicState: outcome.publicState,
215
+ privateState: outcome.privateState,
216
+ outcome,
217
+ message: 'Bonus round started',
218
+ rngOutcome,
219
+ };
220
+ }
221
+
222
+ case 'CHEAT_TRIGGER_BONUS': {
223
+ const params = command.payload as CheatTriggerBonusCommand;
224
+ const result = await this.gameLogic.cheatTriggerBonus(
225
+ publicState,
226
+ privateState,
227
+ params,
228
+ );
229
+
230
+ const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
231
+ this.rngService.removeCommand(command.id);
232
+ return {
233
+ success: true,
234
+ publicState: result.publicState,
235
+ privateState: result.privateState,
236
+ outcome: result.outcome,
237
+ message: 'Bonus triggered',
238
+ rngOutcome,
239
+ };
240
+ }
241
+
242
+ case 'CHEAT_UPDATE_BONUS_METER_PROGRESS': {
243
+ const params = command.payload as CheatUpdateBonusMeterProgressCommand;
244
+ const result = this.gameLogic.cheatUpdateBonusMeterProgress(
245
+ publicState,
246
+ privateState,
247
+ params,
248
+ this.config,
249
+ );
250
+
251
+ const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
252
+ this.rngService.removeCommand(command.id);
253
+ return {
254
+ success: true,
255
+ publicState: result.publicState,
256
+ privateState: result.privateState,
257
+ outcome: result.outcome,
258
+ message: 'Bonus meter progress updated',
259
+ rngOutcome,
260
+ };
261
+ }
262
+
263
+ case 'GET_SYMBOLS': {
264
+ this.rngService.removeCommand(command.id);
265
+ return {
266
+ success: true,
267
+ publicState,
268
+ privateState,
269
+ outcome: this.gameLogic.getSymbols(this.config),
270
+ message: 'Symbols retrieved',
271
+ rngOutcome: undefined,
272
+ };
273
+ }
274
+
275
+ default: {
276
+ return {
277
+ success: false,
278
+ publicState: publicState,
279
+ privateState: privateState,
280
+ outcome: null,
281
+ message: 'Command not found',
282
+ rngOutcome: undefined,
283
+ };
284
+ }
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Resolves init session params from payload, falling back to game logic config when values are missing or invalid.
290
+ */
291
+ private resolveInitGameConfig(payload: Record<string, unknown>): {
292
+ betAmountThresholds: number[];
293
+ defaultBetAmount: number;
294
+ } {
295
+ const rawThresholds = payload.betAmountThresholds;
296
+ const validThresholds =
297
+ Array.isArray(rawThresholds) &&
298
+ rawThresholds.length > 0 &&
299
+ rawThresholds.every(
300
+ (n): n is number => typeof n === 'number' && Number.isFinite(n),
301
+ );
302
+ const betAmountThresholds = validThresholds
303
+ ? rawThresholds
304
+ : this.config.defaultBetAmountThresholds;
305
+
306
+ const rawDefault = payload.defaultBetAmount ?? this.config.defaultBetAmount;
307
+ const defaultBetAmount =
308
+ typeof rawDefault === 'number' && Number.isFinite(rawDefault)
309
+ ? rawDefault
310
+ : this.config.defaultBetAmount;
311
+
312
+ return { betAmountThresholds, defaultBetAmount };
313
+ }
314
+ }
@@ -0,0 +1,27 @@
1
+ import * as path from 'path';
2
+
3
+ import { GameLogicConfigLoader } from './game-logic-config-loader';
4
+ export class FileSystemGameLogicConfigLoader implements GameLogicConfigLoader {
5
+ public async loadConfig<T>(
6
+ gameId: string,
7
+ variantId: string,
8
+ ): Promise<T | null> {
9
+ try {
10
+ const configPath = path.resolve(__dirname, `./game-logic-config.js`);
11
+
12
+ const configModule = await import(configPath);
13
+
14
+ const config = <T>configModule.gameLogicConfig;
15
+
16
+ if (!config) {
17
+ console.warn(`Config object 'gameConfig' missing in ${configPath}`);
18
+ return null;
19
+ }
20
+
21
+ return config;
22
+ } catch (error) {
23
+ console.warn(`Failed to load config for ${gameId}/${variantId}:`, error);
24
+ return null;
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,3 @@
1
+ export interface GameLogicConfigLoader {
2
+ loadConfig<T>(gameId: string, variantId: string): Promise<T | null>;
3
+ }