Skip to content

Unify map generator controls and tune playable defaults - #238

Merged
genixpro merged 2 commits into
masterfrom
codex/map-generator-defaults
Sep 10, 2026
Merged

Unify map generator controls and tune playable defaults#238
genixpro merged 2 commits into
masterfrom
codex/map-generator-defaults

Conversation

@genixpro

@genixpro genixpro commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Random-map defaults now leave more grassy building room while retaining each generator's shape. The lobby, map editor, command-line stress tool and study tooling share one definition of generator names, available controls, ranges, steps and defaults.

Stacked on #237 (codex/custom-game-ux, 82943d9). The diff contains only the generator follow-up.

Changes

  • Tune all eight procedural presets. Against Redesign custom-game lobby with automatic maps, teams, and rules #237's lobby settings, immediately free tiles rise from 9.8% to 40.7% for River and 9.2% to 57.1% for Crater Lakes.
  • Add measured lake-size, channel-width, neutral-island, island-size and bridge-width controls. Both UIs use the same definitions, remember terrain settings per mode, and share dimensions/colony/worker settings. Grass 75 and island size 65 are selectable; bounds and five-unit steps agree everywhere.
  • Remove ineffective wheat/wood/stone/algae ratio widgets; retain working fruit controls. Restore legacy resource placement and fix the invalid team index during island base placement.
  • Rename the legacy modes to Shattered Coast and Rugged Archipelago, with matching labels in all 33 languages.
  • Export the compiled catalog to generate the control reference and chart labels. Add reproducible study seeding without changing normal UI random seeding.

Validation

  • Release game build, shared-control regression, and existing custom-setup headless/native SDL tests pass, including previews, controllers, saves and replays.
  • Every selectable editor value, lobby dropdown/stepper interactions, per-mode memory, and descriptor serialization checked. Add shared-control coverage to Linux CI.
  • 24,000 fixed-seed attempts: 1,000 per generator for tuned, previous lobby, and previous editor settings. 72 independently repeated seed/configuration pairs match. Inspect 24 final map previews plus native UI captures.
  • Strict translation audit, English-fallback regression, Python syntax and diff checks pass.

Both baseline cohorts include the same legacy correctness fixes. Placement failures remain: River has 18/1,000 (versus lobby 4 and editor 145), and Shattered Coast 14/1,000 (versus lobby 0 and editor 2), alongside substantially better building room and starting-resource access. Small crowded maps remain a limitation; this is not a full-match balance study.

Coverage comparison

Generated reports, study data and screenshot files are excluded from the branch tip and final source changes. Existing image embeds reference earlier review commits; future artifacts stay outside tracked source.

Base automatically changed from codex/custom-game-ux to master September 10, 2026 16:57
@genixpro
genixpro force-pushed the codex/map-generator-defaults branch from 9e9f0f1 to b74f6df Compare September 10, 2026 21:36
@genixpro

Copy link
Copy Markdown
Contributor Author

Rebased onto current master (e3a01b051). This branch forked from codex/custom-game-ux at 82943d96e, but #237 merged with more commits added after that point (and was squash-merged, so 82943d96e never became a real ancestor of master). A plain git rebase tried to replay this branch's full pre-fork history against master and produced conflicts across every translation file; I instead used git rebase --onto master 82943d96e to replay only this branch's own 3 commits, which reduced it to genuine, small conflicts:

  • All data/texts.*.txt and data/texts.keys.txt conflicts were pure append/append (both sides added unrelated new keys at the end of the file) — resolved by keeping both additions.
  • src/SConscript was two unrelated new build-target blocks added at the same location — resolved by keeping both.
  • The trailing commit (9e9f0f19d, the Cortex-difficulty-label fix) dropped cleanly during the rebase — its change was already upstream via #237's later commits, confirmed by diff before pushing.

