@slot-engine/core 0.1.14 → 0.2.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.
package/dist/index.d.mts CHANGED
@@ -3,6 +3,76 @@ 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
+ booksUncompressed: (mode: string) => string;
54
+ lookupTable: (mode: string) => string;
55
+ lookupTableIndex: (mode: string) => string;
56
+ lookupTableSegmented: (mode: string) => string;
57
+ lookupTableSegmentedIndex: (mode: string) => string;
58
+ lookupTablePublish: (mode: string) => string;
59
+ forceRecords: (mode: string) => string;
60
+ forceKeys: (mode: string) => string;
61
+ indexJson: string;
62
+ publishFiles: string;
63
+ optimizationFiles: string;
64
+ simulationSummary: string;
65
+ statsPayouts: string;
66
+ statsSummary: string;
67
+ };
68
+ type TemporaryFilePaths = {
69
+ tempBooks: (mode: string, i: number) => string;
70
+ tempLookupTable: (mode: string, i: number) => string;
71
+ tempLookupTableSegmented: (mode: string, i: number) => string;
72
+ tempRecords: (mode: string) => string;
73
+ };
74
+ type FilePaths = PermanentFilePaths & TemporaryFilePaths;
75
+
6
76
  interface GameConfigOptions<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> {
7
77
  /**
8
78
  * The unique identifier of the game, used for configuration and identification.
@@ -68,18 +138,22 @@ interface GameConfigOptions<TGameModes extends AnyGameModes = AnyGameModes, TSym
68
138
  */
69
139
  rootDir?: string;
70
140
  }
