@slot-engine/core 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.mts +0 -7
  3. package/dist/index.d.ts +0 -7
  4. package/dist/index.js +9 -7
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +9 -7
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +5 -1
  9. package/.turbo/turbo-build.log +0 -33
  10. package/.turbo/turbo-typecheck.log +0 -4
  11. package/CHANGELOG.md +0 -7
  12. package/index.ts +0 -205
  13. package/lib/zstd.exe +0 -0
  14. package/optimizer-rust/Cargo.toml +0 -19
  15. package/optimizer-rust/src/exes.rs +0 -154
  16. package/optimizer-rust/src/main.rs +0 -1659
  17. package/src/Board.ts +0 -527
  18. package/src/Book.ts +0 -83
  19. package/src/GameConfig.ts +0 -148
  20. package/src/GameMode.ts +0 -86
  21. package/src/GameState.ts +0 -272
  22. package/src/GameSymbol.ts +0 -61
  23. package/src/ReelGenerator.ts +0 -589
  24. package/src/ResultSet.ts +0 -207
  25. package/src/Simulation.ts +0 -625
  26. package/src/SlotGame.ts +0 -117
  27. package/src/Wallet.ts +0 -203
  28. package/src/WinType.ts +0 -102
  29. package/src/analysis/index.ts +0 -198
  30. package/src/analysis/utils.ts +0 -128
  31. package/src/optimizer/OptimizationConditions.ts +0 -99
  32. package/src/optimizer/OptimizationParameters.ts +0 -46
  33. package/src/optimizer/OptimizationScaling.ts +0 -18
  34. package/src/optimizer/index.ts +0 -142
  35. package/src/utils/math-config.ts +0 -109
  36. package/src/utils/setup-file.ts +0 -36
  37. package/src/utils/zstd.ts +0 -28
  38. package/src/winTypes/ClusterWinType.ts +0 -3
  39. package/src/winTypes/LinesWinType.ts +0 -208
  40. package/src/winTypes/ManywaysWinType.ts +0 -3
  41. package/tsconfig.json +0 -19
  42. package/utils.ts +0 -270
