Skip to content

ndouglas/longtable

Repository files navigation

Longtable

A rule-based simulation engine combining a LISP-like DSL with an archetype-based ECS.

Overview

Longtable is designed for text-based simulation games where complex emergent behavior arises from simple, declarative rules. Think "Zork meets Dwarf Fortress" — rich world simulation driven by pattern-matching rules rather than imperative scripts.

Key Features

  • Persistent World State — Immutable snapshots with structural sharing enable time travel, speculation, and deterministic replay
  • Pattern-Matching Rules — Declarative rules fire when patterns match, with automatic refraction to prevent infinite loops
  • Entity-Component-Relationship — Archetype-based ECS with first-class relationships and typed schemas
  • LISP-like DSL — Homoiconic syntax with macros for domain-specific abstractions
  • Derived Components — Computed values with automatic cache invalidation
  • Constraint Checking — Invariants validated after each tick with rollback support
  • Time Travel Debugging — Git-like branching, rollback, and world state diffing
  • Observability — Tracing, breakpoints, watches, and causal "why" queries
  • 125+ Native Functions — Comprehensive standard library for collections, math, strings, and more

Quick Start

Installation

git clone https://github.com/ndouglas/longtable.git
cd longtable
cargo build --release

Running the REPL

./target/release/longtable

Or with files:

./target/release/longtable examples/adventure/_.lt

Example Session

;; Basic arithmetic
> (+ 1 2 3)
6

;; Collections
> (map (fn [x] (* x 2)) [1 2 3 4 5])
[2 4 6 8 10]

> (filter (fn [x] (> x 2)) [1 2 3 4 5])
[3 4 5]

> (reduce (fn [acc x] (+ acc x)) 0 [1 2 3 4 5])
15

;; String operations
> (str/upper "hello world")
"HELLO WORLD"

> (str/split "a,b,c" ",")
["a" "b" "c"]

;; Math functions
> (sin (/ pi 2))
1.0

> (sqrt 16)
4.0

;; Vector math (for simulations)
> (vec+ [1 2 3] [4 5 6])
[5.0 7.0 9.0]

> (vec-normalize [3 4])
[0.6 0.8]

Sudoku Solver Demo

The examples/sudoku/ directory contains a complete constraint-propagation Sudoku solver implemented in Longtable's DSL:

./target/release/longtable examples/sudoku/_.lt
> (load-puzzle (medium-1))
> (print-grid)
+-------+-------+-------+
| . . . | 2 6 . | 7 . 1 |
| 6 8 . | . 7 . | . 9 . |
| 1 9 . | . . 4 | 5 . . |
+-------+-------+-------+
| 8 2 . | 1 . . | . 4 . |
| . . 4 | 6 . 2 | 9 . . |
| . 5 . | . . 3 | . 2 8 |
+-------+-------+-------+
| . . 9 | 3 . . | . 7 4 |
| . 4 . | . 5 . | . 3 6 |
| 7 . 3 | . 1 8 | . . . |
+-------+-------+-------+

> (solve)
Naked single: R5C1 = 3
Naked single: R6C1 = 9
Naked single: R1C8 = 8
... (45 placements)

+-------+-------+-------+
| 4 3 5 | 2 6 9 | 7 8 1 |
| 6 8 2 | 5 7 1 | 4 9 3 |
| 1 9 7 | 8 3 4 | 5 6 2 |
+-------+-------+-------+
| 8 2 6 | 1 9 5 | 3 4 7 |
| 3 7 4 | 6 8 2 | 9 1 5 |
| 9 5 1 | 7 4 3 | 6 2 8 |
+-------+-------+-------+
| 5 1 9 | 3 2 6 | 8 7 4 |
| 2 4 8 | 9 5 7 | 1 3 6 |
| 7 6 3 | 4 1 8 | 2 5 9 |
+-------+-------+-------+

Puzzle solved

The solver demonstrates:

  • Entity-Component architecture: 81 cells with :position, :value, :candidates components
  • Constraint propagation: Naked singles, hidden singles, X-Wing, Swordfish, XY-Wing
  • Backtracking with state save/restore: For puzzles requiring guessing
  • Declarative logic: Pure functional implementation in ~600 lines of DSL code

Logic Grid Puzzle Solver

The examples/logic-grid/ directory contains a constraint-satisfaction solver for logic grid puzzles (the kind where you match items across categories using clues).

