Skip to content

V5 save + Mag log-magnitude: unbounded play, orphan fix, rebalance#28

Merged
flipbit03 merged 2 commits into
mainfrom
fix/orphan-tree-nodes
May 16, 2026
Merged

V5 save + Mag log-magnitude: unbounded play, orphan fix, rebalance#28
flipbit03 merged 2 commits into
mainfrom
fix/orphan-tree-nodes

Conversation

@flipbit03
Copy link
Copy Markdown
Owner

Summary

  • Mag log-magnitude type (src/bignum.rs) stores log10(value) in an f64. Replaces every gameplay-side counter that compounds multiplicatively (cuques, FPS, costs, tree aggregate multipliers, persisted MulFactor modifiers). Kills the f64-Infinity bug class that broke a long-haul save in triage — cuques saturated to 0, 311-trillion-x PurpleCoin modifiers, 58x-amplified GreenCoin AddPercents.
  • V5 save schema (src/save/versions/v5.rs) with Mag-typed counters. V4 frozen schema untouched; From<GameStateV4> converts f64 → Mag at the migration boundary and collapses any leftover Inf/NaN to Mag::ZERO. Untagged serde shim on Mag accepts both legacy raw JSON numbers and {"log10": x} (huge values past f64::MAX), so V4-shaped saves keep loading and small V5 values still write as plain numbers.
  • Tree orphan fix: new reaches_origin predicate requires a strictly-decreasing-manhattan chain of populated neighbors back to origin. anchor_of's pass-3 fallback was producing closed 2-node islands (the reported "Halo of the Heel / Bless Matins at -6,-17"). Thread-local memo keeps it O(lots visited). New 40-radius BFS test asserts zero orphans (was 50+ across that region before).
  • Procgen rebalance: per-node boon/bane magnitudes ~1000× smaller across the board; multiplicative ops (MulFactor / EffectMul / SpawnRateMul / CostMul) another 100× tighter on top so a 100k-deep stack stays in single-digit-× multiplier territory (was overflowing f64 around stack 1.7k). AddPercent / FlatAdd kept at 1000× because they're linear, not compounding. NODE_COST_GROWTH 1.75 → 2.5 stretches the cost ramp.
  • Infinite alphabetic suffix tail past Decillion in format::big_mag (aa..zz, Aa..Zz, aaa..zzz, …). Precision-aware mul_magnitude / percent_magnitude / flat_magnitude helpers so per-node ×1.0000004 renders with its meaningful digits in the info pane instead of collapsing to ×1.00. ? is now strictly an Inf/NaN sentinel.
  • CI gate widened: cargo clippy --all-targets -- -D warnings (was lib-only), so test-side lint regressions don't slip past CI in future PRs.

Test plan

  • CI green: cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test, the 4-target build matrix, wasm32 check.
  • Smoke test on a fresh save (TUI): buy fingerers, open tree, buy a connected node, refund it back. Info pane shows tiny per-node values like ×1.00000145 effect on Frenzy reward.
  • Stress test on the V4 broken save (the triage fixture, removed from repo but preserved in commit history): loads cleanly under V5, gameplay accrues into Mag-space numbers in alpha-suffix territory (oo / dm / bq), no ? rendered anywhere.
  • Long playtest: 100+ stacked PurpleCoin Buffs on random fingerers. FPS spikes to ~5T, decays cleanly as buffs expire over 30s, no ? rendered.
  • Cross-version save load: V1 / V2 / V3 / V4 saves all migrate cleanly to V5. New unit test v4_json_with_plain_number_fields_still_loads_under_v5 covers the wire-format compatibility.
  • Huge-Mag round-trip: state with cuques.log10 > 300 serializes (struct form) and reloads (struct form accepted by V5 schema) without falling back to default(). Regression test for the V5-blocker found in adversarial review.
  • Prestige earned matches legacy floor(sqrt(lifetime/1e6)) at integer-tier boundaries — lifetime=25M → 5, 81M → 9, etc. Regression test for an off-by-one round-trip-precision bug found in review.

flipbit03 added 2 commits May 16, 2026 14:08
Replaces the f64-typed gameplay counters with a `Mag` log-magnitude type
so end-game stacks no longer overflow to `Infinity`. The runaway-mult bug
class that broke a long-haul save in triage (cuques=0 from a saved Inf,
311-trillion-x PurpleCoin modifiers, 58x amplified GreenCoin AddPercents)
is structurally eliminated.

Mag bignum (src/bignum.rs):
- `Mag { log10: f64 }`. Mul/div fold to log addition (sub-ns ops, no
  allocation); add via `log10(1 + 10^Δ)`; `try_sub` returns `Option`.
- Untagged serde shim accepts both raw JSON numbers (legacy saves) and
  the `{"log10": x}` struct form (values past f64::MAX) so existing
  saves keep loading without a wire-format break for the small-value case.

V5 save schema (src/save/versions/v5.rs):
- Mag-typed counters frozen into a vN-style schema. V1..V4 frozen
  modules untouched; `From<GameStateV4>` does the f64 -> Mag conversion
  at the boundary, collapsing any leftover Inf/NaN to `Mag::ZERO`.
- CURRENT_VERSION = 5. Chain walks v1 -> v2 -> v3 -> v4 -> v5 ->
  GameState.

Tree orphan fix (src/game/tree/node.rs):
- New `reaches_origin` predicate: a lot is a node iff a
  strictly-decreasing-manhattan chain of populated neighbors reaches
  origin. `anchor_of`'s pass-3 fallback was letting two outskirt lots
  become each other's parent — a closed 2-node island unreachable from
  the cuque. Reproduced at (-6,-17) and (-5,-17) in the wild.
- Thread-local memo keeps the recursion O(lots visited).
- 40-radius BFS test asserts zero orphans (was 50+).

Procgen magnitude rebalance (src/game/tree/node.rs::roll_magnitude):
- Per-node boon/bane magnitudes ~1000x smaller across the board.
- Multiplicative ops (MulFactor / EffectMul / SpawnRateMul / CostMul)
  another 100x tighter on top — 100k-deep MulFactor stack = ×148
  (was overflowing f64 around stack 1700). AddPercent and FlatAdd stay
  at 1000x (linear, doesn't compound).
- NODE_COST_GROWTH 1.75 -> 2.5 stretches the cost ramp.

Format / display (src/format.rs):
- Infinite alphabetic suffix tail past Decillion: `aa..zz`, `Aa..Zz`,
  `aaa..zzz`, ... (`big_mag` and `big`).
- Precision-aware `mul_magnitude` / `percent_magnitude` / `flat_magnitude`
  so per-node `×1.0000004` renders with its meaningful digits in the
  info pane instead of collapsing to `×1.00`.
- `?` is now strictly an Inf/NaN sentinel.

CI parity:
- `cargo clippy --all-targets -- -D warnings` so test-side lint
  regressions don't slip past CI.

182 tests pass, fmt and clippy --all-targets clean. Stress-tested with
the original broken save (loads cleanly, gameplay accrues into Mag-space
values past f64::MAX) and a long playtest with 100+ stacked PurpleCoin
Buffs (FPS bounded, decays cleanly, no `?` rendered).
@flipbit03 flipbit03 merged commit bdbcd23 into main May 16, 2026
7 checks passed
@flipbit03 flipbit03 deleted the fix/orphan-tree-nodes branch May 16, 2026 17:16
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.

1 participant