Implement the Core of Minesweeper
AmperitySoftware Engineer · Technical ScreenLast updated
Design an object-oriented Minesweeper game engine featuring state management, board generation, flood-fill cell revealing, and testable boundaries.
Task
Design and implement a testable, headless game engine for classic Minesweeper. Your engine should encapsulate domain logic (game state, cell transitions, flood-fill reveals) while exposing a clean interface for external consumers (such as a CLI or Web UI).
Requirements
Implement the Minesweeper class with the following specifications:
-
Initialization:
__init__(rows: int, cols: int, mines: list[tuple[int, int]])- Initializes a board of size
rows x colswith mines placed at the specified coordinate pairs(r, c). Accepting explicit coordinates ensures deterministic unit testing. - Initial game state is
"IN_PROGRESS".
-
Player Actions:
reveal(row: int, col: int) -> str:- If out of bounds, already revealed, or flagged, the board state does not change. Return the current game status.
- If the revealed cell contains a mine, the game state transitions to
"LOST". - If the revealed cell has 0 adjacent mines (out of the 8 neighboring cells), automatically reveal all adjacent unrevealed, unflagged cells recursively/iteratively (flood fill).
- If all non-mine cells are revealed, the game state transitions to
"WON". - Returns the updated game status (
"IN_PROGRESS","WON", or"LOST"). Once in"WON"or"LOST", subsequent calls should return the final status without modifying the board.
toggle_flag(row: int, col: int) -> None:- Toggles the flag status on an unrevealed cell. Has no effect on already revealed cells or when the game is over.
-
View Boundary:
get_board_view() -> list[list[str]]:- Returns a 2D grid representation of what the player is allowed to see:
'H': Hidden (unrevealed and unflagged)'F': Flagged'0'to'8': Revealed cell showing count of adjacent mines'M': Exposed mine (only displayed when the game is"LOST")
- Returns a 2D grid representation of what the player is allowed to see:
Constraints
1 <= rows, cols <= 500 <= len(mines) < rows * cols- Mine coordinates are distinct and guaranteed to fall within
[0, rows - 1]and[0, cols - 1].
Example
game = Minesweeper(rows=3, cols=3, mines=[(0, 0)])
# Initial state
game.get_board_view()
# [
# ['H', 'H', 'H'],
# ['H', 'H', 'H'],
# ['H', 'H', 'H']
# ]
# Flag a mine
game.toggle_flag(0, 0)
game.get_board_view()[0][0] # 'F'
# Reveal bottom-right corner (adjacent count is 0, triggers flood fill)
status = game.reveal(2, 2)
# Flood fill uncovers all cells except (0, 0), which is the only mine.
# Since all non-mine cells are uncovered:
# status == "WON"
game.get_board_view()
# [
# ['F', '1', '0'],
# ['1', '1', '0'],
# ['0', '0', '0']
# ]
Hints
Hint 1
Separate internal cell properties (has_mine, adjacent_mines, is_revealed, is_flagged) from the external string representation generated by get_board_view.
Hint 2
Use BFS or DFS for the flood fill, ensuring you only push adjacent cells onto the queue/stack if they are unrevealed, unflagged, and inside grid boundaries.
Hint 3
Track the count of unrevealed safe cells so you can evaluate the win condition in O(1) time after each move instead of scanning the full grid.
Follow-up questions
- How would you implement 'first-click safety' so a player is guaranteed never to click a mine on their first move?
- How would you support a 'chording' move (clicking an already revealed number to auto-reveal adjacent cells if the number of surrounding flags matches the cell's number)?
- If the board were sparse and scaled to 1,000,000 x 1,000,000 cells with 1,000 mines, how would you redesign the internal storage and traversal?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.