Appearance
Game Examples
To give you a better idea of how all of the ATLAS concepts weave together into a complete package, here are two full tabletop game implementations written inside our declarative DSL.
Traditional Chess
This specification demonstrates ATLAS's declarative movement grammar: instead of hand-writing a bespoke top-level Action for every piece, each entry under pieces describes its legal moves directly via a movement array (Step, Leap, Slide, Capture, EnPassant), gated by condition expressions that read the destination ($target), the path traveled ($path), and the piece itself (self). The board is seeded in one line via a standard FEN string (board.setup.fen), global legality is enforced by global_rules.move_constraints (e.g. you may never leave your own King in check), the game ends per win_conditions (checkmate/stalemate), and reactive behavior — pawn promotion via a triggers entry, castling via an entity-owned actions entry with targets — is expressed alongside the piece it belongs to. See Movement & Piece Actions for the full grammar reference.
yaml
id: chess
name: Chess
version: 1.0.0
players:
roles: [White, Black]
board:
topology: Grid
layout: 8x8
setup:
fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
global_rules:
turn_order: Alternating
move_constraints:
- id: PreventSelfCheck
condition: $engine.state.is_in_check($player.active) == false
win_conditions:
- id: Checkmate
condition: >
$engine.state.is_in_check($player.active) == true and
$engine.state.get_valid_moves($player.active).count == 0
winner: $player.inactive
- id: Stalemate
condition: >
$engine.state.is_in_check($player.active) == false and
$engine.state.get_valid_moves($player.active).count == 0
result: Draw
pieces:
- id: Pawn
symbol: P
movement:
- type: Step
direction: Forward
range: 1
condition: $target.is_empty
- type: Step
direction: Forward
range: 2
condition: $target.is_empty and $path.is_clear and self.has_moved == false
mutations: [ set self.en_passant_vulnerable = true ]
- type: Capture
direction: ForwardDiagonal
range: 1
condition: $target.has_enemy
- type: EnPassant
direction: ForwardDiagonal
range: 1
condition: $target.is_empty and $engine.board.adjacent_enemy.en_passant_vulnerable == true
mutations: [ destroy $engine.board.adjacent_enemy ]
triggers:
- event: AfterMove
condition: self.y == $board.final_rank(self.owner)
action: Promote(Queen, Rook, Bishop, Knight)
- id: Knight
symbol: N
movement:
- type: Leap
direction: [2, 1]
symmetry: Full
condition: $target.is_empty or $target.has_enemy
- id: Bishop
symbol: B
movement:
- type: Slide
direction: Diagonal
range: Unlimited
condition: $path.is_clear and ($target.is_empty or $target.has_enemy)
- id: Rook
symbol: R
properties:
has_moved: false
movement:
- type: Slide
direction: Orthogonal
range: Unlimited
condition: $path.is_clear and ($target.is_empty or $target.has_enemy)
- id: Queen
symbol: Q
movement:
- type: Slide
direction: Omnidirectional
range: Unlimited
condition: $path.is_clear and ($target.is_empty or $target.has_enemy)
- id: King
symbol: K
movement:
- type: Step
direction: Omnidirectional
range: 1
condition: $target.is_empty or $target.has_enemy
actions:
- id: Castle
condition: self.has_moved == false and $engine.state.is_in_check(self.owner) == false
targets:
- entity: Rook
condition: >
target.has_moved == false and
target.owner == self.owner and
$path.is_clear(self, target) and
$engine.state.is_path_safe(self, target) == true
mutations:
- move self 2 steps toward target
- move target 1 step past self final_positionTraditional Checkers
This specification highlights simple mutations across the board state, leveraging ffects running continuously in the background to automatically upgrade pieces to Kings the moment they arrive at the end of the board.
yaml
# Checkers — rewritten to conform strictly to schema/atlas.proto.
#
# NOTE: atlas.proto currently models rules/topology/actions only. It has no
# fields yet for win conditions or turn phases/timeline, so those concepts
# from the original informal draft of this file are intentionally omitted
# here (see docs/guide/examples.md for the richer, aspirational version of
# this example). King promotion is expressed as a reactive Action owned by
# each checker entity, using `raw_expression` for the position-based guard
# since ConditionNode's structured Comparison can't yet reference board
# coordinates. Initial placement now uses `setup[].board` — a symbol grid
# (one row per rank, top-to-bottom, space-separated symbols) resolved
# through `legend` into entity/owner pairs; "." always means an empty cell.
id: checkers
name: Checkers
global_properties:
author: "Traditional"
version: "1.0.0"
players:
- id: Red
- id: Black
zones:
- id: main_board
topology_type: GRID
visibility_type: PUBLIC
capacity: 64
layout: "8x8"
- id: graveyard
topology_type: SET
visibility_type: PUBLIC
capacity: -1
setup:
- zone_id: main_board
board:
legend:
r:
entity_id: red_checker
b:
entity_id: black_checker
rows:
- "b . b . b . b ."
- ". b . b . b . b"
- "b . b . b . b ."
- ". . . . . . . ."
- ". . . . . . . ."
- ". r . r . r . r"
- "r . r . r . r ."
- ". r . r . r . r"
entities:
- id: red_checker
entity_type: PIECE
tags:
- RED
- CHECKER
string_properties:
name: "Peon"
owner: "Red"
numeric_properties:
is_king: 0
actions:
- id: king_promotion
requires:
raw_expression: "@entity.self.string_properties.owner == 'Red' and @entity.self.position.row == 8"
mutations:
- target: "@entity.self"
operation: SET_PROPERTY
property: "is_king"
value: "1"
triggers:
- mode: FORCED
event: "After.@move_diagonal"
- id: black_checker
entity_type: PIECE
tags:
- BLACK
- CHECKER
string_properties:
name: "Peon"
owner: "Black"
numeric_properties:
is_king: 0
actions:
- id: king_promotion
requires:
raw_expression: "@entity.self.string_properties.owner == 'Black' and @entity.self.position.row == 1"
mutations:
- target: "@entity.self"
operation: SET_PROPERTY
property: "is_king"
value: "1"
triggers:
- mode: FORCED
event: "After.@move_diagonal"
actions:
- id: move_diagonal
interaction_hint:
mode: SPATIAL_TARGET
prompt_text: "Choose a diagonal destination tile"
mutations:
- target: "@entity.selected"
operation: MOVE_ZONE
destination: "@target.zone"
- id: jump_and_capture
interaction_hint:
mode: SPATIAL_TARGET
prompt_text: "Choose a diagonal jump destination"
mutations:
- target: "@target.passed_entity"
operation: MOVE_ZONE
destination: "@zone.graveyard"
- target: "@entity.selected"
operation: MOVE_ZONE
destination: "@target.zone"Go Fish
This specification demonstrates moving entities between hidden deck structures and private player hands. It uses state variables and state-based effects to automatically handle scoring when a player completes a group of four identical cards.
yaml
# Go Fish — rewritten to conform strictly to schema/atlas.proto.
#
# NOTE: atlas.proto currently models rules/topology/actions only. It has no
# fields yet for dealing, win conditions, turn phases/timeline, or
# variable player counts, so those concepts from the original informal
# draft of this file are intentionally omitted here (see
# docs/guide/examples.md for the richer, aspirational version of this
# example). This version is fixed to 2 players with explicit per-player
# zones, since Zone has no `per_player` templating field yet. Mutation has
# no per-entity filter field, so "ask for rank" targets the whole matching
# subset via an @-syntax selector instead of a structured condition. The
# deck is seeded via `setup[].seeds` — a flat instance count, since card
# order within a shuffled stack isn't meaningful to declare statically.
id: go_fish
name: Go Fish
global_properties:
author: "Traditional"
version: "1.0.0"
players:
- id: player_1
- id: player_2
zones:
- id: main_deck
topology_type: STACK
visibility_type: HIDDEN
capacity: 52
- id: player_1_hand
topology_type: SET
visibility_type: PRIVATE
capacity: -1
- id: player_2_hand
topology_type: SET
visibility_type: PRIVATE
capacity: -1
- id: player_1_score_pile
topology_type: SET
visibility_type: PUBLIC
capacity: -1
- id: player_2_score_pile
topology_type: SET
visibility_type: PUBLIC
capacity: -1
setup:
- zone_id: main_deck
seeds:
- entity_id: card
count: 52
entities:
- id: card
entity_type: CARD
string_properties:
suit: "*"
rank: "*"
numeric_properties:
value: 1
actions:
- id: ask_for_rank
interaction_hint:
mode: CONTEXT_MENU
prompt_text: "Choose an opponent and a rank to ask for"
mutations:
- target: "@target.player_hand.cards_matching_rank"
operation: MOVE_ZONE
destination: "@player.current.hand"
- id: go_fish
interaction_hint:
mode: CONTEXT_MENU
prompt_text: "Draw the top card of the deck"
mutations:
- target: "@zone.main_deck.top"
operation: MOVE_ZONE
destination: "@player.current.hand"