├── 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