@slot-engine/core 0.2.3 → 0.2.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 +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +93 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -44,7 +44,8 @@ function createPermanentFilePaths(basePath) {
|
|
|
44
44
|
publishFiles: path.join(basePath, "publish_files"),
|
|
45
45
|
simulationSummary: path.join(basePath, "simulation_summary.json"),
|
|
46
46
|
statsPayouts: path.join(basePath, "stats_payouts.json"),
|
|
47
|
-
statsSummary: path.join(basePath, "stats_summary.json")
|
|
47
|
+
statsSummary: path.join(basePath, "stats_summary.json"),
|
|
48
|
+
statsRecords: path.join(basePath, "stats_records.json")
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
51
|
function createTemporaryFilePaths(basePath, tempFolder) {
|
|
@@ -1053,7 +1054,10 @@ var DataService = class extends AbstractService {
|
|
|
1053
1054
|
* Calls `ctx.services.data.record()` with the provided data.
|
|
1054
1055
|
*/
|
|
1055
1056
|
recordSymbolOccurrence(data) {
|
|
1056
|
-
this.record(
|
|
1057
|
+
this.record({
|
|
1058
|
+
...data,
|
|
1059
|
+
spinType: this.ctx().state.currentSpinType
|
|
1060
|
+
});
|
|
1057
1061
|
}
|
|
1058
1062
|
/**
|
|
1059
1063
|
* Adds an event to the book.
|
|
@@ -3075,15 +3079,21 @@ function getLessBetHitrate(payoutWeights, cost) {
|
|
|
3075
3079
|
|
|
3076
3080
|
// src/analysis/index.ts
|
|
3077
3081
|
import { isMainThread as isMainThread3 } from "worker_threads";
|
|
3082
|
+
import chalk3 from "chalk";
|
|
3078
3083
|
var Analysis = class {
|
|
3079
3084
|
game;
|
|
3080
3085
|
constructor(game) {
|
|
3081
3086
|
this.game = game;
|
|
3082
3087
|
}
|
|
3083
|
-
async runAnalysis(
|
|
3088
|
+
async runAnalysis(opts) {
|
|
3089
|
+
const { gameModes, recordStats = [] } = opts;
|
|
3084
3090
|
if (!isMainThread3) return;
|
|
3091
|
+
console.log(chalk3.gray("Starting analysis..."));
|
|
3085
3092
|
this.getNumberStats(gameModes);
|
|
3086
3093
|
this.getWinRanges(gameModes);
|
|
3094
|
+
if (recordStats.length > 0) {
|
|
3095
|
+
this.getRecordStats(gameModes, recordStats);
|
|
3096
|
+
}
|
|
3087
3097
|
console.log("Analysis complete. Files written to build directory.");
|
|
3088
3098
|
}
|
|
3089
3099
|
getNumberStats(gameModes) {
|
|
@@ -3260,6 +3270,85 @@ var Analysis = class {
|
|
|
3260
3270
|
}
|
|
3261
3271
|
writeJsonFile(meta.paths.statsPayouts, payoutRanges);
|
|
3262
3272
|
}
|
|
3273
|
+
getRecordStats(gameModes, recordStatsConfig) {
|
|
3274
|
+
const meta = this.game.getMetadata();
|
|
3275
|
+
const allStats = [];
|
|
3276
|
+
for (const modeStr of gameModes) {
|
|
3277
|
+
const lutOptimized = parseLookupTable(
|
|
3278
|
+
fs4.readFileSync(meta.paths.lookupTablePublish(modeStr), "utf-8")
|
|
3279
|
+
);
|
|
3280
|
+
const totalWeight = getTotalLutWeight(lutOptimized);
|
|
3281
|
+
const weightMap = /* @__PURE__ */ new Map();
|
|
3282
|
+
lutOptimized.forEach(([bookId, weight]) => {
|
|
3283
|
+
weightMap.set(bookId, weight);
|
|
3284
|
+
});
|
|
3285
|
+
const forceRecordsPath = meta.paths.forceRecords(modeStr);
|
|
3286
|
+
if (!fs4.existsSync(forceRecordsPath)) continue;
|
|
3287
|
+
const forceRecords = JSON.parse(
|
|
3288
|
+
fs4.readFileSync(forceRecordsPath, "utf-8")
|
|
3289
|
+
);
|
|
3290
|
+
const modeStats = {
|
|
3291
|
+
gameMode: modeStr,
|
|
3292
|
+
groups: []
|
|
3293
|
+
};
|
|
3294
|
+
for (const config of recordStatsConfig) {
|
|
3295
|
+
const groupName = config.name || config.groupBy.join("_");
|
|
3296
|
+
const aggregated = /* @__PURE__ */ new Map();
|
|
3297
|
+
for (const record of forceRecords) {
|
|
3298
|
+
const searchMap = new Map(record.search.map((s) => [s.name, s.value]));
|
|
3299
|
+
if (config.filter) {
|
|
3300
|
+
let matches = true;
|
|
3301
|
+
for (const [key2, value] of Object.entries(config.filter)) {
|
|
3302
|
+
if (searchMap.get(key2) !== value) {
|
|
3303
|
+
matches = false;
|
|
3304
|
+
break;
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
if (!matches) continue;
|
|
3308
|
+
}
|
|
3309
|
+
const hasAllProps = config.groupBy.every((prop) => searchMap.has(prop));
|
|
3310
|
+
if (!hasAllProps) continue;
|
|
3311
|
+
const key = config.groupBy.map((prop) => searchMap.get(prop)).join("|");
|
|
3312
|
+
const properties = Object.fromEntries(
|
|
3313
|
+
config.groupBy.map((prop) => [prop, searchMap.get(prop)])
|
|
3314
|
+
);
|
|
3315
|
+
let totalWeight2 = 0;
|
|
3316
|
+
for (const bookId of record.bookIds) {
|
|
3317
|
+
totalWeight2 += weightMap.get(bookId) ?? 0;
|
|
3318
|
+
}
|
|
3319
|
+
const existing = aggregated.get(key);
|
|
3320
|
+
if (existing) {
|
|
3321
|
+
existing.count += record.timesTriggered;
|
|
3322
|
+
existing.totalWeight += totalWeight2;
|
|
3323
|
+
} else {
|
|
3324
|
+
aggregated.set(key, {
|
|
3325
|
+
properties,
|
|
3326
|
+
count: record.timesTriggered,
|
|
3327
|
+
totalWeight: totalWeight2
|
|
3328
|
+
});
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
const items = Array.from(aggregated.entries()).map(([key, data]) => {
|
|
3332
|
+
const hitRate = round(totalWeight / data.totalWeight, 4);
|
|
3333
|
+
return {
|
|
3334
|
+
key,
|
|
3335
|
+
properties: data.properties,
|
|
3336
|
+
count: data.count,
|
|
3337
|
+
hitRateString: `1 in ${Math.round(hitRate).toLocaleString()}`,
|
|
3338
|
+
hitRate
|
|
3339
|
+
};
|
|
3340
|
+
}).sort((a, b) => a.hitRate - b.hitRate);
|
|
3341
|
+
modeStats.groups.push({
|
|
3342
|
+
name: groupName,
|
|
3343
|
+
groupBy: config.groupBy,
|
|
3344
|
+
filter: config.filter,
|
|
3345
|
+
items
|
|
3346
|
+
});
|
|
3347
|
+
}
|
|
3348
|
+
allStats.push(modeStats);
|
|
3349
|
+
}
|
|
3350
|
+
writeJsonFile(meta.paths.statsRecords, allStats);
|
|
3351
|
+
}
|
|
3263
3352
|
getGameModeConfig(mode) {
|
|
3264
3353
|
const config = this.game.getConfig().gameModes[mode];
|
|
3265
3354
|
assert6(config, `Game mode "${mode}" not found in game config`);
|
|
@@ -3624,7 +3713,7 @@ var SlotGame = class _SlotGame {
|
|
|
3624
3713
|
*/
|
|
3625
3714
|
runAnalysis(opts) {
|
|
3626
3715
|
this.analyzer = new Analysis(this);
|
|
3627
|
-
this.analyzer.runAnalysis(opts
|
|
3716
|
+
this.analyzer.runAnalysis(opts);
|
|
3628
3717
|
}
|
|
3629
3718
|
/**
|
|
3630
3719
|
* Runs the configured tasks: simulation, optimization, and/or analysis.
|