package/src/Board.ts DELETED
@@ -1,527 +0,0 @@
1
- import { AnyGameModes, AnySymbols, AnyUserData, CommonGameOptions } from "../index"
2
- import { randomItem, weightedRandom } from "../utils"
3
- import { GameState } from "./GameState"
4
- import { GameSymbol } from "./GameSymbol"
5
- import { Reels } from "./ReelGenerator"
6
- import { AnySimulationContext } from "./Simulation"
7
-
8
- /**
9
- * A version of the Board class that is disconnected from the actual game state\
10
- * and operates on a copy of the game context.
11
- *
12
- * Can be used in custom game logic where you need to evaluate an additional board or reels independent of the main board,\
13
- * similar to the top and bottom reels of the game "San Quentin".
14
- */
15
- export class StandaloneBoard {
16
- protected reels: Reels
17
- protected paddingTop: Reels
18
- protected paddingBottom: Reels
19
- protected anticipation: number[]
20
- protected ctx: AnySimulationContext
21
-
22
- constructor(opts: StandaloneBoardOpts) {
23
- this.reels = []
24
- this.paddingTop = []
25
- this.paddingBottom = []
26
- this.anticipation = []
27
- this.ctx = opts.ctx
28
- }
29
-
30
- /**
31
- * Updates the context used by this board instance.
32
- */
33
- context(ctx: AnySimulationContext) {
34
- this.ctx = ctx
35
- }
36
-
37
- /**
38
- * Resets the board to an empty state.\
39
- * This is called before drawing a new board.
40
- */
41
- resetBoard() {
42
- this.resetReels()
43
- }
44
-
45
- private makeEmptyReels() {
46
- return Array.from({ length: this.ctx.getCurrentGameMode().reelsAmount }, () => [])
47
- }
48
-
49
- private resetReels() {
50
- this.reels = this.makeEmptyReels()
51
- this.anticipation = Array.from(
52
- { length: this.ctx.getCurrentGameMode().reelsAmount },
53
- () => 0,
54
- )
55
- if (this.ctx.config.padSymbols && this.ctx.config.padSymbols > 0) {
56
- this.paddingTop = this.makeEmptyReels()
57
- this.paddingBottom = this.makeEmptyReels()
58
- }
59
- }
60
-
61
- /**
62
- * Counts how many symbols matching the criteria are on a specific reel.
63
- */
64
- countSymbolsOnReel(
65
- symbolOrProperties: GameSymbol | Record<string, any>,
66
- reelIndex: number,
67
- ) {
68
- let total = 0
69
-
70
- for (const symbol of this.reels[reelIndex]!) {
71
- let matches = true
72
- if (symbolOrProperties instanceof GameSymbol) {
73
- if (symbol.id !== symbolOrProperties.id) matches = false
74
- } else {
75
- for (const [key, value] of Object.entries(symbolOrProperties)) {
76
- if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {
77
- matches = false
78
- break
79
- }
80
- }
81
- }
82
- if (matches) {
83
- total++
84
- }
85
- }
86
-
87
- return total
88
- }
89
-
90
- /**
91
- * Counts how many symbols matching the criteria are on the board.
92
- *
93
- * Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.
94
- *
95
- * Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.
96
- */
97
- countSymbolsOnBoard(
98
- symbolOrProperties: GameSymbol | Record<string, any>,
99
- ): [number, Record<number, number>] {
100
- let total = 0
101
- const onReel: Record<number, number> = {}
102
-
103
- for (const [ridx, reel] of this.reels.entries()) {
104
- for (const symbol of reel) {
105
- let matches = true
106
-
107
- if (symbolOrProperties instanceof GameSymbol) {
108
- if (symbol.id !== symbolOrProperties.id) matches = false
109
- } else {
110
- for (const [key, value] of Object.entries(symbolOrProperties)) {
111
- if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {
112
- matches = false
113
- break
114
- }
115
- }
116
- }
117
-
118
- if (matches) {
119
- total++
120
- if (onReel[ridx] === undefined) {
121
- onReel[ridx] = 1
122
- } else {
123
- onReel[ridx]++
124
- }
125
- }
126
- }
127
- }
128
-
129
- return [total, onReel]
130
- }
131
-
132
- /**
133
- * Checks if a symbol appears more than once on any reel in the current reel set.
134
- *
135
- * Useful to check for "forbidden" generations, e.g. 2 scatters on one reel.
136
- */
137
- isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {
138
- for (const reel of this.reels) {
139
- let count = 0
140
- for (const sym of reel) {
141
- if (sym.id === symbol.id) {
142
- count++
143
- }
144
- if (count > 1) {
145
- return true
146
- }
147
- }
148
- }
149
- return false
150
- }
151
-
152
- /**
153
- * Draws a board using specified reel stops.
154
- */
155
- drawBoardWithForcedStops(reels: Reels, forcedStops: Record<string, number>) {
156
- this.drawBoardMixed(reels, forcedStops)
157
- }
158
-
159
- /**
160
- * Draws a board using random reel stops.
161
- */
162
- drawBoardWithRandomStops(reels: Reels) {
163
- this.drawBoardMixed(reels)
164
- }
165
-
166
- private drawBoardMixed(reels: Reels, forcedStops?: Record<string, number>) {
167
- this.resetReels()
168
-
169
- const finalReelStops: (number | null)[] = Array.from(
170
- { length: this.ctx.getCurrentGameMode().reelsAmount },
171
- () => null,
172
- )
173
-
174
- if (forcedStops) {
175
- // Fill in forced stops
176
- for (const [r, stopPos] of Object.entries(forcedStops)) {
177
- const reelIdx = Number(r)
178
- const symCount = this.ctx.getCurrentGameMode().symbolsPerReel[reelIdx]!
179
- finalReelStops[reelIdx] =
180
- stopPos - Math.round(this.ctx.state.rng.randomFloat(0, symCount - 1))
181
- }
182
- }
183
-
184
- // Fill in random stops for reels without a forced stop
185
- for (let i = 0; i < finalReelStops.length; i++) {
186
- if (finalReelStops[i] === null) {
187
- finalReelStops[i] = Math.floor(
188
- this.ctx.state.rng.randomFloat(0, reels[i]!.length - 1),
189
- )
190
- }
191
- }
192
-
193
- for (let ridx = 0; ridx < this.ctx.getCurrentGameMode().reelsAmount; ridx++) {
194
- const reelPos = finalReelStops[ridx]!
195
-
196
- if (this.ctx.config.padSymbols && this.ctx.config.padSymbols > 0) {
197
- for (let p = this.ctx.config.padSymbols - 1; p >= 0; p--) {
198
- const topPos = (reelPos - (p + 1)) % reels[ridx]!.length
199
- this.paddingTop[ridx]!.push(reels[ridx]![topPos]!)
200
- const bottomPos =
201
- (reelPos + this.ctx.getCurrentGameMode().symbolsPerReel[ridx]! + p) %
202
- reels[ridx]!.length
203
- this.paddingBottom[ridx]!.unshift(reels[ridx]![bottomPos]!)
204
- }
205
- }
206
-
207
- for (
208
- let row = 0;
209
- row < this.ctx.getCurrentGameMode().symbolsPerReel[ridx]!;
210
- row++
211
- ) {
212
- const symbol = reels[ridx]![(reelPos + row) % reels[ridx]!.length]
213
-
214
- if (!symbol) {
215
- throw new Error(`Failed to get symbol at pos ${reelPos + row} on reel ${ridx}`)
216
- }
217
-
218
- this.reels[ridx]![row] = symbol
219
- }
220
- }
221
- }
222
- }
223
-
224
- interface StandaloneBoardOpts {
225
- ctx: AnySimulationContext
226
- }
227
-
228
- /**
229
- * Extends GameState. Provides board-related functionality.
230
- */
231
- export class Board<
232
- TGameModes extends AnyGameModes,
233
- TSymbols extends AnySymbols,
234
- TUserState extends AnyUserData,
235
- > extends GameState<TGameModes, TSymbols, TUserState> {
236
- board: {
237
- reels: Reels
238
- paddingTop: Reels
239
- paddingBottom: Reels
240
- anticipation: number[]
241
- }
242
-
243
- constructor(opts: CommonGameOptions<TGameModes, TSymbols, TUserState>) {
244
- super(opts)
245
-
246
- this.board = {
247
- reels: [],
248
- paddingTop: [],
249
- paddingBottom: [],
250
- anticipation: [],
251
- }
252
- }
253
-
254
- /**
255
- * Resets the board to an empty state.\
256
- * This is called before drawing a new board.
257
- */
258
- resetBoard() {
259
- this.resetReels()
260
- }
261
-
262
- private makeEmptyReels() {
263
- return Array.from({ length: this.getCurrentGameMode().reelsAmount }, () => [])
264
- }
265
-
266
- private resetReels() {
267
- this.board.reels = this.makeEmptyReels()
268
- this.board.anticipation = Array.from(
269
- { length: this.getCurrentGameMode().reelsAmount },
270
- () => 0,
271
- )
272
- if (this.config.padSymbols && this.config.padSymbols > 0) {
273
- this.board.paddingTop = this.makeEmptyReels()
274
- this.board.paddingBottom = this.makeEmptyReels()
275
- }
276
- }
277
-
278
- /**
279
- * Counts how many symbols matching the criteria are on a specific reel.
280
- */
281
- countSymbolsOnReel(
282
- symbolOrProperties: GameSymbol | Record<string, any>,
283
- reelIndex: number,
284
- ) {
285
- let total = 0
286
-
287
- for (const symbol of this.board.reels[reelIndex]!) {
288
- let matches = true
289
- if (symbolOrProperties instanceof GameSymbol) {
290
- if (symbol.id !== symbolOrProperties.id) matches = false
291
- } else {
292
- for (const [key, value] of Object.entries(symbolOrProperties)) {
293
- if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {
294
- matches = false
295
- break
296
- }
297
- }
298
- }
299
- if (matches) {
300
- total++
301
- }
302
- }
303
-
304
- return total
305
- }
306
-
307
- /**
308
- * Counts how many symbols matching the criteria are on the board.
309
- *
310
- * Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.
311
- *
312
- * Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.
313
- */
314
- countSymbolsOnBoard(
315
- symbolOrProperties: GameSymbol | Record<string, any>,
316
- ): [number, Record<number, number>] {
317
- let total = 0
318
- const onReel: Record<number, number> = {}
319
-
320
- for (const [ridx, reel] of this.board.reels.entries()) {
321
- for (const symbol of reel) {
322
- let matches = true
323
-
324
- if (symbolOrProperties instanceof GameSymbol) {
325
- if (symbol.id !== symbolOrProperties.id) matches = false
326
- } else {
327
- for (const [key, value] of Object.entries(symbolOrProperties)) {
328
- if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {
329
- matches = false
330
- break
331
- }
332
- }
333
- }
334
-
335
- if (matches) {
336
- total++
337
- if (onReel[ridx] === undefined) {
338
- onReel[ridx] = 1
339
- } else {
340
- onReel[ridx]++
341
- }
342
- }
343
- }
344
- }
345
-
346
- return [total, onReel]
347
- }
348
-
349
- /**
350
- * Checks if a symbol appears more than once on any reel in the current reel set.
351
- *
352
- * Useful to check for "forbidden" generations, e.g. 2 scatters on one reel.
353
- */
354
- isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {
355
- for (const reel of this.board.reels) {
356
- let count = 0
357
- for (const sym of reel) {
358
- if (sym.id === symbol.id) {
359
- count++
360
- }
361
- if (count > 1) {
362
- return true
363
- }
364
- }
365
- }
366
- return false
367
- }
368
-
369
- /**
370
- * Gets all reel stops (positions) where the specified symbol appears in the current reel set.\
371
- * Returns an array of arrays, where each inner array contains the positions for the corresponding reel.
372
- */
373
- getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {
374
- const reelStops: number[][] = []
375
- for (let ridx = 0; ridx < reels.length; ridx++) {
376
- const reel = reels[ridx]!
377
- const positions: number[] = []
378
- for (let pos = 0; pos < reel.length; pos++) {
379
- if (reel[pos]!.id === symbol.id) {
380
- positions.push(pos)
381
- }
382
- }
383
- reelStops.push(positions)
384
- }
385
- return reelStops
386
- }
387
-
388
- /**
389
- * Combines multiple arrays of reel stops into a single array of reel stops.\
390
- */
391
- combineReelStops(...reelStops: number[][][]) {
392
- const combined: number[][] = []
393
- for (let ridx = 0; ridx < this.getCurrentGameMode().reelsAmount; ridx++) {
394
- combined[ridx] = []
395
- for (const stops of reelStops) {
396
- combined[ridx] = combined[ridx]!.concat(stops[ridx]!)
397
- }
398
- }
399
- return combined
400
- }
401
-
402
- /**
403
- * From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.
404
- *
405
- * Mostly useful for placing scatter symbols on the board.
406
- */
407
- getRandomReelStops(reels: Reels, reelStops: number[][], amount: number) {
408
- const reelsAmount = this.getCurrentGameMode().reelsAmount
409
- const symProbsOnReels: number[] = []
410
- const stopPositionsForReels: Record<string, number> = {}
411
-
412
- for (let ridx = 0; ridx < reelsAmount; ridx++) {
413
- symProbsOnReels.push(reelStops[ridx]!.length / reels[ridx]!.length)
414
- }
415
-
416
- while (Object.keys(stopPositionsForReels).length !== amount) {
417
- const possibleReels: number[] = []
418
- for (let i = 0; i < reelsAmount; i++) {
419
- if (symProbsOnReels[i]! > 0) {
420
- possibleReels.push(i)
421
- }
422
- }
423
- const possibleProbs = symProbsOnReels.filter((p) => p > 0)
424
- const weights = Object.fromEntries(
425
- possibleReels.map((ridx, idx) => [ridx, possibleProbs[idx]!]),
426
- )
427
- const chosenReel = weightedRandom(weights, this.state.rng)
428
- const chosenStop = randomItem(reelStops[Number(chosenReel)]!, this.state.rng)
429
- symProbsOnReels[Number(chosenReel)] = 0
430
- stopPositionsForReels[chosenReel] = chosenStop
431
- }
432
-
433
- return stopPositionsForReels
434
- }
435
-
436
- /**
437
- * Selects a random reelset based on the configured weights for the current game mode.\
438
- * Returns the reels as arrays of GameSymbols.
439
- */
440
- getRandomReelset() {
441
- const weights = this.state.currentResultSet.reelWeights
442
- const evalWeights = this.state.currentResultSet.reelWeights.evaluate?.(
443
- this as Board<any, any, any>,
444
- )
445
-
446
- let reelSetId: string = ""
447
-
448
- if (evalWeights) {
449
- reelSetId = weightedRandom(evalWeights, this.state.rng)
450
- } else {
451
- reelSetId = weightedRandom(weights[this.state.currentSpinType]!, this.state.rng)
452
- }
453
-
454
- const reelSet = this.getReelsetById(this.state.currentGameMode, reelSetId)
455
- return reelSet.reels
456
- }
457
-
458
- /**
459
- * Draws a board using specified reel stops.
460
- */
461
- drawBoardWithForcedStops(reels: Reels, forcedStops: Record<string, number>) {
462
- this.drawBoardMixed(reels, forcedStops)
463
- }
464
-
465
- /**
466
- * Draws a board using random reel stops.
467
- */
468
- drawBoardWithRandomStops(reels: Reels) {
469
- this.drawBoardMixed(reels)
470
- }
471
-
472
- private drawBoardMixed(reels: Reels, forcedStops?: Record<string, number>) {
473
- this.resetReels()
474
-
475
- const finalReelStops: (number | null)[] = Array.from(
476
- { length: this.getCurrentGameMode().reelsAmount },
477
- () => null,
478
- )
479
-
480
- if (forcedStops) {
481
- // Fill in forced stops
482
- for (const [r, stopPos] of Object.entries(forcedStops)) {
483
- const reelIdx = Number(r)
484
- const symCount = this.getCurrentGameMode().symbolsPerReel[reelIdx]!
485
- finalReelStops[reelIdx] =
486
- stopPos - Math.round(this.state.rng.randomFloat(0, symCount - 1))
487
- if (finalReelStops[reelIdx]! < 0) {
488
- finalReelStops[reelIdx] = reels[reelIdx]!.length + finalReelStops[reelIdx]!
489
- }
490
- }
491
- }
492
-
493
- // Fill in random stops for reels without a forced stop
494
- for (let i = 0; i < finalReelStops.length; i++) {
495
- if (finalReelStops[i] === null) {
496
- finalReelStops[i] = Math.floor(
497
- this.state.rng.randomFloat(0, reels[i]!.length - 1),
498
- )
499
- }
500
- }
501
-
502
- for (let ridx = 0; ridx < this.getCurrentGameMode().reelsAmount; ridx++) {
503
- const reelPos = finalReelStops[ridx]!
504
-
505
- if (this.config.padSymbols && this.config.padSymbols > 0) {
506
- for (let p = this.config.padSymbols - 1; p >= 0; p--) {
507
- const topPos = (reelPos - (p + 1)) % reels[ridx]!.length
508
- this.board.paddingTop[ridx]!.push(reels[ridx]![topPos]!)
509
- const bottomPos =
510
- (reelPos + this.getCurrentGameMode().symbolsPerReel[ridx]! + p) %
511
- reels[ridx]!.length
512
- this.board.paddingBottom[ridx]!.unshift(reels[ridx]![bottomPos]!)
513
- }
514
- }
515
-
516
- for (let row = 0; row < this.getCurrentGameMode().symbolsPerReel[ridx]!; row++) {
517
- const symbol = reels[ridx]![(reelPos + row) % reels[ridx]!.length]
518
-
519
- if (!symbol) {
520
- throw new Error(`Failed to get symbol at pos ${reelPos + row} on reel ${ridx}`)
521
- }
522
-
523
- this.board.reels[ridx]![row] = symbol
524
- }
525
- }
526
- }
527
- }
package/src/Book.ts DELETED
@@ -1,83 +0,0 @@
1
- import { GameConfig } from "./GameConfig"
2
- import { AnySimulationContext } from "./Simulation"
3
-
4
- export class Book {
5
- id: number
6
- criteria: string = "N/A"
7
- protected events: BookEvent[] = []
8
- protected payout: number = 0
9
- protected basegameWins: number = 0
10
- protected freespinsWins: number = 0
11
-
12
- constructor(opts: BookOpts) {
13
- this.id = opts.id
14
- }
15
-
16
- /**
17
- * Adds an event to the book.
18
- */
19
- addEvent(event: Omit<BookEvent, "index">) {
20
- const index = this.events.length + 1
21
- this.events.push({ index, ...event })
22
- }
23
-
24
- /**
25
- * Transfers the win data from the wallet to the book.
26
- */
27
- writePayout(ctx: AnySimulationContext) {
28
- function process(number: number) {
29
- return Math.round(Math.min(number, ctx.config.maxWinX) * 100) / 100
30
- }
31
-
32
- this.payout = Math.round(process(ctx.wallet.getCurrentWin()) * 100)
33
- this.basegameWins = process(
34
- ctx.wallet.getCurrentWinPerSpinType()[GameConfig.SPIN_TYPE.BASE_GAME] || 0,
35
- )
36
- this.freespinsWins = process(
37
- ctx.wallet.getCurrentWinPerSpinType()[GameConfig.SPIN_TYPE.FREE_SPINS] || 0,
38
- )
39
- }
40
-
41
- getPayout() {
42
- return this.payout
43
- }
44
-
45
- getBasegameWins() {
46
- return this.basegameWins
47
- }
48
-
49
- getFreespinsWins() {
50
- return this.freespinsWins
51
- }
52
-
53
- serialize() {
54
- return {
55
- id: this.id,
56
- criteria: this.criteria,
57
- events: this.events,
58
- payout: this.payout,
59
- basegameWins: this.basegameWins,
60
- freespinsWins: this.freespinsWins,
61
- }
62
- }
63
-
64
- static fromSerialized(data: ReturnType<Book["serialize"]>) {
65
- const book = new Book({ id: data.id })
66
- book.criteria = data.criteria
67
- book.events = data.events
68
- book.payout = data.payout
69
- book.basegameWins = data.basegameWins
70
- book.freespinsWins = data.freespinsWins
71
- return book
72
- }
73
- }
74
-
75
- export interface BookEvent {
76
- index: number
77
- type: string
78
- data: Record<string, any>
79
- }
80
-
81
- interface BookOpts {
82
- id: number
83
- }