Add Week 4 social features, regulation, and safety tradeoffs
Leaderboard page with category tabs and score submission, shareable company stats card with clipboard copy, dynamic regulation system (compliance costs scale with capability and era, regulatory standing tracks safety research), 6 geopolitical events (export controls, energy crisis, natural disaster, AI safety summit, immigration policy, data sovereignty), safety-capability tradeoff (safety score affects benchmark, low safety triggers incidents with reputation damage), and enhanced event consequence handling for regulation and talent types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { GameState, EconomyState, InfrastructureState } from '@ai-tycoon/shared';
|
||||
import { FINANCIAL_SNAPSHOT_INTERVAL, MAX_FINANCIAL_HISTORY } from '@ai-tycoon/shared';
|
||||
import { FINANCIAL_SNAPSHOT_INTERVAL, MAX_FINANCIAL_HISTORY, REGULATION_COMPLIANCE_PER_CAPABILITY } from '@ai-tycoon/shared';
|
||||
import type { MarketTickResult } from './marketSystem';
|
||||
|
||||
export function processEconomy(
|
||||
@@ -15,7 +15,12 @@ export function processEconomy(
|
||||
|
||||
const talentExpenses = state.talent.totalSalaryPerTick;
|
||||
const dataExpenses = state.data.partnerships.reduce((sum, p) => sum + p.costPerTick, 0);
|
||||
const expenses = infraExpenses + talentExpenses + dataExpenses;
|
||||
|
||||
const bestCapability = state.models.trainedModels.reduce((best, m) => Math.max(best, m.benchmarkScore), 0);
|
||||
const eraIdx = ['startup', 'scaleup', 'bigtech', 'agi'].indexOf(state.meta.currentEra);
|
||||
const complianceCost = bestCapability > 30 ? bestCapability * REGULATION_COMPLIANCE_PER_CAPABILITY * (1 + eraIdx * 0.5) / 100 : 0;
|
||||
|
||||
const expenses = infraExpenses + talentExpenses + dataExpenses + complianceCost;
|
||||
|
||||
const money = state.economy.money + revenue - expenses;
|
||||
|
||||
|
||||
@@ -65,10 +65,16 @@ function createTrainedModel(
|
||||
speed: Math.max(1, 100 - compute * 0.5 + efficiencyBonus * 2),
|
||||
};
|
||||
|
||||
const benchmarkScore = (capabilities.reasoning * 0.3 + capabilities.coding * 0.25 +
|
||||
capabilities.creative * 0.2 + capabilities.multimodal * 0.15 + capabilities.agents * 0.1);
|
||||
const safetyResearch = state.research.completedResearch.filter(
|
||||
r => r.includes('alignment') || r.includes('interpretability') || r.includes('constitutional'),
|
||||
).length;
|
||||
const safetyScore = Math.min(100, 30 + safetyResearch * 15 + Math.random() * 10);
|
||||
|
||||
const safetyScore = 50 + Math.random() * 20;
|
||||
const safetyPenalty = safetyScore > 60 ? (safetyScore - 60) * 0.1 : 0;
|
||||
const benchmarkScore = Math.max(0,
|
||||
(capabilities.reasoning * 0.3 + capabilities.coding * 0.25 +
|
||||
capabilities.creative * 0.2 + capabilities.multimodal * 0.15 + capabilities.agents * 0.1) - safetyPenalty,
|
||||
);
|
||||
|
||||
const parameterCount = Math.pow(10, generation) * (0.5 + Math.random());
|
||||
|
||||
|
||||
@@ -1,8 +1,48 @@
|
||||
import type { GameState, ReputationState } from '@ai-tycoon/shared';
|
||||
import { MAX_REPUTATION_HISTORY } from '@ai-tycoon/shared';
|
||||
import {
|
||||
MAX_REPUTATION_HISTORY,
|
||||
SAFETY_INCIDENT_PROBABILITY_BASE,
|
||||
SAFETY_INCIDENT_REPUTATION_HIT,
|
||||
LOW_SAFETY_THRESHOLD,
|
||||
} from '@ai-tycoon/shared';
|
||||
|
||||
export function processReputation(state: GameState): ReputationState {
|
||||
const { safetyRecord, publicPerception, employeeSatisfaction, regulatoryStanding } = state.reputation;
|
||||
export interface ReputationTickResult {
|
||||
reputation: ReputationState;
|
||||
safetyIncident: boolean;
|
||||
}
|
||||
|
||||
export function processReputation(state: GameState): ReputationState & { _safetyIncident?: boolean } {
|
||||
let { safetyRecord, publicPerception, employeeSatisfaction, regulatoryStanding } = state.reputation;
|
||||
|
||||
const bestModel = state.models.trainedModels
|
||||
.filter(m => m.isDeployed)
|
||||
.sort((a, b) => b.benchmarkScore - a.benchmarkScore)[0];
|
||||
|
||||
let safetyIncident = false;
|
||||
if (bestModel) {
|
||||
const safetyLevel = bestModel.safetyScore;
|
||||
if (safetyLevel < LOW_SAFETY_THRESHOLD && state.meta.tickCount % 60 === 0) {
|
||||
const incidentProb = SAFETY_INCIDENT_PROBABILITY_BASE * (LOW_SAFETY_THRESHOLD - safetyLevel);
|
||||
if (Math.random() < incidentProb) {
|
||||
safetyRecord = Math.max(0, safetyRecord - SAFETY_INCIDENT_REPUTATION_HIT);
|
||||
publicPerception = Math.max(0, publicPerception - SAFETY_INCIDENT_REPUTATION_HIT * 0.5);
|
||||
safetyIncident = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const eraIdx = ['startup', 'scaleup', 'bigtech', 'agi'].indexOf(state.meta.currentEra);
|
||||
const regulatoryPressure = eraIdx * 5;
|
||||
const safetyResearchCount = state.research.completedResearch
|
||||
.filter(r => r.includes('alignment') || r.includes('interpretability') || r.includes('constitutional')).length;
|
||||
const complianceBonus = safetyResearchCount * 8;
|
||||
regulatoryStanding = Math.min(100, Math.max(0,
|
||||
50 + complianceBonus - regulatoryPressure,
|
||||
));
|
||||
|
||||
const talentMorale = Object.values(state.talent.departments)
|
||||
.reduce((sum, d) => sum + d.morale, 0) / 4;
|
||||
employeeSatisfaction = talentMorale;
|
||||
|
||||
const score = Math.round(
|
||||
safetyRecord * 0.3 +
|
||||
@@ -19,9 +59,15 @@ export function processReputation(state: GameState): ReputationState {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
const result: ReputationState & { _safetyIncident?: boolean } = {
|
||||
...state.reputation,
|
||||
score,
|
||||
safetyRecord,
|
||||
publicPerception,
|
||||
employeeSatisfaction,
|
||||
regulatoryStanding,
|
||||
reputationHistory,
|
||||
};
|
||||
if (safetyIncident) result._safetyIncident = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user