Puzzle credit: The included "Minnetonka Manatee Company" puzzle is from PuzzleBaron's Logic Puzzles.

./target/release/longtable examples/logic-grid/_.lt
> (solve-manatee-puzzle!)

============================================================
       MINNETONKA MANATEE COMPANY LOGIC PUZZLE
============================================================
Setting up logic grid...
Grid setup queued (588 cells).

Applying basic exclusion clues...
  Clue 2: Sea Cow != Silver Springs
  Clue 3: Rainbow Reef != Jacobson
  Clue 5: Mellow Mel != 3 manatees
  Clue 8: Samantha != 4, Samantha != Silver Springs

Basic clues applied. Running propagation...

=== Applying deduced constraints ===

Clue 10 -> Hollow Hole = 7 manatees, Benny II = Romero = 9
  Solved: :location/:hollow-hole = :manatees/:7
  Solved: :boat/:benny-ii = :captain/:romero
  ...

============================================================
                        SOLUTION
============================================================

Boat          Captain     Manatees  Location
------------  ----------  --------  --------------
:benny-ii     :romero     :9        :betty-beach
:daily-ray    :espinoza   :6        :yellow-bend
:foxy-roxy    :armstrong  :4        :rainbow-reef
:mellow-mel   :yang       :7        :hollow-hole
:samantha     :jacobson   :5        :treys-tunnel
:sea-cow      :quinn      :8        :arnos-spit
:watery-pete  :preston    :3        :silver-springs

============================================================

The solver demonstrates:

  • Grid cell entities: 588 cells tracking all category pairings (boat×captain, boat×location, etc.)
  • Constraint propagation: Eliminating possibilities when cells are solved
  • Bidirectional solving: When boat→captain is solved, captain→boat is automatically solved
  • Declarative clue application: (clue-is! :boat :benny-ii :captain :romero) to assert facts

Text Adventure Demo

The examples/adventure/ directory contains a text adventure game ("The Dark Cave") showcasing natural language parsing and rule-based simulation:

./target/release/longtable examples/adventure/_.lt
===============================================
             THE DARK CAVE
        A Text Adventure Demo
===============================================

Type 'help' for a list of commands.

Welcome to the cave entrance!

> look

Cave Entrance
You stand at the mouth of a dark cave. Sunlight streams in from behind you,
illuminating rough stone walls covered in moss. A cold breeze carries the
smell of damp earth from deeper within.
You can go south.

> l lantern

A sturdy brass lantern with a glass chimney. It provides a warm, steady light.

> s

Main Hall
A vast underground chamber stretches before you. Ancient stalactites hang
from the ceiling like stone fangs. Multiple passages lead off into darkness.
You can go east, west, or north.

> e

Crystal Cavern
The walls here are studded with glowing crystals that cast an eerie blue
light. The crystals hum faintly, resonating with some unknown energy.
You can go west or south.

> l sword

An old iron sword. Despite its rust, the edge is still sharp.

The adventure demonstrates:

  • Natural language parsing: Player input like "examine brass lantern" is parsed into structured commands via vocabulary and syntax patterns
  • Entity-Component storage: Rooms, items, and the player are entities with components (:name, :description, :tag/player, etc.)
  • Relationships: Spatial relationships (in-room, exit/north) connect entities
  • Action system: Actions have parameters, preconditions, and handlers
  • Queryable world state: (query :where [[?p :tag/player true] [?p :in-room ?r] [?r :name ?n]] :return ?n)

CLI Usage

longtable [OPTIONS] [FILES...]

OPTIONS:
    -h, --help         Print help information
    -V, --version      Print version information
    -b, --batch        Load files and exit (no REPL)

DEBUG OPTIONS:
    --trace            Enable rule tracing output
    --trace-vm         Enable VM instruction tracing
    --trace-match      Enable pattern match tracing
    --max-ticks N      Limit ticks before exit (for testing)
    --dump-world       Dump world state after loading files

EXAMPLES:
    longtable                        Start interactive REPL
    longtable world.lt               Load world.lt, then start REPL
    longtable -b test.lt             Load test.lt and exit
    longtable --trace -b sim.lt      Run with rule tracing

REPL Commands

;; Basic commands
(def name value)       ;; Define a session variable
(load "path")          ;; Load a .lt file
(save! "path")         ;; Save world state to file
(load-world! "path")   ;; Load world state from file
(tick!)                ;; Advance simulation by one tick
(inspect entity)       ;; Inspect an entity's details

