import type { GameDefinition, GameSymbol, SymbolStyle, SymbolTier } from "./types"; type Optional = "payScale" | "tags" | "rules" | "featureNames" | "id" | "minBet" | "maxBet" | "betDivisor"; /** `defineGame` fills defaults and derives feature names from the configured mechanics. */ export function defineGame(input: Omit & Partial>): GameDefinition { const def: GameDefinition = { id: input.id ?? input.slug, minBet: input.minBet ?? 10, maxBet: input.maxBet ?? 1000, payScale: input.payScale ?? 1, betDivisor: input.betDivisor ?? (input.payModel.type === "lines" ? input.payModel.lines.length : input.grid.reels >= 6 ? 50 : 25), tags: input.tags ?? [], rules: input.rules ?? [], featureNames: input.featureNames ?? [], ...input, } as GameDefinition; if (def.featureNames.length === 0) def.featureNames = deriveFeatureNames(def); return def; } export function deriveFeatureNames(def: GameDefinition): string[] { const f: string[] = []; if (def.payModel.type === "ways") f.push(`${waysCount(def)} ways`); else f.push(`${def.payModel.lines.length} paylines`); if (def.wild?.expanding) f.push("Expanding wilds"); if (def.wild?.sticky) f.push("Sticky wilds"); if (def.wild?.stacked) f.push("Stacked wilds"); if (def.wild?.exploding) f.push("Exploding wilds"); if (def.wild?.moving) f.push("Moving wilds"); if (def.wild?.floating) f.push("Random wilds"); if (def.wild?.multiplier) f.push("Multiplier wilds"); if (def.cascades) f.push("Cascades"); if (def.scatter) f.push("Free spins"); if (def.spinCollect) f.push("Collect multiplier"); if (def.holdRespin) f.push("Hold & respin"); if (def.pickBonuses?.length) f.push("Bonus game"); if (def.meter) f.push("Charge meter"); if (def.heat) f.push("Heat meter"); if (def.mystery) f.push("Mystery symbols"); if (def.randomFeatures) f.push("Feature drops"); if (def.quantum) f.push("Quantum split"); if (def.dynamicGrid) f.push("Dynamic grid"); if (def.jackpot || def.holdRespin) f.push("Jackpots"); return f; } export function waysCount(def: GameDefinition): number { return Math.pow(def.grid.rows, def.grid.reels); } /** Helper for concise symbol declarations. */ export function sym( id: string, name: string, tier: SymbolTier, pays: Record | null, weight: number | number[], style: SymbolStyle, extra: Partial = {}, ): GameSymbol { return { id, name, kind: extra.kind ?? "regular", tier, pays: pays ?? undefined, weight, style, ...extra, }; } export const wildSym = (id: string, name: string, weight: number | number[], style: SymbolStyle, extra: Partial = {}): GameSymbol => sym(id, name, "special", null, weight, style, { kind: "wild", ...extra }); export const scatterSym = (id: string, name: string, weight: number | number[], style: SymbolStyle, scatterPays?: Record, extra: Partial = {}): GameSymbol => sym(id, name, "special", null, weight, style, { kind: "scatter", scatterPays, ...extra });