@slot-engine/core 0.0.2 → 0.0.4
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 +58 -66
- package/dist/index.d.ts +58 -66
- package/dist/index.js +73 -101
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -101
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/lib/zstd.exe +0 -0
package/dist/index.js
CHANGED
|
@@ -53,6 +53,7 @@ __export(index_exports, {
|
|
|
53
53
|
module.exports = __toCommonJS(index_exports);
|
|
54
54
|
|
|
55
55
|
// src/GameConfig.ts
|
|
56
|
+
var import_assert = __toESM(require("assert"));
|
|
56
57
|
var GameConfig = class _GameConfig {
|
|
57
58
|
config;
|
|
58
59
|
constructor(opts) {
|
|
@@ -76,14 +77,12 @@ var GameConfig = class _GameConfig {
|
|
|
76
77
|
userState: opts.userState,
|
|
77
78
|
outputDir: "__build__"
|
|
78
79
|
};
|
|
79
|
-
for (const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
86
|
-
}
|
|
80
|
+
for (const [key, value] of Object.entries(opts.symbols)) {
|
|
81
|
+
(0, import_assert.default)(
|
|
82
|
+
value.id === key,
|
|
83
|
+
`Symbol key "${key}" does not match symbol id "${value.id}"`
|
|
84
|
+
);
|
|
85
|
+
this.config.symbols.set(key, value);
|
|
87
86
|
}
|
|
88
87
|
function getAnticipationTrigger(spinType) {
|
|
89
88
|
return Math.min(...Object.keys(opts.scatterToFreespins[spinType]).map(Number)) - 1;
|
|
@@ -117,7 +116,7 @@ var GameConfig = class _GameConfig {
|
|
|
117
116
|
`Reel set with id "${id}" not found in game mode "${gameMode}". Available reel sets: ${this.config.gameModes[gameMode].reelSets.map((rs) => rs.id).join(", ")}`
|
|
118
117
|
);
|
|
119
118
|
}
|
|
120
|
-
return reelSet;
|
|
119
|
+
return reelSet.reels;
|
|
121
120
|
}
|
|
122
121
|
/**
|
|
123
122
|
* Retrieves the number of free spins awarded for a given spin type and scatter count.
|
|
@@ -134,7 +133,7 @@ var GameConfig = class _GameConfig {
|
|
|
134
133
|
/**
|
|
135
134
|
* Retrieves a result set by its criteria within a specific game mode.
|
|
136
135
|
*/
|
|
137
|
-
|
|
136
|
+
getResultSetByCriteria(mode, criteria) {
|
|
138
137
|
const gameMode = this.config.gameModes[mode];
|
|
139
138
|
if (!gameMode) {
|
|
140
139
|
throw new Error(`Game mode "${mode}" not found in game config.`);
|
|
@@ -1104,13 +1103,6 @@ var GameState = class extends GameConfig {
|
|
|
1104
1103
|
this.clearPendingRecords();
|
|
1105
1104
|
this.state.userData = this.config.userState || {};
|
|
1106
1105
|
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Checks if a max win is reached by comparing `wallet.currentWin` to `config.maxWin`.
|
|
1109
|
-
*
|
|
1110
|
-
* Should be called after `wallet.confirmSpinWin()`.
|
|
1111
|
-
*/
|
|
1112
|
-
isMaxWinTriggered() {
|
|
1113
|
-
}
|
|
1114
1106
|
/**
|
|
1115
1107
|
* Empties the list of pending records in the recorder.
|
|
1116
1108
|
*/
|
|
@@ -1556,7 +1548,7 @@ var Board = class extends GameState {
|
|
|
1556
1548
|
return stopPositionsForReels;
|
|
1557
1549
|
}
|
|
1558
1550
|
/**
|
|
1559
|
-
* Selects a random
|
|
1551
|
+
* Selects a random reel set based on the configured weights of the current result set.\
|
|
1560
1552
|
* Returns the reels as arrays of GameSymbols.
|
|
1561
1553
|
*/
|
|
1562
1554
|
getRandomReelset() {
|
|
@@ -1571,7 +1563,7 @@ var Board = class extends GameState {
|
|
|
1571
1563
|
reelSetId = weightedRandom(weights[this.state.currentSpinType], this.state.rng);
|
|
1572
1564
|
}
|
|
1573
1565
|
const reelSet = this.getReelsetById(this.state.currentGameMode, reelSetId);
|
|
1574
|
-
return reelSet
|
|
1566
|
+
return reelSet;
|
|
1575
1567
|
}
|
|
1576
1568
|
/**
|
|
1577
1569
|
* Draws a board using specified reel stops.
|
|
@@ -1827,7 +1819,7 @@ var ManywaysWinType = class extends WinType {
|
|
|
1827
1819
|
};
|
|
1828
1820
|
|
|
1829
1821
|
// src/optimizer/OptimizationConditions.ts
|
|
1830
|
-
var
|
|
1822
|
+
var import_assert2 = __toESM(require("assert"));
|
|
1831
1823
|
var OptimizationConditions = class {
|
|
1832
1824
|
rtp;
|
|
1833
1825
|
avgWin;
|
|
@@ -1838,14 +1830,14 @@ var OptimizationConditions = class {
|
|
|
1838
1830
|
constructor(opts) {
|
|
1839
1831
|
let { rtp, avgWin, hitRate, searchConditions, priority } = opts;
|
|
1840
1832
|
if (rtp == void 0 || rtp === "x") {
|
|
1841
|
-
(0,
|
|
1833
|
+
(0, import_assert2.default)(avgWin !== void 0 && hitRate !== void 0, "If RTP is not specified, hit-rate (hr) and average win amount (av_win) must be given.");
|
|
1842
1834
|
rtp = Math.round(avgWin / Number(hitRate) * 1e5) / 1e5;
|
|
1843
1835
|
}
|
|
1844
1836
|
let noneCount = 0;
|
|
1845
1837
|
for (const val of [rtp, avgWin, hitRate]) {
|
|
1846
1838
|
if (val === void 0) noneCount++;
|
|
1847
1839
|
}
|
|
1848
|
-
(0,
|
|
1840
|
+
(0, import_assert2.default)(noneCount <= 1, "Invalid combination of optimization conditions.");
|
|
1849
1841
|
this.searchRange = [-1, -1];
|
|
1850
1842
|
this.forceSearch = {};
|
|
1851
1843
|
if (typeof searchConditions === "number") {
|
|
@@ -1925,41 +1917,11 @@ var OptimizationParameters = class _OptimizationParameters {
|
|
|
1925
1917
|
|
|
1926
1918
|
// src/Simulation.ts
|
|
1927
1919
|
var import_fs3 = __toESM(require("fs"));
|
|
1928
|
-
var
|
|
1929
|
-
var
|
|
1920
|
+
var import_path2 = __toESM(require("path"));
|
|
1921
|
+
var import_assert3 = __toESM(require("assert"));
|
|
1922
|
+
var import_zlib = __toESM(require("zlib"));
|
|
1930
1923
|
var import_esbuild = require("esbuild");
|
|
1931
1924
|
var import_worker_threads2 = require("worker_threads");
|
|
1932
|
-
|
|
1933
|
-
// src/utils/zstd.ts
|
|
1934
|
-
var import_path2 = __toESM(require("path"));
|
|
1935
|
-
var import_child_process = require("child_process");
|
|
1936
|
-
async function zstd(...args) {
|
|
1937
|
-
return new Promise((resolve, reject) => {
|
|
1938
|
-
const task = (0, import_child_process.spawn)(import_path2.default.join(__dirname, "./lib/zstd.exe"), args);
|
|
1939
|
-
task.on("error", (error) => {
|
|
1940
|
-
console.error("Error:", error);
|
|
1941
|
-
reject(error);
|
|
1942
|
-
});
|
|
1943
|
-
task.on("exit", () => {
|
|
1944
|
-
resolve(true);
|
|
1945
|
-
});
|
|
1946
|
-
task.on("close", () => {
|
|
1947
|
-
resolve(true);
|
|
1948
|
-
});
|
|
1949
|
-
task.stdout.on("data", (data) => {
|
|
1950
|
-
console.log(data.toString());
|
|
1951
|
-
});
|
|
1952
|
-
task.stderr.on("data", (data) => {
|
|
1953
|
-
console.log(data.toString());
|
|
1954
|
-
});
|
|
1955
|
-
task.stdout.on("error", (data) => {
|
|
1956
|
-
console.log(data.toString());
|
|
1957
|
-
reject(data.toString());
|
|
1958
|
-
});
|
|
1959
|
-
});
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
// src/Simulation.ts
|
|
1963
1925
|
var completedSimulations = 0;
|
|
1964
1926
|
var TEMP_FILENAME = "__temp_compiled_src_IGNORE.js";
|
|
1965
1927
|
var Simulation = class _Simulation {
|
|
@@ -1980,7 +1942,7 @@ var Simulation = class _Simulation {
|
|
|
1980
1942
|
this.library = /* @__PURE__ */ new Map();
|
|
1981
1943
|
this.records = [];
|
|
1982
1944
|
const gameModeKeys = Object.keys(this.gameConfig.config.gameModes);
|
|
1983
|
-
(0,
|
|
1945
|
+
(0, import_assert3.default)(
|
|
1984
1946
|
Object.values(this.gameConfig.config.gameModes).map((m) => gameModeKeys.includes(m.name)).every((v) => v === true),
|
|
1985
1947
|
"Game mode name must match its key in the gameModes object."
|
|
1986
1948
|
);
|
|
@@ -2017,14 +1979,14 @@ Simulating game mode: ${mode}`);
|
|
|
2017
1979
|
const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode);
|
|
2018
1980
|
await this.spawnWorkersForGameMode({ mode, simNumsToCriteria });
|
|
2019
1981
|
createDirIfNotExists(
|
|
2020
|
-
|
|
1982
|
+
import_path2.default.join(
|
|
2021
1983
|
process.cwd(),
|
|
2022
1984
|
this.gameConfig.config.outputDir,
|
|
2023
1985
|
"optimization_files"
|
|
2024
1986
|
)
|
|
2025
1987
|
);
|
|
2026
1988
|
createDirIfNotExists(
|
|
2027
|
-
|
|
1989
|
+
import_path2.default.join(process.cwd(), this.gameConfig.config.outputDir, "publish_files")
|
|
2028
1990
|
);
|
|
2029
1991
|
_Simulation.writeLookupTableCSV({
|
|
2030
1992
|
gameMode: mode,
|
|
@@ -2120,7 +2082,7 @@ Simulating game mode: ${mode}`);
|
|
|
2120
2082
|
}
|
|
2121
2083
|
}
|
|
2122
2084
|
return new Promise((resolve, reject) => {
|
|
2123
|
-
const scriptPath =
|
|
2085
|
+
const scriptPath = import_path2.default.join(process.cwd(), basePath, TEMP_FILENAME);
|
|
2124
2086
|
const worker = new import_worker_threads2.Worker(scriptPath, {
|
|
2125
2087
|
workerData: {
|
|
2126
2088
|
mode,
|
|
@@ -2168,10 +2130,10 @@ Simulating game mode: ${mode}`);
|
|
|
2168
2130
|
}
|
|
2169
2131
|
rows.sort((a, b) => Number(a.split(",")[0]) - Number(b.split(",")[0]));
|
|
2170
2132
|
let outputFileName = `lookUpTable_${gameMode}.csv`;
|
|
2171
|
-
let outputFilePath =
|
|
2133
|
+
let outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
|
|
2172
2134
|
writeFile(outputFilePath, rows.join("\n"));
|
|
2173
2135
|
outputFileName = `lookUpTable_${gameMode}_0.csv`;
|
|
2174
|
-
outputFilePath =
|
|
2136
|
+
outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
|
|
2175
2137
|
writeFile(outputFilePath, rows.join("\n"));
|
|
2176
2138
|
return outputFilePath;
|
|
2177
2139
|
}
|
|
@@ -2188,21 +2150,21 @@ Simulating game mode: ${mode}`);
|
|
|
2188
2150
|
}
|
|
2189
2151
|
rows.sort((a, b) => Number(a.split(",")[0]) - Number(b.split(",")[0]));
|
|
2190
2152
|
const outputFileName = `lookUpTableSegmented_${gameMode}.csv`;
|
|
2191
|
-
const outputFilePath =
|
|
2153
|
+
const outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
|
|
2192
2154
|
writeFile(outputFilePath, rows.join("\n"));
|
|
2193
2155
|
return outputFilePath;
|
|
2194
2156
|
}
|
|
2195
2157
|
static writeRecords(opts) {
|
|
2196
2158
|
const { gameMode, fileNameWithoutExtension, records, gameConfig, debug } = opts;
|
|
2197
2159
|
const outputFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.json` : `force_record_${gameMode}.json`;
|
|
2198
|
-
const outputFilePath =
|
|
2160
|
+
const outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
|
|
2199
2161
|
writeFile(outputFilePath, JSON.stringify(records, null, 2));
|
|
2200
2162
|
if (debug) _Simulation.logSymbolOccurrences(records);
|
|
2201
2163
|
return outputFilePath;
|
|
2202
2164
|
}
|
|
2203
2165
|
static writeIndexJson(opts) {
|
|
2204
2166
|
const { gameConfig } = opts;
|
|
2205
|
-
const outputFilePath =
|
|
2167
|
+
const outputFilePath = import_path2.default.join(
|
|
2206
2168
|
process.cwd(),
|
|
2207
2169
|
gameConfig.outputDir,
|
|
2208
2170
|
"publish_files",
|
|
@@ -2219,7 +2181,7 @@ Simulating game mode: ${mode}`);
|
|
|
2219
2181
|
static async writeBooksJson(opts) {
|
|
2220
2182
|
const { gameMode, fileNameWithoutExtension, library, gameConfig } = opts;
|
|
2221
2183
|
const outputFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.jsonl` : `books_${gameMode}.jsonl`;
|
|
2222
|
-
const outputFilePath =
|
|
2184
|
+
const outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
|
|
2223
2185
|
const books = Array.from(library.values()).map((b) => b.serialize()).map((b) => ({
|
|
2224
2186
|
id: b.id,
|
|
2225
2187
|
payoutMultiplier: b.payout,
|
|
@@ -2228,14 +2190,15 @@ Simulating game mode: ${mode}`);
|
|
|
2228
2190
|
const contents = JSONL.stringify(books);
|
|
2229
2191
|
writeFile(outputFilePath, contents);
|
|
2230
2192
|
const compressedFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.jsonl.zst` : `books_${gameMode}.jsonl.zst`;
|
|
2231
|
-
const compressedFilePath =
|
|
2193
|
+
const compressedFilePath = import_path2.default.join(
|
|
2232
2194
|
process.cwd(),
|
|
2233
2195
|
gameConfig.outputDir,
|
|
2234
2196
|
"publish_files",
|
|
2235
2197
|
compressedFileName
|
|
2236
2198
|
);
|
|
2237
2199
|
import_fs3.default.rmSync(compressedFilePath, { force: true });
|
|
2238
|
-
|
|
2200
|
+
const compressed = import_zlib.default.zstdCompressSync(Buffer.from(contents));
|
|
2201
|
+
import_fs3.default.writeFileSync(compressedFilePath, compressed);
|
|
2239
2202
|
}
|
|
2240
2203
|
static logSymbolOccurrences(records) {
|
|
2241
2204
|
const validRecords = records.filter(
|
|
@@ -2266,14 +2229,14 @@ Simulating game mode: ${mode}`);
|
|
|
2266
2229
|
* Compiles user configured game to JS for use in different Node processes
|
|
2267
2230
|
*/
|
|
2268
2231
|
preprocessFiles() {
|
|
2269
|
-
const builtFilePath =
|
|
2232
|
+
const builtFilePath = import_path2.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME);
|
|
2270
2233
|
import_fs3.default.rmSync(builtFilePath, { force: true });
|
|
2271
2234
|
(0, import_esbuild.buildSync)({
|
|
2272
2235
|
entryPoints: [process.cwd()],
|
|
2273
2236
|
bundle: true,
|
|
2274
2237
|
platform: "node",
|
|
2275
|
-
outfile:
|
|
2276
|
-
external: ["esbuild"
|
|
2238
|
+
outfile: import_path2.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME),
|
|
2239
|
+
external: ["esbuild"]
|
|
2277
2240
|
});
|
|
2278
2241
|
}
|
|
2279
2242
|
getSimRangesForChunks(total, chunks) {
|
|
@@ -2330,22 +2293,25 @@ var SimulationContext = class extends Board {
|
|
|
2330
2293
|
this.state.currentGameMode = mode;
|
|
2331
2294
|
this.state.currentSimulationId = simId;
|
|
2332
2295
|
this.state.isCriteriaMet = false;
|
|
2296
|
+
const resultSet = this.getResultSetByCriteria(this.state.currentGameMode, criteria);
|
|
2333
2297
|
while (!this.state.isCriteriaMet) {
|
|
2334
2298
|
this.actualSims++;
|
|
2335
2299
|
this.resetSimulation();
|
|
2336
|
-
const resultSet = this.getGameModeCriteria(this.state.currentGameMode, criteria);
|
|
2337
2300
|
this.state.currentResultSet = resultSet;
|
|
2338
2301
|
this.state.book.criteria = resultSet.criteria;
|
|
2339
2302
|
this.handleGameFlow();
|
|
2340
2303
|
if (resultSet.meetsCriteria(this)) {
|
|
2341
2304
|
this.state.isCriteriaMet = true;
|
|
2342
|
-
this.config.hooks.onSimulationAccepted?.(this);
|
|
2343
|
-
this.record({
|
|
2344
|
-
criteria: resultSet.criteria
|
|
2345
|
-
});
|
|
2346
2305
|
}
|
|
2347
2306
|
}
|
|
2348
2307
|
this.wallet.confirmWins(this);
|
|
2308
|
+
if (this.state.book.getPayout() >= this.config.maxWinX) {
|
|
2309
|
+
this.state.triggeredMaxWin = true;
|
|
2310
|
+
}
|
|
2311
|
+
this.record({
|
|
2312
|
+
criteria: resultSet.criteria
|
|
2313
|
+
});
|
|
2314
|
+
this.config.hooks.onSimulationAccepted?.(this);
|
|
2349
2315
|
this.confirmRecords();
|
|
2350
2316
|
import_worker_threads2.parentPort?.postMessage({
|
|
2351
2317
|
type: "complete",
|
|
@@ -2381,8 +2347,8 @@ var SimulationContext = class extends Board {
|
|
|
2381
2347
|
|
|
2382
2348
|
// src/analysis/index.ts
|
|
2383
2349
|
var import_fs4 = __toESM(require("fs"));
|
|
2384
|
-
var
|
|
2385
|
-
var
|
|
2350
|
+
var import_path3 = __toESM(require("path"));
|
|
2351
|
+
var import_assert4 = __toESM(require("assert"));
|
|
2386
2352
|
|
|
2387
2353
|
// src/analysis/utils.ts
|
|
2388
2354
|
function parseLookupTable(content) {
|
|
@@ -2426,8 +2392,11 @@ function getPayoutWeights(lut, opts = {}) {
|
|
|
2426
2392
|
}
|
|
2427
2393
|
function getNonZeroHitrate(payoutWeights) {
|
|
2428
2394
|
const totalWeight = getTotalWeight(payoutWeights);
|
|
2429
|
-
|
|
2430
|
-
|
|
2395
|
+
if (Math.min(...Object.keys(payoutWeights).map(Number)) == 0) {
|
|
2396
|
+
return totalWeight / (totalWeight - (payoutWeights[0] ?? 0) / totalWeight);
|
|
2397
|
+
} else {
|
|
2398
|
+
return 1;
|
|
2399
|
+
}
|
|
2431
2400
|
}
|
|
2432
2401
|
function getNullHitrate(payoutWeights) {
|
|
2433
2402
|
return payoutWeights[0] ?? 0;
|
|
@@ -2488,6 +2457,7 @@ function getLessBetHitrate(payoutWeights, cost) {
|
|
|
2488
2457
|
}
|
|
2489
2458
|
|
|
2490
2459
|
// src/analysis/index.ts
|
|
2460
|
+
var import_worker_threads3 = require("worker_threads");
|
|
2491
2461
|
var Analysis = class {
|
|
2492
2462
|
gameConfig;
|
|
2493
2463
|
optimizerConfig;
|
|
@@ -2498,6 +2468,7 @@ var Analysis = class {
|
|
|
2498
2468
|
this.filePaths = {};
|
|
2499
2469
|
}
|
|
2500
2470
|
async runAnalysis(gameModes) {
|
|
2471
|
+
if (!import_worker_threads3.isMainThread) return;
|
|
2501
2472
|
this.filePaths = this.getPathsForModes(gameModes);
|
|
2502
2473
|
this.getNumberStats(gameModes);
|
|
2503
2474
|
this.getWinRanges(gameModes);
|
|
@@ -2507,28 +2478,28 @@ var Analysis = class {
|
|
|
2507
2478
|
const rootPath = process.cwd();
|
|
2508
2479
|
const paths = {};
|
|
2509
2480
|
for (const modeStr of gameModes) {
|
|
2510
|
-
const lut =
|
|
2481
|
+
const lut = import_path3.default.join(
|
|
2511
2482
|
rootPath,
|
|
2512
2483
|
this.gameConfig.outputDir,
|
|
2513
2484
|
`lookUpTable_${modeStr}.csv`
|
|
2514
2485
|
);
|
|
2515
|
-
const lutSegmented =
|
|
2486
|
+
const lutSegmented = import_path3.default.join(
|
|
2516
2487
|
rootPath,
|
|
2517
2488
|
this.gameConfig.outputDir,
|
|
2518
2489
|
`lookUpTableSegmented_${modeStr}.csv`
|
|
2519
2490
|
);
|
|
2520
|
-
const lutOptimized =
|
|
2491
|
+
const lutOptimized = import_path3.default.join(
|
|
2521
2492
|
rootPath,
|
|
2522
2493
|
this.gameConfig.outputDir,
|
|
2523
2494
|
"publish_files",
|
|
2524
2495
|
`lookUpTable_${modeStr}_0.csv`
|
|
2525
2496
|
);
|
|
2526
|
-
const booksJsonl =
|
|
2497
|
+
const booksJsonl = import_path3.default.join(
|
|
2527
2498
|
rootPath,
|
|
2528
2499
|
this.gameConfig.outputDir,
|
|
2529
2500
|
`books_${modeStr}.jsonl`
|
|
2530
2501
|
);
|
|
2531
|
-
const booksJsonlCompressed =
|
|
2502
|
+
const booksJsonlCompressed = import_path3.default.join(
|
|
2532
2503
|
rootPath,
|
|
2533
2504
|
this.gameConfig.outputDir,
|
|
2534
2505
|
"publish_files",
|
|
@@ -2542,7 +2513,7 @@ var Analysis = class {
|
|
|
2542
2513
|
booksJsonlCompressed
|
|
2543
2514
|
};
|
|
2544
2515
|
for (const p of Object.values(paths[modeStr])) {
|
|
2545
|
-
(0,
|
|
2516
|
+
(0, import_assert4.default)(
|
|
2546
2517
|
import_fs4.default.existsSync(p),
|
|
2547
2518
|
`File "${p}" does not exist. Run optimization to auto-create it.`
|
|
2548
2519
|
);
|
|
@@ -2576,7 +2547,7 @@ var Analysis = class {
|
|
|
2576
2547
|
});
|
|
2577
2548
|
}
|
|
2578
2549
|
writeJsonFile(
|
|
2579
|
-
|
|
2550
|
+
import_path3.default.join(process.cwd(), this.gameConfig.outputDir, "stats_summary.json"),
|
|
2580
2551
|
stats
|
|
2581
2552
|
);
|
|
2582
2553
|
}
|
|
@@ -2614,13 +2585,13 @@ var Analysis = class {
|
|
|
2614
2585
|
}
|
|
2615
2586
|
getGameModeConfig(mode) {
|
|
2616
2587
|
const config = this.gameConfig.gameModes[mode];
|
|
2617
|
-
(0,
|
|
2588
|
+
(0, import_assert4.default)(config, `Game mode "${mode}" not found in game config`);
|
|
2618
2589
|
return config;
|
|
2619
2590
|
}
|
|
2620
2591
|
};
|
|
2621
2592
|
|
|
2622
2593
|
// src/utils/math-config.ts
|
|
2623
|
-
var
|
|
2594
|
+
var import_path4 = __toESM(require("path"));
|
|
2624
2595
|
function makeMathConfig(optimizer, opts = {}) {
|
|
2625
2596
|
const game = optimizer.getGameConfig();
|
|
2626
2597
|
const gameModesCfg = optimizer.getOptimizerGameModes();
|
|
@@ -2664,14 +2635,14 @@ function makeMathConfig(optimizer, opts = {}) {
|
|
|
2664
2635
|
}))
|
|
2665
2636
|
};
|
|
2666
2637
|
if (writeToFile) {
|
|
2667
|
-
const outPath =
|
|
2638
|
+
const outPath = import_path4.default.join(process.cwd(), game.outputDir, "math_config.json");
|
|
2668
2639
|
writeJsonFile(outPath, config);
|
|
2669
2640
|
}
|
|
2670
2641
|
return config;
|
|
2671
2642
|
}
|
|
2672
2643
|
|
|
2673
2644
|
// src/utils/setup-file.ts
|
|
2674
|
-
var
|
|
2645
|
+
var import_path5 = __toESM(require("path"));
|
|
2675
2646
|
function makeSetupFile(optimizer, gameMode) {
|
|
2676
2647
|
const gameConfig = optimizer.getGameConfig();
|
|
2677
2648
|
const optimizerGameModes = optimizer.getOptimizerGameModes();
|
|
@@ -2707,19 +2678,19 @@ function makeSetupFile(optimizer, gameMode) {
|
|
|
2707
2678
|
`;
|
|
2708
2679
|
content += `simulation_trials;${params.simulationTrials}
|
|
2709
2680
|
`;
|
|
2710
|
-
content += `user_game_build_path;${
|
|
2681
|
+
content += `user_game_build_path;${import_path5.default.join(process.cwd(), gameConfig.outputDir)}
|
|
2711
2682
|
`;
|
|
2712
2683
|
content += `pmb_rtp;${params.pmbRtp}
|
|
2713
2684
|
`;
|
|
2714
|
-
const outPath =
|
|
2685
|
+
const outPath = import_path5.default.join(__dirname, "./optimizer-rust/src", "setup.txt");
|
|
2715
2686
|
writeFile(outPath, content);
|
|
2716
2687
|
}
|
|
2717
2688
|
|
|
2718
2689
|
// src/optimizer/index.ts
|
|
2719
|
-
var
|
|
2720
|
-
var
|
|
2721
|
-
var
|
|
2722
|
-
var
|
|
2690
|
+
var import_child_process = require("child_process");
|
|
2691
|
+
var import_path6 = __toESM(require("path"));
|
|
2692
|
+
var import_assert5 = __toESM(require("assert"));
|
|
2693
|
+
var import_worker_threads4 = require("worker_threads");
|
|
2723
2694
|
var Optimizer = class {
|
|
2724
2695
|
gameConfig;
|
|
2725
2696
|
gameModes;
|
|
@@ -2732,12 +2703,13 @@ var Optimizer = class {
|
|
|
2732
2703
|
* Runs the optimization process, and runs analysis after.
|
|
2733
2704
|
*/
|
|
2734
2705
|
async runOptimization({ gameModes }) {
|
|
2735
|
-
if (!
|
|
2706
|
+
if (!import_worker_threads4.isMainThread) return;
|
|
2736
2707
|
const mathConfig = makeMathConfig(this, { writeToFile: true });
|
|
2737
2708
|
for (const mode of gameModes) {
|
|
2738
2709
|
const setupFile = makeSetupFile(this, mode);
|
|
2739
2710
|
await this.runSingleOptimization();
|
|
2740
2711
|
}
|
|
2712
|
+
console.log("Optimization complete. Files written to build directory.");
|
|
2741
2713
|
}
|
|
2742
2714
|
async runSingleOptimization() {
|
|
2743
2715
|
return await rustProgram();
|
|
@@ -2761,7 +2733,7 @@ var Optimizer = class {
|
|
|
2761
2733
|
}
|
|
2762
2734
|
}
|
|
2763
2735
|
const criteria = configMode.resultSets.map((r) => r.criteria);
|
|
2764
|
-
(0,
|
|
2736
|
+
(0, import_assert5.default)(
|
|
2765
2737
|
conditions.every((c) => criteria.includes(c)),
|
|
2766
2738
|
`Not all ResultSet criteria in game mode "${k}" are defined as optimization conditions.`
|
|
2767
2739
|
);
|
|
@@ -2773,7 +2745,7 @@ var Optimizer = class {
|
|
|
2773
2745
|
}
|
|
2774
2746
|
gameModeRtp = Math.round(gameModeRtp * 1e3) / 1e3;
|
|
2775
2747
|
paramRtp = Math.round(paramRtp * 1e3) / 1e3;
|
|
2776
|
-
(0,
|
|
2748
|
+
(0, import_assert5.default)(
|
|
2777
2749
|
gameModeRtp === paramRtp,
|
|
2778
2750
|
`Sum of all RTP conditions (${paramRtp}) does not match the game mode RTP (${gameModeRtp}) in game mode "${k}".`
|
|
2779
2751
|
);
|
|
@@ -2788,9 +2760,9 @@ var Optimizer = class {
|
|
|
2788
2760
|
};
|
|
2789
2761
|
async function rustProgram(...args) {
|
|
2790
2762
|
return new Promise((resolve, reject) => {
|
|
2791
|
-
const task = (0,
|
|
2763
|
+
const task = (0, import_child_process.spawn)("cargo", ["run", "--release", ...args], {
|
|
2792
2764
|
shell: true,
|
|
2793
|
-
cwd:
|
|
2765
|
+
cwd: import_path6.default.join(__dirname, "./optimizer-rust"),
|
|
2794
2766
|
stdio: "pipe"
|
|
2795
2767
|
});
|
|
2796
2768
|
task.on("error", (error) => {
|