Skip to content

feat: adopt break_infinity.js as big-number foundation#118

Merged
AshDevFr merged 1 commit intoAshDevFr:mainfrom
4sh-dev:feature/break-infinity-migration
Mar 14, 2026
Merged

feat: adopt break_infinity.js as big-number foundation#118
AshDevFr merged 1 commit intoAshDevFr:mainfrom
4sh-dev:feature/break-infinity-migration

Conversation

@4sh-dev
Copy link
Collaborator

@4sh-dev 4sh-dev commented Mar 14, 2026

Summary

  • Adds break_infinity.js as the big-number foundation for GLORP, replacing native number for all unbounded numeric values (training data, total TD earned, peak stats, lifetime stats)
  • Creates src/utils/decimal.ts as the centralized import point for Decimal, D() helper, and serialization utilities
  • Updates all engine functions (upgrade, tick, click, offline, evolution, rebirth, milestone), the Zustand game store, save manager, components, hooks, and all 624 tests

Technical Details

Store changes: Six GameState fields converted to Decimal: trainingData, totalTdEarned, peakTdPerSecond, lifetimeTdEarned, lifetimePeakTdPerSecond, lifetimeBestRunTd. Small-value fields (wisdomTokens, evolutionStage, etc.) remain number.

Serialization: Decimal.toJSON() produces strings in localStorage. Zustand persist merge function and saveManager.migrateSave() both hydrate Decimal fields via toDecimal().

formatNumber: Accepts DecimalSource with 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 Decimal where appropriate. Functions accepting currency/TD values use DecimalSource parameter type.

Test Plan

  • All 624 existing tests pass
  • Biome lint clean
  • TypeScript build clean
  • formatNumber handles edge cases (0, fractional, large numbers, negative)
  • Save/load round-trips Decimal fields correctly

Closes #102

-- Sean (HiveLabs senior developer agent)

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
Copy link
Owner

@AshDevFr AshDevFr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 GameState fields chosen for Decimal (trainingData, totalTdEarned, peakTdPerSecond, lifetimeTdEarned, lifetimePeakTdPerSecond, lifetimeBestRunTd) are exactly the unbounded values. Small-value fields (wisdomTokens, evolutionStage, counts) correctly stay as number — good judgment call.
  • Centralized import point. src/utils/decimal.ts with the D() 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 merge function and saveManager.migrateSave() hydrate Decimal fields via toDecimal(), and DECIMAL_KEYS sets are consistent between the two locations. Legacy saves (plain numbers) are handled gracefully.
  • formatNumber native fast-path. The typeof 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 original Math.floor(x * y / z) patterns. The getMaxAffordable log-based formula correctly uses .log10() division instead of Math.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)

  1. nit: DECIMAL_KEYS is duplicated in gameStore.ts and saveManager.ts. Consider extracting to a shared constant in decimal.ts to keep them in sync. Not blocking since they're small and identical.
  2. nit: ZERO and ONE constants are exported from decimal.ts but not used anywhere in this PR. Fine to keep for future use, just noting.
  3. nit: The serializeDecimal and deserializeDecimal functions in decimal.ts are not called in the current code (Decimal's built-in toJSON() handles persistence). They're reasonable to keep as utility API surface but could be trimmed if you want minimal code.
  4. nit: In tooltipHelpers.ts, the percentOfTotal calculation does totalTdForGenerator / grandTotal.toNumber() — since totalTdForGenerator is still a plain number and grandTotal is 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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Adopt big-number library (break_infinity.js) to handle numbers beyond JS safe integer range

2 participants