examples/war: working end-to-end War card game
A complete, playable port of the card game War: title screen with
0/1/2 player menu, animated deal, sliding cards, deck-count HUD, a
"WAR!" tie-break with buried cards, and a victory screen with a
fanfare. Source split across examples/war/*.ne (constants, assets,
audio, deck/queue logic, RNG, render helpers, and one state file
per game state) and pulled in via examples/war.ne.
Drives nearly every NEScript subsystem at once: custom 88-tile
sprite sheet (card frames, ranks, suits, font, BIG WAR letters);
felt background nametable; pulse-1 / pulse-2 / noise sfx; looping
march on pulse 2; an 8-bit Galois LFSR PRNG; queue-based decks
that conserve cards across rounds; a phase machine inside the
Playing state that handles draw/reveal/win/war/check; and an
autopilot that boots straight into 0-PLAYERS mode so the headless
jsnes harness captures real gameplay at frame 180.
While building this I uncovered five compiler bugs / limitations
in the v0.1 implementation; each is documented with a minimal
reproduction, root cause, current workaround, and proposed fix in
examples/war/COMPILER_BUGS.md. The most painful was the
parameter-VarId aliasing one (#1b) — two functions sharing a
parameter NAME end up sharing a single zero-page slot mapping
across the whole program. Once those compiler bugs are fixed, the
workarounds in war/*.ne should be reverted in the same PR.
https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
2026-04-15 15:22:20 +00:00
|
|
|
|
// war/background.ne — the static 32×30 nametable loaded at reset.
|
|
|
|
|
|
//
|
|
|
|
|
|
// NEScript loads the *first* declared `background` into nametable 0
|
|
|
|
|
|
// before rendering is enabled, so whatever this file declares is
|
|
|
|
|
|
// visible on frame 0 of the ROM. We use a single felt-table
|
2026-04-15 16:08:03 +00:00
|
|
|
|
// background — every cell is the dedicated TILE_FELT_BG (tile 75
|
|
|
|
|
|
// in the Tileset sprite, a sparse cross-hatch authored to look
|
|
|
|
|
|
// like dk_green felt with mint flecks when rendered through bg
|
|
|
|
|
|
// sub-palette 0). All UI (title banner, "PRESS A", win banners,
|
|
|
|
|
|
// face-up cards, etc.) is drawn on top via sprites so we never
|
|
|
|
|
|
// pay the cost of a full mid-frame nametable swap.
|
examples/war: working end-to-end War card game
A complete, playable port of the card game War: title screen with
0/1/2 player menu, animated deal, sliding cards, deck-count HUD, a
"WAR!" tie-break with buried cards, and a victory screen with a
fanfare. Source split across examples/war/*.ne (constants, assets,
audio, deck/queue logic, RNG, render helpers, and one state file
per game state) and pulled in via examples/war.ne.
Drives nearly every NEScript subsystem at once: custom 88-tile
sprite sheet (card frames, ranks, suits, font, BIG WAR letters);
felt background nametable; pulse-1 / pulse-2 / noise sfx; looping
march on pulse 2; an 8-bit Galois LFSR PRNG; queue-based decks
that conserve cards across rounds; a phase machine inside the
Playing state that handles draw/reveal/win/war/check; and an
autopilot that boots straight into 0-PLAYERS mode so the headless
jsnes harness captures real gameplay at frame 180.
While building this I uncovered five compiler bugs / limitations
in the v0.1 implementation; each is documented with a minimal
reproduction, root cause, current workaround, and proposed fix in
examples/war/COMPILER_BUGS.md. The most painful was the
parameter-VarId aliasing one (#1b) — two functions sharing a
parameter NAME end up sharing a single zero-page slot mapping
across the whole program. Once those compiler bugs are fixed, the
workarounds in war/*.ne should be reverted in the same PR.
https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
2026-04-15 15:22:20 +00:00
|
|
|
|
//
|
|
|
|
|
|
// The `palette_map:` field is omitted on purpose: every attribute
|
|
|
|
|
|
// byte defaults to 0, which selects bg sub-palette 0 for every
|
2026-04-15 16:08:03 +00:00
|
|
|
|
// metatile. That's the felt palette declared in the top-level
|
|
|
|
|
|
// war.ne (forest / green / mint over a dk_green universal).
|
examples/war: working end-to-end War card game
A complete, playable port of the card game War: title screen with
0/1/2 player menu, animated deal, sliding cards, deck-count HUD, a
"WAR!" tie-break with buried cards, and a victory screen with a
fanfare. Source split across examples/war/*.ne (constants, assets,
audio, deck/queue logic, RNG, render helpers, and one state file
per game state) and pulled in via examples/war.ne.
Drives nearly every NEScript subsystem at once: custom 88-tile
sprite sheet (card frames, ranks, suits, font, BIG WAR letters);
felt background nametable; pulse-1 / pulse-2 / noise sfx; looping
march on pulse 2; an 8-bit Galois LFSR PRNG; queue-based decks
that conserve cards across rounds; a phase machine inside the
Playing state that handles draw/reveal/win/war/check; and an
autopilot that boots straight into 0-PLAYERS mode so the headless
jsnes harness captures real gameplay at frame 180.
While building this I uncovered five compiler bugs / limitations
in the v0.1 implementation; each is documented with a minimal
reproduction, root cause, current workaround, and proposed fix in
examples/war/COMPILER_BUGS.md. The most painful was the
parameter-VarId aliasing one (#1b) — two functions sharing a
parameter NAME end up sharing a single zero-page slot mapping
across the whole program. Once those compiler bugs are fixed, the
workarounds in war/*.ne should be reverted in the same PR.
https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
2026-04-15 15:22:20 +00:00
|
|
|
|
|
|
|
|
|
|
background Felt {
|
|
|
|
|
|
legend {
|
|
|
|
|
|
".": 75 // TILE_FELT_BG — sparse mint flecks on dk_green
|
|
|
|
|
|
}
|
|
|
|
|
|
map: [
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................",
|
|
|
|
|
|
"................................"
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|