Mr. Phil Games’ Blog

Posts for Tag: Godot

Stellar Throne Devlog #8: Validation, Parity, and Precision

Monday was a critical day of validation and bug hunting for Stellar Throne, my 4X strategy game built with Godot for the UI client and Zig for the high-performance simulation engine. After enabling the Zig backend in production over the weekend, my goal was to make absolutely sure both engines stayed perfectly in sync.


Dual-Engine Validation

The big picture: the Zig simulation now runs about fifty-two times faster than the original Godot implementation. But speed means nothing if the results aren’t identical. Monday’s focus was validation, testing, and fixing any divergence between the two engines.

I started the day tracking down a fleet position serialization bug. When fleets moved between star systems, their positions were stored correctly in Zig, but once the data returned to Godot for rendering, the interpolation broke — fleets would jump around the screen instead of moving smoothly along their paths.

The culprit was the serialization layer. Zig and Godot were using different coordinate formats. I standardized the data structure and updated the movement interpolation code to properly handle the incoming data. With that fix, fleet movement became smooth and visually accurate again.


Building Systematic Validation

Ad-hoc bug fixes weren’t enough; I needed systematic validation. I built a comprehensive serialization parity test framework to catch any mismatch before it caused a production bug.

The new test infrastructure compares both engines after every operation. It serializes game state from Godot to JSON, deserializes it in Zig, runs a simulation step, then serializes Zig’s results back to JSON and compares every field — ensuring no corruption or drift between engines.

This framework immediately revealed several subtle issues:

  • Inconsistent resource formatting across empires

  • Rounding errors in planet population data due to floating-point differences

  • Missing serialized fields in certain edge cases

I spent the rest of the day tightening the serialization code. Every numeric field now includes explicit precision validation. Optional fields have null checks. Missing legacy data gets default values. The result is a much more defensive and resilient serialization layer.


Achieving Population Growth Parity

Once the parity tests stabilized, a new challenge surfaced: population growth parity. The tests passed for most systems, but population numbers were diverging slightly between engines.

The root cause turned out to be integer math in Zig where floating-point calculations were required. After switching the intermediate values to floating-point, both engines produced identical results down to the last decimal place — restoring confidence in one of the game’s most important mechanics.


Fixing Test Infrastructure Race Conditions

The deeper I tested, the more subtle issues emerged. The asynchronous turn-processing code in the test framework was causing race conditions: some tests were checking results before a turn had actually finished.

I fixed this by adding proper sequencing and awaits. Each turn now fully completes before validation begins, eliminating false failures and making the test results rock-solid.


Floating-Point Precision in Max Population Calculations

Another precision issue appeared in maximum population calculations. Planets calculate their population limits from habitability scores. Godot used single-precision floats, while Zig used double-precision. Over long playthroughs, these tiny discrepancies added up.

I standardized both engines to use double-precision floating-point for all population math. Now, population caps match exactly — no more subtle drift.


Debugging Star Rendering

The day ended with a star rendering issue. After running turns through the Zig simulation, a few stars failed to render correctly on the galaxy map. The investigation pointed to a coordinate transformation problem in the rendering pipeline. This one’s still in progress, but it’s the next problem on my list.


Technical Breakdown

  • Ten commits completed

  • Three hundred twenty-seven lines added for the serialization parity framework

  • Eighty-one lines changed for population growth fixes across four files

  • Six functions updated to standardize floating-point precision

  • Fifty-three lines added to improve asynchronous test sequencing

The parity suite now runs fifteen validation scenarios, covering empire resources, fleet positions, planet populations, star system states, and cross-turn consistency. Every test executes both engines side by side and compares outputs field by field.


Current Status

The Zig backend is now fully running in production with comprehensive parity validation.
✔ Population growth matches exactly
✔ Serialization round-trips are verified
✔ Fleet movement is smooth and accurate
⚙️ Star rendering issue still under investigation

This validation work might not be flashy, but it’s essential. The dual-engine architecture only works if both engines produce identical results. These parity tests prove that the fifty-two-times performance gain isn’t coming at the cost of correctness.


Next Steps

  • Finish debugging the star rendering issue

  • Add new parity tests for edge cases like empire bankruptcy and fleet combat

  • Begin profiling the Zig simulation to find remaining performance bottlenecks

  • Explore new simulation features that take advantage of the speed boost


Thanks for following along with the development of Stellar Throne.

You can find more updates and technical breakdowns at MrPhilGames.com.

Stellar Throne Devlog #5 — All 21 Zig Simulation Phases Implemented + Parity Tester Online (52.7× Faster)

