@slot-engine/core 0.0.1 → 0.0.3
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/README.md +1 -1
- package/dist/index.d.mts +7 -14
- package/dist/index.d.ts +7 -14
- package/dist/index.js +68 -90
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +68 -90
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
- package/.turbo/turbo-build.log +0 -33
- package/.turbo/turbo-typecheck.log +0 -4
- package/CHANGELOG.md +0 -7
- package/dist/lib/zstd.exe +0 -0
- package/index.ts +0 -205
- package/lib/zstd.exe +0 -0
- package/optimizer-rust/Cargo.toml +0 -19
- package/optimizer-rust/src/exes.rs +0 -154
- package/optimizer-rust/src/main.rs +0 -1659
- package/src/Board.ts +0 -527
- package/src/Book.ts +0 -83
- package/src/GameConfig.ts +0 -148
- package/src/GameMode.ts +0 -86
- package/src/GameState.ts +0 -272
- package/src/GameSymbol.ts +0 -61
- package/src/ReelGenerator.ts +0 -589
- package/src/ResultSet.ts +0 -207
- package/src/Simulation.ts +0 -625
- package/src/SlotGame.ts +0 -117
- package/src/Wallet.ts +0 -203
- package/src/WinType.ts +0 -102
- package/src/analysis/index.ts +0 -198
- package/src/analysis/utils.ts +0 -128
- package/src/optimizer/OptimizationConditions.ts +0 -99
- package/src/optimizer/OptimizationParameters.ts +0 -46
- package/src/optimizer/OptimizationScaling.ts +0 -18
- package/src/optimizer/index.ts +0 -142
- package/src/utils/math-config.ts +0 -109
- package/src/utils/setup-file.ts +0 -36
- package/src/utils/zstd.ts +0 -28
- package/src/winTypes/ClusterWinType.ts +0 -3
- package/src/winTypes/LinesWinType.ts +0 -208
- package/src/winTypes/ManywaysWinType.ts +0 -3
- package/tsconfig.json +0 -19
- package/utils.ts +0 -270
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/GameConfig.ts
|
|
2
|
+
import assert from "assert";
|
|
2
3
|
var GameConfig = class _GameConfig {
|
|
3
4
|
config;
|
|
4
5
|
constructor(opts) {
|
|
5
6
|
this.config = {
|
|
6
7
|
id: opts.id,
|
|
7
8
|
name: opts.name,
|
|
8
|
-
providerNumber: opts.providerNumber,
|
|
9
9
|
gameModes: opts.gameModes,
|
|
10
10
|
symbols: /* @__PURE__ */ new Map(),
|
|
11
11
|
padSymbols: opts.padSymbols || 0,
|
|
@@ -23,14 +23,12 @@ var GameConfig = class _GameConfig {
|
|
|
23
23
|
userState: opts.userState,
|
|
24
24
|
outputDir: "__build__"
|
|
25
25
|
};
|
|
26
|
-
for (const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
33
|
-
}
|
|
26
|
+
for (const [key, value] of Object.entries(opts.symbols)) {
|
|
27
|
+
assert(
|
|
28
|
+
value.id === key,
|
|
29
|
+
`Symbol key "${key}" does not match symbol id "${value.id}"`
|
|
30
|
+
);
|
|
31
|
+
this.config.symbols.set(key, value);
|
|
34
32
|
}
|
|
35
33
|
function getAnticipationTrigger(spinType) {
|
|
36
34
|
return Math.min(...Object.keys(opts.scatterToFreespins[spinType]).map(Number)) - 1;
|
|
@@ -1774,7 +1772,7 @@ var ManywaysWinType = class extends WinType {
|
|
|
1774
1772
|
};
|
|
1775
1773
|
|
|
1776
1774
|
// src/optimizer/OptimizationConditions.ts
|
|
1777
|
-
import
|
|
1775
|
+
import assert2 from "assert";
|
|
1778
1776
|
var OptimizationConditions = class {
|
|
1779
1777
|
rtp;
|
|
1780
1778
|
avgWin;
|
|
@@ -1785,14 +1783,14 @@ var OptimizationConditions = class {
|
|
|
1785
1783
|
constructor(opts) {
|
|
1786
1784
|
let { rtp, avgWin, hitRate, searchConditions, priority } = opts;
|
|
1787
1785
|
if (rtp == void 0 || rtp === "x") {
|
|
1788
|
-
|
|
1786
|
+
assert2(avgWin !== void 0 && hitRate !== void 0, "If RTP is not specified, hit-rate (hr) and average win amount (av_win) must be given.");
|
|
1789
1787
|
rtp = Math.round(avgWin / Number(hitRate) * 1e5) / 1e5;
|
|
1790
1788
|
}
|
|
1791
1789
|
let noneCount = 0;
|
|
1792
1790
|
for (const val of [rtp, avgWin, hitRate]) {
|
|
1793
1791
|
if (val === void 0) noneCount++;
|
|
1794
1792
|
}
|
|
1795
|
-
|
|
1793
|
+
assert2(noneCount <= 1, "Invalid combination of optimization conditions.");
|
|
1796
1794
|
this.searchRange = [-1, -1];
|
|
1797
1795
|
this.forceSearch = {};
|
|
1798
1796
|
if (typeof searchConditions === "number") {
|
|
@@ -1872,41 +1870,11 @@ var OptimizationParameters = class _OptimizationParameters {
|
|
|
1872
1870
|
|
|
1873
1871
|
// src/Simulation.ts
|
|
1874
1872
|
import fs3 from "fs";
|
|
1875
|
-
import
|
|
1876
|
-
import
|
|
1873
|
+
import path2 from "path";
|
|
1874
|
+
import assert3 from "assert";
|
|
1875
|
+
import zlib from "zlib";
|
|
1877
1876
|
import { buildSync } from "esbuild";
|
|
1878
1877
|
import { Worker, isMainThread as isMainThread2, parentPort, workerData } from "worker_threads";
|
|
1879
|
-
|
|
1880
|
-
// src/utils/zstd.ts
|
|
1881
|
-
import path2 from "path";
|
|
1882
|
-
import { spawn } from "child_process";
|
|
1883
|
-
async function zstd(...args) {
|
|
1884
|
-
return new Promise((resolve, reject) => {
|
|
1885
|
-
const task = spawn(path2.join(__dirname, "./lib/zstd.exe"), args);
|
|
1886
|
-
task.on("error", (error) => {
|
|
1887
|
-
console.error("Error:", error);
|
|
1888
|
-
reject(error);
|
|
1889
|
-
});
|
|
1890
|
-
task.on("exit", () => {
|
|
1891
|
-
resolve(true);
|
|
1892
|
-
});
|
|
1893
|
-
task.on("close", () => {
|
|
1894
|
-
resolve(true);
|
|
1895
|
-
});
|
|
1896
|
-
task.stdout.on("data", (data) => {
|
|
1897
|
-
console.log(data.toString());
|
|
1898
|
-
});
|
|
1899
|
-
task.stderr.on("data", (data) => {
|
|
1900
|
-
console.log(data.toString());
|
|
1901
|
-
});
|
|
1902
|
-
task.stdout.on("error", (data) => {
|
|
1903
|
-
console.log(data.toString());
|
|
1904
|
-
reject(data.toString());
|
|
1905
|
-
});
|
|
1906
|
-
});
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
// src/Simulation.ts
|
|
1910
1878
|
var completedSimulations = 0;
|
|
1911
1879
|
var TEMP_FILENAME = "__temp_compiled_src_IGNORE.js";
|
|
1912
1880
|
var Simulation = class _Simulation {
|
|
@@ -1927,7 +1895,7 @@ var Simulation = class _Simulation {
|
|
|
1927
1895
|
this.library = /* @__PURE__ */ new Map();
|
|
1928
1896
|
this.records = [];
|
|
1929
1897
|
const gameModeKeys = Object.keys(this.gameConfig.config.gameModes);
|
|
1930
|
-
|
|
1898
|
+
assert3(
|
|
1931
1899
|
Object.values(this.gameConfig.config.gameModes).map((m) => gameModeKeys.includes(m.name)).every((v) => v === true),
|
|
1932
1900
|
"Game mode name must match its key in the gameModes object."
|
|
1933
1901
|
);
|
|
@@ -1964,14 +1932,14 @@ Simulating game mode: ${mode}`);
|
|
|
1964
1932
|
const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode);
|
|
1965
1933
|
await this.spawnWorkersForGameMode({ mode, simNumsToCriteria });
|
|
1966
1934
|
createDirIfNotExists(
|
|
1967
|
-
|
|
1935
|
+
path2.join(
|
|
1968
1936
|
process.cwd(),
|
|
1969
1937
|
this.gameConfig.config.outputDir,
|
|
1970
1938
|
"optimization_files"
|
|
1971
1939
|
)
|
|
1972
1940
|
);
|
|
1973
1941
|
createDirIfNotExists(
|
|
1974
|
-
|
|
1942
|
+
path2.join(process.cwd(), this.gameConfig.config.outputDir, "publish_files")
|
|
1975
1943
|
);
|
|
1976
1944
|
_Simulation.writeLookupTableCSV({
|
|
1977
1945
|
gameMode: mode,
|
|
@@ -2067,7 +2035,7 @@ Simulating game mode: ${mode}`);
|
|
|
2067
2035
|
}
|
|
2068
2036
|
}
|
|
2069
2037
|
return new Promise((resolve, reject) => {
|
|
2070
|
-
const scriptPath =
|
|
2038
|
+
const scriptPath = path2.join(process.cwd(), basePath, TEMP_FILENAME);
|
|
2071
2039
|
const worker = new Worker(scriptPath, {
|
|
2072
2040
|
workerData: {
|
|
2073
2041
|
mode,
|
|
@@ -2108,14 +2076,17 @@ Simulating game mode: ${mode}`);
|
|
|
2108
2076
|
* `weight` defaults to 1.
|
|
2109
2077
|
*/
|
|
2110
2078
|
static writeLookupTableCSV(opts) {
|
|
2111
|
-
const { gameMode,
|
|
2079
|
+
const { gameMode, library, gameConfig } = opts;
|
|
2112
2080
|
const rows = [];
|
|
2113
2081
|
for (const [bookId, book] of library.entries()) {
|
|
2114
2082
|
rows.push(`${book.id},1,${Math.round(book.getPayout())}`);
|
|
2115
2083
|
}
|
|
2116
2084
|
rows.sort((a, b) => Number(a.split(",")[0]) - Number(b.split(",")[0]));
|
|
2117
|
-
|
|
2118
|
-
|
|
2085
|
+
let outputFileName = `lookUpTable_${gameMode}.csv`;
|
|
2086
|
+
let outputFilePath = path2.join(gameConfig.outputDir, outputFileName);
|
|
2087
|
+
writeFile(outputFilePath, rows.join("\n"));
|
|
2088
|
+
outputFileName = `lookUpTable_${gameMode}_0.csv`;
|
|
2089
|
+
outputFilePath = path2.join(gameConfig.outputDir, outputFileName);
|
|
2119
2090
|
writeFile(outputFilePath, rows.join("\n"));
|
|
2120
2091
|
return outputFilePath;
|
|
2121
2092
|
}
|
|
@@ -2123,7 +2094,7 @@ Simulating game mode: ${mode}`);
|
|
|
2123
2094
|
* Creates a CSV file in the format "simulationId,criteria,payoutBase,payoutFreespins".
|
|
2124
2095
|
*/
|
|
2125
2096
|
static writeLookupTableSegmentedCSV(opts) {
|
|
2126
|
-
const { gameMode,
|
|
2097
|
+
const { gameMode, library, gameConfig } = opts;
|
|
2127
2098
|
const rows = [];
|
|
2128
2099
|
for (const [bookId, book] of library.entries()) {
|
|
2129
2100
|
rows.push(
|
|
@@ -2131,22 +2102,22 @@ Simulating game mode: ${mode}`);
|
|
|
2131
2102
|
);
|
|
2132
2103
|
}
|
|
2133
2104
|
rows.sort((a, b) => Number(a.split(",")[0]) - Number(b.split(",")[0]));
|
|
2134
|
-
const outputFileName =
|
|
2135
|
-
const outputFilePath =
|
|
2105
|
+
const outputFileName = `lookUpTableSegmented_${gameMode}.csv`;
|
|
2106
|
+
const outputFilePath = path2.join(gameConfig.outputDir, outputFileName);
|
|
2136
2107
|
writeFile(outputFilePath, rows.join("\n"));
|
|
2137
2108
|
return outputFilePath;
|
|
2138
2109
|
}
|
|
2139
2110
|
static writeRecords(opts) {
|
|
2140
2111
|
const { gameMode, fileNameWithoutExtension, records, gameConfig, debug } = opts;
|
|
2141
2112
|
const outputFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.json` : `force_record_${gameMode}.json`;
|
|
2142
|
-
const outputFilePath =
|
|
2113
|
+
const outputFilePath = path2.join(gameConfig.outputDir, outputFileName);
|
|
2143
2114
|
writeFile(outputFilePath, JSON.stringify(records, null, 2));
|
|
2144
2115
|
if (debug) _Simulation.logSymbolOccurrences(records);
|
|
2145
2116
|
return outputFilePath;
|
|
2146
2117
|
}
|
|
2147
2118
|
static writeIndexJson(opts) {
|
|
2148
2119
|
const { gameConfig } = opts;
|
|
2149
|
-
const outputFilePath =
|
|
2120
|
+
const outputFilePath = path2.join(
|
|
2150
2121
|
process.cwd(),
|
|
2151
2122
|
gameConfig.outputDir,
|
|
2152
2123
|
"publish_files",
|
|
@@ -2163,7 +2134,7 @@ Simulating game mode: ${mode}`);
|
|
|
2163
2134
|
static async writeBooksJson(opts) {
|
|
2164
2135
|
const { gameMode, fileNameWithoutExtension, library, gameConfig } = opts;
|
|
2165
2136
|
const outputFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.jsonl` : `books_${gameMode}.jsonl`;
|
|
2166
|
-
const outputFilePath =
|
|
2137
|
+
const outputFilePath = path2.join(gameConfig.outputDir, outputFileName);
|
|
2167
2138
|
const books = Array.from(library.values()).map((b) => b.serialize()).map((b) => ({
|
|
2168
2139
|
id: b.id,
|
|
2169
2140
|
payoutMultiplier: b.payout,
|
|
@@ -2172,14 +2143,15 @@ Simulating game mode: ${mode}`);
|
|
|
2172
2143
|
const contents = JSONL.stringify(books);
|
|
2173
2144
|
writeFile(outputFilePath, contents);
|
|
2174
2145
|
const compressedFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.jsonl.zst` : `books_${gameMode}.jsonl.zst`;
|
|
2175
|
-
const compressedFilePath =
|
|
2146
|
+
const compressedFilePath = path2.join(
|
|
2176
2147
|
process.cwd(),
|
|
2177
2148
|
gameConfig.outputDir,
|
|
2178
2149
|
"publish_files",
|
|
2179
2150
|
compressedFileName
|
|
2180
2151
|
);
|
|
2181
2152
|
fs3.rmSync(compressedFilePath, { force: true });
|
|
2182
|
-
|
|
2153
|
+
const compressed = zlib.zstdCompressSync(Buffer.from(contents));
|
|
2154
|
+
fs3.writeFileSync(compressedFilePath, compressed);
|
|
2183
2155
|
}
|
|
2184
2156
|
static logSymbolOccurrences(records) {
|
|
2185
2157
|
const validRecords = records.filter(
|
|
@@ -2210,14 +2182,14 @@ Simulating game mode: ${mode}`);
|
|
|
2210
2182
|
* Compiles user configured game to JS for use in different Node processes
|
|
2211
2183
|
*/
|
|
2212
2184
|
preprocessFiles() {
|
|
2213
|
-
const builtFilePath =
|
|
2185
|
+
const builtFilePath = path2.join(this.gameConfig.config.outputDir, TEMP_FILENAME);
|
|
2214
2186
|
fs3.rmSync(builtFilePath, { force: true });
|
|
2215
2187
|
buildSync({
|
|
2216
2188
|
entryPoints: [process.cwd()],
|
|
2217
2189
|
bundle: true,
|
|
2218
2190
|
platform: "node",
|
|
2219
|
-
outfile:
|
|
2220
|
-
external: ["esbuild"
|
|
2191
|
+
outfile: path2.join(this.gameConfig.config.outputDir, TEMP_FILENAME),
|
|
2192
|
+
external: ["esbuild"]
|
|
2221
2193
|
});
|
|
2222
2194
|
}
|
|
2223
2195
|
getSimRangesForChunks(total, chunks) {
|
|
@@ -2325,8 +2297,8 @@ var SimulationContext = class extends Board {
|
|
|
2325
2297
|
|
|
2326
2298
|
// src/analysis/index.ts
|
|
2327
2299
|
import fs4 from "fs";
|
|
2328
|
-
import
|
|
2329
|
-
import
|
|
2300
|
+
import path3 from "path";
|
|
2301
|
+
import assert4 from "assert";
|
|
2330
2302
|
|
|
2331
2303
|
// src/analysis/utils.ts
|
|
2332
2304
|
function parseLookupTable(content) {
|
|
@@ -2370,8 +2342,11 @@ function getPayoutWeights(lut, opts = {}) {
|
|
|
2370
2342
|
}
|
|
2371
2343
|
function getNonZeroHitrate(payoutWeights) {
|
|
2372
2344
|
const totalWeight = getTotalWeight(payoutWeights);
|
|
2373
|
-
|
|
2374
|
-
|
|
2345
|
+
if (Math.min(...Object.keys(payoutWeights).map(Number)) == 0) {
|
|
2346
|
+
return totalWeight / (totalWeight - (payoutWeights[0] ?? 0) / totalWeight);
|
|
2347
|
+
} else {
|
|
2348
|
+
return 1;
|
|
2349
|
+
}
|
|
2375
2350
|
}
|
|
2376
2351
|
function getNullHitrate(payoutWeights) {
|
|
2377
2352
|
return payoutWeights[0] ?? 0;
|
|
@@ -2432,6 +2407,7 @@ function getLessBetHitrate(payoutWeights, cost) {
|
|
|
2432
2407
|
}
|
|
2433
2408
|
|
|
2434
2409
|
// src/analysis/index.ts
|
|
2410
|
+
import { isMainThread as isMainThread3 } from "worker_threads";
|
|
2435
2411
|
var Analysis = class {
|
|
2436
2412
|
gameConfig;
|
|
2437
2413
|
optimizerConfig;
|
|
@@ -2442,6 +2418,7 @@ var Analysis = class {
|
|
|
2442
2418
|
this.filePaths = {};
|
|
2443
2419
|
}
|
|
2444
2420
|
async runAnalysis(gameModes) {
|
|
2421
|
+
if (!isMainThread3) return;
|
|
2445
2422
|
this.filePaths = this.getPathsForModes(gameModes);
|
|
2446
2423
|
this.getNumberStats(gameModes);
|
|
2447
2424
|
this.getWinRanges(gameModes);
|
|
@@ -2451,28 +2428,28 @@ var Analysis = class {
|
|
|
2451
2428
|
const rootPath = process.cwd();
|
|
2452
2429
|
const paths = {};
|
|
2453
2430
|
for (const modeStr of gameModes) {
|
|
2454
|
-
const lut =
|
|
2431
|
+
const lut = path3.join(
|
|
2455
2432
|
rootPath,
|
|
2456
2433
|
this.gameConfig.outputDir,
|
|
2457
2434
|
`lookUpTable_${modeStr}.csv`
|
|
2458
2435
|
);
|
|
2459
|
-
const lutSegmented =
|
|
2436
|
+
const lutSegmented = path3.join(
|
|
2460
2437
|
rootPath,
|
|
2461
2438
|
this.gameConfig.outputDir,
|
|
2462
2439
|
`lookUpTableSegmented_${modeStr}.csv`
|
|
2463
2440
|
);
|
|
2464
|
-
const lutOptimized =
|
|
2441
|
+
const lutOptimized = path3.join(
|
|
2465
2442
|
rootPath,
|
|
2466
2443
|
this.gameConfig.outputDir,
|
|
2467
2444
|
"publish_files",
|
|
2468
2445
|
`lookUpTable_${modeStr}_0.csv`
|
|
2469
2446
|
);
|
|
2470
|
-
const booksJsonl =
|
|
2447
|
+
const booksJsonl = path3.join(
|
|
2471
2448
|
rootPath,
|
|
2472
2449
|
this.gameConfig.outputDir,
|
|
2473
2450
|
`books_${modeStr}.jsonl`
|
|
2474
2451
|
);
|
|
2475
|
-
const booksJsonlCompressed =
|
|
2452
|
+
const booksJsonlCompressed = path3.join(
|
|
2476
2453
|
rootPath,
|
|
2477
2454
|
this.gameConfig.outputDir,
|
|
2478
2455
|
"publish_files",
|
|
@@ -2486,7 +2463,7 @@ var Analysis = class {
|
|
|
2486
2463
|
booksJsonlCompressed
|
|
2487
2464
|
};
|
|
2488
2465
|
for (const p of Object.values(paths[modeStr])) {
|
|
2489
|
-
|
|
2466
|
+
assert4(
|
|
2490
2467
|
fs4.existsSync(p),
|
|
2491
2468
|
`File "${p}" does not exist. Run optimization to auto-create it.`
|
|
2492
2469
|
);
|
|
@@ -2520,7 +2497,7 @@ var Analysis = class {
|
|
|
2520
2497
|
});
|
|
2521
2498
|
}
|
|
2522
2499
|
writeJsonFile(
|
|
2523
|
-
|
|
2500
|
+
path3.join(process.cwd(), this.gameConfig.outputDir, "stats_summary.json"),
|
|
2524
2501
|
stats
|
|
2525
2502
|
);
|
|
2526
2503
|
}
|
|
@@ -2558,13 +2535,13 @@ var Analysis = class {
|
|
|
2558
2535
|
}
|
|
2559
2536
|
getGameModeConfig(mode) {
|
|
2560
2537
|
const config = this.gameConfig.gameModes[mode];
|
|
2561
|
-
|
|
2538
|
+
assert4(config, `Game mode "${mode}" not found in game config`);
|
|
2562
2539
|
return config;
|
|
2563
2540
|
}
|
|
2564
2541
|
};
|
|
2565
2542
|
|
|
2566
2543
|
// src/utils/math-config.ts
|
|
2567
|
-
import
|
|
2544
|
+
import path4 from "path";
|
|
2568
2545
|
function makeMathConfig(optimizer, opts = {}) {
|
|
2569
2546
|
const game = optimizer.getGameConfig();
|
|
2570
2547
|
const gameModesCfg = optimizer.getOptimizerGameModes();
|
|
@@ -2608,14 +2585,14 @@ function makeMathConfig(optimizer, opts = {}) {
|
|
|
2608
2585
|
}))
|
|
2609
2586
|
};
|
|
2610
2587
|
if (writeToFile) {
|
|
2611
|
-
const outPath =
|
|
2588
|
+
const outPath = path4.join(process.cwd(), game.outputDir, "math_config.json");
|
|
2612
2589
|
writeJsonFile(outPath, config);
|
|
2613
2590
|
}
|
|
2614
2591
|
return config;
|
|
2615
2592
|
}
|
|
2616
2593
|
|
|
2617
2594
|
// src/utils/setup-file.ts
|
|
2618
|
-
import
|
|
2595
|
+
import path5 from "path";
|
|
2619
2596
|
function makeSetupFile(optimizer, gameMode) {
|
|
2620
2597
|
const gameConfig = optimizer.getGameConfig();
|
|
2621
2598
|
const optimizerGameModes = optimizer.getOptimizerGameModes();
|
|
@@ -2651,19 +2628,19 @@ function makeSetupFile(optimizer, gameMode) {
|
|
|
2651
2628
|
`;
|
|
2652
2629
|
content += `simulation_trials;${params.simulationTrials}
|
|
2653
2630
|
`;
|
|
2654
|
-
content += `user_game_build_path;${
|
|
2631
|
+
content += `user_game_build_path;${path5.join(process.cwd(), gameConfig.outputDir)}
|
|
2655
2632
|
`;
|
|
2656
2633
|
content += `pmb_rtp;${params.pmbRtp}
|
|
2657
2634
|
`;
|
|
2658
|
-
const outPath =
|
|
2635
|
+
const outPath = path5.join(__dirname, "./optimizer-rust/src", "setup.txt");
|
|
2659
2636
|
writeFile(outPath, content);
|
|
2660
2637
|
}
|
|
2661
2638
|
|
|
2662
2639
|
// src/optimizer/index.ts
|
|
2663
|
-
import { spawn
|
|
2664
|
-
import
|
|
2665
|
-
import
|
|
2666
|
-
import { isMainThread as
|
|
2640
|
+
import { spawn } from "child_process";
|
|
2641
|
+
import path6 from "path";
|
|
2642
|
+
import assert5 from "assert";
|
|
2643
|
+
import { isMainThread as isMainThread4 } from "worker_threads";
|
|
2667
2644
|
var Optimizer = class {
|
|
2668
2645
|
gameConfig;
|
|
2669
2646
|
gameModes;
|
|
@@ -2676,12 +2653,13 @@ var Optimizer = class {
|
|
|
2676
2653
|
* Runs the optimization process, and runs analysis after.
|
|
2677
2654
|
*/
|
|
2678
2655
|
async runOptimization({ gameModes }) {
|
|
2679
|
-
if (!
|
|
2656
|
+
if (!isMainThread4) return;
|
|
2680
2657
|
const mathConfig = makeMathConfig(this, { writeToFile: true });
|
|
2681
2658
|
for (const mode of gameModes) {
|
|
2682
2659
|
const setupFile = makeSetupFile(this, mode);
|
|
2683
2660
|
await this.runSingleOptimization();
|
|
2684
2661
|
}
|
|
2662
|
+
console.log("Optimization complete. Files written to build directory.");
|
|
2685
2663
|
}
|
|
2686
2664
|
async runSingleOptimization() {
|
|
2687
2665
|
return await rustProgram();
|
|
@@ -2705,7 +2683,7 @@ var Optimizer = class {
|
|
|
2705
2683
|
}
|
|
2706
2684
|
}
|
|
2707
2685
|
const criteria = configMode.resultSets.map((r) => r.criteria);
|
|
2708
|
-
|
|
2686
|
+
assert5(
|
|
2709
2687
|
conditions.every((c) => criteria.includes(c)),
|
|
2710
2688
|
`Not all ResultSet criteria in game mode "${k}" are defined as optimization conditions.`
|
|
2711
2689
|
);
|
|
@@ -2717,7 +2695,7 @@ var Optimizer = class {
|
|
|
2717
2695
|
}
|
|
2718
2696
|
gameModeRtp = Math.round(gameModeRtp * 1e3) / 1e3;
|
|
2719
2697
|
paramRtp = Math.round(paramRtp * 1e3) / 1e3;
|
|
2720
|
-
|
|
2698
|
+
assert5(
|
|
2721
2699
|
gameModeRtp === paramRtp,
|
|
2722
2700
|
`Sum of all RTP conditions (${paramRtp}) does not match the game mode RTP (${gameModeRtp}) in game mode "${k}".`
|
|
2723
2701
|
);
|
|
@@ -2732,9 +2710,9 @@ var Optimizer = class {
|
|
|
2732
2710
|
};
|
|
2733
2711
|
async function rustProgram(...args) {
|
|
2734
2712
|
return new Promise((resolve, reject) => {
|
|
2735
|
-
const task =
|
|
2713
|
+
const task = spawn("cargo", ["run", "--release", ...args], {
|
|
2736
2714
|
shell: true,
|
|
2737
|
-
cwd:
|
|
2715
|
+
cwd: path6.join(__dirname, "./optimizer-rust"),
|
|
2738
2716
|
stdio: "pipe"
|
|
2739
2717
|
});
|
|
2740
2718
|
task.on("error", (error) => {
|
|
@@ -2825,7 +2803,7 @@ var SlotGame = class {
|
|
|
2825
2803
|
console.log("No tasks to run. Enable either simulation, optimization or analysis.");
|
|
2826
2804
|
}
|
|
2827
2805
|
if (opts.doSimulation) {
|
|
2828
|
-
await this.runSimulation(
|
|
2806
|
+
await this.runSimulation(opts.simulationOpts || {});
|
|
2829
2807
|
}
|
|
2830
2808
|
if (opts.doOptimization) {
|
|
2831
2809
|
await this.runOptimization(opts.optimizationOpts || { gameModes: [] });
|