PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates a candidate's competence in data structures, graph traversal and flood-fill concepts, complexity analysis, and scalable system design for sparse board representations.

  • Medium
  • Bridge
  • Coding & Algorithms
  • Software Engineer

Design Minesweeper and Optimize Click Performance

Company: Bridge

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: Medium

Interview Round: Technical Screen

Design a Minesweeper game. At game start, initialize an m×n board by randomly placing k bombs. Implement: ( 1) printBoard(): return a human-readable string of the current player-visible board (unrevealed cells remain hidden; revealed cells show 0–8; you may optionally support flags but keep the API clear). ( 2) click(r, c): if the cell is a bomb, mark game over; otherwise reveal the cell; if its adjacent-bomb count is zero, reveal the contiguous region per standard Minesweeper rules; handle repeated clicks, boundaries, and idempotency. Describe your data structures, key algorithms (e.g., BFS/DFS flood fill), and time/space complexity. Provide test cases. Follow-up: For very large, sparse boards (huge m, n with small k), propose and analyze optimizations to make click() fast and memory-efficient (e.g., lazy board generation, sparse data structures, on-demand neighbor counts, caching, or pruning), and discuss trade-offs.

Quick Answer: This question evaluates a candidate's competence in data structures, graph traversal and flood-fill concepts, complexity analysis, and scalable system design for sparse board representations.

Part 1: Simulate a Minesweeper Board

Implement the core Minesweeper behavior in a single function. For deterministic grading, the bomb locations are given directly instead of being randomized. The function must simulate two operations: `click r c` and `print`. A click on a bomb ends the game and shows that bomb as `X`. A click on a safe numbered cell reveals only that cell. A click on a safe zero cell reveals its full zero-region using standard Minesweeper flood fill, plus all bordering numbered cells. Repeated clicks must be idempotent, out-of-bounds clicks must be ignored, and after game over, future clicks do nothing. The `print` operation should return the current player-visible board as a string with rows separated by newline characters and cells separated by single spaces. Hidden cells are `#`.

Constraints

  • 1 <= m, n <= 200
  • 0 <= len(bombs) <= m * n
  • Bomb coordinates are distinct and within bounds
  • 0 <= len(operations) <= 10^4
  • Click coordinates may be out of bounds; such clicks are ignored
  • Coordinates are 0-indexed

Examples

Input: (3, 3, [[0, 0]], ["click 2 2", "print"])

Expected Output: ["# 1 0\n1 1 0\n0 0 0"]

Explanation: Clicking a zero at the bottom-right reveals the entire connected zero region and its bordering numbered cells.

Input: (2, 2, [[1, 1]], ["click 1 1", "print", "click 0 0", "print"])

Expected Output: ["# #\n# X", "# #\n# X"]

Explanation: The first click hits a bomb and ends the game. The second click is ignored, so both printed boards are identical.

Input: (1, 1, [], ["click 0 0", "click 0 0", "click 3 0", "print"])

Expected Output: ["0"]

Explanation: Single-cell edge case: the only cell is safe with count 0. Repeated and out-of-bounds clicks do not change the result.

Input: (2, 2, [[0, 0]], ["click 0 1", "print"])

Expected Output: ["# 1\n# #"]

Explanation: Clicking a numbered cell reveals only that one cell.

Solution

def solution(m, n, bombs, operations):
    from collections import deque

    bomb_set = {tuple(cell) for cell in bombs}
    counts = [[0] * n for _ in range(m)]
    directions = [(-1, -1), (-1, 0), (-1, 1),
                  (0, -1),           (0, 1),
                  (1, -1),  (1, 0),  (1, 1)]

    for r, c in bomb_set:
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            if 0 <= nr < m and 0 <= nc < n and (nr, nc) not in bomb_set:
                counts[nr][nc] += 1

    revealed = [[False] * n for _ in range(m)]
    game_over = False
    exploded = None

    def reveal_from(sr, sc):
        if revealed[sr][sc]:
            return
        if counts[sr][sc] != 0:
            revealed[sr][sc] = True
            return

        queue = deque([(sr, sc)])
        revealed[sr][sc] = True

        while queue:
            r, c = queue.popleft()
            for dr, dc in directions:
                nr, nc = r + dr, c + dc
                if not (0 <= nr < m and 0 <= nc < n):
                    continue
                if revealed[nr][nc] or (nr, nc) in bomb_set:
                    continue
                revealed[nr][nc] = True
                if counts[nr][nc] == 0:
                    queue.append((nr, nc))

    def print_board():
        lines = []
        for r in range(m):
            row = []
            for c in range(n):
                if exploded == (r, c):
                    row.append('X')
                elif revealed[r][c]:
                    row.append(str(counts[r][c]))
                else:
                    row.append('#')
            lines.append(' '.join(row))
        return '\n'.join(lines)

    outputs = []
    for op in operations:
        parts = op.split()
        if not parts:
            continue

        if parts[0] == 'click':
            if game_over or len(parts) != 3:
                continue
            r, c = int(parts[1]), int(parts[2])
            if not (0 <= r < m and 0 <= c < n):
                continue
            if (r, c) in bomb_set:
                game_over = True
                exploded = (r, c)
            else:
                reveal_from(r, c)
        elif parts[0] == 'print':
            outputs.append(print_board())

    return outputs

Time complexity: O(m*n + P*m*n + R), where P is the number of `print` operations and R is the total number of cells ever revealed by clicks (R <= m*n).. Space complexity: O(m*n).

