dd-timer/src/domain/services/InventaireCalculator.ts
POL Mickaël 3e485fd09b chore: normalise fins de ligne CRLF → LF dans tout le repo
Applique .gitattributes sur tous les fichiers existants.
Élimine les différences fantômes entre WSL et Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 08:55:10 +02:00

70 lines
2.9 KiB
TypeScript
Executable File

import { BREEDING_RECIPES, RACE_GEN } from '@domain/value-objects/Race';
export interface Stock { m: number; f: number; n: number; }
export interface CrossingDetail { aSex: string; bSex: string; }
export interface CrossingResult { name: string; qty: number; parents: readonly [string, string]; gen: number; details: CrossingDetail[]; }
export interface GenerationResult { gen: number; crossings: CrossingResult[]; }
export interface InventaireResult { generations: GenerationResult[]; remaining: Record<string, Stock>; }
export class InventaireCalculator {
compute(inventaire: Readonly<Record<string, { m: number; f: number }>>): InventaireResult {
const avail: Record<string, Stock> = {};
for (const [k, v] of Object.entries(inventaire)) {
if ((v.m + v.f) > 0) avail[k] = { m: v.m, f: v.f, n: 0 };
}
if (Object.keys(avail).length === 0) return { generations: [], remaining: {} };
const hasMale = (s: Stock) => s.m > 0 || s.n > 0;
const hasFemale = (s: Stock) => s.f > 0 || s.n > 0;
const takeMale = (s: Stock) => { if (s.m > 0) s.m--; else s.n--; };
const takeFemale = (s: Stock) => { if (s.f > 0) s.f--; else s.n--; };
const totalOf = (s: Stock) => s.m + s.f + s.n;
const generations: GenerationResult[] = [];
for (let gen = 2; gen <= 10; gen++) {
const crossingsAtGen = Object.entries(BREEDING_RECIPES)
.filter(([name]) => (RACE_GEN[name] ?? 0) === gen)
.map(([name, parents]) => ({ name, parents }));
const genResults: CrossingResult[] = [];
let more = true;
while (more) {
more = false;
for (const cr of crossingsAtGen) {
const [a, b] = cr.parents;
const sa = avail[a] ?? { m: 0, f: 0, n: 0 };
const sb = avail[b] ?? { m: 0, f: 0, n: 0 };
let ok = false, aSex = '', bSex = '';
if (a === b) {
if (totalOf(sa) >= 2 && hasMale(sa) && hasFemale(sa)) {
takeMale(sa); takeFemale(sa); aSex = '\u2642'; bSex = '\u2640'; ok = true;
}
} else if (hasMale(sa) && hasFemale(sb)) {
takeMale(sa); takeFemale(sb); aSex = '\u2642'; bSex = '\u2640'; ok = true;
} else if (hasFemale(sa) && hasMale(sb)) {
takeFemale(sa); takeMale(sb); aSex = '\u2640'; bSex = '\u2642'; ok = true;
}
if (ok) {
if (!avail[a]) avail[a] = sa;
if (!avail[b]) avail[b] = sb;
if (!avail[cr.name]) avail[cr.name] = { m: 0, f: 0, n: 0 };
avail[cr.name]!.n++;
let entry = genResults.find(r => r.name === cr.name);
if (!entry) { entry = { name: cr.name, qty: 0, parents: cr.parents as [string, string], gen, details: [] }; genResults.push(entry); }
entry.qty++;
entry.details.push({ aSex, bSex });
more = true;
}
}
}
if (genResults.length > 0) generations.push({ gen, crossings: genResults });
}
return { generations, remaining: avail };
}
}