Add Week 2 depth systems: research, events, competitors, talent, data

Tech tree with 21 research nodes across 5 categories (infrastructure,
efficiency, generation, specialization, safety). Research page with
category-grouped cards, progress tracking, prerequisite gating.

Event engine with 34 events across industry/regulatory/PR/internal/market
categories, weighted random firing, cooldowns, expiry, and choice modal
with consequence preview. Events auto-expire with default choice.

Competitor system with 3 rival AI labs (Prometheus AI, Nexus Labs, Titan
Computing), personality-driven milestone progression, and comparison UI.

Talent page with department hiring, headcount management, and key hire
recruitment from a pool of 10 named characters with special abilities.

Data marketplace with 8 purchasable datasets, user data flywheel from
subscribers, and data system processing in tick loop.

Era transition system checks revenue/capability/reputation thresholds.
All new systems integrated into tick processor with notifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 17:30:24 -04:00
parent d1d3eb4bf2
commit 8c9555bc08
19 changed files with 3166 additions and 21 deletions
@@ -0,0 +1,51 @@
import type { GameState, CompetitorState } from '@ai-tycoon/shared';
export function processCompetitors(state: GameState): CompetitorState {
const tick = state.meta.tickCount;
const rivals = state.competitors.rivals.map(rival => {
if (rival.status !== 'active') return rival;
if (tick < rival.nextMilestoneAtTick) return rival;
const { personality } = rival;
const capGrowth = (2 + personality.researchFocus * 5 + personality.riskTolerance * 3) *
(1 + tick * 0.00005);
const revenueGrowth = rival.estimatedRevenue * (0.02 + personality.marketingFocus * 0.03);
const userGrowth = rival.estimatedUsers * (0.01 + personality.marketingFocus * 0.02);
const newCapability = Math.min(95, rival.estimatedCapability + capGrowth);
const newRevenue = rival.estimatedRevenue + revenueGrowth + 50;
const newUsers = rival.estimatedUsers + userGrowth + 100;
const repChange = personality.safetyFocus > 0.6
? 1
: personality.riskTolerance > 0.7 ? -1 : 0;
const modelNames = [
'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon',
'Nova', 'Quantum', 'Nexus', 'Apex', 'Zenith',
];
const modelIdx = Math.floor(newCapability / 10);
const latestModelName = `${rival.name.split(' ')[0]}-${modelNames[Math.min(modelIdx, modelNames.length - 1)]}`;
const milestoneInterval = 200 + Math.floor(Math.random() * 200);
return {
...rival,
estimatedCapability: newCapability,
estimatedRevenue: newRevenue,
estimatedUsers: Math.floor(newUsers),
reputation: Math.min(100, Math.max(0, rival.reputation + repChange)),
latestModelName,
nextMilestoneAtTick: tick + milestoneInterval,
};
});
const allCaps = [
...rivals.filter(r => r.status === 'active').map(r => r.estimatedCapability),
state.models.trainedModels.reduce((best, m) => Math.max(best, m.benchmarkScore), 0),
];
const industryBenchmark = allCaps.length > 0 ? Math.max(...allCaps) : 0;
return { rivals, industryBenchmark };
}