Verification on this head (b74f6dffb):

  • Full scons -j8 release=1 server=0 client build is clean.
  • scons -j8 release=1 server=0 map-generator-defaults-test map-generator-study builds clean.
  • MapGeneratorDefaultsTest . passes (shared presets, ranges/steps, editor values, lobby/editor mode memory, serialization and validation).
  • MapGeneratorStudy --catalog runs and emits the expected control catalog for all 9 generator methods.

Requesting review from @Giszmo and @kylelutze — this is still marked draft; let me know if it should come off draft. No merge performed.

genixpro added a commit that referenced this pull request Sep 10, 2026
#238 (already merged) added CustomGamePreferences.h, which serializes
CustomGameSetup::generator by taking Sint32 MapGenerationDescriptor::*
member pointers directly. #240 replaced generator's type with
GenerationRequest, whose method-specific options live in a generic
std::map<std::string,int> instead of fixed struct fields — an
architecture change, not a rename — so the file no longer compiled.

- CustomGamePreferences::encode()/decode() now convert through
  toLegacyDescriptor()/fromLegacyDescriptor() (the compatibility
  adapter #240 already built for exactly this kind of interop), so the
  on-disk wire format and its corruption-recovery bounds are unchanged.
  decode()'s method-validity check now asks the live GeneratorRegistry
  instead of a hardcoded 1-8 range, so it stays correct as generators
  are added or retired.
- Widened several fields() bounds (terrain weights 0-64 -> 0-100,
  riverDiameter's max 64 -> 65, oldIslandSize 1-64 -> 1-70) to match
  the modular registry's current ranges. These are approximate,
  same as before: the reused legacy fields (e.g. riverDiameter also
  stands in for lake size/channel width/bridge width) don't have one
  true bound, so this is a safe envelope, not a tight per-method one.
  Without this, decode() could reject a preferences file that a normal
  save legitimately produced (oldIslandSize's own default already
  exceeded the old 1-64 bound for any method other than Isles/Old
  Islands).
- Found and fixed a related crash bug in the compatibility adapter
  itself while tracing this: Lattice and Maze register wheat/wood/
  stone/algae controls with no entry in legacyField()'s mapping table,
  so converting either method through toLegacyDescriptor/
  fromLegacyDescriptor threw an uncaught std::invalid_argument. Added
  the four missing mappings (they match pre-existing legacy struct
  fields exactly) and changed every other option with no legacy slot
  (loopiness, home-radius, cell-size, ...) from throwing to falling
  back to its control's default value, so a newer generator's full
  option set can never crash this adapter again.
- test/CustomGameSetupHarness.cpp: preferencesModel()/preferencesScreen()
  built a GenerationRequest via the same member-pointer approach;
  updated both to build a temporary MapGenerationDescriptor and convert.
  Switched preferencesModel()'s method from Old Islands to Crater Lakes
  (one of the four modern height-map generators that still exposes a
  repeat-landscape control; Old Islands never did in the new registry,
  so it always round-tripped back to 0). The per-field assertions in
  preferencesScreen() now compare against the same achievable
  conversion rather than raw field maximums, since only the options a
  method actually registers survive a GenerationRequest round trip.

Verified: full scons -j8 release=1 server=0 client build is clean.
CustomGameSetupHarness passes in default, preferences-write and
preferences-read modes. MapGeneratorDefaultsTest and
MapGeneratorStudy --catalog also pass, unaffected by the adapter fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFxsZmLM4qsovemHqDrHGP
@genixpro
genixpro marked this pull request as ready for review September 10, 2026 23:23
@genixpro
genixpro merged commit 8b8c934 into master Sep 10, 2026
3 checks passed
@genixpro
genixpro deleted the codex/map-generator-defaults branch September 10, 2026 23:23
genixpro added a commit that referenced this pull request Sep 10, 2026
#238 (already merged) added CustomGamePreferences.h, which serializes
CustomGameSetup::generator by taking Sint32 MapGenerationDescriptor::*
member pointers directly. #240 replaced generator's type with
GenerationRequest, whose method-specific options live in a generic
std::map<std::string,int> instead of fixed struct fields — an
architecture change, not a rename — so the file no longer compiled.

- CustomGamePreferences::encode()/decode() now convert through
  toLegacyDescriptor()/fromLegacyDescriptor() (the compatibility
  adapter #240 already built for exactly this kind of interop), so the
  on-disk wire format and its corruption-recovery bounds are unchanged.
  decode()'s method-validity check now asks the live GeneratorRegistry
  instead of a hardcoded 1-8 range, so it stays correct as generators
  are added or retired.
- Widened several fields() bounds (terrain weights 0-64 -> 0-100,
  riverDiameter's max 64 -> 65, oldIslandSize 1-64 -> 1-70) to match
  the modular registry's current ranges. These are approximate,
  same as before: the reused legacy fields (e.g. riverDiameter also
  stands in for lake size/channel width/bridge width) don't have one
  true bound, so this is a safe envelope, not a tight per-method one.
  Without this, decode() could reject a preferences file that a normal
  save legitimately produced (oldIslandSize's own default already
  exceeded the old 1-64 bound for any method other than Isles/Old
  Islands).
- Found and fixed a related crash bug in the compatibility adapter
  itself while tracing this: Lattice and Maze register wheat/wood/
  stone/algae controls with no entry in legacyField()'s mapping table,
  so converting either method through toLegacyDescriptor/
  fromLegacyDescriptor threw an uncaught std::invalid_argument. Added
  the four missing mappings (they match pre-existing legacy struct
  fields exactly) and changed every other option with no legacy slot
  (loopiness, home-radius, cell-size, ...) from throwing to falling
  back to its control's default value, so a newer generator's full
  option set can never crash this adapter again.
- test/CustomGameSetupHarness.cpp: preferencesModel()/preferencesScreen()
  built a GenerationRequest via the same member-pointer approach;
  updated both to build a temporary MapGenerationDescriptor and convert.
  Switched preferencesModel()'s method from Old Islands to Crater Lakes
  (one of the four modern height-map generators that still exposes a
  repeat-landscape control; Old Islands never did in the new registry,
  so it always round-tripped back to 0). The per-field assertions in
  preferencesScreen() now compare against the same achievable
  conversion rather than raw field maximums, since only the options a
  method actually registers survive a GenerationRequest round trip.

Verified: full scons -j8 release=1 server=0 client build is clean.
CustomGameSetupHarness passes in default, preferences-write and
preferences-read modes. MapGeneratorDefaultsTest and
MapGeneratorStudy --catalog also pass, unaffected by the adapter fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFxsZmLM4qsovemHqDrHGP
genixpro added a commit that referenced this pull request Sep 10, 2026
Rebased onto master now that #238 has merged. That surfaced a real compile
break: the harness still called Map::oldMakeIslandsMap/Game::oldMakeIslandsMap,
which the registry-driven generator rewrite removed.

Route it through MapGenerator::generateMap(Game&, const MapGenerationDescriptor&,
seed) instead, which owns map sizing and the game association internally. Seed
explicitly since generation determinism no longer follows the global sync-rand
state.

Also start from setMethodDefaults() rather than hand-picked constants: #238's
defaults tuning tightened several control ranges (island-size moved to 50-70,
the shared "workers" control caps at 8), so the harness's old literals
(oldIslandSize=35, nbWorkers=48) now fail request validation. Defaulting first
and overriding only what this decorative colony actually needs to differ keeps
it from rotting the same way again as ranges keep moving.

Verified: full client and menu-colony-harness build clean; `check`, `navigation`,
and `generate` subcommands all pass (generate grows the colony from 8 to 56
units over the same 12,000-tick warmup, confirming the reduced starting worker
count doesn't defeat the decorative intent).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Le23P4h9QuDjwExuK6HU96
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