Fix consumer subscription pricing exploit with perceived-value-based elasticity
Players could set astronomical prices and still retain subscribers because price elasticity floored at 10% for any price above $100, satisfaction ignored pricing entirely, and churn had no price component. Introduces perceived value per tier (model quality × reputation), replaces the broken linear formula with sigmoid decay, adds price-aware satisfaction blending, and applies per-tier price-based churn multipliers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ import {
|
||||
ATTRACTIVENESS_WEIGHTS,
|
||||
OBSOLESCENCE_PENALTY_WEIGHT,
|
||||
NEW_MODEL_BOOST_VALUE,
|
||||
CONSUMER_TIER_BASE_PERCEIVED_VALUE,
|
||||
PERCEIVED_VALUE_REPUTATION_RANGE,
|
||||
} from '@ai-tycoon/shared';
|
||||
|
||||
export interface ParticipantProfile {
|
||||
@@ -27,6 +29,16 @@ export interface ParticipantProfile {
|
||||
hasFreeTier: boolean;
|
||||
}
|
||||
|
||||
function computeTamPriceScore(price: number, qualityScore: number, reputation: number): number {
|
||||
if (price <= 0) return 1;
|
||||
const repRange = PERCEIVED_VALUE_REPUTATION_RANGE;
|
||||
const repMult = repRange.min + (Math.max(0, Math.min(100, reputation)) / 100) * (repRange.max - repRange.min);
|
||||
const refPV = CONSUMER_TIER_BASE_PERCEIVED_VALUE.plus * qualityScore * repMult;
|
||||
if (refPV <= 0) return 0;
|
||||
const ratio = price / refPV;
|
||||
return 1 / (1 + ratio * ratio);
|
||||
}
|
||||
|
||||
export function buildPlayerProfile(
|
||||
qualityScore: number,
|
||||
chatPrice: number,
|
||||
@@ -36,8 +48,7 @@ export function buildPlayerProfile(
|
||||
obsolescence: ObsolescenceState,
|
||||
hasFreeTier: boolean,
|
||||
): ParticipantProfile {
|
||||
const avgPrice = (chatPrice + apiOutputPrice) / 2;
|
||||
const priceScore = Math.max(0, Math.min(1, 1 - avgPrice / 100));
|
||||
const priceScore = computeTamPriceScore(chatPrice, qualityScore, reputation);
|
||||
|
||||
let freshness = obsolescence.playerModelFreshness;
|
||||
if (obsolescence.newModelBoostRemaining > 0) {
|
||||
@@ -56,8 +67,8 @@ export function buildPlayerProfile(
|
||||
}
|
||||
|
||||
export function buildCompetitorProfile(c: Competitor): ParticipantProfile {
|
||||
const avgPrice = (c.products.chatPrice + c.products.apiOutputPrice) / 2;
|
||||
const priceScore = Math.max(0, Math.min(1, 1 - avgPrice / 100));
|
||||
const compQuality = Math.min(1, c.estimatedCapability / 100);
|
||||
const priceScore = computeTamPriceScore(c.products.chatPrice, compQuality, c.reputation);
|
||||
|
||||
return {
|
||||
id: c.id,
|
||||
|
||||
Reference in New Issue
Block a user