DOOM's fire algo, in zig, for 256 color terminals w/no dependencies
doom CLAUDE.md
3.3 kB · 65 lines
Markdown
at main

CLAUDE.md #

Project Overview #

DOOM-fire-zig is a terminal-based DOOM fire effect renderer written in Zig. It's a TTY stress test that pushes ~180KB per frame using ANSI escape codes and the half-block character (▀) for pixel rendering. Fork of https://github.com/const-void/DOOM-fire-zig.

Single source file: src/main.zig. No dependencies beyond libc.

Build & Run #

zig build run -Doptimize=ReleaseFast   # build and run (recommended)
zig build                               # build only
zig build --global-cache-dir /tmp/claude/zig-cache  # if global cache is read-only

The binary is output to zig-out/bin/DOOM-fire. Requires libc (link_libc = true in build.zig).

Exit: Ctrl+C (no graceful key polling yet). At pause prompts: q + Enter to quit, Enter to continue.

Zig Version #

Tracks latest Zig master (currently 0.17.0-dev.607+456b2ec07). Version is recorded in three places that must stay in sync:

  • .zigversion
  • build.zig.zon .version field
  • build.zig.zon .minimum_zig_version field

Architecture #

Everything lives in src/main.zig. The program flow is:

  1. main() -> initTerm() -> checkTermSz() -> showTermCap() -> showDoomFire()
  2. initTerm: Sets up colors, terminal mode (ANSI escape sequences, Windows console API), terminal size, RNG
  3. checkTermSz: Warns if terminal is smaller than 120x22
  4. showTermCap: Renders color palette and scrolling marquee animation to verify terminal capabilities
  5. showDoomFire: The main fire simulation loop - updates fire buffer, renders to screen buffer, paints

Key Design Patterns #

  • Cross-platform via compile-time branching: if (builtin.os.tag == .windows) guards throughout. Windows uses kernel32 API; Linux/macOS uses libc + ANSI.
  • Never use \n directly: On Windows, newlines are emulated via ANSI cursor movement (line_new). Use nl constant everywhere.
  • Buffered rendering: Fire frames are built in bs (buffer string) via drawBuf(), then flushed in one emit() call via paintBuf() for performance.
  • emit() for all output: All terminal output goes through emit() which uses std.c.write on Linux or WriteConsoleA on Windows.
  • Half-height pixels: Each character cell represents two vertical pixels using ▀ with fg=top pixel color, bg=bottom pixel color.

Timing #

Uses std.c.clock_gettime(.MONOTONIC, ...) for both RNG seeding and FPS measurement. No std.time.Instant or std.time.Timer (removed from std in recent Zig versions).

Windows Support #

The win32 struct at the bottom of main.zig contains all Windows kernel32 FFI declarations. Windows uses UTF-16 via WriteConsoleW for wide chars but the project currently uses WriteConsoleA (ASCII) due to Zig's UTF-8 string model. Windows doesn't get fancy UTF-8 glyphs as a result.

Updating Zig Version #

When updating to a new Zig nightly:

  1. Update .zigversion, build.zig.zon (version + minimum_zig_version)
  2. Run zig build and fix any std library API breakage
  3. Common breakage areas: std.time, std.c, std.posix APIs change frequently in Zig master
  4. If the global zig cache is read-only, use --global-cache-dir /tmp/claude/zig-cache
  5. Clear .zig-cache/ if you see stale cache errors after a version change

License #

GPL-3.0. Copyright 2022-2025 const-void.