Appearance
File Structure
A single ATLAS game can be authored as one .yaml/.json file (see Game Examples), or split across multiple files in a project directory. This page describes that directory convention.
Why split files at all?
Formats like Ludii's .lud let you factor a definition into reusable pieces, but that reuse is powered by an implicit, global library of ludeme macros ("HopCapture", "DraughtsEquipment", "BlockWin", ...) defined outside the file you're reading. Understanding any single .lud file requires prior knowledge of that external library — the file is not self-contained.
ATLAS's directory convention solves the same "my definition is too big for one file" problem differently: every file you split out is a plain, complete fragment of the same strict atlas.v1.Game schema — no macros, no external ludeme references, nothing to look up elsewhere. Reading entities/red_checker.yaml on its own tells you everything about that entity, because it is an Entity object, verbatim.
Directory layout
An ATLAS project directory recognizes a fixed set of file names. Each contributes exactly one top-level field of the merged Game:
| File | Required | Contributes |
|---|---|---|
game.yaml | Yes | id, name, global_properties |
players.yaml | No | players |
zones.yaml | No | zones |
setup.yaml | No | setup |
actions.yaml | No | root-level actions |
entities.yaml | No | entities (as a single list) |
entities/ (directory) | No | entities (one file per Entity) |
presentation.yaml | No | loaded separately — see Presentation & assets below |
.yml and .json extensions are accepted everywhere .yaml is shown above.
Self-contained, not implicit
There is no $include/import directive to chase down. The loader discovers files purely by their fixed names in the directory you point it at — nothing references anything outside that one folder.
The entities/ directory
Instead of one entities.yaml containing a list, you can give each Entity blueprint its own file under entities/. Unlike every other split file, an entity file's top-level fields are the Entity object itself — no wrapping entities: key:
yaml
id: red_checker
entity_type: PIECE
tags: [RED, CHECKER]
string_properties:
name: "Peon"
owner: "Red"
numeric_properties:
is_king: 0yaml
id: black_checker
entity_type: PIECE
tags: [BLACK, CHECKER]
string_properties:
name: "Peon"
owner: "Black"
numeric_properties:
is_king: 0entities.yaml and an entities/ directory are mutually exclusive — pick one form per project.
Merge rules
The loader merges every recognized file into a single payload and then validates it exactly as it would a single-file game, so nothing about the strict schema changes because you split your files:
- Every field comes from exactly one file. If
zonesshows up in bothgame.yamlandzones.yaml(orentitiesin bothentities.yamlandentities/), loading fails immediately with an error naming both sources. game.yamlis required. A directory with nogame.yaml/.yml/.jsonfails to load.- Unrecognized files are rejected. A stray
zone.yaml(missing thes) or any other file not in the table above fails loading with an explicit error — the same strict, "reject unknown fields" philosophy the loader already applies to unknown properties now also applies to unknown files.
This keeps splitting deterministic: there's exactly one valid way to read a project directory back into the same Game you'd get from a hand-merged single file.
Presentation & assets
atlas.proto's Game schema deliberately has no fields for sprites, bitmaps, colors, or any other rendering concern — Zones and Entities describe data topology and state only. Visual assets instead live in an optional, separately-validated presentation.yaml, loaded against its own presentation.proto contract and never merged into Game:
yaml
game_id: checkers
entities:
- entity_id: red_checker
assets:
- kind: bitmap
path: "assets/red_checker.png"
variant: "default"
- kind: bitmap
path: "assets/red_checker_king.png"
variant: "king"
style_properties:
color: "#B22222"
zones:
- zone_id: main_board
assets:
- kind: bitmap
path: "assets/board_8x8.png"
style_properties:
board_style: "Chess"entity_id/zone_idmatch the correspondingEntity.id/Zone.idin the rules definition — no other coupling exists between the two files.assetsis a list of{ kind, path, variant }references (a sprite, bitmap, 3D model, icon, etc.);variantdistinguishes states like a promoted/kinged piece.style_propertiesis a free-form string map for renderer hints (color, scale, board skin name, ...) that don't warrant their own asset file.- A game can have multiple presentations (e.g. a
presentation.yamland apresentation.isometric.yamlloaded on demand) since nothing aboutGameever references one.
Because rules and presentation are validated independently, a rules engine, AI, or headless server never needs to load — or even have access to — art assets at all.
Loading a project directory
Both language bindings expose a directory-aware loader alongside the existing single-file one:
csharp
using Atlas.Core.Loading;
var projectLoader = new AtlasProjectLoader();
GameBundle bundle = projectLoader.LoadFromDirectory("examples/checkers-split");
Game game = bundle.Game;
Presentation? presentation = bundle.Presentation; // null if no presentation.yamlts
import { AtlasProjectLoader } from "atlas-core-ts";
const projectLoader = new AtlasProjectLoader();
const { game, presentation } = await projectLoader.loadFromDirectory("examples/checkers-split");
// presentation is undefined if no presentation.yaml existsThe validation CLIs also accept a directory in place of a single file:
sh
dotnet run --project src/Atlas.Cli -- examples/checkers-split
node dist/cli.js examples/checkers-split # from src/atlas-core-tsSee examples/checkers-split in the repository for a complete working project directory built this way, split out of the single-file Checkers example.