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:
@@ -1559,4 +1559,237 @@ export const EVENT_DEFINITIONS: EventDefinition[] = [
|
||||
defaultChoiceIndex: 0,
|
||||
expiryTicks: 360,
|
||||
},
|
||||
|
||||
// ============================================================
|
||||
// GEOPOLITICAL EVENTS (6)
|
||||
// ============================================================
|
||||
{
|
||||
id: 'geo_export_controls',
|
||||
title: 'New AI Export Controls',
|
||||
descriptionTemplate:
|
||||
'The government has announced export controls on advanced AI chips and models. International operations may be affected.',
|
||||
category: 'regulatory',
|
||||
eras: ['scaleup', 'bigtech', 'agi'],
|
||||
weight: 6,
|
||||
cooldownTicks: 1200,
|
||||
maxOccurrences: 2,
|
||||
prerequisites: [],
|
||||
conditions: [{ field: 'models.trainedModels.length', operator: 'gte', value: 2 }],
|
||||
choices: [
|
||||
{
|
||||
label: 'Lobby for exemptions',
|
||||
description: 'Spend money to lobby policymakers for carve-outs.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -100000 },
|
||||
{ type: 'regulation', value: 10 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Comply and adapt',
|
||||
description: 'Accept the restrictions and restructure international operations.',
|
||||
consequences: [
|
||||
{ type: 'reputation', value: 5 },
|
||||
{ type: 'regulation', value: 5 },
|
||||
{ type: 'money', value: -30000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Challenge in court',
|
||||
description: 'File a legal challenge. Risky but could set favorable precedent.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -200000 },
|
||||
{ type: 'regulation', value: -10 },
|
||||
{ type: 'reputation', value: -5 },
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultChoiceIndex: 1,
|
||||
expiryTicks: 300,
|
||||
},
|
||||
{
|
||||
id: 'geo_energy_crisis',
|
||||
title: 'Energy Crisis in Data Center Region',
|
||||
descriptionTemplate:
|
||||
'A regional energy crisis is driving electricity costs up 40% in key data center locations. Your infrastructure costs are spiking.',
|
||||
category: 'market',
|
||||
eras: ['scaleup', 'bigtech', 'agi'],
|
||||
weight: 5,
|
||||
cooldownTicks: 1500,
|
||||
maxOccurrences: 2,
|
||||
prerequisites: [],
|
||||
conditions: [{ field: 'infrastructure.dataCenters.length', operator: 'gte', value: 1 }],
|
||||
choices: [
|
||||
{
|
||||
label: 'Negotiate long-term energy contracts',
|
||||
description: 'Lock in rates now before they go higher. Requires upfront capital.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -150000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Invest in on-site renewable energy',
|
||||
description: 'Install solar panels and battery storage. High upfront cost, long-term savings.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -300000 },
|
||||
{ type: 'reputation', value: 10 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Absorb the costs',
|
||||
description: 'Take the hit and hope prices normalize.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -50000 },
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultChoiceIndex: 2,
|
||||
expiryTicks: 300,
|
||||
},
|
||||
{
|
||||
id: 'geo_natural_disaster',
|
||||
title: 'Natural Disaster Threatens Data Center',
|
||||
descriptionTemplate:
|
||||
'Severe weather has been reported near one of your data center locations. Your ops team is preparing contingency plans.',
|
||||
category: 'industry',
|
||||
eras: ['startup', 'scaleup', 'bigtech', 'agi'],
|
||||
weight: 4,
|
||||
cooldownTicks: 1800,
|
||||
maxOccurrences: 3,
|
||||
prerequisites: [],
|
||||
conditions: [{ field: 'infrastructure.dataCenters.length', operator: 'gte', value: 1 }],
|
||||
choices: [
|
||||
{
|
||||
label: 'Activate disaster recovery',
|
||||
description: 'Failover to backup systems. Costs money but protects uptime.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -50000 },
|
||||
{ type: 'reputation', value: 3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Ride it out',
|
||||
description: 'Hope the storm misses. Save money but risk downtime.',
|
||||
consequences: [
|
||||
{ type: 'reputation', value: -8 },
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultChoiceIndex: 0,
|
||||
expiryTicks: 180,
|
||||
},
|
||||
{
|
||||
id: 'geo_ai_safety_summit',
|
||||
title: 'International AI Safety Summit',
|
||||
descriptionTemplate:
|
||||
'World leaders are convening a summit on AI safety. You have been invited to present your company\'s approach to responsible AI.',
|
||||
category: 'regulatory',
|
||||
eras: ['scaleup', 'bigtech', 'agi'],
|
||||
weight: 6,
|
||||
cooldownTicks: 1500,
|
||||
maxOccurrences: 2,
|
||||
prerequisites: [],
|
||||
conditions: [{ field: 'reputation.score', operator: 'gte', value: 30 }],
|
||||
choices: [
|
||||
{
|
||||
label: 'Sign voluntary safety commitments',
|
||||
description: 'Join the safety pledge. Good PR, but constrains future development speed.',
|
||||
consequences: [
|
||||
{ type: 'reputation', value: 15 },
|
||||
{ type: 'regulation', value: 10 },
|
||||
{ type: 'research_speed', value: -0.05 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Attend but make no commitments',
|
||||
description: 'Show up, listen, and keep your options open.',
|
||||
consequences: [
|
||||
{ type: 'reputation', value: 3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Skip the summit',
|
||||
description: 'Your engineers have models to train. No time for politics.',
|
||||
consequences: [
|
||||
{ type: 'reputation', value: -10 },
|
||||
{ type: 'regulation', value: -5 },
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultChoiceIndex: 1,
|
||||
expiryTicks: 240,
|
||||
},
|
||||
{
|
||||
id: 'geo_talent_immigration',
|
||||
title: 'Immigration Policy Changes',
|
||||
descriptionTemplate:
|
||||
'New immigration restrictions are making it harder to recruit international AI talent. Your HR team is concerned about pipeline impact.',
|
||||
category: 'regulatory',
|
||||
eras: ['scaleup', 'bigtech'],
|
||||
weight: 4,
|
||||
cooldownTicks: 1200,
|
||||
maxOccurrences: 1,
|
||||
prerequisites: [],
|
||||
conditions: [],
|
||||
choices: [
|
||||
{
|
||||
label: 'Open a remote research lab abroad',
|
||||
description: 'Set up a satellite research office in a talent-friendly country.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -200000 },
|
||||
{ type: 'talent', value: 5, target: 'research' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Increase domestic salaries',
|
||||
description: 'Compete harder for local talent with premium compensation.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -100000 },
|
||||
{ type: 'talent', value: 2, target: 'research' },
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultChoiceIndex: 1,
|
||||
expiryTicks: 360,
|
||||
},
|
||||
{
|
||||
id: 'geo_data_sovereignty',
|
||||
title: 'Data Sovereignty Regulations',
|
||||
descriptionTemplate:
|
||||
'Multiple countries are enacting data localization laws. User data must be stored within national borders, complicating your global infrastructure.',
|
||||
category: 'regulatory',
|
||||
eras: ['bigtech', 'agi'],
|
||||
weight: 5,
|
||||
cooldownTicks: 1500,
|
||||
maxOccurrences: 1,
|
||||
prerequisites: [],
|
||||
conditions: [{ field: 'market.consumers.totalSubscribers', operator: 'gte', value: 5000 }],
|
||||
choices: [
|
||||
{
|
||||
label: 'Build regional data centers',
|
||||
description: 'Comply by deploying infrastructure in affected regions. Expensive but thorough.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -500000 },
|
||||
{ type: 'regulation', value: 15 },
|
||||
{ type: 'reputation', value: 5 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Withdraw from those markets',
|
||||
description: 'Stop serving users in affected regions to avoid compliance costs.',
|
||||
consequences: [
|
||||
{ type: 'reputation', value: -10 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Implement data partitioning',
|
||||
description: 'Use technical solutions to segment data by region without new DCs.',
|
||||
consequences: [
|
||||
{ type: 'money', value: -100000 },
|
||||
{ type: 'regulation', value: 5 },
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultChoiceIndex: 2,
|
||||
expiryTicks: 360,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,15 @@ export function processTick(state: GameState): Partial<GameState> {
|
||||
});
|
||||
}
|
||||
|
||||
const reputation = processReputation(stateWithTalent);
|
||||
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);
|
||||
const data = processData(stateWithTalent);
|
||||
const competitors = processCompetitors(stateWithTalent);
|
||||
|
||||
Reference in New Issue
Block a user