ℹ️ See also
A four-layer refinement of this design — splitting
boardinto single-purposeminesandcountsquadrilles, then usingQuadrille.orto merge them into the two-tonecoverin 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
maskvisually hides the board. It’s created by cloningbinaryMaskand replacing every cell color (redfor mines/mine counts,greenfor empties) withmagenta. This uniform layer covers the game state while keeping the underlying data intact.
ℹ️ Ring Method
ringreturns a Quadrille instance of all neighbors within radius 1. Its.orderproperty tells how many of those neighbors are mines. That count is stored inbinaryMaskviafill, 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 inmagenta. As cells are cleared inbinaryMask,maskreflects the updates, revealing the underlyingboard.
Interaction
Mouse clicks modify the binaryMask to uncover cells. The clear method employs three overloaded modes:
- Clear all cells: clear() resets the entire mask, setting all cells to empty (
null). - Single cell: clear(row, col) sets a specific cell to empty (
null). - 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
- Safe first click: Recreate the board after the first click to ensure the player never starts on a mine.
- Difficulty levels: Adjust the grid size, mine count, or mine density.
- Undo / redo timeline: Store previous board states so players can move backward and forward through the game history.
- Timer: Track and display the completion time.
- Flagging mechanic: Use right-click, or another input, to mark suspected mines.
- Win condition: Define when the game is complete, such as revealing all safe cells and/or correctly flagging all mines.
- Enhanced graphics: Replace raw emoticons with custom cell effects.
- Scoring system: Reward safe reveals, correct flags, speed, or efficient play.
- Visual hints: Add an option to display the
binaryMaskor provide other visual aids for learning and accessibility. - Research-inspired feature: After studying one paper from Further Reading, propose and implement one feature inspired by it.
References
Quadrille API
- createQuadrille(width, height, order, value)
- drawQuadrille(quadrille, options)
- visit
- clone()
- read(row, col)
- mouseRow
- mouseCol
- isFilled()
- ring(row, col, dimension, wrap)
- order
- replace(value)
- fill(value)
- clear()
Coding Train Tutorial
Further Reading
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.
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.
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.