Repository navigation
Tidal nerf, ten-level Time Lab, Cryo hub moves and a smarter time split - #5
Merged
Merged
Conversation
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.
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
This branch catches the optimizer up with two game updates and gives it a way to measure itself.
Changes
Game data
tidal_ascendancyis ×1.35. A timeline keeps the figure it jumped in under, so the old ×1.67 stays selectable astidal_ascendancy_legacy, a switch on the Tidal card. Share codes give it its own byte.Measuring the solver
npm run surveysolves 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.--compare base.jsonsets every island against a baseline saved with--jsonand 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.mdalready notes that the Singularity bound is loose, so most of that gap is probably the bound rather than the search.Solver
islandBudgetsSnow 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.planSolveand the app's run-time estimate both use it, so the time shown on the run button matches the run.App
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 knipand Prettier are clean.--compareagainst 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.