Skip to content

Tidal nerf, ten-level Time Lab, Cryo hub moves and a smarter time split - #5

Merged
OGMods merged 11 commits into
mainfrom
anomaly-tuning
Oct 8, 2026
Merged

OGMods merged 11 commits into
mainfrom
anomaly-tuning

Conversation

@OGMods

@OGMods OGMods commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Summary

This branch catches the optimizer up with two game updates and gives it a way to measure itself.

  • The game's numbers. Tidal Ascendancy was cut from ×1.67 to ×1.35. Absolute Zero, Infinite Grid and Stellar Forge now have ten Time Lab levels instead of five, with the same top levels.
  • Measuring the solver. A survey reports how far each island lands from its upper bound, and can compare a change against a saved baseline.
  • Two solver improvements found that way: Cryo Nexus layouts keep their producers on one landmass, and a run spends less time on small islands that cannot improve.

Changes

Game data

  • Tidal nerf (1df6454): tidal_ascendancy is ×1.35. A timeline keeps the figure it jumped in under, so the old ×1.67 stays selectable as tidal_ascendancy_legacy, a switch on the Tidal card. Share codes give it its own byte.
  • Each anomaly's figures written once (1e7d378): card text, comments, docs and tests follow the anomaly table instead of repeating the number, so the next rebalance is a one-line edit.
  • Ten-level research (bb4216c): the new tables from the extractor. The building roster and anomalies are unchanged. The re-extracted icons and sprite sheet differ only by compression noise (checked pixel by pixel and side by side), so they are not included. A saved level is not migrated: the game re-rates research at once, so level 5 now reads as the new level 5, as it does in game. The Time Lab buttons wrap so ten of them fit on a phone.

Measuring the solver

  • Gap survey (440da52): npm run survey solves every map under every anomaly, with obstacles cleared, and ranks islands by how much of their map's bound they leave on the table, with the run-to-run spread beside it.
  • Before-and-after mode (6d340e4): --compare base.json sets every island against a baseline saved with --json and exits 1 if any got worse. Each island is judged on the mean of its runs, against a noise band set by the wider run spread. The golden fixtures cannot see what a change does to a real timed run; this can.

What the survey found, at 15s per map: the solver lands at 96–99.6% of the bound under the base rules and Tidal, and at the bound under Cryo Nexus. Singularity Isolation sits lower (93–96%), and more time barely moves it. docs/SOLVER.md already notes that the Singularity bound is loose, so most of that gap is probably the bound rather than the search.

Solver

  • Cryo Nexus moves whole hubs (174fd0a): a player reported a layout with a hub left on a small second landmass. Moving it onto the main island and filling the second landmass with coolers kept the same power, but the cooler-gathering pass moves one tile at a time and cannot move a hub. A new step moves all of a smaller landmass's producers onto the main one together, then swaps buildings on the main island until power is back. It is kept only if power, every producer and the upgrade plan hold. On the reported board it reproduces the player's layout at the same 611AC. Across the eight cleared maps, 7 now keep every producer on one landmass. Shadowspire's 29-producer second landmass does not fit. It has its own 1s limit, sized for phones; a 15-producer hub takes about 170ms on a desktop.
  • Time split by tile count squared (394df43): solving every island at ¼ to 2× its share showed that islands of 20 tiles or fewer reach the same result at ¼ as at 2×, while every landmass of 67+ tiles was still improving at 2×. islandBudgetsS now weights each island by tiles², with a floor of the smaller of 0.25s and its old share. Entropy Isles' main landmass goes from 12.1s to 13.8s of a 15s run. planSolve and the app's run-time estimate both use it, so the time shown on the run button matches the run.

App

  • Show the full power figure on hover or tap (d74a71c).
  • Show an imported layout's own board straight away (bd6fc19).
  • Focus the code box when Import opens (216c7f5).
  • Prettier formatting (70778ea).

Testing

  • npm test: 869 passing. New tests cover the hub move on the reported board (it fails with the step disabled), islandBudgetsS, and the research tables. Tests that meant the top research level now ask for level 10. The research-then-anomaly order test now uses level 3's ×1.3, the lowest new level where the two orders give different results.
  • npm run check, npm run knip and Prettier are clean.
  • Time split, measured with --compare against the old split (15s × 5 runs, No Anomaly / Tidal / Singularity): no island got worse. Maps 5–8, the fragmented ones it targets, lean about +0.25%. That is within the ±0.8% run-to-run noise at this sample size, so the direction is real but the size is not confirmed.
  • Cryo hub move: fresh solves of all eight cleared maps, old code against new, same seed. Power held or rose everywhere, and the maps with producers on more than one landmass went from 5 to 1.

OGMods added 11 commits October 8, 2026 10:31
Formatting only: these files had drifted from the configured style, so
format:check was failing. No content changes.
The game cut the shore bonus from x1.67 to x1.35, but a timeline keeps the
figure it jumped in under until it jumps again, so both are live.

- tidal_ascendancy is now x1.35. The pre-nerf figure stays selectable as
  tidal_ascendancy_legacy, marked variantOf the live one, so it threads
  through persistence, the worker and blueprints by id like any anomaly.
- The Tidal card in the Time Lab folds the two into one card with a Bonus
  switch styled like the research level chips, shown only while Tidal is
  the chosen anomaly. The card shows the selected build's figures.
- Blueprints give the legacy build byte 4. Codes shared before this read
  byte 2 at the live figure; nothing tells them apart.
