@slot-engine/core 0.0.5 → 0.0.7
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 +1073 -857
- package/dist/index.d.ts +1073 -857
- package/dist/index.js +2245 -1867
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2239 -1859
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,514 +1,473 @@
|
|
|
1
|
-
declare
|
|
1
|
+
declare const SPIN_TYPE: {
|
|
2
|
+
readonly BASE_GAME: "basegame";
|
|
3
|
+
readonly FREE_SPINS: "freespins";
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
interface GameConfigOptions<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> {
|
|
7
|
+
/**
|
|
8
|
+
* The unique identifier of the game, used for configuration and identification.
|
|
9
|
+
*/
|
|
2
10
|
id: string;
|
|
3
|
-
pays?: Record<number, number>;
|
|
4
|
-
properties: Map<string, any>;
|
|
5
|
-
constructor(opts: GameSymbolOpts);
|
|
6
11
|
/**
|
|
7
|
-
*
|
|
12
|
+
* The name of the game, used for display purposes.
|
|
8
13
|
*/
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
interface GameSymbolOpts {
|
|
14
|
+
name: string;
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
16
|
+
* A GameMode is the core structure of a slot, defining the board,\
|
|
17
|
+
* bet cost, win type, and other properties.
|
|
14
18
|
*/
|
|
15
|
-
|
|
19
|
+
gameModes: TGameModes;
|
|
16
20
|
/**
|
|
17
|
-
*
|
|
21
|
+
* A list of all symbols that will appear on the reels.
|
|
18
22
|
*/
|
|
19
|
-
|
|
23
|
+
symbols: TSymbols;
|
|
20
24
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* Properties can help identify special symbols.
|
|
25
|
+
* A mapping from spin type to scatter counts to the number of free spins awarded.
|
|
24
26
|
*
|
|
25
27
|
* @example
|
|
26
|
-
* If your game has a "normal" scatter and a "super" scatter, you can define them like this:
|
|
27
|
-
*
|
|
28
28
|
* ```ts
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* scatterToFreespins: {
|
|
30
|
+
* [SPIN_TYPE.BASE_GAME]: {
|
|
31
|
+
* 3: 10,
|
|
32
|
+
* 4: 12,
|
|
33
|
+
* 5: 15,
|
|
34
|
+
* },
|
|
35
|
+
* [SPIN_TYPE.FREE_SPINS]: {
|
|
36
|
+
* 3: 6,
|
|
37
|
+
* 4: 8,
|
|
38
|
+
* 5: 10,
|
|
39
|
+
* },
|
|
40
|
+
* },
|
|
32
41
|
* ```
|
|
33
42
|
*/
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
declare function weightedRandom<T extends Record<string, number>>(weights: T, rng: RandomNumberGenerator): string;
|
|
38
|
-
declare class RandomNumberGenerator {
|
|
39
|
-
mIdum: number;
|
|
40
|
-
mIy: number;
|
|
41
|
-
mIv: Array<number>;
|
|
42
|
-
NTAB: number;
|
|
43
|
-
IA: number;
|
|
44
|
-
IM: number;
|
|
45
|
-
IQ: number;
|
|
46
|
-
IR: number;
|
|
47
|
-
NDIV: number;
|
|
48
|
-
AM: number;
|
|
49
|
-
RNMX: number;
|
|
50
|
-
protected _currentSeed: number;
|
|
51
|
-
constructor();
|
|
52
|
-
getCurrentSeed(): number;
|
|
53
|
-
protected setCurrentSeed(seed: number): void;
|
|
54
|
-
setSeed(seed: number): void;
|
|
55
|
-
setSeedIfDifferent(seed: number): void;
|
|
56
|
-
generateRandomNumber(): number;
|
|
57
|
-
randomFloat(low: number, high: number): number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* This class is responsible for generating reel sets for slot games based on specified configurations.
|
|
62
|
-
*
|
|
63
|
-
* **While it offers a high degree of customization, some configurations may lead to unsolvable scenarios.**
|
|
64
|
-
*
|
|
65
|
-
* If the reel generator is unable to fulfill niche constraints,\
|
|
66
|
-
* you might need to adjust your configuration, or edit the generated reels manually.\
|
|
67
|
-
* Setting a different seed may also help.
|
|
68
|
-
*/
|
|
69
|
-
declare class ReelGenerator {
|
|
70
|
-
id: string;
|
|
71
|
-
associatedGameModeName: string;
|
|
72
|
-
protected readonly symbolWeights: Map<string, number>;
|
|
73
|
-
protected readonly rowsAmount: number;
|
|
74
|
-
reels: Reels;
|
|
75
|
-
protected limitSymbolsToReels?: Record<string, number[]>;
|
|
76
|
-
protected readonly spaceBetweenSameSymbols?: number | Record<string, number>;
|
|
77
|
-
protected readonly spaceBetweenSymbols?: Record<string, Record<string, number>>;
|
|
78
|
-
protected readonly preferStackedSymbols?: number;
|
|
79
|
-
protected readonly symbolStacks?: Record<string, {
|
|
80
|
-
chance: number | Record<string, number>;
|
|
81
|
-
min?: number | Record<string, number>;
|
|
82
|
-
max?: number | Record<string, number>;
|
|
83
|
-
}>;
|
|
84
|
-
protected readonly symbolQuotas?: Record<string, number | Record<string, number>>;
|
|
85
|
-
csvPath: string;
|
|
86
|
-
overrideExisting: boolean;
|
|
87
|
-
rng: RandomNumberGenerator;
|
|
88
|
-
constructor(opts: ReelGeneratorOpts);
|
|
89
|
-
private validateConfig;
|
|
90
|
-
private isSymbolAllowedOnReel;
|
|
91
|
-
private resolveStacking;
|
|
92
|
-
private tryPlaceStack;
|
|
43
|
+
scatterToFreespins: Record<string, Record<number, number>>;
|
|
93
44
|
/**
|
|
94
|
-
*
|
|
45
|
+
* If set, this will pad the board with symbols on the top and bottom of the reels.\
|
|
46
|
+
* Useful for teasing symbols right above or below the active board.
|
|
47
|
+
*
|
|
48
|
+
* Default: 1
|
|
95
49
|
*/
|
|
96
|
-
|
|
97
|
-
generateReels(gameConf: AnyGameConfig): void;
|
|
50
|
+
padSymbols?: number;
|
|
98
51
|
/**
|
|
99
|
-
*
|
|
52
|
+
* The maximum win multiplier of the game, e.g. 5000 for a 5000x max win.
|
|
100
53
|
*/
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
interface ReelGeneratorOpts {
|
|
54
|
+
maxWinX: number;
|
|
104
55
|
/**
|
|
105
|
-
*
|
|
106
|
-
* Must be unique per game mode.
|
|
56
|
+
* Custom additional state that can be used in game flow logic.
|
|
107
57
|
*/
|
|
108
|
-
|
|
58
|
+
userState?: TUserState;
|
|
109
59
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
60
|
+
* Hooks are used to inject custom logic at specific points in the game flow.\
|
|
61
|
+
* Some required hooks must be implemented for certain features to work.
|
|
112
62
|
*/
|
|
113
|
-
|
|
63
|
+
hooks: GameHooks<TGameModes, TSymbols, TUserState>;
|
|
64
|
+
}
|
|
65
|
+
type GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, "symbols">> & {
|
|
114
66
|
/**
|
|
115
|
-
*
|
|
116
|
-
* Default is 250, but can be adjusted as needed.
|
|
67
|
+
* A map of all symbols.
|
|
117
68
|
*/
|
|
118
|
-
|
|
69
|
+
symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
|
|
70
|
+
outputDir: string;
|
|
119
71
|
/**
|
|
120
|
-
*
|
|
121
|
-
* This can be a single number for all symbols, or a mapping of symbol IDs to
|
|
122
|
-
* their respective spacing values.
|
|
123
|
-
*
|
|
124
|
-
* Must be 1 or higher, if set.
|
|
125
|
-
*
|
|
126
|
-
* **This is overridden by `symbolStacks`**
|
|
72
|
+
* A mapping of spin types to the number of scatter symbols required to trigger anticipation.
|
|
127
73
|
*/
|
|
128
|
-
|
|
74
|
+
anticipationTriggers: Record<(typeof SPIN_TYPE)[keyof typeof SPIN_TYPE], number>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
declare class Simulation {
|
|
78
|
+
readonly gameConfigOpts: GameConfigOptions;
|
|
79
|
+
readonly gameConfig: GameConfig;
|
|
80
|
+
readonly simRunsAmount: Partial<Record<string, number>>;
|
|
81
|
+
readonly concurrency: number;
|
|
82
|
+
private debug;
|
|
83
|
+
private actualSims;
|
|
84
|
+
private library;
|
|
85
|
+
private recorder;
|
|
86
|
+
private wallet;
|
|
87
|
+
constructor(opts: SimulationOptions, gameConfigOpts: GameConfigOptions);
|
|
88
|
+
runSimulation(opts: SimulationConfigOptions): Promise<void>;
|
|
129
89
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* Useful for preventing scatter and super scatter symbols from appearing too close to each other.
|
|
133
|
-
*
|
|
134
|
-
* **This is overridden by `symbolStacks`**
|
|
90
|
+
* Runs all simulations for a specific game mode.
|
|
135
91
|
*/
|
|
136
|
-
|
|
92
|
+
spawnWorkersForGameMode(opts: {
|
|
93
|
+
mode: string;
|
|
94
|
+
simNumsToCriteria: Record<number, string>;
|
|
95
|
+
}): Promise<void>;
|
|
96
|
+
callWorker(opts: {
|
|
97
|
+
basePath: string;
|
|
98
|
+
mode: string;
|
|
99
|
+
simStart: number;
|
|
100
|
+
simEnd: number;
|
|
101
|
+
index: number;
|
|
102
|
+
totalSims: number;
|
|
103
|
+
}): Promise<unknown>;
|
|
137
104
|
/**
|
|
138
|
-
*
|
|
139
|
-
* A value of 0 means no stacked symbols, while 100 means all symbols are stacked.
|
|
140
|
-
*
|
|
141
|
-
* This is only a preference. Symbols may still not be stacked if\
|
|
142
|
-
* other restrictions (like `spaceBetweenSameSymbols`) prevent it.
|
|
143
|
-
*
|
|
144
|
-
* **This is overridden by `symbolStacks`**
|
|
105
|
+
* Will run a single simulation until the specified criteria is met.
|
|
145
106
|
*/
|
|
146
|
-
|
|
107
|
+
runSingleSimulation(opts: {
|
|
108
|
+
simId: number;
|
|
109
|
+
mode: string;
|
|
110
|
+
criteria: string;
|
|
111
|
+
index: number;
|
|
112
|
+
}): void;
|
|
147
113
|
/**
|
|
148
|
-
*
|
|
114
|
+
* If a simulation does not meet the required criteria, reset the state to run it again.
|
|
149
115
|
*
|
|
150
|
-
*
|
|
151
|
-
* ```ts
|
|
152
|
-
* symbolStacks: {
|
|
153
|
-
* "W": {
|
|
154
|
-
* chance: { "1": 20, "2": 20, "3": 20, "4": 20 }, // 20% chance to be stacked on reels 2-5
|
|
155
|
-
* min: 2, // At least 2 wilds in a stack
|
|
156
|
-
* max: 4, // At most 4 wilds in a stack
|
|
157
|
-
* }
|
|
158
|
-
* }
|
|
159
|
-
* ```
|
|
116
|
+
* This also runs once before each simulation to ensure a clean state.
|
|
160
117
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
min?: number | Record<string, number>;
|
|
164
|
-
max?: number | Record<string, number>;
|
|
165
|
-
}>;
|
|
118
|
+
protected resetSimulation(ctx: GameContext): void;
|
|
119
|
+
protected resetState(ctx: GameContext): void;
|
|
166
120
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
121
|
+
* Contains and executes the entire game logic:
|
|
122
|
+
* - Drawing the board
|
|
123
|
+
* - Evaluating wins
|
|
124
|
+
* - Updating wallet
|
|
125
|
+
* - Handling free spins
|
|
126
|
+
* - Recording events
|
|
169
127
|
*
|
|
170
|
-
*
|
|
171
|
-
* ```ts
|
|
172
|
-
* limitSymbolsToReels: {
|
|
173
|
-
* "S": [0, 2, 4], // Remember that reels are 0-indexed.
|
|
174
|
-
* }
|
|
175
|
-
* ```
|
|
128
|
+
* You can customize the game flow by implementing the `onHandleGameFlow` hook in the game configuration.
|
|
176
129
|
*/
|
|
177
|
-
|
|
130
|
+
protected handleGameFlow(ctx: GameContext): void;
|
|
178
131
|
/**
|
|
179
|
-
*
|
|
180
|
-
* The quota (1-100%) defines how often a symbol should appear in the reelset, or in a specific reel.
|
|
181
|
-
*
|
|
182
|
-
* This is particularly useful for controlling the frequency of special symbols like scatters or wilds.
|
|
183
|
-
*
|
|
184
|
-
* Reels not provided for a symbol will use the weights from `symbolWeights`.
|
|
185
|
-
*
|
|
186
|
-
* _Any_ small quota will ensure that the symbol appears at least once on the reel.
|
|
132
|
+
* Creates a CSV file in the format "simulationId,weight,payout".
|
|
187
133
|
*
|
|
188
|
-
*
|
|
189
|
-
* ```ts
|
|
190
|
-
* symbolQuotas: {
|
|
191
|
-
* "S": 3, // 3% of symbols on each reel will be scatters
|
|
192
|
-
* "W": { "1": 10, "2": 5, "3": 3, "4": 1 }, // Wilds will appear with different quotas on selected reels
|
|
193
|
-
* }
|
|
194
|
-
* ```
|
|
134
|
+
* `weight` defaults to 1.
|
|
195
135
|
*/
|
|
196
|
-
|
|
136
|
+
private writeLookupTableCSV;
|
|
197
137
|
/**
|
|
198
|
-
*
|
|
138
|
+
* Creates a CSV file in the format "simulationId,criteria,payoutBase,payoutFreespins".
|
|
199
139
|
*/
|
|
200
|
-
|
|
140
|
+
private writeLookupTableSegmentedCSV;
|
|
141
|
+
private writeRecords;
|
|
142
|
+
private writeIndexJson;
|
|
143
|
+
private writeBooksJson;
|
|
144
|
+
private logSymbolOccurrences;
|
|
201
145
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* Default seed is `0`.
|
|
205
|
-
*
|
|
206
|
-
* Note: Seeds 0 and 1 produce the same results.
|
|
146
|
+
* Compiles user configured game to JS for use in different Node processes
|
|
207
147
|
*/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
declare class GameMode {
|
|
213
|
-
name: GameModeName;
|
|
214
|
-
reelsAmount: number;
|
|
215
|
-
symbolsPerReel: number[];
|
|
216
|
-
cost: number;
|
|
217
|
-
rtp: number;
|
|
218
|
-
reelSets: ReelGenerator[];
|
|
219
|
-
resultSets: ResultSet<any>[];
|
|
220
|
-
isBonusBuy: boolean;
|
|
221
|
-
constructor(opts: GameModeOpts);
|
|
222
|
-
}
|
|
223
|
-
interface GameModeOpts {
|
|
148
|
+
private preprocessFiles;
|
|
149
|
+
private getSimRangesForChunks;
|
|
150
|
+
private mergeRecords;
|
|
224
151
|
/**
|
|
225
|
-
*
|
|
152
|
+
* Generates reelset CSV files for all game modes.
|
|
226
153
|
*/
|
|
227
|
-
|
|
154
|
+
private generateReelsetFiles;
|
|
228
155
|
/**
|
|
229
|
-
*
|
|
156
|
+
* Confirms all pending records and adds them to the main records list.
|
|
230
157
|
*/
|
|
231
|
-
|
|
158
|
+
confirmRecords(ctx: GameContext): void;
|
|
159
|
+
}
|
|
160
|
+
type SimulationOptions = {
|
|
232
161
|
/**
|
|
233
|
-
*
|
|
234
|
-
* The number at an array index represents the number of symbols on that reel.
|
|
162
|
+
* Object containing the game modes and their respective simulation runs amount.
|
|
235
163
|
*/
|
|
236
|
-
|
|
164
|
+
simRunsAmount: Partial<Record<string, number>>;
|
|
237
165
|
/**
|
|
238
|
-
*
|
|
166
|
+
* Number of concurrent processes to use for simulations.
|
|
167
|
+
*
|
|
168
|
+
* Default: 6
|
|
239
169
|
*/
|
|
240
|
-
|
|
170
|
+
concurrency?: number;
|
|
171
|
+
};
|
|
172
|
+
type SimulationConfigOptions = {
|
|
173
|
+
debug?: boolean;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
declare class ResultSet<TUserState extends AnyUserData> {
|
|
177
|
+
criteria: string;
|
|
178
|
+
quota: number;
|
|
179
|
+
multiplier?: number;
|
|
180
|
+
reelWeights: ReelWeights<TUserState>;
|
|
181
|
+
userData?: Record<string, any>;
|
|
182
|
+
forceMaxWin?: boolean;
|
|
183
|
+
forceFreespins?: boolean;
|
|
184
|
+
evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => boolean;
|
|
185
|
+
constructor(opts: ResultSetOpts<TUserState>);
|
|
186
|
+
static assignCriteriaToSimulations(ctx: Simulation, gameModeName: string): Record<number, string>;
|
|
241
187
|
/**
|
|
242
|
-
*
|
|
188
|
+
* Checks if core criteria is met, e.g. target multiplier or max win.
|
|
243
189
|
*/
|
|
244
|
-
|
|
190
|
+
meetsCriteria(ctx: GameContext): boolean;
|
|
191
|
+
}
|
|
192
|
+
interface ResultSetOpts<TUserState extends AnyUserData> {
|
|
245
193
|
/**
|
|
246
|
-
*
|
|
247
|
-
|
|
194
|
+
* A short string to describe the criteria for this ResultSet.
|
|
195
|
+
*/
|
|
196
|
+
criteria: string;
|
|
197
|
+
/**
|
|
198
|
+
* The quota of spins, out of the total simulations, that must be forced to meet the specified criteria.\
|
|
199
|
+
* **Float from 0 to 1. Total quota of all ResultSets in a GameMode must be 1.**
|
|
200
|
+
*/
|
|
201
|
+
quota: number;
|
|
202
|
+
/**
|
|
203
|
+
* The required multiplier for a simulated spin to be accepted.
|
|
204
|
+
*/
|
|
205
|
+
multiplier?: number;
|
|
206
|
+
/**
|
|
207
|
+
* Configure the weights of the reels in this ResultSet.
|
|
248
208
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
209
|
+
* If you need to support dynamic / special reel weights based on the simulation context,\
|
|
210
|
+
* you can provide an `evaluate` function that returns the desired weights.
|
|
211
|
+
*
|
|
212
|
+
* If the `evaluate` function returns a falsy value, the usual spin type based weights will be used.
|
|
252
213
|
*
|
|
253
|
-
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* new ResultSet({
|
|
217
|
+
* criteria: "superFreespins",
|
|
218
|
+
* quota: 0.05,
|
|
219
|
+
* forceFreespins: true,
|
|
220
|
+
* reelWeights: {
|
|
221
|
+
* [SPIN_TYPE.BASE_GAME]: { base1: 1 },
|
|
222
|
+
* [SPIN_TYPE.FREE_SPINS]: { bonus1: 1, bonus2: 2 },
|
|
223
|
+
* evaluate: (ctx) => {
|
|
224
|
+
* if (ctx.state.userData.triggeredSuperFreespins) {
|
|
225
|
+
* return { superbonus: 1 }
|
|
226
|
+
* }
|
|
227
|
+
* }
|
|
228
|
+
* },
|
|
229
|
+
* userData: { forceSuperFreespins: true },
|
|
230
|
+
* }),
|
|
231
|
+
* ```
|
|
254
232
|
*/
|
|
255
|
-
|
|
233
|
+
reelWeights: ReelWeights<TUserState>;
|
|
256
234
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* in the simulations to ensure there are different frontend representations.
|
|
235
|
+
* Optional data to use when evaluating the criteria.\
|
|
236
|
+
* This can be used to pass additional context or parameters needed for the evaluation.
|
|
260
237
|
*/
|
|
261
|
-
|
|
238
|
+
userData?: Record<string, any>;
|
|
262
239
|
/**
|
|
263
|
-
*
|
|
240
|
+
* If set, this will force the game to always trigger a max win.
|
|
264
241
|
*/
|
|
265
|
-
|
|
242
|
+
forceMaxWin?: boolean;
|
|
243
|
+
/**
|
|
244
|
+
* If set, this will force the game to always trigger free spins.
|
|
245
|
+
*/
|
|
246
|
+
forceFreespins?: boolean;
|
|
247
|
+
/**
|
|
248
|
+
* Custom function to evaluate if the criteria is met.
|
|
249
|
+
*
|
|
250
|
+
* E.g. use this to check for free spins that upgraded to super free spins\
|
|
251
|
+
* or other arbitrary simulation criteria.
|
|
252
|
+
*/
|
|
253
|
+
evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => boolean;
|
|
254
|
+
}
|
|
255
|
+
interface ReelWeights<TUserState extends AnyUserData> {
|
|
256
|
+
[SPIN_TYPE.BASE_GAME]: Record<string, number>;
|
|
257
|
+
[SPIN_TYPE.FREE_SPINS]: Record<string, number>;
|
|
258
|
+
evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => Record<string, number> | undefined | null | false;
|
|
266
259
|
}
|
|
267
|
-
type GameModeName = "base" | "base-extra-chance-2x" | "base-extra-chance-10x" | "bonus" | string;
|
|
268
260
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
readonly gameConfig: GameConfig;
|
|
272
|
-
readonly simRunsAmount: Partial<Record<GameModeName, number>>;
|
|
273
|
-
private readonly concurrency;
|
|
274
|
-
private wallet;
|
|
275
|
-
private library;
|
|
276
|
-
readonly records: RecordItem[];
|
|
277
|
-
protected debug: boolean;
|
|
278
|
-
constructor(opts: SimulationConfigOpts, gameConfigOpts: CommonGameOptions);
|
|
279
|
-
runSimulation(opts: SimulationOpts): Promise<void>;
|
|
261
|
+
interface GameStateOptions<TUserState extends AnyUserData> {
|
|
262
|
+
currentSimulationId: number;
|
|
280
263
|
/**
|
|
281
|
-
*
|
|
264
|
+
* e.g. "base", "freespins", etc. (depending on the game config)
|
|
282
265
|
*/
|
|
283
|
-
|
|
284
|
-
mode: string;
|
|
285
|
-
simNumsToCriteria: Record<number, string>;
|
|
286
|
-
}): Promise<void>;
|
|
287
|
-
callWorker(opts: {
|
|
288
|
-
basePath: string;
|
|
289
|
-
mode: string;
|
|
290
|
-
simStart: number;
|
|
291
|
-
simEnd: number;
|
|
292
|
-
index: number;
|
|
293
|
-
totalSims: number;
|
|
294
|
-
}): Promise<unknown>;
|
|
266
|
+
currentGameMode: string;
|
|
295
267
|
/**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
* `weight` defaults to 1.
|
|
268
|
+
* Spin type constant as defined in `SPIN_TYPE`
|
|
299
269
|
*/
|
|
300
|
-
|
|
270
|
+
currentSpinType: SpinType;
|
|
301
271
|
/**
|
|
302
|
-
*
|
|
272
|
+
* The current ResultSet for the active simulation run.
|
|
303
273
|
*/
|
|
304
|
-
|
|
305
|
-
private static writeRecords;
|
|
306
|
-
private static writeIndexJson;
|
|
307
|
-
private static writeBooksJson;
|
|
308
|
-
private static logSymbolOccurrences;
|
|
274
|
+
currentResultSet: ResultSet<any>;
|
|
309
275
|
/**
|
|
310
|
-
*
|
|
276
|
+
* Whether the criteria in the ResultSet for the current simulation has been met.
|
|
311
277
|
*/
|
|
312
|
-
|
|
313
|
-
private getSimRangesForChunks;
|
|
314
|
-
private mergeRecords;
|
|
315
|
-
}
|
|
316
|
-
type SimulationConfigOpts = {
|
|
278
|
+
isCriteriaMet: boolean;
|
|
317
279
|
/**
|
|
318
|
-
*
|
|
280
|
+
* Number of freespins remaining in the current freespin round.
|
|
319
281
|
*/
|
|
320
|
-
|
|
282
|
+
currentFreespinAmount: number;
|
|
321
283
|
/**
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
* Default: 6
|
|
284
|
+
* Total amount of freespins awarded during the active simulation.
|
|
325
285
|
*/
|
|
326
|
-
|
|
327
|
-
};
|
|
328
|
-
/**
|
|
329
|
-
* @internal
|
|
330
|
-
*/
|
|
331
|
-
type AnySimulationContext<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = SimulationContext<TGameModes, TSymbols, TUserState>;
|
|
332
|
-
/**
|
|
333
|
-
* @internal
|
|
334
|
-
*/
|
|
335
|
-
declare class SimulationContext<TGameModes extends AnyGameModes, TSymbols extends AnySymbols, TUserState extends AnyUserData> extends Board<TGameModes, TSymbols, TUserState> {
|
|
336
|
-
constructor(opts: CommonGameOptions<any, any, TUserState>);
|
|
337
|
-
actualSims: number;
|
|
286
|
+
totalFreespinAmount: number;
|
|
338
287
|
/**
|
|
339
|
-
*
|
|
288
|
+
* Custom user data that can be used in game flow logic.
|
|
340
289
|
*/
|
|
341
|
-
|
|
342
|
-
simId: number;
|
|
343
|
-
mode: string;
|
|
344
|
-
criteria: string;
|
|
345
|
-
index: number;
|
|
346
|
-
}): void;
|
|
290
|
+
userData: TUserState;
|
|
347
291
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
* This also runs once before each simulation to ensure a clean state.
|
|
292
|
+
* Whether a max win has been triggered during the active simulation.
|
|
351
293
|
*/
|
|
352
|
-
|
|
294
|
+
triggeredMaxWin: boolean;
|
|
353
295
|
/**
|
|
354
|
-
*
|
|
355
|
-
* - Drawing the board
|
|
356
|
-
* - Evaluating wins
|
|
357
|
-
* - Updating wallet
|
|
358
|
-
* - Handling free spins
|
|
359
|
-
* - Recording events
|
|
360
|
-
*
|
|
361
|
-
* You can customize the game flow by implementing the `onHandleGameFlow` hook in the game configuration.
|
|
296
|
+
* Whether freespins have been triggered during the active simulation.
|
|
362
297
|
*/
|
|
363
|
-
|
|
298
|
+
triggeredFreespins: boolean;
|
|
364
299
|
}
|
|
365
|
-
|
|
366
|
-
|
|
300
|
+
declare function createGameState<TUserState extends AnyUserData = AnyUserData>(opts?: Partial<GameStateOptions<TUserState>>): {
|
|
301
|
+
currentSimulationId: number;
|
|
302
|
+
currentGameMode: string;
|
|
303
|
+
currentSpinType: SpinType;
|
|
304
|
+
currentResultSet: ResultSet<any>;
|
|
305
|
+
isCriteriaMet: boolean;
|
|
306
|
+
currentFreespinAmount: number;
|
|
307
|
+
totalFreespinAmount: number;
|
|
308
|
+
userData: TUserState;
|
|
309
|
+
triggeredMaxWin: boolean;
|
|
310
|
+
triggeredFreespins: boolean;
|
|
311
|
+
};
|
|
312
|
+
type GameState<TUserState extends AnyUserData = AnyUserData> = ReturnType<typeof createGameState<TUserState>>;
|
|
313
|
+
|
|
314
|
+
declare class AbstractService {
|
|
315
|
+
/**
|
|
316
|
+
* Function that returns the current game context.
|
|
317
|
+
*/
|
|
318
|
+
protected ctx: () => GameContext;
|
|
319
|
+
constructor(ctx: () => GameContext);
|
|
367
320
|
}
|
|
368
321
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
322
|
+
declare class GameSymbol {
|
|
323
|
+
readonly id: string;
|
|
324
|
+
readonly pays?: Record<number, number>;
|
|
325
|
+
readonly properties: Map<string, any>;
|
|
326
|
+
constructor(opts: GameSymbolOpts);
|
|
373
327
|
/**
|
|
374
|
-
*
|
|
328
|
+
* Compares this symbol to another symbol or a set of properties.
|
|
375
329
|
*/
|
|
376
|
-
|
|
330
|
+
compare(symbolOrProperties?: GameSymbol | Record<string, any>): boolean;
|
|
331
|
+
}
|
|
332
|
+
interface GameSymbolOpts {
|
|
377
333
|
/**
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* @example
|
|
381
|
-
* ```ts
|
|
382
|
-
* {
|
|
383
|
-
* basegame: 50,
|
|
384
|
-
* freespins: 100,
|
|
385
|
-
* superfreespins: 200,
|
|
386
|
-
* }
|
|
387
|
-
* ```
|
|
334
|
+
* Unique identifier for the symbol, e.g. "W", "H1", "L5", etc.
|
|
388
335
|
*/
|
|
389
|
-
|
|
390
|
-
basegame: number;
|
|
391
|
-
freespins: number;
|
|
392
|
-
};
|
|
336
|
+
id: string;
|
|
393
337
|
/**
|
|
394
|
-
*
|
|
338
|
+
* Paytable for the symbol, where the key is the number of symbols and the value is the payout multiplier.
|
|
395
339
|
*/
|
|
396
|
-
|
|
340
|
+
pays?: Record<number, number>;
|
|
397
341
|
/**
|
|
398
|
-
*
|
|
342
|
+
* Additional properties for the symbol, e.g. `multiplier` or `isWild`.
|
|
343
|
+
*
|
|
344
|
+
* Properties can help identify special symbols.
|
|
399
345
|
*
|
|
400
346
|
* @example
|
|
347
|
+
* If your game has a "normal" scatter and a "super" scatter, you can define them like this:
|
|
348
|
+
*
|
|
401
349
|
* ```ts
|
|
402
|
-
* {
|
|
403
|
-
*
|
|
404
|
-
* freespins: 100,
|
|
405
|
-
* superfreespins: 200,
|
|
350
|
+
* properties: {
|
|
351
|
+
* isScatter: true,
|
|
406
352
|
* }
|
|
407
353
|
* ```
|
|
408
354
|
*/
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
355
|
+
properties?: Record<string, any>;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
declare class BoardService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
|
|
359
|
+
private board;
|
|
360
|
+
constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
|
|
413
361
|
/**
|
|
414
|
-
*
|
|
415
|
-
*
|
|
362
|
+
* Resets the board to an empty state.\
|
|
363
|
+
* This is called before drawing a new board.
|
|
416
364
|
*/
|
|
417
|
-
|
|
365
|
+
resetBoard(): void;
|
|
418
366
|
/**
|
|
419
|
-
*
|
|
367
|
+
* Gets the current reels and symbols on the board.
|
|
420
368
|
*/
|
|
421
|
-
|
|
422
|
-
|
|
369
|
+
getBoardReels(): Reels;
|
|
370
|
+
getPaddingTop(): Reels;
|
|
371
|
+
getPaddingBottom(): Reels;
|
|
372
|
+
getAnticipation(): boolean[];
|
|
373
|
+
private resetReels;
|
|
423
374
|
/**
|
|
424
|
-
*
|
|
375
|
+
* Sets the anticipation value for a specific reel.
|
|
376
|
+
*/
|
|
377
|
+
setAnticipationForReel(reelIndex: number, value: boolean): void;
|
|
378
|
+
/**
|
|
379
|
+
* Counts how many symbols matching the criteria are on a specific reel.
|
|
380
|
+
*/
|
|
381
|
+
countSymbolsOnReel(symbolOrProperties: GameSymbol | Record<string, any>, reelIndex: number): number;
|
|
382
|
+
/**
|
|
383
|
+
* Counts how many symbols matching the criteria are on the board.
|
|
425
384
|
*
|
|
426
|
-
*
|
|
427
|
-
* Or generally call this to add wins during a spin.
|
|
385
|
+
* Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.
|
|
428
386
|
*
|
|
429
|
-
*
|
|
387
|
+
* Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.
|
|
430
388
|
*/
|
|
431
|
-
|
|
389
|
+
countSymbolsOnBoard(symbolOrProperties: GameSymbol | Record<string, any>): [number, Record<number, number>];
|
|
432
390
|
/**
|
|
433
|
-
*
|
|
391
|
+
* Checks if a symbol appears more than once on any reel in the current reel set.
|
|
434
392
|
*
|
|
435
|
-
*
|
|
436
|
-
* and after a (free) spin is played out to finalize the win.
|
|
393
|
+
* Useful to check for "forbidden" generations, e.g. 2 scatters on one reel.
|
|
437
394
|
*/
|
|
438
|
-
|
|
395
|
+
isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol): boolean;
|
|
439
396
|
/**
|
|
440
|
-
*
|
|
397
|
+
* Gets all reel stops (positions) where the specified symbol appears in the current reel set.\
|
|
398
|
+
* Returns an array of arrays, where each inner array contains the positions for the corresponding reel.
|
|
441
399
|
*/
|
|
442
|
-
|
|
400
|
+
getReelStopsForSymbol(reels: Reels, symbol: GameSymbol): number[][];
|
|
443
401
|
/**
|
|
444
|
-
*
|
|
402
|
+
* Combines multiple arrays of reel stops into a single array of reel stops.\
|
|
445
403
|
*/
|
|
446
|
-
|
|
447
|
-
basegame: number;
|
|
448
|
-
freespins: number;
|
|
449
|
-
};
|
|
404
|
+
combineReelStops(...reelStops: number[][][]): number[][];
|
|
450
405
|
/**
|
|
451
|
-
*
|
|
406
|
+
* From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.
|
|
407
|
+
*
|
|
408
|
+
* Mostly useful for placing scatter symbols on the board.
|
|
452
409
|
*/
|
|
453
|
-
|
|
410
|
+
getRandomReelStops(reels: Reels, reelStops: number[][], amount: number): Record<number, number>;
|
|
454
411
|
/**
|
|
455
|
-
*
|
|
412
|
+
* Selects a random reel set based on the configured weights of the current result set.\
|
|
413
|
+
* Returns the reels as arrays of GameSymbols.
|
|
456
414
|
*/
|
|
457
|
-
|
|
458
|
-
basegame: number;
|
|
459
|
-
freespins: number;
|
|
460
|
-
};
|
|
415
|
+
getRandomReelset(): Reels;
|
|
461
416
|
/**
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
* After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`
|
|
417
|
+
* Draws a board using specified reel stops.
|
|
465
418
|
*/
|
|
466
|
-
|
|
419
|
+
drawBoardWithForcedStops(reels: Reels, forcedStops: Record<string, number>): void;
|
|
467
420
|
/**
|
|
468
|
-
*
|
|
421
|
+
* Draws a board using random reel stops.
|
|
469
422
|
*/
|
|
470
|
-
|
|
423
|
+
drawBoardWithRandomStops(reels: Reels): void;
|
|
424
|
+
private drawBoardMixed;
|
|
471
425
|
/**
|
|
472
|
-
*
|
|
426
|
+
* Tumbles the board. All given symbols will be deleted and new symbols will fall from the top.
|
|
473
427
|
*/
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
428
|
+
tumbleBoard(symbolsToDelete: Array<{
|
|
429
|
+
reelIdx: number;
|
|
430
|
+
rowIdx: number;
|
|
431
|
+
}>): void;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
declare class Recorder {
|
|
435
|
+
records: RecordItem[];
|
|
436
|
+
pendingRecords: PendingRecord[];
|
|
437
|
+
constructor();
|
|
438
|
+
}
|
|
439
|
+
interface PendingRecord {
|
|
440
|
+
bookId: number;
|
|
441
|
+
properties: Record<string, string>;
|
|
442
|
+
}
|
|
443
|
+
interface RecordItem {
|
|
444
|
+
search: Array<{
|
|
445
|
+
name: string;
|
|
446
|
+
value: string;
|
|
447
|
+
}>;
|
|
448
|
+
timesTriggered: number;
|
|
449
|
+
bookIds: number[];
|
|
491
450
|
}
|
|
492
451
|
|
|
493
452
|
declare class Book {
|
|
494
|
-
id: number;
|
|
453
|
+
readonly id: number;
|
|
495
454
|
criteria: string;
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
455
|
+
events: BookEvent[];
|
|
456
|
+
payout: number;
|
|
457
|
+
basegameWins: number;
|
|
458
|
+
freespinsWins: number;
|
|
500
459
|
constructor(opts: BookOpts);
|
|
460
|
+
/**
|
|
461
|
+
* Intended for internal use only.
|
|
462
|
+
*/
|
|
463
|
+
setCriteria(criteria: string): void;
|
|
501
464
|
/**
|
|
502
465
|
* Adds an event to the book.
|
|
503
466
|
*/
|
|
504
467
|
addEvent(event: Omit<BookEvent, "index">): void;
|
|
505
468
|
/**
|
|
506
|
-
*
|
|
469
|
+
* Intended for internal use only.
|
|
507
470
|
*/
|
|
508
|
-
writePayout(ctx: AnySimulationContext): void;
|
|
509
|
-
getPayout(): number;
|
|
510
|
-
getBasegameWins(): number;
|
|
511
|
-
getFreespinsWins(): number;
|
|
512
471
|
serialize(): {
|
|
513
472
|
id: number;
|
|
514
473
|
criteria: string;
|
|
@@ -517,6 +476,9 @@ declare class Book {
|
|
|
517
476
|
basegameWins: number;
|
|
518
477
|
freespinsWins: number;
|
|
519
478
|
};
|
|
479
|
+
/**
|
|
480
|
+
* Intended for internal use only.
|
|
481
|
+
*/
|
|
520
482
|
static fromSerialized(data: ReturnType<Book["serialize"]>): Book;
|
|
521
483
|
}
|
|
522
484
|
interface BookEvent {
|
|
@@ -526,85 +488,35 @@ interface BookEvent {
|
|
|
526
488
|
}
|
|
527
489
|
interface BookOpts {
|
|
528
490
|
id: number;
|
|
491
|
+
criteria: string;
|
|
529
492
|
}
|
|
530
493
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* e.g. "base", "freespins", etc. (depending on the game config)
|
|
539
|
-
*/
|
|
540
|
-
currentGameMode: string;
|
|
541
|
-
/**
|
|
542
|
-
* Spin type constant as defined in `GameConfig.SPIN_TYPE`
|
|
543
|
-
*/
|
|
544
|
-
currentSpinType: SpinType;
|
|
545
|
-
/**
|
|
546
|
-
* The current ResultSet for the active simulation run.
|
|
547
|
-
*/
|
|
548
|
-
currentResultSet: ResultSet<any>;
|
|
549
|
-
/**
|
|
550
|
-
* Whether the criteria in the ResultSet for the current simulation has been met.
|
|
551
|
-
*/
|
|
552
|
-
isCriteriaMet: boolean;
|
|
553
|
-
/**
|
|
554
|
-
* Number of freespins remaining in the current freespin round.
|
|
555
|
-
*/
|
|
556
|
-
currentFreespinAmount: number;
|
|
557
|
-
/**
|
|
558
|
-
* Total amount of freespins awarded during the active simulation.
|
|
559
|
-
*/
|
|
560
|
-
totalFreespinAmount: number;
|
|
561
|
-
/**
|
|
562
|
-
* A library of all completed books, indexed by their ID.
|
|
563
|
-
*/
|
|
564
|
-
library: Map<string, Book>;
|
|
565
|
-
/**
|
|
566
|
-
* The current book being recorded.
|
|
567
|
-
*/
|
|
568
|
-
book: Book;
|
|
569
|
-
/**
|
|
570
|
-
* Seeded random number generator instance for the current simulation.
|
|
571
|
-
*/
|
|
572
|
-
rng: RandomNumberGenerator;
|
|
573
|
-
/**
|
|
574
|
-
* Custom user data that can be used in game flow logic.
|
|
575
|
-
*/
|
|
576
|
-
userData: TUserState;
|
|
577
|
-
/**
|
|
578
|
-
* Whether a max win has been triggered during the active simulation.
|
|
579
|
-
*/
|
|
580
|
-
triggeredMaxWin: boolean;
|
|
581
|
-
/**
|
|
582
|
-
* Whether freespins have been triggered during the active simulation.
|
|
583
|
-
*/
|
|
584
|
-
triggeredFreespins: boolean;
|
|
585
|
-
};
|
|
494
|
+
declare class DataService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
|
|
495
|
+
private recorder;
|
|
496
|
+
private book;
|
|
497
|
+
constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
|
|
498
|
+
private ensureRecorder;
|
|
499
|
+
private ensureBook;
|
|
586
500
|
/**
|
|
587
|
-
*
|
|
501
|
+
* Intended for internal use only.
|
|
588
502
|
*/
|
|
589
|
-
|
|
503
|
+
_setRecorder(recorder: Recorder): void;
|
|
590
504
|
/**
|
|
591
|
-
*
|
|
505
|
+
* Intended for internal use only.
|
|
592
506
|
*/
|
|
593
|
-
|
|
594
|
-
constructor(opts: CommonGameOptions<TGameModes, TSymbols, TUserState>);
|
|
507
|
+
_getBook(): Book;
|
|
595
508
|
/**
|
|
596
|
-
*
|
|
509
|
+
* Intended for internal use only.
|
|
597
510
|
*/
|
|
598
|
-
|
|
599
|
-
resetState(): void;
|
|
511
|
+
_setBook(book: Book): void;
|
|
600
512
|
/**
|
|
601
|
-
*
|
|
513
|
+
* Intended for internal use only.
|
|
602
514
|
*/
|
|
603
|
-
|
|
515
|
+
_getRecorder(): Recorder;
|
|
604
516
|
/**
|
|
605
|
-
*
|
|
517
|
+
* Intended for internal use only.
|
|
606
518
|
*/
|
|
607
|
-
|
|
519
|
+
_getRecords(): RecordItem[];
|
|
608
520
|
/**
|
|
609
521
|
* Record data for statistical analysis.
|
|
610
522
|
*/
|
|
@@ -612,7 +524,7 @@ declare class GameState<TGameModes extends AnyGameModes, TSymbols extends AnySym
|
|
|
612
524
|
/**
|
|
613
525
|
* Records a symbol occurrence for statistical analysis.
|
|
614
526
|
*
|
|
615
|
-
* Calls `
|
|
527
|
+
* Calls `ctx.services.data.record()` with the provided data.
|
|
616
528
|
*/
|
|
617
529
|
recordSymbolOccurrence(data: {
|
|
618
530
|
kind: number;
|
|
@@ -621,458 +533,411 @@ declare class GameState<TGameModes extends AnyGameModes, TSymbols extends AnySym
|
|
|
621
533
|
[key: string]: any;
|
|
622
534
|
}): void;
|
|
623
535
|
/**
|
|
624
|
-
*
|
|
536
|
+
* Adds an event to the book.
|
|
625
537
|
*/
|
|
626
|
-
|
|
538
|
+
addBookEvent(event: Omit<BookEvent, "index">): void;
|
|
627
539
|
/**
|
|
628
|
-
*
|
|
540
|
+
* Intended for internal use only.
|
|
629
541
|
*/
|
|
630
|
-
|
|
542
|
+
_clearPendingRecords(): void;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
declare class RngService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
|
|
546
|
+
protected rng: RandomNumberGenerator;
|
|
547
|
+
constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
|
|
631
548
|
/**
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
* Also sets `state.triggeredFreespins` to true.
|
|
549
|
+
* Random weighted selection from a set of items.
|
|
635
550
|
*/
|
|
636
|
-
|
|
551
|
+
weightedRandom: <T extends Record<string, number>>(weights: T) => string;
|
|
637
552
|
/**
|
|
638
|
-
*
|
|
639
|
-
* Returns a valid number of scatters.
|
|
553
|
+
* Selects a random item from an array.
|
|
640
554
|
*/
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
interface RecordItem {
|
|
644
|
-
search: Array<{
|
|
645
|
-
name: string;
|
|
646
|
-
value: string;
|
|
647
|
-
}>;
|
|
648
|
-
timesTriggered: number;
|
|
649
|
-
bookIds: number[];
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* A version of the Board class that is disconnected from the actual game state\
|
|
654
|
-
* and operates on a copy of the game context.
|
|
655
|
-
*
|
|
656
|
-
* Can be used in custom game logic where you need to evaluate an additional board or reels independent of the main board,\
|
|
657
|
-
* similar to the top and bottom reels of the game "San Quentin".
|
|
658
|
-
*/
|
|
659
|
-
declare class StandaloneBoard {
|
|
660
|
-
protected reels: Reels;
|
|
661
|
-
protected paddingTop: Reels;
|
|
662
|
-
protected paddingBottom: Reels;
|
|
663
|
-
protected anticipation: number[];
|
|
664
|
-
protected ctx: AnySimulationContext;
|
|
665
|
-
constructor(opts: StandaloneBoardOpts);
|
|
555
|
+
randomItem: <T>(array: T[]) => NonNullable<T>;
|
|
666
556
|
/**
|
|
667
|
-
*
|
|
557
|
+
* Shuffles an array.
|
|
668
558
|
*/
|
|
669
|
-
|
|
559
|
+
shuffle: <T>(array: T[]) => T[];
|
|
670
560
|
/**
|
|
671
|
-
*
|
|
672
|
-
* This is called before drawing a new board.
|
|
561
|
+
* Generates a random float between two values.
|
|
673
562
|
*/
|
|
674
|
-
|
|
675
|
-
private makeEmptyReels;
|
|
676
|
-
private resetReels;
|
|
563
|
+
randomFloat: (low: number, high: number) => number;
|
|
677
564
|
/**
|
|
678
|
-
*
|
|
565
|
+
* Sets the seed for the RNG.
|
|
679
566
|
*/
|
|
680
|
-
|
|
567
|
+
setSeedIfDifferent: (seed: number) => void;
|
|
568
|
+
}
|
|
569
|
+
declare class RandomNumberGenerator {
|
|
570
|
+
mIdum: number;
|
|
571
|
+
mIy: number;
|
|
572
|
+
mIv: Array<number>;
|
|
573
|
+
NTAB: number;
|
|
574
|
+
IA: number;
|
|
575
|
+
IM: number;
|
|
576
|
+
IQ: number;
|
|
577
|
+
IR: number;
|
|
578
|
+
NDIV: number;
|
|
579
|
+
AM: number;
|
|
580
|
+
RNMX: number;
|
|
581
|
+
protected _currentSeed: number;
|
|
582
|
+
constructor();
|
|
583
|
+
getCurrentSeed(): number;
|
|
584
|
+
protected setCurrentSeed(seed: number): void;
|
|
585
|
+
setSeed(seed: number): void;
|
|
586
|
+
setSeedIfDifferent(seed: number): void;
|
|
587
|
+
generateRandomNumber(): number;
|
|
588
|
+
randomFloat(low: number, high: number): number;
|
|
589
|
+
weightedRandom<T extends Record<string, number>>(weights: T): string;
|
|
590
|
+
randomItem<T>(array: T[]): NonNullable<T>;
|
|
591
|
+
shuffle<T>(array: T[]): T[];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
declare class ReelSet {
|
|
595
|
+
id: string;
|
|
596
|
+
associatedGameModeName: string;
|
|
597
|
+
reels: Reels;
|
|
598
|
+
protected rng: RandomNumberGenerator;
|
|
599
|
+
constructor(opts: ReelSetOptions);
|
|
600
|
+
generateReels(simulation: Simulation): void;
|
|
681
601
|
/**
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
* Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.
|
|
685
|
-
*
|
|
686
|
-
* Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.
|
|
602
|
+
* Reads a reelset CSV file and returns the reels as arrays of GameSymbols.
|
|
687
603
|
*/
|
|
688
|
-
|
|
604
|
+
parseReelsetCSV(reelSetPath: string, config: GameConfig): Reels;
|
|
605
|
+
}
|
|
606
|
+
interface ReelSetOptions {
|
|
689
607
|
/**
|
|
690
|
-
*
|
|
608
|
+
* The unique identifier of the reel generator.\
|
|
609
|
+
* Must be unique per game mode.
|
|
610
|
+
*/
|
|
611
|
+
id: string;
|
|
612
|
+
/**
|
|
613
|
+
* Optional seed for the RNG to ensure reproducible results.
|
|
691
614
|
*
|
|
692
|
-
*
|
|
615
|
+
* Default seed is `0`.
|
|
616
|
+
*
|
|
617
|
+
* Note: Seeds 0 and 1 produce the same results.
|
|
693
618
|
*/
|
|
694
|
-
|
|
619
|
+
seed?: number;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
declare class GameMode {
|
|
623
|
+
readonly name: string;
|
|
624
|
+
readonly reelsAmount: number;
|
|
625
|
+
readonly symbolsPerReel: number[];
|
|
626
|
+
readonly cost: number;
|
|
627
|
+
readonly rtp: number;
|
|
628
|
+
readonly reelSets: ReelSet[];
|
|
629
|
+
readonly resultSets: ResultSet<any>[];
|
|
630
|
+
readonly isBonusBuy: boolean;
|
|
631
|
+
constructor(opts: GameModeOpts);
|
|
632
|
+
}
|
|
633
|
+
interface GameModeOpts {
|
|
695
634
|
/**
|
|
696
|
-
*
|
|
635
|
+
* Name of the game mode.
|
|
697
636
|
*/
|
|
698
|
-
|
|
637
|
+
name: string;
|
|
699
638
|
/**
|
|
700
|
-
*
|
|
639
|
+
* Number of reels the board has.
|
|
701
640
|
*/
|
|
702
|
-
|
|
703
|
-
private drawBoardMixed;
|
|
704
|
-
}
|
|
705
|
-
interface StandaloneBoardOpts {
|
|
706
|
-
ctx: AnySimulationContext;
|
|
707
|
-
}
|
|
708
|
-
/**
|
|
709
|
-
* Extends GameState. Provides board-related functionality.
|
|
710
|
-
*/
|
|
711
|
-
declare class Board<TGameModes extends AnyGameModes, TSymbols extends AnySymbols, TUserState extends AnyUserData> extends GameState<TGameModes, TSymbols, TUserState> {
|
|
712
|
-
board: {
|
|
713
|
-
reels: Reels;
|
|
714
|
-
paddingTop: Reels;
|
|
715
|
-
paddingBottom: Reels;
|
|
716
|
-
anticipation: number[];
|
|
717
|
-
};
|
|
718
|
-
constructor(opts: CommonGameOptions<TGameModes, TSymbols, TUserState>);
|
|
641
|
+
reelsAmount: number;
|
|
719
642
|
/**
|
|
720
|
-
*
|
|
721
|
-
*
|
|
643
|
+
* How many symbols each reel has. Array length must match `reelsAmount`.\
|
|
644
|
+
* The number at an array index represents the number of symbols on that reel.
|
|
722
645
|
*/
|
|
723
|
-
|
|
724
|
-
private makeEmptyReels;
|
|
725
|
-
private resetReels;
|
|
646
|
+
symbolsPerReel: number[];
|
|
726
647
|
/**
|
|
727
|
-
*
|
|
648
|
+
* Cost of the game mode, multiplied by the base bet.
|
|
728
649
|
*/
|
|
729
|
-
|
|
650
|
+
cost: number;
|
|
730
651
|
/**
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
* Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.
|
|
734
|
-
*
|
|
735
|
-
* Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.
|
|
652
|
+
* The target RTP of the game.
|
|
736
653
|
*/
|
|
737
|
-
|
|
654
|
+
rtp: number;
|
|
738
655
|
/**
|
|
739
|
-
*
|
|
656
|
+
* Defines (and generates) all reels for the game.\
|
|
657
|
+
* Which reels are used in a spin is determined by the ResultSet of the current game mode.
|
|
740
658
|
*
|
|
741
|
-
*
|
|
659
|
+
* It is common to have one reel set for the base game and another for free spins.\
|
|
660
|
+
* Each `ResultSet` can then set the weights of these reel sets to control which\
|
|
661
|
+
* reel set is used for a specific criteria.
|
|
742
662
|
*/
|
|
743
|
-
|
|
663
|
+
reelSets: ReelSet[];
|
|
744
664
|
/**
|
|
745
|
-
*
|
|
746
|
-
*
|
|
665
|
+
* A ResultSet defines how often a specific outcome should be generated.\
|
|
666
|
+
* For example, a ResultSet can be used to force a specific ratio of max wins\
|
|
667
|
+
* in the simulations to ensure there are different frontend representations.
|
|
747
668
|
*/
|
|
748
|
-
|
|
669
|
+
resultSets: ResultSet<any>[];
|
|
749
670
|
/**
|
|
750
|
-
*
|
|
671
|
+
* Whether this game mode is a bonus buy.
|
|
751
672
|
*/
|
|
752
|
-
|
|
673
|
+
isBonusBuy: boolean;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
declare class GameService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
|
|
677
|
+
constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
|
|
753
678
|
/**
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
* Mostly useful for placing scatter symbols on the board.
|
|
679
|
+
* Retrieves a reel set by its ID within a specific game mode.
|
|
757
680
|
*/
|
|
758
|
-
|
|
681
|
+
getReelsetById(gameMode: string, id: string): Reels;
|
|
759
682
|
/**
|
|
760
|
-
*
|
|
761
|
-
* Returns the reels as arrays of GameSymbols.
|
|
683
|
+
* Retrieves the number of free spins awarded for a given spin type and scatter count.
|
|
762
684
|
*/
|
|
763
|
-
|
|
685
|
+
getFreeSpinsForScatters(spinType: SpinType, scatterCount: number): number;
|
|
764
686
|
/**
|
|
765
|
-
*
|
|
687
|
+
* Retrieves a result set by its criteria within a specific game mode.
|
|
766
688
|
*/
|
|
767
|
-
|
|
689
|
+
getResultSetByCriteria(mode: string, criteria: string): ResultSet<any>;
|
|
768
690
|
/**
|
|
769
|
-
*
|
|
691
|
+
* Returns all configured symbols as an array.
|
|
770
692
|
*/
|
|
771
|
-
|
|
772
|
-
private drawBoardMixed;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
declare class ResultSet<TUserState extends AnyUserData> {
|
|
776
|
-
criteria: string;
|
|
777
|
-
quota: number;
|
|
778
|
-
multiplier?: number;
|
|
779
|
-
reelWeights: ReelWeights<TUserState>;
|
|
780
|
-
userData?: Record<string, any>;
|
|
781
|
-
forceMaxWin?: boolean;
|
|
782
|
-
forceFreespins?: boolean;
|
|
783
|
-
evaluate?: (ctx: AnySimulationContext<any, any, TUserState>) => boolean;
|
|
784
|
-
constructor(opts: ResultSetOpts<TUserState>);
|
|
785
|
-
static assignCriteriaToSimulations(ctx: Simulation, gameModeName: string): Record<number, string>;
|
|
693
|
+
getSymbolArray(): GameSymbol[];
|
|
786
694
|
/**
|
|
787
|
-
*
|
|
695
|
+
* Gets the configuration for the current game mode.
|
|
788
696
|
*/
|
|
789
|
-
|
|
790
|
-
}
|
|
791
|
-
interface ResultSetOpts<TUserState extends AnyUserData> {
|
|
697
|
+
getCurrentGameMode(): GameMode;
|
|
792
698
|
/**
|
|
793
|
-
*
|
|
699
|
+
* Ensures the requested number of scatters is valid based on the game configuration.\
|
|
700
|
+
* Returns a valid number of scatters.
|
|
794
701
|
*/
|
|
795
|
-
|
|
702
|
+
verifyScatterCount(numScatters: number): number;
|
|
796
703
|
/**
|
|
797
|
-
*
|
|
798
|
-
*
|
|
704
|
+
* Increases the freespin count by the specified amount.
|
|
705
|
+
*
|
|
706
|
+
* Also sets `state.triggeredFreespins` to true.
|
|
799
707
|
*/
|
|
800
|
-
|
|
708
|
+
awardFreespins(amount: number): void;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
declare class Wallet {
|
|
801
712
|
/**
|
|
802
|
-
*
|
|
713
|
+
* Total win amount (as the bet multiplier) from all simulations.
|
|
803
714
|
*/
|
|
804
|
-
|
|
715
|
+
protected cumulativeWins: number;
|
|
805
716
|
/**
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
* If you need to support dynamic / special reel weights based on the simulation context,\
|
|
809
|
-
* you can provide an `evaluate` function that returns the desired weights.
|
|
810
|
-
*
|
|
811
|
-
* If the `evaluate` function returns a falsy value, the usual spin type based weights will be used.
|
|
717
|
+
* Total win amount (as the bet multiplier) per spin type.
|
|
812
718
|
*
|
|
813
719
|
* @example
|
|
814
720
|
* ```ts
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
* [GameConfig.SPIN_TYPE.BASE_GAME]: { base1: 1 },
|
|
821
|
-
* [GameConfig.SPIN_TYPE.FREE_SPINS]: { bonus1: 1, bonus2: 2 },
|
|
822
|
-
* evaluate: (ctx) => {
|
|
823
|
-
* if (ctx.state.userData.triggeredSuperFreespins) {
|
|
824
|
-
* return { superbonus: 1 }
|
|
825
|
-
* }
|
|
826
|
-
* }
|
|
827
|
-
* },
|
|
828
|
-
* userData: { forceSuperFreespins: true },
|
|
829
|
-
* }),
|
|
721
|
+
* {
|
|
722
|
+
* basegame: 50,
|
|
723
|
+
* freespins: 100,
|
|
724
|
+
* superfreespins: 200,
|
|
725
|
+
* }
|
|
830
726
|
* ```
|
|
831
727
|
*/
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
*/
|
|
837
|
-
userData?: Record<string, any>;
|
|
838
|
-
/**
|
|
839
|
-
* If set, this will force the game to always trigger a max win.
|
|
840
|
-
*/
|
|
841
|
-
forceMaxWin?: boolean;
|
|
728
|
+
protected cumulativeWinsPerSpinType: {
|
|
729
|
+
basegame: number;
|
|
730
|
+
freespins: number;
|
|
731
|
+
};
|
|
842
732
|
/**
|
|
843
|
-
*
|
|
733
|
+
* Current win amount (as the bet multiplier) for the ongoing simulation.
|
|
844
734
|
*/
|
|
845
|
-
|
|
735
|
+
protected currentWin: number;
|
|
846
736
|
/**
|
|
847
|
-
*
|
|
737
|
+
* Current win amount (as the bet multiplier) for the ongoing simulation per spin type.
|
|
848
738
|
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
739
|
+
* @example
|
|
740
|
+
* ```ts
|
|
741
|
+
* {
|
|
742
|
+
* basegame: 50,
|
|
743
|
+
* freespins: 100,
|
|
744
|
+
* superfreespins: 200,
|
|
745
|
+
* }
|
|
746
|
+
* ```
|
|
851
747
|
*/
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
[GameConfig.SPIN_TYPE.BASE_GAME]: Record<string, number>;
|
|
856
|
-
[GameConfig.SPIN_TYPE.FREE_SPINS]: Record<string, number>;
|
|
857
|
-
evaluate?: (ctx: EvaluationContext<TUserState>) => Record<string, number> | undefined | null | false;
|
|
858
|
-
}
|
|
859
|
-
type EvaluationContext<TUserState extends AnyUserData> = Board<any, any, TUserState>;
|
|
860
|
-
|
|
861
|
-
/**
|
|
862
|
-
* Static configuration for a slot game.\
|
|
863
|
-
* This shouldn't change during gameplay.
|
|
864
|
-
*/
|
|
865
|
-
declare class GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> {
|
|
866
|
-
readonly config: {
|
|
867
|
-
readonly id: string;
|
|
868
|
-
readonly name: string;
|
|
869
|
-
readonly gameModes: Record<GameModeName, GameMode>;
|
|
870
|
-
readonly symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
|
|
871
|
-
readonly padSymbols?: number;
|
|
872
|
-
readonly scatterToFreespins: Record<string, Record<number, number>>;
|
|
873
|
-
readonly anticipationTriggers: Record<SpinType, number>;
|
|
874
|
-
readonly maxWinX: number;
|
|
875
|
-
readonly outputDir: string;
|
|
876
|
-
readonly hooks: GameHooks<TGameModes, TSymbols, TUserState>;
|
|
877
|
-
readonly userState?: TUserState;
|
|
748
|
+
protected currentWinPerSpinType: {
|
|
749
|
+
basegame: number;
|
|
750
|
+
freespins: number;
|
|
878
751
|
};
|
|
879
|
-
constructor(opts: CommonGameOptions<TGameModes, TSymbols, TUserState>);
|
|
880
752
|
/**
|
|
881
|
-
*
|
|
753
|
+
* Holds the current win amount for a single (free) spin.\
|
|
754
|
+
* After each spin, this amount is added to `currentWinPerSpinType` and then reset to zero.
|
|
882
755
|
*/
|
|
883
|
-
|
|
756
|
+
protected currentSpinWin: number;
|
|
884
757
|
/**
|
|
885
|
-
*
|
|
758
|
+
* Current win amount (as the bet multiplier) for the ongoing tumble sequence.
|
|
886
759
|
*/
|
|
887
|
-
|
|
760
|
+
protected currentTumbleWin: number;
|
|
761
|
+
constructor();
|
|
888
762
|
/**
|
|
889
|
-
*
|
|
763
|
+
* Updates the win for the current spin.
|
|
764
|
+
*
|
|
765
|
+
* Should be called after each tumble event, if applicable.\
|
|
766
|
+
* Or generally call this to add wins during a spin.
|
|
767
|
+
*
|
|
768
|
+
* After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`
|
|
890
769
|
*/
|
|
891
|
-
|
|
770
|
+
addSpinWin(amount: number): void;
|
|
892
771
|
/**
|
|
893
|
-
*
|
|
772
|
+
* Confirms the wins of the current spin.
|
|
773
|
+
*
|
|
774
|
+
* Should be called after `addSpinWin()`, and after your tumble events are played out,\
|
|
775
|
+
* and after a (free) spin is played out to finalize the win.
|
|
894
776
|
*/
|
|
895
|
-
|
|
777
|
+
confirmSpinWin(spinType: SpinType): void;
|
|
896
778
|
/**
|
|
897
|
-
* Returns
|
|
779
|
+
* Returns the accumulated win amount (as the bet multiplier) from all simulations.
|
|
780
|
+
*/
|
|
781
|
+
getCumulativeWins(): number;
|
|
782
|
+
/**
|
|
783
|
+
* Returns the accumulated win amount (as the bet multiplier) per spin type from all simulations.
|
|
898
784
|
*/
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
readonly FREE_SPINS: "freespins";
|
|
785
|
+
getCumulativeWinsPerSpinType(): {
|
|
786
|
+
basegame: number;
|
|
787
|
+
freespins: number;
|
|
903
788
|
};
|
|
904
|
-
}
|
|
905
|
-
type SpinType = (typeof GameConfig.SPIN_TYPE)[keyof typeof GameConfig.SPIN_TYPE];
|
|
906
|
-
type AnyGameConfig = GameConfig<any, any, any>;
|
|
907
|
-
|
|
908
|
-
declare class WinType {
|
|
909
|
-
protected payout: number;
|
|
910
|
-
protected winCombinations: WinCombination[];
|
|
911
|
-
protected ctx: AnySimulationContext;
|
|
912
|
-
protected readonly wildSymbol?: WildSymbol;
|
|
913
|
-
constructor(opts?: WinTypeOpts);
|
|
914
789
|
/**
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
* This gives the WinType access to the current board.
|
|
790
|
+
* Returns the current win amount (as the bet multiplier) for the ongoing simulation.
|
|
918
791
|
*/
|
|
919
|
-
|
|
920
|
-
protected ensureContext(): void;
|
|
792
|
+
getCurrentWin(): number;
|
|
921
793
|
/**
|
|
922
|
-
*
|
|
794
|
+
* Returns the current spin win amount (as the bet multiplier) for the ongoing simulation.
|
|
923
795
|
*/
|
|
924
|
-
|
|
796
|
+
getCurrentSpinWin(): number;
|
|
925
797
|
/**
|
|
926
|
-
*
|
|
798
|
+
* Returns the current tumble win amount (as the bet multiplier) for the ongoing simulation.
|
|
927
799
|
*/
|
|
928
|
-
|
|
800
|
+
getCurrentTumbleWin(): number;
|
|
929
801
|
/**
|
|
930
|
-
* Returns the
|
|
802
|
+
* Returns the current win amount (as the bet multiplier) per spin type for the ongoing simulation.
|
|
931
803
|
*/
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
804
|
+
getCurrentWinPerSpinType(): {
|
|
805
|
+
basegame: number;
|
|
806
|
+
freespins: number;
|
|
935
807
|
};
|
|
936
|
-
}
|
|
937
|
-
interface WinTypeOpts {
|
|
938
808
|
/**
|
|
939
|
-
*
|
|
940
|
-
* You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.
|
|
809
|
+
* Adds a win to `currentSpinWin` and `currentTumbleWin`.
|
|
941
810
|
*
|
|
942
|
-
*
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
*
|
|
811
|
+
* After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`
|
|
812
|
+
*/
|
|
813
|
+
addTumbleWin(amount: number): void;
|
|
814
|
+
/**
|
|
815
|
+
* Intended for internal use only.
|
|
947
816
|
*
|
|
948
|
-
*
|
|
949
|
-
* If you have a single wild symbol instance, you can define:
|
|
950
|
-
* ```ts
|
|
951
|
-
* wildSymbol: myWildSymbol
|
|
952
|
-
* ```
|
|
817
|
+
* Resets the current win amounts to zero.
|
|
953
818
|
*/
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
type WinCombination = {
|
|
957
|
-
payout: number;
|
|
958
|
-
kind: number;
|
|
959
|
-
symbols: Array<{
|
|
960
|
-
symbol: GameSymbol;
|
|
961
|
-
isWild: boolean;
|
|
962
|
-
substitutedFor?: GameSymbol;
|
|
963
|
-
reelIndex: number;
|
|
964
|
-
posIndex: number;
|
|
965
|
-
}>;
|
|
966
|
-
};
|
|
967
|
-
type PostProcessFn<TWinCombs extends WinCombination[]> = (winType: WinType, ctx: AnySimulationContext) => {
|
|
968
|
-
payout: number;
|
|
969
|
-
winCombinations: TWinCombs;
|
|
970
|
-
};
|
|
971
|
-
type WildSymbol = GameSymbol | Record<string, any>;
|
|
972
|
-
|
|
973
|
-
declare class LinesWinType extends WinType {
|
|
974
|
-
protected lines: Record<number, number[]>;
|
|
975
|
-
protected winCombinations: LineWinCombination[];
|
|
976
|
-
context: (ctx: SimulationContext<any, any, any>) => LinesWinType;
|
|
977
|
-
getWins: () => {
|
|
978
|
-
payout: number;
|
|
979
|
-
winCombinations: LineWinCombination[];
|
|
980
|
-
};
|
|
981
|
-
constructor(opts: LinesWinTypeOpts);
|
|
982
|
-
private validateConfig;
|
|
983
|
-
private isWild;
|
|
984
|
-
evaluateWins(): this;
|
|
985
|
-
}
|
|
986
|
-
interface LinesWinTypeOpts extends WinTypeOpts {
|
|
819
|
+
resetCurrentWin(): void;
|
|
987
820
|
/**
|
|
988
|
-
*
|
|
821
|
+
* Intended for internal use only.
|
|
989
822
|
*
|
|
990
|
-
*
|
|
991
|
-
* ```ts
|
|
992
|
-
* lines: {
|
|
993
|
-
* 1: [0, 0, 0, 0, 0],
|
|
994
|
-
* 2: [1, 1, 1, 1, 1],
|
|
995
|
-
* 3: [2, 2, 2, 2, 2],
|
|
996
|
-
* }
|
|
997
|
-
* ```
|
|
823
|
+
* Adds current wins to cumulative wins and resets current wins to zero.
|
|
998
824
|
*/
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
825
|
+
confirmWins(ctx: GameContext): void;
|
|
826
|
+
/**
|
|
827
|
+
* Intended for internal use only.
|
|
828
|
+
*
|
|
829
|
+
* Transfers the win data from the given wallet to the calling book.
|
|
830
|
+
*/
|
|
831
|
+
writePayoutToBook(ctx: GameContext): void;
|
|
832
|
+
/**
|
|
833
|
+
* Intended for internal use only.
|
|
834
|
+
*/
|
|
835
|
+
serialize(): {
|
|
836
|
+
cumulativeWins: number;
|
|
837
|
+
cumulativeWinsPerSpinType: {
|
|
838
|
+
basegame: number;
|
|
839
|
+
freespins: number;
|
|
840
|
+
};
|
|
841
|
+
currentWin: number;
|
|
842
|
+
currentWinPerSpinType: {
|
|
843
|
+
basegame: number;
|
|
844
|
+
freespins: number;
|
|
845
|
+
};
|
|
846
|
+
currentSpinWin: number;
|
|
847
|
+
currentTumbleWin: number;
|
|
1010
848
|
};
|
|
849
|
+
/**
|
|
850
|
+
* Intended for internal use only.
|
|
851
|
+
*/
|
|
852
|
+
merge(wallet: Wallet): void;
|
|
853
|
+
/**
|
|
854
|
+
* Intended for internal use only.
|
|
855
|
+
*/
|
|
856
|
+
mergeSerialized(data: ReturnType<Wallet["serialize"]>): void;
|
|
1011
857
|
}
|
|
1012
858
|
|
|
1013
|
-
declare class
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
declare class OptimizationConditions {
|
|
1020
|
-
protected rtp?: number | "x";
|
|
1021
|
-
protected avgWin?: number;
|
|
1022
|
-
protected hitRate?: number | "x";
|
|
1023
|
-
protected searchRange: number[];
|
|
1024
|
-
protected forceSearch: Record<string, string>;
|
|
1025
|
-
priority: number;
|
|
1026
|
-
constructor(opts: OptimizationConditionsOpts);
|
|
1027
|
-
getRtp(): number | "x" | undefined;
|
|
1028
|
-
getAvgWin(): number | undefined;
|
|
1029
|
-
getHitRate(): number | "x" | undefined;
|
|
1030
|
-
getSearchRange(): number[];
|
|
1031
|
-
getForceSearch(): Record<string, string>;
|
|
1032
|
-
}
|
|
1033
|
-
interface OptimizationConditionsOpts {
|
|
859
|
+
declare class WalletService<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> extends AbstractService {
|
|
860
|
+
private wallet;
|
|
861
|
+
constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>);
|
|
862
|
+
private ensureWallet;
|
|
1034
863
|
/**
|
|
1035
|
-
*
|
|
864
|
+
* Intended for internal use only.
|
|
1036
865
|
*/
|
|
1037
|
-
|
|
866
|
+
_getWallet(): Wallet;
|
|
1038
867
|
/**
|
|
1039
|
-
*
|
|
868
|
+
* Intended for internal use only.
|
|
1040
869
|
*/
|
|
1041
|
-
|
|
870
|
+
_setWallet(wallet: Wallet): void;
|
|
1042
871
|
/**
|
|
1043
|
-
*
|
|
872
|
+
* Adds the given amount to the wallet state.
|
|
873
|
+
*
|
|
874
|
+
* After calculating the win for a board, call this method to update the wallet state.\
|
|
875
|
+
* If your game has tumbling mechanics, you should call this method again after every new tumble and win calculation.
|
|
1044
876
|
*/
|
|
1045
|
-
|
|
877
|
+
addSpinWin(amount: number): void;
|
|
1046
878
|
/**
|
|
1047
|
-
*
|
|
879
|
+
* Helps to add tumble wins to the wallet state.
|
|
1048
880
|
*
|
|
1049
|
-
*
|
|
1050
|
-
* - Force record value, e.g. `{ "symbolId": "scatter" }`
|
|
1051
|
-
* - A range of numbers, e.g. `[0, 100]` (payout multiplier range)
|
|
881
|
+
* This also calls `addSpinWin()` internally, to add the tumble win to the overall spin win.
|
|
1052
882
|
*/
|
|
1053
|
-
|
|
883
|
+
addTumbleWin(amount: number): void;
|
|
1054
884
|
/**
|
|
1055
|
-
*
|
|
1056
|
-
* Higher priority conditions will be evaluated first.\
|
|
1057
|
-
* After a book matching this condition is found, the book will be removed from the pool\
|
|
1058
|
-
* and can't be used to satisfy other conditions with lower priority.
|
|
885
|
+
* Confirms the wins of the current spin.
|
|
1059
886
|
*
|
|
1060
|
-
*
|
|
887
|
+
* Should be called after `addSpinWin()`, and after your tumble events are played out,\
|
|
888
|
+
* and after a (free) spin is played out to finalize the win.
|
|
1061
889
|
*/
|
|
1062
|
-
|
|
890
|
+
confirmSpinWin(): void;
|
|
891
|
+
/**
|
|
892
|
+
* Gets the total win amount of the current simulation.
|
|
893
|
+
*/
|
|
894
|
+
getCurrentWin(): number;
|
|
895
|
+
/**
|
|
896
|
+
* Gets the current spin win amount of the ongoing spin.
|
|
897
|
+
*/
|
|
898
|
+
getCurrentSpinWin(): number;
|
|
899
|
+
/**
|
|
900
|
+
* Gets the current tumble win amount of the ongoing spin.
|
|
901
|
+
*/
|
|
902
|
+
getCurrentTumbleWin(): number;
|
|
1063
903
|
}
|
|
1064
904
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
905
|
+
type GameContext<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> = {
|
|
906
|
+
/**
|
|
907
|
+
* The static configuration of the game.
|
|
908
|
+
*/
|
|
909
|
+
config: GameConfig<TGameModes, TSymbols, TUserState>;
|
|
910
|
+
/**
|
|
911
|
+
* Game state holding information about the current simulation.
|
|
912
|
+
*/
|
|
913
|
+
state: GameState<TUserState>;
|
|
914
|
+
/**
|
|
915
|
+
* Services providing game functionality.
|
|
916
|
+
*/
|
|
917
|
+
services: GameContextServices;
|
|
918
|
+
};
|
|
919
|
+
interface GameContextServices {
|
|
920
|
+
/**
|
|
921
|
+
* Service providing common utility functions.
|
|
922
|
+
*/
|
|
923
|
+
game: GameService;
|
|
924
|
+
/**
|
|
925
|
+
* Service for interacting with the book data or recorder.
|
|
926
|
+
*/
|
|
927
|
+
data: DataService;
|
|
928
|
+
/**
|
|
929
|
+
* Service managing the game board and reels.
|
|
930
|
+
*/
|
|
931
|
+
board: BoardService;
|
|
932
|
+
/**
|
|
933
|
+
* Service providing win related functionality.
|
|
934
|
+
*/
|
|
935
|
+
wallet: WalletService;
|
|
936
|
+
/**
|
|
937
|
+
* Service for seeded random number generation.
|
|
938
|
+
*/
|
|
939
|
+
rng: RngService;
|
|
1069
940
|
}
|
|
1070
|
-
type OptimizationScalingOpts = Array<{
|
|
1071
|
-
criteria: string;
|
|
1072
|
-
scaleFactor: number;
|
|
1073
|
-
winRange: [number, number];
|
|
1074
|
-
probability: number;
|
|
1075
|
-
}>;
|
|
1076
941
|
|
|
1077
942
|
declare class OptimizationParameters {
|
|
1078
943
|
protected parameters: OptimizationParametersOpts;
|
|
@@ -1096,6 +961,64 @@ interface OptimizationParametersOpts {
|
|
|
1096
961
|
readonly scoreType: "rtp";
|
|
1097
962
|
}
|
|
1098
963
|
|
|
964
|
+
declare class OptimizationConditions {
|
|
965
|
+
protected rtp?: number | "x";
|
|
966
|
+
protected avgWin?: number;
|
|
967
|
+
protected hitRate?: number | "x";
|
|
968
|
+
protected searchRange: number[];
|
|
969
|
+
protected forceSearch: Record<string, string>;
|
|
970
|
+
priority: number;
|
|
971
|
+
constructor(opts: OptimizationConditionsOpts);
|
|
972
|
+
getRtp(): number | "x" | undefined;
|
|
973
|
+
getAvgWin(): number | undefined;
|
|
974
|
+
getHitRate(): number | "x" | undefined;
|
|
975
|
+
getSearchRange(): number[];
|
|
976
|
+
getForceSearch(): Record<string, string>;
|
|
977
|
+
}
|
|
978
|
+
interface OptimizationConditionsOpts {
|
|
979
|
+
/**
|
|
980
|
+
* The desired RTP (0-1)
|
|
981
|
+
*/
|
|
982
|
+
rtp?: number | "x";
|
|
983
|
+
/**
|
|
984
|
+
* The desired average win (per spin).
|
|
985
|
+
*/
|
|
986
|
+
avgWin?: number;
|
|
987
|
+
/**
|
|
988
|
+
* The desired hit rate (e.g. `200` to hit 1 in 200 spins).
|
|
989
|
+
*/
|
|
990
|
+
hitRate?: number | "x";
|
|
991
|
+
/**
|
|
992
|
+
* A way of filtering results by
|
|
993
|
+
*
|
|
994
|
+
* - A number (payout multiplier), e.g. `5000`
|
|
995
|
+
* - Force record value, e.g. `{ "symbolId": "scatter" }`
|
|
996
|
+
* - A range of numbers, e.g. `[0, 100]` (payout multiplier range)
|
|
997
|
+
*/
|
|
998
|
+
searchConditions?: number | Record<string, string> | [number, number];
|
|
999
|
+
/**
|
|
1000
|
+
* **Priority matters!**\
|
|
1001
|
+
* Higher priority conditions will be evaluated first.\
|
|
1002
|
+
* After a book matching this condition is found, the book will be removed from the pool\
|
|
1003
|
+
* and can't be used to satisfy other conditions with lower priority.
|
|
1004
|
+
*
|
|
1005
|
+
* TODO add better explanation
|
|
1006
|
+
*/
|
|
1007
|
+
priority: number;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
declare class OptimizationScaling {
|
|
1011
|
+
protected config: OptimizationScalingOpts;
|
|
1012
|
+
constructor(opts: OptimizationScalingOpts);
|
|
1013
|
+
getConfig(): OptimizationScalingOpts;
|
|
1014
|
+
}
|
|
1015
|
+
type OptimizationScalingOpts = Array<{
|
|
1016
|
+
criteria: string;
|
|
1017
|
+
scaleFactor: number;
|
|
1018
|
+
winRange: [number, number];
|
|
1019
|
+
probability: number;
|
|
1020
|
+
}>;
|
|
1021
|
+
|
|
1099
1022
|
interface OptimizationOpts {
|
|
1100
1023
|
gameModes: string[];
|
|
1101
1024
|
}
|
|
@@ -1103,7 +1026,7 @@ interface OptimizerOpts {
|
|
|
1103
1026
|
game: SlotGame<any, any, any>;
|
|
1104
1027
|
gameModes: OptimzierGameModeConfig;
|
|
1105
1028
|
}
|
|
1106
|
-
type OptimzierGameModeConfig = Record<
|
|
1029
|
+
type OptimzierGameModeConfig = Record<string, {
|
|
1107
1030
|
conditions: Record<string, OptimizationConditions>;
|
|
1108
1031
|
scaling: OptimizationScaling;
|
|
1109
1032
|
parameters: OptimizationParameters;
|
|
@@ -1118,16 +1041,16 @@ interface AnalysisOpts {
|
|
|
1118
1041
|
* Main entry point for the slot game.
|
|
1119
1042
|
*/
|
|
1120
1043
|
declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> {
|
|
1121
|
-
private readonly
|
|
1044
|
+
private readonly configOpts;
|
|
1122
1045
|
private simulation?;
|
|
1123
1046
|
private optimizer?;
|
|
1124
1047
|
private analyzer?;
|
|
1125
|
-
constructor(config:
|
|
1048
|
+
constructor(config: GameConfigOptions<TGameModes, TSymbols, TUserState>);
|
|
1126
1049
|
/**
|
|
1127
1050
|
* Sets up the simulation configuration.\
|
|
1128
1051
|
* Must be called before `runTasks()`.
|
|
1129
1052
|
*/
|
|
1130
|
-
configureSimulation(opts:
|
|
1053
|
+
configureSimulation(opts: SimulationOptions): void;
|
|
1131
1054
|
/**
|
|
1132
1055
|
* Sets up the optimization configuration.\
|
|
1133
1056
|
* Must be called before `runTasks()`.
|
|
@@ -1145,34 +1068,39 @@ declare class SlotGame<TGameModes extends AnyGameModes = AnyGameModes, TSymbols
|
|
|
1145
1068
|
* Runs the analysis based on the configured settings.
|
|
1146
1069
|
*/
|
|
1147
1070
|
private runAnalysis;
|
|
1071
|
+
/**
|
|
1072
|
+
* Runs the configured tasks: simulation, optimization, and/or analysis.
|
|
1073
|
+
*/
|
|
1148
1074
|
runTasks(opts?: {
|
|
1149
1075
|
doSimulation?: boolean;
|
|
1150
1076
|
doOptimization?: boolean;
|
|
1151
1077
|
doAnalysis?: boolean;
|
|
1152
|
-
simulationOpts?:
|
|
1078
|
+
simulationOpts?: SimulationConfigOptions;
|
|
1153
1079
|
optimizationOpts?: OptimizationOpts;
|
|
1154
1080
|
analysisOpts?: AnalysisOpts;
|
|
1155
1081
|
}): Promise<void>;
|
|
1156
1082
|
/**
|
|
1157
1083
|
* Gets the game configuration.
|
|
1158
1084
|
*/
|
|
1159
|
-
getConfig():
|
|
1085
|
+
getConfig(): {
|
|
1086
|
+
symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
|
|
1087
|
+
anticipationTriggers: {
|
|
1088
|
+
basegame: number;
|
|
1089
|
+
freespins: number;
|
|
1090
|
+
};
|
|
1091
|
+
outputDir: string;
|
|
1092
|
+
id: string;
|
|
1093
|
+
name: string;
|
|
1094
|
+
gameModes: TGameModes;
|
|
1095
|
+
scatterToFreespins: Record<string, Record<number, number>>;
|
|
1096
|
+
padSymbols: number;
|
|
1097
|
+
maxWinX: number;
|
|
1098
|
+
userState: TUserState;
|
|
1099
|
+
hooks: GameHooks<TGameModes, TSymbols, TUserState>;
|
|
1100
|
+
};
|
|
1160
1101
|
}
|
|
1161
1102
|
|
|
1162
|
-
|
|
1163
|
-
* @internal
|
|
1164
|
-
*/
|
|
1165
|
-
interface CommonGameOptions<TGameModes extends AnyGameModes = AnyGameModes, TSymbols extends AnySymbols = AnySymbols, TUserState extends AnyUserData = AnyUserData> {
|
|
1166
|
-
id: string;
|
|
1167
|
-
name: string;
|
|
1168
|
-
gameModes: Record<GameModeName, GameMode>;
|
|
1169
|
-
symbols: TSymbols;
|
|
1170
|
-
scatterToFreespins: Record<string, Record<number, number>>;
|
|
1171
|
-
padSymbols?: number;
|
|
1172
|
-
maxWinX: number;
|
|
1173
|
-
hooks: GameHooks<TGameModes, TSymbols, TUserState>;
|
|
1174
|
-
userState?: TUserState;
|
|
1175
|
-
}
|
|
1103
|
+
type InferGameType<TGameModes extends AnyGameModes, TSymbols extends AnySymbols, TUserState extends AnyUserData> = SlotGame<TGameModes, TSymbols, TUserState>;
|
|
1176
1104
|
/**
|
|
1177
1105
|
* @internal
|
|
1178
1106
|
*/
|
|
@@ -1207,79 +1135,367 @@ interface GameHooks<TGameModes extends AnyGameModes = AnyGameModes, TSymbols ext
|
|
|
1207
1135
|
* The game flow is not built into the core, because it can vary greatly between different games.\
|
|
1208
1136
|
* This hook provides the flexibility to implement any game flow you need.
|
|
1209
1137
|
*/
|
|
1210
|
-
onHandleGameFlow: (ctx:
|
|
1138
|
+
onHandleGameFlow: (ctx: GameContext<TGameModes, TSymbols, TUserState>) => void;
|
|
1211
1139
|
/**
|
|
1212
1140
|
* This hook is called whenever a simulation is accepted, i.e. when the criteria of the current ResultSet is met.
|
|
1213
1141
|
*/
|
|
1214
|
-
onSimulationAccepted?: (ctx:
|
|
1142
|
+
onSimulationAccepted?: (ctx: GameContext<TGameModes, TSymbols, TUserState>) => void;
|
|
1215
1143
|
}
|
|
1216
|
-
type
|
|
1217
|
-
type
|
|
1144
|
+
type SpinType = (typeof SPIN_TYPE)[keyof typeof SPIN_TYPE];
|
|
1145
|
+
type Reels = GameSymbol[][];
|
|
1218
1146
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1147
|
+
declare function createSlotGame<TGame>(opts: TGame extends InferGameType<infer G, infer S, infer U> ? GameConfigOptions<G, S, U> : never): TGame;
|
|
1148
|
+
declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState) => TUserState;
|
|
1149
|
+
declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
|
|
1150
|
+
declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
|
|
1151
|
+
|
|
1152
|
+
declare class WinType {
|
|
1153
|
+
protected payout: number;
|
|
1154
|
+
protected winCombinations: WinCombination[];
|
|
1155
|
+
protected ctx: GameContext;
|
|
1156
|
+
protected readonly wildSymbol?: WildSymbol;
|
|
1157
|
+
constructor(opts: WinTypeOpts);
|
|
1221
1158
|
/**
|
|
1222
|
-
*
|
|
1159
|
+
* Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
|
|
1223
1160
|
*/
|
|
1224
|
-
|
|
1161
|
+
evaluateWins(board: Reels): this;
|
|
1225
1162
|
/**
|
|
1226
|
-
*
|
|
1163
|
+
* Custom post-processing of wins, e.g. for handling multipliers.
|
|
1227
1164
|
*/
|
|
1228
|
-
|
|
1165
|
+
postProcess(func: PostProcessFn<typeof this.winCombinations>): this;
|
|
1229
1166
|
/**
|
|
1230
|
-
*
|
|
1231
|
-
* bet cost, win type, and other properties.
|
|
1167
|
+
* Returns the total payout and detailed win combinations.
|
|
1232
1168
|
*/
|
|
1233
|
-
|
|
1169
|
+
getWins(): {
|
|
1170
|
+
payout: number;
|
|
1171
|
+
winCombinations: WinCombination[];
|
|
1172
|
+
};
|
|
1173
|
+
protected isWild(symbol: GameSymbol): boolean;
|
|
1174
|
+
}
|
|
1175
|
+
interface WinTypeOpts {
|
|
1234
1176
|
/**
|
|
1235
|
-
* A
|
|
1177
|
+
* A reference to the game context.
|
|
1236
1178
|
*/
|
|
1237
|
-
|
|
1179
|
+
ctx: GameContext<any, any, any>;
|
|
1238
1180
|
/**
|
|
1239
|
-
*
|
|
1181
|
+
* Configuration used to identify wild symbols on the board.\
|
|
1182
|
+
* You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.
|
|
1240
1183
|
*
|
|
1241
1184
|
* @example
|
|
1185
|
+
* If you have different wild symbols, each with a property `isWild: true`, you can define:
|
|
1242
1186
|
* ```ts
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
* 3: 6,
|
|
1251
|
-
* 4: 8,
|
|
1252
|
-
* 5: 10,
|
|
1253
|
-
* },
|
|
1254
|
-
* },
|
|
1187
|
+
* wildSymbol: { isWild: true }
|
|
1188
|
+
* ```
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* If you have a single wild symbol instance, you can define:
|
|
1192
|
+
* ```ts
|
|
1193
|
+
* wildSymbol: myWildSymbol
|
|
1255
1194
|
* ```
|
|
1256
1195
|
*/
|
|
1257
|
-
|
|
1196
|
+
wildSymbol?: WildSymbol;
|
|
1197
|
+
}
|
|
1198
|
+
type WinCombination = {
|
|
1199
|
+
payout: number;
|
|
1200
|
+
kind: number;
|
|
1201
|
+
symbols: Array<{
|
|
1202
|
+
symbol: GameSymbol;
|
|
1203
|
+
isWild: boolean;
|
|
1204
|
+
substitutedFor?: GameSymbol;
|
|
1205
|
+
reelIndex: number;
|
|
1206
|
+
posIndex: number;
|
|
1207
|
+
}>;
|
|
1208
|
+
};
|
|
1209
|
+
type PostProcessFn<TWinCombs extends WinCombination[]> = (winType: WinType, ctx: GameContext) => {
|
|
1210
|
+
payout: number;
|
|
1211
|
+
winCombinations: TWinCombs;
|
|
1212
|
+
};
|
|
1213
|
+
type WildSymbol = GameSymbol | Record<string, any>;
|
|
1214
|
+
|
|
1215
|
+
declare class LinesWinType extends WinType {
|
|
1216
|
+
protected lines: Record<number, number[]>;
|
|
1217
|
+
protected winCombinations: LineWinCombination[];
|
|
1218
|
+
getWins: () => {
|
|
1219
|
+
payout: number;
|
|
1220
|
+
winCombinations: LineWinCombination[];
|
|
1221
|
+
};
|
|
1222
|
+
constructor(opts: LinesWinTypeOpts);
|
|
1223
|
+
private validateConfig;
|
|
1258
1224
|
/**
|
|
1259
|
-
*
|
|
1260
|
-
*
|
|
1225
|
+
* Calculates wins based on the defined paylines and provided board state.\
|
|
1226
|
+
* Retrieve the results using `getWins()` after.
|
|
1227
|
+
*/
|
|
1228
|
+
evaluateWins(board: Reels): this;
|
|
1229
|
+
}
|
|
1230
|
+
interface LinesWinTypeOpts extends WinTypeOpts {
|
|
1231
|
+
/**
|
|
1232
|
+
* Defines the paylines for the slot game.
|
|
1261
1233
|
*
|
|
1262
|
-
*
|
|
1234
|
+
* @example
|
|
1235
|
+
* ```ts
|
|
1236
|
+
* lines: {
|
|
1237
|
+
* 1: [0, 0, 0, 0, 0],
|
|
1238
|
+
* 2: [1, 1, 1, 1, 1],
|
|
1239
|
+
* 3: [2, 2, 2, 2, 2],
|
|
1240
|
+
* }
|
|
1241
|
+
* ```
|
|
1263
1242
|
*/
|
|
1264
|
-
|
|
1243
|
+
lines: Record<number, number[]>;
|
|
1244
|
+
}
|
|
1245
|
+
interface LineWinCombination extends WinCombination {
|
|
1246
|
+
lineNumber: number;
|
|
1247
|
+
symbol: GameSymbol;
|
|
1248
|
+
winType: "pure-wild" | "substituted";
|
|
1249
|
+
substitutedBaseSymbol: GameSymbol | null;
|
|
1250
|
+
stats: {
|
|
1251
|
+
wildCount: number;
|
|
1252
|
+
nonWildCount: number;
|
|
1253
|
+
leadingWilds: number;
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
declare class ClusterWinType extends WinType {
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
declare class ManywaysWinType extends WinType {
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* This class is responsible for generating reel sets for slot games based on specified configurations.
|
|
1265
|
+
*
|
|
1266
|
+
* **While it offers a high degree of customization, some configurations may lead to unsolvable scenarios.**
|
|
1267
|
+
*
|
|
1268
|
+
* If the reel generator is unable to fulfill niche constraints,\
|
|
1269
|
+
* you might need to adjust your configuration, or edit the generated reels manually.\
|
|
1270
|
+
* Setting a different seed may also help.
|
|
1271
|
+
*/
|
|
1272
|
+
declare class GeneratedReelSet extends ReelSet {
|
|
1273
|
+
protected readonly symbolWeights: Map<string, number>;
|
|
1274
|
+
protected readonly rowsAmount: number;
|
|
1275
|
+
protected limitSymbolsToReels?: Record<string, number[]>;
|
|
1276
|
+
protected readonly spaceBetweenSameSymbols?: number | Record<string, number>;
|
|
1277
|
+
protected readonly spaceBetweenSymbols?: Record<string, Record<string, number>>;
|
|
1278
|
+
protected readonly preferStackedSymbols?: number;
|
|
1279
|
+
protected readonly symbolStacks?: Record<string, {
|
|
1280
|
+
chance: number | Record<string, number>;
|
|
1281
|
+
min?: number | Record<string, number>;
|
|
1282
|
+
max?: number | Record<string, number>;
|
|
1283
|
+
}>;
|
|
1284
|
+
protected readonly symbolQuotas?: Record<string, number | Record<string, number>>;
|
|
1285
|
+
private overrideExisting;
|
|
1286
|
+
constructor(opts: GeneratedReelSetOptions);
|
|
1287
|
+
private validateConfig;
|
|
1288
|
+
private isSymbolAllowedOnReel;
|
|
1289
|
+
private resolveStacking;
|
|
1290
|
+
private tryPlaceStack;
|
|
1265
1291
|
/**
|
|
1266
|
-
*
|
|
1292
|
+
* Checks if a symbol can be placed at the target index without violating spacing rules.
|
|
1267
1293
|
*/
|
|
1268
|
-
|
|
1294
|
+
private violatesSpacing;
|
|
1295
|
+
generateReels({ gameConfig: config }: Simulation): void;
|
|
1296
|
+
}
|
|
1297
|
+
interface GeneratedReelSetOptions extends ReelSetOptions {
|
|
1269
1298
|
/**
|
|
1270
|
-
*
|
|
1299
|
+
* The weights of the symbols in the reelset.\
|
|
1300
|
+
* This is a mapping of symbol IDs to their respective weights.
|
|
1271
1301
|
*/
|
|
1272
|
-
|
|
1302
|
+
symbolWeights: Record<string, number>;
|
|
1273
1303
|
/**
|
|
1274
|
-
*
|
|
1275
|
-
*
|
|
1304
|
+
* The number of rows in the reelset.\
|
|
1305
|
+
* Default is 250, but can be adjusted as needed.
|
|
1306
|
+
*/
|
|
1307
|
+
rowsAmount?: number;
|
|
1308
|
+
/**
|
|
1309
|
+
* Prevent the same symbol from appearing directly above or below itself.\
|
|
1310
|
+
* This can be a single number for all symbols, or a mapping of symbol IDs to
|
|
1311
|
+
* their respective spacing values.
|
|
1312
|
+
*
|
|
1313
|
+
* Must be 1 or higher, if set.
|
|
1314
|
+
*
|
|
1315
|
+
* **This is overridden by `symbolStacks`**
|
|
1276
1316
|
*/
|
|
1277
|
-
|
|
1317
|
+
spaceBetweenSameSymbols?: number | Record<string, number>;
|
|
1318
|
+
/**
|
|
1319
|
+
* Prevents specific symbols from appearing within a certain distance of each other.
|
|
1320
|
+
*
|
|
1321
|
+
* Useful for preventing scatter and super scatter symbols from appearing too close to each other.
|
|
1322
|
+
*
|
|
1323
|
+
* **This is overridden by `symbolStacks`**
|
|
1324
|
+
*/
|
|
1325
|
+
spaceBetweenSymbols?: Record<string, Record<string, number>>;
|
|
1326
|
+
/**
|
|
1327
|
+
* A percentage value 0-100 that indicates the likelihood of a symbol being stacked.\
|
|
1328
|
+
* A value of 0 means no stacked symbols, while 100 means all symbols are stacked.
|
|
1329
|
+
*
|
|
1330
|
+
* This is only a preference. Symbols may still not be stacked if\
|
|
1331
|
+
* other restrictions (like `spaceBetweenSameSymbols`) prevent it.
|
|
1332
|
+
*
|
|
1333
|
+
* **This is overridden by `symbolStacks`**
|
|
1334
|
+
*/
|
|
1335
|
+
preferStackedSymbols?: number;
|
|
1336
|
+
/**
|
|
1337
|
+
* A mapping of symbols to their respective advanced stacking configuration.
|
|
1338
|
+
*
|
|
1339
|
+
* @example
|
|
1340
|
+
* ```ts
|
|
1341
|
+
* symbolStacks: {
|
|
1342
|
+
* "W": {
|
|
1343
|
+
* chance: { "1": 20, "2": 20, "3": 20, "4": 20 }, // 20% chance to be stacked on reels 2-5
|
|
1344
|
+
* min: 2, // At least 2 wilds in a stack
|
|
1345
|
+
* max: 4, // At most 4 wilds in a stack
|
|
1346
|
+
* }
|
|
1347
|
+
* }
|
|
1348
|
+
* ```
|
|
1349
|
+
*/
|
|
1350
|
+
symbolStacks?: Record<string, {
|
|
1351
|
+
chance: number | Record<string, number>;
|
|
1352
|
+
min?: number | Record<string, number>;
|
|
1353
|
+
max?: number | Record<string, number>;
|
|
1354
|
+
}>;
|
|
1355
|
+
/**
|
|
1356
|
+
* Configures symbols to be limited to specific reels.\
|
|
1357
|
+
* For example, you could configure Scatters to appear only on reels 1, 3 and 5.
|
|
1358
|
+
*
|
|
1359
|
+
* @example
|
|
1360
|
+
* ```ts
|
|
1361
|
+
* limitSymbolsToReels: {
|
|
1362
|
+
* "S": [0, 2, 4], // Remember that reels are 0-indexed.
|
|
1363
|
+
* }
|
|
1364
|
+
* ```
|
|
1365
|
+
*/
|
|
1366
|
+
limitSymbolsToReels?: Record<string, number[]>;
|
|
1367
|
+
/**
|
|
1368
|
+
* Defines optional quotas for symbols on the reels.\
|
|
1369
|
+
* The quota (1-100%) defines how often a symbol should appear in the reelset, or in a specific reel.
|
|
1370
|
+
*
|
|
1371
|
+
* This is particularly useful for controlling the frequency of special symbols like scatters or wilds.
|
|
1372
|
+
*
|
|
1373
|
+
* Reels not provided for a symbol will use the weights from `symbolWeights`.
|
|
1374
|
+
*
|
|
1375
|
+
* _Any_ small quota will ensure that the symbol appears at least once on the reel.
|
|
1376
|
+
*
|
|
1377
|
+
* @example
|
|
1378
|
+
* ```ts
|
|
1379
|
+
* symbolQuotas: {
|
|
1380
|
+
* "S": 3, // 3% of symbols on each reel will be scatters
|
|
1381
|
+
* "W": { "1": 10, "2": 5, "3": 3, "4": 1 }, // Wilds will appear with different quotas on selected reels
|
|
1382
|
+
* }
|
|
1383
|
+
* ```
|
|
1384
|
+
*/
|
|
1385
|
+
symbolQuotas?: Record<string, number | Record<string, number>>;
|
|
1386
|
+
/**
|
|
1387
|
+
* If true, existing reels CSV files will be overwritten.
|
|
1388
|
+
*/
|
|
1389
|
+
overrideExisting?: boolean;
|
|
1390
|
+
/**
|
|
1391
|
+
* Optional seed for the RNG to ensure reproducible results.
|
|
1392
|
+
*
|
|
1393
|
+
* Default seed is `0`.
|
|
1394
|
+
*
|
|
1395
|
+
* Note: Seeds 0 and 1 produce the same results.
|
|
1396
|
+
*/
|
|
1397
|
+
seed?: number;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* This class is responsible for providing reel sets for slot games based on a static configuration or file.
|
|
1402
|
+
*/
|
|
1403
|
+
declare class StaticReelSet extends ReelSet {
|
|
1404
|
+
reels: Reels;
|
|
1405
|
+
csvPath: string;
|
|
1406
|
+
private _strReels;
|
|
1407
|
+
constructor(opts: StaticReelSetOptions);
|
|
1408
|
+
private validateConfig;
|
|
1409
|
+
generateReels({ gameConfig: config }: Simulation): void;
|
|
1410
|
+
}
|
|
1411
|
+
interface StaticReelSetOptions extends ReelSetOptions {
|
|
1412
|
+
reels?: string[][];
|
|
1413
|
+
csvPath?: string;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
declare class StandaloneBoard {
|
|
1417
|
+
private board;
|
|
1418
|
+
private ctx;
|
|
1419
|
+
private reelsAmount;
|
|
1420
|
+
private symbolsPerReel;
|
|
1421
|
+
private padSymbols;
|
|
1422
|
+
constructor(opts: StandaloneBoardOptions);
|
|
1423
|
+
/**
|
|
1424
|
+
* Resets the board to an empty state.\
|
|
1425
|
+
* This is called before drawing a new board.
|
|
1426
|
+
*/
|
|
1427
|
+
resetBoard(): void;
|
|
1428
|
+
/**
|
|
1429
|
+
* Gets the current reels and symbols on the board.
|
|
1430
|
+
*/
|
|
1431
|
+
getBoardReels(): Reels;
|
|
1432
|
+
getPaddingTop(): Reels;
|
|
1433
|
+
getPaddingBottom(): Reels;
|
|
1434
|
+
private resetReels;
|
|
1435
|
+
/**
|
|
1436
|
+
* Sets the anticipation value for a specific reel.
|
|
1437
|
+
*/
|
|
1438
|
+
setAnticipationForReel(reelIndex: number, value: boolean): void;
|
|
1439
|
+
/**
|
|
1440
|
+
* Counts how many symbols matching the criteria are on a specific reel.
|
|
1441
|
+
*/
|
|
1442
|
+
countSymbolsOnReel(symbolOrProperties: GameSymbol | Record<string, any>, reelIndex: number): number;
|
|
1443
|
+
/**
|
|
1444
|
+
* Counts how many symbols matching the criteria are on the board.
|
|
1445
|
+
*
|
|
1446
|
+
* Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.
|
|
1447
|
+
*
|
|
1448
|
+
* Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.
|
|
1449
|
+
*/
|
|
1450
|
+
countSymbolsOnBoard(symbolOrProperties: GameSymbol | Record<string, any>): [number, Record<number, number>];
|
|
1451
|
+
/**
|
|
1452
|
+
* Checks if a symbol appears more than once on any reel in the current reel set.
|
|
1453
|
+
*
|
|
1454
|
+
* Useful to check for "forbidden" generations, e.g. 2 scatters on one reel.
|
|
1455
|
+
*/
|
|
1456
|
+
isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol): boolean;
|
|
1457
|
+
/**
|
|
1458
|
+
* Gets all reel stops (positions) where the specified symbol appears in the current reel set.\
|
|
1459
|
+
* Returns an array of arrays, where each inner array contains the positions for the corresponding reel.
|
|
1460
|
+
*/
|
|
1461
|
+
getReelStopsForSymbol(reels: Reels, symbol: GameSymbol): number[][];
|
|
1462
|
+
/**
|
|
1463
|
+
* Combines multiple arrays of reel stops into a single array of reel stops.\
|
|
1464
|
+
*/
|
|
1465
|
+
combineReelStops(...reelStops: number[][][]): number[][];
|
|
1466
|
+
/**
|
|
1467
|
+
* From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.
|
|
1468
|
+
*
|
|
1469
|
+
* Mostly useful for placing scatter symbols on the board.
|
|
1470
|
+
*/
|
|
1471
|
+
getRandomReelStops(reels: Reels, reelStops: number[][], amount: number): Record<number, number>;
|
|
1472
|
+
/**
|
|
1473
|
+
* Selects a random reel set based on the configured weights of the current result set.\
|
|
1474
|
+
* Returns the reels as arrays of GameSymbols.
|
|
1475
|
+
*/
|
|
1476
|
+
getRandomReelset(): Reels;
|
|
1477
|
+
/**
|
|
1478
|
+
* Draws a board using specified reel stops.
|
|
1479
|
+
*/
|
|
1480
|
+
drawBoardWithForcedStops(reels: Reels, forcedStops: Record<string, number>): void;
|
|
1481
|
+
/**
|
|
1482
|
+
* Draws a board using random reel stops.
|
|
1483
|
+
*/
|
|
1484
|
+
drawBoardWithRandomStops(reels: Reels): void;
|
|
1485
|
+
private drawBoardMixed;
|
|
1486
|
+
/**
|
|
1487
|
+
* Tumbles the board. All given symbols will be deleted and new symbols will fall from the top.
|
|
1488
|
+
*/
|
|
1489
|
+
tumbleBoard(symbolsToDelete: Array<{
|
|
1490
|
+
reelIdx: number;
|
|
1491
|
+
rowIdx: number;
|
|
1492
|
+
}>): void;
|
|
1493
|
+
}
|
|
1494
|
+
interface StandaloneBoardOptions {
|
|
1495
|
+
ctx: GameContext;
|
|
1496
|
+
reelsAmount: number;
|
|
1497
|
+
symbolsPerReel: number[];
|
|
1498
|
+
padSymbols: number;
|
|
1278
1499
|
}
|
|
1279
|
-
declare function createSlotGame<TGame>(opts: TGame extends InferGameType<infer G, infer S, infer U> ? CreateSlotGameOpts<G, S, U> : never): TGame;
|
|
1280
|
-
declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState) => TUserState;
|
|
1281
|
-
declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
|
|
1282
|
-
declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
|
|
1283
|
-
declare const defineReelSets: <TSymbols extends AnySymbols>(reelSets: ReelGenerator[]) => ReelGenerator[];
|
|
1284
1500
|
|
|
1285
|
-
export { type AnyGameModes, type AnySymbols, type AnyUserData, ClusterWinType, type
|
|
1501
|
+
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, createSlotGame, defineGameModes, defineSymbols, defineUserState };
|