@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.js
CHANGED
|
@@ -420,7 +420,10 @@ var GameSymbol = class _GameSymbol {
|
|
|
420
420
|
constructor(opts) {
|
|
421
421
|
this.id = opts.id;
|
|
422
422
|
this.pays = opts.pays;
|
|
423
|
-
this.properties = new Map(
|
|
423
|
+
this.properties = /* @__PURE__ */ new Map();
|
|
424
|
+
for (const prop in opts.properties) {
|
|
425
|
+
this.properties.set(prop, opts.properties[prop]);
|
|
426
|
+
}
|
|
424
427
|
if (this.pays && Object.keys(this.pays).length === 0) {
|
|
425
428
|
throw new Error(`GameSymbol "${this.id}" must have pays defined.`);
|
|
426
429
|
}
|
|
@@ -436,8 +439,8 @@ var GameSymbol = class _GameSymbol {
|
|
|
436
439
|
if (symbolOrProperties instanceof _GameSymbol) {
|
|
437
440
|
return this.id === symbolOrProperties.id;
|
|
438
441
|
} else {
|
|
439
|
-
for (const
|
|
440
|
-
if (!this.properties.has(
|
|
442
|
+
for (const prop in symbolOrProperties) {
|
|
443
|
+
if (!this.properties.has(prop) || this.properties.get(prop) !== symbolOrProperties[prop]) {
|
|
441
444
|
return false;
|
|
442
445
|
}
|
|
443
446
|
}
|
|
@@ -517,8 +520,8 @@ var Board = class {
|
|
|
517
520
|
updateSymbol(reelIndex, rowIndex, properties) {
|
|
518
521
|
const symbol = this.getSymbol(reelIndex, rowIndex);
|
|
519
522
|
if (symbol) {
|
|
520
|
-
for (const
|
|
521
|
-
symbol.properties.set(
|
|
523
|
+
for (const prop in properties) {
|
|
524
|
+
symbol.properties.set(prop, properties[prop]);
|
|
522
525
|
}
|
|
523
526
|
}
|
|
524
527
|
}
|
|
@@ -534,8 +537,8 @@ var Board = class {
|
|
|
534
537
|
if (symbolOrProperties instanceof GameSymbol) {
|
|
535
538
|
if (symbol.id !== symbolOrProperties.id) matches = false;
|
|
536
539
|
} else {
|
|
537
|
-
for (const
|
|
538
|
-
if (!symbol.properties.has(
|
|
540
|
+
for (const prop in symbolOrProperties) {
|
|
541
|
+
if (!symbol.properties.has(prop) || symbol.properties.get(prop) !== symbolOrProperties[prop]) {
|
|
539
542
|
matches = false;
|
|
540
543
|
break;
|
|
541
544
|
}
|
|
@@ -557,8 +560,8 @@ var Board = class {
|
|
|
557
560
|
if (symbol.id !== symbolOrProperties.id) continue;
|
|
558
561
|
} else {
|
|
559
562
|
let matches = true;
|
|
560
|
-
for (const
|
|
561
|
-
if (!symbol.properties.has(
|
|
563
|
+
for (const prop in symbolOrProperties) {
|
|
564
|
+
if (!symbol.properties.has(prop) || symbol.properties.get(prop) !== symbolOrProperties[prop]) {
|
|
562
565
|
matches = false;
|
|
563
566
|
break;
|
|
564
567
|
}
|
|
@@ -1105,11 +1108,13 @@ var DataService = class extends AbstractService {
|
|
|
1105
1108
|
* Record data for statistical analysis.
|
|
1106
1109
|
*/
|
|
1107
1110
|
record(data) {
|
|
1111
|
+
const properties = {};
|
|
1112
|
+
for (const key in data) {
|
|
1113
|
+
properties[key] = String(data[key]);
|
|
1114
|
+
}
|
|
1108
1115
|
this.recorder.pendingRecords.push({
|
|
1109
1116
|
bookId: this.ctx().state.currentSimulationId,
|
|
1110
|
-
properties
|
|
1111
|
-
Object.entries(data).map(([k, v]) => [k, String(v)])
|
|
1112
|
-
)
|
|
1117
|
+
properties
|
|
1113
1118
|
});
|
|
1114
1119
|
}
|
|
1115
1120
|
/**
|
|
@@ -1463,19 +1468,6 @@ var Book = class {
|
|
|
1463
1468
|
data: copy(event.data)
|
|
1464
1469
|
});
|
|
1465
1470
|
}
|
|
1466
|
-
/**
|
|
1467
|
-
* Intended for internal use only.
|
|
1468
|
-
*/
|
|
1469
|
-
_serialize() {
|
|
1470
|
-
return {
|
|
1471
|
-
id: this.id,
|
|
1472
|
-
criteria: this.criteria,
|
|
1473
|
-
events: this.events,
|
|
1474
|
-
payout: this.payout,
|
|
1475
|
-
basegameWins: this.basegameWins,
|
|
1476
|
-
freespinsWins: this.freespinsWins
|
|
1477
|
-
};
|
|
1478
|
-
}
|
|
1479
1471
|
};
|
|
1480
1472
|
|
|
1481
1473
|
// src/wallet/index.ts
|
|
@@ -1644,7 +1636,7 @@ var Wallet = class {
|
|
|
1644
1636
|
this.currentWin = process2(this.currentWin);
|
|
1645
1637
|
this.cumulativeWins += this.currentWin;
|
|
1646
1638
|
let spinTypeWins = 0;
|
|
1647
|
-
for (const spinType
|
|
1639
|
+
for (const spinType in this.currentWinPerSpinType) {
|
|
1648
1640
|
const st = spinType;
|
|
1649
1641
|
const spinTypeWin = process2(this.currentWinPerSpinType[st]);
|
|
1650
1642
|
this.cumulativeWinsPerSpinType[st] += spinTypeWin;
|
|
@@ -2449,9 +2441,9 @@ var Simulation = class {
|
|
|
2449
2441
|
}
|
|
2450
2442
|
const chunkIndex = this.bookChunkIndexes.get(index);
|
|
2451
2443
|
const bookChunkPath = this.PATHS.booksChunk(mode, index, chunkIndex);
|
|
2452
|
-
const
|
|
2444
|
+
const bookLines = this.bookBuffers.get(index);
|
|
2453
2445
|
await (0, import_promises.pipeline)(
|
|
2454
|
-
import_stream.Readable.from(
|
|
2446
|
+
import_stream.Readable.from(bookLines),
|
|
2455
2447
|
import_zlib.default.createZstdCompress(),
|
|
2456
2448
|
import_fs3.default.createWriteStream(bookChunkPath)
|
|
2457
2449
|
);
|
|
@@ -2532,50 +2524,51 @@ var Simulation = class {
|
|
|
2532
2524
|
}
|
|
2533
2525
|
}
|
|
2534
2526
|
writeChain = writeChain.then(async () => {
|
|
2535
|
-
const
|
|
2536
|
-
const
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2527
|
+
const bookId = msg.bookId;
|
|
2528
|
+
const bookCriteria = msg.bookCriteria;
|
|
2529
|
+
const bookPayout = msg.bookPayout;
|
|
2530
|
+
const bookBasegameWins = msg.bookBasegameWins;
|
|
2531
|
+
const bookFreespinsWins = msg.bookFreespinsWins;
|
|
2532
|
+
const bookLine = msg.bookLine;
|
|
2533
|
+
const bookLineWithNewline = bookLine + "\n";
|
|
2534
|
+
if (!this.summary[mode]?.criteria[bookCriteria]) {
|
|
2535
|
+
this.summary[mode].criteria[bookCriteria] = {
|
|
2543
2536
|
numSims: 0,
|
|
2544
2537
|
bsWins: 0,
|
|
2545
2538
|
fsWins: 0,
|
|
2546
2539
|
rtp: 0
|
|
2547
2540
|
};
|
|
2548
2541
|
}
|
|
2549
|
-
const bsWins = round(
|
|
2550
|
-
const fsWins = round(
|
|
2551
|
-
this.summary[mode].criteria[
|
|
2542
|
+
const bsWins = round(bookBasegameWins, 4);
|
|
2543
|
+
const fsWins = round(bookFreespinsWins, 4);
|
|
2544
|
+
const criteria = this.summary[mode].criteria[bookCriteria];
|
|
2545
|
+
criteria.numSims += 1;
|
|
2552
2546
|
this.summary[mode].total.bsWins += bsWins;
|
|
2553
2547
|
this.summary[mode].total.fsWins += fsWins;
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
const
|
|
2557
|
-
const lineSize = Buffer.byteLength(bookLine + "\n", "utf8");
|
|
2548
|
+
criteria.bsWins += bsWins;
|
|
2549
|
+
criteria.fsWins += fsWins;
|
|
2550
|
+
const lineSize = Buffer.byteLength(bookLineWithNewline, "utf8");
|
|
2558
2551
|
if (this.bookBuffers.has(index)) {
|
|
2559
|
-
this.bookBuffers.get(index).push(
|
|
2552
|
+
this.bookBuffers.get(index).push(bookLineWithNewline);
|
|
2560
2553
|
this.bookBufferSizes.set(
|
|
2561
2554
|
index,
|
|
2562
2555
|
this.bookBufferSizes.get(index) + lineSize
|
|
2563
2556
|
);
|
|
2564
2557
|
} else {
|
|
2565
|
-
this.bookBuffers.set(index, [
|
|
2558
|
+
this.bookBuffers.set(index, [bookLineWithNewline]);
|
|
2566
2559
|
this.bookBufferSizes.set(index, lineSize);
|
|
2567
2560
|
}
|
|
2568
2561
|
if (!this.tempBookIndexPaths.includes(booksIndexPath)) {
|
|
2569
2562
|
this.tempBookIndexPaths.push(booksIndexPath);
|
|
2570
2563
|
}
|
|
2571
2564
|
booksIndexBatch.push(
|
|
2572
|
-
`${
|
|
2565
|
+
`${bookId},${index},${this.bookChunkIndexes.get(index) || 0}
|
|
2573
2566
|
`
|
|
2574
2567
|
);
|
|
2575
|
-
lookupBatch.push(`${
|
|
2568
|
+
lookupBatch.push(`${bookId},1,${Math.round(bookPayout)}
|
|
2576
2569
|
`);
|
|
2577
2570
|
lookupSegBatch.push(
|
|
2578
|
-
`${
|
|
2571
|
+
`${bookId},${bookCriteria},${bookBasegameWins},${bookFreespinsWins}
|
|
2579
2572
|
`
|
|
2580
2573
|
);
|
|
2581
2574
|
if (booksIndexBatch.length >= WRITE_BATCH_SIZE) {
|
|
@@ -2584,15 +2577,10 @@ var Simulation = class {
|
|
|
2584
2577
|
if (this.bookBufferSizes.get(index) >= 10 * 1024 * 1024) {
|
|
2585
2578
|
await flushBookChunk();
|
|
2586
2579
|
}
|
|
2587
|
-
if (this.recordsWriteStream) {
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
this.recordsWriteStream,
|
|
2592
|
-
recordPrefix + JSON.stringify(record)
|
|
2593
|
-
);
|
|
2594
|
-
this.hasWrittenRecord = true;
|
|
2595
|
-
}
|
|
2580
|
+
if (this.recordsWriteStream && typeof msg.recordsLines === "string" && msg.recordsLines.length) {
|
|
2581
|
+
const recordPrefix = this.hasWrittenRecord ? "\n" : "";
|
|
2582
|
+
await write(this.recordsWriteStream, recordPrefix + msg.recordsLines);
|
|
2583
|
+
this.hasWrittenRecord = true;
|
|
2596
2584
|
}
|
|
2597
2585
|
this.wallet.mergeSerialized(msg.wallet);
|
|
2598
2586
|
worker.postMessage({ type: "credit", amount: 1 });
|
|
@@ -2683,9 +2671,11 @@ var Simulation = class {
|
|
|
2683
2671
|
});
|
|
2684
2672
|
}
|
|
2685
2673
|
}
|
|
2686
|
-
ctx.services.wallet._getWallet()
|
|
2687
|
-
|
|
2688
|
-
|
|
2674
|
+
const wallet = ctx.services.wallet._getWallet();
|
|
2675
|
+
wallet.writePayoutToBook(ctx);
|
|
2676
|
+
wallet.confirmWins(ctx);
|
|
2677
|
+
const book = ctx.services.data._getBook();
|
|
2678
|
+
if (book.payout >= ctx.config.maxWinX) {
|
|
2689
2679
|
ctx.state.triggeredMaxWin = true;
|
|
2690
2680
|
}
|
|
2691
2681
|
ctx.services.data.record({
|
|
@@ -2693,12 +2683,24 @@ var Simulation = class {
|
|
|
2693
2683
|
});
|
|
2694
2684
|
ctx.config.hooks.onSimulationAccepted?.(ctx);
|
|
2695
2685
|
this.confirmRecords(ctx);
|
|
2686
|
+
const bookLine = JSON.stringify({
|
|
2687
|
+
id: book.id,
|
|
2688
|
+
payoutMultiplier: book.payout,
|
|
2689
|
+
events: book.events
|
|
2690
|
+
});
|
|
2691
|
+
const records = ctx.services.data._getRecords();
|
|
2692
|
+
const recordsLines = records.length > 0 ? records.map((r) => JSON.stringify(r)).join("\n") : "";
|
|
2696
2693
|
import_worker_threads2.parentPort?.postMessage({
|
|
2697
2694
|
type: "complete",
|
|
2698
2695
|
simId,
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2696
|
+
bookLine,
|
|
2697
|
+
bookId: book.id,
|
|
2698
|
+
bookCriteria: book.criteria,
|
|
2699
|
+
bookPayout: book.payout,
|
|
2700
|
+
bookBasegameWins: book.basegameWins,
|
|
2701
|
+
bookFreespinsWins: book.freespinsWins,
|
|
2702
|
+
wallet: wallet.serialize(),
|
|
2703
|
+
recordsLines
|
|
2702
2704
|
});
|
|
2703
2705
|
}
|
|
2704
2706
|
initCreditListener() {
|
|
@@ -4005,13 +4007,19 @@ var LinesWinType = class extends WinType {
|
|
|
4005
4007
|
this.validateConfig();
|
|
4006
4008
|
const lineWins = [];
|
|
4007
4009
|
const reels = board;
|
|
4008
|
-
|
|
4010
|
+
const reelsLength = reels.length;
|
|
4011
|
+
const lineNumbers = Object.keys(this.lines);
|
|
4012
|
+
const numLines = lineNumbers.length;
|
|
4013
|
+
for (let lidx = 0; lidx < numLines; lidx++) {
|
|
4014
|
+
const lineNumStr = lineNumbers[lidx];
|
|
4009
4015
|
const lineNum = Number(lineNumStr);
|
|
4016
|
+
const line = this.lines[lineNum];
|
|
4010
4017
|
let baseSymbol;
|
|
4011
4018
|
const potentialWinLine = [];
|
|
4012
4019
|
const potentialWildLine = [];
|
|
4013
4020
|
let isInterrupted = false;
|
|
4014
|
-
for (
|
|
4021
|
+
for (let ridx = 0; ridx < reelsLength; ridx++) {
|
|
4022
|
+
const reel = reels[ridx];
|
|
4015
4023
|
const sidx = line[ridx];
|
|
4016
4024
|
const thisSymbol = reel[sidx];
|
|
4017
4025
|
if (!baseSymbol) {
|
|
@@ -4042,66 +4050,91 @@ var LinesWinType = class extends WinType {
|
|
|
4042
4050
|
break;
|
|
4043
4051
|
}
|
|
4044
4052
|
}
|
|
4045
|
-
const
|
|
4046
|
-
|
|
4047
|
-
)
|
|
4053
|
+
const pays = baseSymbol.pays || {};
|
|
4054
|
+
let minSymLine = Infinity;
|
|
4055
|
+
for (const key in pays) {
|
|
4056
|
+
const num = parseInt(key, 10);
|
|
4057
|
+
if (num < minSymLine) minSymLine = num;
|
|
4058
|
+
}
|
|
4048
4059
|
if (potentialWinLine.length < minSymLine) continue;
|
|
4049
4060
|
const linePayout = this.getLinePayout(potentialWinLine);
|
|
4050
4061
|
const wildLinePayout = this.getLinePayout(potentialWildLine);
|
|
4051
|
-
let finalLine
|
|
4052
|
-
kind: potentialWinLine.length,
|
|
4053
|
-
baseSymbol,
|
|
4054
|
-
symbols: potentialWinLine.map((s) => ({
|
|
4055
|
-
symbol: s.symbol,
|
|
4056
|
-
isWild: this.isWild(s.symbol),
|
|
4057
|
-
reelIndex: s.reel,
|
|
4058
|
-
posIndex: s.row
|
|
4059
|
-
})),
|
|
4060
|
-
lineNumber: lineNum,
|
|
4061
|
-
payout: linePayout
|
|
4062
|
-
};
|
|
4062
|
+
let finalLine;
|
|
4063
4063
|
if (wildLinePayout > linePayout) {
|
|
4064
4064
|
baseSymbol = potentialWildLine[0]?.symbol;
|
|
4065
|
+
const wildSymbols = [];
|
|
4066
|
+
const wildLineLength = potentialWildLine.length;
|
|
4067
|
+
for (let i = 0; i < wildLineLength; i++) {
|
|
4068
|
+
const s = potentialWildLine[i];
|
|
4069
|
+
wildSymbols.push({
|
|
4070
|
+
symbol: s.symbol,
|
|
4071
|
+
isWild: this.isWild(s.symbol),
|
|
4072
|
+
reelIndex: s.reel,
|
|
4073
|
+
posIndex: s.row
|
|
4074
|
+
});
|
|
4075
|
+
}
|
|
4065
4076
|
finalLine = {
|
|
4066
|
-
kind:
|
|
4077
|
+
kind: wildLineLength,
|
|
4067
4078
|
baseSymbol,
|
|
4068
|
-
symbols:
|
|
4079
|
+
symbols: wildSymbols,
|
|
4080
|
+
lineNumber: lineNum,
|
|
4081
|
+
payout: wildLinePayout
|
|
4082
|
+
};
|
|
4083
|
+
} else {
|
|
4084
|
+
const symbols = [];
|
|
4085
|
+
const lineLength = potentialWinLine.length;
|
|
4086
|
+
for (let i = 0; i < lineLength; i++) {
|
|
4087
|
+
const s = potentialWinLine[i];
|
|
4088
|
+
symbols.push({
|
|
4069
4089
|
symbol: s.symbol,
|
|
4070
4090
|
isWild: this.isWild(s.symbol),
|
|
4071
4091
|
reelIndex: s.reel,
|
|
4072
4092
|
posIndex: s.row
|
|
4073
|
-
})
|
|
4093
|
+
});
|
|
4094
|
+
}
|
|
4095
|
+
finalLine = {
|
|
4096
|
+
kind: lineLength,
|
|
4097
|
+
baseSymbol,
|
|
4098
|
+
symbols,
|
|
4074
4099
|
lineNumber: lineNum,
|
|
4075
|
-
payout:
|
|
4100
|
+
payout: linePayout
|
|
4076
4101
|
};
|
|
4077
4102
|
}
|
|
4078
4103
|
lineWins.push(finalLine);
|
|
4079
|
-
}
|
|
4080
|
-
for (const win of lineWins) {
|
|
4081
4104
|
this.ctx.services.data.recordSymbolOccurrence({
|
|
4082
|
-
kind:
|
|
4083
|
-
symbolId:
|
|
4105
|
+
kind: finalLine.kind,
|
|
4106
|
+
symbolId: finalLine.baseSymbol.id,
|
|
4084
4107
|
spinType: this.ctx.state.currentSpinType
|
|
4085
4108
|
});
|
|
4086
4109
|
}
|
|
4087
|
-
|
|
4110
|
+
let totalPayout = 0;
|
|
4111
|
+
for (let i = 0; i < lineWins.length; i++) {
|
|
4112
|
+
totalPayout += lineWins[i].payout;
|
|
4113
|
+
}
|
|
4114
|
+
this.payout = totalPayout;
|
|
4088
4115
|
this.winCombinations = lineWins;
|
|
4089
4116
|
return this;
|
|
4090
4117
|
}
|
|
4091
4118
|
getLinePayout(line) {
|
|
4092
|
-
|
|
4093
|
-
|
|
4119
|
+
const lineLength = line.length;
|
|
4120
|
+
if (lineLength === 0) return 0;
|
|
4121
|
+
let baseSymbol;
|
|
4122
|
+
for (let i = 0; i < lineLength; i++) {
|
|
4123
|
+
const s = line[i];
|
|
4124
|
+
if (!this.isWild(s.symbol)) {
|
|
4125
|
+
baseSymbol = s.symbol;
|
|
4126
|
+
break;
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4094
4129
|
if (!baseSymbol) baseSymbol = line[0].symbol;
|
|
4095
|
-
|
|
4096
|
-
const payout = this.getSymbolPayout(baseSymbol, kind);
|
|
4097
|
-
return payout;
|
|
4130
|
+
return this.getSymbolPayout(baseSymbol, lineLength);
|
|
4098
4131
|
}
|
|
4099
4132
|
};
|
|
4100
4133
|
|
|
4101
4134
|
// src/win-types/ClusterWinType.ts
|
|
4102
4135
|
var ClusterWinType = class extends WinType {
|
|
4103
|
-
_checked =
|
|
4104
|
-
_checkedWilds =
|
|
4136
|
+
_checked = /* @__PURE__ */ new Set();
|
|
4137
|
+
_checkedWilds = /* @__PURE__ */ new Set();
|
|
4105
4138
|
_currentBoard = [];
|
|
4106
4139
|
constructor(opts) {
|
|
4107
4140
|
super(opts);
|
|
@@ -4114,121 +4147,169 @@ var ClusterWinType = class extends WinType {
|
|
|
4114
4147
|
*/
|
|
4115
4148
|
evaluateWins(board) {
|
|
4116
4149
|
this.validateConfig();
|
|
4117
|
-
this._checked
|
|
4150
|
+
this._checked.clear();
|
|
4118
4151
|
this._currentBoard = board;
|
|
4119
4152
|
const clusterWins = [];
|
|
4120
4153
|
const potentialClusters = [];
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4154
|
+
const boardLength = board.length;
|
|
4155
|
+
for (let ridx = 0; ridx < boardLength; ridx++) {
|
|
4156
|
+
const reel = board[ridx];
|
|
4157
|
+
const reelLength = reel.length;
|
|
4158
|
+
for (let sidx = 0; sidx < reelLength; sidx++) {
|
|
4159
|
+
const symbol = reel[sidx];
|
|
4160
|
+
this._checkedWilds.clear();
|
|
4124
4161
|
if (this.isWild(symbol)) continue;
|
|
4125
|
-
|
|
4162
|
+
const posKey = ridx * 1e4 + sidx;
|
|
4163
|
+
if (this._checked.has(posKey)) {
|
|
4126
4164
|
continue;
|
|
4127
4165
|
}
|
|
4128
4166
|
const thisSymbol = { reel: ridx, row: sidx, symbol };
|
|
4129
|
-
this._checked.
|
|
4167
|
+
this._checked.add(posKey);
|
|
4130
4168
|
const neighbors = this.getNeighbors(ridx, sidx);
|
|
4131
4169
|
const matchingSymbols = this.evaluateCluster(symbol, neighbors);
|
|
4132
|
-
|
|
4133
|
-
|
|
4170
|
+
const matchingSize = matchingSymbols.size;
|
|
4171
|
+
if (matchingSize >= 1) {
|
|
4172
|
+
const cluster = [thisSymbol];
|
|
4173
|
+
for (const sym of matchingSymbols.values()) {
|
|
4174
|
+
cluster.push(sym);
|
|
4175
|
+
}
|
|
4176
|
+
potentialClusters.push(cluster);
|
|
4134
4177
|
}
|
|
4135
4178
|
}
|
|
4136
4179
|
}
|
|
4137
|
-
for (
|
|
4138
|
-
|
|
4139
|
-
|
|
4180
|
+
for (let ridx = 0; ridx < boardLength; ridx++) {
|
|
4181
|
+
const reel = board[ridx];
|
|
4182
|
+
const reelLength = reel.length;
|
|
4183
|
+
for (let sidx = 0; sidx < reelLength; sidx++) {
|
|
4184
|
+
const symbol = reel[sidx];
|
|
4185
|
+
this._checkedWilds.clear();
|
|
4140
4186
|
if (!this.isWild(symbol)) continue;
|
|
4141
|
-
|
|
4187
|
+
const posKey = ridx * 1e4 + sidx;
|
|
4188
|
+
if (this._checked.has(posKey)) {
|
|
4142
4189
|
continue;
|
|
4143
4190
|
}
|
|
4144
4191
|
const thisSymbol = { reel: ridx, row: sidx, symbol };
|
|
4145
|
-
this._checked.
|
|
4192
|
+
this._checked.add(posKey);
|
|
4146
4193
|
const neighbors = this.getNeighbors(ridx, sidx);
|
|
4147
4194
|
const matchingSymbols = this.evaluateCluster(symbol, neighbors);
|
|
4148
|
-
|
|
4149
|
-
|
|
4195
|
+
const matchingSize = matchingSymbols.size;
|
|
4196
|
+
if (matchingSize >= 1) {
|
|
4197
|
+
const cluster = [thisSymbol];
|
|
4198
|
+
for (const sym of matchingSymbols.values()) {
|
|
4199
|
+
cluster.push(sym);
|
|
4200
|
+
}
|
|
4201
|
+
potentialClusters.push(cluster);
|
|
4150
4202
|
}
|
|
4151
4203
|
}
|
|
4152
4204
|
}
|
|
4153
|
-
|
|
4205
|
+
const numClusters = potentialClusters.length;
|
|
4206
|
+
for (let i = 0; i < numClusters; i++) {
|
|
4207
|
+
const cluster = potentialClusters[i];
|
|
4154
4208
|
const kind = cluster.length;
|
|
4155
|
-
let baseSymbol
|
|
4209
|
+
let baseSymbol;
|
|
4210
|
+
for (let j = 0; j < kind; j++) {
|
|
4211
|
+
const sym = cluster[j].symbol;
|
|
4212
|
+
if (!this.isWild(sym)) {
|
|
4213
|
+
baseSymbol = sym;
|
|
4214
|
+
break;
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4156
4217
|
if (!baseSymbol) baseSymbol = cluster[0].symbol;
|
|
4157
4218
|
const payout = this.getSymbolPayout(baseSymbol, kind);
|
|
4158
4219
|
if (payout === 0) continue;
|
|
4159
|
-
|
|
4160
|
-
|
|
4220
|
+
const pays = baseSymbol.pays;
|
|
4221
|
+
if (!pays) continue;
|
|
4222
|
+
let hasPays = false;
|
|
4223
|
+
for (const _ in pays) {
|
|
4224
|
+
hasPays = true;
|
|
4225
|
+
break;
|
|
4161
4226
|
}
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
symbols
|
|
4227
|
+
if (!hasPays) continue;
|
|
4228
|
+
const symbols = [];
|
|
4229
|
+
for (let j = 0; j < kind; j++) {
|
|
4230
|
+
const s = cluster[j];
|
|
4231
|
+
symbols.push({
|
|
4167
4232
|
symbol: s.symbol,
|
|
4168
4233
|
isWild: this.isWild(s.symbol),
|
|
4169
4234
|
reelIndex: s.reel,
|
|
4170
4235
|
posIndex: s.row
|
|
4171
|
-
})
|
|
4236
|
+
});
|
|
4237
|
+
}
|
|
4238
|
+
clusterWins.push({
|
|
4239
|
+
payout,
|
|
4240
|
+
kind,
|
|
4241
|
+
baseSymbol,
|
|
4242
|
+
symbols
|
|
4172
4243
|
});
|
|
4173
|
-
}
|
|
4174
|
-
for (const win of clusterWins) {
|
|
4175
4244
|
this.ctx.services.data.recordSymbolOccurrence({
|
|
4176
|
-
kind
|
|
4177
|
-
symbolId:
|
|
4245
|
+
kind,
|
|
4246
|
+
symbolId: baseSymbol.id,
|
|
4178
4247
|
spinType: this.ctx.state.currentSpinType
|
|
4179
4248
|
});
|
|
4180
4249
|
}
|
|
4181
|
-
|
|
4250
|
+
let totalPayout = 0;
|
|
4251
|
+
for (let i = 0; i < clusterWins.length; i++) {
|
|
4252
|
+
totalPayout += clusterWins[i].payout;
|
|
4253
|
+
}
|
|
4254
|
+
this.payout = totalPayout;
|
|
4182
4255
|
this.winCombinations = clusterWins;
|
|
4183
4256
|
return this;
|
|
4184
4257
|
}
|
|
4185
4258
|
getNeighbors(ridx, sidx) {
|
|
4186
4259
|
const board = this._currentBoard;
|
|
4187
4260
|
const neighbors = [];
|
|
4188
|
-
|
|
4189
|
-
[ridx - 1
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
];
|
|
4194
|
-
potentialNeighbors.forEach(([nridx, nsidx]) => {
|
|
4195
|
-
if (board[nridx] && board[nridx][nsidx]) {
|
|
4196
|
-
neighbors.push({ reel: nridx, row: nsidx, symbol: board[nridx][nsidx] });
|
|
4261
|
+
if (ridx > 0) {
|
|
4262
|
+
const leftReel = board[ridx - 1];
|
|
4263
|
+
const leftSymbol = leftReel[sidx];
|
|
4264
|
+
if (leftSymbol !== void 0) {
|
|
4265
|
+
neighbors.push({ reel: ridx - 1, row: sidx, symbol: leftSymbol });
|
|
4197
4266
|
}
|
|
4198
|
-
}
|
|
4267
|
+
}
|
|
4268
|
+
const rightReel = board[ridx + 1];
|
|
4269
|
+
if (rightReel !== void 0) {
|
|
4270
|
+
const rightSymbol = rightReel[sidx];
|
|
4271
|
+
if (rightSymbol !== void 0) {
|
|
4272
|
+
neighbors.push({ reel: ridx + 1, row: sidx, symbol: rightSymbol });
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
const currentReel = board[ridx];
|
|
4276
|
+
const topSymbol = currentReel[sidx - 1];
|
|
4277
|
+
if (topSymbol !== void 0) {
|
|
4278
|
+
neighbors.push({ reel: ridx, row: sidx - 1, symbol: topSymbol });
|
|
4279
|
+
}
|
|
4280
|
+
const bottomSymbol = currentReel[sidx + 1];
|
|
4281
|
+
if (bottomSymbol !== void 0) {
|
|
4282
|
+
neighbors.push({ reel: ridx, row: sidx + 1, symbol: bottomSymbol });
|
|
4283
|
+
}
|
|
4199
4284
|
return neighbors;
|
|
4200
4285
|
}
|
|
4201
4286
|
evaluateCluster(rootSymbol, neighbors) {
|
|
4202
4287
|
const matchingSymbols = /* @__PURE__ */ new Map();
|
|
4203
|
-
neighbors.
|
|
4288
|
+
const numNeighbors = neighbors.length;
|
|
4289
|
+
for (let i = 0; i < numNeighbors; i++) {
|
|
4290
|
+
const neighbor = neighbors[i];
|
|
4204
4291
|
const { reel, row, symbol } = neighbor;
|
|
4205
|
-
|
|
4206
|
-
if (this.
|
|
4292
|
+
const posKey = reel * 1e4 + row;
|
|
4293
|
+
if (this._checked.has(posKey)) continue;
|
|
4294
|
+
if (this._checkedWilds.has(posKey)) continue;
|
|
4207
4295
|
if (this.isWild(symbol) || symbol.compare(rootSymbol)) {
|
|
4208
|
-
const key =
|
|
4296
|
+
const key = String(posKey);
|
|
4209
4297
|
matchingSymbols.set(key, { reel, row, symbol });
|
|
4210
4298
|
if (symbol.compare(rootSymbol)) {
|
|
4211
|
-
this._checked.
|
|
4299
|
+
this._checked.add(posKey);
|
|
4212
4300
|
}
|
|
4213
4301
|
if (this.isWild(symbol)) {
|
|
4214
|
-
this._checkedWilds.
|
|
4302
|
+
this._checkedWilds.add(posKey);
|
|
4215
4303
|
}
|
|
4216
|
-
const
|
|
4217
|
-
const nestedMatches = this.evaluateCluster(rootSymbol,
|
|
4218
|
-
nestedMatches.
|
|
4219
|
-
const nkey = `${nsym.reel}-${nsym.row}`;
|
|
4304
|
+
const nestedNeighbors = this.getNeighbors(reel, row);
|
|
4305
|
+
const nestedMatches = this.evaluateCluster(rootSymbol, nestedNeighbors);
|
|
4306
|
+
for (const [nkey, nsym] of nestedMatches.entries()) {
|
|
4220
4307
|
matchingSymbols.set(nkey, nsym);
|
|
4221
|
-
}
|
|
4308
|
+
}
|
|
4222
4309
|
}
|
|
4223
|
-
}
|
|
4310
|
+
}
|
|
4224
4311
|
return matchingSymbols;
|
|
4225
4312
|
}
|
|
4226
|
-
isChecked(ridx, sidx) {
|
|
4227
|
-
return !!this._checked.find((c) => c.reel === ridx && c.row === sidx);
|
|
4228
|
-
}
|
|
4229
|
-
isCheckedWild(ridx, sidx) {
|
|
4230
|
-
return !!this._checkedWilds.find((c) => c.reel === ridx && c.row === sidx);
|
|
4231
|
-
}
|
|
4232
4313
|
};
|
|
4233
4314
|
|
|
4234
4315
|
// src/win-types/ManywaysWinType.ts
|
|
@@ -4247,7 +4328,7 @@ var ManywaysWinType = class extends WinType {
|
|
|
4247
4328
|
const { jumpGaps = false } = opts;
|
|
4248
4329
|
const waysWins = [];
|
|
4249
4330
|
const reels = board;
|
|
4250
|
-
const
|
|
4331
|
+
const numReels = reels.length;
|
|
4251
4332
|
const candidateSymbols = /* @__PURE__ */ new Map();
|
|
4252
4333
|
if (jumpGaps) {
|
|
4253
4334
|
for (const reel of reels) {
|
|
@@ -4258,7 +4339,7 @@ var ManywaysWinType = class extends WinType {
|
|
|
4258
4339
|
} else {
|
|
4259
4340
|
let searchReelIdx = 0;
|
|
4260
4341
|
let searchActive = true;
|
|
4261
|
-
while (searchActive && searchReelIdx <
|
|
4342
|
+
while (searchActive && searchReelIdx < numReels) {
|
|
4262
4343
|
const reel = reels[searchReelIdx];
|
|
4263
4344
|
let hasWild = false;
|
|
4264
4345
|
for (const symbol of reel) {
|
|
@@ -4274,71 +4355,78 @@ var ManywaysWinType = class extends WinType {
|
|
|
4274
4355
|
}
|
|
4275
4356
|
}
|
|
4276
4357
|
for (const baseSymbol of candidateSymbols.values()) {
|
|
4277
|
-
|
|
4278
|
-
let
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4358
|
+
const symbolList = [];
|
|
4359
|
+
let wayLength = 0;
|
|
4360
|
+
let firstNonWildSymbol;
|
|
4361
|
+
let totalWays = 1;
|
|
4362
|
+
for (let ridx = 0; ridx < numReels; ridx++) {
|
|
4363
|
+
const reel = reels[ridx];
|
|
4364
|
+
let reelMatches;
|
|
4365
|
+
for (let sidx = 0; sidx < reel.length; sidx++) {
|
|
4366
|
+
const symbol = reel[sidx];
|
|
4282
4367
|
const isMatch = baseSymbol.compare(symbol) || this.isWild(symbol);
|
|
4283
4368
|
if (isMatch) {
|
|
4284
|
-
if (!
|
|
4285
|
-
|
|
4369
|
+
if (!reelMatches) {
|
|
4370
|
+
reelMatches = [];
|
|
4371
|
+
}
|
|
4372
|
+
reelMatches.push({ reel: ridx, row: sidx, symbol });
|
|
4373
|
+
if (!firstNonWildSymbol && !this.isWild(symbol)) {
|
|
4374
|
+
firstNonWildSymbol = symbol;
|
|
4286
4375
|
}
|
|
4287
|
-
symbolList[ridx].push({ reel: ridx, row: sidx, symbol });
|
|
4288
4376
|
}
|
|
4289
4377
|
}
|
|
4290
|
-
if (
|
|
4291
|
-
|
|
4378
|
+
if (reelMatches) {
|
|
4379
|
+
symbolList[wayLength++] = reelMatches;
|
|
4380
|
+
totalWays *= reelMatches.length;
|
|
4381
|
+
} else if (!jumpGaps) {
|
|
4292
4382
|
break;
|
|
4293
4383
|
}
|
|
4294
4384
|
}
|
|
4295
|
-
const
|
|
4296
|
-
|
|
4297
|
-
)
|
|
4298
|
-
|
|
4385
|
+
const pays = baseSymbol.pays || {};
|
|
4386
|
+
let minSymLine = Infinity;
|
|
4387
|
+
for (const key in pays) {
|
|
4388
|
+
const num = parseInt(key, 10);
|
|
4389
|
+
if (num < minSymLine) minSymLine = num;
|
|
4390
|
+
}
|
|
4299
4391
|
if (wayLength >= minSymLine) {
|
|
4300
|
-
|
|
4392
|
+
const winBaseSymbol = firstNonWildSymbol || symbolList[0][0].symbol;
|
|
4393
|
+
const singleWayPayout = this.getSymbolPayout(winBaseSymbol, wayLength);
|
|
4394
|
+
const totalPayout2 = singleWayPayout * totalWays;
|
|
4395
|
+
const symbols = [];
|
|
4396
|
+
for (let i = 0; i < wayLength; i++) {
|
|
4397
|
+
const reelSyms = symbolList[i];
|
|
4398
|
+
for (let j = 0; j < reelSyms.length; j++) {
|
|
4399
|
+
const s = reelSyms[j];
|
|
4400
|
+
symbols.push({
|
|
4401
|
+
symbol: s.symbol,
|
|
4402
|
+
isWild: this.isWild(s.symbol),
|
|
4403
|
+
reelIndex: s.reel,
|
|
4404
|
+
posIndex: s.row
|
|
4405
|
+
});
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
waysWins.push({
|
|
4409
|
+
kind: wayLength,
|
|
4410
|
+
baseSymbol: winBaseSymbol,
|
|
4411
|
+
symbols,
|
|
4412
|
+
ways: totalWays,
|
|
4413
|
+
payout: totalPayout2
|
|
4414
|
+
});
|
|
4415
|
+
this.ctx.services.data.recordSymbolOccurrence({
|
|
4416
|
+
kind: wayLength,
|
|
4417
|
+
symbolId: winBaseSymbol.id,
|
|
4418
|
+
spinType: this.ctx.state.currentSpinType
|
|
4419
|
+
});
|
|
4301
4420
|
}
|
|
4302
4421
|
}
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
if (!baseSymbol) baseSymbol = symbolList[Object.keys(symbolList)[0]][0].symbol;
|
|
4307
|
-
const singleWayPayout = this.getSymbolPayout(baseSymbol, wayLength);
|
|
4308
|
-
const totalWays = Object.values(symbolList).reduce(
|
|
4309
|
-
(ways, syms) => ways * syms.length,
|
|
4310
|
-
1
|
|
4311
|
-
);
|
|
4312
|
-
const totalPayout = singleWayPayout * totalWays;
|
|
4313
|
-
waysWins.push({
|
|
4314
|
-
kind: wayLength,
|
|
4315
|
-
baseSymbol,
|
|
4316
|
-
symbols: Object.values(symbolList).flatMap(
|
|
4317
|
-
(reel) => reel.map((s) => ({
|
|
4318
|
-
symbol: s.symbol,
|
|
4319
|
-
isWild: this.isWild(s.symbol),
|
|
4320
|
-
reelIndex: s.reel,
|
|
4321
|
-
posIndex: s.row
|
|
4322
|
-
}))
|
|
4323
|
-
),
|
|
4324
|
-
ways: totalWays,
|
|
4325
|
-
payout: totalPayout
|
|
4326
|
-
});
|
|
4327
|
-
}
|
|
4328
|
-
for (const win of waysWins) {
|
|
4329
|
-
this.ctx.services.data.recordSymbolOccurrence({
|
|
4330
|
-
kind: win.kind,
|
|
4331
|
-
symbolId: win.baseSymbol.id,
|
|
4332
|
-
spinType: this.ctx.state.currentSpinType
|
|
4333
|
-
});
|
|
4422
|
+
let totalPayout = 0;
|
|
4423
|
+
for (let i = 0; i < waysWins.length; i++) {
|
|
4424
|
+
totalPayout += waysWins[i].payout;
|
|
4334
4425
|
}
|
|
4335
|
-
this.payout =
|
|
4426
|
+
this.payout = totalPayout;
|
|
4336
4427
|
this.winCombinations = waysWins;
|
|
4337
4428
|
return this;
|
|
4338
4429
|
}
|
|
4339
|
-
getWayLength(symbolList) {
|
|
4340
|
-
return Object.keys(symbolList).length;
|
|
4341
|
-
}
|
|
4342
4430
|
};
|
|
4343
4431
|
|
|
4344
4432
|
// src/reel-set/GeneratedReelSet.ts
|