900d1d5190
CI / build-and-push (push) Successful in 34s
Three intertwined fixes: 1. Zero-capacity utilization: when inference allocation was 0%, the guard clause returned 0% utilization instead of 100%, so the market system never penalized satisfaction and subscribers never churned. 2. Stale compute in market: restructured tick order so capacity is computed before market runs, giving satisfaction calculations current-tick demand/capacity ratio instead of previous tick's. 3. Subscriber growth: replaced pure compound growth (reached billions in minutes) with logistic saturation curve. Era-based market caps: startup 10K, scaleup 1M, bigtech 20M, agi 100M. Quality and reputation expand the effective cap. Also tuned FLOPS-to-tokens multiplier (10 → 26) for balanced demand/capacity feel across all eras, and added market saturation indicator to the Market page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
153 lines
5.0 KiB
TypeScript
153 lines
5.0 KiB
TypeScript
import type { GameState, AchievementDefinition } from '@ai-tycoon/shared';
|
|
import { processEconomy } from './systems/economySystem';
|
|
import { processInfrastructure } from './systems/infrastructureSystem';
|
|
import { computeCapacity, finalizeCompute } from './systems/computeSystem';
|
|
import { processResearch } from './systems/researchSystem';
|
|
import { processModels } from './systems/modelSystem';
|
|
import { processMarket } from './systems/marketSystem';
|
|
import { processReputation } from './systems/reputationSystem';
|
|
import { processTalent } from './systems/talentSystem';
|
|
import { processCompetitors } from './systems/competitorSystem';
|
|
import { processData } from './systems/dataSystem';
|
|
import { checkEraTransition } from './systems/eraSystem';
|
|
import { processAchievements } from './systems/achievementSystem';
|
|
import { computeValuation } from './systems/fundingSystem';
|
|
|
|
export interface TickResult {
|
|
state: Partial<GameState>;
|
|
notifications: TickNotification[];
|
|
}
|
|
|
|
export interface TickNotification {
|
|
title: string;
|
|
message: string;
|
|
type: 'info' | 'success' | 'warning' | 'danger';
|
|
}
|
|
|
|
let cachedAchievementDefs: AchievementDefinition[] | null = null;
|
|
|
|
export function setAchievementDefinitions(defs: AchievementDefinition[]) {
|
|
cachedAchievementDefs = defs;
|
|
}
|
|
|
|
export function processTick(state: GameState): Partial<GameState> {
|
|
const notifications: TickNotification[] = [];
|
|
|
|
const infraResult = processInfrastructure(state);
|
|
const infrastructure = infraResult.infrastructure;
|
|
notifications.push(...infraResult.notifications);
|
|
|
|
const stateWithInfra = { ...state, infrastructure };
|
|
const modelResult = processModels(stateWithInfra);
|
|
|
|
if (modelResult.modelCompleted) {
|
|
notifications.push({
|
|
title: 'Training Complete',
|
|
message: `${modelResult.modelCompleted.name} is ready! Benchmark: ${modelResult.modelCompleted.benchmarkScore.toFixed(1)}/100`,
|
|
type: 'success',
|
|
});
|
|
}
|
|
|
|
const stateWithModels = { ...stateWithInfra, models: modelResult.modelsState };
|
|
|
|
const capacity = computeCapacity(state, infrastructure);
|
|
const market = processMarket(stateWithModels, capacity.tokensPerSecondCapacity);
|
|
const compute = finalizeCompute(capacity, market.totalTokenDemand);
|
|
|
|
const talent = processTalent(stateWithModels);
|
|
const stateWithTalent = { ...stateWithModels, talent };
|
|
const researchResult = processResearch(stateWithTalent, compute);
|
|
|
|
if (researchResult.researchCompleted) {
|
|
notifications.push({
|
|
title: 'Research Complete',
|
|
message: `${researchResult.researchCompleted} has been unlocked!`,
|
|
type: 'success',
|
|
});
|
|
}
|
|
|
|
const reputationResult = processReputation(stateWithTalent);
|
|
const { _safetyIncident, ...reputation } = reputationResult;
|
|
if (_safetyIncident) {
|
|
notifications.push({
|
|
title: 'Safety Incident!',
|
|
message: 'Your AI model caused a safety incident. Public trust and safety record damaged.',
|
|
type: 'danger',
|
|
});
|
|
}
|
|
const economy = processEconomy(stateWithTalent, market, infrastructure, infraResult.repairCosts);
|
|
const data = processData(stateWithTalent);
|
|
const competitors = processCompetitors(stateWithTalent);
|
|
|
|
const tickCount = state.meta.tickCount + 1;
|
|
|
|
let meta = {
|
|
...state.meta,
|
|
tickCount,
|
|
lastTickTimestamp: Date.now(),
|
|
totalPlayTime: state.meta.totalPlayTime + 1,
|
|
};
|
|
|
|
const newEra = checkEraTransition({ ...stateWithTalent, economy, reputation, research: researchResult.research });
|
|
if (newEra) {
|
|
meta = { ...meta, currentEra: newEra };
|
|
notifications.push({
|
|
title: 'Era Transition!',
|
|
message: `Your company has entered the ${newEra === 'scaleup' ? 'Scale-up' : newEra === 'bigtech' ? 'Big Tech' : 'AGI'} era!`,
|
|
type: 'success',
|
|
});
|
|
}
|
|
|
|
const valuation = computeValuation({ ...stateWithTalent, economy, reputation, research: researchResult.research });
|
|
const updatedEconomy = {
|
|
...economy,
|
|
funding: { ...economy.funding, valuation },
|
|
};
|
|
|
|
const stateForAchievements: GameState = {
|
|
...stateWithTalent,
|
|
meta,
|
|
economy: updatedEconomy,
|
|
infrastructure,
|
|
compute,
|
|
research: researchResult.research,
|
|
models: modelResult.modelsState,
|
|
market: market.marketState,
|
|
reputation,
|
|
data,
|
|
competitors,
|
|
achievements: state.achievements,
|
|
};
|
|
|
|
const achievementResult = cachedAchievementDefs
|
|
? processAchievements(stateForAchievements, cachedAchievementDefs)
|
|
: { achievements: state.achievements, newAchievements: [] };
|
|
|
|
for (const name of achievementResult.newAchievements) {
|
|
notifications.push({
|
|
title: 'Achievement Unlocked!',
|
|
message: name,
|
|
type: 'success',
|
|
});
|
|
}
|
|
|
|
const result: Partial<GameState> = {
|
|
meta,
|
|
economy: updatedEconomy,
|
|
infrastructure,
|
|
compute,
|
|
research: researchResult.research,
|
|
models: modelResult.modelsState,
|
|
market: market.marketState,
|
|
talent,
|
|
reputation,
|
|
data,
|
|
competitors,
|
|
achievements: achievementResult.achievements,
|
|
};
|
|
|
|
(result as Record<string, unknown>)['_notifications'] = notifications;
|
|
|
|
return result;
|
|
}
|