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>
70 lines
2.9 KiB
TypeScript
Executable File
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 };
|
|
}
|
|
}
|