Hey everyone — MrPhil here with a major development update for Stellar Throne, my sci-fi 4X strategy game.

This week I finished implementing all 21 simulation phases in Zig and brought the parity testing framework online to compare Zig vs. Godot turn-by-turn. The Zig engine is feature-complete at the phase level, but there are still parity differences to resolve before it becomes the default runtime.


🧠 Dual-Engine Architecture (Why Two Engines?)

  • Godot handles the UI, visualization, and input.

  • Zig runs the high-performance simulation of empires, colonies, economies, and galactic systems.

The goal is to keep Godot flexible while Zig delivers raw simulation speed — both verified through automated parity testing.


⚙️ Track Two Wrap-Up: 21 Phases Implemented

At the start of the week, only a few phases were functional. By the end, all 21 simulation phases were implemented and validated at the unit level.

Highlights:

  • Population growth, colony development, infrastructure maintenance

  • Resource extraction and production chains

  • Fleet movement and logistics

  • Natural disasters and random events

  • Quest system and narrative progression

  • Diplomacy drift, relationship updates, and victory conditions

  • Military combat resolution and research progression

  • Final integration phase that ties everything together

Each phase includes validation tests to ensure predictable behavior.


🧩 Real Data via TOML (No More Magic Numbers)

I ported the TOML configuration system so the simulation now loads real game data:

  • load_gameplay() — balance parameters and constants

  • load_resources() — economy definitions and production chains

  • load_content() — events, quests, and narrative triggers

This added 800 + lines of Zig, replacing thousands of hard-coded values.
On the Godot side, the Research Manager migrated from JSON to TOML for consistency.


🧪 Test Suite Rehab + Fixes

The Zig test suite had bit-rotted (wrong layout, 91 compile errors).
I:

  • Reorganized tests under src/tests

  • Wrote a test health monitor script

  • Fixed 11 failing tests (RNG distribution, TOML table arrays, event weighting, colony growth, diplomacy drift)

✅ All Zig tests now pass.


🌉 Godot ↔ Zig Bridge + Parity Harness

Built the Turn Simulation Service in Godot:

  1. Serialize state to JSON

  2. Run a turn in Zig

  3. Deserialize results back into Godot

Added:

  • Runtime engine toggle (Godot ↔ Zig A/B testing)

  • Parity testing framework that runs identical turns in both engines and compares every field across 10–100 empires and 10–250 systems


🔎 What Parity Testing Found (Work in Progress)

  • Godot and Zig diverge slightly on population growth and stability formulas.

  • Some mismatches come from floating-point drift and minor logic differences.

So, while Zig has feature parity at the phase level, behavioral parity is not yet complete.


⚡ Performance Snapshot

In large galaxy tests, the Zig simulation ran ≈ 52.7× faster than Godot.


📈 Commits at a Glance

Area Lines Added Notes
Zig Simulation ~5 000 Turn simulator, data structures, config loader
Tests ~2 000 9 new test files
Godot Integration 376 Turn Simulation Service
Debug Toggle 139 Runtime engine swap
Parity Framework 864 Cross-engine verification
Docs Extensive Phase summaries + testing guides

✅ Current Status

  • ✅ All 21 phases implemented in Zig

  • ✅ All Zig tests passing

  • ✅ TOML data loader integrated

  • ✅ Parity testing framework online

  • 🚧 Parity differences remain to be resolved

  • 🚧 Zig engine not default yet


🔭 Next Steps

  • Fix remaining parity gaps (population, stability, diplomacy)

  • Reduce floating-point variance

  • Once identical outputs are confirmed, switch to Zig as default for full performance gain


That’s this week’s devlog — thanks for reading!
Follow progress and upcoming milestones at mrphilgames.com.

— MrPhil

Stellar Throne DEVLOG #4 — The Great Migration: GDScript to Zig Complete 🚀


🧠 Win • Challenge • Next

  • Win: The Godot → Zig port reached a huge milestone — gdzig is running smoothly and the simulation core is nearly 100 % native.
  • Challenge: The TOML port was incomplete and needed several file rewrites before integration could succeed.
  • Next: Continue the full simulation port to Zig, building on the completed manager migration.

⚙️ 100 % Zig Migration Complete (23 / 23 Managers)

All 23 game managers are now fully ported from GDScript to Zig.

  • Final additions: ZigAIDirectorManager, ZigAIEmpireManager, and ZigQuestManager
  • 333 tests passing (256 Zig + 77 GUT) — zero compiler warnings, zero leaks

📊 TOML Configuration Migration

Gameplay configuration has moved entirely to TOML:

  • 22 TOML files now act as the single source of truth for balance, events, and systems
  • Legacy JSON assets archived with complete documentation

