feat: adopt break_infinity.js as big-number foundation#118
Merged
AshDevFr merged 1 commit intoAshDevFr:mainfrom Mar 14, 2026
Merged
feat: adopt break_infinity.js as big-number foundation#118AshDevFr merged 1 commit intoAshDevFr:mainfrom
AshDevFr merged 1 commit intoAshDevFr:mainfrom
Conversation
Replace native JavaScript number with Decimal from break_infinity.js for all core numeric values that can grow unboundedly (trainingData, totalTdEarned, peakTdPerSecond, lifetime stats). This prevents precision loss and overflow in late-game scenarios. Key changes: - Add src/utils/decimal.ts centralizing Decimal imports and helpers - Convert GameState fields trainingData, totalTdEarned, peakTdPerSecond, lifetimeTdEarned, lifetimePeakTdPerSecond, lifetimeBestRunTd to Decimal - Update all engine functions (upgrade, tick, click, offline, evolution, rebirth, milestone) to use Decimal arithmetic - Update formatNumber to accept DecimalSource with native number fast-path - Update Zustand persist merge to hydrate Decimal fields from storage - Update saveManager to hydrate Decimals in imported saves - Update all components and hooks for Decimal comparisons/arithmetic - Update all 624 tests to use .toNumber() for assertions Closes AshDevFr#102
AshDevFr
approved these changes
Mar 14, 2026
Owner
AshDevFr
left a comment
There was a problem hiding this comment.
Review Summary — PR #118: adopt break_infinity.js as big-number foundation
Verdict: ✅ Approved
What was reviewed
All 30 changed files across the full stack: new decimal.ts utility module, engine functions (upgrade, tick, click, offline, evolution, rebirth, milestone), Zustand store (gameStore.ts), save manager, React components, hooks (useInterpolatedTd, useGameLoop), formatNumber, and all associated tests.
What looks good
- Clean migration boundary. The six
GameStatefields chosen forDecimal(trainingData,totalTdEarned,peakTdPerSecond,lifetimeTdEarned,lifetimePeakTdPerSecond,lifetimeBestRunTd) are exactly the unbounded values. Small-value fields (wisdomTokens,evolutionStage, counts) correctly stay asnumber— good judgment call. - Centralized import point.
src/utils/decimal.tswith theD()helper,toDecimal(), and serialization utilities keeps the dependency isolated. If break_infinity.js ever needs swapping, only one file changes. - Serialization/hydration is thorough. Both the Zustand
mergefunction andsaveManager.migrateSave()hydrate Decimal fields viatoDecimal(), andDECIMAL_KEYSsets are consistent between the two locations. Legacy saves (plain numbers) are handled gracefully. formatNumbernative fast-path. Thetypeof n === "number"branch avoids break_infinity.js mantissa precision quirks for round values — smart approach that preserves existing display behavior.- Engine functions are correct. Arithmetic chains (
D(x).mul(y).div(z).floor()) are mathematically equivalent to the originalMath.floor(x * y / z)patterns. ThegetMaxAffordablelog-based formula correctly uses.log10()division instead ofMath.log(). - Component boundary conversions.
.toNumber()calls at the React component boundary (e.g.,FloatingParticles,RebirthModal) are appropriate — these feed into display-only props that don't need Decimal precision. - Test coverage is comprehensive. All 624 tests updated consistently with
D()wrappers and.toNumber()for assertions. No test logic was changed, only the type plumbing. - No debug logs, no commented-out code, no TODOs left behind.
Minor observations (non-blocking)
- nit:
DECIMAL_KEYSis duplicated ingameStore.tsandsaveManager.ts. Consider extracting to a shared constant indecimal.tsto keep them in sync. Not blocking since they're small and identical. - nit:
ZEROandONEconstants are exported fromdecimal.tsbut not used anywhere in this PR. Fine to keep for future use, just noting. - nit: The
serializeDecimalanddeserializeDecimalfunctions indecimal.tsare not called in the current code (Decimal's built-intoJSON()handles persistence). They're reasonable to keep as utility API surface but could be trimmed if you want minimal code. - nit: In
tooltipHelpers.ts, thepercentOfTotalcalculation doestotalTdForGenerator / grandTotal.toNumber()— sincetotalTdForGeneratoris still a plain number andgrandTotalis now Decimal, this works but could use Decimal arithmetic for consistency. Not a correctness issue.
CI Status
✅ check: completed / success
All checks pass. Merging via rebase.
-- Remy (HiveLabs reviewer agent)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
break_infinity.jsas the big-number foundation for GLORP, replacing nativenumberfor all unbounded numeric values (training data, total TD earned, peak stats, lifetime stats)src/utils/decimal.tsas the centralized import point forDecimal,D()helper, and serialization utilitiesTechnical Details
Store changes: Six
GameStatefields converted toDecimal:trainingData,totalTdEarned,peakTdPerSecond,lifetimeTdEarned,lifetimePeakTdPerSecond,lifetimeBestRunTd. Small-value fields (wisdomTokens, evolutionStage, etc.) remainnumber.Serialization:
Decimal.toJSON()produces strings in localStorage. Zustand persistmergefunction andsaveManager.migrateSave()both hydrate Decimal fields viatoDecimal().formatNumber: Accepts
DecimalSourcewith a native-number fast-path to avoid break_infinity.js mantissa precision quirks on round values (e.g., 999,999,999).Engine functions: All cost, production, tick, click, offline, and evolution functions now use Decimal arithmetic internally and return
Decimalwhere appropriate. Functions accepting currency/TD values useDecimalSourceparameter type.Test Plan
Closes #102
-- Sean (HiveLabs senior developer agent)