Westmarches Game Blog

Official blog for the Westmarches Game Software

Comparing Game Advancement Across Campaigns

Building a Console Tool to Validate Progression Systems


One of the quiet challenges of running a living campaign system is progression balance.

When advancement rules change, the real question isn’t “Does the code work?” — it’s:

How does this change affect real characters who already exist?

In my Westmarches campaign platform, I recently added a console command designed specifically to answer that question.

This post walks through the motivation, implementation, and lessons behind the Game Advancement Comparison Command.


The Problem: Progression Changes Are Hard to Validate

Westmarches campaigns evolve.

Rules shift.
Advancement systems get tuned.
Experience pacing changes.

When that happens, I need to know:

  • Do characters level faster or slower?
  • Who benefits from the new system?
  • Who gets unintentionally penalized?
  • Is the change fair across the player base?

Manual comparison isn’t realistic once dozens of characters and sessions exist.

So instead of guessing, I built tooling.


The Solution: A Console Comparison Command

This commit introduces a new Yii console controller command:

php yii command/compare-game-advancement <oldCampaignId> <newCampaignId>

The goal is simple:

Calculate character advancement using two different campaign configurations and compare the results.

Rather than modifying data, the command performs a pure analysis pass.


Why a Console Command?

This isn’t an admin UI feature.

It’s developer tooling.

A console command gives me:

  • Fast execution
  • Scriptability
  • Repeatability
  • Zero risk to production data
  • Easy integration into migration workflows

It becomes a diagnostic instrument rather than an application feature.


How It Works

1. Validate Campaign Inputs

The command first ensures both campaigns exist.

If either campaign cannot be found, execution stops immediately with a clear message.

This protects against accidental comparisons and bad input.


2. Gather Characters from the Original Campaign

We treat the old campaign as the source of truth:

  • Load all characters belonging to the original campaign
  • Abort early if none exist

This ensures we’re comparing identical player histories.


3. Recalculate Advancement Twice

For every character:

  1. Fetch all games played
  2. Run advancement calculation using:
    • Old campaign rules
    • New campaign rules

Conceptually:

oldLevel = advancement(oldRules)
newLevel = advancement(newRules)

No database updates occur.
We are strictly observing outcomes.


4. Output Machine-Readable Results

The command prints structured output:

{
  "Aria": ["3", "4"],
  "Thorn": ["5", "5"],
  "Mira": ["2", "3"]
}

Each entry represents:

Character Name: [Old Level, New Level]

This format makes it easy to:

  • Paste into spreadsheets
  • Diff results
  • Feed into scripts
  • Visualize balance changes

Why This Matters

Game systems often fail not because mechanics are wrong, but because migration effects aren’t understood.

Changing progression without analysis can:

  • Accidentally nerf long-time players
  • Inflate power levels
  • Break encounter balance
  • Create perceived unfairness

This tool turns progression design into something measurable.

Instead of arguing about theory, I can now answer:

“What actually happens to my players?”


Hidden Design Principle: Tooling Over Guesswork

This commit reflects a philosophy I try to follow:

If I need to check something twice, it deserves a tool.

Rather than manually inspecting characters, I invested a small amount of engineering effort to create permanent infrastructure.

Future benefits include:

  • Testing experimental advancement systems
  • Comparing seasonal campaign rules
  • Running balance audits before launch
  • Supporting automated regression testing

What I Like About This Approach

A few intentional decisions stand out:

Stateless Analysis

The command reads data but never mutates it.

Safe tools get used more often.


Reuse Existing Logic

Instead of duplicating advancement rules, it calls:

CampaignCharacter::advancement(...)

One source of truth means fewer bugs.


Developer-First UX

Clear stdout messages and structured output make the command pleasant to use.

Small touches matter when tools become part of daily workflow.


Future Improvements

Some ideas already on the roadmap:

  • CSV export option
  • Level delta summaries
  • Percentage change reporting
  • Highlight characters most impacted
  • Automated balance reports in CI

Eventually, this could evolve into a full campaign simulation toolkit.


Final Thoughts

Game design often focuses on creativity, but scalable campaign management demands engineering discipline.

This command bridges the two.

It lets me experiment with progression systems confidently, knowing I can measure the real consequences before players ever feel them.

And honestly, that’s one of my favorite kinds of code:

Not flashy.
Not user-facing.
Just quietly making better decisions possible.


Author

  • I’ve been passionate about computer programming since I finished playing my very first video game. You could say I was obsessed with computers after that. It wasn’t long until my parents got their first home computer. I learned how to connect to a dial-up modem to check email. Then I memorized the file structure of the Operating System. Then I took the computer apart to try and make sense what was going on. I was nine years old.

    I spent my most of my teenage years learning how to make video games on the family computer. I was already taking college level programming classes before I graduated high school.

    I spent my twenties working for many different types of companies, getting mentorship where I could and learning the business side of software.

    Westmarches game is a passion project. I enjoy playing tabletop games with my friends and this software makes our games better. I plan on contributing to it and improving it for a long time. Learn More.