Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

110 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hatz

CI codecov

Deterministic tick-based multi-agent simulation engine. Implements the Hats Simulator (Cohen & Morrison, WSC 2004): a 2D grid world where hats (agents) move, organizations plan taskforces with meeting trees, beacons are attack targets, and an Information Broker provides noisy/paid intelligence to a defender.

Status

The engine was originally written in Zig and has been fully ported to Rust (sim/). The Zig implementation has been removed from the repo; Rust is now the only supported engine.

  • Engine (sim/, crate hatz-engine): ported and passing — population generation, planner (meeting-tree generation with capability-trade routing), meeting execution, movement, attack detection (4-condition rule), Information Broker (17 methods, free + paid/noisy), scoring.
  • 338 unit tests + 1 integration test (cargo test --manifest-path sim/Cargo.toml) — all passing.
  • Contract replay harness (sim/sim-replay/) — replays 33 golden request/response fixtures against the engine. All passing.
  • ~93% line coverage (cargo-llvm-cov) — uploaded to Codecov on every push/PR.
  • WebSocket daemon is live. sim/src/main.rs is a full tokio-tungstenite WebSocket server on ws://127.0.0.1:9876. Handles all message types (sim lifecycle, all 17 broker methods, actions, defaults) and streams sim.advance events in real time. cli/ (Python) and tui/ (Go) clients work against it.

Build

Requires Rust (stable toolchain, rustup/cargo). No Zig toolchain is needed or used — the project migrated off Zig entirely.

# compile the engine
cargo check --manifest-path sim/Cargo.toml

# run unit tests (338 + 1 integration test)
cargo test --manifest-path sim/Cargo.toml

# compile the contract replay harness
cargo check --manifest-path sim/sim-replay/Cargo.toml

# run the contract replay harness (33 fixtures)
cargo run --manifest-path sim/sim-replay/Cargo.toml -- --check

CI (.github/workflows/contract-replay.yml) runs the same two steps (cargo test + replay --check) on ubuntu-latest via dtolnay/rust-toolchain@stable.

Code coverage (.github/workflows/coverage.yml) runs cargo-llvm-cov on every push and PR, uploading results to codecov.io/gh/baranabi/hatz (badge above).

Architecture

sim/
├── Cargo.toml       — crate `hatz-engine`
├── src/
│   ├── lib.rs        — module root, re-exports submodules
│   ├── main.rs       — WebSocket daemon (tokio-tungstenite)
│   ├── types.rs      — core types (HatId, Location, EventRecord, etc.)
│   ├── router.rs     — dispatch by message type → handler
│   ├── sim.rs         — lifecycle: initialize, advance, end + RunState
│   ├── broker.rs      — 17 IB methods (free + paid) + noise model
│   ├── noise.rs       — P(correct) = 1 - e^(-λ·p) noise model
│   ├── actions.rs      — analyst actions (alert_beacon, arrest_hat)
│   ├── defaults.rs     — per-analyst default request scheduling
│   ├── runs.rs         — in-memory run registry
│   ├── population.rs   — seed-driven population generator (hats, orgs, beacons)
│   ├── planner.rs       — generative meeting tree planner with capability-trade routing
│   ├── meetings.rs      — meeting execution with capability trades
│   ├── movement.rs      — hat movement between meetings
│   ├── attack.rs        — beacon attack detection (4-condition rule)
│   ├── logging.rs       — structured logging
│   └── profiling.rs     — timing instrumentation
└── sim-replay/       — contract fixture replay harness (separate crate, depends on hatz-engine)

All state is deterministic from seed. Same seed + params + action sequence = identical output.

Key Components

Planner (planner.rs) — Generates meeting trees for runtime-created taskforces. For each required capability not already held by a taskforce member, finds an org member that holds the capability and schedules a trade at an intermediate or root meeting.

Meetings (meetings.rs) — Executes meetings with full capability trades and participant location updates.

Attack (attack.rs) — Detects beacon attacks when a taskforce at a beacon's location holds capabilities covering the beacon's vulnerabilities. 4-condition rule:

  1. Meeting occurs on the beacon
  2. Meeting is the final planned meeting of a taskforce
  3. Taskforce members' capabilities match beacon vulnerabilities
  4. Taskforce belongs to a terrorist organization

Information Broker (broker.rs) — 17 methods split into free queries (world, beacon, org, hat metadata) and paid queries (locations, capabilities, meeting details) with configurable noise.

