# neurodoom
Deterministic pure-Rust Doom engine with per-pixel semantic and depth
perception buffers for AI and multi-agent work.
- `#![no_std]` core (uses `alloc`), headless by default, 320x200 output.
- Classic single-player reproduction in `ClassicDoomRules`.
- Generic over any `GameRules` implementation for custom multiplayer or
training setups.
### History
Started as a thin wrapper around [PureDOOM](https://github.com/Daivuk/PureDOOM)
(a single-header, dependency-free C port of Doom). The plan was to bind
the C engine and hang perception buffers off it. In practice, almost
every subsystem — WAD parser, map loader, BSP + column rasterizer,
visplanes, sprite system, physics, state machine, sector specials, game
loop — got rewritten in pure Rust so the engine could be `#![no_std]`,
panic-averse, and deterministic.
## At a glance
```rust
use neurodoom::{ClassicEngine, PeerId, PlayerAction};
let wad = std::fs::read("doom1.wad")?;
let mut engine = ClassicEngine::new(&wad, "E1M1")?;
engine.tick_single(PeerId(0), PlayerAction::default());
let rgba = engine.framebuffer(); // 320 * 200 * 4 bytes
let semantic = engine.semantic_buffer(); // 1 SemanticClass per pixel
let depth = engine.depth_buffer(); // fixed-point distance per pixel
```
## Examples
All three examples need a Doom WAD. Point `DOOM_WAD` at one, or place
`doom1.wad` in the project root.
```sh
# Playable single-player with level progression across the episode.
cargo run --release --example classic
# 24 reactive bots in a 6x4 grid, fighting in E1M1 with no monsters.
# Each cell shows the bot's POV with HUD + animated weapon. Dead cells
# switch to depth view.
cargo run --release --example ai_multiplayer
# Play back the recorded DEMO1 / DEMO2 / DEMO3 lumps from the WAD —
# no user input, just feeds the stored tic-commands to the engine.
cargo run --release --example demo
```
Controls are documented in each example's file header.
## Multi-peer rendering
`DoomEngine::tick(...)` is the single-player convenience — it runs
simulation for one tick *and* renders `PeerId(0)`'s view. For multi-peer
work (several AI bots sharing one world, each needing its own view),
split the two:
```rust
engine.simulate(&actions, &[]); // advance sim, no render
for peer in peers {
engine.render_for(peer); // re-render from that peer's eye
// read engine.{framebuffer,semantic_buffer,depth_buffer}()
}
```
See `examples/ai_multiplayer.rs` for the full pattern (including
staggered rendering to amortize the per-peer cost).
## Further reading
Module-level docs (`cargo doc --open`) cover each subsystem. The crate
is panic-averse by design: direct panic sites are surfaced as clippy
warnings and `Cargo.toml` sets `panic = "abort"` on both `dev` and
`release`. See `src/render/mod.rs` for the policy details.
## License
BSD-3-Clause. See [LICENSE](LICENSE).