ℹ️ See also

A four-layer refinement of this design — splitting board into single-purpose mines and counts quadrilles, then using Quadrille.or to merge them into the two-tone cover in two lines — appears in Streamlining Minesweeper, revisited. Same game, each variable with one fixed meaning throughout.

In object-oriented programming (OOP), maximizing API use streamlines development, leading to simpler and more concise code. This clarity not only improves readability but also eases debugging. Leveraging an API effectively in OOP thus ensures a blend of simplicity, efficiency and maintainability in software development.

This demo (partially) implements the minesweeper video game using p5-v2 and the the Quadrille API with a multi‑layered strategy to instantiate and handle two quadrilles: a board and a mask. The former embodies the game board, while the latter covers it. As gameplay progresses, clicking on board cells uncovers them by clearing either individual or connected cells from the mask. Internally, a binaryMask quadrille tracks covered cells and differentiates mines/mine counts from empty cells. The visible mask is derived from binaryMask by uniformly coloring covered cells.

(mouse click to play; press any key to init)

Code
let board, binaryMask, mask;
let size = 20;
let n = size * 2;

function init() {
  // Create a new game board with n random mines
  board = createQuadrille(size, size, n, '💣');
  // Clone the board into the binaryMask
  binaryMask = board.clone();
  // Populate the binaryMask with mine counts, visiting only empty cells
  board.visit(({ row, col }) => {
    // The order variable stores the number of neighboring mines
    const order = board.ring(row, col).order;
    if (order > 0) {
      binaryMask.fill(row, col, order.toString());
    }
  }, ({ value }) => value === null);
  // Clone the updated binaryMask back to the board
  board = binaryMask.clone();
  // Colorize binaryMask cells: red for mines/mine counts, green for empty cells
  binaryMask.replace(color('red')).fill(color('green'));
  // Clone binaryMask onto mask and color covered cells magenta
  mask = binaryMask.clone().replace(color('magenta'));
}

function setup() {
  Quadrille.cellLength = 400 / size;
  createCanvas(400, 400);
  init();
  // Disable right-click menu
  document.oncontextmenu = () => false;
}

function draw() {
  background('moccasin');
  // Draw the static board
  drawQuadrille(board);
  // Draw the mask layer
  drawQuadrille(mask, { outline: color('lime') });
}

function mouseClicked() {
  const row = board.mouseRow;
  const col = board.mouseCol;
  // Update the binaryMask
  if (board.isFilled(row, col)) {
    // Reveal the entire board if a mine is clicked
    if (board.read(row, col) === '💣') {
      // Clear the entire binaryMask
      binaryMask.clear();
    } else {
      // Clear a single filled cell
      binaryMask.clear(row, col);
    }
  } else {
    // Clear multiple connected cells using flood fill
    binaryMask.clear(row, col, true);
  }
  // Update the mask after binaryMask changes
  mask = binaryMask.clone().replace(color('magenta'));
}

function keyPressed() {
  // Restart the game
  init();
}

Init

The init function builds the board and binaryMask quadrilles, then derives the visible mask:

function init() {
  // Create a new game board with n random mines
  board = createQuadrille(size, size, n, '💣');
  // Clone the board into the binaryMask
  binaryMask = board.clone();
  // Populate the binaryMask with mine counts, visiting only empty cells
  board.visit(({ row, col }) => {
    // The order variable stores the number of neighboring mines
    const order = board.ring(row, col).order;
    if (order > 0) {
      binaryMask.fill(row, col, order.toString());
    }
  }, ({ value }) => value === null);
  // Clone the updated binaryMask back to the board
  board = binaryMask.clone();
  // Colorize binaryMask cells: red for mines/mine counts, green for empty cells
  binaryMask.replace(color('red')).fill(color('green'));
  // Clone binaryMask onto mask and color covered cells magenta
  mask = binaryMask.clone().replace(color('magenta'));
}

ℹ️ Mask Display Layer