71
- type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, "symbols">> & {
141
+ type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, "symbols" | "rootDir">> & {
72
142
  /**
73
143
  * A map of all symbols.
74
144
  */
75
145
  symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
76
- outputDir: string;
77
- rootDir: string;
78
146
  /**
79
147
  * A mapping of spin types to the number of scatter symbols required to trigger anticipation.
80
148
  */
81
149
  anticipationTriggers: Record<(typeof SPIN_TYPE)[keyof typeof SPIN_TYPE], number>;
82
150
  };
151
+ type GameMetadata = {
152
+ outputDir: string;
153
+ rootDir: string;
154
+ isCustomRoot: boolean;
155
+ paths: PermanentFilePaths;
156
+ };
83
157
 
84
158
  /**
85
159
  * Class for handling simulations of the slot game.
@@ -99,21 +173,33 @@ type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends
99
173
  */
100
174
  declare class Simulation {
101
175
  readonly gameConfigOpts: GameConfigOptions;
102
- readonly gameConfig: GameConfig;
176
+ readonly gameConfig: GameConfig & GameMetadata;
103
177
  readonly simRunsAmount: Partial<Record<string, number>>;
104
178
  readonly concurrency: number;
179
+ readonly makeUncompressedBooks: boolean;
105
180
  private debug;
106
181
  private actualSims;
107
182
  private wallet;
183
+ private summary;
108
184
  private recordsWriteStream;
109
185
  private hasWrittenRecord;
110
186
  private readonly streamHighWaterMark;
111
187
  private readonly maxPendingSims;
112
188
  private readonly maxHighWaterMark;
113
- private PATHS;
189
+ private panelPort;
190
+ private panelActive;
191
+ private panelWsUrl;
192
+ private socket;
193
+ private tui;
194
+ private tempBookIndexPaths;
195
+ private bookIndexMetas;
196
+ PATHS: FilePaths;
114
197
  private credits;
115
198
  private creditWaiters;
116
199
  private creditListenerInit;
200
+ private bookBuffers;
201
+ private bookBufferSizes;
202
+ private bookChunkIndexes;
117
203
  constructor(opts: SimulationOptions, gameConfigOpts: GameConfigOptions);
118
204
  runSimulation(opts: SimulationConfigOptions): Promise<void>;
119
205
  /**
@@ -165,7 +251,6 @@ declare class Simulation {
165
251
  protected handleGameFlow(ctx: GameContext): void;
166
252
  private writeRecords;
167
253
  private writeIndexJson;
168
- private writeBooksJson;
169
254
  /**
170
255
  * Compiles user configured game to JS for use in different Node processes
171
256
  */
@@ -175,11 +260,14 @@ declare class Simulation {
175
260
  * Generates reelset CSV files for all game modes.
176
261
  */
177
262
  private generateReelsetFiles;
263
+ private getTimeRemaining;
178
264
  private mergeCsv;
179
265
  /**
180
266
  * Confirms all pending records and adds them to the main records list.
181
267
  */
182
268
  confirmRecords(ctx: GameContext): void;
269
+ printSimulationSummary(): Promise<void>;
270
+ private sendSimulationStatus;
183
271
  }
184
272
  type SimulationOptions = {
185
273
  /**
@@ -208,10 +296,29 @@ type SimulationOptions = {
208
296
  * Default: 50
209
297
  */
210
298
  maxDiskBuffer?: number;
299
+ /**
300
+ * Whether to generate uncompressed book files alongside compressed ones.
301
+ */
302
+ makeUncompressedBooks?: boolean;
211
303
  };
212
304
  type SimulationConfigOptions = {
213
305
  debug?: boolean;
306
+ panelPort?: number;
214
307
  };
308
+ type SimulationSummary = Record<string, {
309
+ total: {
310
+ numSims: number;
311
+ bsWins: number;
312
+ fsWins: number;
313
+ rtp: number;
314
+ };
315
+ criteria: Record<string, {
316
+ numSims: number;
317
+ bsWins: number;
318
+ fsWins: number;
319
+ rtp: number;
320
+ }>;
321
+ }>;
215
322
 
216
323
  declare class ResultSet<TUserState extends AnyUserData> {
217
324
  criteria: string;
@@ -359,46 +466,6 @@ declare class AbstractService {
359
466
  constructor(ctx: () => GameContext);
360
467
  }
361
468
 
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
469
  declare class WinType {
403
470
  protected payout: number;
404
471
  protected winCombinations: WinCombination[];
@@ -605,8 +672,13 @@ declare class BoardService<TGameModes extends AnyGameModes = AnyGameModes, TSymb
605
672
 
606
673
  declare class Recorder {
607
674
  records: RecordItem[];
675
+ recordsMap: Map<string, RecordItem>;
608
676
  pendingRecords: PendingRecord[];
609
677
  constructor();
678
+ /**
679
+ * Intended for internal use only.
680
+ */
681
+ _reset(): void;
610
682
  }
611
683
  interface PendingRecord {
612
684
  bookId: number;
@@ -622,7 +694,7 @@ interface RecordItem {
622
694
  }
623
695
 
624
696
  declare class Book {
625
- readonly id: number;
697
+ id: number;
626
698
  criteria: string;
627
699
  events: BookEvent[];
628
700
  payout: number;
@@ -632,7 +704,11 @@ declare class Book {
632
704
  /**
633
705
  * Intended for internal use only.
634
706
  */
635
- setCriteria(criteria: string): void;
707
+ _reset(id: number, criteria: string): void;
708
+ /**
709
+ * Intended for internal use only.
710
+ */
711
+ _setCriteria(criteria: string): void;
636
712
  /**
637
713
  * Adds an event to the book.
638
714
  */
@@ -640,7 +716,7 @@ declare class Book {
640
716
  /**
641
717
  * Intended for internal use only.
642
718
  */
643
- serialize(): {
719
+ _serialize(): {
644
720
  id: number;
645
721
  criteria: string;
646
722
  events: BookEvent[];
@@ -658,13 +734,16 @@ interface BookOpts {
658
734
  id: number;
659
735
  criteria: string;
660
736
  }
737
+ interface WrittenBook {
738
+ id: number;
739
+ payoutMultiplier: number;
740
+ events: BookEvent[];
741
+ }
661
742
 
662
743
  declare class DataService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
663
744
  private recorder;
664
745
  private book;
665
746
  constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
666
- private ensureRecorder;
667
- private ensureBook;
668
747
  /**
669
748
  * Intended for internal use only.
670
749
  */
@@ -704,36 +783,16 @@ declare class DataService<TGameModes extends AnyGameModes = AnyGameModes, TSymbo
704
783
  * Adds an event to the book.
705
784
  */
706
785
  addBookEvent(event: Omit<BookEvent, "index">): void;
786
+ /**
787
+ * Write a log message to the terminal UI.
788
+ */
789
+ log(message: string): void;
707
790
  /**
708
791
  * Intended for internal use only.
709
792
  */
710
793
  _clearPendingRecords(): void;
711
794
  }
712
795
 
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
796
  declare class RandomNumberGenerator {
738
797
  mIdum: number;
739
798
  mIy: number;
@@ -907,6 +966,31 @@ declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbo
907
966
  }[];
908
967
  }
909
968
 
969
+ declare class RngService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
970
+ protected rng: RandomNumberGenerator;
971
+ constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
972
+ /**
973
+ * Random weighted selection from a set of items.
974
+ */
975
+ weightedRandom: <T extends Record<string, number>>(weights: T) => string;
976
+ /**
977
+ * Selects a random item from an array.
978
+ */
979
+ randomItem: <T>(array: T[]) => NonNullable<T>;
980
+ /**
981
+ * Shuffles an array.
982
+ */
983
+ shuffle: <T>(array: T[]) => T[];
984
+ /**
985
+ * Generates a random float between two values.
986
+ */
987
+ randomFloat: (low: number, high: number) => number;
988
+ /**
989
+ * Sets the seed for the RNG.
990
+ */
991
+ setSeedIfDifferent: (seed: number) => void;
992
+ }
993
+
910
994
  declare class Wallet {
911
995
  /**
912
996
  * Total win amount (as the bet multiplier) from all simulations.
@@ -958,6 +1042,10 @@ declare class Wallet {
958
1042
  */
959
1043
  protected currentTumbleWin: number;
960
1044
  constructor();
1045
+ /**
1046
+ * Intended for internal use only.
1047
+ */
1048
+ _reset(): void;
961
1049
  /**
962
1050
  * Updates the win for the current spin.
963
1051
  *
@@ -1058,7 +1146,6 @@ declare class Wallet {
1058
1146
  declare class WalletService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
1059
1147
  private wallet;
1060
1148
  constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
1061
- private ensureWallet;
1062
1149
  /**
1063
1150
  * Intended for internal use only.
1064
1151
  */
@@ -1138,6 +1225,36 @@ interface GameContextServices {
1138
1225
  rng: RngService;
1139
1226
  }
1140
1227
 
1228
+ interface PayoutStatistics {
1229
+ gameMode: string;
1230
+ allPayouts: {
1231
+ overall: Record<string, number>;
1232
+ criteria: Record<string, Record<string, number>>;
1233
+ };
1234
+ uniquePayouts: {
1235
+ overall: Record<string, number>;
1236
+ criteria: Record<string, Record<string, number>>;
1237
+ };
1238
+ }
1239
+ interface AnalysisOpts {
1240
+ gameModes: string[];
1241
+ }
1242
+ interface Statistics {
1243
+ gameMode: string;
1244
+ totalWeight: number;
1245
+ rtp: number;
1246
+ avgWin: number;
1247
+ minWin: number;
1248
+ maxWin: number;
1249
+ stdDev: number;
1250
+ variance: number;
1251
+ maxwinHitRate: number;
1252
+ nonZeroHitRate: number;
1253
+ nullHitRate: number;
1254
+ lessBetHitRate: number;
1255
+ uniquePayouts: number;
1256
+ }
1257
+
1141
1258
  declare class OptimizationParameters {
1142
1259
  protected parameters: OptimizationParametersOpts;
1143
1260
  constructor(opts?: Partial<OptimizationParametersOpts>);
@@ -1231,10 +1348,6 @@ type OptimzierGameModeConfig = Record<string, {
1231
1348
  parameters: OptimizationParameters;
1232
1349
  }>;
1233
1350
 
1234
- interface AnalysisOpts {
1235
- gameModes: string[];
1236
- }
1237
-
1238
1351
  /**
1239
1352
  * SlotGame class that encapsulates the game configuration and state.\
1240
1353
  * Main entry point for the slot game.
@@ -1270,14 +1383,7 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
1270
1383
  /**
1271
1384
  * Runs the configured tasks: simulation, optimization, and/or analysis.
1272
1385
  */
1273
- runTasks(opts?: {
1274
- doSimulation?: boolean;
1275
- doOptimization?: boolean;
1276
- doAnalysis?: boolean;
1277
- simulationOpts?: SimulationConfigOptions;
1278
- optimizationOpts?: OptimizationOpts;
1279
- analysisOpts?: AnalysisOpts;
1280
- }): Promise<void>;
1386
+ runTasks(opts?: TaskOptions): Promise<void>;
1281
1387
  /**
1282
1388
  * Gets the game configuration.
1283
1389
  */
@@ -1287,8 +1393,6 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
1287
1393
  basegame: number;
1288
1394
  freespins: number;
1289
1395
  };
1290
- outputDir: string;
1291
- rootDir: string;
1292
1396
  id: string;
1293
1397
  name: string;
1294
1398
  gameModes: TGameModes;
@@ -1297,8 +1401,26 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
1297
1401
  maxWinX: number;
1298
1402
  userState: TUserState;
1299
1403
  hooks: GameHooks<TGameModes, TSymbols, TUserState>;
1404
+ rootDir?: string;
1300
1405
  };
1406
+ getMetadata(): {
1407
+ outputDir: string;
1408
+ rootDir: string;
1409
+ isCustomRoot: boolean;
1410
+ paths: PermanentFilePaths;
1411
+ };
1412
+ clone(): SlotGame<TGameModes, TSymbols, TUserState>;
1413
+ }
1414
+ interface TaskOptions {
1415
+ _internal_ignore_args?: boolean;
1416
+ doSimulation?: boolean;
1417
+ doOptimization?: boolean;
1418
+ doAnalysis?: boolean;
1419
+ simulationOpts?: SimulationConfigOptions;
1420
+ optimizationOpts?: OptimizationOpts;
1421
+ analysisOpts?: AnalysisOpts;
1301
1422
  }
1423
+ type SlotGameType<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = SlotGame<TGameModes, TSymbols, TUserState>;
1302
1424
 
1303
1425
  type InferGameType<TGameModes extends AnyGameModes, TSymbols extends AnySymbols, TUserState extends AnyUserData> = SlotGame<TGameModes, TSymbols, TUserState>;
1304
1426
  /**
@@ -1343,12 +1465,17 @@ interface GameHooks<TGameModes extends AnyGameModes = AnyGameModes, TSymbols ext
1343
1465
  }
1344
1466
  type SpinType = (typeof SPIN_TYPE)[keyof typeof SPIN_TYPE];
1345
1467
  type Reels = GameSymbol[][];
1468
+ type LookupTable = [number, number, number][];
1469
+ type LookupTableSegmented = [number, string, number, number][];
1346
1470
 
1347
1471
  declare function createSlotGame<TGame>(opts: TGame extends InferGameType<infer G, infer S, infer U> ? GameConfigOptions<G, S, U> : never): TGame;
1348
1472
  declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState) => TUserState;
1349
1473
  declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
1350
1474
  declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
1351
1475
 
1476
+ declare function parseLookupTable(content: string): LookupTable;
1477
+ declare function parseLookupTableSegmented(content: string): LookupTableSegmented;
1478
+
1352
1479
  declare class LinesWinType extends WinType {
1353
1480
  protected lines: Record<number, number[]>;
1354
1481
  protected winCombinations: LineWinCombination[];
@@ -1463,7 +1590,7 @@ declare class GeneratedReelSet extends ReelSet {
1463
1590
  * Checks if a symbol can be placed at the target index without violating spacing rules.
1464
1591
  */
1465
1592
  private violatesSpacing;
1466
- generateReels(config: GameConfig): this;
1593
+ generateReels(config: GameConfig & GameMetadata): this;
1467
1594
  }
1468
1595
  interface GeneratedReelSetOptions extends ReelSetOptions {
1469
1596
  /**
@@ -1729,4 +1856,4 @@ interface StandaloneBoardOptions {
1729
1856
  padSymbols: number;
1730
1857
  }
1731
1858
 
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 };
1859
+ 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 };