Parallel states – War of the Ring

This example demonstrates parallel states using StateChart. A parallel state activates all child regions simultaneously. Each region operates independently – events in one region don’t affect others.

The done.state event fires only when all regions reach a final state.

from statemachine import State
from statemachine import StateChart


class WarMachine(StateChart):
    """The War of the Ring with parallel fronts.

    Three independent fronts run simultaneously inside the ``war`` parallel state:
    Frodo's quest to destroy the Ring, Aragorn's path to kingship, and
    Gandalf's defense of the realms.
    """

    class war(State.Parallel):
        class frodos_quest(State.Compound):
            shire = State("The Shire", initial=True)
            mordor = State("Mordor")
            mount_doom = State("Mount Doom", final=True)

            journey = shire.to(mordor)
            destroy_ring = mordor.to(mount_doom)

        class aragorns_path(State.Compound):
            ranger = State("Ranger", initial=True)
            king = State("King of Gondor", final=True)

            coronation = ranger.to(king)

        class gandalfs_defense(State.Compound):
            rohan = State("Rohan", initial=True)
            gondor = State("Gondor", final=True)

            ride_to_gondor = rohan.to(gondor)

    peace = State("Peace in Middle-earth", final=True)
    done_state_war = war.to(peace)
statechart parallel machine

All regions activate at once

Entering the war parallel state activates the initial child of every region.

sm = WarMachine()
config = set(sm.configuration_values)
print(f"Active states: {sorted(config)}")
expected = {"war", "frodos_quest", "shire", "aragorns_path", "ranger", "gandalfs_defense", "rohan"}
assert expected.issubset(config)
Active states: ['aragorns_path', 'frodos_quest', 'gandalfs_defense', 'ranger', 'rohan', 'shire', 'war']

Independent transitions

An event in one region does not affect others.

sm.send("journey")
print(f"Frodo journeys: {sorted(sm.configuration_values)}")
assert "mordor" in sm.configuration_values
assert "ranger" in sm.configuration_values  # Aragorn unchanged
assert "rohan" in sm.configuration_values  # Gandalf unchanged
Frodo journeys: ['aragorns_path', 'frodos_quest', 'gandalfs_defense', 'mordor', 'ranger', 'rohan', 'war']

Partial completion

One region reaching final doesn’t end the parallel state.

sm.send("coronation")
print(f"Aragorn crowned: {sorted(sm.configuration_values)}")
assert "king" in sm.configuration_values
assert "war" in sm.configuration_values  # parallel still active
Aragorn crowned: ['aragorns_path', 'frodos_quest', 'gandalfs_defense', 'king', 'mordor', 'rohan', 'war']

All regions reach final

When all regions reach final, done.state.war fires and transitions to peace.

sm.send("ride_to_gondor")
print(f"Gandalf in Gondor: {sorted(sm.configuration_values)}")
assert "war" in sm.configuration_values  # Frodo not done yet

sm.send("destroy_ring")
print(f"Peace: {sorted(sm.configuration_values)}")
assert {"peace"} == set(sm.configuration_values)
Gandalf in Gondor: ['aragorns_path', 'frodos_quest', 'gandalfs_defense', 'gondor', 'king', 'mordor', 'war']
Peace: ['peace']