import type { GameState, Era, RackSkuId } from '@ai-tycoon/shared'; import { RACK_SKU_CONFIGS, DC_TIER_CONFIGS, COOLING_ORDER, PARAMETER_OPTIONS, DEFAULT_DATA_MIX, MAX_CONCURRENT_TRAINING, PRETRAINING_BASE_TICKS, CLUSTER_COST_CONFIG, LOCATION_CONFIGS, maxComputeRacks, VRAM_REQUIREMENTS_BY_GENERATION, } from '@ai-tycoon/shared'; import { canRaiseFunding, getNextFundingRound, getAvailableResearch, TECH_TREE, } from '@ai-tycoon/game-engine'; import * as actions from '../actions'; import type { Strategy, SimulationMetrics } from './types'; const ERA_ORDER: Era[] = ['startup', 'scaleup', 'bigtech', 'agi']; const TALENT_TARGETS: Record> = { startup: { research: 5, engineering: 5, operations: 2, sales: 3 }, scaleup: { research: 10, engineering: 10, operations: 4, sales: 8 }, bigtech: { research: 20, engineering: 20, operations: 8, sales: 12 }, agi: { research: 40, engineering: 40, operations: 16, sales: 20 }, }; const RESEARCH_PRIORITY: Record = { 'advanced-cooling': 200, 'dc-engineering-ii': 190, 'advanced-gpu-arch': 180, 'alignment-research': 250, 'transformer-v2': 165, 'quantization': 160, 'data-pipeline': 155, 'developer-relations': 150, 'enterprise-sales': 175, 'redundancy-protocols': 140, 'quality-assurance': 130, 'liquid-cooling-tech': 120, 'next-gen-gpu': 115, 'distributed-training': 110, 'inference-optimization': 105, 'dc-engineering-iii': 100, 'code-generation': 170, 'reasoning-enhancement': 90, 'amd-ecosystem': 85, 'infiniband-networking': 80, 'distillation': 75, 'inference-specialization': 70, 'sdk-platform': 65, 'request-batching': 60, 'request-routing': 55, 'code-assistant-product': 168, 'creative-systems': 45, 'multimodal-fusion': 40, 'network-engineering-i': 35, 'rapid-deployment': 30, 'priority-queues': 25, 'interpretability': 180, 'immersion-cooling-tech': 18, 'frontier-compute': 16, 'dc-engineering-iv': 14, 'network-engineering-ii': 12, 'agentic-architecture': 88, 'constitutional-ai': 160, 'network-redundancy': 6, 'auto-scaling': 5, 'agents-platform-product': 86, 'network-fast-repair': 3, 'rack-scale-compute': 2, 'custom-silicon': 1, 'network-hot-standby': 0, }; function cashSafe(state: GameState, cost: number, runway = 100): boolean { return state.economy.money - cost > state.economy.expensesPerTick * runway; } function getBestAffordableSku(state: GameState): RackSkuId | null { const era = state.meta.currentEra; const completed = state.research.completedResearch; const eligible = (Object.entries(RACK_SKU_CONFIGS) as [RackSkuId, typeof RACK_SKU_CONFIGS[RackSkuId]][]) .filter(([, sku]) => { if (ERA_ORDER.indexOf(era) < ERA_ORDER.indexOf(sku.era)) return false; if (sku.requiredResearch.length > 0 && !sku.requiredResearch.every(r => completed.includes(r))) return false; if (state.economy.money < sku.baseCost) return false; return true; }) .sort((a, b) => (b[1].trainingFlops / b[1].baseCost) - (a[1].trainingFlops / a[1].baseCost)); return eligible.length > 0 ? eligible[0][0] : null; } function getOperationalDCs(state: GameState) { const results: { dcId: string; coolingType: string; rackSkuId: string | null }[] = []; for (const cluster of state.infrastructure.clusters) { for (const campus of cluster.campuses) { for (const dc of campus.dataCenters) { if (dc.status === 'operational') { results.push({ dcId: dc.id, coolingType: dc.coolingType, rackSkuId: dc.rackSkuId }); } } } } return results; } function pickModelParams(state: GameState): number { const vram = state.infrastructure.totalVramGB; const era = state.meta.currentEra; const maxByEra: Record = { startup: 7, scaleup: 70, bigtech: 300, agi: 1400, }; const vramPerBillion = 2; const maxByVram = Math.floor(vram / vramPerBillion); const cap = Math.min(maxByEra[era], maxByVram); let best = PARAMETER_OPTIONS[0]; for (const p of PARAMETER_OPTIONS) { if (p <= cap) best = p; } return best; } export class GreedyStrategy implements Strategy { name = 'greedy'; decide(state: GameState, _metrics: SimulationMetrics[]): void { this.tryRaiseFunding(state); this.tryBuildInfrastructure(state); this.tryDeployRacks(state); this.tryDeployModels(state); this.tryOpenSourceModel(state); this.cancelStalledTraining(state); this.tryStartTraining(state); this.tryEnableRevenue(state); this.tryStartResearch(state); this.tryHireTalent(state); this.tryUpgradeInfra(state); this.tryExpandInfra(state); } private tryRaiseFunding(state: GameState): void { const { canRaise, nextRound } = canRaiseFunding(state); if (canRaise && nextRound) { actions.raiseFunding(state, nextRound); } } private tryBuildInfrastructure(state: GameState): void { if (state.infrastructure.clusters.length === 0) { actions.buildCluster(state, 'Primary', 'us-west'); } for (const cluster of state.infrastructure.clusters) { if (cluster.status !== 'operational') continue; if (cluster.campuses.length === 0) { actions.buildCampus(state, 'Campus-1', cluster.id, 'small'); } for (const campus of cluster.campuses) { if (campus.status !== 'operational') continue; if (campus.dataCenters.length === 0) { actions.buildDataCenter(state, 'DC-1', campus.id); } } } } private tryDeployRacks(state: GameState): void { const skuId = getBestAffordableSku(state); if (!skuId) return; const sku = RACK_SKU_CONFIGS[skuId]; const operationalDCs = getOperationalDCs(state); for (const { dcId, coolingType, rackSkuId } of operationalDCs) { if (rackSkuId !== null && rackSkuId !== skuId) continue; const coolingOk = COOLING_ORDER.indexOf(sku.requiredCooling) <= COOLING_ORDER.indexOf(coolingType as typeof sku.requiredCooling); if (!coolingOk) continue; if (!cashSafe(state, sku.baseCost, 50)) break; actions.fillDCToCapacity(state, dcId, skuId); } } private tryDeployModels(state: GameState): void { const undeployed = state.models.baseModels .filter(m => !m.isDeployed) .sort((a, b) => b.rawCapability - a.rawCapability); if (undeployed.length > 0) { actions.deployModel(state, undeployed[0].id); } } private tryOpenSourceModel(state: GameState): void { if (state.market.openSourcedModels.length > 0) return; const deployed = state.models.baseModels.filter(m => m.isDeployed); if (deployed.length > 0) { actions.openSourceModel(state, deployed[0].id); } } private cancelStalledTraining(state: GameState): void { const stalledPipelines = state.models.activeTrainingPipelines.filter( p => p.status === 'stalled', ); for (const pipeline of stalledPipelines) { const stalledTicks = state.meta.tickCount - pipeline.startedAtTick; if (stalledTicks < 500) continue; const gen = state.models.families.find(f => f.id === pipeline.familyId)?.generation ?? 1; const requiredVram = VRAM_REQUIREMENTS_BY_GENERATION[gen] ?? 0; if (requiredVram > 0 && state.compute.totalVramGB < requiredVram) { state.models.activeTrainingPipelines = state.models.activeTrainingPipelines.filter( p => p.id !== pipeline.id, ); } } } private tryStartTraining(state: GameState): void { const activeCount = state.models.activeTrainingPipelines.filter( p => p.status === 'active' || p.status === 'stalled', ).length; const maxSlots = MAX_CONCURRENT_TRAINING[state.meta.currentEra] ?? 1; if (activeCount >= maxSlots) return; if (state.infrastructure.totalVramGB <= 0) return; const gen = state.models.families.length + 1; const requiredVram = VRAM_REQUIREMENTS_BY_GENERATION[gen] ?? 0; if (requiredVram > 0 && state.compute.totalVramGB < requiredVram) return; const params = pickModelParams(state); const trainingFlops = state.infrastructure.totalTrainingFlops; const totalTicks = trainingFlops > 0 ? Math.max(30, Math.ceil(PRETRAINING_BASE_TICKS / (1 + trainingFlops * 0.1))) : PRETRAINING_BASE_TICKS; const targetTokens = params * 20e9; const hasCodeGen = state.research.completedResearch.includes('code-generation'); const sftSpecs: ('general' | 'code')[] = hasCodeGen ? ['general', 'code'] : ['general']; const hasAlignment = state.research.completedResearch.includes('alignment-research'); actions.startTrainingPipeline(state, { familyName: `SimCorp-${gen}`, architecture: { type: 'dense', totalParameters: params, activeParameters: params, contextWindow: 32, vocabularySize: 32000, }, dataMix: { ...DEFAULT_DATA_MIX }, allocatedComputeFraction: 1.0, targetTokens, totalTicks, sftSpecializations: sftSpecs, alignmentMethod: hasAlignment ? 'rlhf' : 'dpo', alignmentSafetyWeight: 0.75, }); } private tryEnableRevenue(state: GameState): void { if (state.models.bestDeployedModelScore <= 0) return; const ct = state.market.consumerTiers.tiers; if (!ct.free.config.isActive) actions.toggleConsumerTier(state, 'free'); if (!ct.plus.config.isActive) actions.toggleConsumerTier(state, 'plus'); if (!ct.pro.config.isActive && state.models.bestDeployedModelScore >= 20) { actions.toggleConsumerTier(state, 'pro'); } if (!ct.team.config.isActive && state.models.bestDeployedModelScore >= 30) { actions.toggleConsumerTier(state, 'team'); } const at = state.market.apiTiers.tiers; if (!at.free.config.isActive) actions.toggleApiTier(state, 'free'); if (!at.payg.config.isActive) actions.toggleApiTier(state, 'payg'); if (!at.scale.config.isActive && state.models.bestDeployedModelScore >= 25) { actions.toggleApiTier(state, 'scale'); } if (!at['enterprise-api'].config.isActive && state.models.bestDeployedModelScore >= 40) { actions.toggleApiTier(state, 'enterprise-api'); } if (state.research.completedResearch.includes('code-assistant-product') && !state.market.codeAssistant.isActive) { actions.toggleCodeAssistant(state); actions.setCodeAssistantPrice(state, 20); } if (state.research.completedResearch.includes('agents-platform-product') && !state.market.agentsPlatform.isActive) { actions.toggleAgentsPlatform(state); actions.setAgentsPlatformPrice(state, 50); } } private tryStartResearch(state: GameState): void { if (state.research.activeResearch) return; const available = getAvailableResearch(state); if (available.length === 0) return; const sorted = [...available].sort((a, b) => { const pa = RESEARCH_PRIORITY[a.id] ?? 0; const pb = RESEARCH_PRIORITY[b.id] ?? 0; return pb - pa; }); const best = sorted[0]; actions.startResearch(state, { researchId: best.id, progressTicks: 0, totalTicks: best.cost.ticks, allocatedResearchers: 0, allocatedCompute: 0, }); } private tryHireTalent(state: GameState): void { const targets = TALENT_TARGETS[state.meta.currentEra]; const depts = state.talent.departments; for (const [dept, target] of Object.entries(targets)) { const current = depts[dept as keyof typeof depts].headcount; if (current < target) { const needed = Math.min(target - current, 3); const cost = needed * 2000; if (cashSafe(state, cost, 200)) { actions.hireDepartment(state, dept as actions.DepartmentId, needed); } } } } private tryUpgradeInfra(state: GameState): void { for (const cluster of state.infrastructure.clusters) { for (const campus of cluster.campuses) { for (const dc of campus.dataCenters) { if (dc.status !== 'operational') continue; if (dc.coolingType === 'air' && state.research.completedResearch.includes('liquid-cooling-tech') && cashSafe(state, 500_000)) { actions.upgradeCoolingType(state, dc.id, 'liquid'); } if (dc.coolingType === 'liquid' && state.research.completedResearch.includes('immersion-cooling-tech') && cashSafe(state, 1_000_000)) { actions.upgradeCoolingType(state, dc.id, 'immersion'); } if (dc.networkFabric === 'ethernet-100g' && cashSafe(state, 200_000)) { actions.upgradeNetworkFabric(state, dc.id, 'ethernet-400g'); } if (dc.networkFabric === 'ethernet-400g' && state.research.completedResearch.includes('infiniband-networking') && cashSafe(state, 500_000)) { actions.upgradeNetworkFabric(state, dc.id, 'infiniband-ndr'); } } } } } private tryExpandInfra(state: GameState): void { const era = state.meta.currentEra; for (const cluster of state.infrastructure.clusters) { if (cluster.status !== 'operational') continue; for (const campus of cluster.campuses) { if (campus.status !== 'operational') continue; const allFull = campus.dataCenters.length > 0 && campus.dataCenters.every(dc => { if (dc.status !== 'operational') return true; const tierConfig = DC_TIER_CONFIGS[dc.tier]; const mc = maxComputeRacks(tierConfig.rackSlots, dc.tier); const existing = dc.computeRacksOnline + actions.pipelineCount(dc); return existing >= mc; }); if (allFull && campus.dataCenters.length > 0) { const tierConfig = DC_TIER_CONFIGS[campus.dcTier]; if (cashSafe(state, tierConfig.baseCost, 300)) { actions.addDCsToCampus(state, campus.id, 1); } } } } if (ERA_ORDER.indexOf(era) >= ERA_ORDER.indexOf('scaleup')) { const targetTier = state.research.completedResearch.includes('dc-engineering-iii') ? 'large' as const : state.research.completedResearch.includes('dc-engineering-ii') ? 'medium' as const : 'small' as const; for (const cluster of state.infrastructure.clusters) { if (cluster.status !== 'operational') continue; const hasHighTierCampus = cluster.campuses.some(c => c.dcTier === targetTier); if (!hasHighTierCampus && cashSafe(state, 2_000_000, 300)) { actions.buildCampus(state, `${targetTier}-Campus`, cluster.id, targetTier); } } } if (ERA_ORDER.indexOf(era) >= ERA_ORDER.indexOf('scaleup')) { const usedLocations = new Set(state.infrastructure.clusters.map(c => c.locationId)); const candidates: ('eu-north' | 'us-east')[] = ['eu-north', 'us-east']; for (const loc of candidates) { if (!usedLocations.has(loc)) { const locConfig = LOCATION_CONFIGS[loc]; if (ERA_ORDER.indexOf(era) >= ERA_ORDER.indexOf(locConfig.availableAt)) { if (cashSafe(state, CLUSTER_COST_CONFIG.baseCost, 500)) { actions.buildCluster(state, `Cluster-${loc}`, loc); break; } } } } } } }