Clear the board without hitting mines. Auto-reveal uses BFS flood fill.
When you click an empty cell (0 adjacent mines), the game automatically reveals all connected empty cells using Breadth-First Search (BFS).
function revealCells(
board: Cell[][],
startR: number,
startC: number
): Cell[][] {
const newBoard = board.map(row => row.map(cell => ({ ...cell })));
const queue: [number, number][] = [[startR, startC]];
const visited = new Set<string>();
while (queue.length > 0) {
const [r, c] = queue.shift()!;
const key = `${r},${c}`;
if (visited.has(key)) continue;
visited.add(key);
const cell = newBoard[r][c];
if (cell.status === 'flagged' || cell.isMine) continue;
cell.status = 'revealed';
if (cell.adjacentMines === 0) {
for (const [dr, dc] of DIRECTIONS) {
const nr = r + dr;
const nc = c + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
if (!visited.has(`${nr},${nc}`)) queue.push([nr, nc]);
}
}
}
}
return newBoard;
}Mines are placed after the first click. The clicked cell and its 8 neighbors are excluded from mine placement, guaranteeing the first click always opens a safe area.
function placeMines(
rows: number,
cols: number,
mineCount: number,
firstClick: { r: number; c: number }
): Set<string> {
const excluded = new Set<string>();
for (let dr = -1; dr <= 1; dr++)
for (let dc = -1; dc <= 1; dc++)
excluded.add(`${firstClick.r + dr},${firstClick.c + dc}`);
const mines = new Set<string>();
while (mines.size < mineCount) {
const r = Math.floor(Math.random() * rows);
const c = Math.floor(Math.random() * cols);
const key = `${r},${c}`;
if (!excluded.has(key)) mines.add(key);
}
return mines;
}For each cell, we check all 8 neighbors and count mines. Using a Set for mine positions gives O(1) lookup.
function calcAdjacentMines(
r: number,
c: number,
mines: Set<string>,
rows: number,
cols: number
): number {
return DIRECTIONS.reduce((count, [dr, dc]) => {
const nr = r + dr;
const nc = c + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
if (mines.has(`${nr},${nc}`)) count++;
}
return count;
}, 0);
}