import type { GameState, MarketState, ComputeState } from '@ai-tycoon/shared'; import { CONSUMER_BASE_GROWTH, CONSUMER_QUALITY_GROWTH_MULTIPLIER, CONSUMER_BASE_CHURN, API_TOKENS_PER_REQUEST, } from '@ai-tycoon/shared'; export interface MarketTickResult { marketState: MarketState; apiRevenue: number; subscriptionRevenue: number; totalTokenDemand: number; } export function processMarket(state: GameState, compute: ComputeState): MarketTickResult { const bestModel = state.models.trainedModels .filter(m => m.isDeployed) .sort((a, b) => b.benchmarkScore - a.benchmarkScore)[0]; const modelQuality = bestModel ? bestModel.benchmarkScore / 100 : 0; const chatProduct = state.models.productLines.find(p => p.type === 'chat-product'); const textApi = state.models.productLines.find(p => p.type === 'text-api'); // --- Consumer market (subscription product) --- const consumers = { ...state.market.consumers }; let subscriptionRevenue = 0; if (chatProduct?.isActive && bestModel) { const priceAttractiveness = Math.max(0, 1 - chatProduct.pricing.subscriptionPrice / 100); const growthRate = (CONSUMER_BASE_GROWTH + modelQuality * CONSUMER_QUALITY_GROWTH_MULTIPLIER) * (0.5 + priceAttractiveness * 0.5); const churnRate = CONSUMER_BASE_CHURN * (1 + (1 - consumers.satisfaction) * 2); consumers.growthRatePerTick = growthRate; consumers.churnRatePerTick = churnRate; const newSubs = consumers.totalSubscribers * growthRate; const lostSubs = consumers.totalSubscribers * churnRate; consumers.totalSubscribers = Math.max(0, consumers.totalSubscribers + newSubs - lostSubs); if (consumers.totalSubscribers < 100 && modelQuality > 0.1) { consumers.totalSubscribers += 5 + modelQuality * 20; } const loadPenalty = compute.inferenceUtilization > 0.9 ? (compute.inferenceUtilization - 0.9) * 5 : 0; consumers.satisfaction = Math.min(1, Math.max(0, 0.3 + modelQuality * 0.5 + (1 - Math.min(1, compute.inferenceUtilization)) * 0.2 - loadPenalty, )); consumers.viralCoefficient = modelQuality > 0.5 ? 1 + (modelQuality - 0.5) * 2 : 0; subscriptionRevenue = consumers.totalSubscribers * (chatProduct.pricing.subscriptionPrice / 86400); } // --- B2B API market (organic demand based on model quality + reputation) --- const enterprise = { ...state.market.enterprise }; let apiRevenue = 0; let organicApiTokens = 0; if (textApi?.isActive && bestModel) { const reputationFactor = state.reputation.score / 100; const qualityFactor = modelQuality; const priceFactor = Math.max(0.1, 1 - (textApi.pricing.outputTokenPrice / 20)); organicApiTokens = Math.floor( qualityFactor * reputationFactor * priceFactor * 10_000_000 * (1 + state.meta.tickCount * 0.0001), ); let contractTokens = 0; for (const contract of enterprise.activeContracts) { contractTokens += contract.tokensPerTick; apiRevenue += (contract.tokensPerTick / 1_000_000) * contract.pricePerMToken; } const totalApiTokens = organicApiTokens + contractTokens; apiRevenue += (organicApiTokens / 1_000_000) * textApi.pricing.outputTokenPrice; enterprise.totalApiCallsPerTick = totalApiTokens / API_TOKENS_PER_REQUEST; } const totalTokenDemand = organicApiTokens + consumers.totalSubscribers * 100 + enterprise.activeContracts.reduce((s, c) => s + c.tokensPerTick, 0); return { marketState: { ...state.market, consumers, enterprise, }, apiRevenue, subscriptionRevenue, totalTokenDemand, }; }