import { TECH_TREE } from '../data/techTree'; export interface ResearchBonuses { energyCostReduction: number; pipelineSpeedBonus: number; trainingSpeedBonus: number; inferenceEfficiencyBonus: number; tokensPerFlopBonus: number; dataQualityBonus: number; sdkCoverageBonus: number; globalCapabilityBonus: number; reasoningBonus: number; codingBonus: number; creativeBonus: number; multimodalBonus: number; agentsBonus: number; reputationBonus: number; safetyBonus: number; autoScalingBonus: number; } const techTreeById = new Map(TECH_TREE.map(n => [n.id, n])); let _cachedBonuses: ResearchBonuses | null = null; let _cachedResearchCount = -1; export function getResearchBonuses(completedResearch: string[]): ResearchBonuses { if (_cachedBonuses && completedResearch.length === _cachedResearchCount) { return _cachedBonuses; } const bonuses: ResearchBonuses = { energyCostReduction: 0, pipelineSpeedBonus: 0, trainingSpeedBonus: 0, inferenceEfficiencyBonus: 0, tokensPerFlopBonus: 0, dataQualityBonus: 0, sdkCoverageBonus: 0, globalCapabilityBonus: 0, reasoningBonus: 0, codingBonus: 0, creativeBonus: 0, multimodalBonus: 0, agentsBonus: 0, reputationBonus: 0, safetyBonus: 0, autoScalingBonus: 0, }; for (const id of completedResearch) { const node = techTreeById.get(id); if (!node) continue; for (const effect of node.effects) { switch (effect.type) { case 'efficiency_boost': switch (effect.target) { case 'training_speed': bonuses.trainingSpeedBonus += effect.value; break; case 'inference': bonuses.inferenceEfficiencyBonus += effect.value; break; case 'tokens_per_flop': bonuses.tokensPerFlopBonus += effect.value; break; case 'pipeline_speed': bonuses.pipelineSpeedBonus += effect.value; break; case 'data_quality': bonuses.dataQualityBonus += effect.value; break; case 'sdk_coverage': bonuses.sdkCoverageBonus += effect.value; break; case 'auto_scaling': bonuses.autoScalingBonus += effect.value; break; } break; case 'capability_boost': switch (effect.target) { case 'all': bonuses.globalCapabilityBonus += effect.value; break; case 'reasoning': bonuses.reasoningBonus += effect.value; break; case 'coding': bonuses.codingBonus += effect.value; break; case 'creative': bonuses.creativeBonus += effect.value; break; case 'multimodal': bonuses.multimodalBonus += effect.value; break; case 'agents': bonuses.agentsBonus += effect.value; break; case 'reputation': bonuses.reputationBonus += effect.value; break; } break; case 'cost_reduction': if (effect.target === 'energy') bonuses.energyCostReduction += effect.value; break; case 'safety_boost': bonuses.safetyBonus += effect.value; break; } } } _cachedBonuses = bonuses; _cachedResearchCount = completedResearch.length; return bonuses; } export function resetResearchBonusCache(): void { _cachedBonuses = null; _cachedResearchCount = -1; }