Contract Protocol

The engine communicates via JSON envelopes (schemas in contracts/v1/):

// Request
{
  "contractVersion": "1.0.0",
  "type": "sim.initialize",
  "requestId": "req-01",
  "payload": { "seed": 12345, "params": {
    "nHats": 200,
    "eventLogCap": 10000,
    "planningInterval": 10,
    "maxTicks": 1000,
    "plannerChance": 1.0
  }}
}

// Response
{
  "contractVersion": "1.0.0",
  "ok": true,
  "requestId": "req-01",
  "payload": { "runId": "run-12345-1", "startedAtTick": 0 }
}

Message types:

  • sim.initialize / sim.advance / sim.end — lifecycle
    • sim.initialize params (all optional): nHats (200), eventLogCap (10000), planningInterval (10), maxTicks (1000), plannerChance (1.0)
    • plannerChance: probability the planner activates when interval ticks align. 1.0=always, 0.1=sparse, 0=never. Set planningInterval=1, plannerChance=0.1 for spec-compatible continuous sparse planning.
  • broker.call — Information Broker query (17 methods)
  • action.alert_beacon / action.arrest_hat — player actions
  • defaults.* — default request scheduling

These schemas are the API source of truth; they're language-agnostic and unchanged from the original Zig implementation.

Information Broker

Free queries (no cost, always succeed):

  • ib.world_dimensions, ib.beacons, ib.all_capabilities
  • ib.benign_organizations, ib.terrorist_organizations (partial — not all terrorist orgs revealed, per spec)
  • ib.known_terrorist_hats (partial — overt terrorists only; covert terrorists not included, per spec)
  • ib.members, ib.hat_advertised_color
  • ib.events_history, ib.clear_events_history, ib.arrested_hats

Paid queries (cost + noise via P(correct) = 1 - e^(-λ·p)):

  • ib.last_location, ib.capabilities
  • ib.meeting_times, ib.meeting_location
  • ib.meeting_participants, ib.meeting_trades

Scoring

Reported at sim.end():

  • Information Cost — total IB spend
  • False Arrests — failed arrest attempts
  • Beacon Effectiveness — hits vs false positives per beacon per alert level

Clients

These clients speak the WebSocket contract protocol and work against the live daemon on ws://127.0.0.1:9876.

Daemon manager (cli/hatz_mgr.py)

Start, stop, list, and health-check daemon instances. Also usable as a library (from hatz_mgr import DaemonManager).

cd cli && uv sync
uv run hatz_mgr.py start 9876       # start + wait for healthy
uv run hatz_mgr.py start 9877       # run multiple instances
uv run hatz_mgr.py list             # all tracked daemons
uv run hatz_mgr.py logs 9876        # tail daemon logs
uv run hatz_mgr.py stop-all         # kill everything

Or start the daemon directly:

cd sim && cargo run                 # default ws://127.0.0.1:9876
cd sim && cargo run 0.0.0.0:8080   # custom address

Python CLI (cli/)

A REPL client with 14 commands (init, advance, end, beacons, orgs, members, loc, cap, alert, arrest, events, state, help, quit). See cli/README.md.

cd cli && uv sync
uv run cli.py                       # connect to 127.0.0.1:9876
uv run cli.py 127.0.0.1:9877        # custom daemon
uv run cli.py --selftest            # connect, initialize, advance 1 tick, end

Go TUI (tui/)

A Bubbletea interactive TUI with commands /advance, /beacons, /loc, /caps, /color, /orgs, /members, /events, /arrest, /alert, /score, /setdefaults, /help.

cd tui && go build
./tui

Analyst garden (garden/)

A benchmark harness (garden/harness.py) that runs competing analyst strategies against the simulator over a fixed seed set, scoring on attacks prevented, IB spend, false arrests, and AI token cost. See garden/README.md. Uses hatz_mgr to manage daemon lifecycle.

Known Issues

None — all known issues from the port have been resolved. Contract replay passes in CI, the WebSocket daemon is live, and integration tests cover the full protocol.

Roadmap

Now

  • Integration tests for the WebSocket daemon
  • Multi-seed fan-out / parallel tick evaluation

Later

  • SIMD-friendly agent layouts (SoA, cache-locality) toward the 500k+ agent target
  • Profiling harness for large-population bottleneck analysis

Specs & References

About

Deterministic simulation engine for adversarial multi-agent scenarios: deception and identity uncertainty under partial, information-brokered observations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages