@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.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 symbol of opts.symbols) {
80
- if (!this.config.symbols.has(symbol.id)) {
81
- this.config.symbols.set(symbol.id, symbol);
82
- } else {
83
- console.warn(
84
- `Symbol with id "${symbol.id}" already exists in the game config. Skipping duplicate. This is probably not intentional.`
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
- getGameModeCriteria(mode, criteria) {
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 reelset based on the configured weights for the current game mode.\
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.reels;
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 import_assert = __toESM(require("assert"));
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, import_assert.default)(avgWin !== void 0 && hitRate !== void 0, "If RTP is not specified, hit-rate (hr) and average win amount (av_win) must be given.");
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, import_assert.default)(noneCount <= 1, "Invalid combination of optimization conditions.");
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 import_path3 = __toESM(require("path"));
1929
- var import_assert2 = __toESM(require("assert"));
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, import_assert2.default)(
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
- import_path3.default.join(
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
- import_path3.default.join(process.cwd(), this.gameConfig.config.outputDir, "publish_files")
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 = import_path3.default.join(process.cwd(), basePath, TEMP_FILENAME);
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 = import_path3.default.join(gameConfig.outputDir, outputFileName);
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 = import_path3.default.join(gameConfig.outputDir, outputFileName);
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 = import_path3.default.join(gameConfig.outputDir, outputFileName);
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 = import_path3.default.join(gameConfig.outputDir, outputFileName);
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 = import_path3.default.join(
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 = import_path3.default.join(gameConfig.outputDir, outputFileName);
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 = import_path3.default.join(
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
- await zstd("-f", outputFilePath, "-o", compressedFilePath);
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 = import_path3.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME);
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: import_path3.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME),
2276
- external: ["esbuild", "@mongodb-js/zstd"]
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 import_path4 = __toESM(require("path"));
2385
- var import_assert3 = __toESM(require("assert"));
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
- const nonZeroWeight = totalWeight - (payoutWeights[0] ?? 0) * totalWeight;
2430
- return nonZeroWeight / totalWeight;
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 = import_path4.default.join(
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 = import_path4.default.join(
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 = import_path4.default.join(
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 = import_path4.default.join(
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 = import_path4.default.join(
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, import_assert3.default)(
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
- import_path4.default.join(process.cwd(), this.gameConfig.outputDir, "stats_summary.json"),
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, import_assert3.default)(config, `Game mode "${mode}" not found in game config`);
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 import_path5 = __toESM(require("path"));
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 = import_path5.default.join(process.cwd(), game.outputDir, "math_config.json");
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 import_path6 = __toESM(require("path"));
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;${import_path6.default.join(process.cwd(), gameConfig.outputDir)}
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 = import_path6.default.join(__dirname, "./optimizer-rust/src", "setup.txt");
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 import_child_process2 = require("child_process");
2720
- var import_path7 = __toESM(require("path"));
2721
- var import_assert4 = __toESM(require("assert"));
2722
- var import_worker_threads3 = require("worker_threads");
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 (!import_worker_threads3.isMainThread) return;
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, import_assert4.default)(
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, import_assert4.default)(
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, import_child_process2.spawn)("cargo", ["run", "--release", ...args], {
2763
+ const task = (0, import_child_process.spawn)("cargo", ["run", "--release", ...args], {
2792
2764
  shell: true,
2793
- cwd: import_path7.default.join(__dirname, "./optimizer-rust"),
2765
+ cwd: import_path6.default.join(__dirname, "./optimizer-rust"),
2794
2766
  stdio: "pipe"
2795
2767
  });
2796
2768
  task.on("error", (error) => {