import type { Card } from './cards'; import { ecazTreacheryDefinition } from './ecaz-cards'; export const REINFORCEMENTS_CARD = 'ecaz-reinforcements' as const; export class ReinforcementsError extends Error { constructor(message: string) { super(message); this.name = 'ReinforcementsError'; } } /** A matching id alone does not establish the physical variant card's identity. */ export function isReinforcements(card: Card | undefined): boolean { return !!card && card.id === REINFORCEMENTS_CARD && Object.keys(card).sort().join(',') === 'effect,id,kind,name' && ecazTreacheryDefinition(card)?.card.effect === 'reinforcements'; } /** Legal elite counts for the three-counter fee, in ordinary-first order. */ export function reinforcementsEliteOptions(normalReserves: number, eliteReserves: number): number[] { if (![normalReserves, eliteReserves].every(n => typeof n === 'number' && Number.isSafeInteger(n) && n >= 0)) throw new ReinforcementsError('Reinforcements needs valid nonnegative reserve counts.'); const minimum = Math.max(0, 3 - normalReserves); const maximum = Math.min(3, eliteReserves); return minimum > maximum ? [] : Array.from({ length: maximum - minimum + 1 }, (_, i) => minimum + i); } /** Spend exactly three own reserve counters; omitted choice retains the original ordinary-first rule. */ export function quoteReinforcements( normalReserves: number, eliteReserves: number, selectedElite?: number, ): { normal: number; elite: number; bonus: 2 } { const options = reinforcementsEliteOptions(normalReserves, eliteReserves); if (!options.length) throw new ReinforcementsError('Reinforcements needs at least three own forces in reserves.'); const elite = selectedElite === undefined ? options[0] : selectedElite; if (!Number.isSafeInteger(elite) || !options.includes(elite)) throw new ReinforcementsError('Choose an available mix of three ordinary and elite reserve forces.'); return { normal: 3 - elite, elite, bonus: 2 }; }