← All projects

TRUSS

Simulation prototype

A city builder where the buildings are real trusses carrying real loads, and thirty years of storms, codes, and neglect decide what's still standing.

GameSimulationC#/.NETGodotStructural Engineering

At a glance

  • Outcome: TRUSS: Time-Resolved Urban Structural Simulator. A city builder whose core is a real truss solver: buildings carry load, and thirty years of storms, code changes and deferred maintenance decide what survives.
  • Status: Simulation prototype, built across roughly two weeks in August 2026. Ten scoped increments are implemented and gated; none of the close-out is done, the closing review never ran, and six decisions are still open.
  • Role: Solo. Product decisions, system boundaries, solver spec, contracts, and the gate discipline.
  • Stack & libraries: C#/.NET on Godot 4.6, domain core in C# with interaction in GDScript, a Nix flake pinning the toolchain, and shell scripts driving the gate and the release export.
  • Validation: A 92-suite regression gate, verified 92/92 from a clean tree instead of taken on report, plus a working release export and a smoke test on the exported build.
  • Limitations: Not playable as a game yet. The district view is written but hidden, so the campaign reads as text over a single leftover truss. The rename to TRUSS hasn’t landed, so the code still says StructuralCity.

The idea

City builders abstract structure away. You place a building and it exists. TRUSS puts the structure back, and the first question it has to answer is much smaller than a city.

An approved frame braces a ground-floor street bay. The tenant wants a clear storefront and threatens the completion payment, so the contractor pulls the street-side brace and closes the wall over the connections that now do nothing. You get one inspector workday and a limited repair budget before a storm reaches the site. That one scenario is the whole chain the prototype has to prove: approved design, quiet compromise, hidden as-built condition, discovery, repair, wind test.

Underneath, the solver computes an actual member-by-member truss, so a building has a load path and a factor of safety, and whether it stands in a storm is computed instead of rolled. It’s also narrower than real structural software on purpose. It handles members in tension and compression and drops them as they fail. It doesn’t model bending, connections, or anything dynamic. The spec says outright that it isn’t a substitute for professional structural analysis, and neither does this page.

Around that sits the part that makes it a game. Codes change between eras and existing buildings get grandfathered in. Materials decay. Maintenance is a bill you can pay or refuse, and refusing is recorded. Owners hold money, the city holds a ledger, and demolition and rebuilding cost something.

The fantasy is being responsible for a city’s buildings for long enough to watch your own earlier decisions come back.

Where the engineering went

Ten scoped increments, each frozen behind a contract before the work started and gated before it ended. The domain has enough interacting systems that the only way to keep it honest is to make each increment’s authority explicit and prove the previous ones still pass.

The domain object isn’t a building. It’s a project holding three separate truths: the plan the authority approved, the structure that physically exists, and what the player has actually learned. Only the second one reaches the solver. All three are saved separately, and the rule that falls out of the split is what makes it a game: the interface can’t leak a hidden fact through an ordinary plan view.

Knowledge is also ranked, not just recorded. Every fact about a member carries a mark saying how it was learned, and those marks are ordered by how firmly they reflect what was really built. A measurement outranks an observation, and both outrank an approved drawing, because records can be wrong. A firmer mark upgrades a weaker one and never the other way round, so the same evidence always produces the same belief no matter what order it arrived in.

That ordering is what turns structural analysis into investigation. It’s the difference between reading a drawing and going to measure.

Thirty years is authored, not endured. Narrative time and simulation time are separate axes, so an era calendar lays decades onto short blocks of simulation days. The full campaign runs in half a minute, which is what makes the thesis testable at all: maintained parcel against neglected parcel, same owners, same weather, one storm. Simulating thirty real years tick by tick would have been millions of events and no test would ever have run it.

The rest of the discipline:

  • Simulation state is fingerprinted and hashed, so a change that would move a historical result gets caught by a fixture instead of discovered later.
  • Unknown materials, invalid observations and empty record sets refuse instead of guessing.
  • Demolition is a flag beside the lifecycle instead of a stage inside it, which is what let a demolished parcel resolve a condemnation lock without corrupting the ordering.
  • The tick loop, the save path and the solver all carry measured budgets. A late optimization pass turned a quadratic retrofit loop and a full-stream save scan into incremental and indexed forms. The solver itself was never the bottleneck; the project’s own scouting names orchestration, the event stream and save loading as the real constraints.

What isn’t built

The suite passes 92/92 from a clean tree, but all four mayor scenarios hide the district view and lay a text deck over the viewport, and nothing clears the frame. So thirty years of decay, neglect, demolition and rebuilding play out over the first increment’s single truss, reaching the player as rows of text.

The view it needs is already written and already draws parcels, water mains and residents. The last four increments simply hide it.

That’s a fair picture of where the project is. One building’s worth of physics is proven end to end, ten increments of district, code, economy and maintenance authority were built underneath it, and the presentation never caught up.

The name

The expansion is a backronym, in the STAAD / ETABS / RISA / NASTRAN tradition, which is the naming convention of the category it wants to be read as part of. The test a backronym has to pass is that every word describes something the code actually does:

WordWhat it refers to
Time-ResolvedThirty-year campaigns on a compressed era calendar
UrbanParcels, residents, the water network, district-scale civic works
StructuralThe member-by-member truss solver, the domain core
SimulatorDeterministic, gated simulation instead of an abstraction

A truss is also literally what the solver computes and the most recognizable image in structural engineering, so the name and the logo are the same object.

The title-screen tagline isn’t fixed. The menu picks one at random per visit, from a set governed by one rule: every line has to be a real term of art, not a marketing phrase written to sound like one. Critical Load, Design Storm, The Governing Case, Code of Record, As-Built, Yield Line, Factor of Safety, Service Life. A player who looks one up finds a real structural or code concept. That set came out of the naming search itself, when the rejected title candidates turned out to be better as taglines than any one of them would have been as a title.

A note on the rename

The name was decided and written down before the rename was executed, on purpose. The old identifier appears in hundreds of files, and dropping a mechanical substitution that size into the range reviewers are told to read would bury the diff that actually matters, for no benefit.

So the decision is recorded, the rename surface is measured file by file, the parts that need care are named, and it lands as one commit after close-out. Writing the decision down was the way to stop it being re-litigated later without paying for it now.