Add headless, scriptable Random Map Generation (RMG) CLI mode#322
Closed
BlackgamerzVN wants to merge 4 commits into
Closed
Add headless, scriptable Random Map Generation (RMG) CLI mode#322BlackgamerzVN wants to merge 4 commits into
BlackgamerzVN wants to merge 4 commits into
Conversation
- New --generate-map CLI flag (Program.cs) parses theater/width/height/players/seed/output args. - GameClass runs headless generation instead of the interactive UI when requested, hiding the window (still needed for GraphicsDevice/TheaterGraphics) and exiting with a status code. - New HeadlessMapGenerator builds a Map via Map.InitNew, loads TheaterGraphics, runs the existing TerrainGenerationMutation engine over the whole playable area, scatters basic Tiberium/ore resource fields, places N-way symmetric player-start waypoints, and writes the result via Map.AutoSave. - New HeadlessMutationTarget: minimal non-UI IMutationTarget implementation so TerrainGenerationMutation can be driven headlessly. - TerrainGenerationMutation now accepts an optional explicit seed (was previously always DateTime.Now-based) so headless generation can be deterministic. Proof-of-concept scope per plan.md: produces a structurally valid, loadable, symmetric map with resources and player start points. Not build-verified in this environment (no network access for NuGet restore + missing sibling INI Parser checkout); verified via careful manual signature matching against existing call sites (MapSetup.cs, TerrainGeneratorConfigWindow.cs). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- HeadlessRmgOptions.ParseArgs now accepts space-separated '--flag value' args (matching the xna-cncnet-client ExternalProcessRandomMapGenerator contract), in addition to the previously-only-supported '--flag=value' form. - Added optional --gamemode <name> flag (accepted and logged; does not yet alter generation - see plan.md open question). - Logged gamemode alongside seed/output on successful generation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…eadless RMG - Introduce SymmetryMode enum (Rotational, Mirror) and --symmetry CLI flag. - Refactor PlacePlayerStarts to support both N-fold rotational symmetry and left/right axis-mirror symmetry (with odd-player-count axis placement). - Rewrite PlaceResources to generate patch centers within a single fundamental domain (one rotational wedge, or one mirror half) and replicate them via the same symmetry transform used for player starts, so resource patches are balanced per-player instead of purely random. Patch budget now scales with playable map area. - Extract shared PlacePatch/GetSymmetryEllipse/ClampToLocalSize helpers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Collaborator
|
That looks... quite plain. As far as the maps it can make go |
Member
|
bru |
Author
|
fuckkk I forgot to fork it |
Member
Collaborator
Author
|
It is, and AI fucker sent it directly to HERE xD |
Collaborator
You should probably reconsider giving it such broad permissions |
Author
|
noted. |
- New WaveFunctionCollapseSolver: a from-scratch implementation of the 'simple tiled model' WFC algorithm (observe lowest-entropy cell, collapse, propagate adjacency constraints via BFS, detect contradictions). Based on the publicly documented algorithm from mxgmn/WaveFunctionCollapse (MIT licensed); no code from that repo is reused, only the algorithm description. - New --algorithm scatter|wfc CLI flag (default: scatter, preserving prior behavior exactly). wfc mode divides the map into 6x6-cell blocks and runs the solver to decide which single TerrainGeneratorTileGroup governs each block, using the theater's real LATGrounds ground/base adjacency data (plus tileset index 0, the theater's default base/clear tileset) to build the compatibility matrix - so neighboring blocks only ever get tile groups the game's own tileset rules say can actually sit next to each other. - Each block is painted using the existing, already-vetted TerrainGenerationMutation engine restricted to just that block's tile group; auto-LAT blending (already enabled on HeadlessMutationTarget) then smooths the boundaries between adjacent blocks using the same mechanism as manual GUI tile painting. - Falls back to the scatter algorithm if the solver hits a contradiction, if a block's generation throws, or if the active preset has fewer than 2 tile groups (WFC needs at least 2 domains to be meaningful). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Author
|
MOTHERFUCKER |
Author
|
Myeah expect future slops |
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.
This PR was developed in a sandboxed environment with no network access to nuget.org, and the sandbox's global NuGet config also has a stale, unrelated local source (
C:\INI Parser) that breaksdotnet restore/dotnet buildbefore the real dependencies can even be resolved. This was confirmed independently in two separate sandboxes (this session and the collaborating client-side session), and theC:\INI Parserpath has zero references anywhere in this repo (grepped) — it's leftover machine cruft, not a project dependency.As a result, none of the code in this PR has been compiled or run. All of it was written and cross-checked manually against existing call sites, signatures, and types in the repo (
MapSetup.cs,TerrainGeneratorConfigWindow.cs,TerrainGenerationMutation.cs,Map.cs,MapWriter.cs,Theater.cs, etc.), but that is not a substitute for a real build + smoke test.Before merging, someone with a working build environment should:
dotnet buildthe solution and fix any compile errors..mapfile, and that the file loads correctly back in the normal WAE GUI (terrain looks sane, player waypoints exist, resource overlays are present and don't overlap water/roads).--symmetry rotational(default) and--symmetry mirrorfor a few player counts (2, 3, 4).--algorithm wfc(see below) against the default--algorithm scatter, ideally on a theater with a terrain generator preset that has 2+ tile groups (so WFC actually has multiple domains to work with).Risk / verification status by piece, so reviewers know what's what
--generate-map, hidden window, arg parsing)Program.cs/GameClass.cs).--algorithm scatter(default)TerrainGenerationMutationengine exactly as the GUI's Terrain Generator tool calls it, just over the whole map instead of a brush selection. Lowest risk of the terrain paths — closest to "known-good code, new caller."rotational/mirror) + wedge-balanced resources--algorithm wfc(opt-in, non-default)WaveFunctionCollapseSolver.cs) with no prior art in this codebase to cross-check signatures against — only manually reasoned through, not just manually diffed against existing patterns. Strongly recommend reviewing/testing this path last and separately from the rest, and it's safe to ship the rest without it if it needs more iteration (it's fully opt-in; default behavior is untouched).What this adds
A new
--generate-mapheadless CLI mode so an external process (e.g. a CnCNet game lobby client) can invoke WAE non-interactively to produce a randomized, playable.mapfile — no visible window, structured stdout/exit-code contract for automation.Why a hidden window instead of a pure console app
TheaterGraphicsallocates realTexture2DGPU objects in its constructor, so loading theater tile/overlay/smudge data requires a live MonoGameGraphicsDevice— this is true even outside of rendering. Decoupling that is a much bigger refactor with no real benefit for this use case, so this PR keeps a real MonoGame window but never shows/activates it, and skipsMainMenu/all interactive UI when--generate-mapis passed.What it generates
Map.InitNew, same path as the GUI's "new map" flow).--algorithm, see below and the risk table above), both built on the existingTerrainGenerationMutationengine (previously only ever driven by a user-brushed selection) through a new lightweight non-UIIMutationTargetstub (HeadlessMutationTarget).Waypoints (0..N-1), with a choice of:--seed(fixed a pre-existing bug whereTerrainGenerationMutationalways reseeded internally fromDateTime.Now, which would have silently broken determinism for the new--seedflag; now takes an optional explicit seed, backward-compatible for existing GUI callers).Terrain algorithm:
--algorithm scatter|wfc(defaultscatter)scatter(default, unchanged from earlier iterations of this PR): runsTerrainGenerationMutationonce over the whole map, with all configured tile groups competing independently per cell.wfc(new, opt-in, highest-risk piece — see table above): a real Wave Function Collapse solver (WaveFunctionCollapseSolver.cs) — a from-scratch implementation of the "simple tiled model" variant of the algorithm popularized by mxgmn/WaveFunctionCollapse (MIT licensed; no code from that repo is reused here, only the published algorithm description: observe the lowest-Shannon-entropy cell, collapse it to a weighted-random single state, propagate the resulting adjacency constraints via BFS, detect contradictions). It divides the map into 6x6-cell blocks and solves for which singleTerrainGeneratorTileGroupgoverns each block, using the theater's realLATGroundsground/base adjacency data (plus tileset index 0, the theater's default base/clear tileset) to build the compatibility matrix — so neighboring blocks only ever get tile groups the game's own tileset rules say are actually allowed to be adjacent. Each block is then painted with the existingTerrainGenerationMutationengine restricted to just that tile group, and the existing auto-LAT blending (already enabled onHeadlessMutationTarget) smooths the boundaries between adjacent blocks, the same way manual GUI tile painting does. Falls back toscatterautomatically if the solver hits a contradiction, if a block's generation throws, or if the active preset has fewer than 2 tile groups.Finalized CLI contract
Both space-separated (
--flag value) and--flag=valueforms are accepted for every flag, to match what the calling client (xna-cncnet-client'sExternalProcessRandomMapGenerator) already expects.0, stdout:Map generation succeeded. Output written to: <path>1, stderr with an error message (missing/invalid args, missing theater preset, I/O failure, etc.)Known open design questions (flagged, not blocking)
--gamemodeis currently accepted and logged only — it does not yet select a specificTerrainGeneratorPresets.inisection or bias resource density/ratio. Open question for whoever wires up game-mode-specific behavior.OverlayType.Tiberium == truetypes (ore and gem pooled together) are picked uniformly at random per patch.PlaceResources/ plan notes for details. Rotational mode doesn't have this limitation.~900 cells per patch attemptper symmetric replica) and WFC block size (6x6 cells) are first-pass constants, not tuned against real game balance/visual-quality data.Files changed
src/TSMapEditor/Initialization/HeadlessMapGenerator.cs(new) —HeadlessRmgOptions(CLI parsing/validation),HeadlessMapGenerator(orchestration: terrain, resources, player starts, save),SymmetryModeandTerrainAlgorithmenums.src/TSMapEditor/Initialization/WaveFunctionCollapseSolver.cs(new) — the generic WFC solver used by thewfcterrain algorithm.src/TSMapEditor/Initialization/HeadlessMutationTarget.cs(new) — minimal non-UIIMutationTargetstub.src/TSMapEditor/Program.cs— parses--generate-mapand its options.src/TSMapEditor/Rendering/GameClass.cs— hides the window and runs headless generation instead of showingMainMenuwhen--generate-mapis passed.src/TSMapEditor/Mutations/Classes/TerrainGenerationMutation.cs— added optional explicitseedparameter (backward compatible) to fix determinism.License note
This repo is GPLv3. This PR is a straightforward feature addition to the existing GPLv3 codebase (not a fork), so no additional licensing action is needed beyond normal repo contribution. The new
WaveFunctionCollapseSolver.csreimplements a publicly documented algorithm from scratch; it does not copy code from the MIT-licensed reference implementation, and MIT is compatible with GPLv3 in any case.