diff --git a/README.md b/README.md index 7fbb2a07..c14260a9 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,110 @@ # Ludic -An AI-first, ahead-of-time **compiled** game language with an ECS core, a -deterministic fixed-point runtime, and a native 2D backend. `ludicc` lowers -Ludic to LLVM IR itself and emits a native binary — **and `ludicc` is itself -written in Ludic.** +Ludic is an **ahead-of-time compiled** language for 2D games with an +entity-component core, a deterministic fixed-point runtime, and its graphics +stack built into the language. `ludicc` lowers Ludic straight to LLVM IR and +emits a native binary — and **`ludicc` is itself written in Ludic**, compiles +its own source to a byte-exact fixpoint, and rebuilds from a checked-in IR seed +with **no C compiler in the loop**. ``` .ludic ──► ludicc ──► LLVM IR ──► object ──► native binary (in Ludic) ``` -**No C is generated, compiled or linked anywhere in a build.** There is no -interpreter, no transpiler, and no C runtime: the framebuffer, sprites, PNG -decoding, TrueType text, the retained UI, the registers and the RNG are all -written in Ludic (`runtime/native/*.ludic`), and the macOS window is -hand-written LLVM IR (`runtime/native/cocoa.ll`). Beneath that sits only the -platform's own ABI — malloc, fwrite, objc_msgSend, CoreGraphics — reached by -compiler intrinsics, the same floor Rust and Swift stand on. +**No C is generated, compiled or linked in a build.** No interpreter, no +transpiler, no C runtime: the framebuffer, sprites, PNG/DEFLATE decoding, +TrueType text, the retained UI, the registers and the RNG are all written in +Ludic (`runtime/native/*.ludic`); only the window seam — five `win_*` functions +— is hand-written LLVM IR against the platform ABI (`runtime/native/cocoa.ll`), +the same floor Rust and Swift stand on. -**The compiler is written in Ludic.** `selfhost/*.ludic` is a Ludic compiler — -lexer, parser and LLVM-IR backend — that compiles every example (including the -6-file JRPG) to the byte-exact same binary the original C compiler produced, and -compiles **its own source** to a fixpoint. It is built from a checked-in IR seed -(`selfhost/ludicc.seed.ll`) with clang alone; run `bin/x bootstrap-cfree` -to rebuild it with no C compiler in the loop. The former C compiler is gone. +## Backends -Neither the compiler nor the games are C. Beneath both sits only the platform's -own ABI — malloc, fwrite, objc_msgSend, CoreGraphics — reached by intrinsics, the -same floor Rust and Swift stand on. (The wasm/cross-compile/shared-library driver -paths lived in the old C compiler and are not yet re-implemented on the -self-hosted native toolchain.) +| Backend | Status | +|---|---| +| **Native 2D** (macOS/Cocoa window; headless render for CI) | **Shipping** — the default `bin/x app` target. | +| **Web / wasm32** | **In progress.** The browser platform layer is in-tree and documented — `runtime/web/` (the `` window `platform.js`, the libc-free `wasm.ll` floor) and a Node harness that diffs native vs. wasm frame-for-frame (`tools/ludic-web/run.mjs`). Emitting wasm was a capability of the retired C compiler and is **not yet re-wired on the self-hosted toolchain**; see [COMPILING.md](COMPILING.md). | -## Layout +The same is true of `--target` cross-compilation and `--shared` libraries: both +are designed and documented, both lived in the old C compiler, and both are +pending re-implementation on the self-hosted native toolchain. -| Path | What it is | -|------|-----------| -| `selfhost/*.ludic` | **the compiler, written in Ludic** — lexer, parser, and the LLVM-IR backend (ECS storage, queries, spawn, `match`/`machine`, UI, scenes, save/load, fixed-point). Concatenated by `bin/x selfhost-build`; built from `selfhost/ludicc.seed.ll` | -| `tools/x/*.ludic` | **the task runner, written in Ludic** — one binary (`bin/x`) that builds, tests, bootstraps and reseeds the whole project, replacing every shell script. `bin/x bootstrap-cfree` rebuilds the compiler from the IR seed with **no C compiler** and proves it reproduces its own IR | -| `runtime/native/core.ludic` | the runtime written *in Ludic* for the native path (framebuffer, text, registers, RNG, input) | -| `COMPILING.md` | the native pipeline: `ludicc → LLVM IR → exe/dylib`, `module`/`export`, cross-compilation, `rt_*` intrinsics | -| `runtime/native/image.ludic` | PNG decoding, images, sprites, alpha blending and 9-slice — in Ludic | -| `runtime/native/inflate.ludic` | DEFLATE decompression (RFC 1951), so PNG needs no zlib on any target | -| `runtime/native/truetype.ludic` | from-scratch TrueType loader + antialiased glyph rasterizer, in Q16.16 | -| `runtime/native/ui.ludic` | retained UI widget tree: layout, 9-slice, focus, events — in Ludic | -| `runtime/native/cocoa.ll` | the macOS window, written in LLVM IR (Objective-C runtime + CoreGraphics via their C ABI) | -| `runtime/web/wasm.ll` | the web's platform layer in LLVM IR: the allocator, bulk memory and strings, since wasm32 has no libc | -| `runtime/web/platform.js` | the browser's window — the same five `win_*` functions `cocoa.ll` implements, against a `` | -| `runtime/web/index.html` | the page a web build is served from | -| `tools/ludic-web/run.mjs` | runs a headless wasm build under Node, so native and wasm output can be diffed | -| `examples/games/chronorift.ludic` | the JRPG written in Ludic (multi-file via `import`, model-based) | -| `examples/games/menu.ludic` | a retained-UI title screen (9-slice, TrueType, focusable buttons) | -| `examples/games/snake.ludic` | a second, unrelated game — proves the language is general (same toolchain, no engine hardcoding) | -| `bin/x` | the task runner — `bin/x app examples/.ludic` compiles a program; `bin/x help` lists every command | -| `bin/x test` | regression suite: builds the compiler, compiles/runs all examples, checks save/load + diagnostics | -| `tools/ludic-tools/` | the editor toolchain, **in Ludic**: `ludic-fmt` (source formatter) and `ludic-lsp` (language server) — one lexer and one vocabulary shared by both | -| `tools/editors/` | plugins for VS Code and JetBrains, plus configuration for Neovim, Helix, Emacs, Sublime and Zed ([README](tools/editors/README.md)) | -| `bin/x test-tools` | regression suite for the toolchain: proves formatting never changes a program, drives the language server over real LSP traffic | +## Quick start -## Build & run - -Everything is driven by `bin/x`, the project's task runner — one native binary, -written in Ludic and compiled by Ludic, that replaces every build/test/bootstrap -shell script. Bootstrap it once from a clean checkout (the only step Ludic can't -do for itself, since compiling Ludic needs a compiler): +`bin/x` is the project's task runner — one native binary, written in Ludic and +compiled by Ludic, that replaces every build/test/bootstrap shell script. +Bootstrap it once from a clean checkout (the only step Ludic can't do for +itself, since compiling Ludic needs a compiler) with clang alone: ```bash clang selfhost/ludicc.seed.ll -o bin/ludicc && bin/ludicc tools/x/main.ludic -o bin/x ``` -Then `bin/x build` builds the whole toolchain into `bin/` — including `bin/x` -itself — and `bin/x help` lists every command. `ludicc` compiles Ludic straight -to machine code via LLVM IR; see **[COMPILING.md](COMPILING.md)** for the -pipeline and the runtime protocol. +Then build the whole toolchain and run a game: ```bash -bin/x app examples/games/chronorift.ludic -./build/chronorift # opens a native window +bin/x build # -> bin/{ludicc,ludic,x,ludic-fmt,ludic-lsp} +bin/x app examples/games/snake.ludic # compile + open a native window +./build/snake ``` -Headless render (for testing / CI): +Render a frame headlessly (what CI checks) — output lands in `build/`, never the +repo root: ```bash bin/x app examples/games/chronorift.ludic --headless -printf 'ddddwww' | ./build/chronorift_headless # writes out.ppm -sips -s format png out.ppm --out frame.png +mkdir -p build && printf 'ddddwww' | ./build/chronorift_headless # writes build/out.ppm +sips -s format png build/out.ppm --out frame.png ``` -(The shared-library and wasm/web build paths lived in the old C compiler and are -not yet re-implemented on the self-hosted native toolchain, so `bin/x app` builds -native windowed and `--headless` targets today. See -**[COMPILING.md](COMPILING.md)** for the pipeline and the runtime protocol.) +Run the suites: + +```bash +bin/x test # full regression: compiler builds from seed, every example, golden renders +bin/x selfhost-test # correctness + the self-hosting / C-free bootstrap fixpoints +bin/x help # every command +``` + +## Layout + +| Path | What it is | +|------|-----------| +| [`selfhost/*.ludic`](selfhost/) | **the compiler, written in Ludic** — lexer, parser, and the LLVM-IR backend (ECS storage, queries, spawn, `match`/`machine`, UI, scenes, save/load, fixed-point). Built from `selfhost/ludicc.seed.ll` with clang alone. | +| [`selfhost/golden/renders.sha256`](selfhost/golden/renders.sha256) | text baseline of render-output hashes (replaces binary `.ppm` fixtures); regenerate with `bin/x golden`. | +| [`tools/x/*.ludic`](tools/x/) | **the task runner, written in Ludic** — one binary (`bin/x`) that builds, tests, bootstraps and reseeds the project, replacing every shell script. | +| [`runtime/native/`](runtime/native/) | the runtime **in Ludic** for the native path: `core` (framebuffer, input, RNG), `image`/`inflate` (PNG + DEFLATE, no zlib), `truetype` (glyph rasterizer), `ui` (retained widget tree); plus `cocoa.ll`, the macOS window seam in LLVM IR. | +| [`runtime/web/`](runtime/web/) | the browser platform layer: `platform.js` (the `` window), `wasm.ll` (the libc-free floor), `index.html`. | +| [`examples/`](examples/README.md) | the example tour, grouped by intent — `games/`, `rendering/`, `ecs/`, `events/`, `networking/`, `lang/`, `library/`. See [examples/README.md](examples/README.md). | +| [`tools/ludic-tools/`](tools/ludic-tools/) | the editor toolchain **in Ludic**: `ludic-fmt` (formatter) and `ludic-lsp` (language server) — one lexer, one vocabulary shared by both. | +| [`tools/editors/`](tools/editors/README.md) | plugins for VS Code and JetBrains, plus config for Neovim, Helix, Emacs, Sublime and Zed. | +| [`docs/`](docs/) | the per-symbol API reference, regenerated into the docs site. | +| [`COMPILING.md`](COMPILING.md) | the native pipeline: `ludicc → LLVM IR → exe`, the `rt_*` runtime protocol, and the (pending) wasm/cross-compile/shared-library paths. | + +Design and roadmap documents — `LANGUAGE.md`, `EVENTS-DESIGN.md`, +`NETWORKING-DESIGN.md`, `SCENES-DESIGN.md`, `LIFECYCLE-DESIGN.md`, +`SYNTAX-REDESIGN.md`, `MOBILE-DESIGN.md`, `LUANTI-ROADMAP.md`, `BOOTSTRAP.md` — +live at the repository root today and are being migrated to the wiki. + +## Language at a glance + +- `program` / `property` (typed fields + defaults) / `model` (named entity kinds) + / `system` (`phase`, `@annotations`, `reads`/`writes`). +- ECS queries `for (a, b) in query [A, B, {Tag}] where { … }`, + `spawn`/`despawn` with slot reuse, `@`-driven lifecycle hooks. +- An **event bus** (`event` / `emit` / `@On`, cancellable, `@Public` promotion) + and **networking** primitives (`@Sync`, ownership, RPCs) over a built-in + loopback transport — all deterministic, all pure Ludic. +- `scene` / `layer` / `become`, `match` / `machine` + `state`. +- Types `int`, `fixed` (Q16.16), `bool`, `entity`, `str`, `byte`, typed buffers; + a growing namespaced **standard library** (`Math`, `Vector`, `Time`/`Date`/ + `Duration`/`Clock`, `Random`, `Hash`, `Crypto`, sorting, …). +- Deterministic seeded RNG and `save()`/`load()` snapshot of the whole World. +- Built-in 2D: framebuffer primitives, PNG sprites, TrueType text, 9-slice, and + a retained `ui` widget tree declared as data. + +See [LANGUAGE.md](LANGUAGE.md) for the full reference, and +[examples/README.md](examples/README.md) for runnable demos of each feature. ## Editor support @@ -98,78 +113,40 @@ bin/x tools # -> bin/ludic-fmt, bin/ludic-lsp ``` `ludic-lsp` speaks LSP 3.17 over stdio, so one binary serves every editor: -completion that knows whether you are after a `.`, inside a `query [...]` or in -a `ui` block; diagnostics from the compiler itself; go-to-definition and rename -across `import`ed files; comment-preserving formatting. `ludic-fmt` is the same -formatter as a CLI, for pre-commit hooks and CI. +context-aware completion, diagnostics from the compiler itself, +go-to-definition and rename across `import`ed files, and comment-preserving +formatting. `ludic-fmt` is the same formatter as a CLI, for pre-commit hooks and +CI. Both also understand ```` ```ludic ```` fences in Markdown. Plugins and +drop-in config are in [`tools/editors/`](tools/editors/README.md). -Plugins for **VS Code** and **JetBrains IDEs** (Community editions included) and -drop-in configuration for Neovim, Helix, Emacs, Sublime and Zed are in -[`tools/editors/`](tools/editors/README.md). Both tools also understand -```` ```ludic ```` fences in Markdown, so this file and `LANGUAGE.md` get the -same highlighting, checking and formatting as the source tree. +## Chrono Rift — the flagship game -## Language features implemented +[`examples/games/chronorift.ludic`](examples/games/chronorift.ludic) is a +playable co-op JRPG — overworld, dungeon, random encounters, a turn-based co-op +battle, a boss, an item shop and snapshot save/load — split across modules under +[`games/chronorift/`](examples/games/chronorift/). Its art is CC0 +[Kenney](https://kenney.nl) sprites, decoded from PNG at runtime by the +Ludic-written PNG/DEFLATE decoder — no zlib, no external dependency. -- `property` (typed fields + defaults), `system` (`phase`, `@annotations`, - `reads`/`writes` clauses), `const`, `fn` (with `requires`/`ensures` parsed). -- `model` — named entity **kinds** (bundles of properties); identity is one - int per entity, replacing empty tag properties. Filter with `{Kind}`. -- `import "file"` — multi-file programs (fragments spliced in, include-guarded, - per-file diagnostics). -- ECS queries: `for (a, b) in query [A, B, {Tag}] where { … }`. -- `spawn`/`despawn` with entity-slot reuse; nested queries. -- Control flow: `if`/`else`, `when`, `while`, numeric `for i in a .. b`. -- Types: `int`, `fixed` (Q16.16, with correct `*`/`/` lowering), `bool`, - `entity`, `str`. Fixed-point vs int arithmetic is resolved by the typechecker. -- Deterministic seeded RNG; `save()`/`load()` snapshot of the whole ECS World. -- 2D primitives: `clear`, `fill_rect`, `frame_rect`, `put_px`, `present`, `key`. -- TrueType text (`font_load`, `text_ttf`) with full Unicode + anti-aliasing; - arbitrary-size images + `draw_9slice`. -- `ui` — a retained widget tree declared as data (panels, labels, buttons, - images; layout, 9-slice skins, keyboard focus + click events). -- `match` / `machine`+`state`+`become` — dispatch and state machines. -- `scene` / `layer` / `enter` — mutually-exclusive game states, each with - `on enter`/`on exit` hooks and layered systems (layer order = draw order). -- `var` — typed module-level state, included in save/load snapshots. -- `module` + `@export fn` — compile a .ludic file to a shared library whose - exported functions are ordinary C-ABI symbols. -- `extern fn … = "symbol"` — call any C-ABI library, Ludic or otherwise. -- `--target wasm32-unknown-unknown` — the same game in a browser: the runtime, - the ECS and the graphics stack compiled to wasm, rendering frames identical to - the native build's. - -## The game: Chrono Rift - -A playable co-op JRPG in `examples/games/chronorift.ludic`, using CC0 -[Kenney](https://kenney.nl) sprites (Tiny Town + Tiny Dungeon), decoded from -PNG at runtime by the Ludic-written PNG/DEFLATE decoder — no zlib, no external -dependency on any target. - -Controls: - **Overworld:** `WASD` move, `K` save, `L` load. -- **Battle (local co-op):** Player 1 / Knight — `W`/`S` select, `Space` confirm. - Player 2 / Mage — `I`/`K` select, `J` confirm. Each player takes their own - turn each round (Attack / Defend / Run; Mage has Attack / Heal / Defend). +- **Battle (local co-op):** P1/Knight `W`/`S` select, `Space` confirm; + P2/Mage `I`/`K` select, `J` confirm. -## Status +## Status & roadmap -- [x] Compiler pipeline: Ludic → LLVM IR → native binary / shared library -- [x] No C generated, compiled or linked in a build; runtime written in Ludic -- [x] Cross-compilation to ELF (x86-64, aarch64) and Windows COFF -- [x] ECS runtime (properties, systems, phases, queries, entity pooling) -- [x] Windowed 2D rendering (Cocoa driven from LLVM IR) + headless PPM verification -- [x] CC0 Kenney PNG sprites (`png_load`, decoder written in Ludic) + scrolling camera -- [x] Overworld: tilemap, movement, collision -- [x] Random encounters + turn-based battle (HP/MP, seeded-RNG damage) -- [x] Party + **local co-op** (P1 Knight, P2 Mage, per-player turns) -- [x] Leveling (XP → stat growth) and game-over / respawn -- [x] **Self-hosting**: a Ludic-written compiler compiles its own source to a byte-exact fixpoint, and rebuilds itself from a checked-in IR seed with no C compiler in the loop (`selfhost/`, `bin/x bootstrap-cfree`) -- [x] Snapshot save / load (full ECS World) -- [x] Two maps (overworld + dungeon) with map switching via the arch -- [x] Boss encounter (Rift Warden) + victory condition -- [x] Item shop (gold → potions) at the house; potions usable in battle (Knight ITEM) -- [ ] More skills / enemy variety (future) +The compiler self-hosts to a byte-exact fixpoint and rebuilds from its IR seed +with no C compiler; the ECS runtime, windowed + headless 2D rendering, the event +bus, the deterministic networking stack, scenes, and save/load are all in place +and covered by `bin/x test`. + +Active work and proposals — the standard library, a fuller type system, +rendering/animation/lighting extras, input, filesystem/IO, testing, and +re-wiring the web/wasm and cross-compile backends — are tracked as issues, not +inlined here: + +- **Issues & proposals:** +- **Docs site (API reference):** +- **Wiki (design & roadmap):** ## Contributing @@ -186,5 +163,5 @@ permissive license with an explicit patent grant. The bundled [Kenney](https://kenney.nl) art under `assets/kenney/` is third-party and released under **CC0 1.0** (public domain); each pack keeps its -own `License.txt`. Code and assets are licensed separately: the Apache-2.0 -license covers the source, not the art. +own `License.txt`. Code and assets are licensed separately: Apache-2.0 covers +the source, not the art.