├── README.md ├── board.cpp ├── board.h ├── cell.cpp ├── cell.h ├── gameover.h ├── main.cpp ├── res ├── 0.png ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── b.png ├── f.png └── n.png ├── texture_manager.cpp └── texture_manager.h /README.md: -------------------------------------------------------------------------------- 1 | # Minesweeper 💣 2 | Minesweeper Game - in C++ 3 | 4 | 5 | 6 | ### Definition of `Cell` 7 | ```cpp 8 | // cell.h 9 | 10 | struct cell_t { 11 | private: 12 | bool is_mine = false; 13 | bool is_flagged = false; 14 | bool is_revealed = false; 15 | int adjacent_mines = 0; 16 | 17 | public: 18 | cell_t(); 19 | ~cell_t(); 20 | void place_mine(); 21 | bool reveal(); 22 | void toggle_flag(); 23 | void unflag(); 24 | void increment_adjacent_mines(); 25 | bool auto_reveal(); 26 | bool auto_reveal_neighbors(); 27 | char get_display_value(); 28 | void reset(); 29 | }; 30 | ``` 31 | 32 | ### Definition of `Board` 33 | ```cpp 34 | // board.h 35 | 36 | struct board_t { 37 | private: 38 | int width; 39 | int height; 40 | cell_t ***cells; 41 | 42 | public: 43 | board_t(int W, int H); 44 | ~board_t(); 45 | void reset(); 46 | void place_mines(int count); 47 | void place_mine(unsigned int row, unsigned int col); 48 | void reveal_cell(unsigned int row, unsigned int col); 49 | void reveal_all_cells(); 50 | void toggle_flag_cell(unsigned int row, unsigned int col); 51 | void draw(sf::RenderWindow* window, TextureManager *textureManager, int cell_size); 52 | vector > get_adjacent_positions(unsigned int row, unsigned int col); 53 | }; 54 | ``` 55 | -------------------------------------------------------------------------------- /board.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "board.h" 6 | #include "gameover.h" 7 | 8 | using std::pair; 9 | using std::make_pair; 10 | using std::queue; 11 | using std::unordered_set; 12 | 13 | 14 | board_t::board_t(const int W, const int H) { 15 | width = W; 16 | height = H; 17 | cells = new cell_t **[H]; 18 | 19 | for (int i = 0; i < height; ++i) { 20 | cells[i] = new cell_t *[W]; 21 | for (int j = 0; j < width; ++j) { 22 | cells[i][j] = new cell_t(); 23 | } 24 | } 25 | } 26 | 27 | board_t::~board_t() { 28 | for (int i = 0; i < height; ++i) { 29 | for (int j = 0; j < width; ++j) { 30 | delete cells[i][j]; 31 | } 32 | delete[] cells[i]; 33 | } 34 | delete[] cells; 35 | } 36 | 37 | 38 | void board_t::reset() { 39 | for (int i = 0; i < height; ++i) { 40 | for (int j = 0; j < width; ++j) { 41 | cells[i][j]->reset(); 42 | } 43 | } 44 | } 45 | 46 | 47 | void board_t::place_mines(const int count) { 48 | int i = 0; 49 | srand(time(nullptr)); 50 | unordered_set positions; 51 | 52 | while (i < count) { 53 | int pos = rand() % (width * height); 54 | if (positions.count(pos) > 0) 55 | continue; 56 | int row = pos / width; 57 | int col = pos % width; 58 | positions.insert(pos); 59 | 60 | place_mine(row, col); 61 | ++i; 62 | } 63 | } 64 | 65 | void board_t::place_mine(unsigned int row, unsigned int col) { 66 | auto cell = cells[row][col]; 67 | cell->place_mine(); 68 | 69 | auto neighbours = get_adjacent_positions(row, col); 70 | for (auto pos : neighbours) { 71 | cells[pos.first][pos.second]->increment_adjacent_mines(); 72 | } 73 | } 74 | 75 | vector> board_t::get_adjacent_positions(unsigned int row, unsigned int col) { 76 | vector> neighbours; 77 | for (int i = -1; i <= 1; i++) { 78 | for (int j = -1; j <= 1; j++) { 79 | if (i == 0 && j == 0) continue; 80 | if (row + i >= 0 and row + i < height and col + j >= 0 and col + j < width) 81 | neighbours.emplace_back(row + i, col + j); 82 | } 83 | } 84 | return neighbours; 85 | } 86 | 87 | 88 | void board_t::reveal_cell(const unsigned int row, const unsigned int col) { 89 | if (row >= height || col >= width) 90 | return; 91 | 92 | auto cell = cells[row][col]; 93 | bool mine_revealed = cell->reveal(); 94 | if (mine_revealed) 95 | throw GameOver(); 96 | 97 | queue> q; 98 | unordered_set visited; 99 | 100 | q.push(make_pair(row, col)); 101 | 102 | while (!q.empty()) { 103 | auto pos = q.front(); 104 | q.pop(); 105 | cell = cells[pos.first][pos.second]; 106 | 107 | if (visited.count(cell) > 0) { 108 | continue; 109 | } 110 | visited.insert(cell); 111 | 112 | if (!cell->auto_reveal()) 113 | continue; 114 | 115 | cell->reveal(); 116 | if (cell->auto_reveal_neighbors()) { 117 | auto neighbors = get_adjacent_positions(pos.first, pos.second); 118 | for (auto neighbor : neighbors) 119 | q.push(neighbor); 120 | } 121 | } 122 | } 123 | 124 | void board_t::reveal_all_cells() { 125 | for (int i = 0; i < height; ++i) { 126 | for (int j = 0; j < width; ++j) { 127 | cells[i][j]->unflag(); 128 | cells[i][j]->reveal(); 129 | } 130 | } 131 | } 132 | 133 | void board_t::toggle_flag_cell(const unsigned int row, const unsigned int col) { 134 | if (row < height && col < width) 135 | cells[row][col]->toggle_flag(); 136 | } 137 | 138 | 139 | void board_t::draw(sf::RenderWindow* window, TextureManager *textureManager, int cell_size) { 140 | window->clear(); 141 | 142 | for (int i = 0; i < height; ++i) { 143 | for (int j = 0; j < width; ++j) { 144 | cell_t* cell = cells[i][j]; 145 | sf::Texture texture = textureManager->retrieve(cell->get_display_value()); 146 | 147 | cell->sprite->setTexture(texture); 148 | cell->sprite->setPosition(i * cell_size, j * cell_size); 149 | 150 | window->draw(*cell->sprite); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /board.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "texture_manager.h" 3 | #include "cell.h" 4 | 5 | using std::vector; 6 | using std::pair; 7 | using std::fstream; 8 | 9 | struct board_t { 10 | private: 11 | int width; 12 | int height; 13 | cell_t ***cells; 14 | 15 | public: 16 | board_t(int W, int H); 17 | 18 | void reset(); 19 | 20 | void place_mines(int count); 21 | 22 | void place_mine(unsigned int row, unsigned int col); 23 | 24 | void reveal_cell(unsigned int row, unsigned int col); 25 | 26 | void reveal_all_cells(); 27 | 28 | void toggle_flag_cell(unsigned int row, unsigned int col); 29 | 30 | void draw(sf::RenderWindow* window, TextureManager *textureManager, int cell_size); 31 | 32 | vector > get_adjacent_positions(unsigned int row, unsigned int col); 33 | 34 | ~board_t(); 35 | }; 36 | -------------------------------------------------------------------------------- /cell.cpp: -------------------------------------------------------------------------------- 1 | #include "cell.h" 2 | 3 | cell_t::cell_t() { 4 | sprite = new sf::Sprite(); 5 | } 6 | 7 | cell_t::~cell_t() { 8 | delete sprite; 9 | } 10 | 11 | void cell_t::place_mine() { 12 | is_mine = true; 13 | } 14 | 15 | bool cell_t::reveal() { 16 | if (is_flagged) return false; 17 | 18 | is_revealed = true; 19 | return is_mine; 20 | } 21 | 22 | void cell_t::toggle_flag() { 23 | is_flagged = !is_flagged; 24 | } 25 | 26 | void cell_t::unflag() { 27 | is_flagged = false; 28 | } 29 | 30 | void cell_t::increment_adjacent_mines() { 31 | ++adjacent_mines; 32 | } 33 | 34 | bool cell_t::auto_reveal() { 35 | return !is_mine; 36 | } 37 | 38 | bool cell_t::auto_reveal_neighbors() { 39 | return !is_mine && adjacent_mines == 0; 40 | } 41 | 42 | char cell_t::get_display_value() { 43 | if (is_flagged) return 'f'; 44 | if (!is_revealed) return 'n'; 45 | if (is_mine) return 'b'; 46 | 47 | return (char) (adjacent_mines + '0'); 48 | } 49 | 50 | void cell_t::reset() { 51 | adjacent_mines = 0; 52 | is_flagged = false; 53 | is_revealed = false; 54 | is_mine = false; 55 | } 56 | -------------------------------------------------------------------------------- /cell.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using std::string; 5 | 6 | 7 | struct cell_t { 8 | private: 9 | bool is_mine = false; 10 | bool is_flagged = false; 11 | bool is_revealed = false; 12 | int adjacent_mines = 0; 13 | 14 | public: 15 | cell_t(); 16 | 17 | ~cell_t(); 18 | 19 | void place_mine(); 20 | 21 | bool reveal(); 22 | 23 | void toggle_flag(); 24 | 25 | void unflag(); 26 | 27 | void increment_adjacent_mines(); 28 | 29 | bool auto_reveal(); 30 | 31 | bool auto_reveal_neighbors(); 32 | 33 | char get_display_value(); 34 | 35 | void reset(); 36 | 37 | sf::Sprite *sprite; 38 | }; 39 | -------------------------------------------------------------------------------- /gameover.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | struct GameOver : public std::exception { 5 | const char *what() const throw() { 6 | return "Game Is Over"; 7 | } 8 | }; -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "texture_manager.h" 4 | #include "board.h" 5 | #include "gameover.h" 6 | 7 | #define BOARD_WIDTH 12 8 | #define BOARD_HEIGHT 12 9 | #define MINES 20 10 | #define CELL_SIZE 50 11 | #define KEY_R 17 12 | 13 | using std::cin; 14 | using std::cout; 15 | using std::endl; 16 | 17 | 18 | void show_manual(){ 19 | cout << "Manual:" << endl; 20 | cout << "\tReveal Cell\t - Mouse Left Click" << endl; 21 | cout << "\tFlag Cell\t - Mouse Right Click" << endl; 22 | cout << "\tReset Board\t - Press 'R' on Keyboard" << endl; 23 | } 24 | 25 | int main() { 26 | show_manual(); 27 | board_t board(BOARD_WIDTH, BOARD_HEIGHT); 28 | board.place_mines(MINES); 29 | 30 | bool isGameOver = false; 31 | int width = BOARD_WIDTH * CELL_SIZE; 32 | int height = BOARD_HEIGHT * CELL_SIZE; 33 | auto style = sf::Style::Titlebar | sf::Style::Close; 34 | sf::RenderWindow window(sf::VideoMode(width, height), "MineSweeper Game", style); 35 | TextureManager textureManager; 36 | textureManager.loadAll(); 37 | 38 | board.draw(&window, &textureManager, CELL_SIZE); 39 | window.display(); 40 | 41 | while (window.isOpen()) 42 | { 43 | sf::Event event; 44 | while (window.pollEvent(event)) { 45 | if (event.type == sf::Event::Closed) 46 | { 47 | window.close(); 48 | break; 49 | } 50 | else if (event.type == sf::Event::KeyPressed) 51 | { 52 | if (event.key.code == KEY_R){ 53 | isGameOver = false; 54 | board.reset(); 55 | board.place_mines(MINES); 56 | } 57 | } 58 | else if (event.type == sf::Event::MouseButtonPressed && !isGameOver) 59 | { 60 | int i = event.mouseButton.x / CELL_SIZE; 61 | int j = event.mouseButton.y / CELL_SIZE; 62 | 63 | if (event.mouseButton.button == sf::Mouse::Left) 64 | { 65 | try 66 | { 67 | board.reveal_cell(i, j); 68 | } 69 | catch(GameOver& e) 70 | { 71 | isGameOver = true; 72 | board.reveal_all_cells(); 73 | } 74 | } 75 | else 76 | { 77 | board.toggle_flag_cell(i, j); 78 | } 79 | } 80 | 81 | board.draw(&window, &textureManager, CELL_SIZE); 82 | window.display(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /res/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/0.png -------------------------------------------------------------------------------- /res/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/1.png -------------------------------------------------------------------------------- /res/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/2.png -------------------------------------------------------------------------------- /res/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/3.png -------------------------------------------------------------------------------- /res/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/4.png -------------------------------------------------------------------------------- /res/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/5.png -------------------------------------------------------------------------------- /res/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/6.png -------------------------------------------------------------------------------- /res/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/7.png -------------------------------------------------------------------------------- /res/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/8.png -------------------------------------------------------------------------------- /res/b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/b.png -------------------------------------------------------------------------------- /res/f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/f.png -------------------------------------------------------------------------------- /res/n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimjohn/minesweeper/76eb414eff9eb968df5447268961da64a66dc0bd/res/n.png -------------------------------------------------------------------------------- /texture_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "texture_manager.h" 2 | 3 | TextureManager::TextureManager() = default; 4 | 5 | sf::Texture TextureManager::retrieve(const char key) 6 | { 7 | if (data.find(key) != data.end()) 8 | { 9 | return data[key]; 10 | } 11 | return sf::Texture(); 12 | } 13 | 14 | bool TextureManager::load(const string& path, const char key) 15 | { 16 | if (data.find(key) != data.end()) 17 | return true; 18 | else 19 | { 20 | sf::Texture texture; 21 | if (texture.loadFromFile(path)) 22 | { 23 | data[key] = texture; 24 | return true; 25 | } 26 | return false; 27 | } 28 | } 29 | 30 | void TextureManager::loadAll() 31 | { 32 | load("../res/0.png", '0'); 33 | load("../res/1.png", '1'); 34 | load("../res/2.png", '2'); 35 | load("../res/3.png", '3'); 36 | load("../res/4.png", '4'); 37 | load("../res/5.png", '5'); 38 | load("../res/6.png", '6'); 39 | load("../res/7.png", '7'); 40 | load("../res/8.png", '8'); 41 | load("../res/b.png", 'b'); 42 | load("../res/f.png", 'f'); 43 | load("../res/n.png", 'n'); 44 | } 45 | 46 | TextureManager::~TextureManager() = default; 47 | -------------------------------------------------------------------------------- /texture_manager.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using std::string; 5 | 6 | #ifndef TEXTURE_MANAGER_H 7 | #define TEXTURE_MANAGER_H 8 | 9 | class TextureManager 10 | { 11 | private: 12 | typedef std::map TextureMap; 13 | TextureMap data; 14 | 15 | public: 16 | TextureManager(); 17 | sf::Texture retrieve(char key); 18 | bool load(const string& path, char key); 19 | void loadAll(); 20 | ~TextureManager(); 21 | }; 22 | 23 | #endif //TEXTURE_MANAGER_H 24 | --------------------------------------------------------------------------------