Problem
You have a 2D Minesweeper board where each cell is either an unrevealed mine ('M'), an unrevealed empty square ('E'), a revealed blank ('B', meaning no adjacent mines), or a revealed digit ('1'โ'8', the count of adjacent mines). Given a single click on one cell, update the board according to Minesweeper rules and return it.
The rules: if the clicked cell is a mine, mark it 'X' (game over). If it is empty, count its mines in all 8 directions โ if that count is positive, reveal the digit; if zero, reveal 'B' and recursively reveal every unrevealed empty neighbor.
E E E E E
E E M E E
E E E E E
E E E E E
- Input: board above, click = [3, 0]
- Output: the board below
- Explanation: clicking a zero-mine cell triggers a chain reveal that stops at cells adjacent to the mine
B 1 E 1 B
B 1 M 1 B
B 1 1 1 B
B B B B B
Counter-example โ clicking a mine ends the game immediately:
- Input: the same board, click = [1, 2] (the
'M'cell) - Output: that cell becomes
'X', everything else unchanged
Intuition
When a cell has no adjacent mines it acts like an open corridor โ there is nothing to hide, so Minesweeper automatically reveals every connected empty cell until it hits a wall of digits. This is exactly a DFS flood fill: the 'B' cells are the interior, the digit cells form the boundary, and you stop propagating the moment you cross it.
Solution โ DFS Flood Fill
Starting from the clicked cell, count mines in all 8 neighbors. If the count is nonzero, write the digit and stop. If zero, write 'B' and recurse into every still-unrevealed empty neighbor.
- If the clicked cell is
'M', set it to'X'and return โ game over. - Otherwise call
dfs(row, col)on the click position. - In
dfs: count all'M'cells among the 8 neighbors. - If
mine_count > 0, writestr(mine_count)into the cell and return โ no further spreading. - If
mine_count == 0, write'B'into the cell, then calldfson every neighbor that is still'E'. - The check
board[nr][nc] == 'E'prevents re-entering already-revealed cells, guaranteeing termination.
1def updateBoard(board: list[list[str]], click: list[int]) -> list[list[str]]:
2 rows, cols = len(board), len(board[0])
3 row, col = click
4
5 if board[row][col] == 'M':
6 board[row][col] = 'X'
7 return board
8
9 def dfs(r: int, c: int) -> None:
10 mine_count = 0
11 for dr in [-1, 0, 1]:
12 for dc in [-1, 0, 1]:
13 if dr == 0 and dc == 0:
14 continue
15 nr, nc = r + dr, c + dc
16 if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] == 'M':
17 mine_count += 1
18
19 if mine_count > 0:
20 board[r][c] = str(mine_count) # reveal digit and stop โ do not spread past this wall
21 else:
22 board[r][c] = 'B' # mark before recursing so neighbors skip this cell on their own pass
23 for dr in [-1, 0, 1]:
24 for dc in [-1, 0, 1]:
25 if dr == 0 and dc == 0:
26 continue
27 nr, nc = r + dr, c + dc
28 if 0 <= nr < rows and 0 <= nc < cols and board[nr][nc] == 'E':
29 dfs(nr, nc)
30
31 dfs(row, col)
32 return boardTime: O(m ร n) โ each cell is visited at most once since we only recurse into 'E' cells and immediately change them.
Space: O(m ร n) โ the DFS call stack depth is bounded by the total number of cells in the worst case where the entire board is one open region.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| DFS Flood Fill | O(m ร n) | O(m ร n) | Always โ single-pass DFS naturally encodes both the counting and spreading rules |
Common Mistakes
- Using only 4 directions instead of 8 โ Minesweeper counts diagonal neighbors for both the mine tally and the spread; missing diagonals produces wrong digit values and an incorrect reveal region.
- Not marking the cell
'B'before recursing โ if you flip the cell only after all 8 recursive calls return, every diagonal neighbor sees the current cell as still'E'and recurses back into it, causing infinite mutual recursion. - Spreading from digit cells โ only cells with zero adjacent mines become
'B'and propagate; cells with a nonzero count reveal their digit and stop. Continuing to recurse from digit cells incorrectly reveals cells hidden behind the mine boundary. - Including the current cell in its own mine count โ the
(dr == 0 && dc == 0)skip is essential; without it, a cell containing'M'would count itself, inflating every adjacent cell's digit by one. - Forgetting the mine-click branch โ if
board[row][col] == 'M', the cell should become'X'immediately and no DFS should run; skipping this branch passes the mine position into the empty-cell logic and produces garbage output.
Related Problems
Flood Fillโ the same 4-directional flood-fill primitive, without the mine-counting boundary conditionNumber of Islandsโ DFS flood fill to mark and count connected components on a 2D grid01 Matrixโ BFS spreading outward from all zero-cells simultaneously, the BFS counterpart to this DFSRotting Orangesโ BFS grid spread that also distinguishes between source cells and boundary cellsMax Area of Islandโ DFS flood fill that accumulates a size count rather than revealing neighbors