- Tests that pinned x1.67 results now run against the legacy entry.
A rebalance like the Tidal nerf meant hunting x1.67 through card text,
comments, docs and tests. Now each figure is written in one place and
everything else follows it or names it.

- data/anomalies.ts builds each entry from one builder per rule that takes
  only the figures and fills the card strings from them; preNerf() makes
  the legacy variant.
- Solver comments and the docs name the field (coolerMultiplier,
  multiplier, isolated, crowded) instead of the number. The docs note that
  every recorded Tidal measurement is of the pre-nerf build.
- Tests read figures through a new anomalyOfRule helper. Tests that pin an
  exact double keep a named literal (SHORE, CROWDED) or the frozen legacy
  entry, since those bits belong to that number alone.
npm run survey solves every shipped map under every anomaly, --runs times
each, with obstacles cleared, and ranks islands by how much of their map's
bound they leave on the table, with the run-to-run spread beside it. Each
island gets the slice of --time planSolve would give it, through the CLI's
worker pool, so a figure here is one the app would get. Older builds of an
anomaly (variantOf) are skipped by default.
Power and Est. Max on the board stat card open into every digit: hover
with a mouse, tap on a phone (a tap elsewhere or Esc closes it, and a
click pins a hovered one open). 611AC reads 611,210,546AA.

formatNumberFull groups the digits in threes and folds a trailing run of
000 groups into the suffix. The digits are the double's shortest
round-trip spelling, so nothing past the ~17 digits a double holds is
printed as if it were measured. The popup is a native popover, so the
card's scrolling body and backdrop-filter cannot clip or displace it.
Importing a code loaded the new island but never swapped the solve panel
over, so the previous island's solve stayed on screen - its buildings
over the new terrain - until a switch away and back. An import now does
what a template switch does: restore the island's solve (it has none
yet, so the panel clears), close the inspector and recentre.

A run in flight is stopped too, since it belongs to the board leaving
the screen, but only once the code is accepted: importBlueprint takes an
onAccepted callback, so a bad paste mid-run costs nothing.
ModalShell focused Close on open, which no dialog could override. It now
focuses the field a dialog marks data-autofocus, falling back to Close
when there is none or it is disabled. Import marks its code box, so a
paste can follow straight away; at the island cap the box is disabled
and focus stays on Close.
The gather pass moves one tile at a time, and that cannot move a hub: a
generator taken from its reactors loses power at every step, so a hub on
a small second landmass stayed there and its coolers with it. Before the
sweep, each smaller landmass holding producers now has all of them moved
onto the landmass holding the most. A greedy placement puts them on that
landmass's cooler or empty tiles, each displaced cooler crosses to the
tile its producer left, and a swap descent over the main landmass
restores power. The move is kept only if power, every producer and the
upgrade plan hold, so it cannot cost a watt.

On a reported Entropy Isles board this reproduces the player's own
layout at the same 611AC. Across the eight maps with obstacles cleared,
seven now keep every producer on one landmass; Shadowspire's
29-producer second landmass does not fit. Capped at 1s on top of the
sweep's 150ms, sized for a phone; a 15-producer hub takes ~170ms on a
desktop.
npm run survey -- --compare base.json sets every map, anomaly and island
against a baseline written by --json and exits 1 if any island got worse.
The golden fixtures replay the deterministic core with a step cap, so
they cannot see what an engine change does to a real wall-clock run;
this can.

Each island is judged on the mean of its runs rather than the best,
against a noise band as wide as the wider side's run spread (at least
0.1%), so a search that turns erratic cannot hide a drop behind its own
new variance. It warns when --time, --runs or --seed differ from the
baseline, and calls out a moved bound, which means the rules or roster
changed rather than the search.
Solving every island of the eight cleared maps at a quarter, half, one
and two times its share: islands of 20 tiles or fewer reached the same
figure at a quarter as at double, while every landmass of 67 tiles and
up was still climbing at double. A linear split still gave those scraps
up to 19% of a run (Entropy Isles).

islandBudgetsS now weights each island by tiles squared, with a floor of
the smaller of 0.25s and its old share so a scrap still finishes on a
phone. Entropy Isles' main landmass goes from 12.1s to 13.8s of a 15s
run, Shadowspire's from 9.5s to 11.5s. planSolve and the app's run
estimate both call it, so the figure on the mode control models the
budgets the coordinator sends.

Surveyed against the old split at 15s x 5 runs under No Anomaly, Tidal
and Singularity: no island got worse, and the fragmented maps lean about
+0.25% - within the ~0.8% run-to-run noise at that sample size.
The October 2026 game update gives Absolute Zero, Infinite Grid and
Stellar Forge ten levels each instead of five, to the same ceilings:
+10% to +100% in steps of 10 for the first two, +5% to +50% in steps of
5 for Stellar Forge. The roster, the anomalies and their text are
unchanged; the re-extracted icons and atlas differ only by compression
noise, so they are left as they were.

A saved level is a level number, as in the game, and is not migrated:
the game re-rates research at once, so a save or share code at level 5
now reads at the new level 5.

Tests that meant the top level now ask for index 9. The order test needs
a factor that rounds apart from x1.67 in the two orders; x1.25 is gone,
so it uses level 3's x1.3. The level buttons wrap so ten of them cannot
widen the Time Lab card.
@OGMods
OGMods merged commit 6bfb759 into main Oct 8, 2026
3 checks passed
@OGMods
OGMods deleted the anomaly-tuning branch October 8, 2026 13:47
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