🚀 Simulation System Track 2

The Turn Simulator foundation is now live:

  • 23 phases planned — 4 implemented (resource generation, upkeep, colony growth, research)
  • Economic feedback loops and performance benchmarks already show 20–50× speed gain

Track 2 Progress: 21 % complete and climbing.


🧩 Testing, Docs & Performance

  • 333/333 tests passing (100 %) across Zig + GUT
  • Documentation reorganized (72 → 57 files)
  • Hybrid Zig/GDScript architecture yielding major speed ups

6 000 lines of Zig code, 2 300 lines of tests, 2 500 lines of new docs — 31 commits in 24 hours.


🪐 The Big Picture

Track 1 (Migration): ✅ Complete
Track 2 (Simulation): 🚧 Underway (21 %)

The simulation framework is ready to handle complex turn-based economics and AI strategy at empire scale.


CTA: Learn more at MrPhilGames.com

When the Bug Breaks Before You Do

A couple days ago was… maddening.

I’d been wrestling with the same bug for days—the kind that sits in your code like a smug little goblin, daring you to come find it. Every time I thought I had it cornered—poof—it would slip away into another layer of logic.

The problem sounded simple enough: combat is simultaneous. Even if a ship gets destroyed, it should still get to fire its weapon that turn. But the UI shouldn’t mark it as destroyed until after all attacks are resolved.

Except in my game, ships weren’t showing their dramatic “destroyed” flair until the start of the next turn. By then, the moment was gone. It was like telling a joke and waiting five minutes for the punchline to land.

I threw everything at it:

  • The “outside consultant” trick—pretending Claude was a hired expert swooping in to save the day.

  • The “you’re a zookeeper” trick. (Don’t ask.)

  • Breaking the workflow into phases.

  • Having Claude explain the code back to me.

  • Running the debugger subagent.

  • Asking it to think hard… harder… ultra-think.

  • Asking Claude to improve my prompt.

  • Diagramming the problem like a detective on a conspiracy board.

  • Adding a ton of debug logs.

  • Even pulling in ChatGPT to craft a better Claude prompt.

  • Describing the issue in painstaking detail—right down to which variables changed on which frame.

Nothing. Worked.

And this wasn’t even a crash bug—the game ran fine. It was just wrong. The kind of subtle pacing flaw that players might not notice consciously, but would feel in their bones. The kind that makes everything feel just slightly “off.”

By about hour six that day, I was leaning back in my chair, staring at my code, wondering if the bug was less about the project and more about me.

Then—somewhere between frustration and surrender—I tried one more approach. Nothing special about it. No perfect galaxy-brain prompt. Just another attempt in a long line of attempts. And this time… it worked. The ships died exactly when they were supposed to, the UI updated cleanly, and the combat flow finally felt right.

I’d love to tell you it was some brilliant debugging insight or the magic words that unlocked Claude’s genius. But it wasn’t. It was just the luck of the dice—one more roll, one more try, and this time it came up in my favor.

Bugs, Battles, and a Bit of Scroll Sorcery

Today was a tale of two projects — one in the depths of space, the other in the heart of the classroom.


Stellar Throne – The Bug War

Work on Stellar Throne felt like navigating an asteroid field with a faulty nav computer. The main nemesis? The Combat UI’s combat log, which decided to also control the battlefield zoom. Imagine trying to scroll through battle events and suddenly you’re looking at the galaxy from light-years away.

The fix meant tearing apart the screen infrastructure, carefully reassembling it without losing functionality. Once the battlefield stopped “breathing” under my mouse wheel, I ported the same fix to the main starmap. Smooth scrolling across the board now.

Other combat encounters:

  • Mysterious spontaneous ship damage (bug or stealth weapon… to be determined).

  • Mouse clicks going on strike after combat.

  • Destroyed ships clinging to life visually.

It’s not glamorous work, but it’s the kind of quiet victory that makes the rest of the game shine.


ClaudeCraft – Building the Blueprint

Meanwhile, on the teaching front, I made big strides with ClaudeCraft, my upcoming course on AI-assisted game development. Modules 7–10 got their teaching guides, the course outline got tightened to stay laser-focused on Claude workflows (no drifting into generic gamedev land), and I even expanded it to 11 modules with some bonus content for extra punch.

I also whipped up a shiny roadmap image and, in the process, felt the course concept click into place — a clear path from AI novice to confident AI-powered game creator.

I’m still chewing on the challenge of reaching the right audience, but the bones of the course are solid and the waitlist is live at ClaudeCraft.com.