@slot-engine/core 0.2.9 → 0.2.11
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 +0 -14
- package/dist/index.d.ts +0 -14
- package/dist/index.js +294 -206
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +294 -206
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -371,7 +371,10 @@ var GameSymbol = class _GameSymbol {
|
|
|
371
371
|
constructor(opts) {
|
|
372
372
|
this.id = opts.id;
|
|
373
373
|
this.pays = opts.pays;
|
|
374
|
-
this.properties = new Map(
|
|
374
|
+
this.properties = /* @__PURE__ */ new Map();
|
|
375
|
+
for (const prop in opts.properties) {
|
|
376
|
+
this.properties.set(prop, opts.properties[prop]);
|
|
377
|
+
}
|
|
375
378
|
if (this.pays && Object.keys(this.pays).length === 0) {
|
|
376
379
|
throw new Error(`GameSymbol "${this.id}" must have pays defined.`);
|
|
377
380
|
}
|
|
@@ -387,8 +390,8 @@ var GameSymbol = class _GameSymbol {
|
|
|
387
390
|
if (symbolOrProperties instanceof _GameSymbol) {
|
|
388
391
|
return this.id === symbolOrProperties.id;
|
|
389
392
|
} else {
|
|
390
|
-
for (const
|
|
391
|
-
if (!this.properties.has(
|
|
393
|
+
for (const prop in symbolOrProperties) {
|
|
394
|
+
if (!this.properties.has(prop) || this.properties.get(prop) !== symbolOrProperties[prop]) {
|
|
392
395
|
return false;
|
|
393
396
|
}
|
|
394
397
|
}
|
|
@@ -468,8 +471,8 @@ var Board = class {
|
|
|
468
471
|
updateSymbol(reelIndex, rowIndex, properties) {
|
|
469
472
|
const symbol = this.getSymbol(reelIndex, rowIndex);
|
|
470
473
|
if (symbol) {
|
|
471
|
-
for (const
|
|
472
|
-
symbol.properties.set(
|
|
474
|
+
for (const prop in properties) {
|
|
475
|
+
symbol.properties.set(prop, properties[prop]);
|
|
473
476
|
}
|
|
474
477
|
}
|
|
475
478
|
}
|
|
@@ -485,8 +488,8 @@ var Board = class {
|
|
|
485
488
|
if (symbolOrProperties instanceof GameSymbol) {
|
|
486
489
|
if (symbol.id !== symbolOrProperties.id) matches = false;
|
|
487
490
|
} else {
|
|
488
|
-
for (const
|
|
489
|
-
if (!symbol.properties.has(
|
|
491
|
+
for (const prop in symbolOrProperties) {
|
|
492
|
+
if (!symbol.properties.has(prop) || symbol.properties.get(prop) !== symbolOrProperties[prop]) {
|
|
490
493
|
matches = false;
|
|
491
494
|
break;
|
|
492
495
|
}
|
|
@@ -508,8 +511,8 @@ var Board = class {
|
|
|
508
511
|
if (symbol.id !== symbolOrProperties.id) continue;
|
|
509
512
|
} else {
|
|
510
513
|
let matches = true;
|
|
511
|
-
for (const
|
|
512
|
-
if (!symbol.properties.has(
|
|
514
|
+
for (const prop in symbolOrProperties) {
|
|
515
|
+
if (!symbol.properties.has(prop) || symbol.properties.get(prop) !== symbolOrProperties[prop]) {
|
|
513
516
|
matches = false;
|
|
514
517
|
break;
|
|
515
518
|
}
|
|
@@ -1056,11 +1059,13 @@ var DataService = class extends AbstractService {
|
|
|
1056
1059
|
* Record data for statistical analysis.
|
|
1057
1060
|
*/
|
|
1058
1061
|
record(data) {
|
|
1062
|
+
const properties = {};
|
|
1063
|
+
for (const key in data) {
|
|
1064
|
+
properties[key] = String(data[key]);
|
|
1065
|
+
}
|
|
1059
1066
|
this.recorder.pendingRecords.push({
|
|
1060
1067
|
bookId: this.ctx().state.currentSimulationId,
|
|
1061
|
-
properties
|
|
1062
|
-
Object.entries(data).map(([k, v]) => [k, String(v)])
|
|
1063
|
-
)
|
|
1068
|
+
properties
|
|
1064
1069
|
});
|
|
1065
1070
|
}
|
|
1066
1071
|
/**
|
|
@@ -1414,19 +1419,6 @@ var Book = class {
|
|
|
1414
1419
|
data: copy(event.data)
|
|
1415
1420
|
});
|
|
1416
1421
|
}
|
|
1417
|
-
/**
|
|
1418
|
-
* Intended for internal use only.
|
|
1419
|
-
*/
|
|
1420
|
-
_serialize() {
|
|
1421
|
-
return {
|
|
1422
|
-
id: this.id,
|
|
1423
|
-
criteria: this.criteria,
|
|
1424
|
-
events: this.events,
|
|
1425
|
-
payout: this.payout,
|
|
1426
|
-
basegameWins: this.basegameWins,
|
|
1427
|
-
freespinsWins: this.freespinsWins
|
|
1428
|
-
};
|
|
1429
|
-
}
|
|
1430
1422
|
};
|
|
1431
1423
|
|
|
1432
1424
|
// src/wallet/index.ts
|
|
@@ -1595,7 +1587,7 @@ var Wallet = class {
|
|
|
1595
1587
|
this.currentWin = process2(this.currentWin);
|
|
1596
1588
|
this.cumulativeWins += this.currentWin;
|
|
1597
1589
|
let spinTypeWins = 0;
|
|
1598
|
-
for (const spinType
|
|
1590
|
+
for (const spinType in this.currentWinPerSpinType) {
|
|
1599
1591
|
const st = spinType;
|
|
1600
1592
|
const spinTypeWin = process2(this.currentWinPerSpinType[st]);
|
|
1601
1593
|
this.cumulativeWinsPerSpinType[st] += spinTypeWin;
|
|
@@ -2400,9 +2392,9 @@ var Simulation = class {
|
|
|
2400
2392
|
}
|
|
2401
2393
|
const chunkIndex = this.bookChunkIndexes.get(index);
|
|
2402
2394
|
const bookChunkPath = this.PATHS.booksChunk(mode, index, chunkIndex);
|
|
2403
|
-
const
|
|
2395
|
+
const bookLines = this.bookBuffers.get(index);
|
|
2404
2396
|
await pipeline(
|
|
2405
|
-
Readable.from(
|
|
2397
|
+
Readable.from(bookLines),
|
|
2406
2398
|
zlib.createZstdCompress(),
|
|
2407
2399
|
fs3.createWriteStream(bookChunkPath)
|
|
2408
2400
|
);
|
|
@@ -2483,50 +2475,51 @@ var Simulation = class {
|
|
|
2483
2475
|
}
|
|
2484
2476
|
}
|
|
2485
2477
|
writeChain = writeChain.then(async () => {
|
|
2486
|
-
const
|
|
2487
|
-
const
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2478
|
+
const bookId = msg.bookId;
|
|
2479
|
+
const bookCriteria = msg.bookCriteria;
|
|
2480
|
+
const bookPayout = msg.bookPayout;
|
|
2481
|
+
const bookBasegameWins = msg.bookBasegameWins;
|
|
2482
|
+
const bookFreespinsWins = msg.bookFreespinsWins;
|
|
2483
|
+
const bookLine = msg.bookLine;
|
|
2484
|
+
const bookLineWithNewline = bookLine + "\n";
|
|
2485
|
+
if (!this.summary[mode]?.criteria[bookCriteria]) {
|
|
2486
|
+
this.summary[mode].criteria[bookCriteria] = {
|
|
2494
2487
|
numSims: 0,
|
|
2495
2488
|
bsWins: 0,
|
|
2496
2489
|
fsWins: 0,
|
|
2497
2490
|
rtp: 0
|
|
2498
2491
|
};
|
|
2499
2492
|
}
|
|
2500
|
-
const bsWins = round(
|
|
2501
|
-
const fsWins = round(
|
|
2502
|
-
this.summary[mode].criteria[
|
|
2493
|
+
const bsWins = round(bookBasegameWins, 4);
|
|
2494
|
+
const fsWins = round(bookFreespinsWins, 4);
|
|
2495
|
+
const criteria = this.summary[mode].criteria[bookCriteria];
|
|
2496
|
+
criteria.numSims += 1;
|
|
2503
2497
|
this.summary[mode].total.bsWins += bsWins;
|
|
2504
2498
|
this.summary[mode].total.fsWins += fsWins;
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
const
|
|
2508
|
-
const lineSize = Buffer.byteLength(bookLine + "\n", "utf8");
|
|
2499
|
+
criteria.bsWins += bsWins;
|
|
2500
|
+
criteria.fsWins += fsWins;
|
|
2501
|
+
const lineSize = Buffer.byteLength(bookLineWithNewline, "utf8");
|
|
2509
2502
|
if (this.bookBuffers.has(index)) {
|
|
2510
|
-
this.bookBuffers.get(index).push(
|
|
2503
|
+
this.bookBuffers.get(index).push(bookLineWithNewline);
|
|
2511
2504
|
this.bookBufferSizes.set(
|
|
2512
2505
|
index,
|
|
2513
2506
|
this.bookBufferSizes.get(index) + lineSize
|
|
2514
2507
|
);
|
|
2515
2508
|
} else {
|
|
2516
|
-
this.bookBuffers.set(index, [
|
|
2509
|
+
this.bookBuffers.set(index, [bookLineWithNewline]);
|
|
2517
2510
|
this.bookBufferSizes.set(index, lineSize);
|
|
2518
2511
|
}
|
|
2519
2512
|
if (!this.tempBookIndexPaths.includes(booksIndexPath)) {
|
|
2520
2513
|
this.tempBookIndexPaths.push(booksIndexPath);
|
|
2521
2514
|
}
|
|
2522
2515
|
booksIndexBatch.push(
|
|
2523
|
-
`${
|
|
2516
|
+
`${bookId},${index},${this.bookChunkIndexes.get(index) || 0}
|
|
2524
2517
|
`
|
|
2525
2518
|
);
|
|
2526
|
-
lookupBatch.push(`${
|
|
2519
|
+
lookupBatch.push(`${bookId},1,${Math.round(bookPayout)}
|
|
2527
2520
|
`);
|
|
2528
2521
|
lookupSegBatch.push(
|
|
2529
|
-
`${
|
|
2522
|
+
`${bookId},${bookCriteria},${bookBasegameWins},${bookFreespinsWins}
|
|
2530
2523
|
`
|
|
2531
2524
|
);
|
|
2532
2525
|
if (booksIndexBatch.length >= WRITE_BATCH_SIZE) {
|
|
@@ -2535,15 +2528,10 @@ var Simulation = class {
|
|
|
2535
2528
|
if (this.bookBufferSizes.get(index) >= 10 * 1024 * 1024) {
|
|
2536
2529
|
await flushBookChunk();
|
|
2537
2530
|
}
|
|
2538
|
-
if (this.recordsWriteStream) {
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
this.recordsWriteStream,
|
|
2543
|
-
recordPrefix + JSON.stringify(record)
|
|
2544
|
-
);
|
|
2545
|
-
this.hasWrittenRecord = true;
|
|
2546
|
-
}
|
|
2531
|
+
if (this.recordsWriteStream && typeof msg.recordsLines === "string" && msg.recordsLines.length) {
|
|
2532
|
+
const recordPrefix = this.hasWrittenRecord ? "\n" : "";
|
|
2533
|
+
await write(this.recordsWriteStream, recordPrefix + msg.recordsLines);
|
|
2534
|
+
this.hasWrittenRecord = true;
|
|
2547
2535
|
}
|
|
2548
2536
|
this.wallet.mergeSerialized(msg.wallet);
|
|
2549
2537
|
worker.postMessage({ type: "credit", amount: 1 });
|
|
@@ -2634,9 +2622,11 @@ var Simulation = class {
|
|
|
2634
2622
|
});
|
|
2635
2623
|
}
|
|
2636
2624
|
}
|
|
2637
|
-
ctx.services.wallet._getWallet()
|
|
2638
|
-
|
|
2639
|
-
|
|
2625
|
+
const wallet = ctx.services.wallet._getWallet();
|
|
2626
|
+
wallet.writePayoutToBook(ctx);
|
|
2627
|
+
wallet.confirmWins(ctx);
|
|
2628
|
+
const book = ctx.services.data._getBook();
|
|
2629
|
+
if (book.payout >= ctx.config.maxWinX) {
|
|
2640
2630
|
ctx.state.triggeredMaxWin = true;
|
|
2641
2631
|
}
|
|
2642
2632
|
ctx.services.data.record({
|
|
@@ -2644,12 +2634,24 @@ var Simulation = class {
|
|
|
2644
2634
|
});
|
|
2645
2635
|
ctx.config.hooks.onSimulationAccepted?.(ctx);
|
|
2646
2636
|
this.confirmRecords(ctx);
|
|
2637
|
+
const bookLine = JSON.stringify({
|
|
2638
|
+
id: book.id,
|
|
2639
|
+
payoutMultiplier: book.payout,
|
|
2640
|
+
events: book.events
|
|
2641
|
+
});
|
|
2642
|
+
const records = ctx.services.data._getRecords();
|
|
2643
|
+
const recordsLines = records.length > 0 ? records.map((r) => JSON.stringify(r)).join("\n") : "";
|
|
2647
2644
|
parentPort2?.postMessage({
|
|
2648
2645
|
type: "complete",
|
|
2649
2646
|
simId,
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2647
|
+
bookLine,
|
|
2648
|
+
bookId: book.id,
|
|
2649
|
+
bookCriteria: book.criteria,
|
|
2650
|
+
bookPayout: book.payout,
|
|
2651
|
+
bookBasegameWins: book.basegameWins,
|
|
2652
|
+
bookFreespinsWins: book.freespinsWins,
|
|
2653
|
+
wallet: wallet.serialize(),
|
|
2654
|
+
recordsLines
|
|
2653
2655
|
});
|
|
2654
2656
|
}
|
|
2655
2657
|
initCreditListener() {
|
|
@@ -3956,13 +3958,19 @@ var LinesWinType = class extends WinType {
|
|
|
3956
3958
|
this.validateConfig();
|
|
3957
3959
|
const lineWins = [];
|
|
3958
3960
|
const reels = board;
|
|
3959
|
-
|
|
3961
|
+
const reelsLength = reels.length;
|
|
3962
|
+
const lineNumbers = Object.keys(this.lines);
|
|
3963
|
+
const numLines = lineNumbers.length;
|
|
3964
|
+
for (let lidx = 0; lidx < numLines; lidx++) {
|
|
3965
|
+
const lineNumStr = lineNumbers[lidx];
|
|
3960
3966
|
const lineNum = Number(lineNumStr);
|
|
3967
|
+
const line = this.lines[lineNum];
|
|
3961
3968
|
let baseSymbol;
|
|
3962
3969
|
const potentialWinLine = [];
|
|
3963
3970
|
const potentialWildLine = [];
|
|
3964
3971
|
let isInterrupted = false;
|
|
3965
|
-
for (
|
|
3972
|
+
for (let ridx = 0; ridx < reelsLength; ridx++) {
|
|
3973
|
+
const reel = reels[ridx];
|
|
3966
3974
|
const sidx = line[ridx];
|
|
3967
3975
|
const thisSymbol = reel[sidx];
|
|
3968
3976
|
if (!baseSymbol) {
|
|
@@ -3993,66 +4001,91 @@ var LinesWinType = class extends WinType {
|
|
|
3993
4001
|
break;
|
|
3994
4002
|
}
|
|
3995
4003
|
}
|
|
3996
|
-
const
|
|
3997
|
-
|
|
3998
|
-
)
|
|
4004
|
+
const pays = baseSymbol.pays || {};
|
|
4005
|
+
let minSymLine = Infinity;
|
|
4006
|
+
for (const key in pays) {
|
|
4007
|
+
const num = parseInt(key, 10);
|
|
4008
|
+
if (num < minSymLine) minSymLine = num;
|
|
4009
|
+
}
|
|
3999
4010
|
if (potentialWinLine.length < minSymLine) continue;
|
|
4000
4011
|
const linePayout = this.getLinePayout(potentialWinLine);
|
|
4001
4012
|
const wildLinePayout = this.getLinePayout(potentialWildLine);
|
|
4002
|
-
let finalLine
|
|
4003
|
-
kind: potentialWinLine.length,
|
|
4004
|
-
baseSymbol,
|
|
4005
|
-
symbols: potentialWinLine.map((s) => ({
|
|
4006
|
-
symbol: s.symbol,
|
|
4007
|
-
isWild: this.isWild(s.symbol),
|
|
4008
|
-
reelIndex: s.reel,
|
|
4009
|
-
posIndex: s.row
|
|
4010
|
-
})),
|
|
4011
|
-
lineNumber: lineNum,
|
|
4012
|
-
payout: linePayout
|
|
4013
|
-
};
|
|
4013
|
+
let finalLine;
|
|
4014
4014
|
if (wildLinePayout > linePayout) {
|
|
4015
4015
|
baseSymbol = potentialWildLine[0]?.symbol;
|
|
4016
|
+
const wildSymbols = [];
|
|
4017
|
+
const wildLineLength = potentialWildLine.length;
|
|
4018
|
+
for (let i = 0; i < wildLineLength; i++) {
|
|
4019
|
+
const s = potentialWildLine[i];
|
|
4020
|
+
wildSymbols.push({
|
|
4021
|
+
symbol: s.symbol,
|
|
4022
|
+
isWild: this.isWild(s.symbol),
|
|
4023
|
+
reelIndex: s.reel,
|
|
4024
|
+
posIndex: s.row
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4016
4027
|
finalLine = {
|
|
4017
|
-
kind:
|
|
4028
|
+
kind: wildLineLength,
|
|
4018
4029
|
baseSymbol,
|
|
4019
|
-
symbols:
|
|
4030
|
+
symbols: wildSymbols,
|
|
4031
|
+
lineNumber: lineNum,
|
|
4032
|
+
payout: wildLinePayout
|
|
4033
|
+
};
|
|
4034
|
+
} else {
|
|
4035
|
+
const symbols = [];
|
|
4036
|
+
const lineLength = potentialWinLine.length;
|
|
4037
|
+
for (let i = 0; i < lineLength; i++) {
|
|
4038
|
+
const s = potentialWinLine[i];
|
|
4039
|
+
symbols.push({
|
|
4020
4040
|
symbol: s.symbol,
|
|
4021
4041
|
isWild: this.isWild(s.symbol),
|
|
4022
4042
|
reelIndex: s.reel,
|
|
4023
4043
|
posIndex: s.row
|
|
4024
|
-
})
|
|
4044
|
+
});
|
|
4045
|
+
}
|
|
4046
|
+
finalLine = {
|
|
4047
|
+
kind: lineLength,
|
|
4048
|
+
baseSymbol,
|
|
4049
|
+
symbols,
|
|
4025
4050
|
lineNumber: lineNum,
|
|
4026
|
-
payout:
|
|
4051
|
+
payout: linePayout
|
|
4027
4052
|
};
|
|
4028
4053
|
}
|
|
4029
4054
|
lineWins.push(finalLine);
|
|
4030
|
-
}
|
|
4031
|
-
for (const win of lineWins) {
|
|
4032
4055
|
this.ctx.services.data.recordSymbolOccurrence({
|
|
4033
|
-
kind:
|
|
4034
|
-
symbolId:
|
|
4056
|
+
kind: finalLine.kind,
|
|
4057
|
+
symbolId: finalLine.baseSymbol.id,
|
|
4035
4058
|
spinType: this.ctx.state.currentSpinType
|
|
4036
4059
|
});
|
|
4037
4060
|
}
|
|
4038
|
-
|
|
4061
|
+
let totalPayout = 0;
|
|
4062
|
+
for (let i = 0; i < lineWins.length; i++) {
|
|
4063
|
+
totalPayout += lineWins[i].payout;
|
|
4064
|
+
}
|
|
4065
|
+
this.payout = totalPayout;
|
|
4039
4066
|
this.winCombinations = lineWins;
|
|
4040
4067
|
return this;
|
|
4041
4068
|
}
|
|
4042
4069
|
getLinePayout(line) {
|
|
4043
|
-
|
|
4044
|
-
|
|
4070
|
+
const lineLength = line.length;
|
|
4071
|
+
if (lineLength === 0) return 0;
|
|
4072
|
+
let baseSymbol;
|
|
4073
|
+
for (let i = 0; i < lineLength; i++) {
|
|
4074
|
+
const s = line[i];
|
|
4075
|
+
if (!this.isWild(s.symbol)) {
|
|
4076
|
+
baseSymbol = s.symbol;
|
|
4077
|
+
break;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4045
4080
|
if (!baseSymbol) baseSymbol = line[0].symbol;
|
|
4046
|
-
|
|
4047
|
-
const payout = this.getSymbolPayout(baseSymbol, kind);
|
|
4048
|
-
return payout;
|
|
4081
|
+
return this.getSymbolPayout(baseSymbol, lineLength);
|
|
4049
4082
|
}
|
|
4050
4083
|
};
|
|
4051
4084
|
|
|
4052
4085
|
// src/win-types/ClusterWinType.ts
|
|
4053
4086
|
var ClusterWinType = class extends WinType {
|
|
4054
|
-
_checked =
|
|
4055
|
-
_checkedWilds =
|
|
4087
|
+
_checked = /* @__PURE__ */ new Set();
|
|
4088
|
+
_checkedWilds = /* @__PURE__ */ new Set();
|
|
4056
4089
|
_currentBoard = [];
|
|
4057
4090
|
constructor(opts) {
|
|
4058
4091
|
super(opts);
|
|
@@ -4065,121 +4098,169 @@ var ClusterWinType = class extends WinType {
|
|
|
4065
4098
|
*/
|
|
4066
4099
|
evaluateWins(board) {
|
|
4067
4100
|
this.validateConfig();
|
|
4068
|
-
this._checked
|
|
4101
|
+
this._checked.clear();
|
|
4069
4102
|
this._currentBoard = board;
|
|
4070
4103
|
const clusterWins = [];
|
|
4071
4104
|
const potentialClusters = [];
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4105
|
+
const boardLength = board.length;
|
|
4106
|
+
for (let ridx = 0; ridx < boardLength; ridx++) {
|
|
4107
|
+
const reel = board[ridx];
|
|
4108
|
+
const reelLength = reel.length;
|
|
4109
|
+
for (let sidx = 0; sidx < reelLength; sidx++) {
|
|
4110
|
+
const symbol = reel[sidx];
|
|
4111
|
+
this._checkedWilds.clear();
|
|
4075
4112
|
if (this.isWild(symbol)) continue;
|
|
4076
|
-
|
|
4113
|
+
const posKey = ridx * 1e4 + sidx;
|
|
4114
|
+
if (this._checked.has(posKey)) {
|
|
4077
4115
|
continue;
|
|
4078
4116
|
}
|
|
4079
4117
|
const thisSymbol = { reel: ridx, row: sidx, symbol };
|
|
4080
|
-
this._checked.
|
|
4118
|
+
this._checked.add(posKey);
|
|
4081
4119
|
const neighbors = this.getNeighbors(ridx, sidx);
|
|
4082
4120
|
const matchingSymbols = this.evaluateCluster(symbol, neighbors);
|
|
4083
|
-
|
|
4084
|
-
|
|
4121
|
+
const matchingSize = matchingSymbols.size;
|
|
4122
|
+
if (matchingSize >= 1) {
|
|
4123
|
+
const cluster = [thisSymbol];
|
|
4124
|
+
for (const sym of matchingSymbols.values()) {
|
|
4125
|
+
cluster.push(sym);
|
|
4126
|
+
}
|
|
4127
|
+
potentialClusters.push(cluster);
|
|
4085
4128
|
}
|
|
4086
4129
|
}
|
|
4087
4130
|
}
|
|
4088
|
-
for (
|
|
4089
|
-
|
|
4090
|
-
|
|
4131
|
+
for (let ridx = 0; ridx < boardLength; ridx++) {
|
|
4132
|
+
const reel = board[ridx];
|
|
4133
|
+
const reelLength = reel.length;
|
|
4134
|
+
for (let sidx = 0; sidx < reelLength; sidx++) {
|
|
4135
|
+
const symbol = reel[sidx];
|
|
4136
|
+
this._checkedWilds.clear();
|
|
4091
4137
|
if (!this.isWild(symbol)) continue;
|
|
4092
|
-
|
|
4138
|
+
const posKey = ridx * 1e4 + sidx;
|
|
4139
|
+
if (this._checked.has(posKey)) {
|
|
4093
4140
|
continue;
|
|
4094
4141
|
}
|
|
4095
4142
|
const thisSymbol = { reel: ridx, row: sidx, symbol };
|
|
4096
|
-
this._checked.
|
|
4143
|
+
this._checked.add(posKey);
|
|
4097
4144
|
const neighbors = this.getNeighbors(ridx, sidx);
|
|
4098
4145
|
const matchingSymbols = this.evaluateCluster(symbol, neighbors);
|
|
4099
|
-
|
|
4100
|
-
|
|
4146
|
+
const matchingSize = matchingSymbols.size;
|
|
4147
|
+
if (matchingSize >= 1) {
|
|
4148
|
+
const cluster = [thisSymbol];
|
|
4149
|
+
for (const sym of matchingSymbols.values()) {
|
|
4150
|
+
cluster.push(sym);
|
|
4151
|
+
}
|
|
4152
|
+
potentialClusters.push(cluster);
|
|
4101
4153
|
}
|
|
4102
4154
|
}
|
|
4103
4155
|
}
|
|
4104
|
-
|
|
4156
|
+
const numClusters = potentialClusters.length;
|
|
4157
|
+
for (let i = 0; i < numClusters; i++) {
|
|
4158
|
+
const cluster = potentialClusters[i];
|
|
4105
4159
|
const kind = cluster.length;
|
|
4106
|
-
let baseSymbol
|
|
4160
|
+
let baseSymbol;
|
|
4161
|
+
for (let j = 0; j < kind; j++) {
|
|
4162
|
+
const sym = cluster[j].symbol;
|
|
4163
|
+
if (!this.isWild(sym)) {
|
|
4164
|
+
baseSymbol = sym;
|
|
4165
|
+
break;
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4107
4168
|
if (!baseSymbol) baseSymbol = cluster[0].symbol;
|
|
4108
4169
|
const payout = this.getSymbolPayout(baseSymbol, kind);
|
|
4109
4170
|
if (payout === 0) continue;
|
|
4110
|
-
|
|
4111
|
-
|
|
4171
|
+
const pays = baseSymbol.pays;
|
|
4172
|
+
if (!pays) continue;
|
|
4173
|
+
let hasPays = false;
|
|
4174
|
+
for (const _ in pays) {
|
|
4175
|
+
hasPays = true;
|
|
4176
|
+
break;
|
|
4112
4177
|
}
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
symbols
|
|
4178
|
+
if (!hasPays) continue;
|
|
4179
|
+
const symbols = [];
|
|
4180
|
+
for (let j = 0; j < kind; j++) {
|
|
4181
|
+
const s = cluster[j];
|
|
4182
|
+
symbols.push({
|
|
4118
4183
|
symbol: s.symbol,
|
|
4119
4184
|
isWild: this.isWild(s.symbol),
|
|
4120
4185
|
reelIndex: s.reel,
|
|
4121
4186
|
posIndex: s.row
|
|
4122
|
-
})
|
|
4187
|
+
});
|
|
4188
|
+
}
|
|
4189
|
+
clusterWins.push({
|
|
4190
|
+
payout,
|
|
4191
|
+
kind,
|
|
4192
|
+
baseSymbol,
|
|
4193
|
+
symbols
|
|
4123
4194
|
});
|
|
4124
|
-
}
|
|
4125
|
-
for (const win of clusterWins) {
|
|
4126
4195
|
this.ctx.services.data.recordSymbolOccurrence({
|
|
4127
|
-
kind
|
|
4128
|
-
symbolId:
|
|
4196
|
+
kind,
|
|
4197
|
+
symbolId: baseSymbol.id,
|
|
4129
4198
|
spinType: this.ctx.state.currentSpinType
|
|
4130
4199
|
});
|
|
4131
4200
|
}
|
|
4132
|
-
|
|
4201
|
+
let totalPayout = 0;
|
|
4202
|
+
for (let i = 0; i < clusterWins.length; i++) {
|
|
4203
|
+
totalPayout += clusterWins[i].payout;
|
|
4204
|
+
}
|
|
4205
|
+
this.payout = totalPayout;
|
|
4133
4206
|
this.winCombinations = clusterWins;
|
|
4134
4207
|
return this;
|
|
4135
4208
|
}
|
|
4136
4209
|
getNeighbors(ridx, sidx) {
|
|
4137
4210
|
const board = this._currentBoard;
|
|
4138
4211
|
const neighbors = [];
|
|
4139
|
-
|
|
4140
|
-
[ridx - 1
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
];
|
|
4145
|
-
potentialNeighbors.forEach(([nridx, nsidx]) => {
|
|
4146
|
-
if (board[nridx] && board[nridx][nsidx]) {
|
|
4147
|
-
neighbors.push({ reel: nridx, row: nsidx, symbol: board[nridx][nsidx] });
|
|
4212
|
+
if (ridx > 0) {
|
|
4213
|
+
const leftReel = board[ridx - 1];
|
|
4214
|
+
const leftSymbol = leftReel[sidx];
|
|
4215
|
+
if (leftSymbol !== void 0) {
|
|
4216
|
+
neighbors.push({ reel: ridx - 1, row: sidx, symbol: leftSymbol });
|
|
4148
4217
|
}
|
|
4149
|
-
}
|
|
4218
|
+
}
|
|
4219
|
+
const rightReel = board[ridx + 1];
|
|
4220
|
+
if (rightReel !== void 0) {
|
|
4221
|
+
const rightSymbol = rightReel[sidx];
|
|
4222
|
+
if (rightSymbol !== void 0) {
|
|
4223
|
+
neighbors.push({ reel: ridx + 1, row: sidx, symbol: rightSymbol });
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
const currentReel = board[ridx];
|
|
4227
|
+
const topSymbol = currentReel[sidx - 1];
|
|
4228
|
+
if (topSymbol !== void 0) {
|
|
4229
|
+
neighbors.push({ reel: ridx, row: sidx - 1, symbol: topSymbol });
|
|
4230
|
+
}
|
|
4231
|
+
const bottomSymbol = currentReel[sidx + 1];
|
|
4232
|
+
if (bottomSymbol !== void 0) {
|
|
4233
|
+
neighbors.push({ reel: ridx, row: sidx + 1, symbol: bottomSymbol });
|
|
4234
|
+
}
|
|
4150
4235
|
return neighbors;
|
|
4151
4236
|
}
|
|
4152
4237
|
evaluateCluster(rootSymbol, neighbors) {
|
|
4153
4238
|
const matchingSymbols = /* @__PURE__ */ new Map();
|
|
4154
|
-
neighbors.
|
|
4239
|
+
const numNeighbors = neighbors.length;
|
|
4240
|
+
for (let i = 0; i < numNeighbors; i++) {
|
|
4241
|
+
const neighbor = neighbors[i];
|
|
4155
4242
|
const { reel, row, symbol } = neighbor;
|
|
4156
|
-
|
|
4157
|
-
if (this.
|
|
4243
|
+
const posKey = reel * 1e4 + row;
|
|
4244
|
+
if (this._checked.has(posKey)) continue;
|
|
4245
|
+
if (this._checkedWilds.has(posKey)) continue;
|
|
4158
4246
|
if (this.isWild(symbol) || symbol.compare(rootSymbol)) {
|
|
4159
|
-
const key =
|
|
4247
|
+
const key = String(posKey);
|
|
4160
4248
|
matchingSymbols.set(key, { reel, row, symbol });
|
|
4161
4249
|
if (symbol.compare(rootSymbol)) {
|
|
4162
|
-
this._checked.
|
|
4250
|
+
this._checked.add(posKey);
|
|
4163
4251
|
}
|
|
4164
4252
|
if (this.isWild(symbol)) {
|
|
4165
|
-
this._checkedWilds.
|
|
4253
|
+
this._checkedWilds.add(posKey);
|
|
4166
4254
|
}
|
|
4167
|
-
const
|
|
4168
|
-
const nestedMatches = this.evaluateCluster(rootSymbol,
|
|
4169
|
-
nestedMatches.
|
|
4170
|
-
const nkey = `${nsym.reel}-${nsym.row}`;
|
|
4255
|
+
const nestedNeighbors = this.getNeighbors(reel, row);
|
|
4256
|
+
const nestedMatches = this.evaluateCluster(rootSymbol, nestedNeighbors);
|
|
4257
|
+
for (const [nkey, nsym] of nestedMatches.entries()) {
|
|
4171
4258
|
matchingSymbols.set(nkey, nsym);
|
|
4172
|
-
}
|
|
4259
|
+
}
|
|
4173
4260
|
}
|
|
4174
|
-
}
|
|
4261
|
+
}
|
|
4175
4262
|
return matchingSymbols;
|
|
4176
4263
|
}
|
|
4177
|
-
isChecked(ridx, sidx) {
|
|
4178
|
-
return !!this._checked.find((c) => c.reel === ridx && c.row === sidx);
|
|
4179
|
-
}
|
|
4180
|
-
isCheckedWild(ridx, sidx) {
|
|
4181
|
-
return !!this._checkedWilds.find((c) => c.reel === ridx && c.row === sidx);
|
|
4182
|
-
}
|
|
4183
4264
|
};
|
|
4184
4265
|
|
|
4185
4266
|
// src/win-types/ManywaysWinType.ts
|
|
@@ -4198,7 +4279,7 @@ var ManywaysWinType = class extends WinType {
|
|
|
4198
4279
|
const { jumpGaps = false } = opts;
|
|
4199
4280
|
const waysWins = [];
|
|
4200
4281
|
const reels = board;
|
|
4201
|
-
const
|
|
4282
|
+
const numReels = reels.length;
|
|
4202
4283
|
const candidateSymbols = /* @__PURE__ */ new Map();
|
|
4203
4284
|
if (jumpGaps) {
|
|
4204
4285
|
for (const reel of reels) {
|
|
@@ -4209,7 +4290,7 @@ var ManywaysWinType = class extends WinType {
|
|
|
4209
4290
|
} else {
|
|
4210
4291
|
let searchReelIdx = 0;
|
|
4211
4292
|
let searchActive = true;
|
|
4212
|
-
while (searchActive && searchReelIdx <
|
|
4293
|
+
while (searchActive && searchReelIdx < numReels) {
|
|
4213
4294
|
const reel = reels[searchReelIdx];
|
|
4214
4295
|
let hasWild = false;
|
|
4215
4296
|
for (const symbol of reel) {
|
|
@@ -4225,71 +4306,78 @@ var ManywaysWinType = class extends WinType {
|
|
|
4225
4306
|
}
|
|
4226
4307
|
}
|
|
4227
4308
|
for (const baseSymbol of candidateSymbols.values()) {
|
|
4228
|
-
|
|
4229
|
-
let
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4309
|
+
const symbolList = [];
|
|
4310
|
+
let wayLength = 0;
|
|
4311
|
+
let firstNonWildSymbol;
|
|
4312
|
+
let totalWays = 1;
|
|
4313
|
+
for (let ridx = 0; ridx < numReels; ridx++) {
|
|
4314
|
+
const reel = reels[ridx];
|
|
4315
|
+
let reelMatches;
|
|
4316
|
+
for (let sidx = 0; sidx < reel.length; sidx++) {
|
|
4317
|
+
const symbol = reel[sidx];
|
|
4233
4318
|
const isMatch = baseSymbol.compare(symbol) || this.isWild(symbol);
|
|
4234
4319
|
if (isMatch) {
|
|
4235
|
-
if (!
|
|
4236
|
-
|
|
4320
|
+
if (!reelMatches) {
|
|
4321
|
+
reelMatches = [];
|
|
4322
|
+
}
|
|
4323
|
+
reelMatches.push({ reel: ridx, row: sidx, symbol });
|
|
4324
|
+
if (!firstNonWildSymbol && !this.isWild(symbol)) {
|
|
4325
|
+
firstNonWildSymbol = symbol;
|
|
4237
4326
|
}
|
|
4238
|
-
symbolList[ridx].push({ reel: ridx, row: sidx, symbol });
|
|
4239
4327
|
}
|
|
4240
4328
|
}
|
|
4241
|
-
if (
|
|
4242
|
-
|
|
4329
|
+
if (reelMatches) {
|
|
4330
|
+
symbolList[wayLength++] = reelMatches;
|
|
4331
|
+
totalWays *= reelMatches.length;
|
|
4332
|
+
} else if (!jumpGaps) {
|
|
4243
4333
|
break;
|
|
4244
4334
|
}
|
|
4245
4335
|
}
|
|
4246
|
-
const
|
|
4247
|
-
|
|
4248
|
-
)
|
|
4249
|
-
|
|
4336
|
+
const pays = baseSymbol.pays || {};
|
|
4337
|
+
let minSymLine = Infinity;
|
|
4338
|
+
for (const key in pays) {
|
|
4339
|
+
const num = parseInt(key, 10);
|
|
4340
|
+
if (num < minSymLine) minSymLine = num;
|
|
4341
|
+
}
|
|
4250
4342
|
if (wayLength >= minSymLine) {
|
|
4251
|
-
|
|
4343
|
+
const winBaseSymbol = firstNonWildSymbol || symbolList[0][0].symbol;
|
|
4344
|
+
const singleWayPayout = this.getSymbolPayout(winBaseSymbol, wayLength);
|
|
4345
|
+
const totalPayout2 = singleWayPayout * totalWays;
|
|
4346
|
+
const symbols = [];
|
|
4347
|
+
for (let i = 0; i < wayLength; i++) {
|
|
4348
|
+
const reelSyms = symbolList[i];
|
|
4349
|
+
for (let j = 0; j < reelSyms.length; j++) {
|
|
4350
|
+
const s = reelSyms[j];
|
|
4351
|
+
symbols.push({
|
|
4352
|
+
symbol: s.symbol,
|
|
4353
|
+
isWild: this.isWild(s.symbol),
|
|
4354
|
+
reelIndex: s.reel,
|
|
4355
|
+
posIndex: s.row
|
|
4356
|
+
});
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
waysWins.push({
|
|
4360
|
+
kind: wayLength,
|
|
4361
|
+
baseSymbol: winBaseSymbol,
|
|
4362
|
+
symbols,
|
|
4363
|
+
ways: totalWays,
|
|
4364
|
+
payout: totalPayout2
|
|
4365
|
+
});
|
|
4366
|
+
this.ctx.services.data.recordSymbolOccurrence({
|
|
4367
|
+
kind: wayLength,
|
|
4368
|
+
symbolId: winBaseSymbol.id,
|
|
4369
|
+
spinType: this.ctx.state.currentSpinType
|
|
4370
|
+
});
|
|
4252
4371
|
}
|
|
4253
4372
|
}
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
if (!baseSymbol) baseSymbol = symbolList[Object.keys(symbolList)[0]][0].symbol;
|
|
4258
|
-
const singleWayPayout = this.getSymbolPayout(baseSymbol, wayLength);
|
|
4259
|
-
const totalWays = Object.values(symbolList).reduce(
|
|
4260
|
-
(ways, syms) => ways * syms.length,
|
|
4261
|
-
1
|
|
4262
|
-
);
|
|
4263
|
-
const totalPayout = singleWayPayout * totalWays;
|
|
4264
|
-
waysWins.push({
|
|
4265
|
-
kind: wayLength,
|
|
4266
|
-
baseSymbol,
|
|
4267
|
-
symbols: Object.values(symbolList).flatMap(
|
|
4268
|
-
(reel) => reel.map((s) => ({
|
|
4269
|
-
symbol: s.symbol,
|
|
4270
|
-
isWild: this.isWild(s.symbol),
|
|
4271
|
-
reelIndex: s.reel,
|
|
4272
|
-
posIndex: s.row
|
|
4273
|
-
}))
|
|
4274
|
-
),
|
|
4275
|
-
ways: totalWays,
|
|
4276
|
-
payout: totalPayout
|
|
4277
|
-
});
|
|
4278
|
-
}
|
|
4279
|
-
for (const win of waysWins) {
|
|
4280
|
-
this.ctx.services.data.recordSymbolOccurrence({
|
|
4281
|
-
kind: win.kind,
|
|
4282
|
-
symbolId: win.baseSymbol.id,
|
|
4283
|
-
spinType: this.ctx.state.currentSpinType
|
|
4284
|
-
});
|
|
4373
|
+
let totalPayout = 0;
|
|
4374
|
+
for (let i = 0; i < waysWins.length; i++) {
|
|
4375
|
+
totalPayout += waysWins[i].payout;
|
|
4285
4376
|
}
|
|
4286
|
-
this.payout =
|
|
4377
|
+
this.payout = totalPayout;
|
|
4287
4378
|
this.winCombinations = waysWins;
|
|
4288
4379
|
return this;
|
|
4289
4380
|
}
|
|
4290
|
-
getWayLength(symbolList) {
|
|
4291
|
-
return Object.keys(symbolList).length;
|
|
4292
|
-
}
|
|
4293
4381
|
};
|
|
4294
4382
|
|
|
4295
4383
|
// src/reel-set/GeneratedReelSet.ts
|