Skip to content

Instantly share code, notes, and snippets.

@miniriley2012
Last active August 13, 2019 19:07
Show Gist options
  • Save miniriley2012/58a8c45efba799915b239367b9fc0102 to your computer and use it in GitHub Desktop.
Save miniriley2012/58a8c45efba799915b239367b9fc0102 to your computer and use it in GitHub Desktop.
Minesweeper in C++ with ncurses

Minesweeper

This is a C++ implementation of Minesweeper using ncurses

Controls

q - quit
f - toggle flagging
click - test for mine
cmake_minimum_required(VERSION 3.15)
project(minesweeper)
set(CMAKE_CXX_STANDARD 17)
add_executable(minesweeper main.cpp)
target_link_libraries(minesweeper ncurses)
#include <ncurses.h>
#include <sstream>
#include <iostream>
#include <random>
const char empty = ' ';
const char safe = '0';
const char mine = '*';
const char flagged = 'F';
struct Cell {
char character;
bool shown, flagged;
};
class Board {
std::vector<std::vector<Cell>> board;
bool first_click = true;
bool is_mine(int x, int y) {
return x >= 0 && y >= 0 && x < board.size() && y < board[x].size() && board[x][y].character == mine;
}
bool flood_fill(int x, int y) {
if (x >= board.size() || y >= board[x].size()) {
return false;
} else if (board[x][y].character >= safe && board[x][y].character <= safe + 8) {
return false;
} else if (board[x][y].character == mine) {
return true;
}
board[x][y].shown = true;
board[x][y].character = '0';
char mines = 0;
if (flood_fill(x, y - 1)) mines++;
if (flood_fill(x, y + 1)) mines++;
if (flood_fill(x - 1, y)) mines++;
if (flood_fill(x + 1, y)) mines++;
if (is_mine(x - 1, y - 1)) mines++;
if (is_mine(x + 1, y - 1)) mines++;
if (is_mine(x - 1, y + 1)) mines++;
if (is_mine(x + 1, y + 1)) mines++;
board[x][y].character += mines;
return false;
}
public:
bool flagging = false;
Board(int rows, int columns) {
std::mt19937 rng{std::random_device()()};
std::uniform_int_distribution<int> dist(0, 3);
board.assign(rows, {});
std::generate(board.begin(), board.end(), [&rng, &dist, &columns]() {
auto v = std::vector<Cell>(columns);
std::generate(v.begin(), v.end(), [&rng, &dist]() { return dist(rng) > 1 ? Cell{empty} : Cell{mine}; });
return v;
});
}
void reveal() {
for (auto &row : board) {
for (auto &col : row) {
col.shown = true;
}
}
}
void print() {
std::stringstream header;
header << "Mode: " << (flagging ? "Flagging" : "Searching") << '\n';
addstr(header.str().c_str());
addch(ACS_ULCORNER);
bool tee = false;
for (int i = 0; i < static_cast<int>(board.size()) * 4 - 1; i++) {
addch(tee ? ACS_TTEE : ACS_HLINE);
tee = !tee;
}
addch(ACS_URCORNER);
addch('\n');
for (int i = 0; i < static_cast<int>(board.size()); i++) {
addch(ACS_VLINE);
for (auto col : board[i]) {
addch(col.shown ? col.character : col.flagged ? flagged : empty);
addch(ACS_VLINE);
}
addch('\n');
if (i != static_cast<int>(board.size()) - 1) {
addch(ACS_LTEE);
for (int j = 0; j < static_cast<int>(board[i].size()); j++) {
addch(ACS_HLINE);
addch(j != static_cast<int>(board[i].size()) - 1 ? ACS_PLUS : ACS_RTEE);
}
} else {
addch(ACS_LLCORNER);
for (int j = 0; j < static_cast<int>(board[i].size()); j++) {
addch(ACS_HLINE);
if (j != static_cast<int>(board[i].size()) - 1) {
addch(ACS_BTEE);
}
}
addch(ACS_LRCORNER);
}
addch('\n');
}
}
bool play(int x, int y) {
// Make sure clicking on the header doesn't do anything
if (y == 0) {
return false;
}
int r = (y - 1) / 2;
int c = (x - 1) / 2;
if (r >= board.size() || c >= board[r].size()) {
return false;
}
if (flagging) {
board[r][c].flagged = !board[r][c].flagged;
return false;
}
if (first_click) {
board[r][c].character = empty;
first_click = false;
}
return flood_fill(r, c);
}
bool won() {
for (const auto &row : board) {
for (const auto &col : row) {
if (!col.shown && col.character != mine) {
return false;
}
}
}
return true;
}
};
int main() {
initscr();
clear();
curs_set(0);
cbreak();
noecho();
keypad(stdscr, true);
mousemask(ALL_MOUSE_EVENTS, nullptr);
MEVENT event;
Board board(11, 22);
bool playing = true;
bool lost = false;
bool won = false;
do {
erase();
board.print();
refresh();
int c = getch();
switch (c) {
case 'q':
playing = false;
break;
case 'f':
board.flagging = !board.flagging;
break;
case KEY_MOUSE:
if (getmouse(&event) == OK && !lost) {
if (event.bstate & BUTTON1_CLICKED) {
if (board.play(event.x, event.y)) {
lost = true;
board.reveal();
} else if (board.won()) {
won = true;
playing = false;
}
}
}
break;
default:
break;
}
} while (playing);
endwin();
if (won) {
std::cout << "You Won!" << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment