Files
AIHostingTycoon/packages/game-engine/src/tick.ts
T
josh 283c7c7932
CI / build-and-push (push) Successful in 37s
Overhaul dashboard into command center with compute tracking, era-gated sections
Add compute history time-series (capacity vs demand chart), revenue vs expenses
dual-line chart, enhanced system status (training allocation, network uptime,
model freshness), active operations panel, market position bars, and competitor
snapshot. Stat cards expand from 3 to 6 as player progresses through eras.
Graceful v9→v10 save migration preserves existing games.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-25 13:45:16 -04:00

168 lines
5.9 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';
import { getResearchBonuses } from './systems/researchBonuses';
export interface TickResult {
state: Partial<GameState>;
notifications: TickNotification[];
}
export interface TickNotification {
title: string;
message: string;
type: 'info' | 'success' | 'warning' | 'danger';
action?: { label: string; page?: string; modelsTab?: string };
}
let cachedAchievementDefs: AchievementDefinition[] | null = null;
export function setAchievementDefinitions(defs: AchievementDefinition[]) {
cachedAchievementDefs = defs;
}
export function processTick(state: GameState): Partial<GameState> {
const notifications: TickNotification[] = [];
const researchBonuses = getResearchBonuses(state.research.completedResearch);
const infraResult = processInfrastructure(state, researchBonuses);
const infrastructure = infraResult.infrastructure;
notifications.push(...infraResult.notifications);
const stateWithInfra = { ...state, infrastructure };
const modelResult = processModels(stateWithInfra, researchBonuses);
for (const completed of modelResult.completedModels) {
notifications.push({
title: 'Training Complete',
message: `${completed.name} is ready! Capability: ${completed.rawCapability.toFixed(1)}/100. Deploy it to start earning revenue.`,
type: 'success',
action: { label: 'Go to Families', page: 'models', modelsTab: 'models' },
});
}
notifications.push(...modelResult.notifications);
const stateWithModels = { ...stateWithInfra, models: modelResult.modelsState };
const capacity = computeCapacity(state, infrastructure, researchBonuses);
const market = processMarket(stateWithModels, capacity.tokensPerSecondCapacity, capacity.effectiveInferenceFlops, researchBonuses);
const compute = finalizeCompute(capacity, market.totalTokenDemand, state.compute.computeHistory, state.meta.tickCount);
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, researchBonuses);
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',
});
}
if (modelResult.reputationHit > 0) {
reputation.publicPerception = Math.max(0, reputation.publicPerception - modelResult.reputationHit);
reputation.score = Math.round(
reputation.safetyRecord * 0.3 +
reputation.publicPerception * 0.3 +
reputation.employeeSatisfaction * 0.2 +
reputation.regulatoryStanding * 0.2,
);
}
const extraCosts = infraResult.repairCosts + modelResult.legalCosts;
const economy = processEconomy(stateWithTalent, market, infrastructure, extraCosts);
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;
}