Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

 ###  ###  ###  #### ### 
#    #   # #  # #    #  #
 ##  ##### ###  ###  ### 
   # #   # #    #    # # 
###  #   # #    #### #  #

Saper

A Minesweeper clone for the terminal, built specifically to be played by touch on an Android tablet running Termux — no mouse, no keyboard required, just taps.

Written in Go with Bubble Tea, rendered entirely in ANSI box-drawing and 256-color, with a hybrid tap-and-button control scheme designed around one hard constraint: a terminal character cell is too small to reliably hit with a finger.

Why this exists

Terminal apps assume a keyboard. Touchscreens send taps, which a terminal emulator can forward as mouse events (xterm mouse reporting) — but a single character cell on a tablet screen is a few millimeters wide, far smaller than a fingertip. Saper's UI is built around that constraint from the ground up:

  • Tapping a board cell only moves the selection cursor. It never fires an action by itself, so a slightly-off tap can't accidentally detonate a mine.
  • Every action goes through a large, explicitly labeled, colored button (ОТКРЫТЬ / ФЛАГ / CHORD), not a tiny glyph.
  • Keyboard input (arrows/hjkl, enter, f, c, q) works everywhere too, as a fallback and for testing on a desktop terminal.

Features

  • Classic rules: safe first click, flood-fill reveal, flagging, and chording (opening all neighbors of a satisfied number at once).
  • Three presets (Beginner / Intermediate / Expert) plus a custom board size screen with +/- steppers.
  • Save & resume: progress is written to disk after every move; closing the app mid-game and reopening it offers to continue exactly where you left off, with the timer picking back up rather than jumping forward.
  • Statistics screen: games played, win %, best time, average time, and current/best win streak, per difficulty.
  • Settings screen: toggle sound (terminal bell) and vibration (via termux-vibrate, when reachable) independently, plus a high-contrast color theme.
  • Animated title screen: a big block-letter "SAPER" banner with a rainbow color wave and a blinking prompt, in the spirit of old console game intros.
  • Every screen is a single pure function from state to (lines, tap-targets), so what you see and what you can tap are always in sync by construction — see Architecture below.

Controls

Keyboard Touch / mouse
Move selection Arrow keys / h j k l Tap a cell
Reveal Enter / Space Tap the ОТКРЫТЬ button
Flag f Tap the ФЛАГ button
Chord c Tap the CHORD button
Back to menu q / n Tap the МЕНЮ button
Quit Ctrl+C

Getting started

Requirements

  • Go 1.25 or newer (the version declared in go.mod).
  • A terminal emulator with mouse-reporting support if you want touch/mouse input (Termux on Android works out of the box; most desktop terminals do too).

Build

git clone https://github.com/pashki975/saper.git
cd saper
./build.sh   # runs `go vet` + `go test`, then builds ./saper

or manually:

go build -o saper ./cmd/saper

Run

./saper

Clean

./clean.sh   # removes the ./saper binary and Go's build artifacts

Test

go test ./...

Project structure

saper/
├── build.sh                     # vet + test + build → ./saper
├── clean.sh                     # removes build output
├── LICENSE
├── go.mod / go.sum
│
├── cmd/saper/
│   └── main.go                  # entry point: wires internal/ui into a Bubble Tea program
│
└── internal/
    ├── game/                    # pure game engine — no I/O, no UI dependency
    │   ├── board.go             #   board generation, reveal/flood-fill, flag, chord, win/loss
    │   ├── difficulty.go        #   the three presets + custom-difficulty validation
    │   └── board_test.go
    │
    ├── store/                   # persistence — everything under ~/.config/minesweeper-tui/
    │   ├── store.go             #   records.json: per-difficulty stats (win%, avg time, streaks)
    │   ├── session.go           #   session.json: the save/resume snapshot of an in-progress game
    │   ├── settings.go          #   settings.json: sound/vibration/theme toggles
    │   └── *_test.go
    │
    ├── feedback/                # best-effort haptic/audio cues (never a hard dependency)
    │   └── feedback.go          #   terminal bell + termux-vibrate, both gated by settings
    │
    └── ui/                      # the Bubble Tea model — one file per screen
        ├── model.go             #   Model struct, Init/Update/View, mouse hit-testing, game actions
        ├── splash.go             #   animated title screen
        ├── menu.go              #   difficulty picker + resume/stats/settings entries
        ├── custom.go             #   custom board size screen
        ├── game.go               #   the board itself: grid, HUD, action buttons
        ├── stats.go              #   statistics screen
        ├── settings.go           #   settings screen
        ├── style.go              #   the color palette (default + high-contrast theme)
        └── widgets.go            #   shared rendering helpers (borders, tables, button rows)

Architecture

  • internal/game is a dependency-free engine: Board holds all state as exported fields (so it can be JSON-persisted verbatim for save/resume), and every rule — safe first click, iterative flood fill, chording, win detection — is unit tested in isolation from any UI concern.
  • internal/ui is a single Bubble Tea Model shared across screens (splash, menu, custom, game, stats, settings). Each screen implements a *Render() ([]string, []buttonRect) function that is the single source of truth for both what gets drawn (View()) and what can be tapped (layoutButtons()) — the two can never disagree, because they're the same computation.
  • internal/store persists three independent JSON documents (records, session, settings) under the user's XDG config directory, with no shared schema coupling — each survives the others changing shape.
  • internal/feedback wraps sound/vibration behind settings-driven toggles and fails silently if the mechanism isn't available (e.g. termux-vibrate unreachable from inside a proot-distro container) — feedback is cosmetic, never load-bearing.

Platform notes

Saper was built and tested inside Termux — including running a full Linux distribution via proot-distro (e.g. Ubuntu) inside Termux, which works transparently since it's just a regular Go binary underneath. Enable mouse reporting in your terminal if taps aren't registering; Termux does this automatically for apps that request it (which Saper does via Bubble Tea's WithMouseCellMotion).

Player data (best times, statistics, an in-progress game, and settings) lives entirely under ~/.config/minesweeper-tui/ as three small JSON files — nothing is written anywhere else.

License

MIT

About

Classic saper game for Linux console

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages