@slot-engine/core 0.1.14 → 0.2.0

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/dist/index.d.mts CHANGED
@@ -3,6 +3,75 @@ declare const SPIN_TYPE: {
3
3
  readonly FREE_SPINS: "freespins";
4
4
  };
5
5
 
6
+ declare class GameSymbol {
7
+ readonly id: string;
8
+ readonly pays?: Record<number, number>;
9
+ readonly properties: Map<string, any>;
10
+ constructor(opts: GameSymbolOpts);
11
+ /**
12
+ * Compares this symbol to another symbol or a set of properties.
13
+ */
14
+ compare(symbolOrProperties?: GameSymbol | Record<string, any>): boolean;
15
+ /**
16
+ * Creates a clone of this GameSymbol.
17
+ */
18
+ clone(): GameSymbol;
19
+ }
20
+ interface GameSymbolOpts {
21
+ /**
22
+ * Unique identifier for the symbol, e.g. "W", "H1", "L5", etc.
23
+ */
24
+ id: string;
25
+ /**
26
+ * Paytable for the symbol, where the key is the number of symbols and the value is the payout multiplier.
27
+ */
28
+ pays?: Record<number, number>;
29
+ /**
30
+ * Additional properties for the symbol, e.g. `multiplier` or `isWild`.
31
+ *
32
+ * Properties can help identify special symbols.
33
+ *
34
+ * @example
35
+ * If your game has a "normal" scatter and a "super" scatter, you can define them like this:
36
+ *
37
+ * ```ts
38
+ * properties: {
39
+ * isScatter: true,
40
+ * }
41
+ * ```
42
+ */
43
+ properties?: Record<string, any>;
44
+ }
45
+
46
+ type PermanentFilePaths = {
47
+ base: string;
48
+ books: (mode: string) => string;
49
+ booksIndex: (mode: string, worker: number) => string;
50
+ booksIndexMeta: (mode: string) => string;
51
+ booksChunk: (mode: string, worker: number, chunk: number) => string;
52
+ booksCompressed: (mode: string) => string;
53
+ lookupTable: (mode: string) => string;
54
+ lookupTableIndex: (mode: string) => string;
55
+ lookupTableSegmented: (mode: string) => string;
56
+ lookupTableSegmentedIndex: (mode: string) => string;
57
+ lookupTablePublish: (mode: string) => string;
58
+ forceRecords: (mode: string) => string;
59
+ forceKeys: (mode: string) => string;
60
+ indexJson: string;
61
+ publishFiles: string;
62
+ optimizationFiles: string;
63
+ simulationSummary: string;
64
+ statsPayouts: string;
65
+ statsSummary: string;
66
+ };
67
+ type TemporaryFilePaths = {
68
+ tempBooks: (mode: string, i: number) => string;
69
+ tempLookupTable: (mode: string, i: number) => string;
70
+ tempLookupTableSegmented: (mode: string, i: number) => string;
71
+ tempRecords: (mode: string) => string;
72
+ };
73
+ type FilePaths = PermanentFilePaths & TemporaryFilePaths;
74
+
6
75
  interface GameConfigOptions<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> {
7
76
  /**
8
77
  * The unique identifier of the game, used for configuration and identification.
@@ -68,18 +137,22 @@ interface GameConfigOptions<TGameModes extends AnyGameModes = AnyGameModes, TSym
68
137
  */
69
138
  rootDir?: string;
70
139
  }
71
- type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, "symbols">> & {
140
+ type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, "symbols" | "rootDir">> & {
72
141
  /**
73
142
  * A map of all symbols.
74
143
  */
75
144
  symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
76
- outputDir: string;
77
- rootDir: string;
78
145
  /**
79
146
  * A mapping of spin types to the number of scatter symbols required to trigger anticipation.
80
147
  */
81
148
  anticipationTriggers: Record<(typeof SPIN_TYPE)[keyof typeof SPIN_TYPE], number>;
82
149
  };
150
+ type GameMetadata = {
151
+ outputDir: string;
152
+ rootDir: string;
153
+ isCustomRoot: boolean;
154
+ paths: PermanentFilePaths;
155
+ };
83
156
 
84
157
  /**
85
158
  * Class for handling simulations of the slot game.
@@ -99,21 +172,32 @@ type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends
99
172
  */
100
173
  declare class Simulation {
101
174
  readonly gameConfigOpts: GameConfigOptions;
102
- readonly gameConfig: GameConfig;
175
+ readonly gameConfig: GameConfig & GameMetadata;
103
176
  readonly simRunsAmount: Partial<Record<string, number>>;
104
177
  readonly concurrency: number;
105
178
  private debug;
106
179
  private actualSims;
107
180
  private wallet;
181
+ private summary;
108
182
  private recordsWriteStream;
109
183
  private hasWrittenRecord;
110
184
  private readonly streamHighWaterMark;
111
185
  private readonly maxPendingSims;
112
186
  private readonly maxHighWaterMark;
113
- private PATHS;
187
+ private panelPort;
188
+ private panelActive;
189
+ private panelWsUrl;
190
+ private socket;
191
+ private tui;
192
+ private tempBookIndexPaths;
193
+ private bookIndexMetas;
194
+ PATHS: FilePaths;
114
195
  private credits;
115
196
  private creditWaiters;
116
197
  private creditListenerInit;
198
+ private bookBuffers;
199
+ private bookBufferSizes;
200
+ private bookChunkIndexes;
117
201
  constructor(opts: SimulationOptions, gameConfigOpts: GameConfigOptions);
118
202
  runSimulation(opts: SimulationConfigOptions): Promise<void>;
119
203
  /**
@@ -165,7 +249,6 @@ declare class Simulation {
165
249
  protected handleGameFlow(ctx: GameContext): void;
166
250
  private writeRecords;
167
251
  private writeIndexJson;
168
- private writeBooksJson;
169
252
  /**
170
253
  * Compiles user configured game to JS for use in different Node processes
171
254
  */
@@ -175,11 +258,13 @@ declare class Simulation {
175
258
  * Generates reelset CSV files for all game modes.
176
259
  */
177
260
  private generateReelsetFiles;
261
+ private getTimeRemaining;
178
262
  private mergeCsv;
179
263
  /**
180
264
  * Confirms all pending records and adds them to the main records list.
181
265
  */
182
266
  confirmRecords(ctx: GameContext): void;
267
+ printSimulationSummary(): Promise<void>;
183
268
  }
184
269
  type SimulationOptions = {
185
270
  /**
@@ -211,7 +296,22 @@ type SimulationOptions = {
211
296
  };
212
297
  type SimulationConfigOptions = {
213
298
  debug?: boolean;
299
+ panelPort?: number;
214
300
  };
301
+ type SimulationSummary = Record<string, {
302
+ total: {
303
+ numSims: number;
304
+ bsWins: number;
305
+ fsWins: number;
306
+ rtp: number;
307
+ };
308
+ criteria: Record<string, {
309
+ numSims: number;
310
+ bsWins: number;
311
+ fsWins: number;
312
+ rtp: number;
313
+ }>;
314
+ }>;
215
315
 
216
316
  declare class ResultSet<TUserState extends AnyUserData> {
217
317
  criteria: string;
@@ -359,46 +459,6 @@ declare class AbstractService {
359
459
  constructor(ctx: () => GameContext);
360
460
  }
361
461
 
362
- declare class GameSymbol {
363
- readonly id: string;
364
- readonly pays?: Record<number, number>;
365
- readonly properties: Map<string, any>;
366
- constructor(opts: GameSymbolOpts);
367
- /**
368
- * Compares this symbol to another symbol or a set of properties.
369
- */
370
- compare(symbolOrProperties?: GameSymbol | Record<string, any>): boolean;
371
- /**
372
- * Creates a clone of this GameSymbol.
373
- */
374
- clone(): GameSymbol;
375
- }
376
- interface GameSymbolOpts {
377
- /**
378
- * Unique identifier for the symbol, e.g. "W", "H1", "L5", etc.
379
- */
380
- id: string;
381
- /**
382
- * Paytable for the symbol, where the key is the number of symbols and the value is the payout multiplier.
383
- */
384
- pays?: Record<number, number>;
385
- /**
386
- * Additional properties for the symbol, e.g. `multiplier` or `isWild`.
387
- *
388
- * Properties can help identify special symbols.
389
- *
390
- * @example
391
- * If your game has a "normal" scatter and a "super" scatter, you can define them like this:
392
- *
393
- * ```ts
394
- * properties: {
395
- * isScatter: true,
396
- * }
397
- * ```
398
- */
399
- properties?: Record<string, any>;
400
- }
401
-
402
462
  declare class WinType {
403
463
  protected payout: number;
404
464
  protected winCombinations: WinCombination[];
@@ -605,8 +665,13 @@ declare class BoardService<TGameModes extends AnyGameModes = AnyGameModes, TSymb
605
665
 
606
666
  declare class Recorder {
607
667
  records: RecordItem[];
668
+ recordsMap: Map<string, RecordItem>;
608
669
  pendingRecords: PendingRecord[];
609
670
  constructor();
671
+ /**
672
+ * Intended for internal use only.
673
+ */
674
+ _reset(): void;
610
675
  }
611
676
  interface PendingRecord {
612
677
  bookId: number;
@@ -622,7 +687,7 @@ interface RecordItem {
622
687
  }
623
688
 
624
689
  declare class Book {
625
- readonly id: number;
690
+ id: number;
626
691
  criteria: string;
627
692
  events: BookEvent[];
628
693
  payout: number;
@@ -632,7 +697,11 @@ declare class Book {
632
697
  /**
633
698
  * Intended for internal use only.
634
699
  */
635
- setCriteria(criteria: string): void;
700
+ _reset(id: number, criteria: string): void;
701
+ /**
702
+ * Intended for internal use only.
703
+ */
704
+ _setCriteria(criteria: string): void;
636
705
  /**
637
706
  * Adds an event to the book.
638
707
  */
@@ -640,7 +709,7 @@ declare class Book {
640
709
  /**
641
710
  * Intended for internal use only.
642
711
  */
643
- serialize(): {
712
+ _serialize(): {
644
713
  id: number;
645
714
  criteria: string;
646
715
  events: BookEvent[];
@@ -658,13 +727,16 @@ interface BookOpts {
658
727
  id: number;
659
728
  criteria: string;
660
729
  }
730
+ interface WrittenBook {
731
+ id: number;
732
+ payoutMultiplier: number;
733
+ events: BookEvent[];
734
+ }
661
735
 
662
736
  declare class DataService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
663
737
  private recorder;
664
738
  private book;
665
739
  constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
666
- private ensureRecorder;
667
- private ensureBook;
668
740
  /**
669
741
  * Intended for internal use only.
670
742
  */
@@ -704,36 +776,16 @@ declare class DataService<TGameModes extends AnyGameModes = AnyGameModes, TSymbo
704
776
  * Adds an event to the book.
705
777
  */
706
778
  addBookEvent(event: Omit<BookEvent, "index">): void;
779
+ /**
780
+ * Write a log message to the terminal UI.
781
+ */
782
+ log(message: string): void;
707
783
  /**
708
784
  * Intended for internal use only.
709
785
  */
710
786
  _clearPendingRecords(): void;
711
787
  }
712
788
 
713
- declare class RngService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
714
- protected rng: RandomNumberGenerator;
715
- constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
716
- /**
717
- * Random weighted selection from a set of items.
718
- */
719
- weightedRandom: <T extends Record<string, number>>(weights: T) => string;
720
- /**
721
- * Selects a random item from an array.
722
- */
723
- randomItem: <T>(array: T[]) => NonNullable<T>;
724
- /**
725
- * Shuffles an array.
726
- */
727
- shuffle: <T>(array: T[]) => T[];
728
- /**
729
- * Generates a random float between two values.
730
- */
731
- randomFloat: (low: number, high: number) => number;
732
- /**
733
- * Sets the seed for the RNG.
734
- */
735
- setSeedIfDifferent: (seed: number) => void;
736
- }
737
789
  declare class RandomNumberGenerator {
738
790
  mIdum: number;
739
791
  mIy: number;
@@ -907,6 +959,31 @@ declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbo
907
959
  }[];
908
960
  }
909
961
 
962
+ declare class RngService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
963
+ protected rng: RandomNumberGenerator;
964
+ constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
965
+ /**
966
+ * Random weighted selection from a set of items.
967
+ */
968
+ weightedRandom: <T extends Record<string, number>>(weights: T) => string;
969
+ /**
970
+ * Selects a random item from an array.
971
+ */
972
+ randomItem: <T>(array: T[]) => NonNullable<T>;
973
+ /**
974
+ * Shuffles an array.
975
+ */
976
+ shuffle: <T>(array: T[]) => T[];
977
+ /**
978
+ * Generates a random float between two values.
979
+ */
980
+ randomFloat: (low: number, high: number) => number;
981
+ /**
982
+ * Sets the seed for the RNG.
983
+ */
984
+ setSeedIfDifferent: (seed: number) => void;
985
+ }
986
+
910
987
  declare class Wallet {
911
988
  /**
912
989
  * Total win amount (as the bet multiplier) from all simulations.
@@ -958,6 +1035,10 @@ declare class Wallet {
958
1035
  */
959
1036
  protected currentTumbleWin: number;
960
1037
  constructor();
1038
+ /**
1039
+ * Intended for internal use only.
1040
+ */
1041
+ _reset(): void;
961
1042
  /**
962
1043
  * Updates the win for the current spin.
963
1044
  *
@@ -1058,7 +1139,6 @@ declare class Wallet {
1058
1139
  declare class WalletService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
1059
1140
  private wallet;
1060
1141
  constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
1061
- private ensureWallet;
1062
1142
  /**
1063
1143
  * Intended for internal use only.
1064
1144
  */
@@ -1138,6 +1218,36 @@ interface GameContextServices {
1138
1218
  rng: RngService;
1139
1219
  }
1140
1220
 
1221
+ interface PayoutStatistics {
1222
+ gameMode: string;
1223
+ allPayouts: {
1224
+ overall: Record<string, number>;
1225
+ criteria: Record<string, Record<string, number>>;
1226
+ };
1227
+ uniquePayouts: {
1228
+ overall: Record<string, number>;
1229
+ criteria: Record<string, Record<string, number>>;
1230
+ };
1231
+ }
1232
+ interface AnalysisOpts {
1233
+ gameModes: string[];
1234
+ }
1235
+ interface Statistics {
1236
+ gameMode: string;
1237
+ totalWeight: number;
1238
+ rtp: number;
1239
+ avgWin: number;
1240
+ minWin: number;
1241
+ maxWin: number;
1242
+ stdDev: number;
1243
+ variance: number;
1244
+ maxwinHitRate: number;
1245
+ nonZeroHitRate: number;
1246
+ nullHitRate: number;
1247
+ lessBetHitRate: number;
1248
+ uniquePayouts: number;
1249
+ }
1250
+
1141
1251
  declare class OptimizationParameters {
1142
1252
  protected parameters: OptimizationParametersOpts;
1143
1253
  constructor(opts?: Partial<OptimizationParametersOpts>);
@@ -1231,10 +1341,6 @@ type OptimzierGameModeConfig = Record<string, {
1231
1341
  parameters: OptimizationParameters;
1232
1342
  }>;
1233
1343
 
1234
- interface AnalysisOpts {
1235
- gameModes: string[];
1236
- }
1237
-
1238
1344
  /**
1239
1345
  * SlotGame class that encapsulates the game configuration and state.\
1240
1346
  * Main entry point for the slot game.
@@ -1270,14 +1376,7 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
1270
1376
  /**
1271
1377
  * Runs the configured tasks: simulation, optimization, and/or analysis.
1272
1378
  */
1273
- runTasks(opts?: {
1274
- doSimulation?: boolean;
1275
- doOptimization?: boolean;
1276
- doAnalysis?: boolean;
1277
- simulationOpts?: SimulationConfigOptions;
1278
- optimizationOpts?: OptimizationOpts;
1279
- analysisOpts?: AnalysisOpts;
1280
- }): Promise<void>;
1379
+ runTasks(opts?: TaskOptions): Promise<void>;
1281
1380
  /**
1282
1381
  * Gets the game configuration.
1283
1382
  */
@@ -1287,8 +1386,6 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
1287
1386
  basegame: number;
1288
1387
  freespins: number;
1289
1388
  };
1290
- outputDir: string;
1291
- rootDir: string;
1292
1389
  id: string;
1293
1390
  name: string;
1294
1391
  gameModes: TGameModes;
@@ -1297,8 +1394,26 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
1297
1394
  maxWinX: number;
1298
1395
  userState: TUserState;
1299
1396
  hooks: GameHooks<TGameModes, TSymbols, TUserState>;
1397
+ rootDir?: string;
1300
1398
  };
1399
+ getMetadata(): {
1400
+ outputDir: string;
1401
+ rootDir: string;
1402
+ isCustomRoot: boolean;
1403
+ paths: PermanentFilePaths;
1404
+ };
1405
+ clone(): SlotGame<TGameModes, TSymbols, TUserState>;
1406
+ }
1407
+ interface TaskOptions {
1408
+ _internal_ignore_args?: boolean;
1409
+ doSimulation?: boolean;
1410
+ doOptimization?: boolean;
1411
+ doAnalysis?: boolean;
1412
+ simulationOpts?: SimulationConfigOptions;
1413
+ optimizationOpts?: OptimizationOpts;
1414
+ analysisOpts?: AnalysisOpts;
1301
1415
  }
1416
+ type SlotGameType<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = SlotGame<TGameModes, TSymbols, TUserState>;
1302
1417
 
1303
1418
  type InferGameType<TGameModes extends AnyGameModes, TSymbols extends AnySymbols, TUserState extends AnyUserData> = SlotGame<TGameModes, TSymbols, TUserState>;
1304
1419
  /**
@@ -1343,12 +1458,17 @@ interface GameHooks<TGameModes extends AnyGameModes = AnyGameModes, TSymbols ext
1343
1458
  }
1344
1459
  type SpinType = (typeof SPIN_TYPE)[keyof typeof SPIN_TYPE];
1345
1460
  type Reels = GameSymbol[][];
1461
+ type LookupTable = [number, number, number][];
1462
+ type LookupTableSegmented = [number, string, number, number][];
1346
1463
 
1347
1464
  declare function createSlotGame<TGame>(opts: TGame extends InferGameType<infer G, infer S, infer U> ? GameConfigOptions<G, S, U> : never): TGame;
1348
1465
  declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState) => TUserState;
1349
1466
  declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
1350
1467
  declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
1351
1468
 
1469
+ declare function parseLookupTable(content: string): LookupTable;
1470
+ declare function parseLookupTableSegmented(content: string): LookupTableSegmented;
1471
+
1352
1472
  declare class LinesWinType extends WinType {
1353
1473
  protected lines: Record<number, number[]>;
1354
1474
  protected winCombinations: LineWinCombination[];
@@ -1463,7 +1583,7 @@ declare class GeneratedReelSet extends ReelSet {
1463
1583
  * Checks if a symbol can be placed at the target index without violating spacing rules.
1464
1584
  */
1465
1585
  private violatesSpacing;
1466
- generateReels(config: GameConfig): this;
1586
+ generateReels(config: GameConfig & GameMetadata): this;
1467
1587
  }
1468
1588
  interface GeneratedReelSetOptions extends ReelSetOptions {
1469
1589
  /**
@@ -1729,4 +1849,4 @@ interface StandaloneBoardOptions {
1729
1849
  padSymbols: number;
1730
1850
  }
1731
1851
 
1732
- export { type AnyGameModes, type AnySymbols, type AnyUserData, ClusterWinType, type GameContext, type GameHooks, GameMode, GameSymbol, GeneratedReelSet, type InferGameType, LinesWinType, ManywaysWinType, OptimizationConditions, OptimizationParameters, OptimizationScaling, type Reels, ResultSet, SPIN_TYPE, type SpinType, StandaloneBoard, StaticReelSet, type WinCombination, createSlotGame, defineGameModes, defineSymbols, defineUserState };
1852
+ export { type AnyGameModes, type AnySymbols, type AnyUserData, ClusterWinType, type GameContext, type GameHooks, GameMode, GameSymbol, GeneratedReelSet, type InferGameType, LinesWinType, type LookupTable, type LookupTableSegmented, ManywaysWinType, OptimizationConditions, OptimizationParameters, OptimizationScaling, type PayoutStatistics, RandomNumberGenerator, type RecordItem, type Reels, ResultSet, SPIN_TYPE, type SimulationSummary, type SlotGameType as SlotGame, type SpinType, StandaloneBoard, StaticReelSet, type Statistics, type WinCombination, type WrittenBook, createSlotGame, defineGameModes, defineSymbols, defineUserState, parseLookupTable, parseLookupTableSegmented };