1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 08:55:38 +00:00
nescript/examples/match_demo.ne
Claude 1525922faa
examples: add 5 new programs covering match/loop/logic/bitwise/scanline
Fills the biggest feature-coverage gaps in the existing example set:

- match_demo.ne          — match statement over a Screen enum,
                           driving a title / playing / paused /
                           game-over flow with a debounced controller.
- loop_break_continue.ne — `loop { ... }` with `break` and `continue`,
                           scanning an enemy array for the first hit.
- logic_ops.ne           — keyword-based `and` / `or` / `not` gating
                           movement and scoring on alive/paused flags.
- bitwise_ops.ne         — packed status-byte flags with `&` / `|` /
                           `^` / `>>` plus a health-bar render loop.
- scanline_split.ne      — MMC3 `on scanline(120)` handler rewriting
                           the scroll register mid-frame for a
                           classic status-bar split.

All 14 examples (9 existing + 5 new) pass the jsnes smoke test
(`14/14 ROMs rendered successfully`) and still pass `cargo fmt`,
`cargo clippy -D warnings`, and `cargo test`.

Known limitations surfaced while authoring these examples, to be
fixed in follow-up commits:

1. Array-literal global initializers (`var xs: u8[4] = [1,2,3,4]`)
   are silently dropped by `lower_program` — `eval_const` returns
   None for `Expr::ArrayLiteral` and no synthetic per-element
   init code is emitted. Affects `arrays_and_functions`,
   `structs_enums_for`, `loop_break_continue`, and any future
   array-using example. Arrays effectively boot at all-zero.

2. `draw` inside a loop body reuses one static OAM slot —
   `next_oam_slot` increments at IR-codegen time rather than at
   runtime, so N iterations all write to the same 4-byte OAM
   entry. Affects `arrays_and_functions`, `structs_enums_for`,
   `bitwise_ops` (health pips), and any loop that wants to
   render per-iteration sprites.

Both bugs are latent and didn't surface until I tried to write
examples that exercise the relevant features — the existing
integration tests only check iNES header structure, and the
jsnes smoke test's "at least one sprite rendered" bar is
satisfied by one sprite even when several were intended.

https://claude.ai/code/session_014Z5y3Q9krLcAxYpZQJhZ5V
2026-04-12 19:05:18 +00:00

76 lines
2.1 KiB
Text

// Match Demo — demonstrates the `match` statement.
//
// Match dispatches on a value against a sequence of patterns. Each
// arm runs only when its pattern compares equal to the scrutinee,
// and the underscore arm catches everything else. Here we drive a
// tiny title/playing/paused/game-over menu purely through a match
// on a u8 screen mode, cycled by the d-pad buttons.
//
// Build: cargo run -- build examples/match_demo.ne
game "Match Demo" {
mapper: NROM
}
enum Screen { Title, Playing, Paused, GameOver }
var screen: u8 = Title
var x: u8 = 120
var y: u8 = 112
var debounce: u8 = 0
on frame {
// Debounce so a held button doesn't spam transitions each frame.
if debounce > 0 {
debounce -= 1
}
match screen {
Title => {
// Blink the pointer by alternating its position.
draw Arrow at: (80, 112)
if button.start and debounce == 0 {
screen = Playing
debounce = 20
}
}
Playing => {
if button.right { x += 2 }
if button.left { x -= 2 }
if button.down { y += 2 }
if button.up { y -= 2 }
draw Player at: (x, y)
if button.select and debounce == 0 {
screen = Paused
debounce = 20
}
if button.b and debounce == 0 {
screen = GameOver
debounce = 20
}
}
Paused => {
// Freeze the player where it was and show a pause marker.
draw Player at: (x, y)
draw Arrow at: (120, 80)
if button.select and debounce == 0 {
screen = Playing
debounce = 20
}
}
GameOver => {
draw Skull at: (120, 112)
if button.a and debounce == 0 {
screen = Title
debounce = 20
}
}
_ => {
// Defensive: an unexpected value lands back on the title.
screen = Title
}
}
}
start Main