The mask visually hides the board. It’s created by cloning binaryMask and replacing every cell color (red for mines/mine counts, green for empties) with magenta. This uniform layer covers the game state while keeping the underlying data intact.

ℹ️ Ring Method

ring returns a Quadrille instance of all neighbors within radius 1. Its .order property tells how many of those neighbors are mines. That count is stored in binaryMask via fill, avoiding manual neighbor inspection.

ℹ️ Visit Predicate

The counting loop visits only empty cells thanks to the filter argument of visit, the predicate ({ value }) => value === null.

Toggle below to inspect the binaryMask (internal state) versus the board (game state):

(mouse click or press any key to toggle the binaryMask drawing)

Draw

The draw function renders the board and mask layers. The board is drawn first, followed by the dynamically updated mask:

function draw() {
  background('moccasin');
  // Draw the static game board
  drawQuadrille(board);
  // Draw the mask layer
  drawQuadrille(mask, { outline: color('lime') });
}

ℹ️ Info

  • Static layer (board): Shows mines and mine counts.
  • Dynamic layer (mask): Displays covered cells in magenta. As cells are cleared in binaryMask, mask reflects the updates, revealing the underlying board.

Interaction

Mouse clicks modify the binaryMask to uncover cells. The clear method employs three overloaded modes:

  1. Clear all cells: clear() resets the entire mask, setting all cells to empty (null).
  2. Single cell: clear(row, col) sets a specific cell to empty (null).
  3. Connected cells: clear(row, col, border) clears connected cells using flood fill.
function mouseClicked() {
  const row = board.mouseRow;
  const col = board.mouseCol;
  // Update the binaryMask
  if (board.isFilled(row, col)) {
    // Reveal the entire board if a mine is clicked
    if (board.read(row, col) === '💣') {
      // Clear the entire binaryMask
      binaryMask.clear();
    } else {
      // Clear a single filled cell
      binaryMask.clear(row, col);
    }
  } else {
    // Clear multiple connected cells using flood fill
    binaryMask.clear(row, col, true);
  }
  // Update the mask after binaryMask changes
  mask = binaryMask.clone().replace(color('magenta'));
}

Further Exploration

  1. Safe first click: Recreate the board after the first click to ensure the player never starts on a mine.
  2. Difficulty levels: Adjust the grid size, mine count, or mine density.
  3. Undo / redo timeline: Store previous board states so players can move backward and forward through the game history.
  4. Timer: Track and display the completion time.
  5. Flagging mechanic: Use right-click, or another input, to mark suspected mines.
  6. Win condition: Define when the game is complete, such as revealing all safe cells and/or correctly flagging all mines.
  7. Enhanced graphics: Replace raw emoticons with custom cell effects.
  8. Scoring system: Reward safe reveals, correct flags, speed, or efficient play.
  9. Visual hints: Add an option to display the binaryMask or provide other visual aids for learning and accessibility.
  10. Research-inspired feature: After studying one paper from Further Reading, propose and implement one feature inspired by it.

References

Quadrille API

Coding Train Tutorial

Further Reading

  1. Math papers. These papers present Minesweeper as a small mathematical model of constraint reasoning, logical deduction, probability, and computational complexity. Each revealed number constrains the hidden neighboring cells, and the player must reason from those constraints while managing uncertainty. The surprising result is that this reasoning can become computationally very hard, making Minesweeper a useful first example of how familiar games can lead to deep questions about algorithms, logic, and uncertainty.

  2. Research papers. Minesweeper has also been used as a compact experimental platform for studying programming, learning, usability, human-computer interaction, artificial intelligence, sensors, authentication, cognitive science, and quantum-game ideas.

  3. School papers. These papers present Minesweeper as an accessible but rich student project: it can be implemented as a game, analyzed as a computational problem, and extended with solvers based on constraint satisfaction, equations, graphical models, machine learning, flood fill, and reinforcement learning.