;; Explain system
(why entity :component)           ;; Why does entity have this value?
(why entity :component :depth 5)  ;; Multi-hop causal chain
(explain-query (query ...))       ;; Explain query execution

;; Debugging
(break :rule foo)                 ;; Breakpoint on rule
(break :entity ?e :component :hp) ;; Breakpoint on component access
(watch (get ?e :health))          ;; Add watch expression
(continue)                        ;; Resume execution
(step-rule)                       ;; Step to next rule

;; Tracing
(trace!)                          ;; Enable tracing
(trace-off!)                      ;; Disable tracing
(get-traces)                      ;; Get trace buffer

;; Time travel
(rollback! 5)                     ;; Go back 5 ticks
(goto-tick! 42)                   ;; Jump to tick 42
(branch! "experiment")            ;; Create branch at current tick
(checkout! "main")                ;; Switch to branch
(branches)                        ;; List all branches
(merge! "experiment")             ;; Merge branch into current
(diff 40 42)                      ;; Compare two ticks
(history)                         ;; Show recent history
(timeline)                        ;; Show timeline status

Keyboard shortcuts:

  • Ctrl+D — Exit REPL
  • Ctrl+C — Cancel current input
  • Tab — Autocomplete keywords

Crate Structure

longtable_foundation  — Core types, values, persistent collections
longtable_storage     — Entity-component storage, relationships, world state
longtable_language    — Lexer, parser, compiler, bytecode VM
longtable_engine      — Rule engine, pattern matching, queries, constraints
longtable_stdlib      — Standard library functions
longtable_runtime     — REPL, CLI, serialization
longtable_debug       — Tracing, debugging, time travel

Layer Dependencies

Layer 5: longtable_debug     — Tracing, debugging, time travel
Layer 4: longtable_runtime   — REPL, CLI, serialization
         longtable_stdlib    — Standard library functions
Layer 3: longtable_engine    — Rule engine, pattern matching, queries
Layer 2: longtable_language  — Lexer, parser, compiler, bytecode VM
Layer 1: longtable_storage   — Entity-component storage, world state
Layer 0: longtable_foundation — Core types, persistent collections

Standard Library

Collections

map, filter, reduce, first, rest, last, nth, count, empty?, conj, cons, concat, reverse, sort, sort-by, take, drop, take-while, drop-while, partition, group-by, flatten, distinct, dedupe, interleave, interpose, zip, zip-with, repeat, range, into, vec, set, keys, vals, get, assoc, dissoc, merge, contains?, every?, some, not-any?, not-every?, remove

Math

+, -, *, /, mod, rem, abs, neg, inc, dec, min, max, clamp, floor, ceil, round, trunc, sqrt, cbrt, pow, exp, log, log10, log2, sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, pi, e, rand, rand-int

Vector Math

vec+, vec-, vec*, vec-scale, vec-dot, vec-cross, vec-length, vec-length-sq, vec-normalize, vec-distance, vec-lerp, vec-angle

Strings

str, str/len, str/upper, str/lower, str/trim, str/trim-left, str/trim-right, str/split, str/join, str/replace, str/replace-all, str/starts-with?, str/ends-with?, str/contains?, str/blank?, str/substring, format

Predicates

nil?, some?, int?, float?, string?, keyword?, symbol?, bool?, number?, list?, vector?, map?, set?, coll?, fn?, entity?, type

Logic

=, !=, <, <=, >, >=, not, and, or, if, when, cond

Building

cargo build                           # Build all crates
cargo test                            # Run all tests (~1000 tests)
cargo bench                           # Run benchmarks
cargo clippy --all-targets            # Lint
cargo +nightly fmt --all              # Format (requires nightly)
cargo doc --no-deps --open            # Generate documentation

Requires Rust 1.85.0 or later (Edition 2024).

Performance

Benchmark highlights (M1 Mac):

Operation Time Notes
VM simple op ~500 ns 2M ops/sec
Function call ~1 µs Per call overhead
Pattern match ~100-230 ns Per pattern
Component get 66 ns O(1) lookup
World clone ~50 ns Structural sharing
Entity spawn ~480 ns With components

See cargo bench for full benchmark suite.

License

This project is released into the public domain under the Unlicense.

About

LISP + ECS + Rule Engine

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages