Skip to content

Brogue: Community Edition (BrogueCE)

Brogue is a roguelike: descend a 26-level procedurally-generated dungeon to retrieve the Amulet of Yendor from level 26, fighting creatures, dodging traps, and managing a small inventory along the way. Brogue’s reputation rests on doing more with less — it is famous for being the roguelike that proved a minimal glyph set and a disciplined screen layout can carry a deep tactical game without tile art. Brogue: Community Edition (BrogueCE) is the maintained fork that carries the original forward, runs on Linux/Windows/macOS, ships graphical tiles you can toggle with G, and retains a full ncurses text-mode build for pure-terminal play.

For KN-86 this is the headline reference of the games cluster, because Brogue is the cleanest worked example of the exact problem KN-86 has to solve: rendering a color-coded ASCII game state on a single-color grid. Brogue’s full-color build leans hard on color (terrain hazards “dance,” item-identification status is color-cued, threat is color-cued), and yet the game is fully playable in monochrome ncurses — because the underlying design never lets color be the only channel carrying a piece of state. That discipline is precisely what KN-86’s amber-on-black grid demands. (Reference “CLAUDE.md Canonical Hardware Specification” for the grid; not restated here.)

Brogue’s screen layout is a fixed-region terminal composition — directly portable to KN-86’s content area:

RegionBrogue dimensionsRole
Sidebar (left)STAT_BAR_WIDTH = 20 columnsPlayer stats, equipped items, nearby monster/item roster, health/threat readouts
Message log (top)MESSAGE_LINES = 3 linesScrolling event log — combat results, pickups, environmental events
Map (center/right)DCOLS × DROWS = (COLS − 20 − 1) × (ROWS − 3 − 2)The dungeon level, one glyph per cell

Total grid is COLS = 100 × ROWS = 34 (31 map + 3 message). The map is the bulk; the left sidebar is a persistent status/roster column, and the top message log is a rolling 3-line scrollback. This three-region split (map + sidebar + log) is the canonical roguelike IA and maps cleanly onto KN-86’s Rows 1–23 content band — a roguelike cart would carve the content area into a left status strip, a top message band, and a map field, with firmware Rows 0/24 untouched.

  • Inventory management — single-key item interaction; the sidebar shows what’s relevant nearby; a full inventory pane opens on demand. Items carry an identification state (un-identified scrolls/potions/rings show as generic glyphs with descriptive labels until identified) — a state machine worth stealing for any KN-86 cart with collectible items.
  • Look / examine mode — the player can move a cursor over any cell to get a description of what’s there, which is how a monochrome player recovers information that color would otherwise have carried at a glance.

Rendering — and the monochrome problem (headline)

Section titled “Rendering — and the monochrome problem (headline)”

Brogue’s per-cell display model is a glyph plus two RGB colors:

typedef struct cellDisplayBuffer {
enum displayGlyph character;
char foreColorComponents[3]; /* foreground RGB */
char backColorComponents[3]; /* background RGB */
char opacity;
} cellDisplayBuffer;

The color model itself is unusually rich — Brogue colors carry random jitter components and a colorDances flag, so fire, water, and magical effects literally shimmer frame-to-frame:

typedef struct color {
short red, green, blue; /* base */
short redRand, greenRand, blueRand; /* per-component random add */
short rand; /* scalar random add to all */
boolean colorDances; /* re-randomize every refresh */
} color;

Here is the decisive insight for KN-86. Brogue never lets color be the sole carrier of identity. Every entity already has a distinct glyph: the curses platform’s glyphToAscii() maps each internal displayGlyph to a specific ASCII character before drawing —

case G_FLOOR: return '.';
case G_CHASM: return ':';
case G_TRAP: return '%';
case G_FOLIAGE: return '&';
case G_SCROLL: return '?';
case G_RING: return '=';
case G_WEAPON: return '(';
case G_GEM: return '+';
/* monsters fall through to their letter (rat 'r', goblin 'g', etc.) */

