Add console Minesweeper with a UI-free engine

Rules and board state live in Minesweeper.Core with no console calls, so the same board drives both the CLI and the tests.

Engine: seeded or random boards with classic presets; first click always safe (and its ring too when the board has room); cascading reveal that stops at numbers; flagging and chording; win by clearing every safe cell; FromMines() for deterministic layouts.

CLI: typed commands ("3 4", "f 2 5", "chord 6 7") plus optional arrow-key mode. Rows numbered, columns lettered, player input 1-based while engine coordinates stay 0-based, with CommandParser the only place that converts. ANSI colour when writing to a terminal, plain when redirected or NO_COLOR is set.

Tests: 186 covering flood-fill bounds, first-click safety, chording, win/loss, command parsing, session behaviour, and renderer alignment with colour on and off.

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
openhands
2026-09-05 21:50:11 +00:00
commit fd270095df
25 changed files with 2932 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
# Minesweeper
A console Minesweeper for the terminal, built on .NET 10. The rules live in a UI-free engine, so
the same board can be driven by the console client or by tests.
```
9x9 mines left 10 flags 0/10 time 12s
Reveal every cell that is not a mine.
A B C D E F G H I
1 # # 1 · · 1 # # #
2 1 1 1 · · 1 1 2 1
3 · · · · · · · · ·
4 2 2 1 · · · · · ·
5 # # 1 · · · · · ·
6 # # 2 1 2 1 1 · ·
7 # # # # # # 2 2 2
8 # # # # # # # # #
9 # # # # # # # # #
type "<row> <col>" to reveal, f <row> <col> to flag, ? for help, q to quit
>
```
## Play it
```bash
dotnet run --project src/Minesweeper.Cli
```
| Option | Meaning |
| --- | --- |
| `-d, --difficulty <beginner\|intermediate\|expert>` | board preset (default: beginner) |
| `-b`, `-i`, `-e` | shorthand for the three presets |
| `-k, --keys` | arrow-key play instead of typed commands |
| `--seed <n>` | deterministic mines, for reproducible games |
| `-h, --help` | usage, command list, and legend |
Beginner is 9x9 with 10 mines, intermediate 16x16 with 40, expert 30x16 with 99.
## Commands
Rows are numbered `1..N` down the left, columns are lettered `A..Z` (then `AA..`) across the top.
| Command | Does |
| --- | --- |
| `<row> <col>` or `reveal <row> <col>` | open a cell (`r`, `open` are aliases) |
| `flag <row> <col>` | place or remove a flag (`f`, `mark`) |
| `chord <row> <col>` | open the neighbours of a number once its flags match (`c`) |
| `go <row> <col>` | move the cursor without opening anything (`goto`) |
| `difficulty <name>` | switch preset, by name or `1`/`2`/`3` |
| `new` | restart at the current difficulty |
| `help` / `?` | list the commands |
| `quit` / `q` | leave |
In `--keys` mode: arrows move, `Enter`/`Space` reveals, `F` flags, `C` chords, `1`/`2`/`3` change
difficulty, `N` starts a new board, `Q` or `Esc` quits.
Colours are on when writing to a terminal and off when the output is redirected; set `NO_COLOR`
to force them off. The process exits `0` after a win, `1` otherwise.
## Rules
- The first click is always safe. On boards with room, the eight cells around it are safe too.
- Opening a cell with no adjacent mines cascades outward until it hits numbers.
- Chording a number whose surrounding flag count matches opens the rest of its neighbours — so a
mistaken flag costs you the game.
- You win by opening every cell that is not a mine; remaining mines are flagged for you.
## Layout
```
src/Minesweeper.Core rules and board state, no console calls
src/Minesweeper.Cli command parsing, ANSI rendering, the game loop
tests/ xUnit tests for the engine, parser, session, and renderer
```
```bash
dotnet build # compiles all three projects
dotnet test # runs the suite
```