124 lines
5.0 KiB
Markdown
124 lines
5.0 KiB
Markdown
# Minesweeper
|
|
|
|
A terminal game of Minesweeper in C# for .NET 10, plus a built-in solver so the game can be
|
|
played, scripted, and tested without a keyboard.
|
|
|
|
```bash
|
|
dotnet run --project src/Minesweeper # play (Beginner)
|
|
dotnet run --project src/Minesweeper -- -d expert
|
|
dotnet run --project src/Minesweeper -- --auto # watch the solver play
|
|
```
|
|
|
|
## Playing
|
|
|
|
| Key | Action |
|
|
| --- | --- |
|
|
| arrow keys, `Home`, `End` | move the selector |
|
|
| `space` / `enter` | open a square, or chord when standing on a number |
|
|
| `c` | chord the number under the selector |
|
|
| `f` (or `/`) | flag a suspect square |
|
|
| `q` (or `?`) | question mark a square |
|
|
| `p` | pause (freezes the clock) |
|
|
| `n` | new game on the same board |
|
|
| `d`, `1`, `2`, `3` | next difficulty, or jump to Beginner / Intermediate / Expert |
|
|
| `s` | dump the board as text, then return |
|
|
| `h` or `F1` | key list |
|
|
| `esc` | quit and print the final board |
|
|
|
|
Rules are the classic ones: the first click is never a mine (and by default opens an area),
|
|
a number with exactly as many flags around it as it needs opens the rest, and flagged squares
|
|
hold back a flood fill. Losing shows every mine and marks flags that were on safe ground.
|
|
|
|
## Options
|
|
|
|
`--help` lists everything. Highlights:
|
|
|
|
```bash
|
|
minesweeper -d intermediate # 16x16 with 40 mines
|
|
minesweeper -s 30x20x120 # any board up to 120x60
|
|
minesweeper -s 12x8 -m 5 # size plus mine count
|
|
minesweeper --seed 42 # reproducible mines and solver guesses
|
|
minesweeper --compact --ascii # one plain ASCII character per square
|
|
minesweeper --no-color # monochrome (NO_COLOR is honoured too)
|
|
minesweeper --auto-trace --seed 7 # print every solver decision
|
|
minesweeper --list-levels # show the presets
|
|
```
|
|
|
|
## Auto mode
|
|
|
|
`--auto` runs the solver in `src/Minesweeper/Game/Solver.cs`: it applies the two deductions
|
|
every player uses (a number touching exactly as many hidden squares as it still needs is all
|
|
mines; a satisfied number chords), then guesses the lowest risk square when logic runs out.
|
|
It prints the finished board and exits with `0` on a win, `1` on a loss, which makes it a
|
|
handy end-to-end check:
|
|
|
|
```bash
|
|
dotnet run --project src/Minesweeper -- --auto -d beginner --seed 7
|
|
```
|
|
|
|
## Layout
|
|
|
|
| Path | What lives there |
|
|
| --- | --- |
|
|
| `src/Minesweeper/Game/Board.cs` | grid, mine placement, flood fill, chording |
|
|
| `src/Minesweeper/Game/MinesweeperGame.cs` | one round: safe first click, flags, win/loss |
|
|
| `src/Minesweeper/Game/Solver.cs` | the deduction and guessing strategy |
|
|
| `src/Minesweeper/Cli/ConsoleRenderer.cs` | flicker-free drawing (only changed squares are written) |
|
|
| `src/Minesweeper/Cli/InteractiveRunner.cs` | keyboard loop, clock, pause, difficulty switching |
|
|
| `src/Minesweeper/Cli/Options.cs` | command line parsing |
|
|
| `tests/Minesweeper.Tests` | rules, solver, difficulty and CLI tests |
|
|
|
|
The game logic has no console dependencies, so it can be unit tested or driven headlessly.
|
|
|
|
## Building and testing
|
|
|
|
```bash
|
|
dotnet build # or dotnet run --project src/Minesweeper
|
|
dotnet test # 65 tests
|
|
```
|
|
|
|
## Code style
|
|
|
|
Everything here follows the checked-in `.editorconfig`: UTF-8 BOM and four space indents
|
|
for C#, two spaces for project files, `System` usings first, no redundant `this.`, and
|
|
`s_` / `_` prefixes for private static and instance fields. A checkout can be verified
|
|
without changing it:
|
|
|
|
```bash
|
|
dotnet format --verify-no-changes # exits non-zero if anything drifted
|
|
dotnet format whitespace --verify-no-changes # layout only
|
|
```
|
|
|
|
## Standalone binaries
|
|
|
|
`./publish.sh` produces a self-contained, trimmed, single-file binary for each runtime under
|
|
`artifacts/<runtime>/`. Nothing has to be installed on the machine that runs them.
|
|
|
|
```bash
|
|
./publish.sh # linux-x64 and win-x64
|
|
./publish.sh linux-arm64 # any runtime identifier
|
|
|
|
dotnet publish src/Minesweeper -c Release -r win-x64 --self-contained \
|
|
-p:PublishTrimmed=true -p:PublishSingleFile=true # the same thing by hand
|
|
```
|
|
|
|
| Binary | Size | Runs on |
|
|
| --- | --- | --- |
|
|
| `artifacts/linux-x64/minesweeper` | 14 MB | 64-bit Linux with glibc |
|
|
| `artifacts/win-x64/minesweeper.exe` | 13 MB | 64-bit Windows 10 or newer |
|
|
|
|
Trimming keeps only the runtime the game touches, and the publish is warning free. Windows
|
|
binaries can be cross-compiled from Linux or macOS; `--self-contained false` gives a small
|
|
framework-dependent binary instead.
|
|
|
|
## Notes
|
|
|
|
* Interactive play needs a real terminal. With output redirected the game says so and points
|
|
at `--auto`.
|
|
* Board squares are two characters wide so the field looks square; the game quietly drops to
|
|
one character when the terminal is too narrow, and refuses boards that still cannot fit.
|
|
* Colors come off automatically when `NO_COLOR` is set; `--ascii` swaps the shaded blocks,
|
|
flags and mines for `#`, `F`, `*` if a terminal struggles with them.
|
|
* A board keeps at least one safe square: asking for more mines than fit is an error, and a
|
|
board too small for the safe zone simply ends up with fewer mines.
|