Overhaul market system with shared TAM competition, multi-tier pricing, enterprise pipeline, and developer ecosystem
CI / build-and-push (push) Successful in 42s

Replaces the simplified single-subscriber market with a full competitive simulation:
shared TAM with softmax market shares across 4 segments, multi-tier consumer
subscriptions (Free/Plus/Pro/Team) and API tiers (Free/PAYG/Scale/Enterprise),
enterprise sales pipeline (Lead→Qualification→POC→Negotiation→Active→Renewal)
with SLA tracking, developer ecosystem flywheel, technology obsolescence pressure,
seasonal demand cycles, and two new product lines (Code Assistant, AI Agents Platform).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 08:30:24 -04:00
parent 4c1c0e9ff2
commit 09a5cb69a7
34 changed files with 2851 additions and 408 deletions
@@ -0,0 +1,97 @@
import type { ApiTierState, ApiTierId, DeveloperEcosystem } from '@ai-tycoon/shared';
import {
API_TIER_ORDER,
API_CONVERSION_RATES,
API_TIER_CHURN_RATES,
API_TOKENS_PER_DEVELOPER_PER_TICK,
} from '@ai-tycoon/shared';
export interface ApiTickResult {
apiTiers: ApiTierState;
apiRevenue: number;
totalApiTokenDemand: number;
}
export function processApiTiers(
tiers: ApiTierState,
playerDevCustomers: number,
modelQuality: number,
seasonalApiMultiplier: number,
ecosystem: DeveloperEcosystem,
): ApiTickResult {
const updated: ApiTierState = {
tiers: { ...tiers.tiers },
totalDevelopers: 0,
totalTokensPerTick: 0,
};
for (const id of API_TIER_ORDER) {
updated.tiers[id] = { ...tiers.tiers[id], config: { ...tiers.tiers[id].config } };
}
if (modelQuality <= 0) {
return { apiTiers: updated, apiRevenue: 0, totalApiTokenDemand: 0 };
}
const targetFreeDevelopers = playerDevCustomers * 0.1 * seasonalApiMultiplier;
const freeGrowth = (targetFreeDevelopers - updated.tiers.free.developerCount) * 0.03;
updated.tiers.free.developerCount = Math.max(0, updated.tiers.free.developerCount + freeGrowth);
const freeChurn = updated.tiers.free.developerCount * API_TIER_CHURN_RATES.free;
updated.tiers.free.developerCount = Math.max(0, updated.tiers.free.developerCount - freeChurn);
updated.tiers.free.churnRate = API_TIER_CHURN_RATES.free;
const prevTierMap: Record<ApiTierId, ApiTierId | null> = {
free: null,
payg: 'free',
scale: 'payg',
'enterprise-api': 'scale',
};
for (const id of API_TIER_ORDER) {
if (id === 'free') continue;
const tier = updated.tiers[id];
if (!tier.config.isActive) continue;
const prevId = prevTierMap[id];
if (!prevId) continue;
const prevTier = updated.tiers[prevId];
const convKey = `${prevId}->${id}`;
const baseRate = API_CONVERSION_RATES[convKey] ?? 0;
const ecosystemBoost = 1 + ecosystem.ecosystemScore / 200;
const convRate = baseRate * Math.max(0.1, modelQuality) * ecosystemBoost * seasonalApiMultiplier;
const converting = prevTier.developerCount * convRate;
prevTier.developerCount = Math.max(0, prevTier.developerCount - converting);
tier.developerCount += converting;
tier.churnRate = API_TIER_CHURN_RATES[id];
const churned = tier.developerCount * tier.churnRate;
tier.developerCount = Math.max(0, tier.developerCount - churned);
}
let totalDevelopers = 0;
let totalTokens = 0;
let apiRevenue = 0;
for (const id of API_TIER_ORDER) {
const tier = updated.tiers[id];
totalDevelopers += tier.developerCount;
const tokensPerDev = API_TOKENS_PER_DEVELOPER_PER_TICK[id];
tier.tokensPerTick = tier.developerCount * tokensPerDev;
totalTokens += tier.tokensPerTick;
apiRevenue += tier.developerCount * (tier.config.monthlyFee / 86400);
apiRevenue += (tier.tokensPerTick / 1_000_000) * tier.config.outputTokenPrice;
}
updated.totalDevelopers = totalDevelopers;
updated.totalTokensPerTick = totalTokens;
return {
apiTiers: updated,
apiRevenue: Math.max(0, apiRevenue),
totalApiTokenDemand: totalTokens,
};
}