THE RULES BEHIND THE SOFTWARE
The Coalition Race
A policy-driven C++ simulation of agents, offers and coalition formation.
Majority threshold: 61 of 120 mandates.
Parsed source records, not a new simulation.
Visualized from committed fixture data. Source review below.
- CONTEXT
- University coursework
- MY ROLE
- Sole developer
- TIMELINE
- 2022
- EVIDENCE
- Source reviewed · illustrative diagrams
AT A GLANCE
A majority is not the only ending
A coursework simulation represents a 120-mandate parliament as a weighted graph. Agents choose neighboring parties to approach, parties collect and evaluate offers, and each joining party extends a coalition and creates a new agent. JSON snapshots make every simulation step inspectable.
My contribution +
I developed this project as university coursework. This case covers my implementation and the engineering decisions behind it.
WHAT THE SYSTEM HANDLES
The interesting parts.
Separate whom to approach from whom to join
Agent selection policies consider edge weight or party mandates. Party join policies choose the last offer or the offering coalition with the largest mandate total.
Implementation details
- Policies are selected from JSON configuration.
- Parties progress through Waiting, CollectingOffers and Joined states.
Grow a coalition through local decisions
Each simulation step advances agents and then parties. After a delayed offer-collection period, a joining party adds mandates to its selected coalition and gains a cloned agent.
Implementation details
- A new agent immediately takes a step after being cloned.
- Execution ends when a coalition reaches at least 61 mandates or all parties have joined.
Inspect the trace, including unsuccessful majorities
The CLI writes the initial state and every subsequent state as JSON. A separate Python script renders graph snapshots with party, mandate, state and coalition labels.
Implementation details
- Three committed input/output fixture pairs show concrete traces; these were parsed, not rerun.
- Fixture 03 ends with two 60-mandate coalitions: all parties joined, but neither reached a majority.
STRUCTURE & JUDGMENT
How the parts fit.
JSON configuration
Parties, agents, policies and graph
→ Parser + validationParser + validation
120 mandates and symmetric matrix
→ SimulationSimulation
Agent step, party step, termination
→ Selection / join policies / Graph + Party + Coalition / JSON snapshotsSelection / join policies
Independent virtual strategies
→ Graph + Party + CoalitionGraph + Party + Coalition
Offers, states and mandate totals
JSON snapshots
Recorded state after each step
→ Python graph viewerPython graph viewer
NetworkX / Matplotlib plots
- C++11
- Strategy interfaces
- Rule of Five
- nlohmann JSON
- Make / g++
- Python
- NetworkX / Matplotlib
Two strategy boundaries for two decisions
SelectionPolicy chooses which neighboring party an agent approaches; JoinPolicy decides which offer a party accepts. Parser constructs the requested concrete strategies.
WHY IT MATTERSThe two choices can vary independently while the simulation lifecycle stays the same. This design rationale is inferred from source.
Make polymorphic ownership explicit
Agent and Party implement destructors, copy and move operations for their owned policy pointers. Copying clones the virtual policy; moves transfer ownership and clear the source pointer.
WHY IT MATTERSAgent cloning and container operations exercise C++ resource-management mechanics instead of relying on shallow copies of owned pointers.
Record intermediate states instead of only a winner
Main saves the initial state and each simulation step; Parser includes parties, agents, coalition membership, mandates and graph edges in the trace.
WHY IT MATTERSA reviewer can see why a coalition grew and distinguish a majority from the all-parties-joined stopping condition.
Scope, limitations & what I would revisit
- Termination does not guarantee a majority: the committed third fixture ends at 60 versus 60 because every party has joined. This is not a general coalition impossibility solver.
- There is no separate no-progress termination condition. A disconnected configuration can leave unjoined parties without further offers while no coalition reaches 61; this is a source-derived risk, not an executed reproduction.
- Mandates selection initializes its candidate using a different eligibility condition from the later loop. That can retain an ineligible initial candidate, so optimal or universally correct selection is not established.
- Validation consists of source review and inspection of three committed traces, not a new compile or asserted test run. The Python viewer opens graph plots; it is not an interactive web simulator.