Hints

  1. Precompute the adjacent bomb count for every non-bomb cell once at the start.
  2. For revealing a zero cell, use BFS or DFS over 8 directions, and reveal bordering numbered cells as you expand.

Part 2: Sparse Minesweeper Click Queries on a Huge Board

You are given a very large Minesweeper board with sparse bombs. Building the full `m x n` board is impossible. Each click query starts from a completely fresh hidden board and asks: how many cells would be revealed by standard Minesweeper rules if the player clicked cell `(r, c)`? Return `-1` if the click hits a bomb. Return `1` if it hits a numbered safe cell. If it hits a zero cell, reveal its full zero-region using 8-direction connectivity, plus all bordering numbered cells, and return the total number of revealed cells. The goal is to answer many queries quickly while using memory proportional to the number of bombs, not the board area.

Constraints

  • 1 <= m, n <= 10^9
  • 0 <= len(bombs) <= 200
  • 1 <= len(clicks) <= 10^5
  • Bomb coordinates are distinct and within bounds
  • Click coordinates are within bounds
  • Coordinates are 0-indexed

Examples

Input: (1000000000, 1000000000, [], [[123456789, 987654321]])

Expected Output: [1000000000000000000]

Explanation: With no bombs, every cell has count 0, so one click reveals the entire board.

Input: (5, 5, [[2, 2]], [[0, 0], [1, 1], [2, 2]])

Expected Output: [24, 1, -1]

Explanation: Corner click reveals all 24 safe cells, the adjacent numbered cell reveals only itself, and clicking the bomb returns -1.

Input: (5, 5, [[2, 1], [2, 3]], [[0, 0], [4, 4], [2, 2]])

Expected Output: [10, 10, 1]

Explanation: The two bombs create a blocked middle band. The top row and bottom row are separate zero-components; each reveals 5 zero cells plus 5 bordering numbered cells.

Input: (1, 1, [], [[0, 0]])

Expected Output: [1]

Explanation: Single-cell edge case with no bombs.

Solution

def solution(m, n, bombs, clicks):
    from bisect import bisect_right
    from collections import deque

    bomb_set = {tuple(cell) for cell in bombs}
    row_starts = {0, m}
    col_starts = {0, n}

    for r, c in bomb_set:
        for x in (r - 1, r, r + 1):
            if 0 <= x < m:
                row_starts.add(x)
                row_starts.add(x + 1)
        for y in (c - 1, c, c + 1):
            if 0 <= y < n:
                col_starts.add(y)
                col_starts.add(y + 1)

    rows = sorted(row_starts)
    cols = sorted(col_starts)
    R = len(rows) - 1
    C = len(cols) - 1
    row_len = [rows[i + 1] - rows[i] for i in range(R)]
    col_len = [cols[j + 1] - cols[j] for j in range(C)]

    status = [[0] * C for _ in range(R)]
    # 0 = zero region, 1 = numbered cell, 2 = bomb

    for i in range(R):
        if row_len[i] != 1:
            continue
        r0 = rows[i]
        for j in range(C):
            if col_len[j] != 1:
                continue
            c0 = cols[j]
            if (r0, c0) in bomb_set:
                status[i][j] = 2
                continue

            numbered = False
            for dr in (-1, 0, 1):
                for dc in (-1, 0, 1):
                    if dr == 0 and dc == 0:
                        continue
                    if (r0 + dr, c0 + dc) in bomb_set:
                        numbered = True
                        break
                if numbered:
                    break
            if numbered:
                status[i][j] = 1

    comp = [[-1] * C for _ in range(R)]
    reveal_size = []
    dirs = [(-1, -1), (-1, 0), (-1, 1),
            (0, -1),           (0, 1),
            (1, -1),  (1, 0),  (1, 1)]

    for i in range(R):
        for j in range(C):
            if status[i][j] != 0 or comp[i][j] != -1:
                continue

            cid = len(reveal_size)
            queue = deque([(i, j)])
            comp[i][j] = cid
            zero_area = 0
            border = set()

            while queue:
                x, y = queue.popleft()
                zero_area += row_len[x] * col_len[y]

                for dx, dy in dirs:
                    nx, ny = x + dx, y + dy
                    if not (0 <= nx < R and 0 <= ny < C):
                        continue
                    if status[nx][ny] == 0:
                        if comp[nx][ny] == -1:
                            comp[nx][ny] = cid
                            queue.append((nx, ny))
                    elif status[nx][ny] == 1:
                        border.add((nx, ny))

            reveal_size.append(zero_area + len(border))

    answers = []
    for r, c in clicks:
        i = bisect_right(rows, r) - 1
        j = bisect_right(cols, c) - 1
        cell_type = status[i][j]
        if cell_type == 2:
            answers.append(-1)
        elif cell_type == 1:
            answers.append(1)
        else:
            answers.append(reveal_size[comp[i][j]])

    return answers

Time complexity: O(S^2 + q log S), where q = len(clicks) and S = O(k) is the number of compressed row/column segments derived from k bombs.. Space complexity: O(S^2).

Hints

  1. Only rows and columns within distance 1 of some bomb can contain non-zero cells. Everything else is guaranteed to be zero.
  2. Coordinate-compress the interesting rows and columns, build zero-components on the compressed grid, and precompute each component's reveal size.
Last updated: Jun 8, 2026

Related Coding Questions

  • Solve path normalization and nested iterator - Bridge (Medium)

Loading coding console...

PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.