weed-sim / src /shared /deck.ts
Yufok1
Checkpoint Devvit v0.0.24
1a343cd
Raw
History Blame Contribute Delete
3.76 kB
import { germplasmRecords } from './lab';
import type { DeckSummary, GermplasmRecord, Rgb, SeedProfile } from './types';
function mean(values: unknown[]): number {
const numeric = values.filter((value): value is number => typeof value === 'number' && Number.isFinite(value));
return numeric.length ? Math.round((numeric.reduce((sum, value) => sum + value, 0) / numeric.length) * 10) / 10 : 0;
}
function increment(target: Record<string, number>, key: string | number): void {
const normalized = String(key);
target[normalized] = (target[normalized] ?? 0) + 1;
}
export function deckSummary(seeds: SeedProfile[]): DeckSummary {
const records = germplasmRecords(seeds);
const byStage: Record<string, number> = {};
const byType: Record<string, number> = {};
const byGenerationRaw: Record<string, number> = {};
const byBudFamily: Record<string, number> = {};
for (const record of records) {
increment(byStage, record.stage);
increment(byType, record.germplasmType);
increment(byGenerationRaw, record.generation);
increment(byBudFamily, record.budFamily);
}
const breedable = records.filter((record) => record.canAttempt && record.stage === 'MATURE');
const growing = records.filter((record) => record.stage !== 'MATURE');
const crosses = records.filter((record) => record.parents.every(Boolean));
const recentCrosses = crosses
.slice(-6)
.reverse()
.map((record) => ({
id: record.germplasmDbId,
name: record.germplasmName,
generation: record.generation,
pedigree: record.pedigree,
budColor: record.budColor as Rgb,
leafColor: record.leafColor as Rgb,
THC: record.THC,
}));
const generationEntries = Object.entries(byGenerationRaw)
.map(([generation, count]) => [`F${generation}`, count] as const)
.sort(([left], [right]) => left.localeCompare(right));
const topBloom = [...records]
.sort((left, right) => right.Bloom - left.Bloom)
.slice(0, 6)
.map((record) => ({
id: record.germplasmDbId,
name: record.germplasmName,
generation: record.generation,
pedigree: record.pedigree,
budColor: record.budColor,
leafColor: record.leafColor,
budPalette: record.budPalette,
Bloom: record.Bloom,
Volatility: record.Volatility,
budPattern: record.budPattern,
colorCount: record.budBloom.colorCount,
hueSpread: record.budBloom.hueSpread,
THC: record.THC,
}));
return {
accessions: records.length,
byStage,
byType: sortRecord(byType),
byGeneration: Object.fromEntries(generationEntries),
byBudFamily: sortRecord(byBudFamily),
maxGeneration: maxGeneration(records),
breedableCount: breedable.length,
growingCount: growing.length,
traitAverages: {
THC: mean(records.map((record) => record.THC)),
CBD: mean(records.map((record) => record.CBD)),
Yield: mean(records.map((record) => record.Yield)),
GrowTime: mean(records.map((record) => record.GrowTime)),
Stability: mean(records.map((record) => record.Stability)),
Volatility: mean(records.map((record) => record.Volatility)),
Bloom: mean(records.map((record) => record.Bloom)),
},
gamut: records.map((record) => record.budColor),
recentCrosses,
topBloom,
breedable: breedable.map((record) => ({
id: record.germplasmDbId,
name: record.germplasmName,
})),
};
}
function sortRecord(record: Record<string, number>): Record<string, number> {
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
}
function maxGeneration(records: GermplasmRecord[]): number {
return records.length ? Math.max(...records.map((record) => record.generation)) : 0;
}