From a5baaf26eef10522c13ba016af01cb16bbcb0d7b Mon Sep 17 00:00:00 2001 From: DrShahinstein <1taylanozturk@protonmail.com> Date: Sat, 20 Jul 2024 15:41:47 +0300 Subject: [PATCH] First commit --- .gitignore | 2 + CMakeLists.txt | 31 +++++ assets/bomb.png | Bin 0 -> 13835 bytes assets/flag.png | Bin 0 -> 10563 bytes assets/misplacedbomb.png | Bin 0 -> 13520 bytes assets/redbomb.png | Bin 0 -> 11234 bytes include/cell.hpp | 66 +++++++++++ include/game.hpp | 54 +++++++++ include/gamebar.hpp | 30 +++++ include/mainmenu.hpp | 42 +++++++ include/utils.hpp | 7 ++ include/workingdir.h | 10 ++ run | 19 +++ src/cell.cpp | 74 ++++++++++++ src/game.cpp | 249 +++++++++++++++++++++++++++++++++++++++ src/gamebar.cpp | 40 +++++++ src/main.cpp | 80 +++++++++++++ src/mainmenu.cpp | 64 ++++++++++ src/utils.cpp | 23 ++++ 19 files changed, 791 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 assets/bomb.png create mode 100644 assets/flag.png create mode 100644 assets/misplacedbomb.png create mode 100644 assets/redbomb.png create mode 100644 include/cell.hpp create mode 100644 include/game.hpp create mode 100644 include/gamebar.hpp create mode 100644 include/mainmenu.hpp create mode 100644 include/utils.hpp create mode 100644 include/workingdir.h create mode 100755 run create mode 100644 src/cell.cpp create mode 100644 src/game.cpp create mode 100644 src/gamebar.cpp create mode 100644 src/main.cpp create mode 100644 src/mainmenu.cpp create mode 100644 src/utils.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7194ea7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache +build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..53c722c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.14) + +project(minesweeper LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(Qt6 REQUIRED COMPONENTS Widgets) + +option(RELEASE_BUILD "Enable Release build" OFF) +if(RELEASE_BUILD) + set(CMAKE_BUILD_TYPE Release) +endif() + +file(GLOB SOURCES "src/*.cpp") +file(GLOB HEADERS "include/*.hpp" "include/*.h") + +qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) + +if (WIN32) + add_executable(minesweeper WIN32 ${SOURCES} ${MOC_SOURCES}) +else() + add_executable(minesweeper ${SOURCES} ${MOC_SOURCES}) +endif() + +target_link_libraries(minesweeper PRIVATE Qt6::Widgets) +target_include_directories(minesweeper PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_compile_definitions(minesweeper PRIVATE + $<$:RELEASE_BUILD> +) diff --git a/assets/bomb.png b/assets/bomb.png new file mode 100644 index 0000000000000000000000000000000000000000..360dbab742b8bc45f4108370fd452a6b08862971 GIT binary patch literal 13835 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 literal 0 HcmV?d00001 diff --git a/assets/flag.png b/assets/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..55c886786c4e67e86e6c0a7b35fc384222752fae GIT binary patch literal 10563 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 kef(A20sNU1G;RFBvC(<|2VRWzr2qf` literal 0 HcmV?d00001 diff --git a/assets/redbomb.png b/assets/redbomb.png new file mode 100644 index 0000000000000000000000000000000000000000..c7e75a820e947f4ab0aa3586747a75fddda2e890 GIT binary patch literal 11234 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 L_U$g*ML79i-WSGH literal 0 HcmV?d00001 diff --git a/include/cell.hpp b/include/cell.hpp new file mode 100644 index 0000000..ae45574 --- /dev/null +++ b/include/cell.hpp @@ -0,0 +1,66 @@ +#ifndef CELL_HPP +#define CELL_HPP + +#include "workingdir.h" +#include +#include +#include +#include + +#define STRAIGHTBOMB_IMG_PATH WDIR "assets/bomb.png" +#define MISPLACED_BOMB_IMG_PATH WDIR "assets/misplacedbomb.png" +#define REDBOMB_IMG_PATH WDIR "assets/redbomb.png" +#define FLAG_IMG_PATH WDIR "assets/flag.png" + +#define BLACK_RGB QColor(20, 20, 20) +#define BLUE_RGB QColor(0, 0, 205) +#define GREEN_RGB QColor(0, 128, 0) +#define LIGHTRED_RGB QColor(237, 28, 36) +#define DARKBLUE_RGB QColor(25, 80, 232) +#define DARKRED_RGB QColor(216, 0, 0) +#define AQUA_RGB QColor(7, 147, 131) +#define PURPLE_RGB QColor(149, 3, 168) +#define YELLOWISH_RGB QColor(206, 140, 8) +#define AMETHYST_RGB QColor(191, 50, 242) + +enum FontColor { + BLACK, + BLUE, + GREEN, + LIGHTRED, + DARKBLUE, + DARKRED, + AQUA, + PURPLE, + YELLOWISH, + AMETHYST, +}; + +class CellBtn : public QPushButton { + Q_OBJECT + +public: + explicit CellBtn(QWidget *parent = nullptr); + + void set_font(FontColor font_color = BLACK); + void set_icon(QString icon_path); + void remove_icon(); + void disable(); + void flag(); + void unflag(); + bool is_flagged(); + +private: + QFont cell_font; + QPixmap pixmap; + bool flagged; + +private slots: + void mousePressEvent(QMouseEvent *e); + +signals: + void leftClicked(); + void rightClicked(); +}; + +#endif // CELL_HPP diff --git a/include/game.hpp b/include/game.hpp new file mode 100644 index 0000000..9250112 --- /dev/null +++ b/include/game.hpp @@ -0,0 +1,54 @@ +#ifndef GAME_HPP +#define GAME_HPP + +#define WINDOW_TITLE "Minesweeper" +#define CELL_SIZE 30 + +#include "cell.hpp" +#include "gamebar.hpp" +#include +#include +#include +#include +#include + +class GameWindow : public QDialog { + Q_OBJECT + +public: + explicit GameWindow(int rows, int cols, int mines); + +private: + int rows; + int cols; + int mines; + bool is_first_reveal; + GameBar *gamebar; + + QGridLayout *grid_layout; + QVBoxLayout *main_layout; + QVector> grid_buttons; + QVector> mine_map; + QSet> opening_cells; + QTimer *timer; + int elapsed_time; + + bool get_first_reveal(); + void set_first_reveal(bool boolean); + void create_board(); + void make_opening(int start_row, int start_col); + void set_mines(); + void reveal_cell(int row, int col); + void put_flag(int row, int col); + int count_adjacent_mines(int row, int col); + +private slots: + void update_timer(); + void reset_game(); + +signals: + void gameReset(); + void gameClosed(); +}; + +#endif // GAME_HPP diff --git a/include/gamebar.hpp b/include/gamebar.hpp new file mode 100644 index 0000000..e040f35 --- /dev/null +++ b/include/gamebar.hpp @@ -0,0 +1,30 @@ +#ifndef GAMEBAR_HPP +#define GAMEBAR_HPP + +#include +#include +#include +#include + +class GameBar : public QWidget { + Q_OBJECT + +public: + explicit GameBar(int mines, QWidget *parent = nullptr); + + void update_timer(int seconds); + void update_mine_count(int mines); + +private: + QLCDNumber *timer_display; + QLCDNumber *mine_count_display; + QPushButton *reset_button; + +signals: + void reset_game(); + +private slots: + void handle_reset_button(); +}; + +#endif // GAMEBAR_HPP diff --git a/include/mainmenu.hpp b/include/mainmenu.hpp new file mode 100644 index 0000000..a9d5d92 --- /dev/null +++ b/include/mainmenu.hpp @@ -0,0 +1,42 @@ +#ifndef MAINMENU_HPP +#define MAINMENU_HPP + +#include "game.hpp" +#include +#include +#include +#include +#include + +#define MENU_WINDOW_TITLE "Select Game Variant" +#define MENU_FIXED_WIDTH 900 +#define MENU_FIXED_HEIGHT 600 + +enum GameVariants { + _16x16_40, + _16x16_50, + _16x16_60, + _20x20_75, + _20x20_90, + _20x20_120, + _24x24_140, + _24x24_160, + _24x24_180, +}; + +class MainMenu : public QDialog { + Q_OBJECT + +public: + explicit MainMenu(QWidget *parent = nullptr); + GameVariants get_variant(); + +private: + void setup_layout(); + void select_variant(); + + GameVariants variant; + GameWindow *game_window; +}; + +#endif // MAINMENU_HPP diff --git a/include/utils.hpp b/include/utils.hpp new file mode 100644 index 0000000..e1f6749 --- /dev/null +++ b/include/utils.hpp @@ -0,0 +1,7 @@ +#ifndef UTILS_HPP + +namespace utils { +int rng(int start, int end); +} + +#endif // UTILS_HPP diff --git a/include/workingdir.h b/include/workingdir.h new file mode 100644 index 0000000..d068fa2 --- /dev/null +++ b/include/workingdir.h @@ -0,0 +1,10 @@ +#ifndef WORKINGDIR +#define WORKINGDIR + +#ifdef RELEASE_BUILD +#define WDIR "./" +#else +#define WDIR "../" +#endif + +#endif // WORKINGDIR diff --git a/run b/run new file mode 100755 index 0000000..a049ca6 --- /dev/null +++ b/run @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +if [[ "$1" == "--release-build" ]]; then + cmake_options="-DRELEASE_BUILD=ON" +else + cmake_options="" +fi + +mkdir -p "build" +cd "build" + +if [[ "$OS" == "Windows_NT" ]]; then + cmake -G "MinGW Makefiles" $cmake_options .. +else + cmake $cmake_options .. +fi + +make +./minesweeper diff --git a/src/cell.cpp b/src/cell.cpp new file mode 100644 index 0000000..11b4c41 --- /dev/null +++ b/src/cell.cpp @@ -0,0 +1,74 @@ +#include "cell.hpp" + +CellBtn::CellBtn(QWidget *parent) : QPushButton(parent) { + setFocusPolicy(Qt::NoFocus); +}; + +void CellBtn::set_font(FontColor font_color) { + cell_font.setPointSize(13); + cell_font.setBold(true); + QPalette palette = this->palette(); + + switch (font_color) { + case BLUE: + palette.setColor(QPalette::ButtonText, BLUE_RGB); + break; + case GREEN: + palette.setColor(QPalette::ButtonText, GREEN_RGB); + break; + case LIGHTRED: + palette.setColor(QPalette::ButtonText, LIGHTRED_RGB); + break; + case DARKBLUE: + palette.setColor(QPalette::ButtonText, DARKBLUE_RGB); + break; + case DARKRED: + palette.setColor(QPalette::ButtonText, DARKRED_RGB); + break; + case AQUA: + palette.setColor(QPalette::ButtonText, AQUA_RGB); + break; + case PURPLE: + palette.setColor(QPalette::ButtonText, PURPLE_RGB); + break; + case YELLOWISH: + palette.setColor(QPalette::ButtonText, YELLOWISH_RGB); + break; + case AMETHYST: + palette.setColor(QPalette::ButtonText, AMETHYST_RGB); + break; + default: + palette.setColor(QPalette::ButtonText, Qt::black); + cell_font.setBold(false); + cell_font.setPointSize(11); + break; + } + + setFont(cell_font); + setPalette(palette); +} + +void CellBtn::remove_icon() { setIcon(QIcon()); } +void CellBtn::set_icon(QString icon_path) { + pixmap = QPixmap(icon_path); + setIcon(pixmap); + setIconSize(size()); +} + +void CellBtn::disable() { + disconnect(this, &CellBtn::leftClicked, nullptr, nullptr); + disconnect(this, &CellBtn::rightClicked, nullptr, nullptr); +} + +bool CellBtn::is_flagged() { return flagged; } +void CellBtn::flag() { flagged = true; } +void CellBtn::unflag() { flagged = false; } + +void CellBtn::mousePressEvent(QMouseEvent *event) { + if (event->button() == Qt::RightButton) { + emit rightClicked(); + } else if (event->button() == Qt::LeftButton) { + emit leftClicked(); + } + QPushButton::mouseReleaseEvent(event); +} diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..6f9e290 --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,249 @@ +#include "game.hpp" +#include "cell.hpp" +#include "gamebar.hpp" +#include "utils.hpp" +#include +#include +#include +#include +#include + +GameWindow::GameWindow(int rows, int cols, int mines) + : rows(rows), cols(cols), mines(mines) { + + elapsed_time = 0; + is_first_reveal = true; + main_layout = new QVBoxLayout(this); + gamebar = new GameBar(mines, this); + grid_layout = new QGridLayout(); + timer = new QTimer(this); + + mine_map.resize(rows, QVector(cols, false)); + + create_board(); + resize(cols * CELL_SIZE, rows * CELL_SIZE); + setWindowTitle(WINDOW_TITLE); + + connect(timer, &QTimer::timeout, this, &GameWindow::update_timer); + connect(gamebar, &GameBar::reset_game, this, &GameWindow::reset_game); + + main_layout->addWidget(gamebar); + main_layout->addLayout(grid_layout); + setLayout(main_layout); +} + +bool GameWindow::get_first_reveal() { return is_first_reveal; } +void GameWindow::set_first_reveal(bool boolean) { is_first_reveal = boolean; } + +void GameWindow::update_timer() { + elapsed_time++; + gamebar->update_timer(elapsed_time); +} + +void GameWindow::reset_game() { + emit gameClosed(); + close(); +} + +void GameWindow::create_board() { + grid_buttons.resize(rows, QVector(cols, nullptr)); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + grid_buttons[i][j] = new CellBtn(this); + grid_buttons[i][j]->setMinimumSize(30, 30); + grid_buttons[i][j]->set_font(); + grid_layout->setSpacing(1); + grid_layout->setContentsMargins(0, 0, 0, 0); + grid_layout->addWidget(grid_buttons[i][j], i, j); + + connect(grid_buttons[i][j], &CellBtn::leftClicked, + [this, i, j]() { reveal_cell(i, j); }); + + connect(grid_buttons[i][j], &CellBtn::rightClicked, + [this, i, j]() { put_flag(i, j); }); + } + } +} + +void GameWindow::make_opening(int start_row, int start_col) { + opening_cells.clear(); + + int total_cells = rows * cols; + int cells_to_open = total_cells * (utils::rng(12, 24) / 100.0); + + QSet> opened_cells; + QQueue> queue; + + queue.enqueue(qMakePair(start_row, start_col)); + opened_cells.insert(qMakePair(start_row, start_col)); + + while (!queue.isEmpty() && opened_cells.size() < cells_to_open) { + auto cell = queue.dequeue(); + int row = cell.first; + int col = cell.second; + + reveal_cell(row, col); + + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + int new_row = row + i; + int new_col = col + j; + if (new_row >= 0 && new_row < rows && new_col >= 0 && new_col < cols) { + QPair new_cell = qMakePair(new_row, new_col); + if (!opened_cells.contains(new_cell)) { + opened_cells.insert(new_cell); + queue.enqueue(new_cell); + } + } + } + } + } + + opening_cells = opened_cells; + + for (const auto &cell : opened_cells) { + mine_map[cell.first][cell.second] = false; + } +} + +void GameWindow::set_mines() { + QVector> possible_positions; + + // collect all possible positions excluding the opening cells + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + QPair cell = qMakePair(i, j); + if (!opening_cells.contains(cell)) { + possible_positions.append(cell); + grid_buttons[cell.first][cell.second]->unflag(); + } + } + } + + // shuffle the possible positions + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(possible_positions.begin(), possible_positions.end(), g); + + // place mines in the first 'mines' positions + for (int i = 0; i < mines; ++i) { + int row = possible_positions[i].first; + int col = possible_positions[i].second; + mine_map[row][col] = true; + } + + // reveal opening cells + for (const auto &cell : opening_cells) { + reveal_cell(cell.first, cell.second); + } +} + +void GameWindow::reveal_cell(int row, int col) { + if (grid_buttons[row][col]->text() != "") + return; + + if (get_first_reveal()) { + set_first_reveal(false); + timer->start(1000); + + make_opening(row, col); + set_mines(); + + return; + } + + if (mine_map[row][col] == true) { + timer->stop(); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + grid_buttons[i][j]->disable(); + if (mine_map[i][j] == true && !grid_buttons[i][j]->is_flagged()) { + grid_buttons[i][j]->set_icon(STRAIGHTBOMB_IMG_PATH); + } else if (mine_map[i][j] == true && grid_buttons[i][j]->is_flagged()) { + grid_buttons[i][j]->set_icon(MISPLACED_BOMB_IMG_PATH); + } + } + } + grid_buttons[row][col]->set_icon(REDBOMB_IMG_PATH); + } else { + int adjacent_mines = count_adjacent_mines(row, col); + + switch (adjacent_mines) { + case 0: + grid_buttons[row][col]->set_font(FontColor::BLACK); + break; + case 1: + grid_buttons[row][col]->set_font(FontColor::BLUE); + break; + case 2: + grid_buttons[row][col]->set_font(FontColor::GREEN); + break; + case 3: + grid_buttons[row][col]->set_font(FontColor::LIGHTRED); + break; + case 4: + grid_buttons[row][col]->set_font(FontColor::DARKBLUE); + break; + case 5: + grid_buttons[row][col]->set_font(FontColor::DARKRED); + break; + case 6: + grid_buttons[row][col]->set_font(FontColor::AQUA); + break; + case 7: + grid_buttons[row][col]->set_font(FontColor::PURPLE); + break; + case 8: + grid_buttons[row][col]->set_font(FontColor::YELLOWISH); + break; + case 9: + grid_buttons[row][col]->set_font(FontColor::AMETHYST); + break; + } + + grid_buttons[row][col]->setText(QString::number(adjacent_mines)); + grid_buttons[row][col]->disable(); + } +} + +void GameWindow::put_flag(int row, int col) { + if (is_first_reveal) + return; + + if (grid_buttons[row][col]->is_flagged()) { + grid_buttons[row][col]->remove_icon(); + grid_buttons[row][col]->unflag(); + connect(grid_buttons[row][col], &CellBtn::leftClicked, this, + [this, row, col]() { reveal_cell(row, col); }); + } else { + grid_buttons[row][col]->set_icon(FLAG_IMG_PATH); + grid_buttons[row][col]->flag(); + disconnect(grid_buttons[row][col], &CellBtn::leftClicked, nullptr, nullptr); + } +} + +int GameWindow::count_adjacent_mines(int row, int col) { + int count = 0; + + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; j++) { + int new_row = row + i; + int new_col = col + j; + if (new_row >= 0 && new_row < rows && new_col >= 0 && new_col < cols) { + if (mine_map[new_row][new_col]) { + count++; + } + } + } + } + + return count; +} + +/* + +'mine_map[row][col] == true' is easier to read + +*/ diff --git a/src/gamebar.cpp b/src/gamebar.cpp new file mode 100644 index 0000000..3b93f49 --- /dev/null +++ b/src/gamebar.cpp @@ -0,0 +1,40 @@ +#include "gamebar.hpp" + +GameBar::GameBar(int mines, QWidget *parent) : QWidget(parent) { + timer_display = new QLCDNumber(this); + mine_count_display = new QLCDNumber(this); + reset_button = new QPushButton(this); + + timer_display->setDigitCount(3); + timer_display->setMaximumHeight(55); + mine_count_display->setDigitCount(3); + mine_count_display->setMaximumHeight(55); + + update_timer(0); + update_mine_count(mines); + + reset_button->setText("Quit"); + reset_button->setFixedSize(50, 50); + + connect(reset_button, &QPushButton::clicked, this, + &GameBar::handle_reset_button); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(timer_display); + layout->addWidget(reset_button); + layout->addWidget(mine_count_display); + + setLayout(layout); +} + +void GameBar::update_timer(int seconds) { + timer_display->display(seconds); +} + +void GameBar::update_mine_count(int mines) { + mine_count_display->display(mines); +} + +void GameBar::handle_reset_button() { + emit reset_game(); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ef67a45 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,80 @@ +#include "game.hpp" +#include "mainmenu.hpp" +#include + +int main(int argc, char **argv) { + QApplication app(argc, argv); + + while (true) { + MainMenu menu; + + if (menu.exec() == QDialog::Accepted) { + GameVariants variant = menu.get_variant(); + int rows, cols, mines; + + switch (variant) { + case _16x16_40: + rows = 16; + cols = 16; + mines = 40; + break; + case _16x16_50: + rows = 16; + cols = 16; + mines = 50; + break; + case _16x16_60: + rows = 16; + cols = 16; + mines = 60; + break; + case _20x20_75: + rows = 20; + cols = 20; + mines = 75; + break; + case _20x20_90: + rows = 20; + cols = 20; + mines = 90; + break; + case _20x20_120: + rows = 20; + cols = 20; + mines = 120; + break; + case _24x24_140: + rows = 24; + cols = 24; + mines = 140; + break; + case _24x24_160: + rows = 24; + cols = 24; + mines = 160; + break; + case _24x24_180: + rows = 24; + cols = 24; + mines = 180; + break; + default: + rows = 16; + cols = 16; + mines = 40; + break; + } + + GameWindow game(rows, cols, mines); + + QObject::connect(&game, &GameWindow::gameClosed, + [&app, &menu]() { menu.show(); }); + + game.exec(); + } else { + break; + } + } + + return app.exec(); +} diff --git a/src/mainmenu.cpp b/src/mainmenu.cpp new file mode 100644 index 0000000..96fe60b --- /dev/null +++ b/src/mainmenu.cpp @@ -0,0 +1,64 @@ +#include "mainmenu.hpp" + +MainMenu::MainMenu(QWidget *parent) : QDialog(parent) { + setup_layout(); +} + +void MainMenu::setup_layout() { + QVBoxLayout *main_layout = new QVBoxLayout(this); + QGridLayout *grid_layout = new QGridLayout(); + + struct button_info { + QString size_label; + QString mine_label; + }; + + QVector button_infos = { + {"16x16", "40 Mines"}, {"16x16", "50 Mines"}, {"16x16", "60 Mines"}, + {"20x20", "75 Mines"}, {"20x20", "90 Mines"}, {"20x20", "120 Mines"}, + {"24x24", "140 Mines"}, {"24x24", "160 Mines"}, {"24x24", "180 Mines"}}; + + QFont font; + font.setPointSize(16); + + for (int i = 0; i < button_infos.size(); ++i) { + QPushButton *button = new QPushButton(this); + + button->setFocusPolicy(Qt::NoFocus); + button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + button->setMinimumSize(200, 100); + button->setCursor(QCursor(Qt::OpenHandCursor)); + + QVBoxLayout *button_layout = new QVBoxLayout(button); + QLabel *size_label = new QLabel(button_infos[i].size_label, button); + QLabel *mine_label = new QLabel(button_infos[i].mine_label, button); + + size_label->setAlignment(Qt::AlignCenter); + mine_label->setAlignment(Qt::AlignCenter); + size_label->setFont(font); + mine_label->setFont(font); + button_layout->addWidget(size_label); + button_layout->addWidget(mine_label); + + connect(button, &QPushButton::clicked, this, [this, i]() { + variant = static_cast(i); + select_variant(); + }); + + grid_layout->addWidget(button, i / 3, i % 3); + } + + main_layout->addLayout(grid_layout); + + setLayout(main_layout); + setWindowTitle(MENU_WINDOW_TITLE); + setFixedSize(MENU_FIXED_WIDTH, MENU_FIXED_HEIGHT); +} + +GameVariants MainMenu::get_variant() { + return variant; +} + +void MainMenu::select_variant() { + accept(); +} diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..f733348 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,23 @@ +#include "utils.hpp" +#include +#include + +#define INVALID_ARG_MSG \ + "Invalid range: 'start' must be less than or equal to 'end'" + +namespace utils { + +int rng(int start, int end) { + if (start > end) + throw std::invalid_argument(INVALID_ARG_MSG); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis_int(start, end); + + int random_int = dis_int(gen); + + return random_int; +} + +} // namespace utils