diff --git a/apps/web/src/context/gameContext.tsx b/apps/web/src/context/gameContext.tsx index 1823fa0..a1efd91 100644 --- a/apps/web/src/context/gameContext.tsx +++ b/apps/web/src/context/gameContext.tsx @@ -462,12 +462,12 @@ interface GameContextValue { /** * Format a number using the player's chosen notation style. */ - formatNumber: (value: number)=> string; + formatNumber: (value: number, format?: NumberFormat)=> string; /** * Format a whole-number value without decimal places. */ - formatInteger: (value: number, format: NumberFormat)=> string; + formatInteger: (value: number, format?: NumberFormat)=> string; /** * Buy a prestige upgrade from the runestone shop. diff --git a/apps/web/src/utils/format.ts b/apps/web/src/utils/format.ts index f43d12d..1ed9b75 100644 --- a/apps/web/src/utils/format.ts +++ b/apps/web/src/utils/format.ts @@ -57,22 +57,32 @@ const getLetterSuffix = (index: number): string => { /** * Formats a number with a named or letter-based suffix. * @param value - The number to format. + * @param round - Display zero numbers after a decimal point. * @returns The formatted string with suffix. */ -const formatSuffix = (value: number): string => { +const formatSuffix = (value: number, round = false): string => { if (value >= Math.pow(10, letterBaseExp)) { const exp = Math.floor(Math.log10(value)); const stepsAboveBase = Math.floor((exp - letterBaseExp) / 3); const steps = stepsAboveBase * 3; const divisorExp = letterBaseExp + steps; const divisor = Math.pow(10, divisorExp); - return `${(value / divisor).toFixed(2)}${getLetterSuffix(stepsAboveBase)}`; + return `${round + ? String(Math.round(value / divisor)) + : (value / divisor).toFixed(2)}${getLetterSuffix(stepsAboveBase)}`; } for (const { threshold, suffix } of namedSuffixes) { if (value >= threshold) { - return `${(value / threshold).toFixed(2)}${suffix}`; + return `${round + ? String(Math.floor(value / threshold)) + : (value / threshold).toFixed(2)}${suffix}`; } } + + if (round) { + return String(Math.floor(value)); + } + return value < 1 ? value.toFixed(2) : value.toFixed(1); @@ -82,12 +92,18 @@ const formatSuffix = (value: number): string => { * Formats a number in scientific notation: e.g. 1.23e15. * Falls back to K/M/B/T style below 1 million. * @param value - The number to format. + * @param round - Display zero numbers after a decimal point. * @returns The formatted string in scientific notation. */ -const formatScientific = (value: number): string => { +const formatScientific = (value: number, round = false): string => { if (value < 1e6) { - return formatSuffix(value); + return formatSuffix(value, round); } + + if (round) { + return value.toExponential(0).replace("e+", "e"); + } + // ToExponential handles all magnitudes JS can represent (up to ~1.8e308) return value.toExponential(2).replace("e+", "e"); }; @@ -96,16 +112,19 @@ const formatScientific = (value: number): string => { * Formats a number in engineering notation (exponent always a multiple of 3): * e.g. 12.35E12, 1.23E300. Falls back to K/M/B/T style below 1 million. * @param value - The number to format. + * @param round - Display zero numbers after a decimal point. * @returns The formatted string in engineering notation. */ -const formatEngineering = (value: number): string => { +const formatEngineering = (value: number, round = false): string => { if (value < 1e6) { - return formatSuffix(value); + return formatSuffix(value, round); } const exp = Math.floor(Math.log10(value)); const engExp = Math.floor(exp / 3) * 3; const mantissa = value / Math.pow(10, engExp); - return `${mantissa.toFixed(2)}E${String(engExp)}`; + return `${round + ? String(Math.round(mantissa)) + : mantissa.toFixed(2)}E${String(engExp)}`; }; /** @@ -124,37 +143,19 @@ const formatInteger = (value: number, return `-${formatInteger(-value)}`; } - const flooredValue = Math.floor(value); + const roundedValue = Math.floor(value); switch (format) { case "scientific": - return formatScientific(flooredValue); + return formatScientific(roundedValue, true); case "engineering": - return formatEngineering(flooredValue); + return formatEngineering(value, true); case "suffix": - return formatSuffix(flooredValue); + return formatSuffix(value, true); default: { /* V8 ignore next -- @preserve */ - return formatSuffix(flooredValue); + return formatSuffix(value, true); } } - *If (value >= Math.pow(10, letterBaseExp)) { - * const exp = Math.floor(Math.log10(value)); - * const stepsAboveBase = Math.floor((exp - letterBaseExp) / 3); - * const steps = stepsAboveBase * 3; - * const divisorExp = letterBaseExp + steps; - * const divisor = Math.pow(10, divisorExp); - * return `${String(Math.round(value / divisor))}${getLetterSuffix(stepsAboveBase)}`; - *} - * - * - * - *for (const { threshold, suffix } of namedSuffixes) { - * if (value >= threshold) { - * return `${String(Math.floor(value / threshold))}${suffix}`; - * } - *} - *return String(Math.floor(value)); - */ }; /** diff --git a/apps/web/test/format.spec.ts b/apps/web/test/format.spec.ts index 313a2bf..c164896 100644 --- a/apps/web/test/format.spec.ts +++ b/apps/web/test/format.spec.ts @@ -197,108 +197,42 @@ describe("formatInteger", () => { }); }); - describe("suffix format (default)", () => { - it("should format small numbers with one decimal place", () => { - expect(formatInteger(999)).toBe("999.0"); - }); - - it("should format thousands with K suffix", () => { - expect(formatInteger(1000)).toBe("1.00K"); - }); - - it("should format millions with M suffix", () => { - expect(formatInteger(1_000_000)).toBe("1.00M"); - }); - - it("should format billions with B suffix", () => { - expect(formatInteger(1_000_000_000)).toBe("1.00B"); - }); - - it("should format trillions with T suffix", () => { - expect(formatInteger(1e12)).toBe("1.00T"); - }); - - it("should format quadrillions with Qa suffix", () => { - expect(formatInteger(1e15)).toBe("1.00Qa"); - }); - - it("should format quintillions with Qi suffix", () => { - expect(formatInteger(1e18)).toBe("1.00Qi"); - }); - - it("should format sextillions with Sx suffix", () => { - expect(formatInteger(1e21)).toBe("1.00Sx"); - }); - - it("should format septillions with Sp suffix", () => { - expect(formatInteger(1e24)).toBe("1.00Sp"); - }); - - it("should format octillions with Oc suffix", () => { - expect(formatInteger(1e27)).toBe("1.00Oc"); - }); - - it("should format nonillions with No suffix", () => { - expect(formatInteger(1e30)).toBe("1.00No"); - }); - - it("should format decillions with Dc suffix", () => { - expect(formatInteger(1e33)).toBe("1.00Dc"); - }); - - it("should format values >= 1e36 with letter suffix 'a'", () => { - expect(formatInteger(1e36)).toBe("1.00a"); - }); - - it("should format values >= 1e39 with letter suffix 'b'", () => { - expect(formatInteger(1e39)).toBe("1.00b"); - }); - - it("should format values at 26th letter step with 'z'", () => { - expect(formatInteger(1e36 * Math.pow(10, 25 * 3))).toBe("1.00z"); - }); - - it("should format values at 27th letter step with 'aa'", () => { - expect(formatInteger(1e36 * Math.pow(10, 26 * 3))).toBe("1.00aa"); - }); - }); - describe("scientific format", () => { it("should fall back to suffix format below 1e6", () => { - expect(formatInteger(500, "scientific")).toBe("500.0"); + expect(formatInteger(500, "scientific")).toBe("500"); }); it("should format values >= 1e6 in scientific notation", () => { - expect(formatInteger(1_230_000, "scientific")).toBe("1.23e6"); + expect(formatInteger(1_230_000, "scientific")).toBe("1e6"); }); it("should format large values in scientific notation", () => { - expect(formatInteger(1e18, "scientific")).toBe("1.00e18"); + expect(formatInteger(1e18, "scientific")).toBe("1e18"); }); }); describe("engineering format", () => { it("should fall back to suffix format below 1e6", () => { - expect(formatInteger(500, "engineering")).toBe("500.0"); + expect(formatInteger(500, "engineering")).toBe("500"); }); it("should format values >= 1e6 with exponent multiple of 3", () => { - expect(formatInteger(1_230_000, "engineering")).toBe("1.23E6"); + expect(formatInteger(1_230_000, "engineering")).toBe("1E6"); }); it("should format 1e9 correctly in engineering notation", () => { - expect(formatInteger(1e9, "engineering")).toBe("1.00E9"); + expect(formatInteger(1e9, "engineering")).toBe("1E9"); }); it("should format 12350000 correctly in engineering notation", () => { - expect(formatInteger(12_350_000, "engineering")).toBe("12.35E6"); + expect(formatInteger(12_350_000, "engineering")).toBe("12E6"); }); }); describe("unknown format (default branch)", () => { it("should fall back to suffix format for an unrecognised format string", () => { /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Testing unreachable default branch */ - expect(formatInteger(1000, "unknown" as never)).toBe("1.00K"); + expect(formatInteger(1000, "unknown" as never)).toBe("1K"); }); }); });