So in the ncurses text build, the glyph alone disambiguates entity type@ is you, . is floor, r is a rat, ( is a weapon, ? is a scroll. Color is a redundant, secondary channel layered on top: it encodes status and intensity (how hot a fire is, whether a potion is identified, how dangerous a monster is, whether terrain is hazardous), never what the thing is. That is exactly why Brogue survives monochrome: kill the color and you lose the at-a-glance flavor but never the information needed to play.

The curses renderer proves the degradation path is real and shipped: curses_plotChar() takes the same RGB the SDL build uses, divides each component by 100, and hands it to whatever the terminal can do — on a limited-palette terminal it collapses toward the nearest available attribute, and the game stays winnable.

This is the entry’s whole reason for existing. How does color-coded roguelike game state best survive a monochrome amber grid? Brogue answers it by example; here is the read-across for KN-86 (recommended ranking):

  1. Glyph is identity; never color. The first and biggest rule. Every entity type, terrain type, and item class gets a distinct character. If two things must be told apart, they must differ in glyph (or position/box), not in hue. Brogue already does this — KN-86 carts must adopt the same constraint as a hard rule, because amber-on-black has zero color channel to spare.
  2. Inversion (fg/bg swap) as the second channel. KN-86 has amber-on-black and its inverse (black-on-amber). Brogue uses background color to mark the player’s cell, targeting cursors, and hazard tiles; KN-86 maps that onto inversion — the selected/targeted/hazardous cell renders inverted. One bit, highly legible, costs no glyph.
  3. Glyph density / weight for intensity. Where Brogue ramps brightness or saturation to show “how much” (fire intensity, gas concentration, light level), KN-86 ramps CP437 shading glyphs░ ▒ ▓ █ (light→dark) — to encode a scalar in a single cell. This is the monochrome substitute for a brightness gradient and it reads instantly.
  4. Position / dedicated readout for what color used to whisper. Brogue’s sidebar spells out, in text, the threat roster that color-cues on the map. KN-86 leans harder on this: anything color alone conveyed in the full-color build (threat tier, identification state, status effects) gets an explicit sidebar glyph badge or a text token — e.g. a ! suffix on a monster’s roster line for “dangerous,” a ? glyph for “un-identified.”
  5. Animation/blink for the colorDances effect. Brogue’s shimmering fire/water/magic (colorDances) can’t be done with hue on KN-86 — so port it to the time axis: cycle the glyph (░▒▓ alternating) or blink inversion on the animated-redraw tick (the runtime already caps animation at 20 fps). Motion replaces shimmer.

The summary recommendation for KN-86 cart authors: treat color as if it were never available, then add inversion + shading-glyph + blink as your three “extra” channels. Brogue is the proof that a game designed this way is not a compromised game — it’s a famously good one.

  • Movement — arrow keys / hjkl / numpad for the eight directions; movement is the core verb.
  • Single-key item & action verbs — apply, equip, drop, throw, rest, search, travel-to.
  • Look mode — cursor-driven cell inspection (the monochrome player’s information-recovery tool).
  • G — toggle graphical tiles vs ASCII at runtime (in the SDL build). The fact that the same game renders identically-playable in tiles and in ASCII is itself the proof-of-concept KN-86 wants.
  • Keybindings are remappable via a keymap file (curses_remap / a mapsymbol linked list in the curses platform).

Brogue’s structure is a clean game-core / platform-layer split that KN-86’s own two-target (emulator + device) architecture should study:

  • src/brogue/ — all game logic, platform-agnostic (Architect, Combat, Monsters, Items, Light, Movement, Dijkstra pathfinding, the procedural Grid, RNG, save/recording). Renders into an in-memory screenDisplayBuffer (a cellDisplayBuffer[COLS][ROWS] grid).
  • src/platform/ — a brogueConsole struct of function pointers (plotChar, input poll, modifier query, …) that each platform fills in. sdl2-platform.c, curses-platform.c, web-platform.c, and null-platform.c are interchangeable backends behind that one vtable.

The game never knows whether it’s drawing to SDL or ncurses — it writes glyph+color into the buffer and a backend renders it. This is the same separation KN-86 has between the nOSh runtime’s logical 80×25 grid and its two render targets (SDL3 emulator / Pi device). Brogue validates that a single glyph+attribute buffer can drive both a rich graphical build and a pure-terminal build with no game-logic changes — which is the whole bet KN-86 is making.

  • Roguelike cart IA reference. Map + left status sidebar + top message log is the layout a KN-86 roguelike/dungeon cart should adopt wholesale, inside Rows 1–23.
  • The 26-level procedural descent is a textbook depth-of-content-from-little-data model — directly relevant to KN-86’s cart generation-data approach (carts carry generation rules + behavior tables, not hand-authored levels). Cross-link the procedural-generation guidance in the cartridge authoring docs.
  • Item-identification state machine — un-identified → identified is a cheap, deep mechanic that fits the amber grid (an un-identified item is just a generic glyph + ? until used). Good fit for any KN-86 cart with loot.
  • colorDances → motion-as-emphasis. Adopt the principle that “alive/dangerous/active” things move, even on a static amber grid, via the 20 fps animation tick.
  • This is the central single-color problem flagged in the Batch 8 brief. The recommendation above (glyph=identity, inversion=selection/hazard, shading-glyphs=intensity, text/badge=status, blink=animation) is the deliverable.
  • Cross-link the cartridge / gameplay docs: docs/software/cartridges/authoring/screen-design-rules.md (the Row 0 / 1–23 / 24 layout contract that a roguelike cart must honor), docs/software/cartridges/authoring/ui-patterns.md (audio-as-data + reusable UI patterns), and docs/software/cartridges/authoring/lisp-paradigm.md (cell-as-data model). Brogue’s cellDisplayBuffer is conceptually the same as KN-86’s per-cell glyph+attribute model.
  • Cross-link zork.md — the other deep game-history reference; Zork is the Lisp/bytecode-VM precedent, Brogue is the ASCII-rendering precedent.
  • The platform-layer vtable pattern is worth a citation in any future ADR that formalizes the emulator/device render-target split.