Appearance
The State Machine: Flow of Time
A robust deterministic engine relies completely on a tight State Machine. If you don't know exactly what phase a turn is in, you cannot dictate valid logic updates.
ATLAS governs time using a deeply nested structure: Stages -> Turns -> Phases.
1. Stages (Macro-State)
Stages define sweeping chapters of a game, like Drafting, Setup, or MainGameplay. They contain transitions which conditionally route the engine based on global state.
2. Turns (Priority Loops)
Within a Stage, the game typically enters a revolving loop of priority known as Turns.
- Alternating: Player 1 executes their complete turn, then passes priority entirely to Player 2.
- Sequential: Player 1 completes a single Phase, then Player 2 completes the same phase before progressing.
- Simultaneous: Both players execute actions concurrently, resolving once both pass.
3. Phases & Valid Actions (Micro-State)
Phases act as the atomic time block where players are actually authorized to act.
The Whitelist Strategy
A Phase is completely locked down by default. By explicitly defining the valid_actions array, it behaves as a strict security whitelist. If a requested action isn't listed, the engine strictly rejects the request.
yaml
timeline:
stages:
- id: MainGameplay
turns:
type: Alternating
order:
- Player1
- Player2
phases:
- id: UpkeepPhase
valid_actions: []
auto_advance: true
on_enter:
- $Action.DrawCard(@player.current)
- id: ActionPhase
valid_actions:
- PlayCard
- Attack
- UseEntityAction
- Pass
end_condition: $Action.Passjson
{
"timeline": {
"stages": [
{
"id": "MainGameplay",
"turns": {
"type": "Alternating",
"order": ["Player1", "Player2"],
"phases": [
{
"id": "UpkeepPhase",
"valid_actions": [],
"auto_advance": true,
"on_enter": [
"$Action.DrawCard(@player.current)"
]
},
{
"id": "ActionPhase",
"valid_actions": ["PlayCard", "Attack", "UseEntityAction", "Pass"],
"end_condition": "$Action.Pass"
}
]
}
}
]
}
}Engine Rejection
If a user calls a PlayCard action but they are inside an UpkeepPhase (which has valid_actions: []), the backend engine immediately terminates the request. This provides ironclad protection against client-side exploitation.
Shorthand for simple turn orders
Not every game needs a nested Stage/Phase tree. If your game is just a single, uninterrupted Alternating/Sequential loop with no distinct phases (e.g. Chess — one action ends your turn, no upkeep/draw/combat split), you can skip timeline entirely and declare turn_order directly under global_rules instead:
yaml
global_rules:
turn_order: AlternatingAppendix: Flow Reference
Valid Turn Types
When building a Turn block, the type determines when a player hands off root interaction rights back to completing the iteration pool.
Alternating: Player 1 executes their complete turn priority entirely, and then completely passes priority exactly once they hit their macro end condition.Sequential: Player 1 completes a single Phase block, Player 2 completes the identical Phase block simultaneously, and only then do they both advance down the state machine together.Simultaneous: Both players execute actions randomly/concurrently inside the same atomic Phase block. Priority naturally collapses manually.