Note
Go to the end to download the full example code.
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)
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']