@slot-engine/core 0.0.2 → 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/dist/index.d.mts CHANGED
@@ -879,7 +879,7 @@ declare class GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbol
879
879
  readonly id: string;
880
880
  readonly name: string;
881
881
  readonly gameModes: Record<GameModeName, GameMode>;
882
- readonly symbols: Map<TSymbols[number]["id"], TSymbols[number]>;
882
+ readonly symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
883
883
  readonly padSymbols?: number;
884
884
  readonly scatterToFreespins: Record<string, Record<number, number>>;
885
885
  readonly anticipationTriggers: Record<SpinType, number>;
@@ -908,7 +908,7 @@ declare class GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbol
908
908
  /**
909
909
  * Returns all configured symbols as an array.
910
910
  */
911
- getSymbolArray(): TSymbols[number][];
911
+ getSymbolArray(): TSymbols[keyof TSymbols][];
912
912
  static SPIN_TYPE: {
913
913
  readonly BASE_GAME: "basegame";
914
914
  readonly FREE_SPINS: "freespins";
@@ -928,7 +928,7 @@ declare class WinType {
928
928
  *
929
929
  * This gives the WinType access to the current board.
930
930
  */
931
- context(ctx: AnySimulationContext): WinType;
931
+ context(ctx: SimulationContext<any, any, any>): WinType;
932
932
  protected ensureContext(): void;
933
933
  /**
934
934
  * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
@@ -985,7 +985,7 @@ type WildSymbol = GameSymbol | Record<string, any>;
985
985
  declare class LinesWinType extends WinType {
986
986
  protected lines: Record<number, number[]>;
987
987
  protected winCombinations: LineWinCombination[];
988
- context: (ctx: AnySimulationContext) => LinesWinType;
988
+ context: (ctx: SimulationContext<any, any, any>) => LinesWinType;
989
989
  getWins: () => {
990
990
  payout: number;
991
991
  winCombinations: LineWinCombination[];
@@ -1193,7 +1193,7 @@ interface CommonGameOptions<TGameModes extends AnyGameModes = AnyGameModes, TSym
1193
1193
  /**
1194
1194
  * A list of all symbols that will appear on the reels.
1195
1195
  */
1196
- symbols: GameSymbol[];
1196
+ symbols: TSymbols;
1197
1197
  /**
1198
1198
  * A mapping from spin type to scatter counts to the number of free spins awarded.
1199
1199
  *
@@ -1246,7 +1246,7 @@ type AnyGameModes = Record<string, GameMode>;
1246
1246
  /**
1247
1247
  * @internal
1248
1248
  */
1249
- type AnySymbols = GameSymbol[];
1249
+ type AnySymbols = Record<string, GameSymbol>;
1250
1250
  /**
1251
1251
  * @internal
1252
1252
  */
@@ -1292,7 +1292,7 @@ interface CreateSlotGameOpts<TGameModes extends AnyGameModes = AnyGameModes, TSy
1292
1292
  }
1293
1293
  declare function createSlotGame<TGame>(opts: TGame extends InferGameType<infer G, infer S, infer U> ? CreateSlotGameOpts<G, S, U> : never): TGame;
1294
1294
  declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState) => TUserState;
1295
- declare const defineSymbols: <TSymbol extends GameSymbol>(symbols: TSymbol[]) => TSymbol[];
1295
+ declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
1296
1296
  declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
1297
1297
  declare const defineReelSets: <TSymbols extends AnySymbols>(reelSets: ReelGenerator[]) => ReelGenerator[];
1298
1298
 
package/dist/index.d.ts CHANGED
@@ -879,7 +879,7 @@ declare class GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbol
879
879
  readonly id: string;
880
880
  readonly name: string;
881
881
  readonly gameModes: Record<GameModeName, GameMode>;
882
- readonly symbols: Map<TSymbols[number]["id"], TSymbols[number]>;
882
+ readonly symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>;
883
883
  readonly padSymbols?: number;
