The first-pass card tiles were riddled with palette-0 transparent
pixels that let the felt background bleed through every rank
glyph, small suit, and big pip. At arm's length the cards looked
like they had green specks eating them. This commit rewrites all
of the card art from scratch:
- **Opaque card bodies.** Every pixel inside a card tile is now
either white (palette 2), red (1), or black (3). No `.` values
anywhere on a face or back tile. The green felt only shows
outside the card rectangle, where it should.
- **Readable rank glyphs.** The 13 rank tiles (A, 2-9, 10, J, Q, K)
are now drawn as bold black strokes on a solid white body. The
"holes" of the letters (e.g. the triangle inside an "A") are
white, not transparent.
- **16x16 big pips.** The big centre pip is now a 4-tile (16x16)
shape split into TL/TR/BL/BR quadrants per suit. Previously it
was a 2-tile (16x8) half-height strip that looked cramped. The
TL/TR quadrants kept their existing tile indices (28-35) so the
shift is local; the new BL/BR quadrants are appended after the
BIG WAR letters at tiles 88-95 to avoid renumbering the entire
alphabet / digits / UI tile range.
- **Distinct suit shapes.** Spade is a smooth teardrop with a
short stem and base; heart is two symmetric lobes with a V
bottom; diamond is a clean rhombus; club is three circles
joined over a stem. Side-by-side they are unmistakable.
- **Clean checkerboard card back.** The old card back was a
diamond lattice that had the same transparent-bleed problem
and looked noisy anyway. Replaced with a crisp 2-pixel black-
and-white checkerboard that tiles seamlessly across the card
back's 16x24 footprint.
- **draw_card_face now emits 6 sprites in a rank/suit + 4-tile
big-pip layout.** The previous 6-sprite layout was
`[rank][ssuit] / [pipL][pipR] / [blankL][blankR]`; the new
one is `[rank][ssuit] / [pipTL][pipTR] / [pipBL][pipBR]` with
the bottom row carrying the bottom half of the big pip
instead of being wasted blank tiles.
Also:
- New constants TILE_PIP_TL_BASE / TR / BL / BR replace the old
TILE_PIP_L_BASE / TILE_PIP_R_BASE in constants.ne.
- Refreshed war.nes and the goldens. Every emulator harness
test still passes (31/31).
https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
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