Skip to content

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_position
Traditional 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"