Initial scaffold: AI Tycoon monorepo with core game loop
Turborepo monorepo with three packages: - packages/shared: TypeScript types for all 14 game systems + balance constants + formatting utils - packages/game-engine: Pure TS simulation engine with tick processor, economy, infrastructure, compute, research, market, and reputation systems - apps/web: React + Vite + Tailwind + Zustand frontend with sidebar dashboard layout, new game screen, dashboard with charts, infrastructure management, and model training pages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
export const TICK_INTERVAL_MS = 1000;
|
||||
export const MAX_OFFLINE_TICKS = 86_400;
|
||||
export const OFFLINE_EFFICIENCY = 0.8;
|
||||
export const FAST_FORWARD_BATCH_SIZE = 100;
|
||||
export const AUTO_SAVE_INTERVAL_TICKS = 60;
|
||||
export const FINANCIAL_SNAPSHOT_INTERVAL = 60;
|
||||
export const MAX_FINANCIAL_HISTORY = 1000;
|
||||
export const MAX_EVENT_HISTORY = 50;
|
||||
export const MAX_REPUTATION_HISTORY = 500;
|
||||
|
||||
export const STARTING_MONEY = 50_000;
|
||||
export const BASE_ENERGY_COST_PER_FLOP = 0.001;
|
||||
export const BASE_MAINTENANCE_PER_GPU = 0.5;
|
||||
|
||||
export const TRAINING_BASE_TICKS = 120;
|
||||
export const TRAINING_COMPUTE_MULTIPLIER = 0.8;
|
||||
export const TRAINING_DATA_QUALITY_WEIGHT = 0.3;
|
||||
|
||||
export const CAPABILITY_FORMULA = {
|
||||
computeWeight: 0.4,
|
||||
dataWeight: 0.3,
|
||||
researcherWeight: 0.2,
|
||||
efficiencyWeight: 0.1,
|
||||
};
|
||||
|
||||
export const CONSUMER_BASE_GROWTH = 0.002;
|
||||
export const CONSUMER_QUALITY_GROWTH_MULTIPLIER = 0.01;
|
||||
export const CONSUMER_PRICE_ELASTICITY = -0.5;
|
||||
export const CONSUMER_BASE_CHURN = 0.001;
|
||||
|
||||
export const API_TOKENS_PER_REQUEST = 500;
|
||||
export const API_REVENUE_PER_MTOK = 1.0;
|
||||
|
||||
export const ERA_THRESHOLDS = {
|
||||
scaleup: { revenue: 10_000, capability: 15, reputation: 30 },
|
||||
bigtech: { revenue: 1_000_000, capability: 50, reputation: 60 },
|
||||
agi: { revenue: 100_000_000, capability: 90, reputation: 70 },
|
||||
};
|
||||
|
||||
export const GPU_PRICE_VOLATILITY = 0.02;
|
||||
export const GPU_FAILURE_RATE_BASE = 0.0001;
|
||||
export const REDUNDANCY_FAILURE_REDUCTION = 0.5;
|
||||
@@ -0,0 +1,15 @@
|
||||
export * from './types/gameState';
|
||||
export * from './types/economy';
|
||||
export * from './types/infrastructure';
|
||||
export * from './types/compute';
|
||||
export * from './types/research';
|
||||
export * from './types/models';
|
||||
export * from './types/market';
|
||||
export * from './types/competitors';
|
||||
export * from './types/talent';
|
||||
export * from './types/data';
|
||||
export * from './types/reputation';
|
||||
export * from './types/events';
|
||||
export * from './types/achievements';
|
||||
export * from './utils/formatting';
|
||||
export * from './constants/gameBalance';
|
||||
@@ -0,0 +1,28 @@
|
||||
export interface AchievementState {
|
||||
unlocked: UnlockedAchievement[];
|
||||
progress: Record<string, number>;
|
||||
}
|
||||
|
||||
export interface UnlockedAchievement {
|
||||
id: string;
|
||||
unlockedAtTick: number;
|
||||
}
|
||||
|
||||
export interface AchievementDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
condition: AchievementCondition;
|
||||
}
|
||||
|
||||
export interface AchievementCondition {
|
||||
field: string;
|
||||
operator: 'gt' | 'gte' | 'eq';
|
||||
value: number;
|
||||
}
|
||||
|
||||
export const INITIAL_ACHIEVEMENTS: AchievementState = {
|
||||
unlocked: [],
|
||||
progress: {},
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
export interface CompetitorState {
|
||||
rivals: Competitor[];
|
||||
industryBenchmark: number;
|
||||
}
|
||||
|
||||
export interface Competitor {
|
||||
id: string;
|
||||
name: string;
|
||||
archetype: CompetitorArchetype;
|
||||
personality: CompetitorPersonality;
|
||||
status: 'active' | 'acquired' | 'failed';
|
||||
estimatedCapability: number;
|
||||
estimatedRevenue: number;
|
||||
estimatedUsers: number;
|
||||
reputation: number;
|
||||
latestModelName: string;
|
||||
completedMilestones: string[];
|
||||
nextMilestoneAtTick: number;
|
||||
}
|
||||
|
||||
export type CompetitorArchetype = 'safety-first' | 'move-fast' | 'big-tech' | 'open-source' | 'stealth-startup';
|
||||
|
||||
export interface CompetitorPersonality {
|
||||
aggression: number;
|
||||
safetyFocus: number;
|
||||
openSourceTendency: number;
|
||||
marketingFocus: number;
|
||||
researchFocus: number;
|
||||
riskTolerance: number;
|
||||
}
|
||||
|
||||
export const INITIAL_COMPETITORS: CompetitorState = {
|
||||
rivals: [],
|
||||
industryBenchmark: 0,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface ComputeState {
|
||||
totalFlops: number;
|
||||
trainingAllocation: number;
|
||||
inferenceAllocation: number;
|
||||
inferenceUtilization: number;
|
||||
tokensPerSecondCapacity: number;
|
||||
tokensPerSecondDemand: number;
|
||||
}
|
||||
|
||||
export const INITIAL_COMPUTE: ComputeState = {
|
||||
totalFlops: 0,
|
||||
trainingAllocation: 0.5,
|
||||
inferenceAllocation: 0.5,
|
||||
inferenceUtilization: 0,
|
||||
tokensPerSecondCapacity: 0,
|
||||
tokensPerSecondDemand: 0,
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
export interface DataState {
|
||||
ownedDatasets: OwnedDataset[];
|
||||
userDataGenerationRate: number;
|
||||
totalTrainingTokens: number;
|
||||
partnerships: DataPartnership[];
|
||||
}
|
||||
|
||||
export interface OwnedDataset {
|
||||
id: string;
|
||||
name: string;
|
||||
domain: DataDomain;
|
||||
sizeTokens: number;
|
||||
quality: number;
|
||||
legalRisk: number;
|
||||
acquiredAtTick: number;
|
||||
}
|
||||
|
||||
export type DataDomain = 'web' | 'books' | 'code' | 'scientific' | 'conversation'
|
||||
| 'multilingual' | 'images' | 'video' | 'audio' | 'synthetic';
|
||||
|
||||
export interface DataPartnership {
|
||||
id: string;
|
||||
partnerName: string;
|
||||
domain: DataDomain;
|
||||
tokensPerTick: number;
|
||||
costPerTick: number;
|
||||
exclusivity: boolean;
|
||||
durationTicks: number;
|
||||
startTick: number;
|
||||
}
|
||||
|
||||
export const INITIAL_DATA: DataState = {
|
||||
ownedDatasets: [
|
||||
{
|
||||
id: 'web-crawl-basic',
|
||||
name: 'Basic Web Crawl',
|
||||
domain: 'web',
|
||||
sizeTokens: 1_000_000_000,
|
||||
quality: 0.3,
|
||||
legalRisk: 0.2,
|
||||
acquiredAtTick: 0,
|
||||
},
|
||||
],
|
||||
userDataGenerationRate: 0,
|
||||
totalTrainingTokens: 1_000_000_000,
|
||||
partnerships: [],
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
export interface EconomyState {
|
||||
money: number;
|
||||
totalRevenue: number;
|
||||
totalExpenses: number;
|
||||
revenuePerTick: number;
|
||||
expensesPerTick: number;
|
||||
funding: FundingState;
|
||||
financialHistory: FinancialSnapshot[];
|
||||
}
|
||||
|
||||
export interface FundingState {
|
||||
totalRaised: number;
|
||||
currentRound: FundingRound | null;
|
||||
completedRounds: CompletedFundingRound[];
|
||||
founderEquity: number;
|
||||
valuation: number;
|
||||
isPublic: boolean;
|
||||
}
|
||||
|
||||
export type FundingRoundType = 'seed' | 'seriesA' | 'seriesB' | 'seriesC' | 'seriesD' | 'ipo';
|
||||
|
||||
export interface FundingRound {
|
||||
type: FundingRoundType;
|
||||
targetAmount: number;
|
||||
dilution: number;
|
||||
requirements: {
|
||||
minRevenue?: number;
|
||||
minUsers?: number;
|
||||
minReputation?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CompletedFundingRound {
|
||||
type: FundingRoundType;
|
||||
amount: number;
|
||||
dilution: number;
|
||||
completedAtTick: number;
|
||||
}
|
||||
|
||||
export interface FinancialSnapshot {
|
||||
tick: number;
|
||||
money: number;
|
||||
revenue: number;
|
||||
expenses: number;
|
||||
valuation: number;
|
||||
}
|
||||
|
||||
export const INITIAL_ECONOMY: EconomyState = {
|
||||
money: 50_000,
|
||||
totalRevenue: 0,
|
||||
totalExpenses: 0,
|
||||
revenuePerTick: 0,
|
||||
expensesPerTick: 0,
|
||||
funding: {
|
||||
totalRaised: 0,
|
||||
currentRound: null,
|
||||
completedRounds: [],
|
||||
founderEquity: 1.0,
|
||||
valuation: 100_000,
|
||||
isPublic: false,
|
||||
},
|
||||
financialHistory: [],
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
import type { Era } from './gameState';
|
||||
|
||||
export interface EventState {
|
||||
activeEvents: ActiveEvent[];
|
||||
eventHistory: EventHistoryEntry[];
|
||||
eventCooldowns: Record<string, number>;
|
||||
eventOccurrences: Record<string, number>;
|
||||
}
|
||||
|
||||
export interface ActiveEvent {
|
||||
eventId: string;
|
||||
instanceId: string;
|
||||
triggeredAtTick: number;
|
||||
expiresAtTick: number;
|
||||
title: string;
|
||||
description: string;
|
||||
category: EventCategory;
|
||||
choices: EventChoice[];
|
||||
defaultChoiceIndex: number;
|
||||
}
|
||||
|
||||
export type EventCategory = 'industry' | 'regulatory' | 'pr' | 'internal' | 'market';
|
||||
|
||||
export interface EventChoice {
|
||||
label: string;
|
||||
description: string;
|
||||
consequences: EventConsequence[];
|
||||
}
|
||||
|
||||
export interface EventConsequence {
|
||||
type: 'money' | 'reputation' | 'compute' | 'talent' | 'research_speed'
|
||||
| 'regulation' | 'competitor' | 'unlock' | 'lock' | 'chain_event';
|
||||
value: number;
|
||||
target?: string;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export interface EventHistoryEntry {
|
||||
eventId: string;
|
||||
instanceId: string;
|
||||
title: string;
|
||||
category: EventCategory;
|
||||
tick: number;
|
||||
chosenOptionIndex: number;
|
||||
}
|
||||
|
||||
export interface EventDefinition {
|
||||
id: string;
|
||||
title: string;
|
||||
descriptionTemplate: string;
|
||||
category: EventCategory;
|
||||
eras: Era[];
|
||||
weight: number;
|
||||
cooldownTicks: number;
|
||||
maxOccurrences: number;
|
||||
prerequisites: string[];
|
||||
conditions: EventCondition[];
|
||||
choices: EventChoice[];
|
||||
defaultChoiceIndex: number;
|
||||
expiryTicks: number;
|
||||
}
|
||||
|
||||
export interface EventCondition {
|
||||
field: string;
|
||||
operator: 'gt' | 'lt' | 'gte' | 'lte' | 'eq';
|
||||
value: number;
|
||||
}
|
||||
|
||||
export const INITIAL_EVENTS: EventState = {
|
||||
activeEvents: [],
|
||||
eventHistory: [],
|
||||
eventCooldowns: {},
|
||||
eventOccurrences: {},
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { EconomyState } from './economy';
|
||||
import type { InfrastructureState } from './infrastructure';
|
||||
import type { ComputeState } from './compute';
|
||||
import type { ResearchState } from './research';
|
||||
import type { ModelsState } from './models';
|
||||
import type { MarketState } from './market';
|
||||
import type { CompetitorState } from './competitors';
|
||||
import type { TalentState } from './talent';
|
||||
import type { DataState } from './data';
|
||||
import type { ReputationState } from './reputation';
|
||||
import type { EventState } from './events';
|
||||
import type { AchievementState } from './achievements';
|
||||
|
||||
export interface GameState {
|
||||
meta: GameMeta;
|
||||
economy: EconomyState;
|
||||
infrastructure: InfrastructureState;
|
||||
compute: ComputeState;
|
||||
research: ResearchState;
|
||||
models: ModelsState;
|
||||
market: MarketState;
|
||||
competitors: CompetitorState;
|
||||
talent: TalentState;
|
||||
data: DataState;
|
||||
reputation: ReputationState;
|
||||
events: EventState;
|
||||
achievements: AchievementState;
|
||||
}
|
||||
|
||||
export interface GameMeta {
|
||||
saveVersion: number;
|
||||
companyName: string;
|
||||
currentEra: Era;
|
||||
tickCount: number;
|
||||
lastTickTimestamp: number;
|
||||
gameSpeed: GameSpeed;
|
||||
isPaused: boolean;
|
||||
createdAt: number;
|
||||
totalPlayTime: number;
|
||||
settings: GameSettings;
|
||||
}
|
||||
|
||||
export type Era = 'startup' | 'scaleup' | 'bigtech' | 'agi';
|
||||
|
||||
export type GameSpeed = 1 | 2 | 5;
|
||||
|
||||
export interface GameSettings {
|
||||
autoSaveInterval: number;
|
||||
notificationsEnabled: boolean;
|
||||
soundEnabled: boolean;
|
||||
musicVolume: number;
|
||||
sfxVolume: number;
|
||||
}
|
||||
|
||||
export const INITIAL_SETTINGS: GameSettings = {
|
||||
autoSaveInterval: 60,
|
||||
notificationsEnabled: true,
|
||||
soundEnabled: true,
|
||||
musicVolume: 0.5,
|
||||
sfxVolume: 0.7,
|
||||
};
|
||||
|
||||
export const SAVE_VERSION = 1;
|
||||
@@ -0,0 +1,84 @@
|
||||
import type { Era } from './gameState';
|
||||
|
||||
export interface InfrastructureState {
|
||||
dataCenters: DataCenter[];
|
||||
gpuMarketPrices: Record<GpuType, number>;
|
||||
totalFlops: number;
|
||||
totalUptime: number;
|
||||
}
|
||||
|
||||
export interface DataCenter {
|
||||
id: string;
|
||||
name: string;
|
||||
location: LocationId;
|
||||
gpus: GpuInventory[];
|
||||
maxCapacity: number;
|
||||
coolingLevel: number;
|
||||
redundancyLevel: number;
|
||||
currentUptime: number;
|
||||
energyCostPerTick: number;
|
||||
maintenanceCostPerTick: number;
|
||||
}
|
||||
|
||||
export interface GpuInventory {
|
||||
type: GpuType;
|
||||
count: number;
|
||||
healthyCount: number;
|
||||
failedCount: number;
|
||||
}
|
||||
|
||||
export type GpuType = 'consumer' | 't4' | 'a100' | 'h100' | 'b200' | 'custom';
|
||||
|
||||
export type LocationId = 'us-west' | 'us-east' | 'eu-west' | 'eu-north' | 'asia-east' | 'asia-south' | 'middle-east';
|
||||
|
||||
export interface LocationConfig {
|
||||
id: LocationId;
|
||||
name: string;
|
||||
energyCostMultiplier: number;
|
||||
latencyTier: number;
|
||||
regulatoryStrictness: number;
|
||||
politicalStability: number;
|
||||
availableAt: Era;
|
||||
}
|
||||
|
||||
export interface GpuConfig {
|
||||
type: GpuType;
|
||||
name: string;
|
||||
flopsPerUnit: number;
|
||||
basePowerDraw: number;
|
||||
basePrice: number;
|
||||
availableAt: Era;
|
||||
}
|
||||
|
||||
export const GPU_CONFIGS: Record<GpuType, GpuConfig> = {
|
||||
consumer: { type: 'consumer', name: 'Consumer GPU', flopsPerUnit: 1, basePowerDraw: 0.3, basePrice: 800, availableAt: 'startup' },
|
||||
t4: { type: 't4', name: 'NVIDIA T4', flopsPerUnit: 8, basePowerDraw: 0.5, basePrice: 5_000, availableAt: 'startup' },
|
||||
a100: { type: 'a100', name: 'NVIDIA A100', flopsPerUnit: 40, basePowerDraw: 1.0, basePrice: 15_000, availableAt: 'scaleup' },
|
||||
h100: { type: 'h100', name: 'NVIDIA H100', flopsPerUnit: 120, basePowerDraw: 1.2, basePrice: 35_000, availableAt: 'scaleup' },
|
||||
b200: { type: 'b200', name: 'NVIDIA B200', flopsPerUnit: 400, basePowerDraw: 1.5, basePrice: 50_000, availableAt: 'bigtech' },
|
||||
custom: { type: 'custom', name: 'Custom ASIC', flopsPerUnit: 800, basePowerDraw: 1.0, basePrice: 80_000, availableAt: 'agi' },
|
||||
};
|
||||
|
||||
export const LOCATION_CONFIGS: Record<LocationId, LocationConfig> = {
|
||||
'us-west': { id: 'us-west', name: 'US West (Oregon)', energyCostMultiplier: 1.0, latencyTier: 1, regulatoryStrictness: 0.3, politicalStability: 0.9, availableAt: 'startup' },
|
||||
'us-east': { id: 'us-east', name: 'US East (Virginia)', energyCostMultiplier: 1.1, latencyTier: 1, regulatoryStrictness: 0.3, politicalStability: 0.9, availableAt: 'startup' },
|
||||
'eu-west': { id: 'eu-west', name: 'EU West (Ireland)', energyCostMultiplier: 1.3, latencyTier: 2, regulatoryStrictness: 0.7, politicalStability: 0.85, availableAt: 'scaleup' },
|
||||
'eu-north': { id: 'eu-north', name: 'EU North (Sweden)', energyCostMultiplier: 0.8, latencyTier: 2, regulatoryStrictness: 0.6, politicalStability: 0.95, availableAt: 'scaleup' },
|
||||
'asia-east': { id: 'asia-east', name: 'Asia East (Tokyo)', energyCostMultiplier: 1.4, latencyTier: 3, regulatoryStrictness: 0.4, politicalStability: 0.9, availableAt: 'scaleup' },
|
||||
'asia-south': { id: 'asia-south', name: 'Asia South (Mumbai)', energyCostMultiplier: 0.6, latencyTier: 3, regulatoryStrictness: 0.2, politicalStability: 0.7, availableAt: 'bigtech' },
|
||||
'middle-east': { id: 'middle-east', name: 'Middle East (UAE)', energyCostMultiplier: 0.5, latencyTier: 3, regulatoryStrictness: 0.1, politicalStability: 0.6, availableAt: 'bigtech' },
|
||||
};
|
||||
|
||||
export const INITIAL_INFRASTRUCTURE: InfrastructureState = {
|
||||
dataCenters: [],
|
||||
gpuMarketPrices: {
|
||||
consumer: 800,
|
||||
t4: 5_000,
|
||||
a100: 15_000,
|
||||
h100: 35_000,
|
||||
b200: 50_000,
|
||||
custom: 80_000,
|
||||
},
|
||||
totalFlops: 0,
|
||||
totalUptime: 1,
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
export interface MarketState {
|
||||
consumers: ConsumerMarket;
|
||||
enterprise: EnterpriseMarket;
|
||||
overloadPolicy: OverloadPolicy;
|
||||
openSourcedModels: string[];
|
||||
}
|
||||
|
||||
export interface ConsumerMarket {
|
||||
totalSubscribers: number;
|
||||
churnRatePerTick: number;
|
||||
growthRatePerTick: number;
|
||||
satisfaction: number;
|
||||
viralCoefficient: number;
|
||||
}
|
||||
|
||||
export interface EnterpriseMarket {
|
||||
activeContracts: EnterpriseContract[];
|
||||
pendingRFPs: EnterpriseRFP[];
|
||||
totalApiCallsPerTick: number;
|
||||
averageTokensPerCall: number;
|
||||
}
|
||||
|
||||
export interface EnterpriseContract {
|
||||
id: string;
|
||||
customerName: string;
|
||||
segment: 'startup' | 'mid-market' | 'enterprise' | 'government';
|
||||
tokensPerTick: number;
|
||||
pricePerMToken: number;
|
||||
slaUptime: number;
|
||||
startTick: number;
|
||||
durationTicks: number;
|
||||
satisfaction: number;
|
||||
}
|
||||
|
||||
export interface EnterpriseRFP {
|
||||
id: string;
|
||||
customerName: string;
|
||||
segment: 'startup' | 'mid-market' | 'enterprise' | 'government';
|
||||
requiredCapability: number;
|
||||
offeredPricePerMToken: number;
|
||||
requiredSlaUptime: number;
|
||||
expiresAtTick: number;
|
||||
}
|
||||
|
||||
export interface OverloadPolicy {
|
||||
maxQueueDepth: number;
|
||||
rateLimitPerCustomer: number;
|
||||
degradeQualityUnderLoad: boolean;
|
||||
prioritizeEnterprise: boolean;
|
||||
}
|
||||
|
||||
export const INITIAL_MARKET: MarketState = {
|
||||
consumers: {
|
||||
totalSubscribers: 0,
|
||||
churnRatePerTick: 0.001,
|
||||
growthRatePerTick: 0,
|
||||
satisfaction: 0.5,
|
||||
viralCoefficient: 0,
|
||||
},
|
||||
enterprise: {
|
||||
activeContracts: [],
|
||||
pendingRFPs: [],
|
||||
totalApiCallsPerTick: 0,
|
||||
averageTokensPerCall: 500,
|
||||
},
|
||||
overloadPolicy: {
|
||||
maxQueueDepth: 100,
|
||||
rateLimitPerCustomer: 1000,
|
||||
degradeQualityUnderLoad: false,
|
||||
prioritizeEnterprise: true,
|
||||
},
|
||||
openSourcedModels: [],
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
export interface ModelsState {
|
||||
trainedModels: TrainedModel[];
|
||||
activeTraining: TrainingJob | null;
|
||||
productLines: ProductLine[];
|
||||
}
|
||||
|
||||
export interface TrainedModel {
|
||||
id: string;
|
||||
name: string;
|
||||
generation: number;
|
||||
parameterCount: number;
|
||||
trainingDataSize: number;
|
||||
capabilities: ModelCapabilities;
|
||||
safetyScore: number;
|
||||
benchmarkScore: number;
|
||||
tuning: ModelTuning;
|
||||
isDeployed: boolean;
|
||||
trainedAtTick: number;
|
||||
}
|
||||
|
||||
export interface ModelCapabilities {
|
||||
reasoning: number;
|
||||
coding: number;
|
||||
creative: number;
|
||||
multimodal: number;
|
||||
agents: number;
|
||||
speed: number;
|
||||
}
|
||||
|
||||
export interface ModelTuning {
|
||||
preset: TuningPreset;
|
||||
verbosity?: number;
|
||||
safetyLevel?: number;
|
||||
creativity?: number;
|
||||
speedQuality?: number;
|
||||
refusalRate?: number;
|
||||
}
|
||||
|
||||
export type TuningPreset = 'helpful-safe' | 'max-capability' | 'enterprise' | 'creative';
|
||||
|
||||
export interface TrainingJob {
|
||||
modelName: string;
|
||||
generation: number;
|
||||
allocatedCompute: number;
|
||||
allocatedDataTokens: number;
|
||||
progressTicks: number;
|
||||
totalTicks: number;
|
||||
estimatedCapability: number;
|
||||
}
|
||||
|
||||
export interface ProductLine {
|
||||
id: string;
|
||||
type: ProductLineType;
|
||||
name: string;
|
||||
modelId: string | null;
|
||||
isActive: boolean;
|
||||
pricing: ProductPricing;
|
||||
}
|
||||
|
||||
export type ProductLineType = 'text-api' | 'chat-product' | 'image' | 'code' | 'agents';
|
||||
|
||||
export interface ProductPricing {
|
||||
inputTokenPrice: number;
|
||||
outputTokenPrice: number;
|
||||
thinkingTokenBudget: number;
|
||||
cachingEnabled: boolean;
|
||||
subscriptionPrice: number;
|
||||
freeTokenAllowance: number;
|
||||
}
|
||||
|
||||
export const INITIAL_MODELS: ModelsState = {
|
||||
trainedModels: [],
|
||||
activeTraining: null,
|
||||
productLines: [
|
||||
{
|
||||
id: 'text-api',
|
||||
type: 'text-api',
|
||||
name: 'Text API',
|
||||
modelId: null,
|
||||
isActive: false,
|
||||
pricing: {
|
||||
inputTokenPrice: 1.0,
|
||||
outputTokenPrice: 3.0,
|
||||
thinkingTokenBudget: 0,
|
||||
cachingEnabled: false,
|
||||
subscriptionPrice: 0,
|
||||
freeTokenAllowance: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'chat-product',
|
||||
type: 'chat-product',
|
||||
name: 'Chat Product',
|
||||
modelId: null,
|
||||
isActive: false,
|
||||
pricing: {
|
||||
inputTokenPrice: 0,
|
||||
outputTokenPrice: 0,
|
||||
thinkingTokenBudget: 0,
|
||||
cachingEnabled: false,
|
||||
subscriptionPrice: 20,
|
||||
freeTokenAllowance: 10_000,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
export interface ReputationState {
|
||||
score: number;
|
||||
safetyRecord: number;
|
||||
publicPerception: number;
|
||||
employeeSatisfaction: number;
|
||||
regulatoryStanding: number;
|
||||
reputationHistory: ReputationSnapshot[];
|
||||
}
|
||||
|
||||
export interface ReputationSnapshot {
|
||||
tick: number;
|
||||
score: number;
|
||||
}
|
||||
|
||||
export const INITIAL_REPUTATION: ReputationState = {
|
||||
score: 50,
|
||||
safetyRecord: 50,
|
||||
publicPerception: 50,
|
||||
employeeSatisfaction: 70,
|
||||
regulatoryStanding: 50,
|
||||
reputationHistory: [],
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Era } from './gameState';
|
||||
|
||||
export interface ResearchState {
|
||||
completedResearch: string[];
|
||||
activeResearch: ActiveResearch | null;
|
||||
researchPoints: number;
|
||||
}
|
||||
|
||||
export interface ActiveResearch {
|
||||
researchId: string;
|
||||
progressTicks: number;
|
||||
totalTicks: number;
|
||||
allocatedResearchers: number;
|
||||
allocatedCompute: number;
|
||||
}
|
||||
|
||||
export interface ResearchNode {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
era: Era;
|
||||
category: 'generation' | 'efficiency' | 'safety' | 'specialization' | 'infrastructure';
|
||||
branch?: 'reasoning' | 'coding' | 'creative' | 'multimodal' | 'agents';
|
||||
prerequisites: string[];
|
||||
cost: {
|
||||
researchPoints: number;
|
||||
compute: number;
|
||||
ticks: number;
|
||||
};
|
||||
effects: ResearchEffect[];
|
||||
}
|
||||
|
||||
export interface ResearchEffect {
|
||||
type: 'unlock_gpu' | 'unlock_model_tier' | 'efficiency_boost'
|
||||
| 'capability_boost' | 'cost_reduction' | 'unlock_feature'
|
||||
| 'unlock_product_line' | 'safety_boost';
|
||||
target: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export const INITIAL_RESEARCH: ResearchState = {
|
||||
completedResearch: [],
|
||||
activeResearch: null,
|
||||
researchPoints: 0,
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
export interface TalentState {
|
||||
departments: Record<DepartmentId, Department>;
|
||||
keyHires: KeyHire[];
|
||||
hiringPipeline: HiringCandidate[];
|
||||
totalSalaryPerTick: number;
|
||||
}
|
||||
|
||||
export type DepartmentId = 'research' | 'engineering' | 'operations' | 'sales';
|
||||
|
||||
export interface Department {
|
||||
id: DepartmentId;
|
||||
headcount: number;
|
||||
budget: number;
|
||||
effectiveness: number;
|
||||
morale: number;
|
||||
}
|
||||
|
||||
export interface KeyHire {
|
||||
id: string;
|
||||
name: string;
|
||||
department: DepartmentId;
|
||||
specialAbility: string;
|
||||
effects: { type: string; value: number }[];
|
||||
salary: number;
|
||||
hiredAtTick: number;
|
||||
loyalty: number;
|
||||
}
|
||||
|
||||
export interface HiringCandidate {
|
||||
id: string;
|
||||
name: string;
|
||||
department: DepartmentId;
|
||||
quality: number;
|
||||
salaryCost: number;
|
||||
expiresAtTick: number;
|
||||
isKeyHire: boolean;
|
||||
}
|
||||
|
||||
export const INITIAL_TALENT: TalentState = {
|
||||
departments: {
|
||||
research: { id: 'research', headcount: 2, budget: 5_000, effectiveness: 0.5, morale: 0.8 },
|
||||
engineering: { id: 'engineering', headcount: 3, budget: 7_000, effectiveness: 0.5, morale: 0.8 },
|
||||
operations: { id: 'operations', headcount: 1, budget: 3_000, effectiveness: 0.5, morale: 0.8 },
|
||||
sales: { id: 'sales', headcount: 0, budget: 0, effectiveness: 0, morale: 0.8 },
|
||||
},
|
||||
keyHires: [],
|
||||
hiringPipeline: [],
|
||||
totalSalaryPerTick: 0,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
export function formatNumber(n: number): string {
|
||||
if (n < 0) return `-${formatNumber(-n)}`;
|
||||
if (n < 1_000) return Math.floor(n).toString();
|
||||
if (n < 1_000_000) return `${(n / 1_000).toFixed(1)}K`;
|
||||
if (n < 1_000_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
||||
if (n < 1_000_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B`;
|
||||
return `${(n / 1_000_000_000_000).toFixed(1)}T`;
|
||||
}
|
||||
|
||||
export function formatMoney(n: number): string {
|
||||
if (n < 0) return `-$${formatNumber(-n)}`;
|
||||
return `$${formatNumber(n)}`;
|
||||
}
|
||||
|
||||
export function formatPercent(n: number, decimals = 1): string {
|
||||
return `${(n * 100).toFixed(decimals)}%`;
|
||||
}
|
||||
|
||||
export function formatTokens(n: number): string {
|
||||
return `${formatNumber(n)} tok`;
|
||||
}
|
||||
|
||||
export function formatFlops(n: number): string {
|
||||
return `${formatNumber(n)} FLOPS`;
|
||||
}
|
||||
|
||||
export function formatDuration(ticks: number): string {
|
||||
if (ticks < 60) return `${ticks}s`;
|
||||
if (ticks < 3600) return `${Math.floor(ticks / 60)}m ${ticks % 60}s`;
|
||||
const hours = Math.floor(ticks / 3600);
|
||||
const minutes = Math.floor((ticks % 3600) / 60);
|
||||
return `${hours}h ${minutes}m`;
|
||||
}
|
||||
Reference in New Issue
Block a user