Skip to content

Move adults into empty beds when that shortens the total walk - #16

Merged
kramsey458 merged 4 commits into
mainfrom
claude/peaceful-ramanujan-0a1vmp
Sep 27, 2026
Merged

kramsey458 merged 4 commits into
mainfrom
claude/peaceful-ramanujan-0a1vmp

Conversation

@kramsey458

@kramsey458 kramsey458 commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

What changes

A pass used to swap adults only between beds adults already occupied, so an empty home beside a workplace was never used (e.g. an empty Mini Lodge next to a Lumberjack Flag whose worker lives across the map). Now each district is solved over its occupied and empty beds, so a worker moves into the empty lodge when that shortens the colony's total walk.

  • Solver: Hungarian.Step takes n rows by m ≥ n columns (O(n²m)). A district's columns are its adults' beds, then its empty beds (at most one per adult in each home). The solve budget is n²m/8.
  • Snapshot: HomeFree per home. Homes now include every usable home with an empty bed in a district with adults. Children count against beds, so they keep theirs.
  • Apply: Cycles.SplitChains splits moves into cycles (head counts kept) and chains (the last move takes an empty bed). Each can be applied on its own. ApplyCycle's existing capacity check turns a chain away as stale if the game filled that bed meanwhile.
  • Breeding rule: a chain is turned down (counted as rejected) if it would leave the colony fewer homes that can breed, meaning at least two adults and an empty bed. Counts carry across the chains a pass applies, so the colony's number of breeding homes never falls. A 1-bed lodge can never breed, so filling one costs nothing. Moving out of a two-adult home with a spare bed is refused. The solver itself doesn't know the rule, so a refused chain is proposed again the next day until the colony changes.
  • Adapter: Capture lists empty-bed homes through district.GetComponent<DistrictBuildingRegistry>().GetEnabledBuildingsInstant<Dwelling>(), with the same UsableHome and district checks as before.
  • A lone adult is no longer skipped.
  • PassState version 6: a pass running in an older save starts over once, and remembered route costs carry over.

Not done here

  • Not compiled against the game. There are no game DLLs in CI or in this environment. The DistrictBuildingRegistry / GetEnabledBuildingsInstant<Dwelling>() call is unverified. Build locally and run the compiled-adapter check before release.
  • The breeding rule ("two adults and an empty bed") is the one the FAQ's replay used. It hasn't been checked against the game's reproduction code.
  • Website, README player text and PRODUCT.md unchanged. They describe the released 1.1.1, and merging publishes the site. The FAQ #vacant and #breeding, the index rules, the compare table ("Adults per home"), the #how steps, troubleshooting "Empty beds aren't used" and the "41 automated checks" status line all need updating with the release. The README has an Unreleased changelog entry.

Tests

dotnet run --project OptimizedLocalHousing.Tests -c Release → 45 checks passed. That is the 40 existing checks plus:

  • chain/cycle splitting of moves into empty beds, each unit applicable alone;
  • the brute-force optimum on 100 small random colonies with empty beds (exact unless the breeding rule rejected a chain), never overfilling a home;
  • the empty-lodge-beside-the-workplace case (a paused nearer home is never used; a second pass changes nothing);
  • the breeding rule: moving out of a breeding home, or filling a breeding home's last bed, is refused; a move that keeps or makes a breeding home is allowed;
  • 60 colonies with empty beds over several passes: head counts change in all 60, and breeding homes never fall;
  • empty beds added to the lockstep and save/reload-at-every-tick tests.

CLAUDE.md now expects 45 checks passed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J9E8mdmw7xR61ijmdTWXg8

claude and others added 4 commits September 27, 2026 20:59
A pass used to swap adults only between the beds adults already occupied, so an
empty home beside a workplace was never used. Each district is now solved over
its occupied and empty beds (rectangular Hungarian, n adults by m beds). Moves
that end in an empty bed are applied as chains; cycles are unchanged. A home's
number of adults can now change. Children still never move, and paused,
blocked or automation-off homes still gain nobody.

- Snapshot.HomeFree; the adapter lists every usable home with an empty bed in
  each district through its DistrictBuildingRegistry.
- Cycles.SplitChains splits the moves into chains and cycles, each applicable
  on its own.
- A lone adult is no longer skipped: it can move into an empty bed.
- PassState version 6: a pass running in an older save starts over once.
- Tests: chain splitting, brute-force optimum with empty beds, the empty lodge
  beside a workplace, and empty beds in the lockstep and reload tests (43 run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9E8mdmw7xR61ijmdTWXg8
A chain of moves into an empty bed takes an adult out of its first home and
puts one into its last. It is now turned down if that would leave the colony
fewer homes with at least two adults and an empty bed, the homes that can have
a kit. Cycles keep every head count, so they are unaffected. Counts are
carried across the chains a pass applies, so the total never falls.

Tests: the lodge beside a workplace is refused when the worker's home would
stop breeding, and allowed when it keeps or makes a breeding home; 60 colonies
with empty beds over several passes never lose a breeding home (45 run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9E8mdmw7xR61ijmdTWXg8
Checked against the decompiled game (Timberborn 1.1.2.4):

- Breeding (Folktails' ProcreationHouse): a kit needs two adults, a free bed,
  and fewer children than the home's child beds (a third of its beds, rounded
  down) and than half its adults, rounded down. The engine assumed two adults
  and a free bed, so it counted homes such as 6 beds with 2 adults and a child
  as breeding, and turned moves out of them down.
- Adult beds (Dwelling): only beds - beds/3 are for adults. The game marks a
  home with more adults than that as overpopulated and moves an adult out, so a
  chain that put an adult in a child's bed would be undone.

The snapshot now carries each home's beds, adults and children from the game;
HomeRules.FreeForAdults gives the empty beds an adult may keep and
HomeRules.CanBreed the breeding rule. ApplyCycle also refuses a move that would
put more adults in a home than its adult beds. The test fake follows the game
the same way, with new cases pinning both rules; reverting either one fails the
suite.

46 checks pass, including the compiled-adapter check against the installed game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kramsey458
kramsey458 merged commit 434c475 into main Sep 27, 2026
1 check passed
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.

2 participants