Adjacent Cells and Bomb Counting

Implementing a Minesweeper Hint System

Can we implement a hint system that reveals a safe cell when the player is stuck?

Abstract art representing computer programming

Absolutely! Implementing a hint system can greatly enhance the player experience, especially for beginners or when facing particularly challenging board configurations. Let's explore how we can add this feature to our Minesweeper game.

Hint System Design

Our hint system will reveal a safe cell that is adjacent to an already revealed cell. This approach ensures that the hint is always useful and doesn't just reveal a random safe cell that might be disconnected from the current game state.

Here's how we can implement this:

  1. Find all revealed cells with unrevealed neighbors.
  2. Among these neighbors, identify the safe ones (non-bomb cells).
  3. Randomly select one of these safe cells to reveal as a hint.

Let's modify our MinesweeperGrid class to include this hint system:

#include <algorithm>
#include <iostream>
#include <random>
#include <vector>

class MinesweeperCell {
public:
  bool hasBomb{false};
  bool isRevealed{false};
  int adjacentBombs{0};
};

class MinesweeperGrid {
public:
  MinesweeperGrid(int rows, int cols,
                  int bombs) :
      rows{rows},
      cols{cols}, bombCount{bombs},
      grid(rows,
           std::vector<MinesweeperCell>(cols)) {
    placeBombs();
    calculateAdjacentBombs();
  }

  bool revealCell(int row, int col) {
    if (row < 0 || row >= rows || col < 0 ||
        col >= cols)
      return false;
    if (grid[row][col].isRevealed) return false;

    grid[row][col].isRevealed = true;
    return !grid[row][col].hasBomb;
  }

  std::pair<int, int> getHint() {
    std::vector<std::pair<int, int>> safeCells;

    for (int i = 0; i < rows; ++i) {
      for (int j = 0; j < cols; ++j) {
        if (grid[i][j].isRevealed &&
            hasUnrevealedNeighbors(i, j)) {
          auto neighbors =
            getSafeUnrevealedNeighbors(i, j);
          safeCells.insert(safeCells.end(),
                           neighbors.begin(),
                           neighbors.end());
        }
      }
    }

    if (safeCells.empty())
      return {-1, -1}; // No hint available

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(
      0, safeCells.size() - 1);
    return safeCells[dis(gen)];
  }

  void printGrid() {
    for (const auto &row : grid) {
      for (const auto &cell : row) {
        if (cell.isRevealed) {
          std::cout
            << (cell.hasBomb
                  ? "*"
                  : std::to_string(
                      cell.adjacentBombs))
            << " ";
        } else {
          std::cout << ". ";
        }
      }
      std::cout << "\n";
    }
  }

private:
  int rows, cols, bombCount;
  std::vector<std::vector<MinesweeperCell>>
    grid;

  void placeBombs() {
    std::vector<int> cells(rows * cols);
    std::iota(cells.begin(), cells.end(), 0);
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(cells.begin(), cells.end(), g);

    for (int i = 0; i < bombCount; ++i) {
      int row = cells[i] / cols;
      int col = cells[i] % cols;
      grid[row][col].hasBomb = true;
    }
  }

  void calculateAdjacentBombs() {
    for (int i = 0; i < rows; ++i) {
      for (int j = 0; j < cols; ++j) {
        if (!grid[i][j].hasBomb) {
          grid[i][j].adjacentBombs =
            countAdjacentBombs(i, j);
        }
      }
    }
  }

  int countAdjacentBombs(int row, int col) {
    int count = 0;
    for (int i = std::max(0, row - 1);
         i <= std::min(rows - 1, row + 1);
         ++i) {
      for (int j = std::max(0, col - 1);
           j <= std::min(cols - 1, col + 1);
           ++j) {
        if (grid[i][j].hasBomb) ++count;
      }
    }
    return count;
  }

  bool hasUnrevealedNeighbors(int row,
                              int col) {
    for (int i = std::max(0, row - 1);
         i <= std::min(rows - 1, row + 1);
         ++i) {
      for (int j = std::max(0, col - 1);
           j <= std::min(cols - 1, col + 1);
           ++j) {
        if (!grid[i][j].isRevealed) return true;
      }
    }
    return false;
  }

  std::vector<std::pair<int, int>>
    getSafeUnrevealedNeighbors(int row,
                               int col) {
    std::vector<std::pair<int, int>>
      safeNeighbors;
    for (int i = std::max(0, row - 1);
         i <= std::min(rows - 1, row + 1);
         ++i) {
      for (int j = std::max(0, col - 1);
           j <= std::min(cols - 1, col + 1);
           ++j) {
        if (!grid[i][j].isRevealed &&
            !grid[i][j].hasBomb) {
          safeNeighbors.emplace_back(i, j);
        }
      }
    }
    return safeNeighbors;
  }
};

int main() {
  MinesweeperGrid game(8, 8, 10);
  // Simulate some gameplay
  game.revealCell(0, 0);
  game.revealCell(1, 1);
  game.printGrid();

  std::cout << "\nGetting a hint...\n";
  auto [hintRow, hintCol] = game.getHint();
  if (hintRow != -1 && hintCol != -1) {
    std::cout << "Hint: Try cell (" << hintRow
              << ", " << hintCol << ")\n";
    game.revealCell(hintRow, hintCol);
    game.printGrid();
  } else {
    std::cout << "No hint available.\n";
  }
}

This implementation adds a getHint() method to our MinesweeperGrid class. When called, it searches for a safe cell to reveal and returns its coordinates. The main function demonstrates how to use this hint system in the game loop.

Remember to integrate this hint system with your existing game logic and UI. You might want to limit the number of hints available to maintain the game's challenge level.

This Question is from the Lesson:

Adjacent Cells and Bomb Counting

Implement the techniques for detecting nearby bombs and clearing empty cells automatically.

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

Adjacent Cells and Bomb Counting

Implement the techniques for detecting nearby bombs and clearing empty cells automatically.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 51 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved