coding

How to Code Minesweeper: A Complete Guide to Building the Game from Scratch

Por Henrick June 27, 2026 7 min read 42 visualizações

Minesweeper is one of the best projects for learning to program. It packs a 2D grid, recursion, randomness, event handling, and a clean win/lose state into a game small enough to finish in an afternoon yet deep enough to teach real engineering. This pillar guide walks through how to code Minesweeper from first principles, then points you to focused, step-by-step tutorials for Python, Java, C#, and even Scratch. We finish with the math behind the board generator and the famous result that Minesweeper is NP-complete.

Want to study a finished, polished implementation while you build? Play the live game at Expert mode or try our no-guessing boards to see exactly how a correct reveal and flag flow should feel.

The core data model: representing the board

Every Minesweeper engine starts with two pieces of state per cell: what the cell is (a mine or a number) and what the player has done to it (hidden, revealed, or flagged). Beginners often try to cram everything into a single grid of characters, but separating the hidden truth from the visible state keeps your logic clean.

A simple, language-agnostic model uses a struct or object per cell:

Cell {
  isMine: boolean        // true if a bomb lives here
  adjacentMines: int     // 0-8, computed once at setup
  isRevealed: boolean
  isFlagged: boolean
}

Board = 2D array of Cell, sized rows x cols

The adjacentMines count is the number you draw on the tile. You can compute it lazily when a cell is revealed, but most engines precompute it once after mines are placed because it never changes during a game. This is also where you decide your difficulty: Beginner is 9x9 with 10 mines, Intermediate 16x16 with 40, and Expert 16x30 with 99.

Placing mines and guaranteeing a safe first click

Random mine placement looks trivial, but there is one rule that separates a toy from a real game: the player's first click must never be a mine, and on good boards the first click opens an empty region. The standard technique is deferred placement: do not place mines until after the first click, then exclude the clicked cell (and usually its eight neighbors) from the candidate set.

function placeMines(board, count, safeRow, safeCol):
    forbidden = neighborsOf(safeRow, safeCol) + (safeRow, safeCol)
    placed = 0
    while placed < count:
        r, c = randomCell()
        if (r, c) in forbidden: continue
        if board[r][c].isMine: continue
        board[r][c].isMine = true
        placed += 1
    computeAllAdjacentCounts(board)

To avoid an infinite loop on dense boards, you can instead build a flat list of all eligible cells, shuffle it (Fisher-Yates), and take the first count entries. Once mines are down, walk every cell and count mines in its up-to-eight neighbors to fill adjacentMines.

A subtle upgrade used by competitive sites is the no-guess guarantee: after generating a board you run a solver to confirm it can be completed by logic alone, regenerating until it can. We dig into that in the algorithm article linked below.

Revealing cells with flood fill

When a player clicks a hidden cell, three things can happen. If it is a mine, the game ends. If it has a number greater than zero, you reveal just that one cell. If it is a zero (no adjacent mines), you cascade outward and automatically reveal the whole connected empty region plus its numbered border. That cascade is a classic flood fill, the single most important algorithm in the game.

function reveal(board, r, c):
    cell = board[r][c]
    if cell.isRevealed or cell.isFlagged: return
    cell.isRevealed = true
    if cell.isMine:
        gameOver(); return
    if cell.adjacentMines == 0:
        for (nr, nc) in neighborsOf(r, c):
            reveal(board, nr, nc)   // recurse into empties

This recursive version is the easiest to read. On large Expert boards, deep recursion can risk a stack overflow in some languages, so production engines swap recursion for an explicit stack or queue (iterative breadth/depth-first search). Either way the result is identical: clicking one empty square opens an entire room.

Flagging, chording, and win/lose detection

Flagging is purely a player-state toggle: right-click (or long-press) flips isFlagged on a hidden cell. Flags never affect the hidden truth; they are just the player's notes, so your win check must ignore them. A popular advanced input is chording: clicking a revealed number whose flag count equals its value reveals all remaining neighbors at once, which is how fast players clear boards.

Win and lose detection are simpler than newcomers expect:

  • Lose: the player reveals any mine. End immediately and typically reveal all mines.
  • Win: every non-mine cell is revealed. Equivalently, the number of revealed cells equals (rows x cols) - mineCount. You do not need every mine to be flagged.
function hasWon(board):
    safeCells = rows*cols - mineCount
    return revealedCount == safeCells

Track revealedCount incrementally as you reveal cells rather than scanning the whole grid each click; it is faster and keeps the loop trivial.

Building the UI and game loop

With the engine done, the user interface is mostly translation: draw the grid, map clicks to (row, col), and re-render after each action. Keep the engine and the UI separate so you can reuse the same logic across a console app, a desktop window, or a web canvas. A typical loop is: render board, wait for input, apply the action (reveal, flag, or chord), check for win or lose, repeat. Add a timer and a mine counter (mines minus flags placed) and you have the classic chrome players expect.

Pick your language: focused build tutorials

The model above is universal, but the syntax, libraries, and UI toolkit change per language. Use whichever tutorial matches your stack:

The algorithm behind fair boards

Once your game works, the interesting question becomes board quality. Why do good Minesweeper boards rarely force a coin-flip guess, and how does a generator guarantee that? The answer combines randomized placement with a constraint solver that checks solvability before serving the board. For the full breakdown of fair-board generation, read Minesweeper algorithm explained: how the game generates fair boards. It connects directly to the deferred-placement and no-guess ideas mentioned above.

The theory: is Minesweeper NP-complete?

Minesweeper is not just a coding exercise; it sits on a famous result in computational complexity. Determining whether a given partially revealed board is logically consistent is NP-complete, which links the humble grid to some of the deepest open problems in computer science. If you enjoy the math behind the mines, explore is Minesweeper NP-complete? The mathematics and logic behind the game. Understanding it will sharpen both your solver and your own play.

Putting it all together

To recap the build: model each cell with its hidden truth and player state; defer mine placement so the first click is always safe; precompute adjacency counts; use flood fill to cascade empty regions; treat flags as notes only; and detect a win when all non-mine cells are revealed. Layer a thin UI on top, add a timer and mine counter, and you have a complete game. From there, the algorithm and theory articles take you from a working clone to a genuinely fair, well-engineered Minesweeper.

The fastest way to develop intuition for correct behavior is to play a reference build while you code. Open the no-guessing mode to study fair-board logic, or jump straight into a full Expert game and watch how reveals, chords, and the win state should behave. Happy building.

Browse all coding articles →
Put It Into Practice

Try what you just read: Beginner Minesweeper · Intermediate Minesweeper · Expert Minesweeper · No-Guessing mode · Desafio Diário. For technique deep-dives, see the patterns guide, efficiency & 3BV/s guide e Minesweeper FAQ.

Publicações relacionadas