fix: rewrite prestige/transcendence formula and rebalance progression
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m8s
CI / Lint, Build & Test (pull_request) Successful in 1m11s

This commit is contained in:
2026-03-24 20:44:25 -07:00
committed by Naomi Carrigan
parent 0d6d05e50b
commit 0ae6aa12b2
7 changed files with 98 additions and 88 deletions
+1 -1
View File
@@ -158,7 +158,7 @@ describe("transcendence route", () => {
const res = await post("/buy-upgrade", { upgradeId: "echo_income_1" });
expect(res.status).toBe(200);
const body = await res.json() as { echoesRemaining: number; purchasedUpgradeIds: string[] };
expect(body.echoesRemaining).toBe(95); // 100 - 5
expect(body.echoesRemaining).toBe(98); // 100 - 2
expect(body.purchasedUpgradeIds).toContain("echo_income_1");
});
+10 -7
View File
@@ -55,15 +55,18 @@ const makeMinimalState = (overrides: Partial<GameState> = {}): GameState =>
describe("calculatePrestigeThreshold", () => {
it("returns base threshold at count 0", () => {
// base × (0+1)^2 = 1_000_000 × 1 = 1_000_000
expect(calculatePrestigeThreshold(0)).toBe(1_000_000);
});
it("returns 5× at count 1", () => {
expect(calculatePrestigeThreshold(1)).toBe(5_000_000);
it("returns 4× base at count 1", () => {
// base × (1+1)^2 = 1_000_000 × 4 = 4_000_000
expect(calculatePrestigeThreshold(1)).toBe(4_000_000);
});
it("returns 25× at count 2", () => {
expect(calculatePrestigeThreshold(2)).toBe(25_000_000);
it("returns 9× base at count 2", () => {
// base × (2+1)^2 = 1_000_000 × 9 = 9_000_000
expect(calculatePrestigeThreshold(2)).toBe(9_000_000);
});
it("applies threshold multiplier correctly", () => {
@@ -128,12 +131,12 @@ describe("calculateProductionMultiplier", () => {
expect(calculateProductionMultiplier(0)).toBe(1);
});
it("returns 1.15 at count 1", () => {
expect(calculateProductionMultiplier(1)).toBeCloseTo(1.15);
it("returns 1.25 at count 1", () => {
expect(calculateProductionMultiplier(1)).toBeCloseTo(1.25);
});
it("scales exponentially", () => {
expect(calculateProductionMultiplier(10)).toBeCloseTo(Math.pow(1.15, 10));
expect(calculateProductionMultiplier(10)).toBeCloseTo(Math.pow(1.25, 10));
});
});
+11 -5
View File
@@ -97,20 +97,21 @@ describe("isEligibleForTranscendence", () => {
describe("calculateEchoes", () => {
it("handles prestige count of 0 by treating it as 1", () => {
// safeCount = max(0, 1) = 1; floor(853 / sqrt(1)) = 853
expect(calculateEchoes(0, 1)).toBe(853);
// safeCount = max(0, 1) = 1; floor(224 / sqrt(1)) = 224
expect(calculateEchoes(0, 1)).toBe(224);
});
it("calculates echoes at count 1", () => {
expect(calculateEchoes(1, 1)).toBe(853);
// floor(224 / sqrt(1)) = 224
expect(calculateEchoes(1, 1)).toBe(224);
});
it("decreases echoes with higher prestige count", () => {
const echoesAt1 = calculateEchoes(1, 1);
const echoesAt4 = calculateEchoes(4, 1);
expect(echoesAt4).toBeLessThan(echoesAt1);
// floor(853 / sqrt(4)) = floor(853 / 2) = 426
expect(echoesAt4).toBe(426);
// floor(224 / sqrt(4)) = floor(224 / 2) = 112
expect(echoesAt4).toBe(112);
});
it("applies echoMetaMultiplier", () => {
@@ -118,6 +119,11 @@ describe("calculateEchoes", () => {
const withMult = calculateEchoes(1, 2);
expect(withMult).toBe(base * 2);
});
it("returns 50 echoes at the target prestige 20", () => {
// floor(224 / sqrt(20)) = floor(224 / 4.472) = floor(50.09) = 50
expect(calculateEchoes(20, 1)).toBe(50);
});
});
describe("buildPostTranscendenceState", () => {