Files
mckero 61344e486e Minesweeper: verify its logic on the host, and document it in both languages
apps/minesweeper/host_test.c includes the app source with a fake app_api_t, so the real state machine runs on a PC: every string it draws is recorded and the lit pixels are counted. Measured -- a reveal/flag/new-game/digit/quit script returns normally, draws the title, the mine count and lights 24 pixels; a script that blindly reveals 85 cells reaches a terminal state eleven times, draws BOOM, and MENU starts a new game after it (terminal at record 356, title again at 4180). The win path is the one branch blind play does not reach.

Both READMEs now say what is verified (compiles clean under gcc -Wall -Wextra -Werror against upstream's real app_api.h, which is what caught the API's true member names and the absence of left/right keys; the logic runs on the host) and what is not (never built for ARM, never run on the radio -- no toolchain and no Docker here).
2026-10-01 22:31:22 +08:00

3.5 KiB

Minesweeper — an overlay app for the F4HWN Labs edition

Our own app: a 9x9 minesweeper that runs on the radio inside the 4 KiB overlay that App/apps/app_overlay.h reserves. It is written against upstream's App/apps/app_api.h and built with upstream's app.ld — neither is vendored here (both are Apache-2.0 files from armel/uv-k1-k5v3-firmware-custom), so drop this folder into App/apps/minesweeper/ next to them, or point -I at a copy.

Why it looks the way it does

constraint consequence
the radio has no left/right keys (UP, DOWN, MENU, EXIT, STAR, F, 0-9 only) the cursor walks the field with UP/DOWN and digits jump to a row then a column: 3 5 = row 3, column 5
4 KiB for text+rodata+data+bss together no lookup tables, no floats, no libc; adjacency is counted on the fly and each cell is one bit
81 cells do not fit a 16-bit mask three 9-byte bit arrays addressed by cell >> 3, cell & 7 — a uint16_t version compiled fine and was wrong past cell 15
the resident pixel helpers do not bound-check put()/invert() clip
no rand() in a freestanding blob a small LCG; mines are placed after the first reveal, keeping the 3x3 around it clear

Keys: UP/DOWN move, 1-9 pick row then column, MENU reveal, F flag, STAR new game, EXIT quit. M in the corner is the remaining-mine count, A1 is the cursor.

Build

arm-none-eabi-gcc -mcpu=cortex-m0plus -mthumb -Os -std=gnu11 -ffreestanding \
    -nostdlib -nostartfiles -T app.ld -Wl,--defsym,APP_VMA=0x20000280 \
    -o minesweeper.elf minesweeper_app.c
arm-none-eabi-objcopy -O binary minesweeper.elf minesweeper.bin
pack_app.py minesweeper.bin Minesweeper.app --name Minesweeper --ver 1.0 \
    --vma 0x20000280 --api-min 1      # pack_app.py lives in App/apps/

build.sh does exactly that and needs the Arm GNU Toolchain on PATH.

Then install it from the page: Overlay apps → a slot → pick Minesweeper.app → Install → Ask the radio should answer Minesweeper. On the radio press F then 7 and MENU to run it.

What is verified, and what is not

Verified here: the source compiles clean with gcc -Wall -Wextra -Werror against upstream's real app_api.h (that check caught the API's actual member names — api->fb, print_tiny(s, x, y, statusbar, fill) with five arguments — and the fact that APP_KEY_LEFT/APP_KEY_RIGHT do not exist).

Not verified: it has never been built for ARM or run on the radio, because no arm-none-eabi-gcc and no Docker exist on the machine it was written on. Treat the first build and the first run as the real review.

The host harness (how far verification got)

host_test.c includes the app source and hands it a fake app_api_t, so the real state machine runs on the PC -- every string it draws is recorded and the lit pixels are counted:

gcc -std=gnu11 -O1 -Wall -Wextra -Werror -I.. -o host_test host_test.c && ./host_test

Measured: a reveal/flag/new-game/digit/quit script returns normally, draws the title and the mine count, and lights pixels; a script that blindly reveals 85 cells reaches a terminal state eleven times, draws BOOM, and MENU starts a new game afterwards. The win path (CLEAR) was not reached by blind play, so it is the one branch this harness does not exercise.

Still unverified: never built for ARM, never run on the radio -- no arm-none-eabi-gcc and no Docker where it was written. The first build and the first run are the real review.