Add complete game loop: training, revenue, market, offline catch-up

- Model training system: training jobs produce TrainedModels with
  calculated capabilities based on compute, data, and research
- Market system: organic API demand and consumer subscriptions now
  generate real revenue from deployed models
- Talent system: salary costs calculated from department headcount
- Toast notification system for game events (training complete, etc.)
- Offline catch-up: progress bar + summary screen when returning
- Market page: pricing controls for API and subscription products
- Finance page: income statement, cash charts, funding history
- Tick processor now runs all 7 systems in correct dependency order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 17:02:58 -04:00
parent fdc8e544ae
commit 9a48c188ad
12 changed files with 757 additions and 21 deletions
+47 -5
View File
@@ -3,20 +3,56 @@ import { processEconomy } from './systems/economySystem';
import { processInfrastructure } from './systems/infrastructureSystem';
import { processCompute } 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';
export interface TickResult {
state: Partial<GameState>;
notifications: TickNotification[];
}
export interface TickNotification {
title: string;
message: string;
type: 'info' | 'success' | 'warning' | 'danger';
}
export function processTick(state: GameState): Partial<GameState> {
const notifications: TickNotification[] = [];
const infrastructure = processInfrastructure(state);
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 market = processMarket(stateWithModels, state.compute);
const compute = processCompute(state, infrastructure);
const research = processResearch(state, compute);
const market = processMarket(state, compute);
const reputation = processReputation(state);
const economy = processEconomy(state, market, infrastructure);
compute.tokensPerSecondDemand = market.totalTokenDemand;
compute.inferenceUtilization = compute.tokensPerSecondCapacity > 0
? Math.min(1, market.totalTokenDemand / compute.tokensPerSecondCapacity)
: 0;
const talent = processTalent(stateWithModels);
const stateWithTalent = { ...stateWithModels, talent };
const research = processResearch(stateWithTalent, compute);
const reputation = processReputation(stateWithTalent);
const economy = processEconomy(stateWithTalent, market, infrastructure);
const tickCount = state.meta.tickCount + 1;
return {
const result: Partial<GameState> = {
meta: {
...state.meta,
tickCount,
@@ -27,7 +63,13 @@ export function processTick(state: GameState): Partial<GameState> {
infrastructure,
compute,
research,
models: modelResult.modelsState,
market: market.marketState,
talent,
reputation,
};
(result as Record<string, unknown>)['_notifications'] = notifications;
return result;
}