@slot-engine/core 0.1.5 → 0.1.7

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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../src/constants.ts","../src/game-config/index.ts","../src/simulation/index.ts","../src/result-set/index.ts","../src/service/index.ts","../src/service/rng.ts","../utils.ts","../src/game-state/index.ts","../src/recorder/index.ts","../src/board/index.ts","../src/game-symbol/index.ts","../src/service/board.ts","../src/service/data.ts","../src/service/game.ts","../src/service/wallet.ts","../src/game-context/index.ts","../src/book/index.ts","../src/wallet/index.ts","../src/analysis/index.ts","../src/analysis/utils.ts","../src/optimizer/index.ts","../src/utils/math-config.ts","../src/utils/setup-file.ts","../src/optimizer/OptimizationConditions.ts","../src/optimizer/OptimizationScaling.ts","../src/optimizer/OptimizationParameters.ts","../src/slot-game/index.ts","../src/createSlotGame.ts","../src/game-mode/index.ts","../src/win-types/index.ts","../src/win-types/LinesWinType.ts","../src/win-types/ClusterWinType.ts","../src/win-types/ManywaysWinType.ts","../src/reel-set/GeneratedReelSet.ts","../src/reel-set/index.ts","../src/reel-set/StaticReelSet.ts","../src/board/StandaloneBoard.ts"],"sourcesContent":["export {\n type InferGameType,\n type AnyUserData,\n type AnyGameModes,\n type AnySymbols,\n type GameHooks,\n type SpinType,\n type Reels,\n} from \"./src/types\"\n\nexport { SPIN_TYPE } from \"./src/constants\"\n\nexport {\n createSlotGame,\n defineGameModes,\n defineSymbols,\n defineUserState,\n} from \"./src/createSlotGame\"\n\nexport { GameMode } from \"./src/game-mode\"\nexport { GameSymbol } from \"./src/game-symbol\"\nexport { ResultSet } from \"./src/result-set\"\n\nexport {\n OptimizationConditions,\n OptimizationParameters,\n OptimizationScaling,\n} from \"./src/optimizer\"\n\nexport { type GameContext } from \"./src/game-context\"\n\nexport { LinesWinType } from \"./src/win-types/LinesWinType\"\nexport { ClusterWinType } from \"./src/win-types/ClusterWinType\"\nexport { ManywaysWinType } from \"./src/win-types/ManywaysWinType\"\nexport { type WinCombination } from \"./src/win-types\"\n\nexport { GeneratedReelSet } from \"./src/reel-set/GeneratedReelSet\"\nexport { StaticReelSet } from \"./src/reel-set/StaticReelSet\"\n\nexport { StandaloneBoard } from \"./src/board/StandaloneBoard\"\n","export const SPIN_TYPE = {\n BASE_GAME: \"basegame\",\n FREE_SPINS: \"freespins\",\n} as const\n","import assert from \"assert\"\nimport { AnyGameModes, AnySymbols, AnyUserData, GameHooks } from \"../types\"\nimport { SPIN_TYPE } from \"../constants\"\n\nexport interface GameConfigOptions<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> {\n /**\n * The unique identifier of the game, used for configuration and identification.\n */\n id: string\n /**\n * The name of the game, used for display purposes.\n */\n name: string\n /**\n * A GameMode is the core structure of a slot, defining the board,\\\n * bet cost, win type, and other properties.\n */\n gameModes: TGameModes\n /**\n * A list of all symbols that will appear on the reels.\n */\n symbols: TSymbols\n /**\n * A mapping from spin type to scatter counts to the number of free spins awarded.\n *\n * @example\n * ```ts\n * scatterToFreespins: {\n * [SPIN_TYPE.BASE_GAME]: {\n * 3: 10,\n * 4: 12,\n * 5: 15,\n * },\n * [SPIN_TYPE.FREE_SPINS]: {\n * 3: 6,\n * 4: 8,\n * 5: 10,\n * },\n * },\n * ```\n */\n scatterToFreespins: Record<string, Record<number, number>>\n /**\n * If set, this will pad the board with symbols on the top and bottom of the reels.\\\n * Useful for teasing symbols right above or below the active board.\n *\n * Default: 1\n */\n padSymbols?: number\n /**\n * The maximum win multiplier of the game, e.g. 5000 for a 5000x max win.\n */\n maxWinX: number\n /**\n * Custom additional state that can be used in game flow logic.\n */\n userState?: TUserState\n /**\n * Hooks are used to inject custom logic at specific points in the game flow.\\\n * Some required hooks must be implemented for certain features to work.\n */\n hooks: GameHooks<TGameModes, TSymbols, TUserState>\n /**\n * If, for some reason, you run your game WITHOUT `cd`ing into the game root,\\\n * you can specify the root directory here to ensure assets are resolved correctly.\\\n * Normally, this is not needed.\n */\n rootDir?: string\n}\n\nexport function createGameConfig<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n>(opts: GameConfigOptions<TGameModes, TSymbols, TUserState>) {\n const symbols = new Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>()\n\n for (const [key, value] of Object.entries(opts.symbols)) {\n assert(value.id === key, `Symbol key \"${key}\" does not match symbol id \"${value.id}\"`)\n symbols.set(key, value as TSymbols[keyof TSymbols])\n }\n\n const getAnticipationTrigger = (spinType: string) => {\n return (\n Math.min(...Object.keys(opts.scatterToFreespins[spinType] || {}).map(Number)) - 1\n )\n }\n\n return {\n padSymbols: opts.padSymbols || 1,\n userState: opts.userState || ({} as TUserState),\n ...opts,\n symbols,\n anticipationTriggers: {\n [SPIN_TYPE.BASE_GAME]: getAnticipationTrigger(SPIN_TYPE.BASE_GAME),\n [SPIN_TYPE.FREE_SPINS]: getAnticipationTrigger(SPIN_TYPE.FREE_SPINS),\n },\n outputDir: \"__build__\",\n rootDir: opts.rootDir || process.cwd(),\n }\n}\n\nexport type GameConfig<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, \"symbols\">> & {\n /**\n * A map of all symbols.\n */\n symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>\n outputDir: string\n rootDir: string\n /**\n * A mapping of spin types to the number of scatter symbols required to trigger anticipation.\n */\n anticipationTriggers: Record<(typeof SPIN_TYPE)[keyof typeof SPIN_TYPE], number>\n}\n","import fs from \"fs\"\nimport path from \"path\"\nimport assert from \"assert\"\nimport zlib from \"zlib\"\nimport readline from \"readline\"\nimport { buildSync } from \"esbuild\"\nimport { Worker, isMainThread, parentPort, workerData } from \"worker_threads\"\nimport { createGameConfig, GameConfigOptions, GameConfig } from \"../game-config\"\nimport { createGameContext, GameContext } from \"../game-context\"\nimport { createDirIfNotExists, JSONL, writeFile } from \"../../utils\"\nimport { SPIN_TYPE } from \"../constants\"\nimport { Book } from \"../book\"\nimport { Recorder, RecordItem } from \"../recorder\"\nimport { Wallet } from \"../wallet\"\nimport { ResultSet } from \"../result-set\"\nimport { pipeline } from \"stream/promises\"\n\nlet completedSimulations = 0\nconst TEMP_FILENAME = \"__temp_compiled_src_IGNORE.js\"\nconst TEMP_FOLDER = \"temp_files\"\n\nexport class Simulation {\n readonly gameConfigOpts: GameConfigOptions\n readonly gameConfig: GameConfig\n readonly simRunsAmount: Partial<Record<string, number>>\n readonly concurrency: number\n private debug = false\n private actualSims = 0\n private library: Map<number, Book>\n private wallet: Wallet\n private recordsWriteStream: fs.WriteStream | undefined\n private hasWrittenRecord = false\n\n constructor(opts: SimulationOptions, gameConfigOpts: GameConfigOptions) {\n this.gameConfig = createGameConfig(gameConfigOpts)\n this.gameConfigOpts = gameConfigOpts\n this.simRunsAmount = opts.simRunsAmount || {}\n this.concurrency = (opts.concurrency || 6) >= 2 ? opts.concurrency || 6 : 2\n this.library = new Map()\n this.wallet = new Wallet()\n\n const gameModeKeys = Object.keys(this.gameConfig.gameModes)\n assert(\n Object.values(this.gameConfig.gameModes)\n .map((m) => gameModeKeys.includes(m.name))\n .every((v) => v === true),\n \"Game mode name must match its key in the gameModes object.\",\n )\n\n if (isMainThread) {\n this.preprocessFiles()\n }\n }\n\n async runSimulation(opts: SimulationConfigOptions) {\n const debug = opts.debug || false\n this.debug = debug\n\n const gameModesToSimulate = Object.keys(this.simRunsAmount)\n const configuredGameModes = Object.keys(this.gameConfig.gameModes)\n\n if (gameModesToSimulate.length === 0) {\n throw new Error(\"No game modes configured for simulation.\")\n }\n\n this.generateReelsetFiles()\n\n // Code that runs when the user executes the simulations.\n // This spawns individual processes and merges the results afterwards.\n if (isMainThread) {\n const debugDetails: Record<string, Record<string, any>> = {}\n\n for (const mode of gameModesToSimulate) {\n completedSimulations = 0\n this.wallet = new Wallet()\n this.library = new Map()\n this.hasWrittenRecord = false\n\n debugDetails[mode] = {}\n\n console.log(`\\nSimulating game mode: ${mode}`)\n console.time(mode)\n\n const runs = this.simRunsAmount[mode] || 0\n\n if (runs <= 0) continue\n\n if (!configuredGameModes.includes(mode)) {\n throw new Error(\n `Tried to simulate game mode \"${mode}\", but it's not configured in the game config.`,\n )\n }\n\n const booksPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n `books_${mode}.jsonl`,\n )\n\n const tempRecordsPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FOLDER,\n `temp_records_${mode}.jsonl`,\n )\n\n createDirIfNotExists(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir),\n )\n createDirIfNotExists(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, TEMP_FOLDER),\n )\n\n this.recordsWriteStream = fs.createWriteStream(tempRecordsPath)\n\n const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode)\n\n await this.spawnWorkersForGameMode({ mode, simNumsToCriteria })\n\n // Merge temporary book files into the final sorted file\n const finalBookStream = fs.createWriteStream(booksPath)\n const numSims = Object.keys(simNumsToCriteria).length\n const chunks = this.getSimRangesForChunks(numSims, this.concurrency!)\n\n let isFirstChunk = true\n for (let i = 0; i < chunks.length; i++) {\n const tempBookPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FOLDER,\n `temp_books_${mode}_${i}.jsonl`,\n )\n\n if (fs.existsSync(tempBookPath)) {\n if (!isFirstChunk) {\n finalBookStream.write(\"\\n\")\n }\n const content = fs.createReadStream(tempBookPath)\n for await (const chunk of content) {\n finalBookStream.write(chunk)\n }\n fs.rmSync(tempBookPath)\n isFirstChunk = false\n }\n }\n finalBookStream.end()\n await new Promise<void>((resolve) => finalBookStream.on(\"finish\", resolve))\n\n if (this.recordsWriteStream) {\n await new Promise<void>((resolve) => {\n this.recordsWriteStream!.end(() => {\n resolve()\n })\n })\n this.recordsWriteStream = undefined\n }\n\n createDirIfNotExists(\n path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"optimization_files\",\n ),\n )\n createDirIfNotExists(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, \"publish_files\"),\n )\n\n console.log(`Writing final files for game mode: ${mode} ...`)\n this.writeLookupTableCSV(mode)\n this.writeLookupTableSegmentedCSV(mode)\n this.writeRecords(mode)\n await this.writeBooksJson(mode)\n this.writeIndexJson()\n console.log(`Mode ${mode} done!`)\n\n debugDetails[mode].rtp =\n this.wallet.getCumulativeWins() / (runs * this.gameConfig.gameModes[mode]!.cost)\n\n debugDetails[mode].wins = this.wallet.getCumulativeWins()\n debugDetails[mode].winsPerSpinType = this.wallet.getCumulativeWinsPerSpinType()\n\n console.timeEnd(mode)\n }\n\n console.log(\"\\n=== SIMULATION SUMMARY ===\")\n console.table(debugDetails)\n }\n\n let desiredSims = 0\n let actualSims = 0\n const criteriaToRetries: Record<string, number> = {}\n\n // Code that runs for individual processes\n if (!isMainThread) {\n const { mode, simStart, simEnd, index } = workerData\n\n const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode)\n\n // Run each simulation until the criteria is met.\n for (let simId = simStart; simId <= simEnd; simId++) {\n if (this.debug) desiredSims++\n\n const criteria = simNumsToCriteria[simId] || \"N/A\"\n\n if (!criteriaToRetries[criteria]) {\n criteriaToRetries[criteria] = 0\n }\n\n this.runSingleSimulation({ simId, mode, criteria, index })\n\n if (this.debug) {\n criteriaToRetries[criteria] += this.actualSims - 1\n actualSims += this.actualSims\n }\n }\n\n if (this.debug) {\n console.log(`Desired ${desiredSims}, Actual ${actualSims}`)\n console.log(`Retries per criteria:`, criteriaToRetries)\n }\n\n parentPort?.postMessage({\n type: \"done\",\n workerNum: index,\n })\n }\n }\n\n /**\n * Runs all simulations for a specific game mode.\n */\n async spawnWorkersForGameMode(opts: {\n mode: string\n simNumsToCriteria: Record<number, string>\n }) {\n const { mode, simNumsToCriteria } = opts\n\n const numSims = Object.keys(simNumsToCriteria).length\n const simRangesPerChunk = this.getSimRangesForChunks(numSims, this.concurrency!)\n\n await Promise.all(\n simRangesPerChunk.map(([simStart, simEnd], index) => {\n return this.callWorker({\n basePath: path.join(this.gameConfig.rootDir, this.gameConfig.outputDir),\n mode,\n simStart,\n simEnd,\n index,\n totalSims: numSims,\n })\n }),\n )\n }\n\n async callWorker(opts: {\n basePath: string\n mode: string\n simStart: number\n simEnd: number\n index: number\n totalSims: number\n }) {\n const { mode, simEnd, simStart, basePath, index, totalSims } = opts\n\n function logArrowProgress(current: number, total: number) {\n const percentage = (current / total) * 100\n const progressBarLength = 50\n const filledLength = Math.round((progressBarLength * current) / total)\n const bar = \"█\".repeat(filledLength) + \"-\".repeat(progressBarLength - filledLength)\n process.stdout.write(`\\r[${bar}] ${percentage.toFixed(2)}% (${current}/${total})`)\n if (current === total) {\n process.stdout.write(\"\\n\")\n }\n }\n\n return new Promise((resolve, reject) => {\n const scriptPath = path.join(basePath, TEMP_FILENAME)\n\n const worker = new Worker(scriptPath, {\n workerData: {\n mode,\n simStart,\n simEnd,\n index,\n },\n })\n\n const tempBookPath = path.join(\n basePath,\n TEMP_FOLDER,\n `temp_books_${mode}_${index}.jsonl`,\n )\n const bookStream = fs.createWriteStream(tempBookPath)\n\n worker.on(\"message\", (msg) => {\n if (msg.type === \"log\") {\n } else if (msg.type === \"complete\") {\n completedSimulations++\n\n if (completedSimulations % 250 === 0) {\n logArrowProgress(completedSimulations, totalSims)\n }\n\n // Write data to global library\n const book = Book.fromSerialized(msg.book)\n\n const bookData = {\n id: book.id,\n payoutMultiplier: book.payout,\n events: book.events,\n }\n\n const prefix = book.id === simStart ? \"\" : \"\\n\"\n bookStream.write(prefix + JSONL.stringify([bookData]))\n\n book.events = []\n this.library.set(book.id, book)\n\n if (this.recordsWriteStream) {\n for (const record of msg.records) {\n const recordPrefix = this.hasWrittenRecord ? \"\\n\" : \"\"\n this.recordsWriteStream.write(recordPrefix + JSONL.stringify([record]))\n this.hasWrittenRecord = true\n }\n }\n\n this.wallet.mergeSerialized(msg.wallet)\n } else if (msg.type === \"done\") {\n resolve(true)\n }\n })\n\n worker.on(\"error\", (error) => {\n process.stdout.write(`\\n${error.message}\\n`)\n process.stdout.write(`\\n${error.stack}\\n`)\n reject(error)\n })\n\n worker.on(\"exit\", (code) => {\n if (code !== 0) {\n reject(new Error(`Worker stopped with exit code ${code}`))\n }\n })\n })\n }\n\n /**\n * Will run a single simulation until the specified criteria is met.\n */\n runSingleSimulation(opts: {\n simId: number\n mode: string\n criteria: string\n index: number\n }) {\n const { simId, mode, criteria } = opts\n\n const ctx = createGameContext({\n config: this.gameConfig,\n })\n\n ctx.state.currentGameMode = mode\n ctx.state.currentSimulationId = simId\n ctx.state.isCriteriaMet = false\n\n const resultSet = ctx.services.game.getResultSetByCriteria(\n ctx.state.currentGameMode,\n criteria,\n )\n\n ctx.state.currentResultSet = resultSet\n\n while (!ctx.state.isCriteriaMet) {\n this.actualSims++\n this.resetSimulation(ctx)\n\n this.handleGameFlow(ctx)\n\n if (resultSet.meetsCriteria(ctx)) {\n ctx.state.isCriteriaMet = true\n }\n }\n\n ctx.services.wallet._getWallet().writePayoutToBook(ctx)\n ctx.services.wallet._getWallet().confirmWins(ctx)\n\n if (ctx.services.data._getBook().payout >= ctx.config.maxWinX) {\n ctx.state.triggeredMaxWin = true\n }\n\n ctx.services.data.record({\n criteria: resultSet.criteria,\n })\n\n ctx.config.hooks.onSimulationAccepted?.(ctx)\n\n this.confirmRecords(ctx)\n\n parentPort?.postMessage({\n type: \"complete\",\n simId,\n book: ctx.services.data._getBook().serialize(),\n wallet: ctx.services.wallet._getWallet().serialize(),\n records: ctx.services.data._getRecords(),\n })\n }\n\n /**\n * If a simulation does not meet the required criteria, reset the state to run it again.\n *\n * This also runs once before each simulation to ensure a clean state.\n */\n protected resetSimulation(ctx: GameContext) {\n this.resetState(ctx)\n ctx.services.board.resetBoard()\n ctx.services.data._setRecorder(new Recorder())\n ctx.services.wallet._setWallet(new Wallet())\n ctx.services.data._setBook(\n new Book({\n id: ctx.state.currentSimulationId,\n criteria: ctx.state.currentResultSet.criteria,\n }),\n )\n }\n\n protected resetState(ctx: GameContext) {\n ctx.services.rng.setSeedIfDifferent(ctx.state.currentSimulationId)\n ctx.state.currentSpinType = SPIN_TYPE.BASE_GAME\n ctx.state.currentFreespinAmount = 0\n ctx.state.totalFreespinAmount = 0\n ctx.state.triggeredMaxWin = false\n ctx.state.triggeredFreespins = false\n ctx.state.userData = ctx.config.userState || {}\n }\n\n /**\n * Contains and executes the entire game logic:\n * - Drawing the board\n * - Evaluating wins\n * - Updating wallet\n * - Handling free spins\n * - Recording events\n *\n * You can customize the game flow by implementing the `onHandleGameFlow` hook in the game configuration.\n */\n protected handleGameFlow(ctx: GameContext) {\n this.gameConfig.hooks.onHandleGameFlow(ctx)\n }\n\n /**\n * Creates a CSV file in the format \"simulationId,weight,payout\".\n *\n * `weight` defaults to 1.\n */\n private writeLookupTableCSV(gameMode: string) {\n const rows: string[] = []\n\n for (const [bookId, book] of this.library.entries()) {\n rows.push(`${book.id},1,${Math.round(book.payout)}`)\n }\n\n rows.sort((a, b) => Number(a.split(\",\")[0]) - Number(b.split(\",\")[0]))\n\n let outputFileName = `lookUpTable_${gameMode}.csv`\n let outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n outputFileName,\n )\n writeFile(outputFilePath, rows.join(\"\\n\"))\n\n outputFileName = `lookUpTable_${gameMode}_0.csv`\n outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"publish_files\",\n outputFileName,\n )\n writeFile(outputFilePath, rows.join(\"\\n\"))\n\n return outputFilePath\n }\n\n /**\n * Creates a CSV file in the format \"simulationId,criteria,payoutBase,payoutFreespins\".\n */\n private writeLookupTableSegmentedCSV(gameMode: string) {\n const rows: string[] = []\n\n for (const [bookId, book] of this.library.entries()) {\n rows.push(`${book.id},${book.criteria},${book.basegameWins},${book.freespinsWins}`)\n }\n\n rows.sort((a, b) => Number(a.split(\",\")[0]) - Number(b.split(\",\")[0]))\n\n const outputFileName = `lookUpTableSegmented_${gameMode}.csv`\n\n const outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n outputFileName,\n )\n writeFile(outputFilePath, rows.join(\"\\n\"))\n\n return outputFilePath\n }\n\n private async writeRecords(mode: string) {\n const tempRecordsPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FOLDER,\n `temp_records_${mode}.jsonl`,\n )\n\n const forceRecordsPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n `force_record_${mode}.json`,\n )\n\n // Use a local Map to aggregate records efficiently without cluttering the main Recorder\n // Key is the stringified search criteria\n const aggregatedRecords = new Map<string, RecordItem>()\n\n if (fs.existsSync(tempRecordsPath)) {\n const fileStream = fs.createReadStream(tempRecordsPath)\n\n const rl = readline.createInterface({\n input: fileStream,\n crlfDelay: Infinity,\n })\n\n for await (const line of rl) {\n if (line.trim() === \"\") continue\n const record: RecordItem = JSON.parse(line)\n\n const key = JSON.stringify(record.search)\n\n let existing = aggregatedRecords.get(key)\n if (!existing) {\n existing = {\n search: record.search,\n timesTriggered: 0,\n bookIds: [],\n }\n aggregatedRecords.set(key, existing)\n }\n\n existing.timesTriggered += record.timesTriggered\n\n for (const bookId of record.bookIds) {\n existing.bookIds.push(bookId)\n }\n }\n }\n\n fs.rmSync(forceRecordsPath, { force: true })\n\n const writeStream = fs.createWriteStream(forceRecordsPath, { encoding: \"utf-8\" })\n writeStream.write(\"[\\n\")\n\n let isFirst = true\n for (const record of aggregatedRecords.values()) {\n if (!isFirst) {\n writeStream.write(\",\\n\")\n }\n writeStream.write(JSON.stringify(record))\n isFirst = false\n }\n\n writeStream.write(\"\\n]\")\n writeStream.end()\n\n await new Promise<void>((resolve) => {\n writeStream.on(\"finish\", () => resolve())\n })\n\n fs.rmSync(tempRecordsPath, { force: true })\n }\n\n private writeIndexJson() {\n const outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"publish_files\",\n \"index.json\",\n )\n\n const modes = Object.keys(this.simRunsAmount).map((id) => {\n const mode = this.gameConfig.gameModes[id]\n assert(mode, `Game mode \"${id}\" not found in game config.`)\n\n return {\n name: mode.name,\n cost: mode.cost,\n events: `books_${mode.name}.jsonl.zst`,\n weights: `lookUpTable_${mode.name}_0.csv`,\n }\n })\n\n writeFile(outputFilePath, JSON.stringify({ modes }, null, 2))\n }\n\n private async writeBooksJson(gameMode: string) {\n const outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n `books_${gameMode}.jsonl`,\n )\n\n const compressedFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"publish_files\",\n `books_${gameMode}.jsonl.zst`,\n )\n\n fs.rmSync(compressedFilePath, { force: true })\n\n if (fs.existsSync(outputFilePath)) {\n await pipeline(\n fs.createReadStream(outputFilePath),\n zlib.createZstdCompress(),\n fs.createWriteStream(compressedFilePath),\n )\n }\n }\n\n /**\n * Compiles user configured game to JS for use in different Node processes\n */\n private preprocessFiles() {\n const builtFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FILENAME,\n )\n fs.rmSync(builtFilePath, { force: true })\n buildSync({\n entryPoints: [this.gameConfig.rootDir],\n bundle: true,\n platform: \"node\",\n outfile: path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FILENAME,\n ),\n external: [\"esbuild\"],\n })\n }\n\n private getSimRangesForChunks(total: number, chunks: number): [number, number][] {\n const base = Math.floor(total / chunks)\n const remainder = total % chunks\n const result: [number, number][] = []\n\n let current = 1\n\n for (let i = 0; i < chunks; i++) {\n const size = base + (i < remainder ? 1 : 0)\n const start = current\n const end = current + size - 1\n result.push([start, end])\n current = end + 1\n }\n\n return result\n }\n\n /**\n * Generates reelset CSV files for all game modes.\n */\n private generateReelsetFiles() {\n for (const mode of Object.values(this.gameConfig.gameModes)) {\n if (mode.reelSets && mode.reelSets.length > 0) {\n for (const reelSet of Object.values(mode.reelSets)) {\n reelSet.associatedGameModeName = mode.name\n reelSet.generateReels(this.gameConfig)\n }\n } else {\n throw new Error(\n `Game mode \"${mode.name}\" has no reel sets defined. Cannot generate reelset files.`,\n )\n }\n }\n }\n\n /**\n * Confirms all pending records and adds them to the main records list.\n */\n confirmRecords(ctx: GameContext) {\n const recorder = ctx.services.data._getRecorder()\n\n for (const pendingRecord of recorder.pendingRecords) {\n const search = Object.entries(pendingRecord.properties)\n .map(([name, value]) => ({ name, value }))\n .sort((a, b) => a.name.localeCompare(b.name))\n\n let record = recorder.records.find((r) => {\n if (r.search.length !== search.length) return false\n for (let i = 0; i < r.search.length; i++) {\n if (r.search[i]!.name !== search[i]!.name) return false\n if (r.search[i]!.value !== search[i]!.value) return false\n }\n return true\n })\n if (!record) {\n record = {\n search,\n timesTriggered: 0,\n bookIds: [],\n }\n recorder.records.push(record)\n }\n record.timesTriggered++\n if (!record.bookIds.includes(pendingRecord.bookId)) {\n record.bookIds.push(pendingRecord.bookId)\n }\n }\n\n recorder.pendingRecords = []\n }\n}\n\nexport type SimulationOptions = {\n /**\n * Object containing the game modes and their respective simulation runs amount.\n */\n simRunsAmount: Partial<Record<string, number>>\n /**\n * Number of concurrent processes to use for simulations.\n *\n * Default: 6\n */\n concurrency?: number\n}\n\nexport type SimulationConfigOptions = {\n debug?: boolean\n}\n","import assert from \"assert\"\nimport { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\nimport { GameContext } from \"../game-context\"\nimport { Simulation } from \"../simulation\"\nimport { RandomNumberGenerator } from \"../service/rng\"\nimport { copy } from \"../../utils\"\nimport { Wallet } from \"../wallet\"\nimport { SPIN_TYPE } from \"../constants\"\n\nexport class ResultSet<TUserState extends AnyUserData> {\n criteria: string\n quota: number\n multiplier?: number\n reelWeights: ReelWeights<TUserState>\n userData?: Record<string, any>\n forceMaxWin?: boolean\n forceFreespins?: boolean\n evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => boolean\n\n constructor(opts: ResultSetOpts<TUserState>) {\n this.criteria = opts.criteria\n this.quota = opts.quota\n this.multiplier = opts.multiplier\n this.reelWeights = opts.reelWeights\n this.userData = opts.userData\n this.forceMaxWin = opts.forceMaxWin\n this.forceFreespins = opts.forceFreespins\n this.evaluate = opts.evaluate\n }\n\n static assignCriteriaToSimulations(ctx: Simulation, gameModeName: string) {\n const rng = new RandomNumberGenerator()\n rng.setSeed(0)\n\n assert(ctx.simRunsAmount, \"Simulation configuration is not set.\")\n\n const simNums = ctx.simRunsAmount[gameModeName]\n const resultSets = ctx.gameConfig.gameModes[gameModeName]?.resultSets\n\n if (!resultSets || resultSets.length === 0) {\n throw new Error(`No ResultSets found for game mode: ${gameModeName}.`)\n }\n\n if (simNums === undefined || simNums <= 0) {\n throw new Error(`No simulations configured for game mode \"${gameModeName}\".`)\n }\n\n const totalQuota = resultSets.reduce((sum, rs) => sum + rs.quota, 0)\n\n const numberOfSimsForCriteria: Record<string, number> = Object.fromEntries(\n resultSets.map((rs) => {\n const normalizedQuota = totalQuota > 0 ? rs.quota / totalQuota : 0\n return [rs.criteria, Math.max(Math.floor(normalizedQuota * simNums), 1)]\n }),\n )\n\n let totalSims = Object.values(numberOfSimsForCriteria).reduce(\n (sum, num) => sum + num,\n 0,\n )\n\n let reduceSims = totalSims > simNums\n\n const criteriaToWeights = Object.fromEntries(\n resultSets.map((rs) => [rs.criteria, rs.quota]),\n )\n\n while (totalSims != simNums) {\n const rs = rng.weightedRandom(criteriaToWeights)\n if (reduceSims && numberOfSimsForCriteria[rs]! > 1) {\n numberOfSimsForCriteria[rs]! -= 1\n } else if (!reduceSims) {\n numberOfSimsForCriteria[rs]! += 1\n }\n\n totalSims = Object.values(numberOfSimsForCriteria).reduce(\n (sum, num) => sum + num,\n 0,\n )\n reduceSims = totalSims > simNums\n }\n\n let allCriteria: string[] = []\n const simNumsToCriteria: Record<number, string> = {}\n\n Object.entries(numberOfSimsForCriteria).forEach(([criteria, num]) => {\n for (let i = 0; i <= num; i++) {\n allCriteria.push(criteria)\n }\n })\n\n allCriteria = rng.shuffle(allCriteria)\n\n for (let i = 1; i <= Math.min(simNums, allCriteria.length); i++) {\n simNumsToCriteria[i] = allCriteria[i]!\n }\n\n return simNumsToCriteria\n }\n\n /**\n * Checks if core criteria is met, e.g. target multiplier or max win.\n */\n meetsCriteria(ctx: GameContext) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n const customEval = this.evaluate?.(copy(ctx))\n\n const freespinsMet = this.forceFreespins ? ctx.state.triggeredFreespins : true\n\n const wallet = ctx.services.wallet._getWallet()\n\n const multiplierMet =\n this.multiplier !== undefined\n ? wallet.getCurrentWin() === this.multiplier && !this.forceMaxWin\n : wallet.getCurrentWin() > 0 && (!this.forceMaxWin || true)\n\n const maxWinMet = this.forceMaxWin\n ? wallet.getCurrentWin() >= ctx.config.maxWinX\n : true\n\n const coreCriteriaMet = freespinsMet && multiplierMet && maxWinMet\n\n const finalResult =\n customEval !== undefined ? coreCriteriaMet && customEval === true : coreCriteriaMet\n\n if (this.forceMaxWin && maxWinMet) {\n ctx.services.data.record({\n maxwin: true,\n })\n }\n\n return finalResult\n }\n}\n\ninterface ResultSetOpts<TUserState extends AnyUserData> {\n /**\n * A short string to describe the criteria for this ResultSet.\n */\n criteria: string\n /**\n * The quota of spins, out of the total simulations, that must be forced to meet the specified criteria.\\\n * **Float from 0 to 1. Total quota of all ResultSets in a GameMode must be 1.**\n */\n quota: number\n /**\n * The required multiplier for a simulated spin to be accepted.\n */\n multiplier?: number\n /**\n * Configure the weights of the reels in this ResultSet.\n *\n * If you need to support dynamic / special reel weights based on the simulation context,\\\n * you can provide an `evaluate` function that returns the desired weights.\n *\n * If the `evaluate` function returns a falsy value, the usual spin type based weights will be used.\n *\n * @example\n * ```ts\n * new ResultSet({\n * criteria: \"superFreespins\",\n * quota: 0.05,\n * forceFreespins: true,\n * reelWeights: {\n * [SPIN_TYPE.BASE_GAME]: { base1: 1 },\n * [SPIN_TYPE.FREE_SPINS]: { bonus1: 1, bonus2: 2 },\n * evaluate: (ctx) => {\n * if (ctx.state.userData.triggeredSuperFreespins) {\n * return { superbonus: 1 }\n * }\n * }\n * },\n * userData: { forceSuperFreespins: true },\n * }),\n * ```\n */\n reelWeights: ReelWeights<TUserState>\n /**\n * Optional data to use when evaluating the criteria.\\\n * This can be used to pass additional context or parameters needed for the evaluation.\n */\n userData?: Record<string, any>\n /**\n * If set, this will force the game to always trigger a max win.\n */\n forceMaxWin?: boolean\n /**\n * If set, this will force the game to always trigger free spins.\n */\n forceFreespins?: boolean\n /**\n * Custom function to evaluate if the criteria is met.\n *\n * E.g. use this to check for free spins that upgraded to super free spins\\\n * or other arbitrary simulation criteria.\n */\n evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => boolean\n}\n\ninterface ReelWeights<TUserState extends AnyUserData> {\n [SPIN_TYPE.BASE_GAME]: Record<string, number>\n [SPIN_TYPE.FREE_SPINS]: Record<string, number>\n evaluate?: (\n ctx: GameContext<AnyGameModes, AnySymbols, TUserState>,\n ) => Record<string, number> | undefined | null | false\n}\n","import { GameContext } from \"../game-context\"\n\nexport class AbstractService {\n /**\n * Function that returns the current game context.\n */\n protected ctx: () => GameContext\n\n constructor(ctx: () => GameContext) {\n this.ctx = ctx\n }\n}\n","import { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\n\nexport class RngService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n protected rng = new RandomNumberGenerator()\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n /**\n * Random weighted selection from a set of items.\n */\n weightedRandom = this.rng.weightedRandom.bind(this.rng)\n\n /**\n * Selects a random item from an array.\n */\n randomItem = this.rng.randomItem.bind(this.rng)\n\n /**\n * Shuffles an array.\n */\n shuffle = this.rng.shuffle.bind(this.rng)\n\n /**\n * Generates a random float between two values.\n */\n randomFloat = this.rng.randomFloat.bind(this.rng)\n\n /**\n * Sets the seed for the RNG.\n */\n setSeedIfDifferent = this.rng.setSeedIfDifferent.bind(this.rng)\n}\n\nexport class RandomNumberGenerator {\n mIdum: number\n mIy: number\n mIv: Array<number>\n NTAB: number\n IA: number\n IM: number\n IQ: number\n IR: number\n NDIV: number\n AM: number\n RNMX: number\n\n protected _currentSeed: number = 0\n\n constructor() {\n this.mIdum = 0\n this.mIy = 0\n this.mIv = []\n\n this.NTAB = 32\n this.IA = 16807\n this.IM = 2147483647\n this.IQ = 127773\n this.IR = 2836\n this.NDIV = 1 + (this.IM - 1) / this.NTAB\n this.AM = 1.0 / this.IM\n this.RNMX = 1.0 - 1.2e-7\n }\n\n getCurrentSeed() {\n return this._currentSeed\n }\n\n protected setCurrentSeed(seed: number) {\n this._currentSeed = seed\n }\n\n setSeed(seed: number): void {\n this.mIdum = seed\n this.setCurrentSeed(seed)\n\n if (seed >= 0) {\n this.mIdum = -seed\n }\n\n this.mIy = 0\n }\n\n setSeedIfDifferent(seed: number) {\n if (this.getCurrentSeed() !== seed) {\n this.setSeed(seed)\n }\n }\n\n generateRandomNumber(): number {\n let k: number\n let j: number\n\n if (this.mIdum <= 0 || this.mIy === 0) {\n if (-this.mIdum < 1) {\n this.mIdum = 1\n } else {\n this.mIdum = -this.mIdum\n }\n\n for (j = this.NTAB + 7; j >= 0; j -= 1) {\n k = Math.floor(this.mIdum / this.IQ)\n this.mIdum = Math.floor(this.IA * (this.mIdum - k * this.IQ) - this.IR * k)\n\n if (this.mIdum < 0) {\n this.mIdum += this.IM\n }\n\n if (j < this.NTAB) {\n this.mIv[j] = this.mIdum\n }\n }\n\n ;[this.mIy as any] = this.mIv\n }\n\n k = Math.floor(this.mIdum / this.IQ)\n this.mIdum = Math.floor(this.IA * (this.mIdum - k * this.IQ) - this.IR * k)\n\n if (this.mIdum < 0) {\n this.mIdum += this.IM\n }\n\n j = Math.floor(this.mIy / this.NDIV)\n\n this.mIy = Math.floor(this.mIv[j] as any)\n this.mIv[j] = this.mIdum\n\n return this.mIy\n }\n\n randomFloat(low: number, high: number): number {\n let float: number = this.AM * this.generateRandomNumber()\n\n if (float > this.RNMX) {\n float = this.RNMX\n }\n\n return float * (high - low) + low\n }\n\n weightedRandom<T extends Record<string, number>>(weights: T) {\n const totalWeight = Object.values(weights).reduce(\n (sum: number, weight) => sum + (weight as number),\n 0,\n )\n const randomValue = this.randomFloat(0, 1) * totalWeight\n\n let cumulativeWeight = 0\n for (const [key, weight] of Object.entries(weights)) {\n cumulativeWeight += weight as number\n if (randomValue < cumulativeWeight) {\n return key\n }\n }\n\n throw new Error(\"No item selected in weighted random selection.\")\n }\n\n randomItem<T>(array: T[]) {\n if (array.length === 0) {\n throw new Error(\"Cannot select a random item from an empty array.\")\n }\n const randomIndex = Math.floor(this.randomFloat(0, 1) * array.length)\n return array[randomIndex]!\n }\n\n shuffle<T>(array: T[]): T[] {\n const newArray = [...array]\n let currentIndex = newArray.length,\n randomIndex\n\n while (currentIndex != 0) {\n randomIndex = Math.floor(this.randomFloat(0, 1) * currentIndex)\n currentIndex--\n ;[newArray[currentIndex] as any, newArray[randomIndex] as any] = [\n newArray[randomIndex],\n newArray[currentIndex],\n ]\n }\n\n return newArray\n }\n}\n","import fs from \"fs\"\nimport readline from \"readline\"\nimport { BoardService } from \"./src/service/board\"\n\nexport function createDirIfNotExists(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true })\n }\n}\n\nexport function writeJsonFile(filePath: string, data: object | any[]) {\n try {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), {\n encoding: \"utf8\",\n })\n } catch (error) {\n throw new Error(`Failed to write JSON file at ${filePath}: ${error}`)\n }\n}\n\nexport function writeFile(filePath: string, data: string) {\n try {\n fs.writeFileSync(filePath, data, { encoding: \"utf8\" })\n } catch (error) {\n throw new Error(`Failed to write file at ${filePath}: ${error}`)\n }\n}\n\n/**\n * Creates a deep copy of an object or array.\n */\nexport function copy<T>(obj: T): T {\n return JSON.parse(JSON.stringify(obj))\n}\n\n/**\n * Prints the board to the console in a readable format.\n */\nexport function printBoard(board: BoardService) {\n const fullBoard = board.getBoardReels().map((reel, ridx) => {\n return [...board.getPaddingTop()[ridx]!, ...reel, ...board.getPaddingBottom()[ridx]!]\n })\n\n const rows = Math.max(...fullBoard.map((reel) => reel.length))\n const cellWidth = 4 // inner width of symbol area\n\n const padSymbol = (sym: string) => {\n if (sym.length > cellWidth) sym = sym.slice(0, cellWidth)\n const left = Math.floor((cellWidth - sym.length) / 2)\n const right = cellWidth - sym.length - left\n return \" \".repeat(left) + sym + \" \".repeat(right)\n }\n\n const maxTop = Math.max(...board.getPaddingTop().map((p) => p?.length ?? 0))\n const maxBottom = Math.max(...board.getPaddingBottom().map((p) => p?.length ?? 0))\n const boardStart = maxTop\n const boardEnd = rows - maxBottom - 1\n\n const makeSeparator = () => {\n return fullBoard.map(() => `═${\"═\".repeat(cellWidth)}═ `).join(\"\")\n }\n\n for (let row = 0; row < rows; row++) {\n if (row === boardStart) {\n console.log(makeSeparator()) // top border of board\n }\n\n let top = \"\"\n let mid = \"\"\n let bot = \"\"\n for (let col = 0; col < fullBoard.length; col++) {\n const sym = fullBoard[col]![row]?.id ?? \" \"\n const padded = padSymbol(sym)\n top += `┌${\"─\".repeat(cellWidth)}┐ `\n mid += `│${padded}│ `\n bot += `└${\"─\".repeat(cellWidth)}┘ `\n }\n\n console.log(top)\n console.log(mid)\n console.log(bot)\n\n if (row === boardEnd) {\n console.log(makeSeparator()) // bottom border of board\n }\n }\n}\n\nexport function weightedAverage(dist: Record<number, number>) {\n const keys = Object.keys(dist).map(Number)\n const values = Object.values(dist)\n\n const totalWeight = round(\n values.reduce((a, b) => a + b, 0),\n 6,\n )\n const weightedSum = keys.reduce((sum, key, i) => sum + key * values[i]!, 0)\n\n return weightedSum / totalWeight\n}\n\nexport class JSONL {\n public static stringify(array: object[]): string {\n return array.map((object) => JSON.stringify(object)).join(\"\\n\")\n }\n\n public static parse<T>(jsonl: string): Array<T> {\n return jsonl\n .split(\"\\n\")\n .filter((s) => s !== \"\")\n .map((str) => JSON.parse(str))\n }\n\n public static async convertToJson(\n inputPath: string,\n outputPath: string,\n ): Promise<void> {\n const writeStream = fs.createWriteStream(outputPath, { encoding: \"utf-8\" })\n writeStream.write(\"[\\n\")\n\n const rl = readline.createInterface({\n input: fs.createReadStream(inputPath),\n crlfDelay: Infinity,\n })\n\n let isFirst = true\n\n for await (const line of rl) {\n if (line.trim() === \"\") continue\n if (!isFirst) {\n writeStream.write(\",\\n\")\n }\n writeStream.write(line)\n isFirst = false\n }\n\n writeStream.write(\"\\n]\")\n writeStream.end()\n\n return new Promise<void>((resolve, reject) => {\n writeStream.on(\"finish\", () => resolve())\n writeStream.on(\"error\", reject)\n })\n }\n}\n\nexport function round(value: number, decimals: number) {\n return Number(Math.round(Number(value + \"e\" + decimals)) + \"e-\" + decimals)\n}\n","import { AnyUserData, SpinType } from \"../types\"\nimport { SPIN_TYPE } from \"../constants\"\nimport { ResultSet } from \"../result-set\"\n\nexport interface GameStateOptions<TUserState extends AnyUserData> {\n currentSimulationId: number\n /**\n * e.g. \"base\", \"freespins\", etc. (depending on the game config)\n */\n currentGameMode: string\n /**\n * Spin type constant as defined in `SPIN_TYPE`\n */\n currentSpinType: SpinType\n /**\n * The current ResultSet for the active simulation run.\n */\n currentResultSet: ResultSet<any>\n /**\n * Whether the criteria in the ResultSet for the current simulation has been met.\n */\n isCriteriaMet: boolean\n /**\n * Number of freespins remaining in the current freespin round.\n */\n currentFreespinAmount: number\n /**\n * Total amount of freespins awarded during the active simulation.\n */\n totalFreespinAmount: number\n /**\n * Custom user data that can be used in game flow logic.\n */\n userData: TUserState\n /**\n * Whether a max win has been triggered during the active simulation.\n */\n triggeredMaxWin: boolean\n /**\n * Whether freespins have been triggered during the active simulation.\n */\n triggeredFreespins: boolean\n}\n\nexport function createGameState<TUserState extends AnyUserData = AnyUserData>(\n opts?: Partial<GameStateOptions<TUserState>>,\n) {\n return {\n currentSimulationId: opts?.currentSimulationId || 0,\n currentGameMode: opts?.currentGameMode || \"N/A\",\n currentSpinType: opts?.currentSpinType || SPIN_TYPE.BASE_GAME,\n currentResultSet:\n opts?.currentResultSet ||\n new ResultSet({\n criteria: \"N/A\",\n quota: 0,\n reelWeights: {\n [SPIN_TYPE.BASE_GAME]: {},\n [SPIN_TYPE.FREE_SPINS]: {},\n },\n }),\n isCriteriaMet: opts?.isCriteriaMet || false,\n currentFreespinAmount: opts?.currentFreespinAmount || 0,\n totalFreespinAmount: opts?.totalFreespinAmount || 0,\n userData: opts?.userData || ({} as TUserState),\n triggeredMaxWin: opts?.triggeredMaxWin || false,\n triggeredFreespins: opts?.triggeredFreespins || false,\n }\n}\n\nexport type GameState<TUserState extends AnyUserData = AnyUserData> = ReturnType<\n typeof createGameState<TUserState>\n>\n","export class Recorder {\n records: RecordItem[]\n pendingRecords: PendingRecord[]\n\n constructor() {\n this.records = []\n this.pendingRecords = []\n }\n}\n\nexport interface PendingRecord {\n bookId: number\n properties: Record<string, string>\n}\n\nexport interface RecordItem {\n search: Array<{ name: string; value: string }>\n timesTriggered: number\n bookIds: number[]\n}\n","import assert from \"assert\"\nimport { GameContext } from \"../game-context\"\nimport { Reels } from \"../types\"\nimport { GameSymbol } from \"../game-symbol\"\n\n/**\n * This general board class is designed to function with or without a game context.\n */\nexport class Board {\n /**\n * The current reels on the board.\\\n * Includes only the visible symbols (without padding).\n */\n reels: Reels\n /**\n * The top padding symbols on the board.\\\n * These are the symbols above the visible area.\n */\n paddingTop: Reels\n /**\n * The bottom padding symbols on the board.\\\n * These are the symbols below the visible area.\n */\n paddingBottom: Reels\n /**\n * The anticipation values for each reel on the board.\\\n * Used for triggering anticipation effects.\n */\n anticipation: boolean[]\n lastDrawnReelStops: number[]\n lastUsedReels: Reels\n\n constructor() {\n this.reels = []\n this.paddingTop = []\n this.paddingBottom = []\n this.anticipation = []\n this.lastDrawnReelStops = []\n this.lastUsedReels = []\n }\n\n getSymbol(reelIndex: number, rowIndex: number) {\n return this.reels[reelIndex]?.[rowIndex]\n }\n\n setSymbol(reelIndex: number, rowIndex: number, symbol: GameSymbol) {\n this.reels[reelIndex] = this.reels[reelIndex] || []\n this.reels[reelIndex]![rowIndex] = symbol\n }\n\n makeEmptyReels(opts: { ctx: GameContext; reelsAmount?: number }) {\n const length =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n assert(length, \"Cannot make empty reels without context or reelsAmount.\")\n\n return Array.from({ length }, () => [])\n }\n\n countSymbolsOnReel(\n symbolOrProperties: GameSymbol | Record<string, any>,\n reelIndex: number,\n ) {\n let total = 0\n\n for (const symbol of this.reels[reelIndex]!) {\n let matches = true\n if (symbolOrProperties instanceof GameSymbol) {\n if (symbol.id !== symbolOrProperties.id) matches = false\n } else {\n for (const [key, value] of Object.entries(symbolOrProperties)) {\n if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {\n matches = false\n break\n }\n }\n }\n if (matches) {\n total++\n }\n }\n\n return total\n }\n\n countSymbolsOnBoard(\n symbolOrProperties: GameSymbol | Record<string, any>,\n ): [number, Record<number, number>] {\n let total = 0\n const onReel: Record<number, number> = {}\n\n for (const [ridx, reel] of this.reels.entries()) {\n for (const symbol of reel) {\n let matches = true\n\n if (symbolOrProperties instanceof GameSymbol) {\n if (symbol.id !== symbolOrProperties.id) matches = false\n } else {\n for (const [key, value] of Object.entries(symbolOrProperties)) {\n if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {\n matches = false\n break\n }\n }\n }\n\n if (matches) {\n total++\n if (onReel[ridx] === undefined) {\n onReel[ridx] = 1\n } else {\n onReel[ridx]++\n }\n }\n }\n }\n\n return [total, onReel]\n }\n\n isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {\n for (const reel of this.reels) {\n let count = 0\n for (const sym of reel) {\n if (sym.id === symbol.id) {\n count++\n }\n if (count > 1) {\n return true\n }\n }\n }\n return false\n }\n\n getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {\n const reelStops: number[][] = []\n for (let ridx = 0; ridx < reels.length; ridx++) {\n const reel = reels[ridx]!\n const positions: number[] = []\n for (let pos = 0; pos < reel.length; pos++) {\n if (reel[pos]!.id === symbol.id) {\n positions.push(pos)\n }\n }\n reelStops.push(positions)\n }\n return reelStops\n }\n\n combineReelStops(opts: {\n ctx: GameContext\n reelsAmount?: number\n reelStops: number[][][]\n }) {\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n assert(reelsAmount, \"Cannot combine reel stops without context or reelsAmount.\")\n\n const combined: number[][] = []\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n combined[ridx] = []\n for (const stops of opts.reelStops) {\n combined[ridx] = combined[ridx]!.concat(stops[ridx]!)\n }\n }\n return combined\n }\n\n getRandomReelStops(opts: {\n ctx: GameContext\n reelsAmount?: number\n reels: Reels\n reelStops: number[][]\n amount: number\n }) {\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n assert(reelsAmount, \"Cannot get random reel stops without context or reelsAmount.\")\n\n const symProbsOnReels: number[] = []\n const stopPositionsForReels: Record<number, number> = {}\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n symProbsOnReels.push(opts.reelStops[ridx]!.length / opts.reels[ridx]!.length)\n }\n\n while (Object.keys(stopPositionsForReels).length !== opts.amount) {\n const possibleReels: number[] = []\n for (let i = 0; i < reelsAmount; i++) {\n if (symProbsOnReels[i]! > 0) {\n possibleReels.push(i)\n }\n }\n const possibleProbs = symProbsOnReels.filter((p) => p > 0)\n const weights = Object.fromEntries(\n possibleReels.map((ridx, idx) => [ridx, possibleProbs[idx]!]),\n )\n const chosenReel = opts.ctx.services.rng.weightedRandom(weights)\n const chosenStop = opts.ctx.services.rng.randomItem(\n opts.reelStops[Number(chosenReel)]!,\n )\n symProbsOnReels[Number(chosenReel)] = 0\n stopPositionsForReels[Number(chosenReel)] = chosenStop\n }\n\n return stopPositionsForReels\n }\n\n getRandomReelset(ctx: GameContext) {\n const weights = ctx.state.currentResultSet.reelWeights\n const evalWeights = ctx.state.currentResultSet.reelWeights.evaluate?.(ctx)\n\n let reelSetId: string = \"\"\n\n if (evalWeights) {\n reelSetId = ctx.services.rng.weightedRandom(evalWeights)\n } else {\n reelSetId = ctx.services.rng.weightedRandom(weights[ctx.state.currentSpinType]!)\n }\n\n const reelSet = ctx.services.game.getReelsetById(ctx.state.currentGameMode, reelSetId)\n\n return reelSet\n }\n\n resetReels(opts: { ctx: GameContext; reelsAmount?: number }) {\n const length =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n this.reels = this.makeEmptyReels(opts)\n this.anticipation = Array.from({ length }, () => false)\n this.paddingTop = this.makeEmptyReels(opts)\n this.paddingBottom = this.makeEmptyReels(opts)\n }\n\n drawBoardMixed(opts: {\n ctx: GameContext\n reels: Reels\n forcedStops?: Record<string, number>\n forcedStopsOffset?: boolean\n reelsAmount?: number\n symbolsPerReel?: number[]\n padSymbols?: number\n }) {\n this.resetReels(opts)\n\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n const symbolsPerReel =\n opts.symbolsPerReel ?? opts.ctx.services.game.getCurrentGameMode().symbolsPerReel\n const padSymbols = opts.padSymbols ?? opts.ctx.config.padSymbols\n\n const finalReelStops: (number | null)[] = Array.from(\n { length: reelsAmount },\n () => null,\n )\n\n if (opts.forcedStops) {\n // Fill in forced stops\n for (const [r, stopPos] of Object.entries(opts.forcedStops)) {\n const reelIdx = Number(r)\n\n const symCount = symbolsPerReel[reelIdx]!\n\n if (opts.forcedStopsOffset !== false) {\n finalReelStops[reelIdx] =\n stopPos - Math.round(opts.ctx.services.rng.randomFloat(0, symCount - 1))\n } else {\n finalReelStops[reelIdx] = stopPos\n }\n\n if (finalReelStops[reelIdx]! < 0) {\n finalReelStops[reelIdx] = opts.reels[reelIdx]!.length + finalReelStops[reelIdx]!\n }\n }\n }\n\n // Fill in random stops for reels without a forced stop\n for (let i = 0; i < finalReelStops.length; i++) {\n if (finalReelStops[i] === null) {\n finalReelStops[i] = Math.floor(\n opts.ctx.services.rng.randomFloat(0, opts.reels[i]!.length - 1),\n )\n }\n }\n\n this.lastDrawnReelStops = finalReelStops.map((pos) => pos!) as number[]\n this.lastUsedReels = opts.reels\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n const reelPos = finalReelStops[ridx]!\n\n for (let p = padSymbols - 1; p >= 0; p--) {\n const topPos = (reelPos - (p + 1)) % opts.reels[ridx]!.length\n this.paddingTop[ridx]!.push(opts.reels[ridx]![topPos]!)\n const bottomPos = (reelPos + symbolsPerReel[ridx]! + p) % opts.reels[ridx]!.length\n this.paddingBottom[ridx]!.unshift(opts.reels[ridx]![bottomPos]!)\n }\n\n for (let row = 0; row < symbolsPerReel[ridx]!; row++) {\n const symbol = opts.reels[ridx]![(reelPos + row) % opts.reels[ridx]!.length]\n\n if (!symbol) {\n throw new Error(`Failed to get symbol at pos ${reelPos + row} on reel ${ridx}`)\n }\n\n this.reels[ridx]![row] = symbol\n }\n }\n }\n\n tumbleBoard(opts: {\n ctx: GameContext\n symbolsToDelete: Array<{ reelIdx: number; rowIdx: number }>\n reelsAmount?: number\n symbolsPerReel?: number[]\n padSymbols?: number\n }) {\n assert(this.lastDrawnReelStops.length > 0, \"Cannot tumble board before drawing it.\")\n\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n const symbolsPerReel =\n opts.symbolsPerReel ?? opts.ctx.services.game.getCurrentGameMode().symbolsPerReel\n const padSymbols = opts.padSymbols ?? opts.ctx.config.padSymbols\n\n if (!opts.ctx && !reelsAmount && !symbolsPerReel) {\n throw new Error(\n \"If ctx is not provided, reelsAmount and symbolsPerReel must be given.\",\n )\n }\n\n const reels = this.lastUsedReels\n\n // Sort deletions by row index descending to avoid index shifting issues\n const sortedDeletions = [...opts.symbolsToDelete].sort((a, b) => b.rowIdx - a.rowIdx)\n\n sortedDeletions.forEach(({ reelIdx, rowIdx }) => {\n this.reels[reelIdx]!.splice(rowIdx, 1)\n })\n\n const newFirstSymbolPositions: Record<number, number> = {}\n\n /**\n * A mapping of reel index to the new symbols that were added to that reel during the tumble.\\\n * The topmost added symbols are at the start of the array.\n */\n const newBoardSymbols: Record<string, GameSymbol[]> = {}\n\n /**\n * A mapping of reel index to the new padding top symbols that were added to that reel during the tumble.\n * The topmost added symbols are at the start of the array.\n */\n const newPaddingTopSymbols: Record<string, GameSymbol[]> = {}\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n // Drop down padding symbols from top for as long as reel is not filled and padding top has symbols\n while (this.reels[ridx]!.length < symbolsPerReel[ridx]!) {\n const padSymbol = this.paddingTop[ridx]!.pop()\n if (padSymbol) {\n this.reels[ridx]!.unshift(padSymbol)\n\n if (!newBoardSymbols[ridx]) {\n newBoardSymbols[ridx] = []\n }\n\n newBoardSymbols[ridx]!.unshift(padSymbol)\n } else {\n break\n }\n }\n\n const previousStop = this.lastDrawnReelStops[ridx]!\n const stopBeforePad = previousStop - padSymbols - 1\n const symbolsNeeded = symbolsPerReel[ridx]! - this.reels[ridx]!.length\n // Drop rest of symbols\n for (let s = 0; s < symbolsNeeded; s++) {\n const symbolPos = (stopBeforePad - s + reels[ridx]!.length) % reels[ridx]!.length\n const newSymbol = reels[ridx]![symbolPos]\n\n assert(newSymbol, \"Failed to get new symbol for tumbling.\")\n\n this.reels[ridx]!.unshift(newSymbol)\n newFirstSymbolPositions[ridx] = symbolPos\n\n if (!newBoardSymbols[ridx]) {\n newBoardSymbols[ridx] = []\n }\n\n newBoardSymbols[ridx]!.unshift(newSymbol)\n }\n }\n\n // Add new padding top symbols\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n const firstSymbolPos = newFirstSymbolPositions[ridx]!\n\n if (firstSymbolPos === undefined) continue\n\n for (let p = 1; p <= padSymbols; p++) {\n const topPos = (firstSymbolPos - p + reels[ridx]!.length) % reels[ridx]!.length\n const padSymbol = reels[ridx]![topPos]\n\n assert(padSymbol, \"Failed to get new padding symbol for tumbling.\")\n\n this.paddingTop[ridx]!.unshift(padSymbol)\n\n if (!newPaddingTopSymbols[ridx]) {\n newPaddingTopSymbols[ridx] = []\n }\n\n newPaddingTopSymbols[ridx]!.unshift(padSymbol)\n }\n }\n\n // Ensure future tumbles start from the new top positions\n this.lastDrawnReelStops = this.lastDrawnReelStops.map((stop, ridx) => {\n return newFirstSymbolPositions[ridx] ?? stop\n })\n\n return {\n newBoardSymbols,\n newPaddingTopSymbols,\n }\n }\n}\n","export class GameSymbol {\n readonly id: string\n readonly pays?: Record<number, number>\n readonly properties: Map<string, any>\n\n constructor(opts: GameSymbolOpts) {\n this.id = opts.id\n this.pays = opts.pays\n this.properties = new Map<string, any>(Object.entries(opts.properties || {}))\n\n if (this.pays && Object.keys(this.pays).length === 0) {\n throw new Error(`GameSymbol \"${this.id}\" must have pays defined.`)\n }\n }\n\n /**\n * Compares this symbol to another symbol or a set of properties.\n */\n compare(symbolOrProperties?: GameSymbol | Record<string, any>) {\n if (!symbolOrProperties) {\n console.warn(\"No symbol or properties provided for comparison.\")\n return false\n }\n if (symbolOrProperties instanceof GameSymbol) {\n return this.id === symbolOrProperties.id\n } else {\n for (const [key, value] of Object.entries(symbolOrProperties)) {\n if (!this.properties.has(key) || this.properties.get(key) !== value) {\n return false\n }\n }\n return true\n }\n }\n\n /**\n * Creates a clone of this GameSymbol.\n */\n clone() {\n return new GameSymbol({\n id: this.id,\n pays: this.pays ? { ...this.pays } : undefined,\n properties: Object.fromEntries(this.properties),\n })\n }\n}\n\nexport interface GameSymbolOpts {\n /**\n * Unique identifier for the symbol, e.g. \"W\", \"H1\", \"L5\", etc.\n */\n id: string\n /**\n * Paytable for the symbol, where the key is the number of symbols and the value is the payout multiplier.\n */\n pays?: Record<number, number>\n /**\n * Additional properties for the symbol, e.g. `multiplier` or `isWild`.\n *\n * Properties can help identify special symbols.\n * \n * @example\n * If your game has a \"normal\" scatter and a \"super\" scatter, you can define them like this:\n * \n * ```ts\n * properties: {\n * isScatter: true,\n * }\n * ```\n */\n properties?: Record<string, any>\n}\n","import { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { AnyGameModes, AnySymbols, AnyUserData, Reels } from \"../types\"\nimport { Board } from \"../board\"\n\nexport class BoardService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n private board: Board\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n\n this.board = new Board()\n }\n\n /**\n * Resets the board to an empty state.\\\n * This is called before drawing a new board.\n */\n resetBoard() {\n this.resetReels()\n this.board.lastDrawnReelStops = []\n }\n\n /**\n * Gets the current reels and symbols on the board.\n */\n getBoardReels() {\n return this.board.reels\n }\n\n getPaddingTop() {\n return this.board.paddingTop\n }\n\n getPaddingBottom() {\n return this.board.paddingBottom\n }\n\n getAnticipation() {\n return this.board.anticipation\n }\n\n /**\n * Gets the symbol at the specified reel and row index.\n */\n getSymbol(reelIndex: number, rowIndex: number) {\n return this.board.getSymbol(reelIndex, rowIndex)\n }\n\n /**\n * Sets the symbol at the specified reel and row index.\n */\n setSymbol(reelIndex: number, rowIndex: number, symbol: GameSymbol) {\n this.board.setSymbol(reelIndex, rowIndex, symbol)\n }\n\n private resetReels() {\n this.board.resetReels({\n ctx: this.ctx(),\n })\n }\n\n /**\n * Sets the anticipation value for a specific reel.\n */\n setAnticipationForReel(reelIndex: number, value: boolean) {\n this.board.anticipation[reelIndex] = value\n }\n\n /**\n * Counts how many symbols matching the criteria are on a specific reel.\n */\n countSymbolsOnReel(\n symbolOrProperties: GameSymbol | Record<string, any>,\n reelIndex: number,\n ) {\n return this.board.countSymbolsOnReel(symbolOrProperties, reelIndex)\n }\n\n /**\n * Counts how many symbols matching the criteria are on the board.\n *\n * Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.\n *\n * Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.\n */\n countSymbolsOnBoard(\n symbolOrProperties: GameSymbol | Record<string, any>,\n ): [number, Record<number, number>] {\n return this.board.countSymbolsOnBoard(symbolOrProperties)\n }\n\n /**\n * Checks if a symbol appears more than once on any reel in the current reel set.\n *\n * Useful to check for \"forbidden\" generations, e.g. 2 scatters on one reel.\n */\n isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {\n return this.board.isSymbolOnAnyReelMultipleTimes(symbol)\n }\n\n /**\n * Gets all reel stops (positions) where the specified symbol appears in the current reel set.\\\n * Returns an array of arrays, where each inner array contains the positions for the corresponding reel.\n */\n getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {\n return this.board.getReelStopsForSymbol(reels, symbol)\n }\n\n /**\n * Combines multiple arrays of reel stops into a single array of reel stops.\\\n */\n combineReelStops(...reelStops: number[][][]) {\n return this.board.combineReelStops({\n ctx: this.ctx(),\n reelStops,\n })\n }\n\n /**\n * From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.\n *\n * Mostly useful for placing scatter symbols on the board.\n */\n getRandomReelStops(reels: Reels, reelStops: number[][], amount: number) {\n return this.board.getRandomReelStops({\n ctx: this.ctx(),\n reels,\n reelStops,\n amount,\n })\n }\n\n /**\n * Selects a random reel set based on the configured weights of the current result set.\\\n * Returns the reels as arrays of GameSymbols.\n */\n getRandomReelset() {\n return this.board.getRandomReelset(this.ctx())\n }\n\n /**\n * Draws a board using specified reel stops.\n */\n drawBoardWithForcedStops(opts: {\n reels: Reels\n forcedStops: Record<string, number>\n randomOffset?: boolean\n }) {\n this.drawBoardMixed(opts.reels, opts.forcedStops, opts.randomOffset)\n }\n\n /**\n * Draws a board using random reel stops.\n */\n drawBoardWithRandomStops(reels: Reels) {\n this.drawBoardMixed(reels)\n }\n\n private drawBoardMixed(\n reels: Reels,\n forcedStops?: Record<string, number>,\n forcedStopsOffset?: boolean,\n ) {\n this.board.drawBoardMixed({\n ctx: this.ctx(),\n reels,\n forcedStops,\n forcedStopsOffset,\n })\n }\n\n /**\n * Tumbles the board. All given symbols will be deleted and new symbols will fall from the top.\n */\n tumbleBoard(symbolsToDelete: Array<{ reelIdx: number; rowIdx: number }>) {\n return this.board.tumbleBoard({\n ctx: this.ctx(),\n symbolsToDelete,\n })\n }\n}\n","import assert from \"assert\"\nimport { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { Recorder } from \"../recorder\"\nimport { AnyGameModes, AnySymbols, AnyUserData, SpinType } from \"../types\"\nimport { Book, BookEvent } from \"../book\"\n\nexport class DataService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n private recorder!: Recorder\n private book!: Book\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n private ensureRecorder() {\n assert(this.recorder, \"Recorder not set in DataService. Call setRecorder() first.\")\n }\n\n private ensureBook() {\n assert(this.book, \"Book not set in DataService. Call setBook() first.\")\n }\n\n /**\n * Intended for internal use only.\n */\n _setRecorder(recorder: Recorder) {\n this.recorder = recorder\n }\n\n /**\n * Intended for internal use only.\n */\n _getBook() {\n return this.book\n }\n\n /**\n * Intended for internal use only.\n */\n _setBook(book: Book) {\n this.book = book\n }\n\n /**\n * Intended for internal use only.\n */\n _getRecorder() {\n return this.recorder\n }\n\n /**\n * Intended for internal use only.\n */\n _getRecords() {\n this.ensureRecorder()\n return this.recorder.records\n }\n\n /**\n * Record data for statistical analysis.\n */\n record(data: Record<string, string | number | boolean>) {\n this.ensureRecorder()\n\n this.recorder.pendingRecords.push({\n bookId: this.ctx().state.currentSimulationId,\n properties: Object.fromEntries(\n Object.entries(data).map(([k, v]) => [k, String(v)]),\n ),\n })\n }\n\n /**\n * Records a symbol occurrence for statistical analysis.\n *\n * Calls `ctx.services.data.record()` with the provided data.\n */\n recordSymbolOccurrence(data: {\n kind: number\n symbolId: string\n spinType: SpinType\n [key: string]: any\n }) {\n this.record(data)\n }\n\n /**\n * Adds an event to the book.\n */\n addBookEvent(event: Omit<BookEvent, \"index\">) {\n this.ensureBook()\n this.book.addEvent(event)\n }\n\n /**\n * Intended for internal use only.\n */\n _clearPendingRecords() {\n this.ensureRecorder()\n this.recorder.pendingRecords = []\n }\n}\n","import { AbstractService } from \".\"\nimport { GameConfig } from \"../game-config\"\nimport { GameContext } from \"../game-context\"\nimport { AnyGameModes, AnySymbols, AnyUserData, SpinType } from \"../types\"\nimport { WinCombination } from \"../win-types\"\n\nexport class GameService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n /**\n * Intended for internal use only.\\\n * Generates reels for all reel sets in the game configuration.\n */\n _generateReels() {\n const config = this.ctx().config\n for (const mode of Object.values(config.gameModes)) {\n if (mode.reelSets && mode.reelSets.length > 0) {\n for (const reelSet of Object.values(mode.reelSets)) {\n reelSet.associatedGameModeName = mode.name\n reelSet.generateReels(config)\n }\n } else {\n throw new Error(\n `Game mode \"${mode.name}\" has no reel sets defined. Cannot generate reelset files.`,\n )\n }\n }\n }\n\n /**\n * Retrieves a reel set by its ID within a specific game mode.\n */\n getReelsetById(gameMode: string, id: string) {\n const reelSet = this.ctx().config.gameModes[gameMode]!.reelSets.find(\n (rs) => rs.id === id,\n )\n if (!reelSet) {\n throw new Error(\n `Reel set with id \"${id}\" not found in game mode \"${gameMode}\". Available reel sets: ${this.ctx()\n .config.gameModes[gameMode]!.reelSets.map((rs) => rs.id)\n .join(\", \")}`,\n )\n }\n return reelSet.reels\n }\n\n /**\n * Retrieves the number of free spins awarded for a given spin type and scatter count.\n */\n getFreeSpinsForScatters(spinType: SpinType, scatterCount: number) {\n const freespinsConfig = this.ctx().config.scatterToFreespins[spinType]\n if (!freespinsConfig) {\n throw new Error(\n `No free spins configuration found for spin type \"${spinType}\". Please check your game configuration.`,\n )\n }\n return freespinsConfig[scatterCount] || 0\n }\n\n /**\n * Retrieves a result set by its criteria within a specific game mode.\n */\n getResultSetByCriteria(mode: string, criteria: string) {\n const gameMode = this.ctx().config.gameModes[mode]\n if (!gameMode) {\n throw new Error(`Game mode \"${mode}\" not found in game config.`)\n }\n const resultSet = gameMode.resultSets.find((rs) => rs.criteria === criteria)\n if (!resultSet) {\n throw new Error(\n `Criteria \"${criteria}\" not found in game mode \"${mode}\". Available criteria: ${gameMode.resultSets\n .map((rs) => rs.criteria)\n .join(\", \")}`,\n )\n }\n return resultSet\n }\n\n /**\n * Returns all configured symbols as an array.\n */\n getSymbolArray() {\n return Array.from(this.ctx().config.symbols).map(([n, v]) => v)\n }\n\n /**\n * Gets the configuration for the current game mode.\n */\n getCurrentGameMode() {\n return this.ctx().config.gameModes[this.ctx().state.currentGameMode]!\n }\n\n /**\n * Ensures the requested number of scatters is valid based on the game configuration.\\\n * Returns a valid number of scatters.\n */\n verifyScatterCount(numScatters: number) {\n const scatterCounts =\n this.ctx().config.scatterToFreespins[this.ctx().state.currentSpinType]\n if (!scatterCounts) {\n throw new Error(\n `No scatter counts defined for spin type \"${this.ctx().state.currentSpinType}\". Please check your game configuration.`,\n )\n }\n const validCounts = Object.keys(scatterCounts).map((key) => parseInt(key, 10))\n if (validCounts.length === 0) {\n throw new Error(\n `No scatter counts defined for spin type \"${this.ctx().state.currentSpinType}\". Please check your game configuration.`,\n )\n }\n if (numScatters < Math.min(...validCounts)) {\n return Math.min(...validCounts)\n }\n if (numScatters > Math.max(...validCounts)) {\n return Math.max(...validCounts)\n }\n return numScatters\n }\n\n /**\n * Increases the freespin count by the specified amount.\n *\n * Also sets `state.triggeredFreespins` to true.\n */\n awardFreespins(amount: number) {\n this.ctx().state.currentFreespinAmount += amount\n this.ctx().state.totalFreespinAmount += amount\n this.ctx().state.triggeredFreespins = true\n }\n\n /**\n * Dedupes win symbols.\n *\n * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\\\n * this method ensures that each symbol is only listed once.\n * \n * If you want to tumble based on winning symbols, run them through this method first.\n */\n dedupeWinSymbols(winCombinations: WinCombination[]) {\n const symbolsMap = new Map<string, { reelIdx: number; rowIdx: number }>()\n winCombinations.forEach((wc) => {\n wc.symbols.forEach((s) => {\n symbolsMap.set(`${s.reelIndex},${s.posIndex}`, {\n reelIdx: s.reelIndex,\n rowIdx: s.posIndex,\n })\n })\n })\n const symbolsToRemove = Array.from(symbolsMap.values())\n return symbolsToRemove\n }\n}\n","import assert from \"assert\"\nimport { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { AnyGameModes, AnySymbols, AnyUserData, SpinType } from \"../types\"\nimport { Wallet } from \"../wallet\"\n\nexport class WalletService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n private wallet!: Wallet\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n private ensureWallet() {\n assert(this.wallet, \"Wallet not set in WalletService. Call setWallet() first.\")\n }\n\n /**\n * Intended for internal use only.\n */\n _getWallet() {\n this.ensureWallet()\n return this.wallet\n }\n\n /**\n * Intended for internal use only.\n */\n _setWallet(wallet: Wallet) {\n this.wallet = wallet\n }\n\n /**\n * Adds the given amount to the wallet state.\n *\n * After calculating the win for a board, call this method to update the wallet state.\\\n * If your game has tumbling mechanics, you should call this method again after every new tumble and win calculation.\n */\n addSpinWin(amount: number) {\n this.ensureWallet()\n this.wallet.addSpinWin(amount)\n }\n\n /**\n * Helps to add tumble wins to the wallet state.\n *\n * This also calls `addSpinWin()` internally, to add the tumble win to the overall spin win.\n */\n addTumbleWin(amount: number) {\n this.ensureWallet()\n this.wallet.addTumbleWin(amount)\n }\n\n /**\n * Confirms the wins of the current spin.\n *\n * Should be called after `addSpinWin()`, and after your tumble events are played out,\\\n * and after a (free) spin is played out to finalize the win.\n */\n confirmSpinWin() {\n this.ensureWallet()\n this.wallet.confirmSpinWin(this.ctx().state.currentSpinType)\n }\n\n /**\n * Gets the total win amount of the current simulation.\n */\n getCurrentWin() {\n this.ensureWallet()\n return this.wallet.getCurrentWin()\n }\n\n /**\n * Gets the current spin win amount of the ongoing spin.\n */\n getCurrentSpinWin() {\n this.ensureWallet()\n return this.wallet.getCurrentSpinWin()\n }\n\n /**\n * Gets the current tumble win amount of the ongoing spin.\n */\n getCurrentTumbleWin() {\n this.ensureWallet()\n return this.wallet.getCurrentTumbleWin()\n }\n}\n","import { createGameConfig, GameConfig, GameConfigOptions } from \"../game-config\"\nimport { createGameState, GameState } from \"../game-state\"\nimport { Recorder } from \"../recorder\"\nimport { BoardService } from \"../service/board\"\nimport { DataService } from \"../service/data\"\nimport { GameService } from \"../service/game\"\nimport { RngService } from \"../service/rng\"\nimport { WalletService } from \"../service/wallet\"\nimport { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\n\nexport interface GameContextOptions<\n TGameModes extends AnyGameModes,\n TSymbols extends AnySymbols,\n TUserState extends AnyUserData,\n> {\n config: GameConfig<TGameModes, TSymbols, TUserState>\n state?: Partial<GameState<TUserState>>\n services?: Partial<GameContextServices>\n}\n\nexport function createGameContext<\n TGameModes extends AnyGameModes,\n TSymbols extends AnySymbols,\n TUserState extends AnyUserData,\n>(opts: GameContextOptions<TGameModes, TSymbols, TUserState>) {\n const context = {\n config: opts.config,\n state: createGameState(opts.state),\n services: {} as GameContextServices,\n }\n\n const getContext = () => context\n\n function createServices() {\n return {\n game: new GameService(getContext),\n data: new DataService(getContext),\n board: new BoardService(getContext),\n wallet: new WalletService(getContext),\n rng: new RngService(getContext),\n ...opts.services,\n }\n }\n\n context.services = createServices()\n\n return context\n}\n\nexport type GameContext<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> = {\n /**\n * The static configuration of the game.\n */\n config: GameConfig<TGameModes, TSymbols, TUserState>\n /**\n * Game state holding information about the current simulation.\n */\n state: GameState<TUserState>\n /**\n * Services providing game functionality.\n */\n services: GameContextServices\n}\n\nexport interface GameContextServices {\n /**\n * Service providing common utility functions.\n */\n game: GameService\n /**\n * Service for interacting with the book data or recorder.\n */\n data: DataService\n /**\n * Service managing the game board and reels.\n */\n board: BoardService\n /**\n * Service providing win related functionality.\n */\n wallet: WalletService\n /**\n * Service for seeded random number generation.\n */\n rng: RngService\n}\n\n/**\n * Intended for testing purposes only.\\\n * Creates a game context with a minimal configuration.\n */\nexport function createTestContext(config?: Partial<GameConfigOptions>) {\n const ctx = createGameContext({\n config: createGameConfig({\n id: \"\",\n name: \"\",\n gameModes: {},\n symbols: {},\n scatterToFreespins: {},\n maxWinX: 10000,\n hooks: {\n onHandleGameFlow() {},\n },\n ...config,\n }),\n })\n\n // it needs a recorder to work properly\n ctx.services.data._setRecorder(new Recorder())\n\n return ctx\n}\n","export class Book {\n readonly id: number\n criteria: string = \"N/A\"\n events: BookEvent[] = []\n payout: number = 0\n basegameWins: number = 0\n freespinsWins: number = 0\n\n constructor(opts: BookOpts) {\n this.id = opts.id\n this.criteria = opts.criteria\n }\n\n /**\n * Intended for internal use only.\n */\n setCriteria(criteria: string) {\n this.criteria = criteria\n }\n\n /**\n * Adds an event to the book.\n */\n addEvent(event: Omit<BookEvent, \"index\">) {\n const index = this.events.length + 1\n this.events.push({ index, ...event })\n }\n\n /**\n * Intended for internal use only.\n */\n serialize() {\n return {\n id: this.id,\n criteria: this.criteria,\n events: this.events,\n payout: this.payout,\n basegameWins: this.basegameWins,\n freespinsWins: this.freespinsWins,\n }\n }\n\n /**\n * Intended for internal use only.\n */\n static fromSerialized(data: ReturnType<Book[\"serialize\"]>) {\n const book = new Book({ id: data.id, criteria: data.criteria })\n book.events = data.events\n book.payout = data.payout\n book.basegameWins = data.basegameWins\n book.freespinsWins = data.freespinsWins\n return book\n }\n}\n\nexport interface BookEvent {\n index: number\n type: string\n data: Record<string, any>\n}\n\ninterface BookOpts {\n id: number\n criteria: string\n}\n","import { SPIN_TYPE } from \"../constants\"\nimport { GameContext } from \"../game-context\"\nimport { SpinType } from \"../types\"\n\nexport class Wallet {\n /**\n * Total win amount (as the bet multiplier) from all simulations.\n */\n protected cumulativeWins = 0\n /**\n * Total win amount (as the bet multiplier) per spin type.\n *\n * @example\n * ```ts\n * {\n * basegame: 50,\n * freespins: 100,\n * superfreespins: 200,\n * }\n * ```\n */\n protected cumulativeWinsPerSpinType = {\n [SPIN_TYPE.BASE_GAME]: 0,\n [SPIN_TYPE.FREE_SPINS]: 0,\n }\n /**\n * Current win amount (as the bet multiplier) for the ongoing simulation.\n */\n protected currentWin = 0\n /**\n * Current win amount (as the bet multiplier) for the ongoing simulation per spin type.\n *\n * @example\n * ```ts\n * {\n * basegame: 50,\n * freespins: 100,\n * superfreespins: 200,\n * }\n * ```\n */\n protected currentWinPerSpinType = {\n [SPIN_TYPE.BASE_GAME]: 0,\n [SPIN_TYPE.FREE_SPINS]: 0,\n }\n /**\n * Holds the current win amount for a single (free) spin.\\\n * After each spin, this amount is added to `currentWinPerSpinType` and then reset to zero.\n */\n protected currentSpinWin = 0\n /**\n * Current win amount (as the bet multiplier) for the ongoing tumble sequence.\n */\n protected currentTumbleWin = 0\n\n constructor() {}\n\n /**\n * Updates the win for the current spin.\n *\n * Should be called after each tumble event, if applicable.\\\n * Or generally call this to add wins during a spin.\n *\n * After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`\n */\n addSpinWin(amount: number) {\n this.currentSpinWin += amount\n }\n\n /**\n * Confirms the wins of the current spin.\n *\n * Should be called after `addSpinWin()`, and after your tumble events are played out,\\\n * and after a (free) spin is played out to finalize the win.\n */\n confirmSpinWin(spinType: SpinType) {\n if (!Object.keys(this.currentWinPerSpinType).includes(spinType)) {\n throw new Error(`Spin type \"${spinType}\" does not exist in the wallet.`)\n }\n this.currentWinPerSpinType[spinType]! += this.currentSpinWin\n this.currentWin += this.currentSpinWin\n this.currentSpinWin = 0\n this.currentTumbleWin = 0\n }\n\n /**\n * Returns the accumulated win amount (as the bet multiplier) from all simulations.\n */\n getCumulativeWins() {\n return this.cumulativeWins\n }\n\n /**\n * Returns the accumulated win amount (as the bet multiplier) per spin type from all simulations.\n */\n getCumulativeWinsPerSpinType() {\n return this.cumulativeWinsPerSpinType\n }\n\n /**\n * Returns the current win amount (as the bet multiplier) for the ongoing simulation.\n */\n getCurrentWin() {\n return this.currentWin\n }\n\n /**\n * Returns the current spin win amount (as the bet multiplier) for the ongoing simulation.\n */\n getCurrentSpinWin() {\n return this.currentSpinWin\n }\n\n /**\n * Returns the current tumble win amount (as the bet multiplier) for the ongoing simulation.\n */\n getCurrentTumbleWin() {\n return this.currentTumbleWin\n }\n\n /**\n * Returns the current win amount (as the bet multiplier) per spin type for the ongoing simulation.\n */\n getCurrentWinPerSpinType() {\n return this.currentWinPerSpinType\n }\n\n /**\n * Adds a win to `currentSpinWin` and `currentTumbleWin`.\n *\n * After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`\n */\n addTumbleWin(amount: number) {\n this.currentTumbleWin += amount\n this.addSpinWin(amount)\n }\n\n /**\n * Intended for internal use only.\n * \n * Resets the current win amounts to zero.\n */\n resetCurrentWin() {\n this.currentWin = 0\n this.currentSpinWin = 0\n this.currentTumbleWin = 0\n\n for (const spinType of Object.keys(this.currentWinPerSpinType)) {\n this.currentWinPerSpinType[spinType as SpinType] = 0\n }\n }\n\n /**\n * Intended for internal use only.\n * \n * Adds current wins to cumulative wins and resets current wins to zero.\n */\n confirmWins(ctx: GameContext) {\n function process(number: number) {\n return Math.round(Math.min(number, ctx.config.maxWinX) * 100) / 100\n }\n\n this.currentWin = process(this.currentWin)\n this.cumulativeWins += this.currentWin\n let spinTypeWins = 0\n\n for (const spinType of Object.keys(this.currentWinPerSpinType)) {\n const st = spinType as SpinType\n const spinTypeWin = process(this.currentWinPerSpinType[st])\n this.cumulativeWinsPerSpinType[st]! += spinTypeWin\n spinTypeWins += spinTypeWin\n }\n\n if (process(spinTypeWins) !== this.currentWin) {\n throw new Error(\n `Inconsistent wallet state: currentWin (${this.currentWin}) does not equal spinTypeWins (${spinTypeWins}).`,\n )\n }\n\n this.resetCurrentWin()\n }\n\n /**\n * Intended for internal use only.\n *\n * Transfers the win data from the given wallet to the calling book.\n */\n writePayoutToBook(ctx: GameContext) {\n function process(number: number) {\n return Math.round(Math.min(number, ctx.config.maxWinX) * 100) / 100\n }\n\n const wallet = ctx.services.wallet._getWallet()\n const book = ctx.services.data._getBook()\n\n book.payout = Math.round(process(wallet.getCurrentWin()) * 100)\n book.basegameWins = process(\n wallet.getCurrentWinPerSpinType()[SPIN_TYPE.BASE_GAME] || 0,\n )\n book.freespinsWins = process(\n wallet.getCurrentWinPerSpinType()[SPIN_TYPE.FREE_SPINS] || 0,\n )\n }\n\n /**\n * Intended for internal use only.\n */\n serialize() {\n return {\n cumulativeWins: this.cumulativeWins,\n cumulativeWinsPerSpinType: this.cumulativeWinsPerSpinType,\n currentWin: this.currentWin,\n currentWinPerSpinType: this.currentWinPerSpinType,\n currentSpinWin: this.currentSpinWin,\n currentTumbleWin: this.currentTumbleWin,\n }\n }\n\n /**\n * Intended for internal use only.\n */\n merge(wallet: Wallet) {\n this.cumulativeWins += wallet.getCumulativeWins()\n const otherWinsPerSpinType = wallet.getCumulativeWinsPerSpinType()\n\n for (const spinType of Object.keys(this.cumulativeWinsPerSpinType)) {\n this.cumulativeWinsPerSpinType[spinType as SpinType]! +=\n otherWinsPerSpinType[spinType as SpinType] || 0\n }\n }\n\n /**\n * Intended for internal use only.\n */\n mergeSerialized(data: ReturnType<Wallet[\"serialize\"]>) {\n this.cumulativeWins += data.cumulativeWins\n for (const spinType of Object.keys(this.cumulativeWinsPerSpinType)) {\n this.cumulativeWinsPerSpinType[spinType as SpinType]! +=\n data.cumulativeWinsPerSpinType[spinType as SpinType] || 0\n }\n this.currentWin += data.currentWin\n this.currentSpinWin += data.currentSpinWin\n this.currentTumbleWin += data.currentTumbleWin\n for (const spinType of Object.keys(this.currentWinPerSpinType)) {\n this.currentWinPerSpinType[spinType as SpinType]! +=\n data.currentWinPerSpinType[spinType as SpinType] || 0\n }\n }\n}\n","import fs from \"fs\"\nimport path from \"path\"\nimport { GameConfig } from \"../game-config\"\nimport { Optimizer, OptimzierGameModeConfig } from \"../optimizer\"\nimport assert from \"assert\"\nimport {\n getAvgWin,\n getLessBetHitrate,\n getMaxWin,\n getMaxwinHitrate,\n getMinWin,\n getNonZeroHitrate,\n getNullHitrate,\n getPayoutWeights,\n getRtp,\n getStandardDeviation,\n getTotalLutWeight,\n getUniquePayouts,\n getVariance,\n parseLookupTable,\n parseLookupTableSegmented,\n} from \"./utils\"\nimport { writeJsonFile } from \"../../utils\"\nimport { isMainThread } from \"worker_threads\"\n\nexport class Analysis {\n protected readonly gameConfig: GameConfig\n protected readonly optimizerConfig: OptimzierGameModeConfig\n protected filePaths: Record<string, FilePaths>\n\n constructor(optimizer: Optimizer) {\n this.gameConfig = optimizer.getGameConfig()\n this.optimizerConfig = optimizer.getOptimizerGameModes()\n this.filePaths = {}\n }\n\n async runAnalysis(gameModes: string[]) {\n if (!isMainThread) return // IMPORTANT: Prevent workers from kicking off (multiple) analysis runs\n\n this.filePaths = this.getPathsForModes(gameModes)\n this.getNumberStats(gameModes)\n this.getWinRanges(gameModes)\n // TODO: this.getSymbolStats(gameModes)\n console.log(\"Analysis complete. Files written to build directory.\")\n }\n\n private getPathsForModes(gameModes: string[]) {\n const rootPath = this.gameConfig.rootDir\n const paths: Record<string, FilePaths> = {}\n\n for (const modeStr of gameModes) {\n const lut = path.join(\n rootPath,\n this.gameConfig.outputDir,\n `lookUpTable_${modeStr}.csv`,\n )\n const lutSegmented = path.join(\n rootPath,\n this.gameConfig.outputDir,\n `lookUpTableSegmented_${modeStr}.csv`,\n )\n const lutOptimized = path.join(\n rootPath,\n this.gameConfig.outputDir,\n \"publish_files\",\n `lookUpTable_${modeStr}_0.csv`,\n )\n const booksJsonl = path.join(\n rootPath,\n this.gameConfig.outputDir,\n `books_${modeStr}.jsonl`,\n )\n const booksJsonlCompressed = path.join(\n rootPath,\n this.gameConfig.outputDir,\n \"publish_files\",\n `books_${modeStr}.jsonl.zst`,\n )\n\n paths[modeStr] = {\n lut,\n lutSegmented,\n lutOptimized,\n booksJsonl,\n booksJsonlCompressed,\n }\n\n for (const p of Object.values(paths[modeStr])) {\n assert(\n fs.existsSync(p),\n `File \"${p}\" does not exist. Run optimization to auto-create it.`,\n )\n }\n }\n\n return paths\n }\n\n private getNumberStats(gameModes: string[]) {\n const stats: Statistics[] = []\n\n for (const modeStr of gameModes) {\n const mode = this.getGameModeConfig(modeStr)\n\n const lutOptimized = parseLookupTable(\n fs.readFileSync(this.filePaths[modeStr]!.lutOptimized, \"utf-8\"),\n )\n const totalWeight = getTotalLutWeight(lutOptimized)\n const payoutWeights = getPayoutWeights(lutOptimized)\n\n stats.push({\n gameMode: mode.name,\n totalWeight,\n avgWin: getAvgWin(payoutWeights),\n rtp: getRtp(payoutWeights, mode.cost),\n minWin: getMinWin(payoutWeights),\n maxWin: getMaxWin(payoutWeights),\n stdDev: getStandardDeviation(payoutWeights),\n variance: getVariance(payoutWeights),\n nonZeroHitRate: getNonZeroHitrate(payoutWeights),\n nullHitRate: getNullHitrate(payoutWeights),\n maxwinHitRate: getMaxwinHitrate(payoutWeights),\n lessBetHitRate: getLessBetHitrate(payoutWeights, mode.cost),\n uniquePayouts: getUniquePayouts(payoutWeights),\n })\n }\n\n writeJsonFile(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, \"stats_summary.json\"),\n stats,\n )\n }\n\n private getWinRanges(gameModes: string[]) {\n const winRanges: [number, number][] = [\n [0, 0.1],\n [0, 0.99],\n [1, 1.99],\n [2, 2.99],\n [3, 4.99],\n [5, 9.99],\n [10, 19.99],\n [20, 49.99],\n [50, 99.99],\n [100, 199.99],\n [200, 499.99],\n [500, 999.99],\n [1000, 1999.99],\n [2000, 2999.99],\n [3000, 4999.99],\n [5000, 7499.99],\n [7500, 9999.99],\n [10000, 14999.99],\n [15000, 19999.99],\n [20000, 24999.99],\n ]\n\n const payoutRanges: Record<\n string,\n {\n overall: Record<string, number>\n criteria: Record<string, Record<string, number>>\n }\n > = {}\n\n for (const modeStr of gameModes) {\n payoutRanges[modeStr] = { overall: {}, criteria: {} }\n\n const lutOptimized = parseLookupTable(\n fs.readFileSync(this.filePaths[modeStr]!.lutOptimized, \"utf-8\"),\n )\n\n const lutSegmented = parseLookupTableSegmented(\n fs.readFileSync(this.filePaths[modeStr]!.lutSegmented, \"utf-8\"),\n )\n\n lutOptimized.forEach(([, , p]) => {\n const payout = p / 100\n for (const [min, max] of winRanges) {\n if (payout >= min && payout <= max) {\n const rangeKey = `${min}-${max}`\n if (!payoutRanges[modeStr]!.overall[rangeKey]) {\n payoutRanges[modeStr]!.overall[rangeKey] = 0\n }\n payoutRanges[modeStr]!.overall[rangeKey] += 1\n break\n }\n }\n })\n\n lutSegmented.forEach(([, criteria, bp, fsp]) => {\n const basePayout = bp / 100\n const freeSpinPayout = fsp / 100\n const payout = basePayout + freeSpinPayout\n\n for (const [min, max] of winRanges) {\n if (payout >= min && payout <= max) {\n const rangeKey = `${min}-${max}`\n\n // Overall\n if (!payoutRanges[modeStr]!.overall[rangeKey]) {\n payoutRanges[modeStr]!.overall[rangeKey] = 0\n }\n payoutRanges[modeStr]!.overall[rangeKey] += 1\n\n // Criteria\n if (!payoutRanges[modeStr]!.criteria[criteria]) {\n payoutRanges[modeStr]!.criteria[criteria] = {}\n }\n if (!payoutRanges[modeStr]!.criteria[criteria]![rangeKey]) {\n payoutRanges[modeStr]!.criteria[criteria]![rangeKey] = 0\n }\n payoutRanges[modeStr]!.criteria[criteria]![rangeKey] += 1\n break\n }\n }\n })\n\n const orderedOverall: Record<string, number> = {}\n Object.keys(payoutRanges[modeStr]!.overall)\n .sort((a, b) => {\n const [aMin] = a.split(\"-\").map(Number)\n const [bMin] = b.split(\"-\").map(Number)\n return aMin! - bMin!\n })\n .forEach((key) => {\n orderedOverall[key] = payoutRanges[modeStr]!.overall[key]!\n })\n\n const orderedCriteria: Record<string, Record<string, number>> = {}\n Object.keys(payoutRanges[modeStr]!.criteria).forEach((crit) => {\n const critMap = payoutRanges[modeStr]!.criteria[crit]!\n const orderedCritMap: Record<string, number> = {}\n Object.keys(critMap)\n .sort((a, b) => {\n const [aMin] = a.split(\"-\").map(Number)\n const [bMin] = b.split(\"-\").map(Number)\n return aMin! - bMin!\n })\n .forEach((key) => {\n orderedCritMap[key] = critMap[key]!\n })\n orderedCriteria[crit] = orderedCritMap\n })\n\n payoutRanges[modeStr] = {\n overall: orderedOverall,\n criteria: {},\n }\n }\n\n writeJsonFile(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, \"stats_payouts.json\"),\n payoutRanges,\n )\n }\n\n private getGameModeConfig(mode: string) {\n const config = this.gameConfig.gameModes[mode]\n assert(config, `Game mode \"${mode}\" not found in game config`)\n return config\n }\n}\n\nexport interface AnalysisOpts {\n gameModes: string[]\n}\n\ninterface FilePaths {\n lut: string\n lutSegmented: string\n lutOptimized: string\n booksJsonl: string\n booksJsonlCompressed: string\n}\n\ninterface Statistics {\n gameMode: string\n totalWeight: number\n rtp: number\n avgWin: number\n minWin: number\n maxWin: number\n stdDev: number\n variance: number\n maxwinHitRate: number\n nonZeroHitRate: number\n nullHitRate: number\n lessBetHitRate: number\n uniquePayouts: number\n}\n","export function parseLookupTable(content: string) {\n const lines = content.trim().split(\"\\n\")\n const lut: LookupTable = []\n for (const line of lines) {\n const [indexStr, weightStr, payoutStr] = line.split(\",\")\n const index = parseInt(indexStr!.trim())\n const weight = parseInt(weightStr!.trim())\n const payout = parseFloat(payoutStr!.trim())\n lut.push([index, weight, payout])\n }\n return lut\n}\n\nexport function parseLookupTableSegmented(content: string) {\n const lines = content.trim().split(\"\\n\")\n const lut: LookupTableSegmented = []\n for (const line of lines) {\n const [indexStr, criteria, weightStr, payoutStr] = line.split(\",\")\n const index = parseInt(indexStr!.trim())\n const weight = parseInt(weightStr!.trim())\n const payout = parseFloat(payoutStr!.trim())\n lut.push([index, criteria!, weight, payout])\n }\n return lut\n}\n\nexport type LookupTable = [number, number, number][]\nexport type LookupTableSegmented = [number, string, number, number][]\n\nexport function getTotalLutWeight(lut: LookupTable) {\n return lut.reduce((sum, [, weight]) => sum + weight, 0)\n}\n\nexport function getTotalWeight(payoutWeights: PayoutWeights) {\n return Object.values(payoutWeights).reduce((sum, w) => sum + w, 0)\n}\n\ntype PayoutWeights = Record<number, number>\n\nexport function getPayoutWeights(\n lut: LookupTable,\n opts: { normalize?: boolean } = {},\n): PayoutWeights {\n const { normalize = true } = opts\n const totalWeight = getTotalLutWeight(lut)\n\n let payoutWeights: Record<number, number> = {}\n\n for (const [, weight, p] of lut) {\n const payout = p / 100\n if (payoutWeights[payout] === undefined) {\n payoutWeights[payout] = 0\n }\n payoutWeights[payout] += weight\n }\n\n // Sort by payout value\n payoutWeights = Object.fromEntries(\n Object.entries(payoutWeights).sort(([a], [b]) => parseFloat(a) - parseFloat(b)),\n )\n\n if (normalize) {\n for (const payout in payoutWeights) {\n payoutWeights[payout]! /= totalWeight\n }\n }\n\n return payoutWeights\n}\n\nexport function getNonZeroHitrate(payoutWeights: PayoutWeights) {\n const totalWeight = getTotalWeight(payoutWeights)\n if (Math.min(...Object.keys(payoutWeights).map(Number)) == 0) {\n return totalWeight / (totalWeight - (payoutWeights[0] ?? 0) / totalWeight)\n } else {\n return 1\n }\n}\n\nexport function getNullHitrate(payoutWeights: PayoutWeights) {\n return payoutWeights[0] ?? 0\n}\n\nexport function getMaxwinHitrate(payoutWeights: PayoutWeights) {\n const totalWeight = getTotalWeight(payoutWeights)\n const maxWin = Math.max(...Object.keys(payoutWeights).map(Number))\n const hitRate = (payoutWeights[maxWin] || 0) / totalWeight\n return 1 / hitRate\n}\n\nexport function getUniquePayouts(payoutWeights: PayoutWeights) {\n return Object.keys(payoutWeights).length\n}\n\nexport function getMinWin(payoutWeights: PayoutWeights) {\n const payouts = Object.keys(payoutWeights).map(Number)\n return Math.min(...payouts)\n}\n\nexport function getMaxWin(payoutWeights: PayoutWeights) {\n const payouts = Object.keys(payoutWeights).map(Number)\n return Math.max(...payouts)\n}\n\nexport function getAvgWin(payoutWeights: PayoutWeights) {\n let avgWin = 0\n for (const [payoutStr, weight] of Object.entries(payoutWeights)) {\n const payout = parseFloat(payoutStr)\n avgWin += payout * weight\n }\n return avgWin\n}\n\nexport function getRtp(payoutWeights: PayoutWeights, cost: number) {\n const avgWin = getAvgWin(payoutWeights)\n return avgWin / cost\n}\n\nexport function getStandardDeviation(payoutWeights: PayoutWeights) {\n const variance = getVariance(payoutWeights)\n return Math.sqrt(variance)\n}\n\nexport function getVariance(payoutWeights: PayoutWeights) {\n const totalWeight = getTotalWeight(payoutWeights)\n const avgWin = getAvgWin(payoutWeights)\n let variance = 0\n for (const [payoutStr, weight] of Object.entries(payoutWeights)) {\n const payout = parseFloat(payoutStr)\n variance += Math.pow(payout - avgWin, 2) * (weight / totalWeight)\n }\n return variance\n}\n\nexport function getLessBetHitrate(payoutWeights: PayoutWeights, cost: number) {\n let lessBetWeight = 0\n const totalWeight = getTotalWeight(payoutWeights)\n for (const [payoutStr, weight] of Object.entries(payoutWeights)) {\n const payout = parseFloat(payoutStr)\n if (payout < cost) {\n lessBetWeight += weight\n }\n }\n return lessBetWeight / totalWeight\n}\n","import path from \"path\"\nimport assert from \"assert\"\nimport { spawn } from \"child_process\"\nimport { isMainThread } from \"worker_threads\"\nimport { makeMathConfig } from \"../utils/math-config\"\nimport { makeSetupFile } from \"../utils/setup-file\"\nimport { OptimizationParameters } from \"./OptimizationParameters\"\nimport { OptimizationConditions } from \"./OptimizationConditions\"\nimport { OptimizationScaling } from \"./OptimizationScaling\"\nimport { SlotGame } from \"../slot-game\"\nimport { GameConfig } from \"../game-config\"\n\nexport class Optimizer {\n protected readonly gameConfig: GameConfig\n protected readonly gameModes: OptimzierGameModeConfig\n\n constructor(opts: OptimizerOpts) {\n this.gameConfig = opts.game.getConfig()\n this.gameModes = opts.gameModes\n this.verifyConfig()\n }\n\n /**\n * Runs the optimization process, and runs analysis after.\n */\n async runOptimization({ gameModes }: OptimizationOpts) {\n if (!isMainThread) return // IMPORTANT: Prevent workers from kicking off (multiple) optimizations\n\n const mathConfig = makeMathConfig(this, { writeToFile: true })\n\n for (const mode of gameModes) {\n const setupFile = makeSetupFile(this, mode)\n await this.runSingleOptimization()\n }\n console.log(\"Optimization complete. Files written to build directory.\")\n }\n\n private async runSingleOptimization() {\n return await rustProgram()\n }\n\n private verifyConfig() {\n for (const [k, mode] of Object.entries(this.gameModes)) {\n const configMode = this.gameConfig.gameModes[k]\n\n if (!configMode) {\n throw new Error(\n `Game mode \"${mode}\" defined in optimizer config does not exist in the game config.`,\n )\n }\n\n const conditions = Object.keys(mode.conditions)\n const scalings = Object.keys(mode.scaling)\n const parameters = Object.keys(mode.parameters)\n\n for (const rs of configMode.resultSets) {\n if (!conditions.includes(rs.criteria)) {\n throw new Error(\n `ResultSet criteria \"${rs.criteria}\" in game mode \"${k}\" does not have a corresponding optimization condition defined.`,\n )\n }\n }\n\n let gameModeRtp = configMode.rtp\n let paramRtp = 0\n for (const cond of conditions) {\n const paramConfig = mode.conditions[cond]!\n paramRtp += Number(paramConfig.getRtp())\n }\n\n gameModeRtp = Math.round(gameModeRtp * 1000) / 1000\n paramRtp = Math.round(paramRtp * 1000) / 1000\n\n assert(\n gameModeRtp === paramRtp,\n `Sum of all RTP conditions (${paramRtp}) does not match the game mode RTP (${gameModeRtp}) in game mode \"${k}\".`,\n )\n }\n }\n\n getGameConfig() {\n return this.gameConfig\n }\n\n getOptimizerGameModes() {\n return this.gameModes\n }\n}\n\nasync function rustProgram(...args: string[]) {\n console.log(\"Starting Rust optimizer. This may take a while...\")\n\n return new Promise((resolve, reject) => {\n const task = spawn(\"cargo\", [\"run\", \"-q\", \"--release\", ...args], {\n cwd: path.join(__dirname, \"./optimizer-rust\"),\n stdio: \"pipe\",\n })\n task.on(\"error\", (error) => {\n console.error(\"Error:\", error)\n reject(error)\n })\n task.on(\"exit\", () => {\n resolve(true)\n })\n task.on(\"close\", () => {\n resolve(true)\n })\n task.stdout.on(\"data\", (data) => {\n console.log(data.toString())\n })\n task.stderr.on(\"data\", (data) => {\n console.log(data.toString())\n })\n task.stdout.on(\"error\", (data) => {\n console.log(data.toString())\n reject(data.toString())\n })\n })\n}\n\nexport interface OptimizationOpts {\n gameModes: string[]\n}\n\nexport interface OptimizerOpts {\n game: SlotGame<any, any, any>\n gameModes: OptimzierGameModeConfig\n}\n\nexport type OptimzierGameModeConfig = Record<\n string,\n {\n conditions: Record<string, OptimizationConditions>\n scaling: OptimizationScaling\n parameters: OptimizationParameters\n }\n>\n\nexport { OptimizationConditions } from \"./OptimizationConditions\"\nexport { OptimizationScaling } from \"./OptimizationScaling\"\nexport { OptimizationParameters } from \"./OptimizationParameters\"","import path from \"path\"\nimport { type Optimizer } from \"../optimizer\"\nimport { writeJsonFile } from \"../../utils\"\n\nexport function makeMathConfig(\n optimizer: Optimizer,\n opts: { writeToFile?: boolean } = {},\n) {\n const game = optimizer.getGameConfig()\n const gameModesCfg = optimizer.getOptimizerGameModes()\n const { writeToFile } = opts\n\n const isDefined = <T>(v: T | undefined): v is T => v !== undefined\n\n const config: MathConfig = {\n game_id: game.id,\n bet_modes: Object.entries(game.gameModes).map(([key, mode]) => ({\n bet_mode: mode.name,\n cost: mode.cost,\n rtp: mode.rtp,\n max_win: game.maxWinX,\n })),\n fences: Object.entries(gameModesCfg).map(([gameModeName, modeCfg]) => ({\n bet_mode: gameModeName,\n fences: Object.entries(modeCfg.conditions)\n .map(([fenceName, fence]) => ({\n name: fenceName,\n avg_win: isDefined(fence.getAvgWin())\n ? fence.getAvgWin()!.toString()\n : undefined,\n hr: isDefined(fence.getHitRate()) ? fence.getHitRate()!.toString() : undefined,\n rtp: isDefined(fence.getRtp()) ? fence.getRtp()!.toString() : undefined,\n identity_condition: {\n search: Object.entries(fence.getForceSearch()).map(([k, v]) => ({\n name: k,\n value: v,\n })),\n win_range_start: fence.getSearchRange()[0]!,\n win_range_end: fence.getSearchRange()[1]!,\n opposite: false,\n },\n priority: fence.priority,\n }))\n .sort((a, b) => b.priority - a.priority),\n })),\n dresses: Object.entries(gameModesCfg).flatMap(([gameModeName, modeCfg]) => ({\n bet_mode: gameModeName,\n dresses: modeCfg.scaling.getConfig().map((s) => ({\n fence: s.criteria,\n scale_factor: s.scaleFactor.toString(),\n identity_condition_win_range: s.winRange,\n prob: s.probability,\n })),\n })),\n }\n\n if (writeToFile) {\n const outPath = path.join(game.rootDir, game.outputDir, \"math_config.json\")\n writeJsonFile(outPath, config)\n }\n\n return config\n}\n\nexport type MathConfig = {\n game_id: string\n bet_modes: Array<{\n bet_mode: string\n cost: number\n rtp: number\n max_win: number\n }>\n fences: Array<{\n bet_mode: string\n fences: Array<Fence>\n }>\n dresses: Array<{\n bet_mode: string\n dresses: Dress[]\n }>\n}\n\ninterface Search {\n name: string\n value: string\n}\n\ninterface IdentityCondition {\n search: Search[]\n opposite: boolean\n win_range_start: number\n win_range_end: number\n}\n\ninterface Fence {\n name: string\n avg_win?: string\n rtp?: string\n hr?: string\n identity_condition: IdentityCondition\n priority: number\n}\n\ninterface Dress {\n fence: string\n scale_factor: string\n identity_condition_win_range: [number, number]\n prob: number\n}\n","import path from \"path\"\nimport { writeFile } from \"../../utils\"\nimport { Optimizer } from \"../optimizer\"\n\nexport function makeSetupFile(optimizer: Optimizer, gameMode: string) {\n const gameConfig = optimizer.getGameConfig()\n const optimizerGameModes = optimizer.getOptimizerGameModes()\n const modeConfig = optimizerGameModes[gameMode]\n\n if (!modeConfig) {\n throw new Error(`Game mode \"${gameMode}\" not found in optimizer configuration.`)\n }\n\n const params = modeConfig.parameters.getParameters()\n\n let content = \"\"\n content += `game_name;${gameConfig.id}\\n`\n content += `bet_type;${gameMode}\\n`\n content += `num_show_pigs;${params.numShowPigs}\\n`\n content += `num_pigs_per_fence;${params.numPigsPerFence}\\n`\n content += `threads_for_fence_construction;${params.threadsFenceConstruction}\\n`\n content += `threads_for_show_construction;${params.threadsShowConstruction}\\n`\n content += `score_type;${params.scoreType}\\n`\n content += `test_spins;${JSON.stringify(params.testSpins)}\\n`\n content += `test_spins_weights;${JSON.stringify(params.testSpinsWeights)}\\n`\n content += `simulation_trials;${params.simulationTrials}\\n`\n content += `graph_indexes;0\\n`\n content += `run_1000_batch;False\\n`\n content += `simulation_trials;${params.simulationTrials}\\n`\n content += `user_game_build_path;${path.join(gameConfig.rootDir, gameConfig.outputDir)}\\n`\n content += `pmb_rtp;${params.pmbRtp}\\n`\n\n const outPath = path.join(__dirname, \"./optimizer-rust/src\", \"setup.txt\")\n\n writeFile(outPath, content)\n}\n","import assert from \"assert\"\n\nexport class OptimizationConditions {\n protected rtp?: number | \"x\"\n protected avgWin?: number\n protected hitRate?: number | \"x\"\n protected searchRange: number[]\n protected forceSearch: Record<string, string>\n priority: number\n\n constructor(opts: OptimizationConditionsOpts) {\n let { rtp, avgWin, hitRate, searchConditions, priority } = opts\n\n if (rtp == undefined || rtp === \"x\") {\n assert(avgWin !== undefined && hitRate !== undefined, \"If RTP is not specified, hit-rate (hr) and average win amount (av_win) must be given.\")\n rtp = Math.round((avgWin! / Number(hitRate)) * 100000) / 100000\n }\n\n let noneCount = 0\n for (const val of [rtp, avgWin, hitRate]) {\n if (val === undefined) noneCount++\n }\n assert(noneCount <= 1, \"Invalid combination of optimization conditions.\")\n\n this.searchRange = [-1, -1]\n this.forceSearch = {}\n\n if (typeof searchConditions === \"number\") {\n this.searchRange = [searchConditions, searchConditions]\n }\n if (Array.isArray(searchConditions)) {\n if (searchConditions[0] > searchConditions[1] || searchConditions.length !== 2) {\n throw new Error(\"Invalid searchConditions range.\")\n }\n this.searchRange = searchConditions\n }\n if (typeof searchConditions === \"object\" && !Array.isArray(searchConditions)) {\n this.searchRange = [-1, -1]\n this.forceSearch = searchConditions\n }\n\n this.rtp = rtp\n this.avgWin = avgWin\n this.hitRate = hitRate\n this.priority = priority\n }\n\n getRtp() {\n return this.rtp\n }\n\n getAvgWin() {\n return this.avgWin\n }\n\n getHitRate() {\n return this.hitRate\n }\n\n getSearchRange() {\n return this.searchRange\n }\n\n getForceSearch() {\n return this.forceSearch\n }\n}\n\ninterface OptimizationConditionsOpts {\n /**\n * The desired RTP (0-1)\n */\n rtp?: number | \"x\"\n /**\n * The desired average win (per spin).\n */\n avgWin?: number\n /**\n * The desired hit rate (e.g. `200` to hit 1 in 200 spins).\n */\n hitRate?: number | \"x\"\n /**\n * A way of filtering results by\n *\n * - A number (payout multiplier), e.g. `5000`\n * - Force record value, e.g. `{ \"symbolId\": \"scatter\" }`\n * - A range of numbers, e.g. `[0, 100]` (payout multiplier range)\n */\n searchConditions?: number | Record<string, string> | [number, number]\n /**\n * **Priority matters!**\\\n * Higher priority conditions will be evaluated first.\\\n * After a book matching this condition is found, the book will be removed from the pool\\\n * and can't be used to satisfy other conditions with lower priority.\n * \n * TODO add better explanation\n */\n priority: number\n}\n","export class OptimizationScaling {\n protected config: OptimizationScalingOpts\n\n constructor(opts: OptimizationScalingOpts) {\n this.config = opts\n }\n\n getConfig() {\n return this.config\n }\n}\n\ntype OptimizationScalingOpts = Array<{\n criteria: string\n scaleFactor: number\n winRange: [number, number]\n probability: number\n}>\n","export class OptimizationParameters {\n protected parameters: OptimizationParametersOpts\n\n constructor(opts?: Partial<OptimizationParametersOpts>) {\n this.parameters = {\n ...OptimizationParameters.DEFAULT_PARAMETERS,\n ...opts,\n }\n }\n\n static DEFAULT_PARAMETERS: OptimizationParametersOpts = {\n numShowPigs: 5000,\n numPigsPerFence: 10000,\n threadsFenceConstruction: 16,\n threadsShowConstruction: 16,\n testSpins: [50, 100, 200],\n testSpinsWeights: [0.3, 0.4, 0.3],\n simulationTrials: 5000,\n graphIndexes: [],\n run1000Batch: false,\n minMeanToMedian: 4,\n maxMeanToMedian: 8,\n pmbRtp: 1.0,\n scoreType: \"rtp\",\n }\n\n getParameters() {\n return this.parameters\n }\n}\n\nexport interface OptimizationParametersOpts {\n readonly numShowPigs: number\n readonly numPigsPerFence: number\n readonly threadsFenceConstruction: number\n readonly threadsShowConstruction: number\n readonly testSpins: number[]\n readonly testSpinsWeights: number[]\n readonly simulationTrials: number\n readonly graphIndexes: number[]\n readonly run1000Batch: false\n readonly minMeanToMedian: number\n readonly maxMeanToMedian: number\n readonly pmbRtp: number\n readonly scoreType: \"rtp\"\n}\n","import { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\nimport { createGameConfig, GameConfigOptions } from \"../game-config\"\nimport { Simulation, SimulationConfigOptions, SimulationOptions } from \"../simulation\"\nimport { Analysis, AnalysisOpts } from \"../analysis\"\nimport { OptimizationOpts, Optimizer, OptimizerOpts } from \"../optimizer\"\nimport { isMainThread } from \"worker_threads\"\n\n/**\n * SlotGame class that encapsulates the game configuration and state.\\\n * Main entry point for the slot game.\n */\nexport class SlotGame<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> {\n private readonly configOpts: GameConfigOptions<TGameModes, TSymbols, TUserState>\n private simulation?: Simulation\n private optimizer?: Optimizer\n private analyzer?: Analysis\n\n constructor(config: GameConfigOptions<TGameModes, TSymbols, TUserState>) {\n this.configOpts = config\n }\n\n /**\n * Sets up the simulation configuration.\\\n * Must be called before `runTasks()`.\n */\n configureSimulation(opts: SimulationOptions) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n this.simulation = new Simulation(opts, this.configOpts)\n }\n\n /**\n * Sets up the optimization configuration.\\\n * Must be called before `runTasks()`.\n */\n configureOptimization(opts: Pick<OptimizerOpts, \"gameModes\">) {\n this.optimizer = new Optimizer({\n game: this,\n gameModes: opts.gameModes,\n })\n }\n\n /**\n * Runs the simulation based on the configured settings.\n */\n private async runSimulation(opts: SimulationConfigOptions = {}) {\n if (!this.simulation) {\n throw new Error(\n \"Simulation is not configured. Do so by calling configureSimulation() first.\",\n )\n }\n await this.simulation.runSimulation(opts)\n }\n\n /**\n * Runs the optimization based on the configured settings.\n */\n private async runOptimization(opts: OptimizationOpts) {\n if (!this.optimizer) {\n throw new Error(\n \"Optimization is not configured. Do so by calling configureOptimization() first.\",\n )\n }\n\n await this.optimizer.runOptimization(opts)\n }\n\n /**\n * Runs the analysis based on the configured settings.\n */\n private async runAnalysis(opts: AnalysisOpts) {\n if (!this.optimizer) {\n throw new Error(\n \"Optimization must be configured to run analysis. Do so by calling configureOptimization() first.\",\n )\n }\n this.analyzer = new Analysis(this.optimizer)\n await this.analyzer.runAnalysis(opts.gameModes)\n }\n\n /**\n * Runs the configured tasks: simulation, optimization, and/or analysis.\n */\n async runTasks(\n opts: {\n doSimulation?: boolean\n doOptimization?: boolean\n doAnalysis?: boolean\n simulationOpts?: SimulationConfigOptions\n optimizationOpts?: OptimizationOpts\n analysisOpts?: AnalysisOpts\n } = {},\n ) {\n if (!opts.doSimulation && !opts.doOptimization && !opts.doAnalysis) {\n console.log(\"No tasks to run. Enable either simulation, optimization or analysis.\")\n }\n\n if (opts.doSimulation) {\n await this.runSimulation(opts.simulationOpts || {})\n }\n\n if (opts.doOptimization) {\n await this.runOptimization(opts.optimizationOpts || { gameModes: [] })\n }\n\n if (opts.doAnalysis) {\n await this.runAnalysis(opts.analysisOpts || { gameModes: [] })\n }\n\n if (isMainThread) console.log(\"Finishing up...\")\n }\n\n /**\n * Gets the game configuration.\n */\n getConfig() {\n return createGameConfig(this.configOpts)\n }\n}\n","import { GameConfigOptions } from \"./game-config\"\nimport { SlotGame } from \"./slot-game\"\nimport { AnyGameModes, AnySymbols, AnyUserData, InferGameType } from \"./types\"\n\nexport function createSlotGame<TGame>(\n opts: TGame extends InferGameType<infer G, infer S, infer U>\n ? GameConfigOptions<G, S, U>\n : never,\n) {\n return new SlotGame(opts) as TGame\n}\n\nexport const defineUserState = <TUserState extends AnyUserData>(data: TUserState) => data\n\nexport const defineSymbols = <TSymbols extends AnySymbols>(symbols: TSymbols) => symbols\n\nexport const defineGameModes = <TGameModes extends AnyGameModes>(gameModes: TGameModes) =>\n gameModes\n","import assert from \"assert\"\nimport { ResultSet } from \"../result-set\"\nimport { ReelSet } from \"../reel-set\"\n\nexport class GameMode {\n readonly name: string\n readonly reelsAmount: number\n readonly symbolsPerReel: number[]\n readonly cost: number\n readonly rtp: number\n readonly reelSets: ReelSet[]\n readonly resultSets: ResultSet<any>[]\n readonly isBonusBuy: boolean\n\n constructor(opts: GameModeOpts) {\n this.name = opts.name\n this.reelsAmount = opts.reelsAmount\n this.symbolsPerReel = opts.symbolsPerReel\n this.cost = opts.cost\n this.rtp = opts.rtp\n this.reelSets = opts.reelSets\n this.resultSets = opts.resultSets\n this.isBonusBuy = opts.isBonusBuy\n\n assert(this.rtp >= 0.9 && this.rtp <= 0.99, \"RTP must be between 0.9 and 0.99\")\n\n assert(\n this.symbolsPerReel.length === this.reelsAmount,\n \"symbolsPerReel length must match reelsAmount.\",\n )\n\n assert(this.reelSets.length > 0, \"GameMode must have at least one ReelSet defined.\")\n }\n}\n\nexport interface GameModeOpts {\n /**\n * Name of the game mode.\n */\n name: string\n /**\n * Number of reels the board has.\n */\n reelsAmount: number\n /**\n * How many symbols each reel has. Array length must match `reelsAmount`.\\\n * The number at an array index represents the number of symbols on that reel.\n */\n symbolsPerReel: number[]\n /**\n * Cost of the game mode, multiplied by the base bet.\n */\n cost: number\n /**\n * The target RTP of the game.\n */\n rtp: number\n /**\n * Defines (and generates) all reels for the game.\\\n * Which reels are used in a spin is determined by the ResultSet of the current game mode.\n *\n * It is common to have one reel set for the base game and another for free spins.\\\n * Each `ResultSet` can then set the weights of these reel sets to control which\\\n * reel set is used for a specific criteria.\n */\n reelSets: ReelSet[]\n /**\n * A ResultSet defines how often a specific outcome should be generated.\\\n * For example, a ResultSet can be used to force a specific ratio of max wins\\\n * in the simulations to ensure there are different frontend representations.\n */\n resultSets: ResultSet<any>[]\n /**\n * Whether this game mode is a bonus buy.\n */\n isBonusBuy: boolean\n}\n","import { GameContext } from \"../game-context\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { Reels } from \"../types\"\n\nexport class WinType {\n protected payout: number\n protected winCombinations: WinCombination[]\n protected ctx: GameContext\n protected readonly wildSymbol?: WildSymbol\n\n constructor(opts: WinTypeOpts) {\n this.ctx = opts.ctx\n this.payout = 0\n this.winCombinations = []\n this.wildSymbol = opts?.wildSymbol\n }\n\n /**\n * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.\n */\n evaluateWins(board: Reels) {\n return this\n }\n\n /**\n * Custom post-processing of wins, e.g. for handling multipliers.\n */\n postProcess(func: PostProcessFn<typeof this.winCombinations>) {\n const result = func(this.winCombinations, this.ctx)\n this.winCombinations = result.winCombinations\n this.payout = result.winCombinations.reduce((sum, w) => sum + w.payout, 0)\n return this\n }\n\n /**\n * Returns the total payout and detailed win combinations.\n */\n getWins() {\n return {\n payout: this.payout,\n winCombinations: this.winCombinations,\n }\n }\n\n protected isWild(symbol: GameSymbol) {\n return !!this.wildSymbol && symbol.compare(this.wildSymbol)\n }\n\n protected getSymbolPayout(symbol: GameSymbol, count: number) {\n if (!symbol.pays) return 0\n\n let clusterSize = 0\n\n const sizes = Object.keys(symbol.pays)\n .map((s) => parseInt(s, 10))\n .filter((n) => Number.isFinite(n))\n .sort((a, b) => a - b)\n\n for (const size of sizes) {\n if (size > count) break\n clusterSize = size\n }\n\n return symbol.pays[clusterSize] || 0\n }\n}\n\nexport interface WinTypeOpts {\n /**\n * A reference to the game context.\n */\n ctx: GameContext<any, any, any> // TODO: Hacky and stupid. Fix AnyTypes.\n /**\n * Configuration used to identify wild symbols on the board.\\\n * You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.\n *\n * @example\n * If you have different wild symbols, each with a property `isWild: true`, you can define:\n * ```ts\n * wildSymbol: { isWild: true }\n * ```\n *\n * @example\n * If you have a single wild symbol instance, you can define:\n * ```ts\n * wildSymbol: myWildSymbol\n * ```\n */\n wildSymbol?: WildSymbol\n}\n\nexport type WinCombination = {\n payout: number\n kind: number\n baseSymbol: GameSymbol\n symbols: Array<{\n symbol: GameSymbol\n isWild: boolean\n substitutedFor?: GameSymbol\n reelIndex: number\n posIndex: number\n }>\n}\n\ntype PostProcessFn<TWinCombs extends WinCombination[]> = (\n wins: TWinCombs,\n ctx: GameContext,\n) => {\n winCombinations: TWinCombs\n}\n\ntype WildSymbol = GameSymbol | Record<string, any>\n\nexport type Symbol = { reel: number; row: number; symbol: GameSymbol }\nexport type SymbolList = Array<Symbol>\nexport type SymbolMap = Map<string, Symbol>\n","import { GameSymbol } from \"../game-symbol\"\nimport { SymbolList, WinCombination, WinType, WinTypeOpts } from \".\"\nimport { Reels } from \"../types\"\nimport assert from \"assert\"\n\nexport class LinesWinType extends WinType {\n protected lines: Record<number, number[]>\n declare protected winCombinations: LineWinCombination[]\n declare getWins: () => {\n payout: number\n winCombinations: LineWinCombination[]\n }\n\n constructor(opts: LinesWinTypeOpts) {\n super(opts)\n this.lines = opts.lines\n\n if (Object.keys(this.lines).length === 0) {\n throw new Error(\"LinesWinType must have at least one line defined.\")\n }\n }\n\n private validateConfig() {\n const reelsAmount = this.ctx.services.game.getCurrentGameMode().reelsAmount\n const symsPerReel = this.ctx.services.game.getCurrentGameMode().symbolsPerReel\n\n for (const [lineNum, positions] of Object.entries(this.lines)) {\n if (positions.length !== reelsAmount) {\n throw new Error(\n `Line ${lineNum} has ${positions.length} positions, but the current game mode has ${reelsAmount} reels.`,\n )\n }\n for (let i = 0; i < positions.length; i++) {\n if (positions[i]! < 0 || positions[i]! >= symsPerReel[i]!) {\n throw new Error(\n `Line ${lineNum} has an invalid position ${positions[i]} on reel ${i}. Valid range is 0 to ${\n symsPerReel[i]! - 1\n }.`,\n )\n }\n }\n }\n\n const firstLine = Math.min(...Object.keys(this.lines).map(Number))\n if (firstLine !== 1) {\n throw new Error(\n `Lines must start from 1. Found line ${firstLine} as the first line.`,\n )\n }\n }\n\n /**\n * Calculates wins based on the defined paylines and provided board state.\\\n * Retrieve the results using `getWins()` after.\n */\n evaluateWins(board: Reels) {\n this.validateConfig()\n\n const lineWins: LineWinCombination[] = []\n\n const reels = board\n\n for (const [lineNumStr, line] of Object.entries(this.lines)) {\n const lineNum = Number(lineNumStr)\n let baseSymbol: GameSymbol | undefined\n const potentialWinLine: SymbolList = []\n const potentialWildLine: SymbolList = []\n\n for (const [ridx, reel] of reels.entries()) {\n const sidx = line[ridx]!\n const thisSymbol = reel[sidx]\n\n if (!baseSymbol) {\n baseSymbol = thisSymbol\n }\n\n assert(baseSymbol, `No symbol found at line ${lineNum}, reel ${ridx}`)\n assert(thisSymbol, `No symbol found at line ${lineNum}, reel ${ridx}`)\n\n if (potentialWinLine.length == 0) {\n if (this.isWild(thisSymbol)) {\n potentialWildLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n }\n potentialWinLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n continue\n }\n\n if (this.isWild(baseSymbol)) {\n if (this.isWild(thisSymbol)) {\n potentialWildLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n } else {\n baseSymbol = thisSymbol\n }\n potentialWinLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n continue\n }\n\n if (baseSymbol.compare(thisSymbol) || this.isWild(thisSymbol)) {\n potentialWinLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n }\n }\n\n const minSymLine = Math.min(\n ...Object.keys(baseSymbol!.pays || {}).map((k) => parseInt(k, 10)),\n )\n\n if (potentialWinLine.length < minSymLine) continue\n\n const linePayout = this.getLinePayout(potentialWinLine)\n const wildLinePayout = this.getLinePayout(potentialWildLine)\n\n let finalLine: LineWinCombination = {\n kind: potentialWinLine.length,\n baseSymbol: baseSymbol!,\n symbols: potentialWinLine.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n lineNumber: lineNum,\n payout: linePayout,\n }\n\n if (wildLinePayout > linePayout) {\n baseSymbol = potentialWildLine[0]?.symbol\n\n finalLine = {\n kind: potentialWildLine.length,\n baseSymbol: baseSymbol!,\n symbols: potentialWildLine.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n lineNumber: lineNum,\n payout: wildLinePayout,\n }\n }\n\n lineWins.push(finalLine)\n }\n\n for (const win of lineWins) {\n this.ctx.services.data.recordSymbolOccurrence({\n kind: win.kind,\n symbolId: win.baseSymbol.id,\n spinType: this.ctx.state.currentSpinType,\n })\n }\n\n this.payout = lineWins.reduce((sum, l) => sum + l.payout, 0)\n this.winCombinations = lineWins\n\n return this\n }\n\n private getLinePayout(line: SymbolList) {\n if (line.length === 0) return 0\n\n let baseSymbol = line.find((s) => !this.isWild(s.symbol))?.symbol\n if (!baseSymbol) baseSymbol = line[0]!.symbol\n\n const kind = line.length\n const payout = this.getSymbolPayout(baseSymbol, kind)\n\n return payout\n }\n}\n\ninterface LinesWinTypeOpts extends WinTypeOpts {\n /**\n * Defines the paylines for the slot game.\n *\n * @example\n * ```ts\n * lines: {\n * 1: [0, 0, 0, 0, 0],\n * 2: [1, 1, 1, 1, 1],\n * 3: [2, 2, 2, 2, 2],\n * }\n * ```\n */\n lines: Record<number, number[]>\n}\n\nexport interface LineWinCombination extends WinCombination {\n lineNumber: number\n}\n","import { GameSymbol } from \"../game-symbol\"\nimport { SymbolList, SymbolMap, WinCombination, WinType, WinTypeOpts } from \".\"\nimport { Reels } from \"../types\"\n\nexport class ClusterWinType extends WinType {\n declare protected winCombinations: ClusterWinCombination[]\n declare getWins: () => {\n payout: number\n winCombinations: ClusterWinCombination[]\n }\n\n private _checked: SymbolList = []\n private _checkedWilds: SymbolList = []\n private _currentBoard: Reels = []\n\n constructor(opts: ClusterWinTypeOpts) {\n super(opts)\n }\n\n private validateConfig() {}\n\n /**\n * Calculates wins based on symbol cluster size and provided board state.\\\n * Retrieve the results using `getWins()` after.\n */\n evaluateWins(board: Reels) {\n this.validateConfig()\n this._checked = []\n this._currentBoard = board\n\n const clusterWins: ClusterWinCombination[] = []\n const potentialClusters: SymbolList[] = []\n\n // Get normal symbol clusters\n for (const [ridx, reel] of board.entries()) {\n for (const [sidx, symbol] of reel.entries()) {\n this._checkedWilds = [] // each cluster can check wilds anew\n\n if (this.isWild(symbol)) continue\n\n if (this.isChecked(ridx, sidx)) {\n continue\n }\n\n const thisSymbol = { reel: ridx, row: sidx, symbol }\n this._checked.push(thisSymbol)\n\n const neighbors = this.getNeighbors(ridx, sidx)\n const matchingSymbols = this.evaluateCluster(symbol, neighbors)\n\n // Record clusters from 2 symbols and up\n if (matchingSymbols.size >= 1) {\n potentialClusters.push([thisSymbol, ...matchingSymbols.values()])\n }\n }\n }\n\n // Get wild only clusters\n for (const [ridx, reel] of board.entries()) {\n for (const [sidx, symbol] of reel.entries()) {\n this._checkedWilds = []\n\n if (!this.isWild(symbol)) continue\n\n if (this.isChecked(ridx, sidx)) {\n continue\n }\n\n const thisSymbol = { reel: ridx, row: sidx, symbol }\n this._checked.push(thisSymbol)\n\n const neighbors = this.getNeighbors(ridx, sidx)\n const matchingSymbols = this.evaluateCluster(symbol, neighbors)\n\n // Record clusters from 2 symbols and up\n if (matchingSymbols.size >= 1) {\n potentialClusters.push([thisSymbol, ...matchingSymbols.values()])\n }\n }\n }\n\n for (const cluster of potentialClusters) {\n const kind = cluster.length\n let baseSymbol = cluster.find((s) => !this.isWild(s.symbol))?.symbol\n if (!baseSymbol) baseSymbol = cluster[0]!.symbol\n\n const payout = this.getSymbolPayout(baseSymbol, kind)\n if (payout === 0) continue\n\n if (!baseSymbol.pays || Object.keys(baseSymbol.pays).length === 0) {\n continue // don't add non-paying symbols to final clusters\n }\n\n clusterWins.push({\n payout,\n kind,\n baseSymbol,\n symbols: cluster.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n })\n }\n\n for (const win of clusterWins) {\n this.ctx.services.data.recordSymbolOccurrence({\n kind: win.kind,\n symbolId: win.baseSymbol.id,\n spinType: this.ctx.state.currentSpinType,\n })\n }\n\n this.payout = clusterWins.reduce((sum, c) => sum + c.payout, 0)\n this.winCombinations = clusterWins\n\n return this\n }\n\n private getNeighbors(ridx: number, sidx: number) {\n const board = this._currentBoard\n const neighbors: SymbolList = []\n\n const potentialNeighbors: Array<[number, number]> = [\n [ridx - 1, sidx],\n [ridx + 1, sidx],\n [ridx, sidx - 1],\n [ridx, sidx + 1],\n ]\n\n potentialNeighbors.forEach(([nridx, nsidx]) => {\n if (board[nridx] && board[nridx][nsidx]) {\n neighbors.push({ reel: nridx, row: nsidx, symbol: board[nridx][nsidx] })\n }\n })\n\n return neighbors\n }\n\n private evaluateCluster(rootSymbol: GameSymbol, neighbors: SymbolList) {\n const matchingSymbols: SymbolMap = new Map()\n\n neighbors.forEach((neighbor) => {\n const { reel, row, symbol } = neighbor\n\n if (this.isChecked(reel, row)) return\n if (this.isCheckedWild(reel, row)) return\n\n if (this.isWild(symbol) || symbol.compare(rootSymbol)) {\n const key = `${reel}-${row}`\n matchingSymbols.set(key, { reel, row, symbol })\n\n if (symbol.compare(rootSymbol)) {\n this._checked.push(neighbor)\n }\n\n if (this.isWild(symbol)) {\n this._checkedWilds.push(neighbor)\n }\n\n const neighbors = this.getNeighbors(reel, row)\n const nestedMatches = this.evaluateCluster(rootSymbol, neighbors)\n nestedMatches.forEach((nsym) => {\n const nkey = `${nsym.reel}-${nsym.row}`\n matchingSymbols.set(nkey, nsym)\n })\n }\n })\n\n return matchingSymbols\n }\n\n private isChecked(ridx: number, sidx: number) {\n return !!this._checked.find((c) => c.reel === ridx && c.row === sidx)\n }\n\n private isCheckedWild(ridx: number, sidx: number) {\n return !!this._checkedWilds.find((c) => c.reel === ridx && c.row === sidx)\n }\n}\n\ninterface ClusterWinTypeOpts extends WinTypeOpts {}\n\nexport interface ClusterWinCombination extends WinCombination {}\n","import { GameSymbol } from \"../game-symbol\"\nimport { SymbolList, WinCombination, WinType, WinTypeOpts } from \".\"\nimport { Reels } from \"../types\"\n\nexport class ManywaysWinType extends WinType {\n declare protected winCombinations: ManywaysWinCombination[]\n declare getWins: () => {\n payout: number\n winCombinations: ManywaysWinCombination[]\n }\n\n constructor(opts: ManywaysWinTypeOpts) {\n super(opts)\n }\n\n private validateConfig() {}\n\n /**\n * Calculates wins based on the defined paylines and provided board state.\\\n * Retrieve the results using `getWins()` after.\n */\n evaluateWins(board: Reels) {\n this.validateConfig()\n\n const waysWins: ManywaysWinCombination[] = []\n\n const reels = board\n\n // A wild on the first reel can start a win for any symbol on second reel.\n // Store the possible wins in a map with the base symbol id as key.\n // The value is an array of \"reels\" containing all matching symbols for that base symbol.\n const possibleWaysWins = new Map<string, Record<string, SymbolList>>()\n\n // Identify all candidate symbols that can form a win starting from Reel 1.\n // We scan from left to right. If a reel contains a Wild, it can \"bridge\" to the next reel.\n // We collect all non-wild symbols we encounter as potential win candidates.\n // We also collect the Wild symbol itself as a candidate for pure wild wins.\n const candidateSymbols = new Map<string, GameSymbol>()\n let searchReelIdx = 0\n let searchActive = true\n\n while (searchActive && searchReelIdx < reels.length) {\n const reel = reels[searchReelIdx]!\n let hasWild = false\n\n for (const symbol of reel) {\n candidateSymbols.set(symbol.id, symbol)\n if (this.isWild(symbol)) {\n hasWild = true\n }\n }\n\n // If this reel has no wilds, we can't extend the search for *new* starting symbols\n // beyond this reel (because a win must be continuous from the start).\n if (!hasWild) {\n searchActive = false\n }\n searchReelIdx++\n }\n\n for (const baseSymbol of candidateSymbols.values()) {\n let symbolList: Record<string, SymbolList> = {}\n let isInterrupted = false\n\n for (const [ridx, reel] of reels.entries()) {\n if (isInterrupted) break\n\n for (const [sidx, symbol] of reel.entries()) {\n const isMatch = baseSymbol.compare(symbol) || this.isWild(symbol)\n\n if (isMatch) {\n if (!symbolList[ridx]) {\n symbolList[ridx] = []\n }\n symbolList[ridx].push({ reel: ridx, row: sidx, symbol })\n }\n }\n\n if (!symbolList[ridx]) {\n isInterrupted = true\n break\n }\n }\n\n const minSymLine = Math.min(\n ...Object.keys(baseSymbol!.pays || {}).map((k) => parseInt(k, 10)),\n )\n const wayLength = this.getWayLength(symbolList)\n\n if (wayLength >= minSymLine) {\n possibleWaysWins.set(baseSymbol.id, symbolList)\n }\n }\n\n for (const [baseSymbolId, symbolList] of possibleWaysWins.entries()) {\n const wayLength = this.getWayLength(symbolList)\n\n let baseSymbol = Object.values(symbolList)\n .flatMap((l) => l.map((s) => s))\n .find((s) => !this.isWild(s.symbol))?.symbol\n\n if (!baseSymbol) baseSymbol = symbolList[0]![0]!.symbol\n\n const singleWayPayout = this.getSymbolPayout(baseSymbol, wayLength)\n const totalWays = Object.values(symbolList).reduce(\n (ways, syms) => ways * syms.length,\n 1,\n )\n const totalPayout = singleWayPayout * totalWays\n\n waysWins.push({\n kind: wayLength,\n baseSymbol,\n symbols: Object.values(symbolList).flatMap((reel) =>\n reel.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n ),\n ways: totalWays,\n payout: totalPayout,\n })\n }\n\n for (const win of waysWins) {\n this.ctx.services.data.recordSymbolOccurrence({\n kind: win.kind,\n symbolId: win.baseSymbol.id,\n spinType: this.ctx.state.currentSpinType,\n })\n }\n\n this.payout = waysWins.reduce((sum, l) => sum + l.payout, 0)\n this.winCombinations = waysWins\n\n return this\n }\n\n private getWayLength(symbolList: Record<string, SymbolList>) {\n return Math.max(...Object.keys(symbolList).map((k) => parseInt(k, 10))) + 1\n }\n}\n\ninterface ManywaysWinTypeOpts extends WinTypeOpts {}\n\nexport interface ManywaysWinCombination extends WinCombination {\n ways: number\n}\n","import fs from \"fs\"\nimport path from \"path\"\nimport { isMainThread } from \"worker_threads\"\nimport { ReelSet, ReelSetOptions } from \".\"\nimport { GameConfig } from \"../game-config\"\nimport { GameSymbol } from \"../game-symbol\"\n\n/**\n * This class is responsible for generating reel sets for slot games based on specified configurations.\n *\n * **While it offers a high degree of customization, some configurations may lead to unsolvable scenarios.**\n *\n * If the reel generator is unable to fulfill niche constraints,\\\n * you might need to adjust your configuration, or edit the generated reels manually.\\\n * Setting a different seed may also help.\n */\nexport class GeneratedReelSet extends ReelSet {\n protected readonly symbolWeights: Map<string, number> = new Map()\n protected readonly rowsAmount: number\n protected limitSymbolsToReels?: Record<string, number[]>\n protected readonly spaceBetweenSameSymbols?: number | Record<string, number>\n protected readonly spaceBetweenSymbols?: Record<string, Record<string, number>>\n protected readonly preferStackedSymbols?: number\n protected readonly symbolStacks?: Record<\n string,\n {\n chance: number | Record<string, number>\n min?: number | Record<string, number>\n max?: number | Record<string, number>\n }\n >\n protected readonly symbolQuotas?: Record<string, number | Record<string, number>>\n private overrideExisting: boolean\n\n constructor(opts: GeneratedReelSetOptions) {\n super(opts)\n this.id = opts.id\n this.symbolWeights = new Map(Object.entries(opts.symbolWeights))\n this.rowsAmount = opts.rowsAmount || 250\n\n if (opts.limitSymbolsToReels) this.limitSymbolsToReels = opts.limitSymbolsToReels\n\n this.overrideExisting = opts.overrideExisting || false\n this.spaceBetweenSameSymbols = opts.spaceBetweenSameSymbols\n this.spaceBetweenSymbols = opts.spaceBetweenSymbols\n this.preferStackedSymbols = opts.preferStackedSymbols\n this.symbolStacks = opts.symbolStacks\n this.symbolQuotas = opts.symbolQuotas\n\n if (\n (typeof this.spaceBetweenSameSymbols == \"number\" &&\n (this.spaceBetweenSameSymbols < 1 || this.spaceBetweenSameSymbols > 8)) ||\n (typeof this.spaceBetweenSameSymbols == \"object\" &&\n Object.values(this.spaceBetweenSameSymbols).some((v) => v < 1 || v > 8))\n ) {\n throw new Error(\n `spaceBetweenSameSymbols must be between 1 and 8, got ${this.spaceBetweenSameSymbols}.`,\n )\n }\n\n if (\n Object.values(this.spaceBetweenSymbols || {}).some((o) =>\n Object.values(o).some((v) => v < 1 || v > 8),\n )\n ) {\n throw new Error(\n `spaceBetweenSymbols must be between 1 and 8, got ${this.spaceBetweenSymbols}.`,\n )\n }\n\n if (\n this.preferStackedSymbols &&\n (this.preferStackedSymbols < 0 || this.preferStackedSymbols > 100)\n ) {\n throw new Error(\n `preferStackedSymbols must be between 0 and 100, got ${this.preferStackedSymbols}.`,\n )\n }\n }\n\n private validateConfig(config: GameConfig) {\n this.symbolWeights.forEach((_, symbol) => {\n if (!config.symbols.has(symbol)) {\n throw new Error(\n `Symbol \"${symbol}\" of the reel generator ${this.id} for mode ${this.associatedGameModeName} is not defined in the game config`,\n )\n }\n })\n\n if (this.limitSymbolsToReels && Object.keys(this.limitSymbolsToReels).length == 0) {\n this.limitSymbolsToReels = undefined\n }\n }\n\n private isSymbolAllowedOnReel(symbolId: string, reelIdx: number) {\n if (!this.limitSymbolsToReels) return true\n const allowedReels = this.limitSymbolsToReels[symbolId]\n if (!allowedReels || allowedReels.length === 0) return true\n return allowedReels.includes(reelIdx)\n }\n\n private resolveStacking(symbolId: string, reelIdx: number) {\n const cfg = this.symbolStacks?.[symbolId]\n if (!cfg) return null\n\n const STACKING_MIN = 1\n const STACKING_MAX = 4\n\n const chance =\n typeof cfg.chance === \"number\" ? cfg.chance : (cfg.chance?.[reelIdx] ?? 0)\n if (chance <= 0) return null\n\n let min = typeof cfg.min === \"number\" ? cfg.min : (cfg.min?.[reelIdx] ?? STACKING_MIN)\n let max = typeof cfg.max === \"number\" ? cfg.max : (cfg.max?.[reelIdx] ?? STACKING_MAX)\n\n return { chance, min, max }\n }\n\n private tryPlaceStack(\n reel: Array<GameSymbol | null>,\n config: GameConfig,\n reelIdx: number,\n symbolId: string,\n startIndex: number,\n maxStack: number,\n ) {\n if (!this.isSymbolAllowedOnReel(symbolId, reelIdx)) return 0\n\n let canPlace = 0\n for (let j = 0; j < maxStack; j++) {\n const idx = (startIndex + j) % this.rowsAmount\n if (reel[idx] !== null) break\n canPlace++\n }\n if (canPlace === 0) return 0\n\n const symObj = config.symbols.get(symbolId)\n if (!symObj) {\n throw new Error(\n `Symbol with id \"${symbolId}\" not found in the game config symbols map.`,\n )\n }\n\n for (let j = 0; j < canPlace; j++) {\n const idx = (startIndex + j) % reel.length\n reel[idx] = symObj\n }\n return canPlace\n }\n\n /**\n * Checks if a symbol can be placed at the target index without violating spacing rules.\n */\n private violatesSpacing(\n reel: Array<GameSymbol | null>,\n symbolId: string,\n targetIndex: number,\n ) {\n const circDist = (a: number, b: number) => {\n const diff = Math.abs(a - b)\n return Math.min(diff, this.rowsAmount - diff)\n }\n\n const spacingType = this.spaceBetweenSameSymbols ?? undefined\n const sameSpacing =\n typeof spacingType === \"number\" ? spacingType : (spacingType?.[symbolId] ?? 0)\n\n for (let i = 0; i <= reel.length; i++) {\n const placed = reel[i]\n if (!placed) continue\n\n const dist = circDist(targetIndex, i)\n\n // Same symbol spacing\n if (sameSpacing >= 1 && placed.id === symbolId) {\n if (dist <= sameSpacing) return true\n }\n\n // Cross-symbol spacing\n if (this.spaceBetweenSymbols) {\n const forward = this.spaceBetweenSymbols[symbolId]?.[placed.id] ?? 0\n if (forward >= 1 && dist <= forward) return true\n\n const reverse = this.spaceBetweenSymbols[placed.id]?.[symbolId] ?? 0\n if (reverse >= 1 && dist <= reverse) return true\n }\n }\n\n return false\n }\n\n generateReels(config: GameConfig) {\n this.validateConfig(config)\n\n const gameMode = config.gameModes[this.associatedGameModeName]\n\n if (!gameMode) {\n throw new Error(\n `Error generating reels for game mode \"${this.associatedGameModeName}\". It's not defined in the game config.`,\n )\n }\n\n const filePath = path.join(\n config.rootDir,\n config.outputDir,\n `reels_${this.associatedGameModeName}-${this.id}.csv`,\n )\n\n const exists = fs.existsSync(filePath)\n\n if (exists && !this.overrideExisting) {\n this.reels = this.parseReelsetCSV(filePath, config)\n return this\n }\n\n if (!exists && this.symbolWeights.size === 0) {\n throw new Error(\n `Cannot generate reels for generator \"${this.id}\" of mode \"${this.associatedGameModeName}\" because the symbol weights are empty.`,\n )\n }\n\n const reelsAmount = gameMode.reelsAmount\n const weightsObj = Object.fromEntries(this.symbolWeights)\n\n // Generate initial reels with random symbols\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n const reel: Array<GameSymbol | null> = new Array(this.rowsAmount).fill(null)\n\n const reelQuotas: Record<string, number> = {}\n const quotaCounts: Record<string, number> = {}\n let totalReelsQuota = 0\n\n // Get quotas for this reel, across all symbols\n for (const [sym, quotaConf] of Object.entries(this.symbolQuotas || {})) {\n const q = typeof quotaConf === \"number\" ? quotaConf : quotaConf[ridx]\n if (!q) continue\n reelQuotas[sym] = q\n totalReelsQuota += q\n }\n\n if (totalReelsQuota > 100) {\n throw new Error(\n `Total symbol quotas for reel ${ridx} exceed 100%. Adjust your configuration on reel set \"${this.id}\".`,\n )\n }\n\n if (totalReelsQuota > 0) {\n for (const [sym, quota] of Object.entries(reelQuotas)) {\n const quotaCount = Math.max(1, Math.floor((this.rowsAmount * quota) / 100))\n quotaCounts[sym] = quotaCount\n }\n }\n\n // Place required quotas first (use stacking over spacing, if configured)\n for (const [sym, targetCount] of Object.entries(quotaCounts)) {\n let remaining = targetCount\n let attempts = 0\n\n while (remaining > 0) {\n if (attempts++ > this.rowsAmount * 10) {\n throw new Error(\n `Failed to place ${targetCount} of symbol ${sym} on reel ${ridx} (likely spacing/stacking too strict).`,\n )\n }\n\n const pos = Math.round(this.rng.randomFloat(0, this.rowsAmount - 1))\n const stackCfg = this.resolveStacking(sym, ridx)\n let placed = 0\n\n // Try to place a symbol stack first, if configured\n if (stackCfg && Math.round(this.rng.randomFloat(1, 100)) <= stackCfg.chance) {\n const stackSize = Math.max(\n 0,\n Math.round(this.rng.randomFloat(stackCfg.min, stackCfg.max)),\n )\n const toPlace = Math.min(stackSize, remaining)\n placed = this.tryPlaceStack(reel, config, ridx, sym, pos, toPlace)\n }\n\n // Not enough space, fall back to placing single symbols\n if (\n placed === 0 &&\n reel[pos] === null &&\n this.isSymbolAllowedOnReel(sym, ridx) &&\n !this.violatesSpacing(reel, sym, pos)\n ) {\n reel[pos] = config.symbols.get(sym)!\n placed = 1\n }\n\n remaining -= placed\n }\n }\n\n // Fill the rest of the reel randomly\n for (let r = 0; r < this.rowsAmount; r++) {\n if (reel[r] !== null) continue // already placed quota\n\n let chosenSymbolId = this.rng.weightedRandom(weightsObj)\n\n // If symbolStacks is NOT configured for the next choice, allow \"preferStackedSymbols\" fallback\n const nextHasStackCfg = !!this.resolveStacking(chosenSymbolId, ridx)\n if (!nextHasStackCfg && this.preferStackedSymbols && reel.length > 0) {\n const prevSymbol = r - 1 >= 0 ? reel[r - 1] : reel[reel.length - 1]\n if (\n prevSymbol &&\n Math.round(this.rng.randomFloat(1, 100)) <= this.preferStackedSymbols &&\n (!this.spaceBetweenSameSymbols ||\n !this.violatesSpacing(reel, prevSymbol.id, r))\n ) {\n chosenSymbolId = prevSymbol.id\n }\n }\n\n // Check for stacking preference\n if (this.preferStackedSymbols && reel.length > 0) {\n const prevSymbol = r - 1 >= 0 ? reel[r - 1] : reel[reel.length - 1]\n\n if (\n Math.round(this.rng.randomFloat(1, 100)) <= this.preferStackedSymbols &&\n (!this.spaceBetweenSameSymbols ||\n !this.violatesSpacing(reel, prevSymbol!.id, r))\n ) {\n chosenSymbolId = prevSymbol!.id\n }\n }\n\n // If symbol has stack config, try to place a stack (ignore spacing)\n const stackCfg = this.resolveStacking(chosenSymbolId, ridx)\n if (stackCfg && this.isSymbolAllowedOnReel(chosenSymbolId, ridx)) {\n const roll = Math.round(this.rng.randomFloat(1, 100))\n if (roll <= stackCfg.chance) {\n const desiredSize = Math.max(\n 1,\n Math.round(this.rng.randomFloat(stackCfg.min, stackCfg.max)),\n )\n const placed = this.tryPlaceStack(\n reel,\n config,\n ridx,\n chosenSymbolId,\n r,\n desiredSize,\n )\n if (placed > 0) {\n // advance loop to skip the cells we just filled on this side of the boundary\n // (wrapped cells at the start are already filled and will be skipped when encountered)\n r += placed - 1\n continue\n }\n }\n }\n\n let tries = 0\n const maxTries = 2500\n\n while (\n !this.isSymbolAllowedOnReel(chosenSymbolId, ridx) ||\n this.violatesSpacing(reel, chosenSymbolId, r)\n ) {\n if (++tries > maxTries) {\n throw new Error(\n [\n `Failed to place a symbol on reel ${ridx} at position ${r} after ${maxTries} attempts.\\n`,\n \"Try to change the seed or adjust your configuration.\\n\",\n ].join(\" \"),\n )\n }\n chosenSymbolId = this.rng.weightedRandom(weightsObj)\n\n const hasStackCfg = !!this.resolveStacking(chosenSymbolId, ridx)\n if (!hasStackCfg && this.preferStackedSymbols && reel.length > 0) {\n const prevSymbol = r - 1 >= 0 ? reel[r - 1] : reel[reel.length - 1]\n if (\n prevSymbol &&\n Math.round(this.rng.randomFloat(1, 100)) <= this.preferStackedSymbols &&\n (!this.spaceBetweenSameSymbols ||\n !this.violatesSpacing(reel, prevSymbol.id, r))\n ) {\n chosenSymbolId = prevSymbol.id\n }\n }\n }\n\n const symbol = config.symbols.get(chosenSymbolId)\n\n if (!symbol) {\n throw new Error(\n `Symbol with id \"${chosenSymbolId}\" not found in the game config symbols map.`,\n )\n }\n\n reel[r] = symbol\n }\n\n if (reel.some((s) => s === null)) {\n throw new Error(`Reel ${ridx} has unfilled positions after generation.`)\n }\n\n this.reels.push(reel as GameSymbol[])\n }\n\n // Write the CSV\n const csvRows: string[][] = Array.from({ length: this.rowsAmount }, () =>\n Array.from({ length: reelsAmount }, () => \"\"),\n )\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n for (let r = 0; r < this.rowsAmount; r++) {\n csvRows[r]![ridx] = this.reels[ridx]![r]!.id\n }\n }\n\n const csvString = csvRows.map((row) => row.join(\",\")).join(\"\\n\")\n\n if (isMainThread) {\n fs.writeFileSync(filePath, csvString)\n\n this.reels = this.parseReelsetCSV(filePath, config)\n console.log(\n `Generated reelset ${this.id} for game mode ${this.associatedGameModeName}`,\n )\n }\n\n return this\n }\n}\n\ninterface GeneratedReelSetOptions extends ReelSetOptions {\n /**\n * The weights of the symbols in the reelset.\\\n * This is a mapping of symbol IDs to their respective weights.\n */\n symbolWeights: Record<string, number>\n /**\n * The number of rows in the reelset.\\\n * Default is 250, but can be adjusted as needed.\n */\n rowsAmount?: number\n /**\n * Prevent the same symbol from appearing directly above or below itself.\\\n * This can be a single number for all symbols, or a mapping of symbol IDs to\n * their respective spacing values.\n *\n * Must be 1 or higher, if set.\n *\n * **This is overridden by `symbolStacks`**\n */\n spaceBetweenSameSymbols?: number | Record<string, number>\n /**\n * Prevents specific symbols from appearing within a certain distance of each other.\n *\n * Useful for preventing scatter and super scatter symbols from appearing too close to each other.\n *\n * **This is overridden by `symbolStacks`**\n */\n spaceBetweenSymbols?: Record<string, Record<string, number>>\n /**\n * A percentage value 0-100 that indicates the likelihood of a symbol being stacked.\\\n * A value of 0 means no stacked symbols, while 100 means all symbols are stacked.\n *\n * This is only a preference. Symbols may still not be stacked if\\\n * other restrictions (like `spaceBetweenSameSymbols`) prevent it.\n *\n * **This is overridden by `symbolStacks`**\n */\n preferStackedSymbols?: number\n /**\n * A mapping of symbols to their respective advanced stacking configuration.\n *\n * @example\n * ```ts\n * symbolStacks: {\n * \"W\": {\n * chance: { \"1\": 20, \"2\": 20, \"3\": 20, \"4\": 20 }, // 20% chance to be stacked on reels 2-5\n * min: 2, // At least 2 wilds in a stack\n * max: 4, // At most 4 wilds in a stack\n * }\n * }\n * ```\n */\n symbolStacks?: Record<\n string,\n {\n chance: number | Record<string, number>\n min?: number | Record<string, number>\n max?: number | Record<string, number>\n }\n >\n /**\n * Configures symbols to be limited to specific reels.\\\n * For example, you could configure Scatters to appear only on reels 1, 3 and 5.\n *\n * @example\n * ```ts\n * limitSymbolsToReels: {\n * \"S\": [0, 2, 4], // Remember that reels are 0-indexed.\n * }\n * ```\n */\n limitSymbolsToReels?: Record<string, number[]>\n /**\n * Defines optional quotas for symbols on the reels.\\\n * The quota (1-100%) defines how often a symbol should appear in the reelset, or in a specific reel.\n *\n * This is particularly useful for controlling the frequency of special symbols like scatters or wilds.\n *\n * Reels not provided for a symbol will use the weights from `symbolWeights`.\n *\n * _Any_ small quota will ensure that the symbol appears at least once on the reel.\n *\n * @example\n * ```ts\n * symbolQuotas: {\n * \"S\": 3, // 3% of symbols on each reel will be scatters\n * \"W\": { \"1\": 10, \"2\": 5, \"3\": 3, \"4\": 1 }, // Wilds will appear with different quotas on selected reels\n * }\n * ```\n */\n symbolQuotas?: Record<string, number | Record<string, number>>\n /**\n * If true, existing reels CSV files will be overwritten.\n */\n overrideExisting?: boolean\n /**\n * Optional seed for the RNG to ensure reproducible results.\n *\n * Default seed is `0`.\n *\n * Note: Seeds 0 and 1 produce the same results.\n */\n seed?: number\n}\n\nexport type Reels = GameSymbol[][]\n","import fs from \"fs\"\nimport path from \"path\"\nimport { GameConfig } from \"../game-config\"\nimport { RandomNumberGenerator } from \"../service/rng\"\nimport { Reels } from \"../types\"\n\nexport class ReelSet {\n id: string\n associatedGameModeName: string\n reels: Reels\n protected rng: RandomNumberGenerator\n\n constructor(opts: ReelSetOptions) {\n this.id = opts.id\n this.associatedGameModeName = \"\"\n this.reels = []\n this.rng = new RandomNumberGenerator()\n this.rng.setSeed(opts.seed ?? 0)\n }\n\n generateReels(config: GameConfig): ReelSet {\n throw new Error(\"Not implemented\")\n }\n\n /**\n * Reads a reelset CSV file and returns the reels as arrays of GameSymbols.\n */\n parseReelsetCSV(reelSetPath: string, config: GameConfig) {\n if (!fs.existsSync(reelSetPath)) {\n throw new Error(`Reelset CSV file not found at path: ${reelSetPath}`)\n }\n\n const allowedExtensions = [\".csv\"]\n const ext = path.extname(reelSetPath).toLowerCase()\n if (!allowedExtensions.includes(ext)) {\n throw new Error(\n `Invalid file extension for reelset CSV: ${ext}. Allowed extensions are: ${allowedExtensions.join(\n \", \",\n )}`,\n )\n }\n\n const csvData = fs.readFileSync(reelSetPath, \"utf8\")\n const rows = csvData.split(\"\\n\").filter((line) => line.trim() !== \"\")\n const reels: Reels = Array.from(\n { length: config.gameModes[this.associatedGameModeName]!.reelsAmount },\n () => [],\n )\n\n rows.forEach((row) => {\n const symsInRow = row.split(\",\").map((symbolId) => {\n const symbol = config.symbols.get(symbolId.trim())\n if (!symbol) {\n throw new Error(`Symbol with id \"${symbolId}\" not found in game config.`)\n }\n return symbol\n })\n symsInRow.forEach((symbol, ridx) => {\n if (ridx >= reels.length) {\n throw new Error(\n `Row in reelset CSV has more symbols than expected reels amount (${reels.length})`,\n )\n }\n reels[ridx]!.push(symbol)\n })\n })\n\n const reelLengths = reels.map((r) => r.length)\n const uniqueLengths = new Set(reelLengths)\n if (uniqueLengths.size > 1) {\n throw new Error(\n `Inconsistent reel lengths in reelset CSV at ${reelSetPath}: ${[\n ...uniqueLengths,\n ].join(\", \")}`,\n )\n }\n\n return reels\n }\n}\n\nexport interface ReelSetOptions {\n /**\n * The unique identifier of the reel generator.\\\n * Must be unique per game mode.\n */\n id: string\n /**\n * Optional seed for the RNG to ensure reproducible results.\n *\n * Default seed is `0`.\n *\n * Note: Seeds 0 and 1 produce the same results.\n */\n seed?: number\n}\n","import assert from \"assert\"\nimport { ReelSet, ReelSetOptions } from \".\"\nimport { GameConfig } from \"../game-config\"\nimport { Reels } from \"../types\"\n\n/**\n * This class is responsible for providing reel sets for slot games based on a static configuration or file.\n */\nexport class StaticReelSet extends ReelSet {\n reels: Reels\n csvPath: string\n private _strReels: string[][]\n\n constructor(opts: StaticReelSetOptions) {\n super(opts)\n\n this.reels = []\n this._strReels = opts.reels || []\n this.csvPath = opts.csvPath || \"\"\n\n assert(\n opts.reels || opts.csvPath,\n `Either 'reels' or 'csvPath' must be provided for StaticReelSet ${this.id}`,\n )\n }\n\n private validateConfig(config: GameConfig) {\n this.reels.forEach((reel) => {\n reel.forEach((symbol) => {\n if (!config.symbols.has(symbol.id)) {\n throw new Error(\n `Symbol \"${symbol}\" of the reel set ${this.id} for mode ${this.associatedGameModeName} is not defined in the game config`,\n )\n }\n })\n })\n\n if (this.csvPath && this._strReels.length > 0) {\n throw new Error(\n `Both 'csvPath' and 'reels' are provided for StaticReelSet ${this.id}. Please provide only one.`,\n )\n }\n }\n\n generateReels(config: GameConfig) {\n this.validateConfig(config)\n\n if (this._strReels.length > 0) {\n this.reels = this._strReels.map((reel) => {\n return reel.map((symbolId) => {\n const symbol = config.symbols.get(symbolId)\n if (!symbol) {\n throw new Error(\n `Symbol \"${symbolId}\" of the reel set ${this.id} for mode ${this.associatedGameModeName} is not defined in the game config`,\n )\n }\n return symbol\n })\n })\n }\n\n if (this.csvPath) {\n this.reels = this.parseReelsetCSV(this.csvPath, config)\n }\n\n return this\n }\n}\n\ninterface StaticReelSetOptions extends ReelSetOptions {\n reels?: string[][]\n csvPath?: string\n}\n","import { GameContext } from \"../game-context\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { Reels } from \"../types\"\nimport { Board } from \".\"\n\nexport class StandaloneBoard {\n private board: Board\n private ctx: GameContext\n private reelsAmount: number\n private symbolsPerReel: number[]\n private padSymbols: number\n\n constructor(opts: StandaloneBoardOptions) {\n this.board = new Board()\n this.ctx = opts.ctx\n this.reelsAmount = opts.reelsAmount\n this.symbolsPerReel = opts.symbolsPerReel\n this.padSymbols = opts.padSymbols\n }\n\n /**\n * Resets the board to an empty state.\\\n * This is called before drawing a new board.\n */\n resetBoard() {\n this.resetReels()\n this.board.lastDrawnReelStops = []\n }\n\n /**\n * Gets the current reels and symbols on the board.\n */\n getBoardReels() {\n return this.board.reels\n }\n\n getPaddingTop() {\n return this.board.paddingTop\n }\n\n getPaddingBottom() {\n return this.board.paddingBottom\n }\n\n /**\n * Gets the symbol at the specified reel and row index.\n */\n getSymbol(reelIndex: number, rowIndex: number) {\n return this.board.getSymbol(reelIndex, rowIndex)\n }\n\n /**\n * Sets the symbol at the specified reel and row index.\n */\n setSymbol(reelIndex: number, rowIndex: number, symbol: GameSymbol) {\n this.board.setSymbol(reelIndex, rowIndex, symbol)\n }\n\n private resetReels() {\n this.board.resetReels({\n ctx: this.ctx,\n })\n }\n\n /**\n * Sets the anticipation value for a specific reel.\n */\n setAnticipationForReel(reelIndex: number, value: boolean) {\n this.board.anticipation[reelIndex] = value\n }\n\n /**\n * Counts how many symbols matching the criteria are on a specific reel.\n */\n countSymbolsOnReel(\n symbolOrProperties: GameSymbol | Record<string, any>,\n reelIndex: number,\n ) {\n return this.board.countSymbolsOnReel(symbolOrProperties, reelIndex)\n }\n\n /**\n * Counts how many symbols matching the criteria are on the board.\n *\n * Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.\n *\n * Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.\n */\n countSymbolsOnBoard(\n symbolOrProperties: GameSymbol | Record<string, any>,\n ): [number, Record<number, number>] {\n return this.board.countSymbolsOnBoard(symbolOrProperties)\n }\n\n /**\n * Checks if a symbol appears more than once on any reel in the current reel set.\n *\n * Useful to check for \"forbidden\" generations, e.g. 2 scatters on one reel.\n */\n isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {\n return this.board.isSymbolOnAnyReelMultipleTimes(symbol)\n }\n\n /**\n * Gets all reel stops (positions) where the specified symbol appears in the current reel set.\\\n * Returns an array of arrays, where each inner array contains the positions for the corresponding reel.\n */\n getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {\n return this.board.getReelStopsForSymbol(reels, symbol)\n }\n\n /**\n * Combines multiple arrays of reel stops into a single array of reel stops.\\\n */\n combineReelStops(...reelStops: number[][][]) {\n return this.board.combineReelStops({\n ctx: this.ctx,\n reelStops,\n })\n }\n\n /**\n * From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.\n *\n * Mostly useful for placing scatter symbols on the board.\n */\n getRandomReelStops(reels: Reels, reelStops: number[][], amount: number) {\n return this.board.getRandomReelStops({\n ctx: this.ctx,\n reels,\n reelsAmount: this.reelsAmount,\n reelStops,\n amount,\n })\n }\n\n /**\n * Selects a random reel set based on the configured weights of the current result set.\\\n * Returns the reels as arrays of GameSymbols.\n */\n getRandomReelset() {\n return this.board.getRandomReelset(this.ctx)\n }\n\n /**\n * Draws a board using specified reel stops.\n */\n drawBoardWithForcedStops(opts: {\n reels: Reels\n forcedStops: Record<string, number>\n randomOffset?: boolean\n }) {\n this.drawBoardMixed(opts.reels, opts.forcedStops, opts.randomOffset)\n }\n\n /**\n * Draws a board using random reel stops.\n */\n drawBoardWithRandomStops(reels: Reels) {\n this.drawBoardMixed(reels)\n }\n\n private drawBoardMixed(\n reels: Reels,\n forcedStops?: Record<string, number>,\n forcedStopsOffset?: boolean,\n ) {\n this.board.drawBoardMixed({\n ctx: this.ctx,\n reels,\n forcedStops,\n forcedStopsOffset,\n reelsAmount: this.reelsAmount,\n symbolsPerReel: this.symbolsPerReel,\n padSymbols: this.padSymbols,\n })\n }\n\n /**\n * Tumbles the board. All given symbols will be deleted and new symbols will fall from the top.\n */\n tumbleBoard(symbolsToDelete: Array<{ reelIdx: number; rowIdx: number }>) {\n return this.board.tumbleBoard({\n ctx: this.ctx,\n symbolsToDelete,\n reelsAmount: this.reelsAmount,\n symbolsPerReel: this.symbolsPerReel,\n padSymbols: this.padSymbols,\n })\n }\n}\n\ninterface StandaloneBoardOptions {\n ctx: GameContext\n reelsAmount: number\n symbolsPerReel: number[]\n padSymbols: number\n}\n"],"mappings":"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","names":["assert","import_fs","import_assert","import_readline","import_assert","fs","fs","readline","assert","import_assert","assert","import_assert","assert","import_assert","assert","process","assert","path","fs","readline","zlib","import_fs","import_path","import_assert","import_worker_threads","path","assert","fs","import_path","import_assert","import_worker_threads","import_path","path","import_path","path","import_assert","assert","assert","path","import_worker_threads","import_assert","assert","import_assert","assert","neighbors","import_fs","import_path","import_worker_threads","import_fs","import_path","fs","path","path","fs","import_assert","assert"]}
1
+ {"version":3,"sources":["../index.ts","../src/constants.ts","../src/game-config/index.ts","../src/simulation/index.ts","../src/result-set/index.ts","../src/service/index.ts","../src/service/rng.ts","../utils.ts","../src/game-state/index.ts","../src/recorder/index.ts","../src/board/index.ts","../src/game-symbol/index.ts","../src/service/board.ts","../src/service/data.ts","../src/service/game.ts","../src/service/wallet.ts","../src/game-context/index.ts","../src/book/index.ts","../src/wallet/index.ts","../src/analysis/index.ts","../src/analysis/utils.ts","../src/optimizer/index.ts","../src/utils/math-config.ts","../src/utils/setup-file.ts","../src/optimizer/OptimizationConditions.ts","../src/optimizer/OptimizationScaling.ts","../src/optimizer/OptimizationParameters.ts","../src/slot-game/index.ts","../src/createSlotGame.ts","../src/game-mode/index.ts","../src/win-types/index.ts","../src/win-types/LinesWinType.ts","../src/win-types/ClusterWinType.ts","../src/win-types/ManywaysWinType.ts","../src/reel-set/GeneratedReelSet.ts","../src/reel-set/index.ts","../src/reel-set/StaticReelSet.ts","../src/board/StandaloneBoard.ts"],"sourcesContent":["export {\n type InferGameType,\n type AnyUserData,\n type AnyGameModes,\n type AnySymbols,\n type GameHooks,\n type SpinType,\n type Reels,\n} from \"./src/types\"\n\nexport { SPIN_TYPE } from \"./src/constants\"\n\nexport {\n createSlotGame,\n defineGameModes,\n defineSymbols,\n defineUserState,\n} from \"./src/createSlotGame\"\n\nexport { GameMode } from \"./src/game-mode\"\nexport { GameSymbol } from \"./src/game-symbol\"\nexport { ResultSet } from \"./src/result-set\"\n\nexport {\n OptimizationConditions,\n OptimizationParameters,\n OptimizationScaling,\n} from \"./src/optimizer\"\n\nexport { type GameContext } from \"./src/game-context\"\n\nexport { LinesWinType } from \"./src/win-types/LinesWinType\"\nexport { ClusterWinType } from \"./src/win-types/ClusterWinType\"\nexport { ManywaysWinType } from \"./src/win-types/ManywaysWinType\"\nexport { type WinCombination } from \"./src/win-types\"\n\nexport { GeneratedReelSet } from \"./src/reel-set/GeneratedReelSet\"\nexport { StaticReelSet } from \"./src/reel-set/StaticReelSet\"\n\nexport { StandaloneBoard } from \"./src/board/StandaloneBoard\"\n","export const SPIN_TYPE = {\n BASE_GAME: \"basegame\",\n FREE_SPINS: \"freespins\",\n} as const\n","import assert from \"assert\"\nimport { AnyGameModes, AnySymbols, AnyUserData, GameHooks } from \"../types\"\nimport { SPIN_TYPE } from \"../constants\"\n\nexport interface GameConfigOptions<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> {\n /**\n * The unique identifier of the game, used for configuration and identification.\n */\n id: string\n /**\n * The name of the game, used for display purposes.\n */\n name: string\n /**\n * A GameMode is the core structure of a slot, defining the board,\\\n * bet cost, win type, and other properties.\n */\n gameModes: TGameModes\n /**\n * A list of all symbols that will appear on the reels.\n */\n symbols: TSymbols\n /**\n * A mapping from spin type to scatter counts to the number of free spins awarded.\n *\n * @example\n * ```ts\n * scatterToFreespins: {\n * [SPIN_TYPE.BASE_GAME]: {\n * 3: 10,\n * 4: 12,\n * 5: 15,\n * },\n * [SPIN_TYPE.FREE_SPINS]: {\n * 3: 6,\n * 4: 8,\n * 5: 10,\n * },\n * },\n * ```\n */\n scatterToFreespins: Record<string, Record<number, number>>\n /**\n * If set, this will pad the board with symbols on the top and bottom of the reels.\\\n * Useful for teasing symbols right above or below the active board.\n *\n * Default: 1\n */\n padSymbols?: number\n /**\n * The maximum win multiplier of the game, e.g. 5000 for a 5000x max win.\n */\n maxWinX: number\n /**\n * Custom additional state that can be used in game flow logic.\n */\n userState?: TUserState\n /**\n * Hooks are used to inject custom logic at specific points in the game flow.\\\n * Some required hooks must be implemented for certain features to work.\n */\n hooks: GameHooks<TGameModes, TSymbols, TUserState>\n /**\n * If, for some reason, you run your game WITHOUT `cd`ing into the game root,\\\n * you can specify the root directory here to ensure assets are resolved correctly.\\\n * Normally, this is not needed.\n */\n rootDir?: string\n}\n\nexport function createGameConfig<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n>(opts: GameConfigOptions<TGameModes, TSymbols, TUserState>) {\n const symbols = new Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>()\n\n for (const [key, value] of Object.entries(opts.symbols)) {\n assert(value.id === key, `Symbol key \"${key}\" does not match symbol id \"${value.id}\"`)\n symbols.set(key, value as TSymbols[keyof TSymbols])\n }\n\n const getAnticipationTrigger = (spinType: string) => {\n return (\n Math.min(...Object.keys(opts.scatterToFreespins[spinType] || {}).map(Number)) - 1\n )\n }\n\n return {\n padSymbols: opts.padSymbols || 1,\n userState: opts.userState || ({} as TUserState),\n ...opts,\n symbols,\n anticipationTriggers: {\n [SPIN_TYPE.BASE_GAME]: getAnticipationTrigger(SPIN_TYPE.BASE_GAME),\n [SPIN_TYPE.FREE_SPINS]: getAnticipationTrigger(SPIN_TYPE.FREE_SPINS),\n },\n outputDir: \"__build__\",\n rootDir: opts.rootDir || process.cwd(),\n }\n}\n\nexport type GameConfig<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> = Required<Omit<GameConfigOptions<TGameModes, TSymbols, TUserState>, \"symbols\">> & {\n /**\n * A map of all symbols.\n */\n symbols: Map<keyof TSymbols & string, TSymbols[keyof TSymbols]>\n outputDir: string\n rootDir: string\n /**\n * A mapping of spin types to the number of scatter symbols required to trigger anticipation.\n */\n anticipationTriggers: Record<(typeof SPIN_TYPE)[keyof typeof SPIN_TYPE], number>\n}\n","import fs from \"fs\"\nimport path from \"path\"\nimport assert from \"assert\"\nimport zlib from \"zlib\"\nimport readline from \"readline\"\nimport { buildSync } from \"esbuild\"\nimport { Worker, isMainThread, parentPort, workerData } from \"worker_threads\"\nimport { createGameConfig, GameConfigOptions, GameConfig } from \"../game-config\"\nimport { createGameContext, GameContext } from \"../game-context\"\nimport { createDirIfNotExists, JSONL, writeFile } from \"../../utils\"\nimport { SPIN_TYPE } from \"../constants\"\nimport { Book } from \"../book\"\nimport { Recorder, RecordItem } from \"../recorder\"\nimport { Wallet } from \"../wallet\"\nimport { ResultSet } from \"../result-set\"\nimport { pipeline } from \"stream/promises\"\n\nlet completedSimulations = 0\nconst TEMP_FILENAME = \"__temp_compiled_src_IGNORE.js\"\nconst TEMP_FOLDER = \"temp_files\"\n\nexport class Simulation {\n readonly gameConfigOpts: GameConfigOptions\n readonly gameConfig: GameConfig\n readonly simRunsAmount: Partial<Record<string, number>>\n readonly concurrency: number\n private debug = false\n private actualSims = 0\n private library: Map<number, Book>\n private wallet: Wallet\n private recordsWriteStream: fs.WriteStream | undefined\n private hasWrittenRecord = false\n\n constructor(opts: SimulationOptions, gameConfigOpts: GameConfigOptions) {\n this.gameConfig = createGameConfig(gameConfigOpts)\n this.gameConfigOpts = gameConfigOpts\n this.simRunsAmount = opts.simRunsAmount || {}\n this.concurrency = (opts.concurrency || 6) >= 2 ? opts.concurrency || 6 : 2\n this.library = new Map()\n this.wallet = new Wallet()\n\n const gameModeKeys = Object.keys(this.gameConfig.gameModes)\n assert(\n Object.values(this.gameConfig.gameModes)\n .map((m) => gameModeKeys.includes(m.name))\n .every((v) => v === true),\n \"Game mode name must match its key in the gameModes object.\",\n )\n\n if (isMainThread) {\n this.preprocessFiles()\n }\n }\n\n async runSimulation(opts: SimulationConfigOptions) {\n const debug = opts.debug || false\n this.debug = debug\n\n const gameModesToSimulate = Object.keys(this.simRunsAmount)\n const configuredGameModes = Object.keys(this.gameConfig.gameModes)\n\n if (gameModesToSimulate.length === 0) {\n throw new Error(\"No game modes configured for simulation.\")\n }\n\n this.generateReelsetFiles()\n\n // Code that runs when the user executes the simulations.\n // This spawns individual processes and merges the results afterwards.\n if (isMainThread) {\n const debugDetails: Record<string, Record<string, any>> = {}\n\n for (const mode of gameModesToSimulate) {\n completedSimulations = 0\n this.wallet = new Wallet()\n this.library = new Map()\n this.hasWrittenRecord = false\n\n debugDetails[mode] = {}\n\n console.log(`\\nSimulating game mode: ${mode}`)\n console.time(mode)\n\n const runs = this.simRunsAmount[mode] || 0\n\n if (runs <= 0) continue\n\n if (!configuredGameModes.includes(mode)) {\n throw new Error(\n `Tried to simulate game mode \"${mode}\", but it's not configured in the game config.`,\n )\n }\n\n const booksPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n `books_${mode}.jsonl`,\n )\n\n const tempRecordsPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FOLDER,\n `temp_records_${mode}.jsonl`,\n )\n\n createDirIfNotExists(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir),\n )\n createDirIfNotExists(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, TEMP_FOLDER),\n )\n\n this.recordsWriteStream = fs.createWriteStream(tempRecordsPath)\n\n const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode)\n\n await this.spawnWorkersForGameMode({ mode, simNumsToCriteria })\n\n // Merge temporary book files into the final sorted file\n const finalBookStream = fs.createWriteStream(booksPath)\n const numSims = Object.keys(simNumsToCriteria).length\n const chunks = this.getSimRangesForChunks(numSims, this.concurrency!)\n\n let isFirstChunk = true\n for (let i = 0; i < chunks.length; i++) {\n const tempBookPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FOLDER,\n `temp_books_${mode}_${i}.jsonl`,\n )\n\n if (fs.existsSync(tempBookPath)) {\n if (!isFirstChunk) {\n finalBookStream.write(\"\\n\")\n }\n const content = fs.createReadStream(tempBookPath)\n for await (const chunk of content) {\n finalBookStream.write(chunk)\n }\n fs.rmSync(tempBookPath)\n isFirstChunk = false\n }\n }\n finalBookStream.end()\n await new Promise<void>((resolve) => finalBookStream.on(\"finish\", resolve))\n\n if (this.recordsWriteStream) {\n await new Promise<void>((resolve) => {\n this.recordsWriteStream!.end(() => {\n resolve()\n })\n })\n this.recordsWriteStream = undefined\n }\n\n createDirIfNotExists(\n path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"optimization_files\",\n ),\n )\n createDirIfNotExists(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, \"publish_files\"),\n )\n\n console.log(`Writing final files for game mode: ${mode} ...`)\n this.writeLookupTableCSV(mode)\n this.writeLookupTableSegmentedCSV(mode)\n this.writeRecords(mode)\n await this.writeBooksJson(mode)\n this.writeIndexJson()\n console.log(`Mode ${mode} done!`)\n\n debugDetails[mode].rtp =\n this.wallet.getCumulativeWins() / (runs * this.gameConfig.gameModes[mode]!.cost)\n\n debugDetails[mode].wins = this.wallet.getCumulativeWins()\n debugDetails[mode].winsPerSpinType = this.wallet.getCumulativeWinsPerSpinType()\n\n console.timeEnd(mode)\n }\n\n console.log(\"\\n=== SIMULATION SUMMARY ===\")\n console.table(debugDetails)\n }\n\n let desiredSims = 0\n let actualSims = 0\n const criteriaToRetries: Record<string, number> = {}\n\n // Code that runs for individual processes\n if (!isMainThread) {\n const { mode, simStart, simEnd, index } = workerData\n\n const simNumsToCriteria = ResultSet.assignCriteriaToSimulations(this, mode)\n\n // Run each simulation until the criteria is met.\n for (let simId = simStart; simId <= simEnd; simId++) {\n if (this.debug) desiredSims++\n\n const criteria = simNumsToCriteria[simId] || \"N/A\"\n\n if (!criteriaToRetries[criteria]) {\n criteriaToRetries[criteria] = 0\n }\n\n this.runSingleSimulation({ simId, mode, criteria, index })\n\n if (this.debug) {\n criteriaToRetries[criteria] += this.actualSims - 1\n actualSims += this.actualSims\n }\n }\n\n if (this.debug) {\n console.log(`Desired ${desiredSims}, Actual ${actualSims}`)\n console.log(`Retries per criteria:`, criteriaToRetries)\n }\n\n parentPort?.postMessage({\n type: \"done\",\n workerNum: index,\n })\n }\n }\n\n /**\n * Runs all simulations for a specific game mode.\n */\n async spawnWorkersForGameMode(opts: {\n mode: string\n simNumsToCriteria: Record<number, string>\n }) {\n const { mode, simNumsToCriteria } = opts\n\n const numSims = Object.keys(simNumsToCriteria).length\n const simRangesPerChunk = this.getSimRangesForChunks(numSims, this.concurrency!)\n\n await Promise.all(\n simRangesPerChunk.map(([simStart, simEnd], index) => {\n return this.callWorker({\n basePath: path.join(this.gameConfig.rootDir, this.gameConfig.outputDir),\n mode,\n simStart,\n simEnd,\n index,\n totalSims: numSims,\n })\n }),\n )\n }\n\n async callWorker(opts: {\n basePath: string\n mode: string\n simStart: number\n simEnd: number\n index: number\n totalSims: number\n }) {\n const { mode, simEnd, simStart, basePath, index, totalSims } = opts\n\n function logArrowProgress(current: number, total: number) {\n const percentage = (current / total) * 100\n const progressBarLength = 50\n const filledLength = Math.round((progressBarLength * current) / total)\n const bar = \"█\".repeat(filledLength) + \"-\".repeat(progressBarLength - filledLength)\n process.stdout.write(`\\r[${bar}] ${percentage.toFixed(2)}% (${current}/${total})`)\n if (current === total) {\n process.stdout.write(\"\\n\")\n }\n }\n\n return new Promise((resolve, reject) => {\n const scriptPath = path.join(basePath, TEMP_FILENAME)\n\n const worker = new Worker(scriptPath, {\n workerData: {\n mode,\n simStart,\n simEnd,\n index,\n },\n })\n\n const tempBookPath = path.join(\n basePath,\n TEMP_FOLDER,\n `temp_books_${mode}_${index}.jsonl`,\n )\n const bookStream = fs.createWriteStream(tempBookPath)\n\n worker.on(\"message\", (msg) => {\n if (msg.type === \"log\") {\n } else if (msg.type === \"complete\") {\n completedSimulations++\n\n if (completedSimulations % 250 === 0) {\n logArrowProgress(completedSimulations, totalSims)\n }\n\n // Write data to global library\n const book = Book.fromSerialized(msg.book)\n\n const bookData = {\n id: book.id,\n payoutMultiplier: book.payout,\n events: book.events,\n }\n\n const prefix = book.id === simStart ? \"\" : \"\\n\"\n bookStream.write(prefix + JSONL.stringify([bookData]))\n\n book.events = []\n this.library.set(book.id, book)\n\n if (this.recordsWriteStream) {\n for (const record of msg.records) {\n const recordPrefix = this.hasWrittenRecord ? \"\\n\" : \"\"\n this.recordsWriteStream.write(recordPrefix + JSONL.stringify([record]))\n this.hasWrittenRecord = true\n }\n }\n\n this.wallet.mergeSerialized(msg.wallet)\n } else if (msg.type === \"done\") {\n resolve(true)\n }\n })\n\n worker.on(\"error\", (error) => {\n process.stdout.write(`\\n${error.message}\\n`)\n process.stdout.write(`\\n${error.stack}\\n`)\n reject(error)\n })\n\n worker.on(\"exit\", (code) => {\n if (code !== 0) {\n reject(new Error(`Worker stopped with exit code ${code}`))\n }\n })\n })\n }\n\n /**\n * Will run a single simulation until the specified criteria is met.\n */\n runSingleSimulation(opts: {\n simId: number\n mode: string\n criteria: string\n index: number\n }) {\n const { simId, mode, criteria } = opts\n\n const ctx = createGameContext({\n config: this.gameConfig,\n })\n\n ctx.state.currentGameMode = mode\n ctx.state.currentSimulationId = simId\n ctx.state.isCriteriaMet = false\n\n const resultSet = ctx.services.game.getResultSetByCriteria(\n ctx.state.currentGameMode,\n criteria,\n )\n\n ctx.state.currentResultSet = resultSet\n\n while (!ctx.state.isCriteriaMet) {\n this.actualSims++\n this.resetSimulation(ctx)\n\n this.handleGameFlow(ctx)\n\n if (resultSet.meetsCriteria(ctx)) {\n ctx.state.isCriteriaMet = true\n }\n }\n\n ctx.services.wallet._getWallet().writePayoutToBook(ctx)\n ctx.services.wallet._getWallet().confirmWins(ctx)\n\n if (ctx.services.data._getBook().payout >= ctx.config.maxWinX) {\n ctx.state.triggeredMaxWin = true\n }\n\n ctx.services.data.record({\n criteria: resultSet.criteria,\n })\n\n ctx.config.hooks.onSimulationAccepted?.(ctx)\n\n this.confirmRecords(ctx)\n\n parentPort?.postMessage({\n type: \"complete\",\n simId,\n book: ctx.services.data._getBook().serialize(),\n wallet: ctx.services.wallet._getWallet().serialize(),\n records: ctx.services.data._getRecords(),\n })\n }\n\n /**\n * If a simulation does not meet the required criteria, reset the state to run it again.\n *\n * This also runs once before each simulation to ensure a clean state.\n */\n protected resetSimulation(ctx: GameContext) {\n this.resetState(ctx)\n ctx.services.board.resetBoard()\n ctx.services.data._setRecorder(new Recorder())\n ctx.services.wallet._setWallet(new Wallet())\n ctx.services.data._setBook(\n new Book({\n id: ctx.state.currentSimulationId,\n criteria: ctx.state.currentResultSet.criteria,\n }),\n )\n }\n\n protected resetState(ctx: GameContext) {\n ctx.services.rng.setSeedIfDifferent(ctx.state.currentSimulationId)\n ctx.state.currentSpinType = SPIN_TYPE.BASE_GAME\n ctx.state.currentFreespinAmount = 0\n ctx.state.totalFreespinAmount = 0\n ctx.state.triggeredMaxWin = false\n ctx.state.triggeredFreespins = false\n ctx.state.userData = ctx.config.userState || {}\n }\n\n /**\n * Contains and executes the entire game logic:\n * - Drawing the board\n * - Evaluating wins\n * - Updating wallet\n * - Handling free spins\n * - Recording events\n *\n * You can customize the game flow by implementing the `onHandleGameFlow` hook in the game configuration.\n */\n protected handleGameFlow(ctx: GameContext) {\n this.gameConfig.hooks.onHandleGameFlow(ctx)\n }\n\n /**\n * Creates a CSV file in the format \"simulationId,weight,payout\".\n *\n * `weight` defaults to 1.\n */\n private writeLookupTableCSV(gameMode: string) {\n const rows: string[] = []\n\n for (const [bookId, book] of this.library.entries()) {\n rows.push(`${book.id},1,${Math.round(book.payout)}`)\n }\n\n rows.sort((a, b) => Number(a.split(\",\")[0]) - Number(b.split(\",\")[0]))\n\n let outputFileName = `lookUpTable_${gameMode}.csv`\n let outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n outputFileName,\n )\n writeFile(outputFilePath, rows.join(\"\\n\"))\n\n outputFileName = `lookUpTable_${gameMode}_0.csv`\n outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"publish_files\",\n outputFileName,\n )\n writeFile(outputFilePath, rows.join(\"\\n\"))\n\n return outputFilePath\n }\n\n /**\n * Creates a CSV file in the format \"simulationId,criteria,payoutBase,payoutFreespins\".\n */\n private writeLookupTableSegmentedCSV(gameMode: string) {\n const rows: string[] = []\n\n for (const [bookId, book] of this.library.entries()) {\n rows.push(`${book.id},${book.criteria},${book.basegameWins},${book.freespinsWins}`)\n }\n\n rows.sort((a, b) => Number(a.split(\",\")[0]) - Number(b.split(\",\")[0]))\n\n const outputFileName = `lookUpTableSegmented_${gameMode}.csv`\n\n const outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n outputFileName,\n )\n writeFile(outputFilePath, rows.join(\"\\n\"))\n\n return outputFilePath\n }\n\n private async writeRecords(mode: string) {\n const tempRecordsPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FOLDER,\n `temp_records_${mode}.jsonl`,\n )\n\n const forceRecordsPath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n `force_record_${mode}.json`,\n )\n\n // Use a local Map to aggregate records efficiently without cluttering the main Recorder\n // Key is the stringified search criteria\n const aggregatedRecords = new Map<string, RecordItem>()\n\n if (fs.existsSync(tempRecordsPath)) {\n const fileStream = fs.createReadStream(tempRecordsPath)\n\n const rl = readline.createInterface({\n input: fileStream,\n crlfDelay: Infinity,\n })\n\n for await (const line of rl) {\n if (line.trim() === \"\") continue\n const record: RecordItem = JSON.parse(line)\n\n const key = JSON.stringify(record.search)\n\n let existing = aggregatedRecords.get(key)\n if (!existing) {\n existing = {\n search: record.search,\n timesTriggered: 0,\n bookIds: [],\n }\n aggregatedRecords.set(key, existing)\n }\n\n existing.timesTriggered += record.timesTriggered\n\n for (const bookId of record.bookIds) {\n existing.bookIds.push(bookId)\n }\n }\n }\n\n fs.rmSync(forceRecordsPath, { force: true })\n\n const writeStream = fs.createWriteStream(forceRecordsPath, { encoding: \"utf-8\" })\n writeStream.write(\"[\\n\")\n\n let isFirst = true\n for (const record of aggregatedRecords.values()) {\n if (!isFirst) {\n writeStream.write(\",\\n\")\n }\n writeStream.write(JSON.stringify(record))\n isFirst = false\n }\n\n writeStream.write(\"\\n]\")\n writeStream.end()\n\n await new Promise<void>((resolve) => {\n writeStream.on(\"finish\", () => resolve())\n })\n\n fs.rmSync(tempRecordsPath, { force: true })\n }\n\n private writeIndexJson() {\n const outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"publish_files\",\n \"index.json\",\n )\n\n const modes = Object.keys(this.simRunsAmount).map((id) => {\n const mode = this.gameConfig.gameModes[id]\n assert(mode, `Game mode \"${id}\" not found in game config.`)\n\n return {\n name: mode.name,\n cost: mode.cost,\n events: `books_${mode.name}.jsonl.zst`,\n weights: `lookUpTable_${mode.name}_0.csv`,\n }\n })\n\n writeFile(outputFilePath, JSON.stringify({ modes }, null, 2))\n }\n\n private async writeBooksJson(gameMode: string) {\n const outputFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n `books_${gameMode}.jsonl`,\n )\n\n const compressedFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n \"publish_files\",\n `books_${gameMode}.jsonl.zst`,\n )\n\n fs.rmSync(compressedFilePath, { force: true })\n\n if (fs.existsSync(outputFilePath)) {\n await pipeline(\n fs.createReadStream(outputFilePath),\n zlib.createZstdCompress(),\n fs.createWriteStream(compressedFilePath),\n )\n }\n }\n\n /**\n * Compiles user configured game to JS for use in different Node processes\n */\n private preprocessFiles() {\n const builtFilePath = path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FILENAME,\n )\n fs.rmSync(builtFilePath, { force: true })\n buildSync({\n entryPoints: [this.gameConfig.rootDir],\n bundle: true,\n platform: \"node\",\n outfile: path.join(\n this.gameConfig.rootDir,\n this.gameConfig.outputDir,\n TEMP_FILENAME,\n ),\n external: [\"esbuild\"],\n })\n }\n\n private getSimRangesForChunks(total: number, chunks: number): [number, number][] {\n const base = Math.floor(total / chunks)\n const remainder = total % chunks\n const result: [number, number][] = []\n\n let current = 1\n\n for (let i = 0; i < chunks; i++) {\n const size = base + (i < remainder ? 1 : 0)\n const start = current\n const end = current + size - 1\n result.push([start, end])\n current = end + 1\n }\n\n return result\n }\n\n /**\n * Generates reelset CSV files for all game modes.\n */\n private generateReelsetFiles() {\n for (const mode of Object.values(this.gameConfig.gameModes)) {\n if (mode.reelSets && mode.reelSets.length > 0) {\n for (const reelSet of Object.values(mode.reelSets)) {\n reelSet.associatedGameModeName = mode.name\n reelSet.generateReels(this.gameConfig)\n }\n } else {\n throw new Error(\n `Game mode \"${mode.name}\" has no reel sets defined. Cannot generate reelset files.`,\n )\n }\n }\n }\n\n /**\n * Confirms all pending records and adds them to the main records list.\n */\n confirmRecords(ctx: GameContext) {\n const recorder = ctx.services.data._getRecorder()\n\n for (const pendingRecord of recorder.pendingRecords) {\n const search = Object.entries(pendingRecord.properties)\n .map(([name, value]) => ({ name, value }))\n .sort((a, b) => a.name.localeCompare(b.name))\n\n let record = recorder.records.find((r) => {\n if (r.search.length !== search.length) return false\n for (let i = 0; i < r.search.length; i++) {\n if (r.search[i]!.name !== search[i]!.name) return false\n if (r.search[i]!.value !== search[i]!.value) return false\n }\n return true\n })\n if (!record) {\n record = {\n search,\n timesTriggered: 0,\n bookIds: [],\n }\n recorder.records.push(record)\n }\n record.timesTriggered++\n if (!record.bookIds.includes(pendingRecord.bookId)) {\n record.bookIds.push(pendingRecord.bookId)\n }\n }\n\n recorder.pendingRecords = []\n }\n}\n\nexport type SimulationOptions = {\n /**\n * Object containing the game modes and their respective simulation runs amount.\n */\n simRunsAmount: Partial<Record<string, number>>\n /**\n * Number of concurrent processes to use for simulations.\n *\n * Default: 6\n */\n concurrency?: number\n}\n\nexport type SimulationConfigOptions = {\n debug?: boolean\n}\n","import assert from \"assert\"\nimport { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\nimport { GameContext } from \"../game-context\"\nimport { Simulation } from \"../simulation\"\nimport { RandomNumberGenerator } from \"../service/rng\"\nimport { copy } from \"../../utils\"\nimport { Wallet } from \"../wallet\"\nimport { SPIN_TYPE } from \"../constants\"\n\nexport class ResultSet<TUserState extends AnyUserData> {\n criteria: string\n quota: number\n multiplier?: number\n reelWeights: ReelWeights<TUserState>\n userData?: Record<string, any>\n forceMaxWin?: boolean\n forceFreespins?: boolean\n evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => boolean\n\n constructor(opts: ResultSetOpts<TUserState>) {\n this.criteria = opts.criteria\n this.quota = opts.quota\n this.multiplier = opts.multiplier\n this.reelWeights = opts.reelWeights\n this.userData = opts.userData\n this.forceMaxWin = opts.forceMaxWin\n this.forceFreespins = opts.forceFreespins\n this.evaluate = opts.evaluate\n }\n\n static assignCriteriaToSimulations(ctx: Simulation, gameModeName: string) {\n const rng = new RandomNumberGenerator()\n rng.setSeed(0)\n\n assert(ctx.simRunsAmount, \"Simulation configuration is not set.\")\n\n const simNums = ctx.simRunsAmount[gameModeName]\n const resultSets = ctx.gameConfig.gameModes[gameModeName]?.resultSets\n\n if (!resultSets || resultSets.length === 0) {\n throw new Error(`No ResultSets found for game mode: ${gameModeName}.`)\n }\n\n if (simNums === undefined || simNums <= 0) {\n throw new Error(`No simulations configured for game mode \"${gameModeName}\".`)\n }\n\n const totalQuota = resultSets.reduce((sum, rs) => sum + rs.quota, 0)\n\n const numberOfSimsForCriteria: Record<string, number> = Object.fromEntries(\n resultSets.map((rs) => {\n const normalizedQuota = totalQuota > 0 ? rs.quota / totalQuota : 0\n return [rs.criteria, Math.max(Math.floor(normalizedQuota * simNums), 1)]\n }),\n )\n\n let totalSims = Object.values(numberOfSimsForCriteria).reduce(\n (sum, num) => sum + num,\n 0,\n )\n\n let reduceSims = totalSims > simNums\n\n const criteriaToWeights = Object.fromEntries(\n resultSets.map((rs) => [rs.criteria, rs.quota]),\n )\n\n while (totalSims != simNums) {\n const rs = rng.weightedRandom(criteriaToWeights)\n if (reduceSims && numberOfSimsForCriteria[rs]! > 1) {\n numberOfSimsForCriteria[rs]! -= 1\n } else if (!reduceSims) {\n numberOfSimsForCriteria[rs]! += 1\n }\n\n totalSims = Object.values(numberOfSimsForCriteria).reduce(\n (sum, num) => sum + num,\n 0,\n )\n reduceSims = totalSims > simNums\n }\n\n let allCriteria: string[] = []\n const simNumsToCriteria: Record<number, string> = {}\n\n Object.entries(numberOfSimsForCriteria).forEach(([criteria, num]) => {\n for (let i = 0; i <= num; i++) {\n allCriteria.push(criteria)\n }\n })\n\n allCriteria = rng.shuffle(allCriteria)\n\n for (let i = 1; i <= Math.min(simNums, allCriteria.length); i++) {\n simNumsToCriteria[i] = allCriteria[i]!\n }\n\n return simNumsToCriteria\n }\n\n /**\n * Checks if core criteria is met, e.g. target multiplier or max win.\n */\n meetsCriteria(ctx: GameContext) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n const customEval = this.evaluate?.(copy(ctx))\n\n const freespinsMet = this.forceFreespins ? ctx.state.triggeredFreespins : true\n\n const wallet = ctx.services.wallet._getWallet()\n\n const multiplierMet =\n this.multiplier !== undefined\n ? wallet.getCurrentWin() === this.multiplier && !this.forceMaxWin\n : wallet.getCurrentWin() > 0 && (!this.forceMaxWin || true)\n\n const maxWinMet = this.forceMaxWin\n ? wallet.getCurrentWin() >= ctx.config.maxWinX\n : true\n\n const coreCriteriaMet = freespinsMet && multiplierMet && maxWinMet\n\n const finalResult =\n customEval !== undefined ? coreCriteriaMet && customEval === true : coreCriteriaMet\n\n if (this.forceMaxWin && maxWinMet) {\n ctx.services.data.record({\n maxwin: true,\n })\n }\n\n return finalResult\n }\n}\n\ninterface ResultSetOpts<TUserState extends AnyUserData> {\n /**\n * A short string to describe the criteria for this ResultSet.\n */\n criteria: string\n /**\n * The quota of spins, out of the total simulations, that must be forced to meet the specified criteria.\\\n * **Float from 0 to 1. Total quota of all ResultSets in a GameMode must be 1.**\n */\n quota: number\n /**\n * The required multiplier for a simulated spin to be accepted.\n */\n multiplier?: number\n /**\n * Configure the weights of the reels in this ResultSet.\n *\n * If you need to support dynamic / special reel weights based on the simulation context,\\\n * you can provide an `evaluate` function that returns the desired weights.\n *\n * If the `evaluate` function returns a falsy value, the usual spin type based weights will be used.\n *\n * @example\n * ```ts\n * new ResultSet({\n * criteria: \"superFreespins\",\n * quota: 0.05,\n * forceFreespins: true,\n * reelWeights: {\n * [SPIN_TYPE.BASE_GAME]: { base1: 1 },\n * [SPIN_TYPE.FREE_SPINS]: { bonus1: 1, bonus2: 2 },\n * evaluate: (ctx) => {\n * if (ctx.state.userData.triggeredSuperFreespins) {\n * return { superbonus: 1 }\n * }\n * }\n * },\n * userData: { forceSuperFreespins: true },\n * }),\n * ```\n */\n reelWeights: ReelWeights<TUserState>\n /**\n * Optional data to use when evaluating the criteria.\\\n * This can be used to pass additional context or parameters needed for the evaluation.\n */\n userData?: Record<string, any>\n /**\n * If set, this will force the game to always trigger a max win.\n */\n forceMaxWin?: boolean\n /**\n * If set, this will force the game to always trigger free spins.\n */\n forceFreespins?: boolean\n /**\n * Custom function to evaluate if the criteria is met.\n *\n * E.g. use this to check for free spins that upgraded to super free spins\\\n * or other arbitrary simulation criteria.\n */\n evaluate?: (ctx: GameContext<AnyGameModes, AnySymbols, TUserState>) => boolean\n}\n\ninterface ReelWeights<TUserState extends AnyUserData> {\n [SPIN_TYPE.BASE_GAME]: Record<string, number>\n [SPIN_TYPE.FREE_SPINS]: Record<string, number>\n evaluate?: (\n ctx: GameContext<AnyGameModes, AnySymbols, TUserState>,\n ) => Record<string, number> | undefined | null | false\n}\n","import { GameContext } from \"../game-context\"\n\nexport class AbstractService {\n /**\n * Function that returns the current game context.\n */\n protected ctx: () => GameContext\n\n constructor(ctx: () => GameContext) {\n this.ctx = ctx\n }\n}\n","import { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\n\nexport class RngService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n protected rng = new RandomNumberGenerator()\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n /**\n * Random weighted selection from a set of items.\n */\n weightedRandom = this.rng.weightedRandom.bind(this.rng)\n\n /**\n * Selects a random item from an array.\n */\n randomItem = this.rng.randomItem.bind(this.rng)\n\n /**\n * Shuffles an array.\n */\n shuffle = this.rng.shuffle.bind(this.rng)\n\n /**\n * Generates a random float between two values.\n */\n randomFloat = this.rng.randomFloat.bind(this.rng)\n\n /**\n * Sets the seed for the RNG.\n */\n setSeedIfDifferent = this.rng.setSeedIfDifferent.bind(this.rng)\n}\n\nexport class RandomNumberGenerator {\n mIdum: number\n mIy: number\n mIv: Array<number>\n NTAB: number\n IA: number\n IM: number\n IQ: number\n IR: number\n NDIV: number\n AM: number\n RNMX: number\n\n protected _currentSeed: number = 0\n\n constructor() {\n this.mIdum = 0\n this.mIy = 0\n this.mIv = []\n\n this.NTAB = 32\n this.IA = 16807\n this.IM = 2147483647\n this.IQ = 127773\n this.IR = 2836\n this.NDIV = 1 + (this.IM - 1) / this.NTAB\n this.AM = 1.0 / this.IM\n this.RNMX = 1.0 - 1.2e-7\n }\n\n getCurrentSeed() {\n return this._currentSeed\n }\n\n protected setCurrentSeed(seed: number) {\n this._currentSeed = seed\n }\n\n setSeed(seed: number): void {\n this.mIdum = seed\n this.setCurrentSeed(seed)\n\n if (seed >= 0) {\n this.mIdum = -seed\n }\n\n this.mIy = 0\n }\n\n setSeedIfDifferent(seed: number) {\n if (this.getCurrentSeed() !== seed) {\n this.setSeed(seed)\n }\n }\n\n generateRandomNumber(): number {\n let k: number\n let j: number\n\n if (this.mIdum <= 0 || this.mIy === 0) {\n if (-this.mIdum < 1) {\n this.mIdum = 1\n } else {\n this.mIdum = -this.mIdum\n }\n\n for (j = this.NTAB + 7; j >= 0; j -= 1) {\n k = Math.floor(this.mIdum / this.IQ)\n this.mIdum = Math.floor(this.IA * (this.mIdum - k * this.IQ) - this.IR * k)\n\n if (this.mIdum < 0) {\n this.mIdum += this.IM\n }\n\n if (j < this.NTAB) {\n this.mIv[j] = this.mIdum\n }\n }\n\n ;[this.mIy as any] = this.mIv\n }\n\n k = Math.floor(this.mIdum / this.IQ)\n this.mIdum = Math.floor(this.IA * (this.mIdum - k * this.IQ) - this.IR * k)\n\n if (this.mIdum < 0) {\n this.mIdum += this.IM\n }\n\n j = Math.floor(this.mIy / this.NDIV)\n\n this.mIy = Math.floor(this.mIv[j] as any)\n this.mIv[j] = this.mIdum\n\n return this.mIy\n }\n\n randomFloat(low: number, high: number): number {\n let float: number = this.AM * this.generateRandomNumber()\n\n if (float > this.RNMX) {\n float = this.RNMX\n }\n\n return float * (high - low) + low\n }\n\n weightedRandom<T extends Record<string, number>>(weights: T) {\n const totalWeight = Object.values(weights).reduce(\n (sum: number, weight) => sum + (weight as number),\n 0,\n )\n const randomValue = this.randomFloat(0, 1) * totalWeight\n\n let cumulativeWeight = 0\n for (const [key, weight] of Object.entries(weights)) {\n cumulativeWeight += weight as number\n if (randomValue < cumulativeWeight) {\n return key\n }\n }\n\n throw new Error(\"No item selected in weighted random selection.\")\n }\n\n randomItem<T>(array: T[]) {\n if (array.length === 0) {\n throw new Error(\"Cannot select a random item from an empty array.\")\n }\n const randomIndex = Math.floor(this.randomFloat(0, 1) * array.length)\n return array[randomIndex]!\n }\n\n shuffle<T>(array: T[]): T[] {\n const newArray = [...array]\n let currentIndex = newArray.length,\n randomIndex\n\n while (currentIndex != 0) {\n randomIndex = Math.floor(this.randomFloat(0, 1) * currentIndex)\n currentIndex--\n ;[newArray[currentIndex] as any, newArray[randomIndex] as any] = [\n newArray[randomIndex],\n newArray[currentIndex],\n ]\n }\n\n return newArray\n }\n}\n","import fs from \"fs\"\nimport readline from \"readline\"\nimport { BoardService } from \"./src/service/board\"\n\nexport function createDirIfNotExists(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true })\n }\n}\n\nexport function writeJsonFile(filePath: string, data: object | any[]) {\n try {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), {\n encoding: \"utf8\",\n })\n } catch (error) {\n throw new Error(`Failed to write JSON file at ${filePath}: ${error}`)\n }\n}\n\nexport function writeFile(filePath: string, data: string) {\n try {\n fs.writeFileSync(filePath, data, { encoding: \"utf8\" })\n } catch (error) {\n throw new Error(`Failed to write file at ${filePath}: ${error}`)\n }\n}\n\n/**\n * Creates a deep copy of an object or array.\n */\nexport function copy<T>(obj: T): T {\n return JSON.parse(JSON.stringify(obj))\n}\n\n/**\n * Prints the board to the console in a readable format.\n */\nexport function printBoard(board: BoardService) {\n const fullBoard = board.getBoardReels().map((reel, ridx) => {\n return [...board.getPaddingTop()[ridx]!, ...reel, ...board.getPaddingBottom()[ridx]!]\n })\n\n const rows = Math.max(...fullBoard.map((reel) => reel.length))\n const cellWidth = 4 // inner width of symbol area\n\n const padSymbol = (sym: string) => {\n if (sym.length > cellWidth) sym = sym.slice(0, cellWidth)\n const left = Math.floor((cellWidth - sym.length) / 2)\n const right = cellWidth - sym.length - left\n return \" \".repeat(left) + sym + \" \".repeat(right)\n }\n\n const maxTop = Math.max(...board.getPaddingTop().map((p) => p?.length ?? 0))\n const maxBottom = Math.max(...board.getPaddingBottom().map((p) => p?.length ?? 0))\n const boardStart = maxTop\n const boardEnd = rows - maxBottom - 1\n\n const makeSeparator = () => {\n return fullBoard.map(() => `═${\"═\".repeat(cellWidth)}═ `).join(\"\")\n }\n\n for (let row = 0; row < rows; row++) {\n if (row === boardStart) {\n console.log(makeSeparator()) // top border of board\n }\n\n let top = \"\"\n let mid = \"\"\n let bot = \"\"\n for (let col = 0; col < fullBoard.length; col++) {\n const sym = fullBoard[col]![row]?.id ?? \" \"\n const padded = padSymbol(sym)\n top += `┌${\"─\".repeat(cellWidth)}┐ `\n mid += `│${padded}│ `\n bot += `└${\"─\".repeat(cellWidth)}┘ `\n }\n\n console.log(top)\n console.log(mid)\n console.log(bot)\n\n if (row === boardEnd) {\n console.log(makeSeparator()) // bottom border of board\n }\n }\n}\n\nexport function weightedAverage(dist: Record<number, number>) {\n const keys = Object.keys(dist).map(Number)\n const values = Object.values(dist)\n\n const totalWeight = round(\n values.reduce((a, b) => a + b, 0),\n 6,\n )\n const weightedSum = keys.reduce((sum, key, i) => sum + key * values[i]!, 0)\n\n return weightedSum / totalWeight\n}\n\nexport class JSONL {\n public static stringify(array: object[]): string {\n return array.map((object) => JSON.stringify(object)).join(\"\\n\")\n }\n\n public static parse<T>(jsonl: string): Array<T> {\n return jsonl\n .split(\"\\n\")\n .filter((s) => s !== \"\")\n .map((str) => JSON.parse(str))\n }\n\n public static async convertToJson(\n inputPath: string,\n outputPath: string,\n ): Promise<void> {\n const writeStream = fs.createWriteStream(outputPath, { encoding: \"utf-8\" })\n writeStream.write(\"[\\n\")\n\n const rl = readline.createInterface({\n input: fs.createReadStream(inputPath),\n crlfDelay: Infinity,\n })\n\n let isFirst = true\n\n for await (const line of rl) {\n if (line.trim() === \"\") continue\n if (!isFirst) {\n writeStream.write(\",\\n\")\n }\n writeStream.write(line)\n isFirst = false\n }\n\n writeStream.write(\"\\n]\")\n writeStream.end()\n\n return new Promise<void>((resolve, reject) => {\n writeStream.on(\"finish\", () => resolve())\n writeStream.on(\"error\", reject)\n })\n }\n}\n\nexport function round(value: number, decimals: number) {\n return Number(Math.round(Number(value + \"e\" + decimals)) + \"e-\" + decimals)\n}\n","import { AnyUserData, SpinType } from \"../types\"\nimport { SPIN_TYPE } from \"../constants\"\nimport { ResultSet } from \"../result-set\"\n\nexport interface GameStateOptions<TUserState extends AnyUserData> {\n currentSimulationId: number\n /**\n * e.g. \"base\", \"freespins\", etc. (depending on the game config)\n */\n currentGameMode: string\n /**\n * Spin type constant as defined in `SPIN_TYPE`\n */\n currentSpinType: SpinType\n /**\n * The current ResultSet for the active simulation run.\n */\n currentResultSet: ResultSet<any>\n /**\n * Whether the criteria in the ResultSet for the current simulation has been met.\n */\n isCriteriaMet: boolean\n /**\n * Number of freespins remaining in the current freespin round.\n */\n currentFreespinAmount: number\n /**\n * Total amount of freespins awarded during the active simulation.\n */\n totalFreespinAmount: number\n /**\n * Custom user data that can be used in game flow logic.\n */\n userData: TUserState\n /**\n * Whether a max win has been triggered during the active simulation.\n */\n triggeredMaxWin: boolean\n /**\n * Whether freespins have been triggered during the active simulation.\n */\n triggeredFreespins: boolean\n}\n\nexport function createGameState<TUserState extends AnyUserData = AnyUserData>(\n opts?: Partial<GameStateOptions<TUserState>>,\n) {\n return {\n currentSimulationId: opts?.currentSimulationId || 0,\n currentGameMode: opts?.currentGameMode || \"N/A\",\n currentSpinType: opts?.currentSpinType || SPIN_TYPE.BASE_GAME,\n currentResultSet:\n opts?.currentResultSet ||\n new ResultSet({\n criteria: \"N/A\",\n quota: 0,\n reelWeights: {\n [SPIN_TYPE.BASE_GAME]: {},\n [SPIN_TYPE.FREE_SPINS]: {},\n },\n }),\n isCriteriaMet: opts?.isCriteriaMet || false,\n currentFreespinAmount: opts?.currentFreespinAmount || 0,\n totalFreespinAmount: opts?.totalFreespinAmount || 0,\n userData: opts?.userData || ({} as TUserState),\n triggeredMaxWin: opts?.triggeredMaxWin || false,\n triggeredFreespins: opts?.triggeredFreespins || false,\n }\n}\n\nexport type GameState<TUserState extends AnyUserData = AnyUserData> = ReturnType<\n typeof createGameState<TUserState>\n>\n","export class Recorder {\n records: RecordItem[]\n pendingRecords: PendingRecord[]\n\n constructor() {\n this.records = []\n this.pendingRecords = []\n }\n}\n\nexport interface PendingRecord {\n bookId: number\n properties: Record<string, string>\n}\n\nexport interface RecordItem {\n search: Array<{ name: string; value: string }>\n timesTriggered: number\n bookIds: number[]\n}\n","import assert from \"assert\"\nimport { GameContext } from \"../game-context\"\nimport { Reels } from \"../types\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { WinCombination } from \"../win-types\"\n\n/**\n * This general board class is designed to function with or without a game context.\n */\nexport class Board {\n /**\n * The current reels on the board.\\\n * Includes only the visible symbols (without padding).\n */\n reels: Reels\n /**\n * The top padding symbols on the board.\\\n * These are the symbols above the visible area.\n */\n paddingTop: Reels\n /**\n * The bottom padding symbols on the board.\\\n * These are the symbols below the visible area.\n */\n paddingBottom: Reels\n /**\n * The anticipation values for each reel on the board.\\\n * Used for triggering anticipation effects.\n */\n anticipation: boolean[]\n lastDrawnReelStops: number[]\n lastUsedReels: Reels\n\n constructor() {\n this.reels = []\n this.paddingTop = []\n this.paddingBottom = []\n this.anticipation = []\n this.lastDrawnReelStops = []\n this.lastUsedReels = []\n }\n\n getSymbol(reelIndex: number, rowIndex: number) {\n return this.reels[reelIndex]?.[rowIndex]\n }\n\n setSymbol(reelIndex: number, rowIndex: number, symbol: GameSymbol) {\n this.reels[reelIndex] = this.reels[reelIndex] || []\n this.reels[reelIndex]![rowIndex] = symbol\n }\n\n makeEmptyReels(opts: { ctx: GameContext; reelsAmount?: number }) {\n const length =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n assert(length, \"Cannot make empty reels without context or reelsAmount.\")\n\n return Array.from({ length }, () => [])\n }\n\n countSymbolsOnReel(\n symbolOrProperties: GameSymbol | Record<string, any>,\n reelIndex: number,\n ) {\n let total = 0\n\n for (const symbol of this.reels[reelIndex]!) {\n let matches = true\n if (symbolOrProperties instanceof GameSymbol) {\n if (symbol.id !== symbolOrProperties.id) matches = false\n } else {\n for (const [key, value] of Object.entries(symbolOrProperties)) {\n if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {\n matches = false\n break\n }\n }\n }\n if (matches) {\n total++\n }\n }\n\n return total\n }\n\n countSymbolsOnBoard(\n symbolOrProperties: GameSymbol | Record<string, any>,\n ): [number, Record<number, number>] {\n let total = 0\n const onReel: Record<number, number> = {}\n\n for (const [ridx, reel] of this.reels.entries()) {\n for (const symbol of reel) {\n let matches = true\n\n if (symbolOrProperties instanceof GameSymbol) {\n if (symbol.id !== symbolOrProperties.id) matches = false\n } else {\n for (const [key, value] of Object.entries(symbolOrProperties)) {\n if (!symbol.properties.has(key) || symbol.properties.get(key) !== value) {\n matches = false\n break\n }\n }\n }\n\n if (matches) {\n total++\n if (onReel[ridx] === undefined) {\n onReel[ridx] = 1\n } else {\n onReel[ridx]++\n }\n }\n }\n }\n\n return [total, onReel]\n }\n\n isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {\n for (const reel of this.reels) {\n let count = 0\n for (const sym of reel) {\n if (sym.id === symbol.id) {\n count++\n }\n if (count > 1) {\n return true\n }\n }\n }\n return false\n }\n\n getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {\n const reelStops: number[][] = []\n for (let ridx = 0; ridx < reels.length; ridx++) {\n const reel = reels[ridx]!\n const positions: number[] = []\n for (let pos = 0; pos < reel.length; pos++) {\n if (reel[pos]!.id === symbol.id) {\n positions.push(pos)\n }\n }\n reelStops.push(positions)\n }\n return reelStops\n }\n\n combineReelStops(opts: {\n ctx: GameContext\n reelsAmount?: number\n reelStops: number[][][]\n }) {\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n assert(reelsAmount, \"Cannot combine reel stops without context or reelsAmount.\")\n\n const combined: number[][] = []\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n combined[ridx] = []\n for (const stops of opts.reelStops) {\n combined[ridx] = combined[ridx]!.concat(stops[ridx]!)\n }\n }\n return combined\n }\n\n getRandomReelStops(opts: {\n ctx: GameContext\n reelsAmount?: number\n reels: Reels\n reelStops: number[][]\n amount: number\n }) {\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n assert(reelsAmount, \"Cannot get random reel stops without context or reelsAmount.\")\n\n const symProbsOnReels: number[] = []\n const stopPositionsForReels: Record<number, number> = {}\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n symProbsOnReels.push(opts.reelStops[ridx]!.length / opts.reels[ridx]!.length)\n }\n\n while (Object.keys(stopPositionsForReels).length !== opts.amount) {\n const possibleReels: number[] = []\n for (let i = 0; i < reelsAmount; i++) {\n if (symProbsOnReels[i]! > 0) {\n possibleReels.push(i)\n }\n }\n const possibleProbs = symProbsOnReels.filter((p) => p > 0)\n const weights = Object.fromEntries(\n possibleReels.map((ridx, idx) => [ridx, possibleProbs[idx]!]),\n )\n const chosenReel = opts.ctx.services.rng.weightedRandom(weights)\n const chosenStop = opts.ctx.services.rng.randomItem(\n opts.reelStops[Number(chosenReel)]!,\n )\n symProbsOnReels[Number(chosenReel)] = 0\n stopPositionsForReels[Number(chosenReel)] = chosenStop\n }\n\n return stopPositionsForReels\n }\n\n getRandomReelset(ctx: GameContext) {\n const weights = ctx.state.currentResultSet.reelWeights\n const evalWeights = ctx.state.currentResultSet.reelWeights.evaluate?.(ctx)\n\n let reelSetId: string = \"\"\n\n if (evalWeights) {\n reelSetId = ctx.services.rng.weightedRandom(evalWeights)\n } else {\n reelSetId = ctx.services.rng.weightedRandom(weights[ctx.state.currentSpinType]!)\n }\n\n const reelSet = ctx.services.game.getReelsetById(ctx.state.currentGameMode, reelSetId)\n\n return reelSet\n }\n\n resetReels(opts: { ctx: GameContext; reelsAmount?: number }) {\n const length =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n\n this.reels = this.makeEmptyReels(opts)\n this.anticipation = Array.from({ length }, () => false)\n this.paddingTop = this.makeEmptyReels(opts)\n this.paddingBottom = this.makeEmptyReels(opts)\n }\n\n drawBoardMixed(opts: {\n ctx: GameContext\n reels: Reels\n forcedStops?: Record<string, number>\n forcedStopsOffset?: boolean\n reelsAmount?: number\n symbolsPerReel?: number[]\n padSymbols?: number\n }) {\n this.resetReels(opts)\n\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n const symbolsPerReel =\n opts.symbolsPerReel ?? opts.ctx.services.game.getCurrentGameMode().symbolsPerReel\n const padSymbols = opts.padSymbols ?? opts.ctx.config.padSymbols\n\n const finalReelStops: (number | null)[] = Array.from(\n { length: reelsAmount },\n () => null,\n )\n\n if (opts.forcedStops) {\n // Fill in forced stops\n for (const [r, stopPos] of Object.entries(opts.forcedStops)) {\n const reelIdx = Number(r)\n\n const symCount = symbolsPerReel[reelIdx]!\n\n if (opts.forcedStopsOffset !== false) {\n finalReelStops[reelIdx] =\n stopPos - Math.round(opts.ctx.services.rng.randomFloat(0, symCount - 1))\n } else {\n finalReelStops[reelIdx] = stopPos\n }\n\n if (finalReelStops[reelIdx]! < 0) {\n finalReelStops[reelIdx] = opts.reels[reelIdx]!.length + finalReelStops[reelIdx]!\n }\n }\n }\n\n // Fill in random stops for reels without a forced stop\n for (let i = 0; i < finalReelStops.length; i++) {\n if (finalReelStops[i] === null) {\n finalReelStops[i] = Math.floor(\n opts.ctx.services.rng.randomFloat(0, opts.reels[i]!.length - 1),\n )\n }\n }\n\n this.lastDrawnReelStops = finalReelStops.map((pos) => pos!) as number[]\n this.lastUsedReels = opts.reels\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n const reelPos = finalReelStops[ridx]!\n const reelLength = opts.reels[ridx]!.length\n\n for (let p = padSymbols - 1; p >= 0; p--) {\n const topPos = (((reelPos - (p + 1)) % reelLength) + reelLength) % reelLength\n this.paddingTop[ridx]!.push(opts.reels[ridx]![topPos]!)\n const bottomPos = (reelPos + symbolsPerReel[ridx]! + p) % reelLength\n this.paddingBottom[ridx]!.unshift(opts.reels[ridx]![bottomPos]!)\n }\n\n for (let row = 0; row < symbolsPerReel[ridx]!; row++) {\n const symbol = opts.reels[ridx]![(reelPos + row) % reelLength]\n\n if (!symbol) {\n throw new Error(`Failed to get symbol at pos ${reelPos + row} on reel ${ridx}`)\n }\n\n this.reels[ridx]![row] = symbol\n }\n }\n }\n\n tumbleBoard(opts: {\n ctx: GameContext\n symbolsToDelete: Array<{ reelIdx: number; rowIdx: number }>\n reelsAmount?: number\n symbolsPerReel?: number[]\n padSymbols?: number\n }) {\n assert(this.lastDrawnReelStops.length > 0, \"Cannot tumble board before drawing it.\")\n\n const reelsAmount =\n opts.reelsAmount ?? opts.ctx.services.game.getCurrentGameMode().reelsAmount\n const symbolsPerReel =\n opts.symbolsPerReel ?? opts.ctx.services.game.getCurrentGameMode().symbolsPerReel\n const padSymbols = opts.padSymbols ?? opts.ctx.config.padSymbols\n\n if (!opts.ctx && !reelsAmount && !symbolsPerReel) {\n throw new Error(\n \"If ctx is not provided, reelsAmount and symbolsPerReel must be given.\",\n )\n }\n\n const reels = this.lastUsedReels\n\n // Sort deletions by row index descending to avoid index shifting issues\n const sortedDeletions = [...opts.symbolsToDelete].sort((a, b) => b.rowIdx - a.rowIdx)\n\n sortedDeletions.forEach(({ reelIdx, rowIdx }) => {\n this.reels[reelIdx]!.splice(rowIdx, 1)\n })\n\n const newFirstSymbolPositions: Record<number, number> = {}\n\n /**\n * A mapping of reel index to the new symbols that were added to that reel during the tumble.\\\n * The topmost added symbols are at the start of the array.\n */\n const newBoardSymbols: Record<string, GameSymbol[]> = {}\n\n /**\n * A mapping of reel index to the new padding top symbols that were added to that reel during the tumble.\n * The topmost added symbols are at the start of the array.\n */\n const newPaddingTopSymbols: Record<string, GameSymbol[]> = {}\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n // Drop down padding symbols from top for as long as reel is not filled and padding top has symbols\n while (this.reels[ridx]!.length < symbolsPerReel[ridx]!) {\n const padSymbol = this.paddingTop[ridx]!.pop()\n if (padSymbol) {\n this.reels[ridx]!.unshift(padSymbol)\n\n if (!newBoardSymbols[ridx]) {\n newBoardSymbols[ridx] = []\n }\n\n newBoardSymbols[ridx]!.unshift(padSymbol)\n } else {\n break\n }\n }\n\n const previousStop = this.lastDrawnReelStops[ridx]!\n const stopBeforePad = previousStop - padSymbols - 1\n const symbolsNeeded = symbolsPerReel[ridx]! - this.reels[ridx]!.length\n // Drop rest of symbols\n for (let s = 0; s < symbolsNeeded; s++) {\n const symbolPos = (stopBeforePad - s + reels[ridx]!.length) % reels[ridx]!.length\n const newSymbol = reels[ridx]![symbolPos]\n\n assert(newSymbol, \"Failed to get new symbol for tumbling.\")\n\n this.reels[ridx]!.unshift(newSymbol)\n newFirstSymbolPositions[ridx] = symbolPos\n\n if (!newBoardSymbols[ridx]) {\n newBoardSymbols[ridx] = []\n }\n\n newBoardSymbols[ridx]!.unshift(newSymbol)\n }\n }\n\n // Add new padding top symbols\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n const firstSymbolPos = newFirstSymbolPositions[ridx]!\n\n if (firstSymbolPos === undefined) continue\n\n for (let p = 1; p <= padSymbols; p++) {\n const topPos = (firstSymbolPos - p + reels[ridx]!.length) % reels[ridx]!.length\n const padSymbol = reels[ridx]![topPos]\n\n assert(padSymbol, \"Failed to get new padding symbol for tumbling.\")\n\n this.paddingTop[ridx]!.unshift(padSymbol)\n\n if (!newPaddingTopSymbols[ridx]) {\n newPaddingTopSymbols[ridx] = []\n }\n\n newPaddingTopSymbols[ridx]!.unshift(padSymbol)\n }\n }\n\n // Ensure future tumbles start from the new top positions\n this.lastDrawnReelStops = this.lastDrawnReelStops.map((stop, ridx) => {\n return newFirstSymbolPositions[ridx] ?? stop\n })\n\n return {\n newBoardSymbols,\n newPaddingTopSymbols,\n }\n }\n\n dedupeWinSymbolsForTumble(winCombinations: WinCombination[]) {\n const symbolsMap = new Map<string, { reelIdx: number; rowIdx: number }>()\n winCombinations.forEach((wc) => {\n wc.symbols.forEach((s) => {\n symbolsMap.set(`${s.reelIndex},${s.posIndex}`, {\n reelIdx: s.reelIndex,\n rowIdx: s.posIndex,\n })\n })\n })\n const symbolsToRemove = Array.from(symbolsMap.values())\n return symbolsToRemove\n }\n}\n","export class GameSymbol {\n readonly id: string\n readonly pays?: Record<number, number>\n readonly properties: Map<string, any>\n\n constructor(opts: GameSymbolOpts) {\n this.id = opts.id\n this.pays = opts.pays\n this.properties = new Map<string, any>(Object.entries(opts.properties || {}))\n\n if (this.pays && Object.keys(this.pays).length === 0) {\n throw new Error(`GameSymbol \"${this.id}\" must have pays defined.`)\n }\n }\n\n /**\n * Compares this symbol to another symbol or a set of properties.\n */\n compare(symbolOrProperties?: GameSymbol | Record<string, any>) {\n if (!symbolOrProperties) {\n console.warn(\"No symbol or properties provided for comparison.\")\n return false\n }\n if (symbolOrProperties instanceof GameSymbol) {\n return this.id === symbolOrProperties.id\n } else {\n for (const [key, value] of Object.entries(symbolOrProperties)) {\n if (!this.properties.has(key) || this.properties.get(key) !== value) {\n return false\n }\n }\n return true\n }\n }\n\n /**\n * Creates a clone of this GameSymbol.\n */\n clone() {\n return new GameSymbol({\n id: this.id,\n pays: this.pays ? { ...this.pays } : undefined,\n properties: Object.fromEntries(this.properties),\n })\n }\n}\n\nexport interface GameSymbolOpts {\n /**\n * Unique identifier for the symbol, e.g. \"W\", \"H1\", \"L5\", etc.\n */\n id: string\n /**\n * Paytable for the symbol, where the key is the number of symbols and the value is the payout multiplier.\n */\n pays?: Record<number, number>\n /**\n * Additional properties for the symbol, e.g. `multiplier` or `isWild`.\n *\n * Properties can help identify special symbols.\n * \n * @example\n * If your game has a \"normal\" scatter and a \"super\" scatter, you can define them like this:\n * \n * ```ts\n * properties: {\n * isScatter: true,\n * }\n * ```\n */\n properties?: Record<string, any>\n}\n","import { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { AnyGameModes, AnySymbols, AnyUserData, Reels } from \"../types\"\nimport { Board } from \"../board\"\nimport { WinCombination } from \"../win-types\"\n\nexport class BoardService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n private board: Board\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n\n this.board = new Board()\n }\n\n /**\n * Resets the board to an empty state.\\\n * This is called before drawing a new board.\n */\n resetBoard() {\n this.resetReels()\n this.board.lastDrawnReelStops = []\n }\n\n /**\n * Gets the current reels and symbols on the board.\n */\n getBoardReels() {\n return this.board.reels\n }\n\n getPaddingTop() {\n return this.board.paddingTop\n }\n\n getPaddingBottom() {\n return this.board.paddingBottom\n }\n\n getAnticipation() {\n return this.board.anticipation\n }\n\n /**\n * Gets the symbol at the specified reel and row index.\n */\n getSymbol(reelIndex: number, rowIndex: number) {\n return this.board.getSymbol(reelIndex, rowIndex)\n }\n\n /**\n * Sets the symbol at the specified reel and row index.\n */\n setSymbol(reelIndex: number, rowIndex: number, symbol: GameSymbol) {\n this.board.setSymbol(reelIndex, rowIndex, symbol)\n }\n\n private resetReels() {\n this.board.resetReels({\n ctx: this.ctx(),\n })\n }\n\n /**\n * Sets the anticipation value for a specific reel.\n */\n setAnticipationForReel(reelIndex: number, value: boolean) {\n this.board.anticipation[reelIndex] = value\n }\n\n /**\n * Counts how many symbols matching the criteria are on a specific reel.\n */\n countSymbolsOnReel(\n symbolOrProperties: GameSymbol | Record<string, any>,\n reelIndex: number,\n ) {\n return this.board.countSymbolsOnReel(symbolOrProperties, reelIndex)\n }\n\n /**\n * Counts how many symbols matching the criteria are on the board.\n *\n * Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.\n *\n * Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.\n */\n countSymbolsOnBoard(\n symbolOrProperties: GameSymbol | Record<string, any>,\n ): [number, Record<number, number>] {\n return this.board.countSymbolsOnBoard(symbolOrProperties)\n }\n\n /**\n * Checks if a symbol appears more than once on any reel in the current reel set.\n *\n * Useful to check for \"forbidden\" generations, e.g. 2 scatters on one reel.\n */\n isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {\n return this.board.isSymbolOnAnyReelMultipleTimes(symbol)\n }\n\n /**\n * Gets all reel stops (positions) where the specified symbol appears in the current reel set.\\\n * Returns an array of arrays, where each inner array contains the positions for the corresponding reel.\n */\n getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {\n return this.board.getReelStopsForSymbol(reels, symbol)\n }\n\n /**\n * Combines multiple arrays of reel stops into a single array of reel stops.\\\n */\n combineReelStops(...reelStops: number[][][]) {\n return this.board.combineReelStops({\n ctx: this.ctx(),\n reelStops,\n })\n }\n\n /**\n * From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.\n *\n * Mostly useful for placing scatter symbols on the board.\n */\n getRandomReelStops(reels: Reels, reelStops: number[][], amount: number) {\n return this.board.getRandomReelStops({\n ctx: this.ctx(),\n reels,\n reelStops,\n amount,\n })\n }\n\n /**\n * Selects a random reel set based on the configured weights of the current result set.\\\n * Returns the reels as arrays of GameSymbols.\n */\n getRandomReelset() {\n return this.board.getRandomReelset(this.ctx())\n }\n\n /**\n * Draws a board using specified reel stops.\n */\n drawBoardWithForcedStops(opts: {\n reels: Reels\n forcedStops: Record<string, number>\n randomOffset?: boolean\n }) {\n this.drawBoardMixed(opts.reels, opts.forcedStops, opts.randomOffset)\n }\n\n /**\n * Draws a board using random reel stops.\n */\n drawBoardWithRandomStops(reels: Reels) {\n this.drawBoardMixed(reels)\n }\n\n private drawBoardMixed(\n reels: Reels,\n forcedStops?: Record<string, number>,\n forcedStopsOffset?: boolean,\n ) {\n this.board.drawBoardMixed({\n ctx: this.ctx(),\n reels,\n forcedStops,\n forcedStopsOffset,\n })\n }\n\n /**\n * Tumbles the board. All given symbols will be deleted and new symbols will fall from the top.\n */\n tumbleBoard(symbolsToDelete: Array<{ reelIdx: number; rowIdx: number }>) {\n return this.board.tumbleBoard({\n ctx: this.ctx(),\n symbolsToDelete,\n })\n }\n\n /**\n * Dedupes win symbols for tumble.\\\n * Returns a list of symbols to remove from the board based on the given win combinations.\n *\n * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\\\n * this method ensures that each symbol is only listed once for removal. Otherwise tumbling may break.\n */\n dedupeWinSymbolsForTumble(winCombinations: WinCombination[]) {\n return this.board.dedupeWinSymbolsForTumble(winCombinations)\n }\n}\n","import assert from \"assert\"\nimport { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { Recorder } from \"../recorder\"\nimport { AnyGameModes, AnySymbols, AnyUserData, SpinType } from \"../types\"\nimport { Book, BookEvent } from \"../book\"\n\nexport class DataService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n private recorder!: Recorder\n private book!: Book\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n private ensureRecorder() {\n assert(this.recorder, \"Recorder not set in DataService. Call setRecorder() first.\")\n }\n\n private ensureBook() {\n assert(this.book, \"Book not set in DataService. Call setBook() first.\")\n }\n\n /**\n * Intended for internal use only.\n */\n _setRecorder(recorder: Recorder) {\n this.recorder = recorder\n }\n\n /**\n * Intended for internal use only.\n */\n _getBook() {\n return this.book\n }\n\n /**\n * Intended for internal use only.\n */\n _setBook(book: Book) {\n this.book = book\n }\n\n /**\n * Intended for internal use only.\n */\n _getRecorder() {\n return this.recorder\n }\n\n /**\n * Intended for internal use only.\n */\n _getRecords() {\n this.ensureRecorder()\n return this.recorder.records\n }\n\n /**\n * Record data for statistical analysis.\n */\n record(data: Record<string, string | number | boolean>) {\n this.ensureRecorder()\n\n this.recorder.pendingRecords.push({\n bookId: this.ctx().state.currentSimulationId,\n properties: Object.fromEntries(\n Object.entries(data).map(([k, v]) => [k, String(v)]),\n ),\n })\n }\n\n /**\n * Records a symbol occurrence for statistical analysis.\n *\n * Calls `ctx.services.data.record()` with the provided data.\n */\n recordSymbolOccurrence(data: {\n kind: number\n symbolId: string\n spinType: SpinType\n [key: string]: any\n }) {\n this.record(data)\n }\n\n /**\n * Adds an event to the book.\n */\n addBookEvent(event: Omit<BookEvent, \"index\">) {\n this.ensureBook()\n this.book.addEvent(event)\n }\n\n /**\n * Intended for internal use only.\n */\n _clearPendingRecords() {\n this.ensureRecorder()\n this.recorder.pendingRecords = []\n }\n}\n","import { AbstractService } from \".\"\nimport { GameConfig } from \"../game-config\"\nimport { GameContext } from \"../game-context\"\nimport { AnyGameModes, AnySymbols, AnyUserData, SpinType } from \"../types\"\nimport { WinCombination } from \"../win-types\"\n\nexport class GameService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n /**\n * Intended for internal use only.\\\n * Generates reels for all reel sets in the game configuration.\n */\n _generateReels() {\n const config = this.ctx().config\n for (const mode of Object.values(config.gameModes)) {\n if (mode.reelSets && mode.reelSets.length > 0) {\n for (const reelSet of Object.values(mode.reelSets)) {\n reelSet.associatedGameModeName = mode.name\n reelSet.generateReels(config)\n }\n } else {\n throw new Error(\n `Game mode \"${mode.name}\" has no reel sets defined. Cannot generate reelset files.`,\n )\n }\n }\n }\n\n /**\n * Retrieves a reel set by its ID within a specific game mode.\n */\n getReelsetById(gameMode: string, id: string) {\n const reelSet = this.ctx().config.gameModes[gameMode]!.reelSets.find(\n (rs) => rs.id === id,\n )\n if (!reelSet) {\n throw new Error(\n `Reel set with id \"${id}\" not found in game mode \"${gameMode}\". Available reel sets: ${this.ctx()\n .config.gameModes[gameMode]!.reelSets.map((rs) => rs.id)\n .join(\", \")}`,\n )\n }\n return reelSet.reels\n }\n\n /**\n * Retrieves the number of free spins awarded for a given spin type and scatter count.\n */\n getFreeSpinsForScatters(spinType: SpinType, scatterCount: number) {\n const freespinsConfig = this.ctx().config.scatterToFreespins[spinType]\n if (!freespinsConfig) {\n throw new Error(\n `No free spins configuration found for spin type \"${spinType}\". Please check your game configuration.`,\n )\n }\n return freespinsConfig[scatterCount] || 0\n }\n\n /**\n * Retrieves a result set by its criteria within a specific game mode.\n */\n getResultSetByCriteria(mode: string, criteria: string) {\n const gameMode = this.ctx().config.gameModes[mode]\n if (!gameMode) {\n throw new Error(`Game mode \"${mode}\" not found in game config.`)\n }\n const resultSet = gameMode.resultSets.find((rs) => rs.criteria === criteria)\n if (!resultSet) {\n throw new Error(\n `Criteria \"${criteria}\" not found in game mode \"${mode}\". Available criteria: ${gameMode.resultSets\n .map((rs) => rs.criteria)\n .join(\", \")}`,\n )\n }\n return resultSet\n }\n\n /**\n * Returns all configured symbols as an array.\n */\n getSymbolArray() {\n return Array.from(this.ctx().config.symbols).map(([n, v]) => v)\n }\n\n /**\n * Gets the configuration for the current game mode.\n */\n getCurrentGameMode() {\n return this.ctx().config.gameModes[this.ctx().state.currentGameMode]!\n }\n\n /**\n * Ensures the requested number of scatters is valid based on the game configuration.\\\n * Returns a valid number of scatters.\n */\n verifyScatterCount(numScatters: number) {\n const scatterCounts =\n this.ctx().config.scatterToFreespins[this.ctx().state.currentSpinType]\n if (!scatterCounts) {\n throw new Error(\n `No scatter counts defined for spin type \"${this.ctx().state.currentSpinType}\". Please check your game configuration.`,\n )\n }\n const validCounts = Object.keys(scatterCounts).map((key) => parseInt(key, 10))\n if (validCounts.length === 0) {\n throw new Error(\n `No scatter counts defined for spin type \"${this.ctx().state.currentSpinType}\". Please check your game configuration.`,\n )\n }\n if (numScatters < Math.min(...validCounts)) {\n return Math.min(...validCounts)\n }\n if (numScatters > Math.max(...validCounts)) {\n return Math.max(...validCounts)\n }\n return numScatters\n }\n\n /**\n * Increases the freespin count by the specified amount.\n *\n * Also sets `state.triggeredFreespins` to true.\n */\n awardFreespins(amount: number) {\n this.ctx().state.currentFreespinAmount += amount\n this.ctx().state.totalFreespinAmount += amount\n this.ctx().state.triggeredFreespins = true\n }\n\n /**\n * Dedupes win symbols.\n *\n * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\\\n * this method ensures that each symbol is only listed once.\n * \n * If you want to tumble based on winning symbols, run them through this method first.\n */\n dedupeWinSymbols(winCombinations: WinCombination[]) {\n const symbolsMap = new Map<string, { reelIdx: number; rowIdx: number }>()\n winCombinations.forEach((wc) => {\n wc.symbols.forEach((s) => {\n symbolsMap.set(`${s.reelIndex},${s.posIndex}`, {\n reelIdx: s.reelIndex,\n rowIdx: s.posIndex,\n })\n })\n })\n const symbolsToRemove = Array.from(symbolsMap.values())\n return symbolsToRemove\n }\n}\n","import assert from \"assert\"\nimport { AbstractService } from \".\"\nimport { GameContext } from \"../game-context\"\nimport { AnyGameModes, AnySymbols, AnyUserData, SpinType } from \"../types\"\nimport { Wallet } from \"../wallet\"\n\nexport class WalletService<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> extends AbstractService {\n private wallet!: Wallet\n\n constructor(ctx: () => GameContext<TGameModes, TSymbols, TUserState>) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n super(ctx)\n }\n\n private ensureWallet() {\n assert(this.wallet, \"Wallet not set in WalletService. Call setWallet() first.\")\n }\n\n /**\n * Intended for internal use only.\n */\n _getWallet() {\n this.ensureWallet()\n return this.wallet\n }\n\n /**\n * Intended for internal use only.\n */\n _setWallet(wallet: Wallet) {\n this.wallet = wallet\n }\n\n /**\n * Adds the given amount to the wallet state.\n *\n * After calculating the win for a board, call this method to update the wallet state.\\\n * If your game has tumbling mechanics, you should call this method again after every new tumble and win calculation.\n */\n addSpinWin(amount: number) {\n this.ensureWallet()\n this.wallet.addSpinWin(amount)\n }\n\n /**\n * Helps to add tumble wins to the wallet state.\n *\n * This also calls `addSpinWin()` internally, to add the tumble win to the overall spin win.\n */\n addTumbleWin(amount: number) {\n this.ensureWallet()\n this.wallet.addTumbleWin(amount)\n }\n\n /**\n * Confirms the wins of the current spin.\n *\n * Should be called after `addSpinWin()`, and after your tumble events are played out,\\\n * and after a (free) spin is played out to finalize the win.\n */\n confirmSpinWin() {\n this.ensureWallet()\n this.wallet.confirmSpinWin(this.ctx().state.currentSpinType)\n }\n\n /**\n * Gets the total win amount of the current simulation.\n */\n getCurrentWin() {\n this.ensureWallet()\n return this.wallet.getCurrentWin()\n }\n\n /**\n * Gets the current spin win amount of the ongoing spin.\n */\n getCurrentSpinWin() {\n this.ensureWallet()\n return this.wallet.getCurrentSpinWin()\n }\n\n /**\n * Gets the current tumble win amount of the ongoing spin.\n */\n getCurrentTumbleWin() {\n this.ensureWallet()\n return this.wallet.getCurrentTumbleWin()\n }\n}\n","import { createGameConfig, GameConfig, GameConfigOptions } from \"../game-config\"\nimport { createGameState, GameState } from \"../game-state\"\nimport { Recorder } from \"../recorder\"\nimport { BoardService } from \"../service/board\"\nimport { DataService } from \"../service/data\"\nimport { GameService } from \"../service/game\"\nimport { RngService } from \"../service/rng\"\nimport { WalletService } from \"../service/wallet\"\nimport { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\n\nexport interface GameContextOptions<\n TGameModes extends AnyGameModes,\n TSymbols extends AnySymbols,\n TUserState extends AnyUserData,\n> {\n config: GameConfig<TGameModes, TSymbols, TUserState>\n state?: Partial<GameState<TUserState>>\n services?: Partial<GameContextServices>\n}\n\nexport function createGameContext<\n TGameModes extends AnyGameModes,\n TSymbols extends AnySymbols,\n TUserState extends AnyUserData,\n>(opts: GameContextOptions<TGameModes, TSymbols, TUserState>) {\n const context = {\n config: opts.config,\n state: createGameState(opts.state),\n services: {} as GameContextServices,\n }\n\n const getContext = () => context\n\n function createServices() {\n return {\n game: new GameService(getContext),\n data: new DataService(getContext),\n board: new BoardService(getContext),\n wallet: new WalletService(getContext),\n rng: new RngService(getContext),\n ...opts.services,\n }\n }\n\n context.services = createServices()\n\n return context\n}\n\nexport type GameContext<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> = {\n /**\n * The static configuration of the game.\n */\n config: GameConfig<TGameModes, TSymbols, TUserState>\n /**\n * Game state holding information about the current simulation.\n */\n state: GameState<TUserState>\n /**\n * Services providing game functionality.\n */\n services: GameContextServices\n}\n\nexport interface GameContextServices {\n /**\n * Service providing common utility functions.\n */\n game: GameService\n /**\n * Service for interacting with the book data or recorder.\n */\n data: DataService\n /**\n * Service managing the game board and reels.\n */\n board: BoardService\n /**\n * Service providing win related functionality.\n */\n wallet: WalletService\n /**\n * Service for seeded random number generation.\n */\n rng: RngService\n}\n\n/**\n * Intended for testing purposes only.\\\n * Creates a game context with a minimal configuration.\n */\nexport function createTestContext(config?: Partial<GameConfigOptions>) {\n const ctx = createGameContext({\n config: createGameConfig({\n id: \"\",\n name: \"\",\n gameModes: {},\n symbols: {},\n scatterToFreespins: {},\n maxWinX: 10000,\n hooks: {\n onHandleGameFlow() {},\n },\n ...config,\n }),\n })\n\n // it needs a recorder to work properly\n ctx.services.data._setRecorder(new Recorder())\n\n return ctx\n}\n","export class Book {\n readonly id: number\n criteria: string = \"N/A\"\n events: BookEvent[] = []\n payout: number = 0\n basegameWins: number = 0\n freespinsWins: number = 0\n\n constructor(opts: BookOpts) {\n this.id = opts.id\n this.criteria = opts.criteria\n }\n\n /**\n * Intended for internal use only.\n */\n setCriteria(criteria: string) {\n this.criteria = criteria\n }\n\n /**\n * Adds an event to the book.\n */\n addEvent(event: Omit<BookEvent, \"index\">) {\n const index = this.events.length + 1\n this.events.push({ index, ...event })\n }\n\n /**\n * Intended for internal use only.\n */\n serialize() {\n return {\n id: this.id,\n criteria: this.criteria,\n events: this.events,\n payout: this.payout,\n basegameWins: this.basegameWins,\n freespinsWins: this.freespinsWins,\n }\n }\n\n /**\n * Intended for internal use only.\n */\n static fromSerialized(data: ReturnType<Book[\"serialize\"]>) {\n const book = new Book({ id: data.id, criteria: data.criteria })\n book.events = data.events\n book.payout = data.payout\n book.basegameWins = data.basegameWins\n book.freespinsWins = data.freespinsWins\n return book\n }\n}\n\nexport interface BookEvent {\n index: number\n type: string\n data: Record<string, any>\n}\n\ninterface BookOpts {\n id: number\n criteria: string\n}\n","import { SPIN_TYPE } from \"../constants\"\nimport { GameContext } from \"../game-context\"\nimport { SpinType } from \"../types\"\n\nexport class Wallet {\n /**\n * Total win amount (as the bet multiplier) from all simulations.\n */\n protected cumulativeWins = 0\n /**\n * Total win amount (as the bet multiplier) per spin type.\n *\n * @example\n * ```ts\n * {\n * basegame: 50,\n * freespins: 100,\n * superfreespins: 200,\n * }\n * ```\n */\n protected cumulativeWinsPerSpinType = {\n [SPIN_TYPE.BASE_GAME]: 0,\n [SPIN_TYPE.FREE_SPINS]: 0,\n }\n /**\n * Current win amount (as the bet multiplier) for the ongoing simulation.\n */\n protected currentWin = 0\n /**\n * Current win amount (as the bet multiplier) for the ongoing simulation per spin type.\n *\n * @example\n * ```ts\n * {\n * basegame: 50,\n * freespins: 100,\n * superfreespins: 200,\n * }\n * ```\n */\n protected currentWinPerSpinType = {\n [SPIN_TYPE.BASE_GAME]: 0,\n [SPIN_TYPE.FREE_SPINS]: 0,\n }\n /**\n * Holds the current win amount for a single (free) spin.\\\n * After each spin, this amount is added to `currentWinPerSpinType` and then reset to zero.\n */\n protected currentSpinWin = 0\n /**\n * Current win amount (as the bet multiplier) for the ongoing tumble sequence.\n */\n protected currentTumbleWin = 0\n\n constructor() {}\n\n /**\n * Updates the win for the current spin.\n *\n * Should be called after each tumble event, if applicable.\\\n * Or generally call this to add wins during a spin.\n *\n * After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`\n */\n addSpinWin(amount: number) {\n this.currentSpinWin += amount\n }\n\n /**\n * Confirms the wins of the current spin.\n *\n * Should be called after `addSpinWin()`, and after your tumble events are played out,\\\n * and after a (free) spin is played out to finalize the win.\n */\n confirmSpinWin(spinType: SpinType) {\n if (!Object.keys(this.currentWinPerSpinType).includes(spinType)) {\n throw new Error(`Spin type \"${spinType}\" does not exist in the wallet.`)\n }\n this.currentWinPerSpinType[spinType]! += this.currentSpinWin\n this.currentWin += this.currentSpinWin\n this.currentSpinWin = 0\n this.currentTumbleWin = 0\n }\n\n /**\n * Returns the accumulated win amount (as the bet multiplier) from all simulations.\n */\n getCumulativeWins() {\n return this.cumulativeWins\n }\n\n /**\n * Returns the accumulated win amount (as the bet multiplier) per spin type from all simulations.\n */\n getCumulativeWinsPerSpinType() {\n return this.cumulativeWinsPerSpinType\n }\n\n /**\n * Returns the current win amount (as the bet multiplier) for the ongoing simulation.\n */\n getCurrentWin() {\n return this.currentWin\n }\n\n /**\n * Returns the current spin win amount (as the bet multiplier) for the ongoing simulation.\n */\n getCurrentSpinWin() {\n return this.currentSpinWin\n }\n\n /**\n * Returns the current tumble win amount (as the bet multiplier) for the ongoing simulation.\n */\n getCurrentTumbleWin() {\n return this.currentTumbleWin\n }\n\n /**\n * Returns the current win amount (as the bet multiplier) per spin type for the ongoing simulation.\n */\n getCurrentWinPerSpinType() {\n return this.currentWinPerSpinType\n }\n\n /**\n * Adds a win to `currentSpinWin` and `currentTumbleWin`.\n *\n * After each (free) spin, this amount should be added to `currentWinPerSpinType` via `confirmSpinWin()`\n */\n addTumbleWin(amount: number) {\n this.currentTumbleWin += amount\n this.addSpinWin(amount)\n }\n\n /**\n * Intended for internal use only.\n * \n * Resets the current win amounts to zero.\n */\n resetCurrentWin() {\n this.currentWin = 0\n this.currentSpinWin = 0\n this.currentTumbleWin = 0\n\n for (const spinType of Object.keys(this.currentWinPerSpinType)) {\n this.currentWinPerSpinType[spinType as SpinType] = 0\n }\n }\n\n /**\n * Intended for internal use only.\n * \n * Adds current wins to cumulative wins and resets current wins to zero.\n */\n confirmWins(ctx: GameContext) {\n function process(number: number) {\n return Math.round(Math.min(number, ctx.config.maxWinX) * 100) / 100\n }\n\n this.currentWin = process(this.currentWin)\n this.cumulativeWins += this.currentWin\n let spinTypeWins = 0\n\n for (const spinType of Object.keys(this.currentWinPerSpinType)) {\n const st = spinType as SpinType\n const spinTypeWin = process(this.currentWinPerSpinType[st])\n this.cumulativeWinsPerSpinType[st]! += spinTypeWin\n spinTypeWins += spinTypeWin\n }\n\n if (process(spinTypeWins) !== this.currentWin) {\n throw new Error(\n `Inconsistent wallet state: currentWin (${this.currentWin}) does not equal spinTypeWins (${spinTypeWins}).`,\n )\n }\n\n this.resetCurrentWin()\n }\n\n /**\n * Intended for internal use only.\n *\n * Transfers the win data from the given wallet to the calling book.\n */\n writePayoutToBook(ctx: GameContext) {\n function process(number: number) {\n return Math.round(Math.min(number, ctx.config.maxWinX) * 100) / 100\n }\n\n const wallet = ctx.services.wallet._getWallet()\n const book = ctx.services.data._getBook()\n\n book.payout = Math.round(process(wallet.getCurrentWin()) * 100)\n book.basegameWins = process(\n wallet.getCurrentWinPerSpinType()[SPIN_TYPE.BASE_GAME] || 0,\n )\n book.freespinsWins = process(\n wallet.getCurrentWinPerSpinType()[SPIN_TYPE.FREE_SPINS] || 0,\n )\n }\n\n /**\n * Intended for internal use only.\n */\n serialize() {\n return {\n cumulativeWins: this.cumulativeWins,\n cumulativeWinsPerSpinType: this.cumulativeWinsPerSpinType,\n currentWin: this.currentWin,\n currentWinPerSpinType: this.currentWinPerSpinType,\n currentSpinWin: this.currentSpinWin,\n currentTumbleWin: this.currentTumbleWin,\n }\n }\n\n /**\n * Intended for internal use only.\n */\n merge(wallet: Wallet) {\n this.cumulativeWins += wallet.getCumulativeWins()\n const otherWinsPerSpinType = wallet.getCumulativeWinsPerSpinType()\n\n for (const spinType of Object.keys(this.cumulativeWinsPerSpinType)) {\n this.cumulativeWinsPerSpinType[spinType as SpinType]! +=\n otherWinsPerSpinType[spinType as SpinType] || 0\n }\n }\n\n /**\n * Intended for internal use only.\n */\n mergeSerialized(data: ReturnType<Wallet[\"serialize\"]>) {\n this.cumulativeWins += data.cumulativeWins\n for (const spinType of Object.keys(this.cumulativeWinsPerSpinType)) {\n this.cumulativeWinsPerSpinType[spinType as SpinType]! +=\n data.cumulativeWinsPerSpinType[spinType as SpinType] || 0\n }\n this.currentWin += data.currentWin\n this.currentSpinWin += data.currentSpinWin\n this.currentTumbleWin += data.currentTumbleWin\n for (const spinType of Object.keys(this.currentWinPerSpinType)) {\n this.currentWinPerSpinType[spinType as SpinType]! +=\n data.currentWinPerSpinType[spinType as SpinType] || 0\n }\n }\n}\n","import fs from \"fs\"\nimport path from \"path\"\nimport { GameConfig } from \"../game-config\"\nimport { Optimizer, OptimzierGameModeConfig } from \"../optimizer\"\nimport assert from \"assert\"\nimport {\n getAvgWin,\n getLessBetHitrate,\n getMaxWin,\n getMaxwinHitrate,\n getMinWin,\n getNonZeroHitrate,\n getNullHitrate,\n getPayoutWeights,\n getRtp,\n getStandardDeviation,\n getTotalLutWeight,\n getUniquePayouts,\n getVariance,\n parseLookupTable,\n parseLookupTableSegmented,\n} from \"./utils\"\nimport { writeJsonFile } from \"../../utils\"\nimport { isMainThread } from \"worker_threads\"\n\nexport class Analysis {\n protected readonly gameConfig: GameConfig\n protected readonly optimizerConfig: OptimzierGameModeConfig\n protected filePaths: Record<string, FilePaths>\n\n constructor(optimizer: Optimizer) {\n this.gameConfig = optimizer.getGameConfig()\n this.optimizerConfig = optimizer.getOptimizerGameModes()\n this.filePaths = {}\n }\n\n async runAnalysis(gameModes: string[]) {\n if (!isMainThread) return // IMPORTANT: Prevent workers from kicking off (multiple) analysis runs\n\n this.filePaths = this.getPathsForModes(gameModes)\n this.getNumberStats(gameModes)\n this.getWinRanges(gameModes)\n // TODO: this.getSymbolStats(gameModes)\n console.log(\"Analysis complete. Files written to build directory.\")\n }\n\n private getPathsForModes(gameModes: string[]) {\n const rootPath = this.gameConfig.rootDir\n const paths: Record<string, FilePaths> = {}\n\n for (const modeStr of gameModes) {\n const lut = path.join(\n rootPath,\n this.gameConfig.outputDir,\n `lookUpTable_${modeStr}.csv`,\n )\n const lutSegmented = path.join(\n rootPath,\n this.gameConfig.outputDir,\n `lookUpTableSegmented_${modeStr}.csv`,\n )\n const lutOptimized = path.join(\n rootPath,\n this.gameConfig.outputDir,\n \"publish_files\",\n `lookUpTable_${modeStr}_0.csv`,\n )\n const booksJsonl = path.join(\n rootPath,\n this.gameConfig.outputDir,\n `books_${modeStr}.jsonl`,\n )\n const booksJsonlCompressed = path.join(\n rootPath,\n this.gameConfig.outputDir,\n \"publish_files\",\n `books_${modeStr}.jsonl.zst`,\n )\n\n paths[modeStr] = {\n lut,\n lutSegmented,\n lutOptimized,\n booksJsonl,\n booksJsonlCompressed,\n }\n\n for (const p of Object.values(paths[modeStr])) {\n assert(\n fs.existsSync(p),\n `File \"${p}\" does not exist. Run optimization to auto-create it.`,\n )\n }\n }\n\n return paths\n }\n\n private getNumberStats(gameModes: string[]) {\n const stats: Statistics[] = []\n\n for (const modeStr of gameModes) {\n const mode = this.getGameModeConfig(modeStr)\n\n const lutOptimized = parseLookupTable(\n fs.readFileSync(this.filePaths[modeStr]!.lutOptimized, \"utf-8\"),\n )\n const totalWeight = getTotalLutWeight(lutOptimized)\n const payoutWeights = getPayoutWeights(lutOptimized)\n\n stats.push({\n gameMode: mode.name,\n totalWeight,\n avgWin: getAvgWin(payoutWeights),\n rtp: getRtp(payoutWeights, mode.cost),\n minWin: getMinWin(payoutWeights),\n maxWin: getMaxWin(payoutWeights),\n stdDev: getStandardDeviation(payoutWeights),\n variance: getVariance(payoutWeights),\n nonZeroHitRate: getNonZeroHitrate(payoutWeights),\n nullHitRate: getNullHitrate(payoutWeights),\n maxwinHitRate: getMaxwinHitrate(payoutWeights),\n lessBetHitRate: getLessBetHitrate(payoutWeights, mode.cost),\n uniquePayouts: getUniquePayouts(payoutWeights),\n })\n }\n\n writeJsonFile(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, \"stats_summary.json\"),\n stats,\n )\n }\n\n private getWinRanges(gameModes: string[]) {\n const winRanges: [number, number][] = [\n [0, 0.1],\n [0, 0.99],\n [1, 1.99],\n [2, 2.99],\n [3, 4.99],\n [5, 9.99],\n [10, 19.99],\n [20, 49.99],\n [50, 99.99],\n [100, 199.99],\n [200, 499.99],\n [500, 999.99],\n [1000, 1999.99],\n [2000, 2999.99],\n [3000, 4999.99],\n [5000, 7499.99],\n [7500, 9999.99],\n [10000, 14999.99],\n [15000, 19999.99],\n [20000, 24999.99],\n ]\n\n const payoutRanges: Record<\n string,\n {\n overall: Record<string, number>\n criteria: Record<string, Record<string, number>>\n }\n > = {}\n\n for (const modeStr of gameModes) {\n payoutRanges[modeStr] = { overall: {}, criteria: {} }\n\n const lutSegmented = parseLookupTableSegmented(\n fs.readFileSync(this.filePaths[modeStr]!.lutSegmented, \"utf-8\"),\n )\n\n lutSegmented.forEach(([, criteria, bp, fsp]) => {\n const basePayout = bp\n const freeSpinPayout = fsp\n const payout = basePayout + freeSpinPayout\n\n for (const [min, max] of winRanges) {\n if (payout >= min && payout <= max) {\n const rangeKey = `${min}-${max}`\n\n // Overall\n if (!payoutRanges[modeStr]!.overall[rangeKey]) {\n payoutRanges[modeStr]!.overall[rangeKey] = 0\n }\n payoutRanges[modeStr]!.overall[rangeKey] += 1\n\n // Criteria\n if (!payoutRanges[modeStr]!.criteria[criteria]) {\n payoutRanges[modeStr]!.criteria[criteria] = {}\n }\n if (!payoutRanges[modeStr]!.criteria[criteria]![rangeKey]) {\n payoutRanges[modeStr]!.criteria[criteria]![rangeKey] = 0\n }\n payoutRanges[modeStr]!.criteria[criteria]![rangeKey] += 1\n break\n }\n }\n })\n\n const orderedOverall: Record<string, number> = {}\n Object.keys(payoutRanges[modeStr]!.overall)\n .sort((a, b) => {\n const [aMin] = a.split(\"-\").map(Number)\n const [bMin] = b.split(\"-\").map(Number)\n return aMin! - bMin!\n })\n .forEach((key) => {\n orderedOverall[key] = payoutRanges[modeStr]!.overall[key]!\n })\n\n const orderedCriteria: Record<string, Record<string, number>> = {}\n Object.keys(payoutRanges[modeStr]!.criteria).forEach((crit) => {\n const critMap = payoutRanges[modeStr]!.criteria[crit]!\n const orderedCritMap: Record<string, number> = {}\n Object.keys(critMap)\n .sort((a, b) => {\n const [aMin] = a.split(\"-\").map(Number)\n const [bMin] = b.split(\"-\").map(Number)\n return aMin! - bMin!\n })\n .forEach((key) => {\n orderedCritMap[key] = critMap[key]!\n })\n orderedCriteria[crit] = orderedCritMap\n })\n\n payoutRanges[modeStr] = {\n overall: orderedOverall,\n criteria: {},\n }\n }\n\n writeJsonFile(\n path.join(this.gameConfig.rootDir, this.gameConfig.outputDir, \"stats_payouts.json\"),\n payoutRanges,\n )\n }\n\n private getGameModeConfig(mode: string) {\n const config = this.gameConfig.gameModes[mode]\n assert(config, `Game mode \"${mode}\" not found in game config`)\n return config\n }\n}\n\nexport interface AnalysisOpts {\n gameModes: string[]\n}\n\ninterface FilePaths {\n lut: string\n lutSegmented: string\n lutOptimized: string\n booksJsonl: string\n booksJsonlCompressed: string\n}\n\ninterface Statistics {\n gameMode: string\n totalWeight: number\n rtp: number\n avgWin: number\n minWin: number\n maxWin: number\n stdDev: number\n variance: number\n maxwinHitRate: number\n nonZeroHitRate: number\n nullHitRate: number\n lessBetHitRate: number\n uniquePayouts: number\n}\n","export function parseLookupTable(content: string) {\n const lines = content.trim().split(\"\\n\")\n const lut: LookupTable = []\n for (const line of lines) {\n const [indexStr, weightStr, payoutStr] = line.split(\",\")\n const index = parseInt(indexStr!.trim())\n const weight = parseInt(weightStr!.trim())\n const payout = parseFloat(payoutStr!.trim())\n lut.push([index, weight, payout])\n }\n return lut\n}\n\nexport function parseLookupTableSegmented(content: string) {\n const lines = content.trim().split(\"\\n\")\n const lut: LookupTableSegmented = []\n for (const line of lines) {\n const [indexStr, criteria, weightStr, payoutStr] = line.split(\",\")\n const index = parseInt(indexStr!.trim())\n const weight = parseInt(weightStr!.trim())\n const payout = parseFloat(payoutStr!.trim())\n lut.push([index, criteria!, weight, payout])\n }\n return lut\n}\n\nexport type LookupTable = [number, number, number][]\nexport type LookupTableSegmented = [number, string, number, number][]\n\nexport function getTotalLutWeight(lut: LookupTable) {\n return lut.reduce((sum, [, weight]) => sum + weight, 0)\n}\n\nexport function getTotalWeight(payoutWeights: PayoutWeights) {\n return Object.values(payoutWeights).reduce((sum, w) => sum + w, 0)\n}\n\ntype PayoutWeights = Record<number, number>\n\nexport function getPayoutWeights(\n lut: LookupTable,\n opts: { normalize?: boolean } = {},\n): PayoutWeights {\n const { normalize = true } = opts\n const totalWeight = getTotalLutWeight(lut)\n\n let payoutWeights: Record<number, number> = {}\n\n for (const [, weight, p] of lut) {\n const payout = p / 100\n if (payoutWeights[payout] === undefined) {\n payoutWeights[payout] = 0\n }\n payoutWeights[payout] += weight\n }\n\n // Sort by payout value\n payoutWeights = Object.fromEntries(\n Object.entries(payoutWeights).sort(([a], [b]) => parseFloat(a) - parseFloat(b)),\n )\n\n if (normalize) {\n for (const payout in payoutWeights) {\n payoutWeights[payout]! /= totalWeight\n }\n }\n\n return payoutWeights\n}\n\nexport function getNonZeroHitrate(payoutWeights: PayoutWeights) {\n const totalWeight = getTotalWeight(payoutWeights)\n if (Math.min(...Object.keys(payoutWeights).map(Number)) == 0) {\n return totalWeight / (totalWeight - (payoutWeights[0] ?? 0) / totalWeight)\n } else {\n return 1\n }\n}\n\nexport function getNullHitrate(payoutWeights: PayoutWeights) {\n return payoutWeights[0] ?? 0\n}\n\nexport function getMaxwinHitrate(payoutWeights: PayoutWeights) {\n const totalWeight = getTotalWeight(payoutWeights)\n const maxWin = Math.max(...Object.keys(payoutWeights).map(Number))\n const hitRate = (payoutWeights[maxWin] || 0) / totalWeight\n return 1 / hitRate\n}\n\nexport function getUniquePayouts(payoutWeights: PayoutWeights) {\n return Object.keys(payoutWeights).length\n}\n\nexport function getMinWin(payoutWeights: PayoutWeights) {\n const payouts = Object.keys(payoutWeights).map(Number)\n return Math.min(...payouts)\n}\n\nexport function getMaxWin(payoutWeights: PayoutWeights) {\n const payouts = Object.keys(payoutWeights).map(Number)\n return Math.max(...payouts)\n}\n\nexport function getAvgWin(payoutWeights: PayoutWeights) {\n let avgWin = 0\n for (const [payoutStr, weight] of Object.entries(payoutWeights)) {\n const payout = parseFloat(payoutStr)\n avgWin += payout * weight\n }\n return avgWin\n}\n\nexport function getRtp(payoutWeights: PayoutWeights, cost: number) {\n const avgWin = getAvgWin(payoutWeights)\n return avgWin / cost\n}\n\nexport function getStandardDeviation(payoutWeights: PayoutWeights) {\n const variance = getVariance(payoutWeights)\n return Math.sqrt(variance)\n}\n\nexport function getVariance(payoutWeights: PayoutWeights) {\n const totalWeight = getTotalWeight(payoutWeights)\n const avgWin = getAvgWin(payoutWeights)\n let variance = 0\n for (const [payoutStr, weight] of Object.entries(payoutWeights)) {\n const payout = parseFloat(payoutStr)\n variance += Math.pow(payout - avgWin, 2) * (weight / totalWeight)\n }\n return variance\n}\n\nexport function getLessBetHitrate(payoutWeights: PayoutWeights, cost: number) {\n let lessBetWeight = 0\n const totalWeight = getTotalWeight(payoutWeights)\n for (const [payoutStr, weight] of Object.entries(payoutWeights)) {\n const payout = parseFloat(payoutStr)\n if (payout < cost) {\n lessBetWeight += weight\n }\n }\n return lessBetWeight / totalWeight\n}\n","import path from \"path\"\nimport assert from \"assert\"\nimport { spawn } from \"child_process\"\nimport { isMainThread } from \"worker_threads\"\nimport { makeMathConfig } from \"../utils/math-config\"\nimport { makeSetupFile } from \"../utils/setup-file\"\nimport { OptimizationParameters } from \"./OptimizationParameters\"\nimport { OptimizationConditions } from \"./OptimizationConditions\"\nimport { OptimizationScaling } from \"./OptimizationScaling\"\nimport { SlotGame } from \"../slot-game\"\nimport { GameConfig } from \"../game-config\"\n\nexport class Optimizer {\n protected readonly gameConfig: GameConfig\n protected readonly gameModes: OptimzierGameModeConfig\n\n constructor(opts: OptimizerOpts) {\n this.gameConfig = opts.game.getConfig()\n this.gameModes = opts.gameModes\n this.verifyConfig()\n }\n\n /**\n * Runs the optimization process, and runs analysis after.\n */\n async runOptimization({ gameModes }: OptimizationOpts) {\n if (!isMainThread) return // IMPORTANT: Prevent workers from kicking off (multiple) optimizations\n\n const mathConfig = makeMathConfig(this, { writeToFile: true })\n\n for (const mode of gameModes) {\n const setupFile = makeSetupFile(this, mode)\n await this.runSingleOptimization()\n }\n console.log(\"Optimization complete. Files written to build directory.\")\n }\n\n private async runSingleOptimization() {\n return await rustProgram()\n }\n\n private verifyConfig() {\n for (const [k, mode] of Object.entries(this.gameModes)) {\n const configMode = this.gameConfig.gameModes[k]\n\n if (!configMode) {\n throw new Error(\n `Game mode \"${mode}\" defined in optimizer config does not exist in the game config.`,\n )\n }\n\n const conditions = Object.keys(mode.conditions)\n const scalings = Object.keys(mode.scaling)\n const parameters = Object.keys(mode.parameters)\n\n for (const rs of configMode.resultSets) {\n if (!conditions.includes(rs.criteria)) {\n throw new Error(\n `ResultSet criteria \"${rs.criteria}\" in game mode \"${k}\" does not have a corresponding optimization condition defined.`,\n )\n }\n }\n\n let gameModeRtp = configMode.rtp\n let paramRtp = 0\n for (const cond of conditions) {\n const paramConfig = mode.conditions[cond]!\n paramRtp += Number(paramConfig.getRtp())\n }\n\n gameModeRtp = Math.round(gameModeRtp * 1000) / 1000\n paramRtp = Math.round(paramRtp * 1000) / 1000\n\n assert(\n gameModeRtp === paramRtp,\n `Sum of all RTP conditions (${paramRtp}) does not match the game mode RTP (${gameModeRtp}) in game mode \"${k}\".`,\n )\n }\n }\n\n getGameConfig() {\n return this.gameConfig\n }\n\n getOptimizerGameModes() {\n return this.gameModes\n }\n}\n\nasync function rustProgram(...args: string[]) {\n console.log(\"Starting Rust optimizer. This may take a while...\")\n\n return new Promise((resolve, reject) => {\n const task = spawn(\"cargo\", [\"run\", \"-q\", \"--release\", ...args], {\n cwd: path.join(__dirname, \"./optimizer-rust\"),\n stdio: \"pipe\",\n })\n task.on(\"error\", (error) => {\n console.error(\"Error:\", error)\n reject(error)\n })\n task.on(\"exit\", () => {\n resolve(true)\n })\n task.on(\"close\", () => {\n resolve(true)\n })\n task.stdout.on(\"data\", (data) => {\n console.log(data.toString())\n })\n task.stderr.on(\"data\", (data) => {\n console.log(data.toString())\n })\n task.stdout.on(\"error\", (data) => {\n console.log(data.toString())\n reject(data.toString())\n })\n })\n}\n\nexport interface OptimizationOpts {\n gameModes: string[]\n}\n\nexport interface OptimizerOpts {\n game: SlotGame<any, any, any>\n gameModes: OptimzierGameModeConfig\n}\n\nexport type OptimzierGameModeConfig = Record<\n string,\n {\n conditions: Record<string, OptimizationConditions>\n scaling: OptimizationScaling\n parameters: OptimizationParameters\n }\n>\n\nexport { OptimizationConditions } from \"./OptimizationConditions\"\nexport { OptimizationScaling } from \"./OptimizationScaling\"\nexport { OptimizationParameters } from \"./OptimizationParameters\"","import path from \"path\"\nimport { type Optimizer } from \"../optimizer\"\nimport { writeJsonFile } from \"../../utils\"\n\nexport function makeMathConfig(\n optimizer: Optimizer,\n opts: { writeToFile?: boolean } = {},\n) {\n const game = optimizer.getGameConfig()\n const gameModesCfg = optimizer.getOptimizerGameModes()\n const { writeToFile } = opts\n\n const isDefined = <T>(v: T | undefined): v is T => v !== undefined\n\n const config: MathConfig = {\n game_id: game.id,\n bet_modes: Object.entries(game.gameModes).map(([key, mode]) => ({\n bet_mode: mode.name,\n cost: mode.cost,\n rtp: mode.rtp,\n max_win: game.maxWinX,\n })),\n fences: Object.entries(gameModesCfg).map(([gameModeName, modeCfg]) => ({\n bet_mode: gameModeName,\n fences: Object.entries(modeCfg.conditions)\n .map(([fenceName, fence]) => ({\n name: fenceName,\n avg_win: isDefined(fence.getAvgWin())\n ? fence.getAvgWin()!.toString()\n : undefined,\n hr: isDefined(fence.getHitRate()) ? fence.getHitRate()!.toString() : undefined,\n rtp: isDefined(fence.getRtp()) ? fence.getRtp()!.toString() : undefined,\n identity_condition: {\n search: Object.entries(fence.getForceSearch()).map(([k, v]) => ({\n name: k,\n value: v,\n })),\n win_range_start: fence.getSearchRange()[0]!,\n win_range_end: fence.getSearchRange()[1]!,\n opposite: false,\n },\n priority: fence.priority,\n }))\n .sort((a, b) => b.priority - a.priority),\n })),\n dresses: Object.entries(gameModesCfg).flatMap(([gameModeName, modeCfg]) => ({\n bet_mode: gameModeName,\n dresses: modeCfg.scaling.getConfig().map((s) => ({\n fence: s.criteria,\n scale_factor: s.scaleFactor.toString(),\n identity_condition_win_range: s.winRange,\n prob: s.probability,\n })),\n })),\n }\n\n if (writeToFile) {\n const outPath = path.join(game.rootDir, game.outputDir, \"math_config.json\")\n writeJsonFile(outPath, config)\n }\n\n return config\n}\n\nexport type MathConfig = {\n game_id: string\n bet_modes: Array<{\n bet_mode: string\n cost: number\n rtp: number\n max_win: number\n }>\n fences: Array<{\n bet_mode: string\n fences: Array<Fence>\n }>\n dresses: Array<{\n bet_mode: string\n dresses: Dress[]\n }>\n}\n\ninterface Search {\n name: string\n value: string\n}\n\ninterface IdentityCondition {\n search: Search[]\n opposite: boolean\n win_range_start: number\n win_range_end: number\n}\n\ninterface Fence {\n name: string\n avg_win?: string\n rtp?: string\n hr?: string\n identity_condition: IdentityCondition\n priority: number\n}\n\ninterface Dress {\n fence: string\n scale_factor: string\n identity_condition_win_range: [number, number]\n prob: number\n}\n","import path from \"path\"\nimport { writeFile } from \"../../utils\"\nimport { Optimizer } from \"../optimizer\"\n\nexport function makeSetupFile(optimizer: Optimizer, gameMode: string) {\n const gameConfig = optimizer.getGameConfig()\n const optimizerGameModes = optimizer.getOptimizerGameModes()\n const modeConfig = optimizerGameModes[gameMode]\n\n if (!modeConfig) {\n throw new Error(`Game mode \"${gameMode}\" not found in optimizer configuration.`)\n }\n\n const params = modeConfig.parameters.getParameters()\n\n let content = \"\"\n content += `game_name;${gameConfig.id}\\n`\n content += `bet_type;${gameMode}\\n`\n content += `num_show_pigs;${params.numShowPigs}\\n`\n content += `num_pigs_per_fence;${params.numPigsPerFence}\\n`\n content += `threads_for_fence_construction;${params.threadsFenceConstruction}\\n`\n content += `threads_for_show_construction;${params.threadsShowConstruction}\\n`\n content += `score_type;${params.scoreType}\\n`\n content += `test_spins;${JSON.stringify(params.testSpins)}\\n`\n content += `test_spins_weights;${JSON.stringify(params.testSpinsWeights)}\\n`\n content += `simulation_trials;${params.simulationTrials}\\n`\n content += `graph_indexes;0\\n`\n content += `run_1000_batch;False\\n`\n content += `simulation_trials;${params.simulationTrials}\\n`\n content += `user_game_build_path;${path.join(gameConfig.rootDir, gameConfig.outputDir)}\\n`\n content += `pmb_rtp;${params.pmbRtp}\\n`\n\n const outPath = path.join(__dirname, \"./optimizer-rust/src\", \"setup.txt\")\n\n writeFile(outPath, content)\n}\n","import assert from \"assert\"\n\nexport class OptimizationConditions {\n protected rtp?: number | \"x\"\n protected avgWin?: number\n protected hitRate?: number | \"x\"\n protected searchRange: number[]\n protected forceSearch: Record<string, string>\n priority: number\n\n constructor(opts: OptimizationConditionsOpts) {\n let { rtp, avgWin, hitRate, searchConditions, priority } = opts\n\n if (rtp == undefined || rtp === \"x\") {\n assert(avgWin !== undefined && hitRate !== undefined, \"If RTP is not specified, hit-rate (hr) and average win amount (av_win) must be given.\")\n rtp = Math.round((avgWin! / Number(hitRate)) * 100000) / 100000\n }\n\n let noneCount = 0\n for (const val of [rtp, avgWin, hitRate]) {\n if (val === undefined) noneCount++\n }\n assert(noneCount <= 1, \"Invalid combination of optimization conditions.\")\n\n this.searchRange = [-1, -1]\n this.forceSearch = {}\n\n if (typeof searchConditions === \"number\") {\n this.searchRange = [searchConditions, searchConditions]\n }\n if (Array.isArray(searchConditions)) {\n if (searchConditions[0] > searchConditions[1] || searchConditions.length !== 2) {\n throw new Error(\"Invalid searchConditions range.\")\n }\n this.searchRange = searchConditions\n }\n if (typeof searchConditions === \"object\" && !Array.isArray(searchConditions)) {\n this.searchRange = [-1, -1]\n this.forceSearch = searchConditions\n }\n\n this.rtp = rtp\n this.avgWin = avgWin\n this.hitRate = hitRate\n this.priority = priority\n }\n\n getRtp() {\n return this.rtp\n }\n\n getAvgWin() {\n return this.avgWin\n }\n\n getHitRate() {\n return this.hitRate\n }\n\n getSearchRange() {\n return this.searchRange\n }\n\n getForceSearch() {\n return this.forceSearch\n }\n}\n\ninterface OptimizationConditionsOpts {\n /**\n * The desired RTP (0-1)\n */\n rtp?: number | \"x\"\n /**\n * The desired average win (per spin).\n */\n avgWin?: number\n /**\n * The desired hit rate (e.g. `200` to hit 1 in 200 spins).\n */\n hitRate?: number | \"x\"\n /**\n * A way of filtering results by\n *\n * - A number (payout multiplier), e.g. `5000`\n * - Force record value, e.g. `{ \"symbolId\": \"scatter\" }`\n * - A range of numbers, e.g. `[0, 100]` (payout multiplier range)\n */\n searchConditions?: number | Record<string, string> | [number, number]\n /**\n * **Priority matters!**\\\n * Higher priority conditions will be evaluated first.\\\n * After a book matching this condition is found, the book will be removed from the pool\\\n * and can't be used to satisfy other conditions with lower priority.\n * \n * TODO add better explanation\n */\n priority: number\n}\n","export class OptimizationScaling {\n protected config: OptimizationScalingOpts\n\n constructor(opts: OptimizationScalingOpts) {\n this.config = opts\n }\n\n getConfig() {\n return this.config\n }\n}\n\ntype OptimizationScalingOpts = Array<{\n criteria: string\n scaleFactor: number\n winRange: [number, number]\n probability: number\n}>\n","export class OptimizationParameters {\n protected parameters: OptimizationParametersOpts\n\n constructor(opts?: Partial<OptimizationParametersOpts>) {\n this.parameters = {\n ...OptimizationParameters.DEFAULT_PARAMETERS,\n ...opts,\n }\n }\n\n static DEFAULT_PARAMETERS: OptimizationParametersOpts = {\n numShowPigs: 5000,\n numPigsPerFence: 10000,\n threadsFenceConstruction: 16,\n threadsShowConstruction: 16,\n testSpins: [50, 100, 200],\n testSpinsWeights: [0.3, 0.4, 0.3],\n simulationTrials: 5000,\n graphIndexes: [],\n run1000Batch: false,\n minMeanToMedian: 4,\n maxMeanToMedian: 8,\n pmbRtp: 1.0,\n scoreType: \"rtp\",\n }\n\n getParameters() {\n return this.parameters\n }\n}\n\nexport interface OptimizationParametersOpts {\n readonly numShowPigs: number\n readonly numPigsPerFence: number\n readonly threadsFenceConstruction: number\n readonly threadsShowConstruction: number\n readonly testSpins: number[]\n readonly testSpinsWeights: number[]\n readonly simulationTrials: number\n readonly graphIndexes: number[]\n readonly run1000Batch: false\n readonly minMeanToMedian: number\n readonly maxMeanToMedian: number\n readonly pmbRtp: number\n readonly scoreType: \"rtp\"\n}\n","import { AnyGameModes, AnySymbols, AnyUserData } from \"../types\"\nimport { createGameConfig, GameConfigOptions } from \"../game-config\"\nimport { Simulation, SimulationConfigOptions, SimulationOptions } from \"../simulation\"\nimport { Analysis, AnalysisOpts } from \"../analysis\"\nimport { OptimizationOpts, Optimizer, OptimizerOpts } from \"../optimizer\"\nimport { isMainThread } from \"worker_threads\"\n\n/**\n * SlotGame class that encapsulates the game configuration and state.\\\n * Main entry point for the slot game.\n */\nexport class SlotGame<\n TGameModes extends AnyGameModes = AnyGameModes,\n TSymbols extends AnySymbols = AnySymbols,\n TUserState extends AnyUserData = AnyUserData,\n> {\n private readonly configOpts: GameConfigOptions<TGameModes, TSymbols, TUserState>\n private simulation?: Simulation\n private optimizer?: Optimizer\n private analyzer?: Analysis\n\n constructor(config: GameConfigOptions<TGameModes, TSymbols, TUserState>) {\n this.configOpts = config\n }\n\n /**\n * Sets up the simulation configuration.\\\n * Must be called before `runTasks()`.\n */\n configureSimulation(opts: SimulationOptions) {\n // @ts-ignore TODO: Fix type errors with AnyTypes\n this.simulation = new Simulation(opts, this.configOpts)\n }\n\n /**\n * Sets up the optimization configuration.\\\n * Must be called before `runTasks()`.\n */\n configureOptimization(opts: Pick<OptimizerOpts, \"gameModes\">) {\n this.optimizer = new Optimizer({\n game: this,\n gameModes: opts.gameModes,\n })\n }\n\n /**\n * Runs the simulation based on the configured settings.\n */\n private async runSimulation(opts: SimulationConfigOptions = {}) {\n if (!this.simulation) {\n throw new Error(\n \"Simulation is not configured. Do so by calling configureSimulation() first.\",\n )\n }\n await this.simulation.runSimulation(opts)\n }\n\n /**\n * Runs the optimization based on the configured settings.\n */\n private async runOptimization(opts: OptimizationOpts) {\n if (!this.optimizer) {\n throw new Error(\n \"Optimization is not configured. Do so by calling configureOptimization() first.\",\n )\n }\n\n await this.optimizer.runOptimization(opts)\n }\n\n /**\n * Runs the analysis based on the configured settings.\n */\n private async runAnalysis(opts: AnalysisOpts) {\n if (!this.optimizer) {\n throw new Error(\n \"Optimization must be configured to run analysis. Do so by calling configureOptimization() first.\",\n )\n }\n this.analyzer = new Analysis(this.optimizer)\n await this.analyzer.runAnalysis(opts.gameModes)\n }\n\n /**\n * Runs the configured tasks: simulation, optimization, and/or analysis.\n */\n async runTasks(\n opts: {\n doSimulation?: boolean\n doOptimization?: boolean\n doAnalysis?: boolean\n simulationOpts?: SimulationConfigOptions\n optimizationOpts?: OptimizationOpts\n analysisOpts?: AnalysisOpts\n } = {},\n ) {\n if (!opts.doSimulation && !opts.doOptimization && !opts.doAnalysis) {\n console.log(\"No tasks to run. Enable either simulation, optimization or analysis.\")\n }\n\n if (opts.doSimulation) {\n await this.runSimulation(opts.simulationOpts || {})\n }\n\n if (opts.doOptimization) {\n await this.runOptimization(opts.optimizationOpts || { gameModes: [] })\n }\n\n if (opts.doAnalysis) {\n await this.runAnalysis(opts.analysisOpts || { gameModes: [] })\n }\n\n if (isMainThread) console.log(\"Finishing up...\")\n }\n\n /**\n * Gets the game configuration.\n */\n getConfig() {\n return createGameConfig(this.configOpts)\n }\n}\n","import { GameConfigOptions } from \"./game-config\"\nimport { SlotGame } from \"./slot-game\"\nimport { AnyGameModes, AnySymbols, AnyUserData, InferGameType } from \"./types\"\n\nexport function createSlotGame<TGame>(\n opts: TGame extends InferGameType<infer G, infer S, infer U>\n ? GameConfigOptions<G, S, U>\n : never,\n) {\n return new SlotGame(opts) as TGame\n}\n\nexport const defineUserState = <TUserState extends AnyUserData>(data: TUserState) => data\n\nexport const defineSymbols = <TSymbols extends AnySymbols>(symbols: TSymbols) => symbols\n\nexport const defineGameModes = <TGameModes extends AnyGameModes>(gameModes: TGameModes) =>\n gameModes\n","import assert from \"assert\"\nimport { ResultSet } from \"../result-set\"\nimport { ReelSet } from \"../reel-set\"\n\nexport class GameMode {\n readonly name: string\n readonly reelsAmount: number\n readonly symbolsPerReel: number[]\n readonly cost: number\n readonly rtp: number\n readonly reelSets: ReelSet[]\n readonly resultSets: ResultSet<any>[]\n readonly isBonusBuy: boolean\n\n constructor(opts: GameModeOpts) {\n this.name = opts.name\n this.reelsAmount = opts.reelsAmount\n this.symbolsPerReel = opts.symbolsPerReel\n this.cost = opts.cost\n this.rtp = opts.rtp\n this.reelSets = opts.reelSets\n this.resultSets = opts.resultSets\n this.isBonusBuy = opts.isBonusBuy\n\n assert(this.rtp >= 0.9 && this.rtp <= 0.99, \"RTP must be between 0.9 and 0.99\")\n\n assert(\n this.symbolsPerReel.length === this.reelsAmount,\n \"symbolsPerReel length must match reelsAmount.\",\n )\n\n assert(this.reelSets.length > 0, \"GameMode must have at least one ReelSet defined.\")\n }\n}\n\nexport interface GameModeOpts {\n /**\n * Name of the game mode.\n */\n name: string\n /**\n * Number of reels the board has.\n */\n reelsAmount: number\n /**\n * How many symbols each reel has. Array length must match `reelsAmount`.\\\n * The number at an array index represents the number of symbols on that reel.\n */\n symbolsPerReel: number[]\n /**\n * Cost of the game mode, multiplied by the base bet.\n */\n cost: number\n /**\n * The target RTP of the game.\n */\n rtp: number\n /**\n * Defines (and generates) all reels for the game.\\\n * Which reels are used in a spin is determined by the ResultSet of the current game mode.\n *\n * It is common to have one reel set for the base game and another for free spins.\\\n * Each `ResultSet` can then set the weights of these reel sets to control which\\\n * reel set is used for a specific criteria.\n */\n reelSets: ReelSet[]\n /**\n * A ResultSet defines how often a specific outcome should be generated.\\\n * For example, a ResultSet can be used to force a specific ratio of max wins\\\n * in the simulations to ensure there are different frontend representations.\n */\n resultSets: ResultSet<any>[]\n /**\n * Whether this game mode is a bonus buy.\n */\n isBonusBuy: boolean\n}\n","import { GameContext } from \"../game-context\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { Reels } from \"../types\"\n\nexport class WinType {\n protected payout: number\n protected winCombinations: WinCombination[]\n protected ctx: GameContext\n protected readonly wildSymbol?: WildSymbol\n\n constructor(opts: WinTypeOpts) {\n this.ctx = opts.ctx\n this.payout = 0\n this.winCombinations = []\n this.wildSymbol = opts?.wildSymbol\n }\n\n /**\n * Implementation of win evaluation logic. Sets `this.payout` and `this.winCombinations`.\n */\n evaluateWins(board: Reels) {\n return this\n }\n\n /**\n * Custom post-processing of wins, e.g. for handling multipliers.\n */\n postProcess(func: PostProcessFn<typeof this.winCombinations>) {\n const result = func(this.winCombinations, this.ctx)\n this.winCombinations = result.winCombinations\n this.payout = result.winCombinations.reduce((sum, w) => sum + w.payout, 0)\n return this\n }\n\n /**\n * Returns the total payout and detailed win combinations.\n */\n getWins() {\n return {\n payout: this.payout,\n winCombinations: this.winCombinations,\n }\n }\n\n protected isWild(symbol: GameSymbol) {\n return !!this.wildSymbol && symbol.compare(this.wildSymbol)\n }\n\n protected getSymbolPayout(symbol: GameSymbol, count: number) {\n if (!symbol.pays) return 0\n\n let clusterSize = 0\n\n const sizes = Object.keys(symbol.pays)\n .map((s) => parseInt(s, 10))\n .filter((n) => Number.isFinite(n))\n .sort((a, b) => a - b)\n\n for (const size of sizes) {\n if (size > count) break\n clusterSize = size\n }\n\n return symbol.pays[clusterSize] || 0\n }\n}\n\nexport interface WinTypeOpts {\n /**\n * A reference to the game context.\n */\n ctx: GameContext<any, any, any> // TODO: Hacky and stupid. Fix AnyTypes.\n /**\n * Configuration used to identify wild symbols on the board.\\\n * You can either provide a specific `GameSymbol` instance or a set of properties to match against symbols on the board.\n *\n * @example\n * If you have different wild symbols, each with a property `isWild: true`, you can define:\n * ```ts\n * wildSymbol: { isWild: true }\n * ```\n *\n * @example\n * If you have a single wild symbol instance, you can define:\n * ```ts\n * wildSymbol: myWildSymbol\n * ```\n */\n wildSymbol?: WildSymbol\n}\n\nexport type WinCombination = {\n payout: number\n kind: number\n baseSymbol: GameSymbol\n symbols: Array<{\n symbol: GameSymbol\n isWild: boolean\n substitutedFor?: GameSymbol\n reelIndex: number\n posIndex: number\n }>\n}\n\ntype PostProcessFn<TWinCombs extends WinCombination[]> = (\n wins: TWinCombs,\n ctx: GameContext,\n) => {\n winCombinations: TWinCombs\n}\n\ntype WildSymbol = GameSymbol | Record<string, any>\n\nexport type Symbol = { reel: number; row: number; symbol: GameSymbol }\nexport type SymbolList = Array<Symbol>\nexport type SymbolMap = Map<string, Symbol>\n","import { GameSymbol } from \"../game-symbol\"\nimport { SymbolList, WinCombination, WinType, WinTypeOpts } from \".\"\nimport { Reels } from \"../types\"\nimport assert from \"assert\"\n\nexport class LinesWinType extends WinType {\n protected lines: Record<number, number[]>\n declare protected winCombinations: LineWinCombination[]\n declare getWins: () => {\n payout: number\n winCombinations: LineWinCombination[]\n }\n\n constructor(opts: LinesWinTypeOpts) {\n super(opts)\n this.lines = opts.lines\n\n if (Object.keys(this.lines).length === 0) {\n throw new Error(\"LinesWinType must have at least one line defined.\")\n }\n }\n\n private validateConfig() {\n const reelsAmount = this.ctx.services.game.getCurrentGameMode().reelsAmount\n const symsPerReel = this.ctx.services.game.getCurrentGameMode().symbolsPerReel\n\n for (const [lineNum, positions] of Object.entries(this.lines)) {\n if (positions.length !== reelsAmount) {\n throw new Error(\n `Line ${lineNum} has ${positions.length} positions, but the current game mode has ${reelsAmount} reels.`,\n )\n }\n for (let i = 0; i < positions.length; i++) {\n if (positions[i]! < 0 || positions[i]! >= symsPerReel[i]!) {\n throw new Error(\n `Line ${lineNum} has an invalid position ${positions[i]} on reel ${i}. Valid range is 0 to ${\n symsPerReel[i]! - 1\n }.`,\n )\n }\n }\n }\n\n const firstLine = Math.min(...Object.keys(this.lines).map(Number))\n if (firstLine !== 1) {\n throw new Error(\n `Lines must start from 1. Found line ${firstLine} as the first line.`,\n )\n }\n }\n\n /**\n * Calculates wins based on the defined paylines and provided board state.\\\n * Retrieve the results using `getWins()` after.\n */\n evaluateWins(board: Reels) {\n this.validateConfig()\n\n const lineWins: LineWinCombination[] = []\n\n const reels = board\n\n for (const [lineNumStr, line] of Object.entries(this.lines)) {\n const lineNum = Number(lineNumStr)\n let baseSymbol: GameSymbol | undefined\n const potentialWinLine: SymbolList = []\n const potentialWildLine: SymbolList = []\n let isInterrupted = false\n\n for (const [ridx, reel] of reels.entries()) {\n const sidx = line[ridx]!\n const thisSymbol = reel[sidx]\n\n if (!baseSymbol) {\n baseSymbol = thisSymbol\n }\n\n assert(baseSymbol, `No symbol found at line ${lineNum}, reel ${ridx}`)\n assert(thisSymbol, `No symbol found at line ${lineNum}, reel ${ridx}`)\n\n if (potentialWinLine.length == 0) {\n if (this.isWild(thisSymbol)) {\n potentialWildLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n }\n potentialWinLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n continue\n }\n\n if (this.isWild(baseSymbol)) {\n if (this.isWild(thisSymbol)) {\n potentialWildLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n } else {\n baseSymbol = thisSymbol\n }\n potentialWinLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n continue\n }\n\n if (baseSymbol.compare(thisSymbol) || this.isWild(thisSymbol)) {\n potentialWinLine.push({ reel: ridx, row: sidx, symbol: thisSymbol })\n } else {\n isInterrupted = true\n break\n }\n }\n\n const minSymLine = Math.min(\n ...Object.keys(baseSymbol!.pays || {}).map((k) => parseInt(k, 10)),\n )\n\n if (potentialWinLine.length < minSymLine) continue\n\n const linePayout = this.getLinePayout(potentialWinLine)\n const wildLinePayout = this.getLinePayout(potentialWildLine)\n\n let finalLine: LineWinCombination = {\n kind: potentialWinLine.length,\n baseSymbol: baseSymbol!,\n symbols: potentialWinLine.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n lineNumber: lineNum,\n payout: linePayout,\n }\n\n if (wildLinePayout > linePayout) {\n baseSymbol = potentialWildLine[0]?.symbol\n\n finalLine = {\n kind: potentialWildLine.length,\n baseSymbol: baseSymbol!,\n symbols: potentialWildLine.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n lineNumber: lineNum,\n payout: wildLinePayout,\n }\n }\n\n lineWins.push(finalLine)\n }\n\n for (const win of lineWins) {\n this.ctx.services.data.recordSymbolOccurrence({\n kind: win.kind,\n symbolId: win.baseSymbol.id,\n spinType: this.ctx.state.currentSpinType,\n })\n }\n\n this.payout = lineWins.reduce((sum, l) => sum + l.payout, 0)\n this.winCombinations = lineWins\n\n return this\n }\n\n private getLinePayout(line: SymbolList) {\n if (line.length === 0) return 0\n\n let baseSymbol = line.find((s) => !this.isWild(s.symbol))?.symbol\n if (!baseSymbol) baseSymbol = line[0]!.symbol\n\n const kind = line.length\n const payout = this.getSymbolPayout(baseSymbol, kind)\n\n return payout\n }\n}\n\ninterface LinesWinTypeOpts extends WinTypeOpts {\n /**\n * Defines the paylines for the slot game.\n *\n * @example\n * ```ts\n * lines: {\n * 1: [0, 0, 0, 0, 0],\n * 2: [1, 1, 1, 1, 1],\n * 3: [2, 2, 2, 2, 2],\n * }\n * ```\n */\n lines: Record<number, number[]>\n}\n\nexport interface LineWinCombination extends WinCombination {\n lineNumber: number\n}\n","import { GameSymbol } from \"../game-symbol\"\nimport { SymbolList, SymbolMap, WinCombination, WinType, WinTypeOpts } from \".\"\nimport { Reels } from \"../types\"\n\nexport class ClusterWinType extends WinType {\n declare protected winCombinations: ClusterWinCombination[]\n declare getWins: () => {\n payout: number\n winCombinations: ClusterWinCombination[]\n }\n\n private _checked: SymbolList = []\n private _checkedWilds: SymbolList = []\n private _currentBoard: Reels = []\n\n constructor(opts: ClusterWinTypeOpts) {\n super(opts)\n }\n\n private validateConfig() {}\n\n /**\n * Calculates wins based on symbol cluster size and provided board state.\\\n * Retrieve the results using `getWins()` after.\n */\n evaluateWins(board: Reels) {\n this.validateConfig()\n this._checked = []\n this._currentBoard = board\n\n const clusterWins: ClusterWinCombination[] = []\n const potentialClusters: SymbolList[] = []\n\n // Get normal symbol clusters\n for (const [ridx, reel] of board.entries()) {\n for (const [sidx, symbol] of reel.entries()) {\n this._checkedWilds = [] // each cluster can check wilds anew\n\n if (this.isWild(symbol)) continue\n\n if (this.isChecked(ridx, sidx)) {\n continue\n }\n\n const thisSymbol = { reel: ridx, row: sidx, symbol }\n this._checked.push(thisSymbol)\n\n const neighbors = this.getNeighbors(ridx, sidx)\n const matchingSymbols = this.evaluateCluster(symbol, neighbors)\n\n // Record clusters from 2 symbols and up\n if (matchingSymbols.size >= 1) {\n potentialClusters.push([thisSymbol, ...matchingSymbols.values()])\n }\n }\n }\n\n // Get wild only clusters\n for (const [ridx, reel] of board.entries()) {\n for (const [sidx, symbol] of reel.entries()) {\n this._checkedWilds = []\n\n if (!this.isWild(symbol)) continue\n\n if (this.isChecked(ridx, sidx)) {\n continue\n }\n\n const thisSymbol = { reel: ridx, row: sidx, symbol }\n this._checked.push(thisSymbol)\n\n const neighbors = this.getNeighbors(ridx, sidx)\n const matchingSymbols = this.evaluateCluster(symbol, neighbors)\n\n // Record clusters from 2 symbols and up\n if (matchingSymbols.size >= 1) {\n potentialClusters.push([thisSymbol, ...matchingSymbols.values()])\n }\n }\n }\n\n for (const cluster of potentialClusters) {\n const kind = cluster.length\n let baseSymbol = cluster.find((s) => !this.isWild(s.symbol))?.symbol\n if (!baseSymbol) baseSymbol = cluster[0]!.symbol\n\n const payout = this.getSymbolPayout(baseSymbol, kind)\n if (payout === 0) continue\n\n if (!baseSymbol.pays || Object.keys(baseSymbol.pays).length === 0) {\n continue // don't add non-paying symbols to final clusters\n }\n\n clusterWins.push({\n payout,\n kind,\n baseSymbol,\n symbols: cluster.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n })\n }\n\n for (const win of clusterWins) {\n this.ctx.services.data.recordSymbolOccurrence({\n kind: win.kind,\n symbolId: win.baseSymbol.id,\n spinType: this.ctx.state.currentSpinType,\n })\n }\n\n this.payout = clusterWins.reduce((sum, c) => sum + c.payout, 0)\n this.winCombinations = clusterWins\n\n return this\n }\n\n private getNeighbors(ridx: number, sidx: number) {\n const board = this._currentBoard\n const neighbors: SymbolList = []\n\n const potentialNeighbors: Array<[number, number]> = [\n [ridx - 1, sidx],\n [ridx + 1, sidx],\n [ridx, sidx - 1],\n [ridx, sidx + 1],\n ]\n\n potentialNeighbors.forEach(([nridx, nsidx]) => {\n if (board[nridx] && board[nridx][nsidx]) {\n neighbors.push({ reel: nridx, row: nsidx, symbol: board[nridx][nsidx] })\n }\n })\n\n return neighbors\n }\n\n private evaluateCluster(rootSymbol: GameSymbol, neighbors: SymbolList) {\n const matchingSymbols: SymbolMap = new Map()\n\n neighbors.forEach((neighbor) => {\n const { reel, row, symbol } = neighbor\n\n if (this.isChecked(reel, row)) return\n if (this.isCheckedWild(reel, row)) return\n\n if (this.isWild(symbol) || symbol.compare(rootSymbol)) {\n const key = `${reel}-${row}`\n matchingSymbols.set(key, { reel, row, symbol })\n\n if (symbol.compare(rootSymbol)) {\n this._checked.push(neighbor)\n }\n\n if (this.isWild(symbol)) {\n this._checkedWilds.push(neighbor)\n }\n\n const neighbors = this.getNeighbors(reel, row)\n const nestedMatches = this.evaluateCluster(rootSymbol, neighbors)\n nestedMatches.forEach((nsym) => {\n const nkey = `${nsym.reel}-${nsym.row}`\n matchingSymbols.set(nkey, nsym)\n })\n }\n })\n\n return matchingSymbols\n }\n\n private isChecked(ridx: number, sidx: number) {\n return !!this._checked.find((c) => c.reel === ridx && c.row === sidx)\n }\n\n private isCheckedWild(ridx: number, sidx: number) {\n return !!this._checkedWilds.find((c) => c.reel === ridx && c.row === sidx)\n }\n}\n\ninterface ClusterWinTypeOpts extends WinTypeOpts {}\n\nexport interface ClusterWinCombination extends WinCombination {}\n","import { GameSymbol } from \"../game-symbol\"\nimport { SymbolList, WinCombination, WinType, WinTypeOpts } from \".\"\nimport { Reels } from \"../types\"\n\nexport class ManywaysWinType extends WinType {\n declare protected winCombinations: ManywaysWinCombination[]\n declare getWins: () => {\n payout: number\n winCombinations: ManywaysWinCombination[]\n }\n\n constructor(opts: ManywaysWinTypeOpts) {\n super(opts)\n }\n\n private validateConfig() {}\n\n /**\n * Calculates wins based on the defined paylines and provided board state.\\\n * Retrieve the results using `getWins()` after.\n */\n evaluateWins(board: Reels) {\n this.validateConfig()\n\n const waysWins: ManywaysWinCombination[] = []\n\n const reels = board\n\n // A wild on the first reel can start a win for any symbol on second reel.\n // Store the possible wins in a map with the base symbol id as key.\n // The value is an array of \"reels\" containing all matching symbols for that base symbol.\n const possibleWaysWins = new Map<string, Record<string, SymbolList>>()\n\n // Identify all candidate symbols that can form a win starting from Reel 1.\n // We scan from left to right. If a reel contains a Wild, it can \"bridge\" to the next reel.\n // We collect all non-wild symbols we encounter as potential win candidates.\n // We also collect the Wild symbol itself as a candidate for pure wild wins.\n const candidateSymbols = new Map<string, GameSymbol>()\n let searchReelIdx = 0\n let searchActive = true\n\n while (searchActive && searchReelIdx < reels.length) {\n const reel = reels[searchReelIdx]!\n let hasWild = false\n\n for (const symbol of reel) {\n candidateSymbols.set(symbol.id, symbol)\n if (this.isWild(symbol)) {\n hasWild = true\n }\n }\n\n // If this reel has no wilds, we can't extend the search for *new* starting symbols\n // beyond this reel (because a win must be continuous from the start).\n if (!hasWild) {\n searchActive = false\n }\n searchReelIdx++\n }\n\n for (const baseSymbol of candidateSymbols.values()) {\n let symbolList: Record<string, SymbolList> = {}\n let isInterrupted = false\n\n for (const [ridx, reel] of reels.entries()) {\n if (isInterrupted) break\n\n for (const [sidx, symbol] of reel.entries()) {\n const isMatch = baseSymbol.compare(symbol) || this.isWild(symbol)\n\n if (isMatch) {\n if (!symbolList[ridx]) {\n symbolList[ridx] = []\n }\n symbolList[ridx].push({ reel: ridx, row: sidx, symbol })\n }\n }\n\n if (!symbolList[ridx]) {\n isInterrupted = true\n break\n }\n }\n\n const minSymLine = Math.min(\n ...Object.keys(baseSymbol!.pays || {}).map((k) => parseInt(k, 10)),\n )\n const wayLength = this.getWayLength(symbolList)\n\n if (wayLength >= minSymLine) {\n possibleWaysWins.set(baseSymbol.id, symbolList)\n }\n }\n\n for (const [baseSymbolId, symbolList] of possibleWaysWins.entries()) {\n const wayLength = this.getWayLength(symbolList)\n\n let baseSymbol = Object.values(symbolList)\n .flatMap((l) => l.map((s) => s))\n .find((s) => !this.isWild(s.symbol))?.symbol\n\n if (!baseSymbol) baseSymbol = symbolList[0]![0]!.symbol\n\n const singleWayPayout = this.getSymbolPayout(baseSymbol, wayLength)\n const totalWays = Object.values(symbolList).reduce(\n (ways, syms) => ways * syms.length,\n 1,\n )\n const totalPayout = singleWayPayout * totalWays\n\n waysWins.push({\n kind: wayLength,\n baseSymbol,\n symbols: Object.values(symbolList).flatMap((reel) =>\n reel.map((s) => ({\n symbol: s.symbol,\n isWild: this.isWild(s.symbol),\n reelIndex: s.reel,\n posIndex: s.row,\n })),\n ),\n ways: totalWays,\n payout: totalPayout,\n })\n }\n\n for (const win of waysWins) {\n this.ctx.services.data.recordSymbolOccurrence({\n kind: win.kind,\n symbolId: win.baseSymbol.id,\n spinType: this.ctx.state.currentSpinType,\n })\n }\n\n this.payout = waysWins.reduce((sum, l) => sum + l.payout, 0)\n this.winCombinations = waysWins\n\n return this\n }\n\n private getWayLength(symbolList: Record<string, SymbolList>) {\n return Math.max(...Object.keys(symbolList).map((k) => parseInt(k, 10))) + 1\n }\n}\n\ninterface ManywaysWinTypeOpts extends WinTypeOpts {}\n\nexport interface ManywaysWinCombination extends WinCombination {\n ways: number\n}\n","import fs from \"fs\"\nimport path from \"path\"\nimport { isMainThread } from \"worker_threads\"\nimport { ReelSet, ReelSetOptions } from \".\"\nimport { GameConfig } from \"../game-config\"\nimport { GameSymbol } from \"../game-symbol\"\n\n/**\n * This class is responsible for generating reel sets for slot games based on specified configurations.\n *\n * **While it offers a high degree of customization, some configurations may lead to unsolvable scenarios.**\n *\n * If the reel generator is unable to fulfill niche constraints,\\\n * you might need to adjust your configuration, or edit the generated reels manually.\\\n * Setting a different seed may also help.\n */\nexport class GeneratedReelSet extends ReelSet {\n protected readonly symbolWeights: Map<string, number> = new Map()\n protected readonly rowsAmount: number\n protected limitSymbolsToReels?: Record<string, number[]>\n protected readonly spaceBetweenSameSymbols?: number | Record<string, number>\n protected readonly spaceBetweenSymbols?: Record<string, Record<string, number>>\n protected readonly preferStackedSymbols?: number\n protected readonly symbolStacks?: Record<\n string,\n {\n chance: number | Record<string, number>\n min?: number | Record<string, number>\n max?: number | Record<string, number>\n }\n >\n protected readonly symbolQuotas?: Record<string, number | Record<string, number>>\n private overrideExisting: boolean\n\n constructor(opts: GeneratedReelSetOptions) {\n super(opts)\n this.id = opts.id\n this.symbolWeights = new Map(Object.entries(opts.symbolWeights))\n this.rowsAmount = opts.rowsAmount || 250\n\n if (opts.limitSymbolsToReels) this.limitSymbolsToReels = opts.limitSymbolsToReels\n\n this.overrideExisting = opts.overrideExisting || false\n this.spaceBetweenSameSymbols = opts.spaceBetweenSameSymbols\n this.spaceBetweenSymbols = opts.spaceBetweenSymbols\n this.preferStackedSymbols = opts.preferStackedSymbols\n this.symbolStacks = opts.symbolStacks\n this.symbolQuotas = opts.symbolQuotas\n\n if (\n (typeof this.spaceBetweenSameSymbols == \"number\" &&\n (this.spaceBetweenSameSymbols < 1 || this.spaceBetweenSameSymbols > 8)) ||\n (typeof this.spaceBetweenSameSymbols == \"object\" &&\n Object.values(this.spaceBetweenSameSymbols).some((v) => v < 1 || v > 8))\n ) {\n throw new Error(\n `spaceBetweenSameSymbols must be between 1 and 8, got ${this.spaceBetweenSameSymbols}.`,\n )\n }\n\n if (\n Object.values(this.spaceBetweenSymbols || {}).some((o) =>\n Object.values(o).some((v) => v < 1 || v > 8),\n )\n ) {\n throw new Error(\n `spaceBetweenSymbols must be between 1 and 8, got ${this.spaceBetweenSymbols}.`,\n )\n }\n\n if (\n this.preferStackedSymbols &&\n (this.preferStackedSymbols < 0 || this.preferStackedSymbols > 100)\n ) {\n throw new Error(\n `preferStackedSymbols must be between 0 and 100, got ${this.preferStackedSymbols}.`,\n )\n }\n }\n\n private validateConfig(config: GameConfig) {\n this.symbolWeights.forEach((_, symbol) => {\n if (!config.symbols.has(symbol)) {\n throw new Error(\n `Symbol \"${symbol}\" of the reel generator ${this.id} for mode ${this.associatedGameModeName} is not defined in the game config`,\n )\n }\n })\n\n if (this.limitSymbolsToReels && Object.keys(this.limitSymbolsToReels).length == 0) {\n this.limitSymbolsToReels = undefined\n }\n }\n\n private isSymbolAllowedOnReel(symbolId: string, reelIdx: number) {\n if (!this.limitSymbolsToReels) return true\n const allowedReels = this.limitSymbolsToReels[symbolId]\n if (!allowedReels || allowedReels.length === 0) return true\n return allowedReels.includes(reelIdx)\n }\n\n private resolveStacking(symbolId: string, reelIdx: number) {\n const cfg = this.symbolStacks?.[symbolId]\n if (!cfg) return null\n\n const STACKING_MIN = 1\n const STACKING_MAX = 4\n\n const chance =\n typeof cfg.chance === \"number\" ? cfg.chance : (cfg.chance?.[reelIdx] ?? 0)\n if (chance <= 0) return null\n\n let min = typeof cfg.min === \"number\" ? cfg.min : (cfg.min?.[reelIdx] ?? STACKING_MIN)\n let max = typeof cfg.max === \"number\" ? cfg.max : (cfg.max?.[reelIdx] ?? STACKING_MAX)\n\n return { chance, min, max }\n }\n\n private tryPlaceStack(\n reel: Array<GameSymbol | null>,\n config: GameConfig,\n reelIdx: number,\n symbolId: string,\n startIndex: number,\n maxStack: number,\n ) {\n if (!this.isSymbolAllowedOnReel(symbolId, reelIdx)) return 0\n\n let canPlace = 0\n for (let j = 0; j < maxStack; j++) {\n const idx = (startIndex + j) % this.rowsAmount\n if (reel[idx] !== null) break\n canPlace++\n }\n if (canPlace === 0) return 0\n\n const symObj = config.symbols.get(symbolId)\n if (!symObj) {\n throw new Error(\n `Symbol with id \"${symbolId}\" not found in the game config symbols map.`,\n )\n }\n\n for (let j = 0; j < canPlace; j++) {\n const idx = (startIndex + j) % reel.length\n reel[idx] = symObj\n }\n return canPlace\n }\n\n /**\n * Checks if a symbol can be placed at the target index without violating spacing rules.\n */\n private violatesSpacing(\n reel: Array<GameSymbol | null>,\n symbolId: string,\n targetIndex: number,\n ) {\n const circDist = (a: number, b: number) => {\n const diff = Math.abs(a - b)\n return Math.min(diff, this.rowsAmount - diff)\n }\n\n const spacingType = this.spaceBetweenSameSymbols ?? undefined\n const sameSpacing =\n typeof spacingType === \"number\" ? spacingType : (spacingType?.[symbolId] ?? 0)\n\n for (let i = 0; i <= reel.length; i++) {\n const placed = reel[i]\n if (!placed) continue\n\n const dist = circDist(targetIndex, i)\n\n // Same symbol spacing\n if (sameSpacing >= 1 && placed.id === symbolId) {\n if (dist <= sameSpacing) return true\n }\n\n // Cross-symbol spacing\n if (this.spaceBetweenSymbols) {\n const forward = this.spaceBetweenSymbols[symbolId]?.[placed.id] ?? 0\n if (forward >= 1 && dist <= forward) return true\n\n const reverse = this.spaceBetweenSymbols[placed.id]?.[symbolId] ?? 0\n if (reverse >= 1 && dist <= reverse) return true\n }\n }\n\n return false\n }\n\n generateReels(config: GameConfig) {\n this.validateConfig(config)\n\n const gameMode = config.gameModes[this.associatedGameModeName]\n\n if (!gameMode) {\n throw new Error(\n `Error generating reels for game mode \"${this.associatedGameModeName}\". It's not defined in the game config.`,\n )\n }\n\n const filePath = path.join(\n config.rootDir,\n config.outputDir,\n `reels_${this.associatedGameModeName}-${this.id}.csv`,\n )\n\n const exists = fs.existsSync(filePath)\n\n if (exists && !this.overrideExisting) {\n this.reels = this.parseReelsetCSV(filePath, config)\n return this\n }\n\n if (!exists && this.symbolWeights.size === 0) {\n throw new Error(\n `Cannot generate reels for generator \"${this.id}\" of mode \"${this.associatedGameModeName}\" because the symbol weights are empty.`,\n )\n }\n\n const reelsAmount = gameMode.reelsAmount\n const weightsObj = Object.fromEntries(this.symbolWeights)\n\n // Generate initial reels with random symbols\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n const reel: Array<GameSymbol | null> = new Array(this.rowsAmount).fill(null)\n\n const reelQuotas: Record<string, number> = {}\n const quotaCounts: Record<string, number> = {}\n let totalReelsQuota = 0\n\n // Get quotas for this reel, across all symbols\n for (const [sym, quotaConf] of Object.entries(this.symbolQuotas || {})) {\n const q = typeof quotaConf === \"number\" ? quotaConf : quotaConf[ridx]\n if (!q) continue\n reelQuotas[sym] = q\n totalReelsQuota += q\n }\n\n if (totalReelsQuota > 100) {\n throw new Error(\n `Total symbol quotas for reel ${ridx} exceed 100%. Adjust your configuration on reel set \"${this.id}\".`,\n )\n }\n\n if (totalReelsQuota > 0) {\n for (const [sym, quota] of Object.entries(reelQuotas)) {\n const quotaCount = Math.max(1, Math.floor((this.rowsAmount * quota) / 100))\n quotaCounts[sym] = quotaCount\n }\n }\n\n // Place required quotas first (use stacking over spacing, if configured)\n for (const [sym, targetCount] of Object.entries(quotaCounts)) {\n let remaining = targetCount\n let attempts = 0\n\n while (remaining > 0) {\n if (attempts++ > this.rowsAmount * 10) {\n throw new Error(\n `Failed to place ${targetCount} of symbol ${sym} on reel ${ridx} (likely spacing/stacking too strict).`,\n )\n }\n\n const pos = Math.round(this.rng.randomFloat(0, this.rowsAmount - 1))\n const stackCfg = this.resolveStacking(sym, ridx)\n let placed = 0\n\n // Try to place a symbol stack first, if configured\n if (stackCfg && Math.round(this.rng.randomFloat(1, 100)) <= stackCfg.chance) {\n const stackSize = Math.max(\n 0,\n Math.round(this.rng.randomFloat(stackCfg.min, stackCfg.max)),\n )\n const toPlace = Math.min(stackSize, remaining)\n placed = this.tryPlaceStack(reel, config, ridx, sym, pos, toPlace)\n }\n\n // Not enough space, fall back to placing single symbols\n if (\n placed === 0 &&\n reel[pos] === null &&\n this.isSymbolAllowedOnReel(sym, ridx) &&\n !this.violatesSpacing(reel, sym, pos)\n ) {\n reel[pos] = config.symbols.get(sym)!\n placed = 1\n }\n\n remaining -= placed\n }\n }\n\n // Fill the rest of the reel randomly\n for (let r = 0; r < this.rowsAmount; r++) {\n if (reel[r] !== null) continue // already placed quota\n\n let chosenSymbolId = this.rng.weightedRandom(weightsObj)\n\n // If symbolStacks is NOT configured for the next choice, allow \"preferStackedSymbols\" fallback\n const nextHasStackCfg = !!this.resolveStacking(chosenSymbolId, ridx)\n if (!nextHasStackCfg && this.preferStackedSymbols && reel.length > 0) {\n const prevSymbol = r - 1 >= 0 ? reel[r - 1] : reel[reel.length - 1]\n if (\n prevSymbol &&\n Math.round(this.rng.randomFloat(1, 100)) <= this.preferStackedSymbols &&\n (!this.spaceBetweenSameSymbols ||\n !this.violatesSpacing(reel, prevSymbol.id, r))\n ) {\n chosenSymbolId = prevSymbol.id\n }\n }\n\n // Check for stacking preference\n if (this.preferStackedSymbols && reel.length > 0) {\n const prevSymbol = r - 1 >= 0 ? reel[r - 1] : reel[reel.length - 1]\n\n if (\n Math.round(this.rng.randomFloat(1, 100)) <= this.preferStackedSymbols &&\n (!this.spaceBetweenSameSymbols ||\n !this.violatesSpacing(reel, prevSymbol!.id, r))\n ) {\n chosenSymbolId = prevSymbol!.id\n }\n }\n\n // If symbol has stack config, try to place a stack (ignore spacing)\n const stackCfg = this.resolveStacking(chosenSymbolId, ridx)\n if (stackCfg && this.isSymbolAllowedOnReel(chosenSymbolId, ridx)) {\n const roll = Math.round(this.rng.randomFloat(1, 100))\n if (roll <= stackCfg.chance) {\n const desiredSize = Math.max(\n 1,\n Math.round(this.rng.randomFloat(stackCfg.min, stackCfg.max)),\n )\n const placed = this.tryPlaceStack(\n reel,\n config,\n ridx,\n chosenSymbolId,\n r,\n desiredSize,\n )\n if (placed > 0) {\n // advance loop to skip the cells we just filled on this side of the boundary\n // (wrapped cells at the start are already filled and will be skipped when encountered)\n r += placed - 1\n continue\n }\n }\n }\n\n let tries = 0\n const maxTries = 2500\n\n while (\n !this.isSymbolAllowedOnReel(chosenSymbolId, ridx) ||\n this.violatesSpacing(reel, chosenSymbolId, r)\n ) {\n if (++tries > maxTries) {\n throw new Error(\n [\n `Failed to place a symbol on reel ${ridx} at position ${r} after ${maxTries} attempts.\\n`,\n \"Try to change the seed or adjust your configuration.\\n\",\n ].join(\" \"),\n )\n }\n chosenSymbolId = this.rng.weightedRandom(weightsObj)\n\n const hasStackCfg = !!this.resolveStacking(chosenSymbolId, ridx)\n if (!hasStackCfg && this.preferStackedSymbols && reel.length > 0) {\n const prevSymbol = r - 1 >= 0 ? reel[r - 1] : reel[reel.length - 1]\n if (\n prevSymbol &&\n Math.round(this.rng.randomFloat(1, 100)) <= this.preferStackedSymbols &&\n (!this.spaceBetweenSameSymbols ||\n !this.violatesSpacing(reel, prevSymbol.id, r))\n ) {\n chosenSymbolId = prevSymbol.id\n }\n }\n }\n\n const symbol = config.symbols.get(chosenSymbolId)\n\n if (!symbol) {\n throw new Error(\n `Symbol with id \"${chosenSymbolId}\" not found in the game config symbols map.`,\n )\n }\n\n reel[r] = symbol\n }\n\n if (reel.some((s) => s === null)) {\n throw new Error(`Reel ${ridx} has unfilled positions after generation.`)\n }\n\n this.reels.push(reel as GameSymbol[])\n }\n\n // Write the CSV\n const csvRows: string[][] = Array.from({ length: this.rowsAmount }, () =>\n Array.from({ length: reelsAmount }, () => \"\"),\n )\n\n for (let ridx = 0; ridx < reelsAmount; ridx++) {\n for (let r = 0; r < this.rowsAmount; r++) {\n csvRows[r]![ridx] = this.reels[ridx]![r]!.id\n }\n }\n\n const csvString = csvRows.map((row) => row.join(\",\")).join(\"\\n\")\n\n if (isMainThread) {\n fs.writeFileSync(filePath, csvString)\n\n this.reels = this.parseReelsetCSV(filePath, config)\n console.log(\n `Generated reelset ${this.id} for game mode ${this.associatedGameModeName}`,\n )\n }\n\n return this\n }\n}\n\ninterface GeneratedReelSetOptions extends ReelSetOptions {\n /**\n * The weights of the symbols in the reelset.\\\n * This is a mapping of symbol IDs to their respective weights.\n */\n symbolWeights: Record<string, number>\n /**\n * The number of rows in the reelset.\\\n * Default is 250, but can be adjusted as needed.\n */\n rowsAmount?: number\n /**\n * Prevent the same symbol from appearing directly above or below itself.\\\n * This can be a single number for all symbols, or a mapping of symbol IDs to\n * their respective spacing values.\n *\n * Must be 1 or higher, if set.\n *\n * **This is overridden by `symbolStacks`**\n */\n spaceBetweenSameSymbols?: number | Record<string, number>\n /**\n * Prevents specific symbols from appearing within a certain distance of each other.\n *\n * Useful for preventing scatter and super scatter symbols from appearing too close to each other.\n *\n * **This is overridden by `symbolStacks`**\n */\n spaceBetweenSymbols?: Record<string, Record<string, number>>\n /**\n * A percentage value 0-100 that indicates the likelihood of a symbol being stacked.\\\n * A value of 0 means no stacked symbols, while 100 means all symbols are stacked.\n *\n * This is only a preference. Symbols may still not be stacked if\\\n * other restrictions (like `spaceBetweenSameSymbols`) prevent it.\n *\n * **This is overridden by `symbolStacks`**\n */\n preferStackedSymbols?: number\n /**\n * A mapping of symbols to their respective advanced stacking configuration.\n *\n * @example\n * ```ts\n * symbolStacks: {\n * \"W\": {\n * chance: { \"1\": 20, \"2\": 20, \"3\": 20, \"4\": 20 }, // 20% chance to be stacked on reels 2-5\n * min: 2, // At least 2 wilds in a stack\n * max: 4, // At most 4 wilds in a stack\n * }\n * }\n * ```\n */\n symbolStacks?: Record<\n string,\n {\n chance: number | Record<string, number>\n min?: number | Record<string, number>\n max?: number | Record<string, number>\n }\n >\n /**\n * Configures symbols to be limited to specific reels.\\\n * For example, you could configure Scatters to appear only on reels 1, 3 and 5.\n *\n * @example\n * ```ts\n * limitSymbolsToReels: {\n * \"S\": [0, 2, 4], // Remember that reels are 0-indexed.\n * }\n * ```\n */\n limitSymbolsToReels?: Record<string, number[]>\n /**\n * Defines optional quotas for symbols on the reels.\\\n * The quota (1-100%) defines how often a symbol should appear in the reelset, or in a specific reel.\n *\n * This is particularly useful for controlling the frequency of special symbols like scatters or wilds.\n *\n * Reels not provided for a symbol will use the weights from `symbolWeights`.\n *\n * _Any_ small quota will ensure that the symbol appears at least once on the reel.\n *\n * @example\n * ```ts\n * symbolQuotas: {\n * \"S\": 3, // 3% of symbols on each reel will be scatters\n * \"W\": { \"1\": 10, \"2\": 5, \"3\": 3, \"4\": 1 }, // Wilds will appear with different quotas on selected reels\n * }\n * ```\n */\n symbolQuotas?: Record<string, number | Record<string, number>>\n /**\n * If true, existing reels CSV files will be overwritten.\n */\n overrideExisting?: boolean\n /**\n * Optional seed for the RNG to ensure reproducible results.\n *\n * Default seed is `0`.\n *\n * Note: Seeds 0 and 1 produce the same results.\n */\n seed?: number\n}\n\nexport type Reels = GameSymbol[][]\n","import fs from \"fs\"\nimport path from \"path\"\nimport { GameConfig } from \"../game-config\"\nimport { RandomNumberGenerator } from \"../service/rng\"\nimport { Reels } from \"../types\"\n\nexport class ReelSet {\n id: string\n associatedGameModeName: string\n reels: Reels\n protected rng: RandomNumberGenerator\n\n constructor(opts: ReelSetOptions) {\n this.id = opts.id\n this.associatedGameModeName = \"\"\n this.reels = []\n this.rng = new RandomNumberGenerator()\n this.rng.setSeed(opts.seed ?? 0)\n }\n\n generateReels(config: GameConfig): ReelSet {\n throw new Error(\"Not implemented\")\n }\n\n /**\n * Reads a reelset CSV file and returns the reels as arrays of GameSymbols.\n */\n parseReelsetCSV(reelSetPath: string, config: GameConfig) {\n if (!fs.existsSync(reelSetPath)) {\n throw new Error(`Reelset CSV file not found at path: ${reelSetPath}`)\n }\n\n const allowedExtensions = [\".csv\"]\n const ext = path.extname(reelSetPath).toLowerCase()\n if (!allowedExtensions.includes(ext)) {\n throw new Error(\n `Invalid file extension for reelset CSV: ${ext}. Allowed extensions are: ${allowedExtensions.join(\n \", \",\n )}`,\n )\n }\n\n const csvData = fs.readFileSync(reelSetPath, \"utf8\")\n const rows = csvData.split(\"\\n\").filter((line) => line.trim() !== \"\")\n const reels: Reels = Array.from(\n { length: config.gameModes[this.associatedGameModeName]!.reelsAmount },\n () => [],\n )\n\n rows.forEach((row) => {\n const symsInRow = row.split(\",\").map((symbolId) => {\n const symbol = config.symbols.get(symbolId.trim())\n if (!symbol) {\n throw new Error(`Symbol with id \"${symbolId}\" not found in game config.`)\n }\n return symbol\n })\n symsInRow.forEach((symbol, ridx) => {\n if (ridx >= reels.length) {\n throw new Error(\n `Row in reelset CSV has more symbols than expected reels amount (${reels.length})`,\n )\n }\n reels[ridx]!.push(symbol)\n })\n })\n\n const reelLengths = reels.map((r) => r.length)\n const uniqueLengths = new Set(reelLengths)\n if (uniqueLengths.size > 1) {\n throw new Error(\n `Inconsistent reel lengths in reelset CSV at ${reelSetPath}: ${[\n ...uniqueLengths,\n ].join(\", \")}`,\n )\n }\n\n return reels\n }\n}\n\nexport interface ReelSetOptions {\n /**\n * The unique identifier of the reel generator.\\\n * Must be unique per game mode.\n */\n id: string\n /**\n * Optional seed for the RNG to ensure reproducible results.\n *\n * Default seed is `0`.\n *\n * Note: Seeds 0 and 1 produce the same results.\n */\n seed?: number\n}\n","import assert from \"assert\"\nimport { ReelSet, ReelSetOptions } from \".\"\nimport { GameConfig } from \"../game-config\"\nimport { Reels } from \"../types\"\n\n/**\n * This class is responsible for providing reel sets for slot games based on a static configuration or file.\n */\nexport class StaticReelSet extends ReelSet {\n reels: Reels\n csvPath: string\n private _strReels: string[][]\n\n constructor(opts: StaticReelSetOptions) {\n super(opts)\n\n this.reels = []\n this._strReels = opts.reels || []\n this.csvPath = opts.csvPath || \"\"\n\n assert(\n opts.reels || opts.csvPath,\n `Either 'reels' or 'csvPath' must be provided for StaticReelSet ${this.id}`,\n )\n }\n\n private validateConfig(config: GameConfig) {\n this.reels.forEach((reel) => {\n reel.forEach((symbol) => {\n if (!config.symbols.has(symbol.id)) {\n throw new Error(\n `Symbol \"${symbol}\" of the reel set ${this.id} for mode ${this.associatedGameModeName} is not defined in the game config`,\n )\n }\n })\n })\n\n if (this.csvPath && this._strReels.length > 0) {\n throw new Error(\n `Both 'csvPath' and 'reels' are provided for StaticReelSet ${this.id}. Please provide only one.`,\n )\n }\n }\n\n generateReels(config: GameConfig) {\n this.validateConfig(config)\n\n if (this._strReels.length > 0) {\n this.reels = this._strReels.map((reel) => {\n return reel.map((symbolId) => {\n const symbol = config.symbols.get(symbolId)\n if (!symbol) {\n throw new Error(\n `Symbol \"${symbolId}\" of the reel set ${this.id} for mode ${this.associatedGameModeName} is not defined in the game config`,\n )\n }\n return symbol\n })\n })\n }\n\n if (this.csvPath) {\n this.reels = this.parseReelsetCSV(this.csvPath, config)\n }\n\n return this\n }\n}\n\ninterface StaticReelSetOptions extends ReelSetOptions {\n reels?: string[][]\n csvPath?: string\n}\n","import { GameContext } from \"../game-context\"\nimport { GameSymbol } from \"../game-symbol\"\nimport { Reels } from \"../types\"\nimport { Board } from \".\"\nimport { WinCombination } from \"../win-types\"\n\nexport class StandaloneBoard {\n private board: Board\n private ctx: GameContext\n private reelsAmount: number\n private symbolsPerReel: number[]\n private padSymbols: number\n\n constructor(opts: StandaloneBoardOptions) {\n this.board = new Board()\n this.ctx = opts.ctx\n this.reelsAmount = opts.reelsAmount\n this.symbolsPerReel = opts.symbolsPerReel\n this.padSymbols = opts.padSymbols\n }\n\n /**\n * Resets the board to an empty state.\\\n * This is called before drawing a new board.\n */\n resetBoard() {\n this.resetReels()\n this.board.lastDrawnReelStops = []\n }\n\n /**\n * Gets the current reels and symbols on the board.\n */\n getBoardReels() {\n return this.board.reels\n }\n\n getPaddingTop() {\n return this.board.paddingTop\n }\n\n getPaddingBottom() {\n return this.board.paddingBottom\n }\n\n /**\n * Gets the symbol at the specified reel and row index.\n */\n getSymbol(reelIndex: number, rowIndex: number) {\n return this.board.getSymbol(reelIndex, rowIndex)\n }\n\n /**\n * Sets the symbol at the specified reel and row index.\n */\n setSymbol(reelIndex: number, rowIndex: number, symbol: GameSymbol) {\n this.board.setSymbol(reelIndex, rowIndex, symbol)\n }\n\n private resetReels() {\n this.board.resetReels({\n ctx: this.ctx,\n })\n }\n\n /**\n * Sets the anticipation value for a specific reel.\n */\n setAnticipationForReel(reelIndex: number, value: boolean) {\n this.board.anticipation[reelIndex] = value\n }\n\n /**\n * Counts how many symbols matching the criteria are on a specific reel.\n */\n countSymbolsOnReel(\n symbolOrProperties: GameSymbol | Record<string, any>,\n reelIndex: number,\n ) {\n return this.board.countSymbolsOnReel(symbolOrProperties, reelIndex)\n }\n\n /**\n * Counts how many symbols matching the criteria are on the board.\n *\n * Passing a GameSymbol will compare by ID, passing a properties object will compare by properties.\n *\n * Returns a tuple where the first element is the total count, and the second element is a record of counts per reel index.\n */\n countSymbolsOnBoard(\n symbolOrProperties: GameSymbol | Record<string, any>,\n ): [number, Record<number, number>] {\n return this.board.countSymbolsOnBoard(symbolOrProperties)\n }\n\n /**\n * Checks if a symbol appears more than once on any reel in the current reel set.\n *\n * Useful to check for \"forbidden\" generations, e.g. 2 scatters on one reel.\n */\n isSymbolOnAnyReelMultipleTimes(symbol: GameSymbol) {\n return this.board.isSymbolOnAnyReelMultipleTimes(symbol)\n }\n\n /**\n * Gets all reel stops (positions) where the specified symbol appears in the current reel set.\\\n * Returns an array of arrays, where each inner array contains the positions for the corresponding reel.\n */\n getReelStopsForSymbol(reels: Reels, symbol: GameSymbol) {\n return this.board.getReelStopsForSymbol(reels, symbol)\n }\n\n /**\n * Combines multiple arrays of reel stops into a single array of reel stops.\\\n */\n combineReelStops(...reelStops: number[][][]) {\n return this.board.combineReelStops({\n ctx: this.ctx,\n reelStops,\n })\n }\n\n /**\n * From a list of reel stops on reels, selects a random stop for a speficied number of random symbols.\n *\n * Mostly useful for placing scatter symbols on the board.\n */\n getRandomReelStops(reels: Reels, reelStops: number[][], amount: number) {\n return this.board.getRandomReelStops({\n ctx: this.ctx,\n reels,\n reelsAmount: this.reelsAmount,\n reelStops,\n amount,\n })\n }\n\n /**\n * Selects a random reel set based on the configured weights of the current result set.\\\n * Returns the reels as arrays of GameSymbols.\n */\n getRandomReelset() {\n return this.board.getRandomReelset(this.ctx)\n }\n\n /**\n * Draws a board using specified reel stops.\n */\n drawBoardWithForcedStops(opts: {\n reels: Reels\n forcedStops: Record<string, number>\n randomOffset?: boolean\n }) {\n this.drawBoardMixed(opts.reels, opts.forcedStops, opts.randomOffset)\n }\n\n /**\n * Draws a board using random reel stops.\n */\n drawBoardWithRandomStops(reels: Reels) {\n this.drawBoardMixed(reels)\n }\n\n private drawBoardMixed(\n reels: Reels,\n forcedStops?: Record<string, number>,\n forcedStopsOffset?: boolean,\n ) {\n this.board.drawBoardMixed({\n ctx: this.ctx,\n reels,\n forcedStops,\n forcedStopsOffset,\n reelsAmount: this.reelsAmount,\n symbolsPerReel: this.symbolsPerReel,\n padSymbols: this.padSymbols,\n })\n }\n\n /**\n * Tumbles the board. All given symbols will be deleted and new symbols will fall from the top.\n */\n tumbleBoard(symbolsToDelete: Array<{ reelIdx: number; rowIdx: number }>) {\n return this.board.tumbleBoard({\n ctx: this.ctx,\n symbolsToDelete,\n reelsAmount: this.reelsAmount,\n symbolsPerReel: this.symbolsPerReel,\n padSymbols: this.padSymbols,\n })\n }\n\n /**\n * Dedupes win symbols for tumble.\\\n * Returns a list of symbols to remove from the board based on the given win combinations.\n * \n * Since it may be possible that multiple win combinations include the same symbol (e.g. Wilds),\\\n * this method ensures that each symbol is only listed once for removal. Otherwise tumbling may break.\n */\n dedupeWinSymbolsForTumble(winCombinations: WinCombination[]) {\n return this.board.dedupeWinSymbolsForTumble(winCombinations)\n }\n}\n\ninterface StandaloneBoardOptions {\n ctx: GameContext\n reelsAmount: number\n symbolsPerReel: number[]\n padSymbols: number\n}\n"],"mappings":"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","names":["assert","import_fs","import_assert","import_readline","import_assert","fs","fs","readline","assert","import_assert","assert","import_assert","assert","import_assert","assert","process","assert","path","fs","readline","zlib","import_fs","import_path","import_assert","import_worker_threads","path","assert","fs","import_path","import_assert","import_worker_threads","import_path","path","import_path","path","import_assert","assert","assert","path","import_worker_threads","import_assert","assert","import_assert","assert","neighbors","import_fs","import_path","import_worker_threads","import_fs","import_path","fs","path","path","fs","import_assert","assert"]}