884
884
  readonly scatterToFreespins: Record<string, Record<number, number>>;
885
885
  readonly anticipationTriggers: Record<SpinType, number>;
@@ -908,7 +908,7 @@ declare class GameConfig<TGameModes extends AnyGameModes = AnyGameModes, TSymbol
908
908
  /**
909
909
  * Returns all configured symbols as an array.
910
910
  */
911
- getSymbolArray(): TSymbols[number][];
911
+ getSymbolArray(): TSymbols[keyof TSymbols][];
912
912
  static SPIN_TYPE: {
913
913
  readonly BASE_GAME: "basegame";
914
914
  readonly FREE_SPINS: "freespins";
@@ -928,7 +928,7 @@ declare class WinType {
928
928
  *
929
929
  * This gives the WinType access to the current board.
930
930
  */
931
- context(ctx: AnySimulationContext): WinType;
931
+ context(ctx: SimulationContext<any, any, any>): WinType;
932
932
  protected ensureContext(): void;
933
933
  /**
934
934
  * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.
@@ -985,7 +985,7 @@ type WildSymbol = GameSymbol | Record<string, any>;
985
985
  declare class LinesWinType extends WinType {
986
986
  protected lines: Record<number, number[]>;
987
987
  protected winCombinations: LineWinCombination[];
988
- context: (ctx: AnySimulationContext) => LinesWinType;
988
+ context: (ctx: SimulationContext<any, any, any>) => LinesWinType;
989
989
  getWins: () => {
990
990
  payout: number;
991
991
  winCombinations: LineWinCombination[];
@@ -1193,7 +1193,7 @@ interface CommonGameOptions<TGameModes extends AnyGameModes = AnyGameModes, TSym
1193
1193
  /**
1194
1194
  * A list of all symbols that will appear on the reels.
1195
1195
  */
1196
- symbols: GameSymbol[];
1196
+ symbols: TSymbols;
1197
1197
  /**
1198
1198
  * A mapping from spin type to scatter counts to the number of free spins awarded.
1199
1199
  *
@@ -1246,7 +1246,7 @@ type AnyGameModes = Record<string, GameMode>;
1246
1246
  /**
1247
1247
  * @internal
1248
1248
  */
1249
- type AnySymbols = GameSymbol[];
1249
+ type AnySymbols = Record<string, GameSymbol>;
1250
1250
  /**
1251
1251
  * @internal
1252
1252
  */
@@ -1292,7 +1292,7 @@ interface CreateSlotGameOpts<TGameModes extends AnyGameModes = AnyGameModes, TSy
1292
1292
  }
1293
1293
  declare function createSlotGame<TGame>(opts: TGame extends InferGameType<infer G, infer S, infer U> ? CreateSlotGameOpts<G, S, U> : never): TGame;
1294
1294
  declare const defineUserState: <TUserState extends AnyUserData>(data: TUserState) => TUserState;
1295
- declare const defineSymbols: <TSymbol extends GameSymbol>(symbols: TSymbol[]) => TSymbol[];
1295
+ declare const defineSymbols: <TSymbols extends AnySymbols>(symbols: TSymbols) => TSymbols;
1296
1296
  declare const defineGameModes: <TGameModes extends AnyGameModes>(gameModes: TGameModes) => TGameModes;
1297
1297
  declare const defineReelSets: <TSymbols extends AnySymbols>(reelSets: ReelGenerator[]) => ReelGenerator[];
1298
1298
 
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;
@@ -1827,7 +1826,7 @@ var ManywaysWinType = class extends WinType {
1827
1826
  };
1828
1827
 
1829
1828
  // src/optimizer/OptimizationConditions.ts
1830
- var import_assert = __toESM(require("assert"));
1829
+ var import_assert2 = __toESM(require("assert"));
1831
1830
  var OptimizationConditions = class {
1832
1831
  rtp;
1833
1832
  avgWin;
@@ -1838,14 +1837,14 @@ var OptimizationConditions = class {
1838
1837
  constructor(opts) {
1839
1838
  let { rtp, avgWin, hitRate, searchConditions, priority } = opts;
1840
1839
  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.");
1840
+ (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
1841
  rtp = Math.round(avgWin / Number(hitRate) * 1e5) / 1e5;
1843
1842
  }
1844
1843
  let noneCount = 0;
1845
1844
  for (const val of [rtp, avgWin, hitRate]) {
1846
1845
  if (val === void 0) noneCount++;
1847
1846
  }
1848
- (0, import_assert.default)(noneCount <= 1, "Invalid combination of optimization conditions.");
1847
+ (0, import_assert2.default)(noneCount <= 1, "Invalid combination of optimization conditions.");
1849
1848
  this.searchRange = [-1, -1];
1850
1849
  this.forceSearch = {};
1851
1850
  if (typeof searchConditions === "number") {
@@ -1925,41 +1924,11 @@ var OptimizationParameters = class _OptimizationParameters {
1925
1924
 
1926
1925
  // src/Simulation.ts
1927
1926
  var import_fs3 = __toESM(require("fs"));
1928
- var import_path3 = __toESM(require("path"));
1929
- var import_assert2 = __toESM(require("assert"));
1927
+ var import_path2 = __toESM(require("path"));
1928
+ var import_assert3 = __toESM(require("assert"));
1929
+ var import_zlib = __toESM(require("zlib"));
1930
1930
  var import_esbuild = require("esbuild");
1931
1931
  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
1932
  var completedSimulations = 0;
1964
1933
  var TEMP_FILENAME = "__temp_compiled_src_IGNORE.js";
1965
1934
  var Simulation = class _Simulation {
@@ -1980,7 +1949,7 @@ var Simulation = class _Simulation {
1980
1949
  this.library = /* @__PURE__ */ new Map();
1981
1950
  this.records = [];
1982
1951
  const gameModeKeys = Object.keys(this.gameConfig.config.gameModes);
1983
- (0, import_assert2.default)(
1952
+ (0, import_assert3.default)(
1984
1953
  Object.values(this.gameConfig.config.gameModes).map((m) => gameModeKeys.includes(m.name)).every((v) => v === true),
1985
1954
  "Game mode name must match its key in the gameModes object."
1986
1955
  );
@@ -2017,14 +1986,14 @@ Simulating game mode: ${mode}`);
2017
1986
  const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode);
2018
1987
  await this.spawnWorkersForGameMode({ mode, simNumsToCriteria });
2019
1988
  createDirIfNotExists(
2020
- import_path3.default.join(
1989
+ import_path2.default.join(
2021
1990
  process.cwd(),
2022
1991
  this.gameConfig.config.outputDir,
2023
1992
  "optimization_files"
2024
1993
  )
2025
1994
  );
2026
1995
  createDirIfNotExists(
2027
- import_path3.default.join(process.cwd(), this.gameConfig.config.outputDir, "publish_files")
1996
+ import_path2.default.join(process.cwd(), this.gameConfig.config.outputDir, "publish_files")
2028
1997
  );
2029
1998
  _Simulation.writeLookupTableCSV({
2030
1999
  gameMode: mode,
@@ -2120,7 +2089,7 @@ Simulating game mode: ${mode}`);
2120
2089
  }
2121
2090
  }
2122
2091
  return new Promise((resolve, reject) => {
2123
- const scriptPath = import_path3.default.join(process.cwd(), basePath, TEMP_FILENAME);
2092
+ const scriptPath = import_path2.default.join(process.cwd(), basePath, TEMP_FILENAME);
2124
2093
  const worker = new import_worker_threads2.Worker(scriptPath, {
2125
2094
  workerData: {
2126
2095
  mode,
@@ -2168,10 +2137,10 @@ Simulating game mode: ${mode}`);
2168
2137
  }
2169
2138
  rows.sort((a, b) => Number(a.split(",")[0]) - Number(b.split(",")[0]));
2170
2139
  let outputFileName = `lookUpTable_${gameMode}.csv`;
2171
- let outputFilePath = import_path3.default.join(gameConfig.outputDir, outputFileName);
2140
+ let outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
2172
2141
  writeFile(outputFilePath, rows.join("\n"));
2173
2142
  outputFileName = `lookUpTable_${gameMode}_0.csv`;
2174
- outputFilePath = import_path3.default.join(gameConfig.outputDir, outputFileName);
2143
+ outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
2175
2144
  writeFile(outputFilePath, rows.join("\n"));
2176
2145
  return outputFilePath;
2177
2146
  }
@@ -2188,21 +2157,21 @@ Simulating game mode: ${mode}`);
2188
2157
  }
2189
2158
  rows.sort((a, b) => Number(a.split(",")[0]) - Number(b.split(",")[0]));
2190
2159
  const outputFileName = `lookUpTableSegmented_${gameMode}.csv`;
2191
- const outputFilePath = import_path3.default.join(gameConfig.outputDir, outputFileName);
2160
+ const outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
2192
2161
  writeFile(outputFilePath, rows.join("\n"));
2193
2162
  return outputFilePath;
2194
2163
  }
2195
2164
  static writeRecords(opts) {
2196
2165
  const { gameMode, fileNameWithoutExtension, records, gameConfig, debug } = opts;
2197
2166
  const outputFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.json` : `force_record_${gameMode}.json`;
2198
- const outputFilePath = import_path3.default.join(gameConfig.outputDir, outputFileName);
2167
+ const outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
2199
2168
  writeFile(outputFilePath, JSON.stringify(records, null, 2));
2200
2169
  if (debug) _Simulation.logSymbolOccurrences(records);
2201
2170
  return outputFilePath;
2202
2171
  }
2203
2172
  static writeIndexJson(opts) {
2204
2173
  const { gameConfig } = opts;
2205
- const outputFilePath = import_path3.default.join(
2174
+ const outputFilePath = import_path2.default.join(
2206
2175
  process.cwd(),
2207
2176
  gameConfig.outputDir,
2208
2177
  "publish_files",
@@ -2219,7 +2188,7 @@ Simulating game mode: ${mode}`);
2219
2188
  static async writeBooksJson(opts) {
2220
2189
  const { gameMode, fileNameWithoutExtension, library, gameConfig } = opts;
2221
2190
  const outputFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.jsonl` : `books_${gameMode}.jsonl`;
2222
- const outputFilePath = import_path3.default.join(gameConfig.outputDir, outputFileName);
2191
+ const outputFilePath = import_path2.default.join(gameConfig.outputDir, outputFileName);
2223
2192
  const books = Array.from(library.values()).map((b) => b.serialize()).map((b) => ({
2224
2193
  id: b.id,
2225
2194
  payoutMultiplier: b.payout,
@@ -2228,14 +2197,15 @@ Simulating game mode: ${mode}`);
2228
2197
  const contents = JSONL.stringify(books);
2229
2198
  writeFile(outputFilePath, contents);
2230
2199
  const compressedFileName = fileNameWithoutExtension ? `${fileNameWithoutExtension}.jsonl.zst` : `books_${gameMode}.jsonl.zst`;
2231
- const compressedFilePath = import_path3.default.join(
2200
+ const compressedFilePath = import_path2.default.join(
2232
2201
  process.cwd(),
2233
2202
  gameConfig.outputDir,
2234
2203
  "publish_files",
2235
2204
  compressedFileName
2236
2205
  );
2237
2206
  import_fs3.default.rmSync(compressedFilePath, { force: true });
2238
- await zstd("-f", outputFilePath, "-o", compressedFilePath);
2207
+ const compressed = import_zlib.default.zstdCompressSync(Buffer.from(contents));
2208
+ import_fs3.default.writeFileSync(compressedFilePath, compressed);
2239
2209
  }
2240
2210
  static logSymbolOccurrences(records) {
2241
2211
  const validRecords = records.filter(
@@ -2266,14 +2236,14 @@ Simulating game mode: ${mode}`);
2266
2236
  * Compiles user configured game to JS for use in different Node processes
2267
2237
  */
2268
2238
  preprocessFiles() {
2269
- const builtFilePath = import_path3.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME);
2239
+ const builtFilePath = import_path2.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME);
2270
2240
  import_fs3.default.rmSync(builtFilePath, { force: true });
2271
2241
  (0, import_esbuild.buildSync)({
2272
2242
  entryPoints: [process.cwd()],
2273
2243
  bundle: true,
2274
2244
  platform: "node",
2275
- outfile: import_path3.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME),
2276
- external: ["esbuild", "@mongodb-js/zstd"]
2245
+ outfile: import_path2.default.join(this.gameConfig.config.outputDir, TEMP_FILENAME),
2246
+ external: ["esbuild"]
2277
2247
  });
2278
2248
  }
2279
2249
  getSimRangesForChunks(total, chunks) {
@@ -2381,8 +2351,8 @@ var SimulationContext = class extends Board {
2381
2351
 
2382
2352
  // src/analysis/index.ts
2383
2353
  var import_fs4 = __toESM(require("fs"));
2384
- var import_path4 = __toESM(require("path"));
2385
- var import_assert3 = __toESM(require("assert"));
2354
+ var import_path3 = __toESM(require("path"));
2355
+ var import_assert4 = __toESM(require("assert"));
2386
2356
 
2387
2357
  // src/analysis/utils.ts
2388
2358
  function parseLookupTable(content) {
@@ -2426,8 +2396,11 @@ function getPayoutWeights(lut, opts = {}) {
2426
2396
  }
2427
2397
  function getNonZeroHitrate(payoutWeights) {
2428
2398
  const totalWeight = getTotalWeight(payoutWeights);
2429
- const nonZeroWeight = totalWeight - (payoutWeights[0] ?? 0) * totalWeight;
2430
- return nonZeroWeight / totalWeight;
2399
+ if (Math.min(...Object.keys(payoutWeights).map(Number)) == 0) {
2400
+ return totalWeight / (totalWeight - (payoutWeights[0] ?? 0) / totalWeight);
2401
+ } else {
2402
+ return 1;
2403
+ }
2431
2404
  }
2432
2405
  function getNullHitrate(payoutWeights) {
2433
2406
  return payoutWeights[0] ?? 0;
@@ -2488,6 +2461,7 @@ function getLessBetHitrate(payoutWeights, cost) {
2488
2461
  }
2489
2462
 
2490
2463
  // src/analysis/index.ts
2464
+ var import_worker_threads3 = require("worker_threads");
2491
2465
  var Analysis = class {
2492
2466
  gameConfig;
2493
2467
  optimizerConfig;
@@ -2498,6 +2472,7 @@ var Analysis = class {
2498
2472
  this.filePaths = {};
2499
2473
  }
2500
2474
  async runAnalysis(gameModes) {
2475
+ if (!import_worker_threads3.isMainThread) return;
2501
2476
  this.filePaths = this.getPathsForModes(gameModes);
2502
2477
  this.getNumberStats(gameModes);
2503
2478
  this.getWinRanges(gameModes);
@@ -2507,28 +2482,28 @@ var Analysis = class {
2507
2482
  const rootPath = process.cwd();
2508
2483
  const paths = {};
2509
2484
  for (const modeStr of gameModes) {
2510
- const lut = import_path4.default.join(
2485
+ const lut = import_path3.default.join(
2511
2486
  rootPath,
2512
2487
  this.gameConfig.outputDir,
2513
2488
  `lookUpTable_${modeStr}.csv`
2514
2489
  );
2515
- const lutSegmented = import_path4.default.join(
2490
+ const lutSegmented = import_path3.default.join(
2516
2491
  rootPath,
2517
2492
  this.gameConfig.outputDir,
2518
2493
  `lookUpTableSegmented_${modeStr}.csv`
2519
2494
  );
2520
- const lutOptimized = import_path4.default.join(
2495
+ const lutOptimized = import_path3.default.join(
2521
2496
  rootPath,
2522
2497
  this.gameConfig.outputDir,
2523
2498
  "publish_files",
2524
2499
  `lookUpTable_${modeStr}_0.csv`
2525
2500
  );
2526
- const booksJsonl = import_path4.default.join(
2501
+ const booksJsonl = import_path3.default.join(
2527
2502
  rootPath,
2528
2503
  this.gameConfig.outputDir,
2529
2504
  `books_${modeStr}.jsonl`
2530
2505
  );
2531
- const booksJsonlCompressed = import_path4.default.join(
2506
+ const booksJsonlCompressed = import_path3.default.join(
2532
2507
  rootPath,
2533
2508
  this.gameConfig.outputDir,
2534
2509
  "publish_files",
@@ -2542,7 +2517,7 @@ var Analysis = class {
2542
2517
  booksJsonlCompressed
2543
2518
  };
2544
2519
  for (const p of Object.values(paths[modeStr])) {
2545
- (0, import_assert3.default)(
2520
+ (0, import_assert4.default)(
2546
2521
  import_fs4.default.existsSync(p),
2547
2522
  `File "${p}" does not exist. Run optimization to auto-create it.`
2548
2523
  );
@@ -2576,7 +2551,7 @@ var Analysis = class {
2576
2551
  });
2577
2552
  }
2578
2553
  writeJsonFile(
2579
- import_path4.default.join(process.cwd(), this.gameConfig.outputDir, "stats_summary.json"),
2554
+ import_path3.default.join(process.cwd(), this.gameConfig.outputDir, "stats_summary.json"),
2580
2555
  stats
2581
2556
  );
2582
2557
  }
@@ -2614,13 +2589,13 @@ var Analysis = class {
2614
2589
  }
2615
2590
  getGameModeConfig(mode) {
2616
2591
  const config = this.gameConfig.gameModes[mode];
2617
- (0, import_assert3.default)(config, `Game mode "${mode}" not found in game config`);
2592
+ (0, import_assert4.default)(config, `Game mode "${mode}" not found in game config`);
2618
2593
  return config;
2619
2594
  }
2620
2595
  };
2621
2596
 
2622
2597
  // src/utils/math-config.ts
2623
- var import_path5 = __toESM(require("path"));
2598
+ var import_path4 = __toESM(require("path"));
2624
2599
  function makeMathConfig(optimizer, opts = {}) {
2625
2600
  const game = optimizer.getGameConfig();
2626
2601
  const gameModesCfg = optimizer.getOptimizerGameModes();
@@ -2664,14 +2639,14 @@ function makeMathConfig(optimizer, opts = {}) {
2664
2639
  }))
2665
2640
  };
2666
2641
  if (writeToFile) {
2667
- const outPath = import_path5.default.join(process.cwd(), game.outputDir, "math_config.json");
2642
+ const outPath = import_path4.default.join(process.cwd(), game.outputDir, "math_config.json");
2668
2643
  writeJsonFile(outPath, config);
2669
2644
  }
2670
2645
  return config;
2671
2646
  }
2672
2647
 
2673
2648
  // src/utils/setup-file.ts
2674
- var import_path6 = __toESM(require("path"));
2649
+ var import_path5 = __toESM(require("path"));
2675
2650
  function makeSetupFile(optimizer, gameMode) {
2676
2651
  const gameConfig = optimizer.getGameConfig();
2677
2652
  const optimizerGameModes = optimizer.getOptimizerGameModes();
@@ -2707,19 +2682,19 @@ function makeSetupFile(optimizer, gameMode) {
2707
2682
  `;
2708
2683
  content += `simulation_trials;${params.simulationTrials}
2709
2684
  `;
2710
- content += `user_game_build_path;${import_path6.default.join(process.cwd(), gameConfig.outputDir)}
2685
+ content += `user_game_build_path;${import_path5.default.join(process.cwd(), gameConfig.outputDir)}
2711
2686
  `;
2712
2687
  content += `pmb_rtp;${params.pmbRtp}
2713
2688
  `;
2714
- const outPath = import_path6.default.join(__dirname, "./optimizer-rust/src", "setup.txt");
2689
+ const outPath = import_path5.default.join(__dirname, "./optimizer-rust/src", "setup.txt");
2715
2690
  writeFile(outPath, content);
2716
2691
  }
2717
2692
 
2718
2693
  // 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");
2694
+ var import_child_process = require("child_process");
2695
+ var import_path6 = __toESM(require("path"));
2696
+ var import_assert5 = __toESM(require("assert"));
2697
+ var import_worker_threads4 = require("worker_threads");
2723
2698
  var Optimizer = class {
2724
2699
  gameConfig;
2725
2700
  gameModes;
@@ -2732,12 +2707,13 @@ var Optimizer = class {
2732
2707
  * Runs the optimization process, and runs analysis after.
2733
2708
  */
2734
2709
  async runOptimization({ gameModes }) {
2735
- if (!import_worker_threads3.isMainThread) return;
2710
+ if (!import_worker_threads4.isMainThread) return;
2736
2711
  const mathConfig = makeMathConfig(this, { writeToFile: true });
2737
2712
  for (const mode of gameModes) {
2738
2713
  const setupFile = makeSetupFile(this, mode);
2739
2714
  await this.runSingleOptimization();
2740
2715
  }
2716
+ console.log("Optimization complete. Files written to build directory.");
2741
2717
  }
2742
2718
  async runSingleOptimization() {
2743
2719
  return await rustProgram();
@@ -2761,7 +2737,7 @@ var Optimizer = class {
2761
2737
  }
2762
2738
  }
2763
2739
  const criteria = configMode.resultSets.map((r) => r.criteria);
2764
- (0, import_assert4.default)(
2740
+ (0, import_assert5.default)(
2765
2741
  conditions.every((c) => criteria.includes(c)),
2766
2742
  `Not all ResultSet criteria in game mode "${k}" are defined as optimization conditions.`
2767
2743
  );
@@ -2773,7 +2749,7 @@ var Optimizer = class {
2773
2749
  }
2774
2750
  gameModeRtp = Math.round(gameModeRtp * 1e3) / 1e3;
2775
2751
  paramRtp = Math.round(paramRtp * 1e3) / 1e3;
2776
- (0, import_assert4.default)(
2752
+ (0, import_assert5.default)(
2777
2753
  gameModeRtp === paramRtp,
2778
2754
  `Sum of all RTP conditions (${paramRtp}) does not match the game mode RTP (${gameModeRtp}) in game mode "${k}".`
2779
2755
  );
@@ -2788,9 +2764,9 @@ var Optimizer = class {
2788
2764
  };
2789
2765
  async function rustProgram(...args) {
2790
2766
  return new Promise((resolve, reject) => {
2791
- const task = (0, import_child_process2.spawn)("cargo", ["run", "--release", ...args], {
2767
+ const task = (0, import_child_process.spawn)("cargo", ["run", "--release", ...args], {
2792
2768
  shell: true,
2793
- cwd: import_path7.default.join(__dirname, "./optimizer-rust"),
2769
+ cwd: import_path6.default.join(__dirname, "./optimizer-rust"),
2794
2770
  stdio: "pipe"
2795
2771
  });
2796
2772
  task.on("error", (error) => {