Files
uv-k5-v3-emulator/apps/minesweeper/README.md
T
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

62 lines
3.5 KiB
Markdown

# 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](https://github.com/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.