From b5f75a29b72abda9d08682b4ad2b95b062295502 Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Thu, 10 Apr 2025 17:15:14 +0100 Subject: [PATCH] cleaning up --- meson.build | 4 +- src/main.cpp | 30 ------ src/minefield.cpp | 172 ++++++++++++++++------------------ src/minefield.hpp | 72 +++++++------- src/window.cpp | 232 +++++++++++++++++++++++++--------------------- src/window.hpp | 23 +++-- 6 files changed, 252 insertions(+), 281 deletions(-) delete mode 100644 src/main.cpp diff --git a/meson.build b/meson.build index e0f43b7..860be85 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,8 @@ res = gnome.compile_resources( ) deps = dependency(['gtkmm-4.0', 'sigc++-3.0']) -src = ['src/window.cpp', 'src/window.hpp', 'src/minefield.hpp', 'src/minefield.cpp', res] +src = ['src/window.cpp', 'src/window.hpp', 'src/minefield.hpp', 'src/minefield.cpp', + 'src/timer.hpp', 'src/timer.cpp', res] + exe = executable('minesweeper', src, dependencies : deps, install : true) diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index c13ef29..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "MineField.hpp" -#include - -int main() { - - MineField field(160,160, 100); - - srand(time(NULL)); - - int x = rand() % 160; - int y = rand() % 160; - - field.initBombs(x, y); - - printf("Opened cell: %i %i\n", x, y); - printf("Neighboor bombs: %i\n", field.bombsNearby(x,y)); - - while(!field.isGameOver()) { - x = rand() % 160; - y = rand() % 160; - - if(field.clearCell(x, y)) { - printf("Opened cell: %i %i\n", x, y); - printf("Neighboor bombs: %i\n", field.bombsNearby(x,y)); - } - else { - printf("Bomb found in cell: %i %i\n", x, y); - } - } -} diff --git a/src/minefield.cpp b/src/minefield.cpp index 14725bb..8668655 100644 --- a/src/minefield.cpp +++ b/src/minefield.cpp @@ -1,166 +1,158 @@ #include "minefield.hpp" #include -MineField::MineField(int cols, int rows, int mines): m_rows(rows), - m_cols(cols), - m_totalMines(mines), - m_remainingFlags(mines), - m_openCells(0), - m_exploded(false) { - for(int i=0; i< m_cols*m_rows; i++) { +MineField::MineField(int cols, int rows, int mines) : m_rows(rows), + m_cols(cols), + m_totalMines(mines), + m_remainingFlags(mines), + m_openCells(0), + m_gameOver(false) +{ + for (int i = 0; i < m_cols * m_rows; i++) + { std::shared_ptr cell = std::make_shared(); m_cells.push_back(cell); } } -MineField::~MineField() { - if(m_timerRunning) { - stopTimer(); - } +MineField::~MineField() +{ + m_cells.clear(); } -void MineField::timerTick() { - - auto start = std::chrono::system_clock::now(); - - while(m_timerRunning) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - auto now = std::chrono::system_clock::now(); - const auto duration = now - start; - std::chrono::milliseconds ms = std::chrono::duration_cast(duration); - m_time += ms.count(); - timerSignal.emit(m_time); - start = std::chrono::system_clock::now(); - } -} - -void MineField::startTimer() { - m_time = 0; - m_timerRunning = true; - m_timerThread = std::thread(&MineField::timerTick, this); -} - -void MineField::stopTimer() { - m_timerRunning = false; - - if(m_timerThread.joinable()) { - m_timerThread.join(); - } -} - -void MineField::initBombs(int x, int y) { +void MineField::initBombs(int x, int y) +{ int remainingMines = m_totalMines; int startPos = x + y * m_rows; - srand(time(NULL)); //initialize rand() + srand(time(NULL)); // initialize rand() - while(remainingMines > 0) { + while (remainingMines > 0) + { int position = rand() % (m_cols * m_rows); - if(isBomb(position % m_cols, position / m_cols) || position == startPos) { + if (isBomb(position % m_cols, position / m_cols) || position == startPos) + { continue; } m_cells.at(position)->isBomb = true; --remainingMines; } - - startTimer(); - - //init the timer to zero and start the timer thread - //timerThread.detach(); //not sure if this is okay (better to call join() when I set the condition to stop the thread) } -bool MineField::openCell(int x, int y) { - if(isBomb(x, y)) { - m_exploded = true; +bool MineField::openCell(int x, int y) +{ + if (isBomb(x, y)) + { + m_gameOver = true; gameOverSignal.emit(); - stopTimer(); + // stopTimer(); return false; } setOpenCell(x, y); - if (bombsNearby(x, y) == 0) { + if (bombsNearby(x, y) == 0) + { openNeighboorhood(x, y); } return true; } -void MineField::computeBombsNearby(int x, int y) { - int total = 0; - //compute bombs in neighboorhood - for(int i=-1; i<2; i++) { - for(int j=-1; j<2; j++) { - if(x+i >= 0 && x+i < m_cols && y+j >= 0 && y+j < m_rows) { - if(isBomb(x+i, y+j)){ - ++total; - } +void MineField::computeBombsNearby(int x, int y) +{ + int total = 0; + // compute bombs in neighboorhood + for (int i = -1; i < 2; i++) + { + for (int j = -1; j < 2; j++) + { + if (x + i >= 0 && x + i < m_cols && y + j >= 0 && y + j < m_rows) + { + if (isBomb(x + i, y + j)) + { + ++total; + } } } } m_cells.at(x + y * m_rows)->bombsNearby = total; } -void MineField::openNeighboorhood(int x, int y) { - //compute bombs in neighboorhood - for(int i=-1; i<2; i++) { - for(int j=-1; j<2; j++) { - if(x+i >= 0 && x+i < m_cols && y+j >= 0 && y+j < m_rows) { - if((isOpened(x+i, y+j) == false) && (isBomb(x+i, y+j) == false)){ - setOpenCell((x+i), (y+j)); - if(bombsNearby(x+i, y+j) == 0) { - openNeighboorhood(x+i, y+j); - } - } +void MineField::openNeighboorhood(int x, int y) +{ + // compute bombs in neighboorhood + for (int i = -1; i < 2; i++) + { + for (int j = -1; j < 2; j++) + { + if (x + i >= 0 && x + i < m_cols && y + j >= 0 && y + j < m_rows) + { + if ((isOpened(x + i, y + j) == false) && (isBomb(x + i, y + j) == false)) + { + setOpenCell((x + i), (y + j)); + if (bombsNearby(x + i, y + j) == 0) + { + openNeighboorhood(x + i, y + j); + } + } } } } } - -bool MineField::isOpened(int x, int y) { + +bool MineField::isOpened(int x, int y) +{ return m_cells.at(x + y * m_rows)->isCleared; } -bool MineField::isFlagged(int x, int y) { +bool MineField::isFlagged(int x, int y) +{ return m_cells.at(x + y * m_rows)->isFlagged; } -bool MineField::isBomb(int x, int y) { +bool MineField::isBomb(int x, int y) +{ return m_cells.at(x + y * m_rows)->isBomb; } -int MineField::bombsNearby(int x, int y) { - if(m_cells.at(x + y * m_rows)->bombsNearby == -1) { +int MineField::bombsNearby(int x, int y) +{ + if (m_cells.at(x + y * m_rows)->bombsNearby == -1) + { computeBombsNearby(x, y); - } + } return m_cells.at(x + y * m_rows)->bombsNearby; } -void MineField::setOpenCell(int x, int y) { +void MineField::setOpenCell(int x, int y) +{ m_cells.at(x + y * m_rows)->isCleared = true; openCellSignal.emit(x, y); ++m_openCells; checkGameWon(); } -void MineField::checkGameWon() { - if((m_openCells == (m_cols * m_rows - m_totalMines)) && (m_exploded == false) && (m_remainingFlags == 0)) { +void MineField::checkGameWon() +{ + if ((m_openCells == (m_cols * m_rows - m_totalMines)) && (m_gameOver == false) && (m_remainingFlags == 0)) + { m_gameWon = true; - stopTimer(); gameWonSignal.emit(); } - std::cout << "Open cells: " << m_openCells << "\n" << "Remaining Flags: " << m_remainingFlags << "\n"; - } -bool MineField::toggleFlag(int x, int y) { - if(m_cells.at(x + y * m_rows)->isFlagged == true) { +bool MineField::toggleFlag(int x, int y) +{ + if (m_cells.at(x + y * m_rows)->isFlagged == true) + { m_cells.at(x + y * m_rows)->isFlagged = false; ++m_remainingFlags; remainingFlagsSignal.emit(m_remainingFlags); return true; } - else if(m_remainingFlags > 0) { + else if (m_remainingFlags > 0) + { m_cells.at(x + y * m_rows)->isFlagged = true; --m_remainingFlags; remainingFlagsSignal.emit(m_remainingFlags); diff --git a/src/minefield.hpp b/src/minefield.hpp index 45d8384..60f5d06 100644 --- a/src/minefield.hpp +++ b/src/minefield.hpp @@ -1,43 +1,35 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include #include -#include -#include +#include +#include -struct Cell { - bool isFlagged; - bool isCleared; - bool isBomb; - int bombsNearby; - Cell(): isFlagged(false), isCleared(false), isBomb(false), bombsNearby(-1) {}; -}; +class MineField +{ + + struct Cell + { + bool isFlagged = false; + bool isCleared = false; + bool isBomb = false; + int bombsNearby = -1; + }; -class MineField { std::vector> m_cells; - int m_rows; - int m_cols; - int m_totalMines; - int m_remainingFlags; - int m_openCells; - bool m_exploded; - bool m_gameWon; - size_t m_time; - std::atomic_bool m_timerRunning; - std::thread m_timerThread; + int m_rows; + int m_cols; + int m_totalMines; + int m_remainingFlags; + int m_openCells; + bool m_gameOver; + bool m_gameWon; + void computeBombsNearby(int x, int y); void openNeighboorhood(int x, int y); void setOpenCell(int x, int y); void checkGameWon(); - void timerTick(); - void startTimer(); - void stopTimer(); - + public: MineField(int cols, int rows, int mines); ~MineField(); @@ -47,18 +39,16 @@ public: bool isOpened(int x, int y); bool openCell(int x, int y); int bombsNearby(int x, int y); - bool isGameOver() {return m_exploded; }; - int getCols() {return m_cols; }; - int getRows() {return m_rows; }; + bool isGameOver() { return m_gameOver; }; + int getCols() { return m_cols; }; + int getRows() { return m_rows; }; bool toggleFlag(int x, int y); - int getRemainingFlags() {return m_remainingFlags; }; - size_t getCurrentTime() {return m_time; }; - int getTotalMines() {return m_totalMines; }; + int getRemainingFlags() { return m_remainingFlags; }; + int getTotalMines() { return m_totalMines; }; void startNewGame(int cols, int rows, int mines); - sigc::signal openCellSignal; - sigc::signal remainingFlagsSignal; - sigc::signal gameWonSignal; - sigc::signal gameOverSignal; - sigc::signal timerSignal; + sigc::signal openCellSignal; + sigc::signal remainingFlagsSignal; + sigc::signal gameWonSignal; + sigc::signal gameOverSignal; }; diff --git a/src/window.cpp b/src/window.cpp index 7ab40e8..7660f6a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3,7 +3,6 @@ #include "sigc++/adaptors/bind.h" #include "sigc++/functors/mem_fun.h" - //} // void MainWindow::ApplyStyles() { // // Load and apply the CSS file @@ -12,21 +11,25 @@ // Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER); // } -void MainWindow::OnCellRightClick(int n_press, double n_x, double n_y, int index) { +void MainWindow::OnCellRightClick(int n_press, double n_x, double n_y, int index) +{ (void)n_press, (void)n_x, (void)n_y; int x = index % field.getCols(); int y = index / field.getCols(); int pos = x + y * field.getRows(); - if(field.isOpened(x, y) == false) { + if (field.isOpened(x, y) == false) + { field.toggleFlag(x, y); - if(field.isFlagged(x, y)) { + if (field.isFlagged(x, y)) + { auto imgflag = Gtk::make_managed(); imgflag->set(m_textureFlag); buttons.at(pos)->set_child(*imgflag); buttons.at(pos)->set_active(true); } - else { + else + { buttons.at(pos)->unset_child(); buttons.at(pos)->queue_draw(); buttons.at(pos)->set_active(false); @@ -34,7 +37,8 @@ void MainWindow::OnCellRightClick(int n_press, double n_x, double n_y, int index } } -void MainWindow::updateFlagsLabel(int flags) { +void MainWindow::updateFlagsLabel(int flags) +{ Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", flags); flagLabel.set_label(msg); } @@ -57,78 +61,89 @@ void MainWindow::updateFlagsLabel(int flags) { // clockConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::UpdateClockLabel), 100); // } +void MainWindow::OnCellClick(int x, int y) +{ + if (newGame) + { + field.initBombs(x, y); + newGame = false; + } - - -void MainWindow::OnCellClick(int x, int y) { - if (newGame) { - field.initBombs(x, y); - newGame = false; + if (field.isFlagged(x, y)) + { + buttons.at(x + y * field.getRows())->set_active(true); + } + else + { + field.openCell(x, y); + if (field.isBomb(x, y)) + { + openBombs(); } + } +} - if(field.isFlagged(x, y)) { - buttons.at(x + y * field.getRows())->set_active(true); - } - else { - field.openCell(x, y); - if(field.isBomb(x, y)) { - openBombs(); - } - } -} - -void MainWindow::openBombs() { - for(int i=0; i < field.getCols() * field.getRows(); i++) { +void MainWindow::openBombs() +{ + for (int i = 0; i < field.getCols() * field.getRows(); i++) + { int x = i % field.getCols(); int y = i / field.getCols(); buttons.at(i)->set_sensitive(false); - if(field.isBomb(x, y)) { - if(field.isFlagged(x, y)) { - auto imgFlagBomb = std::make_shared(); - imgFlagBomb->set(m_textureFlagBomb); - buttons.at(i)->set_child(*imgFlagBomb); + if (field.isBomb(x, y)) + { + if (field.isFlagged(x, y)) + { + auto imgFlagBomb = std::make_shared(); + imgFlagBomb->set(m_textureFlagBomb); + buttons.at(i)->set_child(*imgFlagBomb); } - else { - auto imgBomb = std::make_shared(); - imgBomb->set(m_textureBomb); - buttons.at(i)->set_child(*imgBomb); + else + { + auto imgBomb = std::make_shared(); + imgBomb->set(m_textureBomb); + buttons.at(i)->set_child(*imgBomb); } buttons.at(i)->set_active(true); } } } -void MainWindow::updateCell(int x, int y) { +void MainWindow::updateCell(int x, int y) +{ int pos = x + y * field.getRows(); - if(field.isOpened(x, y)) { - if (field.bombsNearby(x, y) > 0) { - switch(field.bombsNearby(x, y)) { + if (field.isOpened(x, y)) + { + if (field.bombsNearby(x, y) > 0) + { + switch (field.bombsNearby(x, y)) + { case 1: - buttons.at(pos)->get_style_context()->add_class("label-1"); - break; + buttons.at(pos)->get_style_context()->add_class("label-1"); + break; case 2: - buttons.at(pos)->get_style_context()->add_class("label-2"); - break; + buttons.at(pos)->get_style_context()->add_class("label-2"); + break; case 3: - buttons.at(pos)->get_style_context()->add_class("label-3"); - break; + buttons.at(pos)->get_style_context()->add_class("label-3"); + break; case 4: - buttons.at(pos)->get_style_context()->add_class("label-4"); - break; + buttons.at(pos)->get_style_context()->add_class("label-4"); + break; case 5: - buttons.at(pos)->get_style_context()->add_class("label-5"); - break; + buttons.at(pos)->get_style_context()->add_class("label-5"); + break; case 6: - buttons.at(pos)->get_style_context()->add_class("label-6"); - break; + buttons.at(pos)->get_style_context()->add_class("label-6"); + break; case 7: - buttons.at(pos)->get_style_context()->add_class("label-7"); - break; + buttons.at(pos)->get_style_context()->add_class("label-7"); + break; case 8: - buttons.at(pos)->get_style_context()->add_class("label-8"); - break; + buttons.at(pos)->get_style_context()->add_class("label-8"); + break; } buttons.at(pos)->set_label(Glib::ustring::format(field.bombsNearby(x, y))); } @@ -150,7 +165,6 @@ void MainWindow::updateCell(int x, int y) { // } // } - // bool MainWindow::AllCellsOpened() // { // for(int i=0; iload_from_data( ".label-1 { font-weight: bold; font-size: 1.5em; color: Blue; }\ @@ -244,11 +259,12 @@ MainWindow::MainWindow() .label-6 { font-weight: bold; font-size: 1.5em; color: Salmon; }\ .label-7 { font-weight: bold; font-size: 1.5em; color: Turquoise; }\ .label-8 { font-weight: bold; font-size: 1.5em; color: Magenta; }"); - + auto display = Gdk::Display::get_default(); Gtk::StyleContext::add_provider_for_display(display, css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER); - - for (int i = 0; i < field.getCols() * field.getRows(); i++) { + + for (int i = 0; i < field.getCols() * field.getRows(); i++) + { auto button = std::make_shared(); button->set_size_request(50, 40); button->set_sensitive(true); @@ -257,55 +273,57 @@ MainWindow::MainWindow() int y = i / field.getRows(); button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::OnCellClick), x, y)); - //button->get_style_context()->add_class("fixed-button"); + // button->get_style_context()->add_class("fixed-button"); auto gesture = Gtk::GestureClick::create(); gesture->set_button(3); - gesture->signal_released().connect(sigc::bind(sigc::mem_fun(*this, \ - &MainWindow::OnCellRightClick), i)); + gesture->signal_released().connect(sigc::bind(sigc::mem_fun(*this, + &MainWindow::OnCellRightClick), + i)); button->add_controller(gesture); buttons.push_back(button); - + grid.attach(*button, x, y); } field.openCellSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateCell))); field.remainingFlagsSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateFlagsLabel))); field.gameOverSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::gameOver))); - //newGameButton.set_label("New"); - //newGameButton.add_css_class("suggested-action"); - //newGameButton.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::OnNewButtonClick)); + // newGameButton.set_label("New"); + // newGameButton.add_css_class("suggested-action"); + // newGameButton.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::OnNewButtonClick)); - //optionButton.set_icon_name("open-menu"); + // optionButton.set_icon_name("open-menu"); - field.timerSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::handleClockSig))); + // field.timerSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::handleClockSig))); m_clockDispatch.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateClockLabel))); - //field.timerSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateClockLabel))); - //if (clockSignalConn.connected()) clockSignalConn.disconnect(); - //elapsedTime = 0; - //clockSignalConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::updateClockLabel), 100); - //} - //create the minefield - //field = new MineField(COLS, MINES); + // field.timerSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateClockLabel))); + // if (clockSignalConn.connected()) clockSignalConn.disconnect(); + // elapsedTime = 0; + // clockSignalConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::updateClockLabel), 100); + // } + // create the minefield + // field = new MineField(COLS, MINES); - //bar.pack_start(newGameButton); - //bar.pack_end(optionButton); - - //grid.set_row_homogeneous(false); - //grid.set_column_homogeneous(false); + // bar.pack_start(newGameButton); + // bar.pack_end(optionButton); + + // grid.set_row_homogeneous(false); + // grid.set_column_homogeneous(false); grid.set_margin(10); - //grid.set_vexpand(true); - //grid.set_hexpand(true); - // grid.set_fill(false); + // grid.set_vexpand(true); + // grid.set_hexpand(true); + // grid.set_fill(false); boxV.append(grid); - + this->set_titlebar(bar); this->set_child(boxV); } -int main(int argc, char **argv) { - auto app = Gtk::Application::create("eu.minesweeper"); +int main(int argc, char **argv) +{ + auto app = Gtk::Application::create("eu.bernardomagri.minesweeper"); return app->make_window_and_run(argc, argv); } diff --git a/src/window.hpp b/src/window.hpp index 6634384..259e384 100644 --- a/src/window.hpp +++ b/src/window.hpp @@ -12,10 +12,9 @@ #define PROJECT_NAME "minesweeper" - class MainWindow : public Gtk::Window { - Gtk::Box boxV{Gtk::Orientation::VERTICAL}; + Gtk::Box boxV{Gtk::Orientation::VERTICAL}; Gtk::Box boxH{Gtk::Orientation::HORIZONTAL}; std::vector> buttons; Gtk::Grid grid; @@ -24,7 +23,7 @@ class MainWindow : public Gtk::Window Gtk::Button optionButton; Gtk::Label flagLabel; Gtk::Label clockLabel; - MineField field {16, 16, 1}; + MineField field{16, 16, 1}; int m_elapsedTime; bool newGame; std::shared_ptr m_textureBomb; @@ -39,16 +38,16 @@ class MainWindow : public Gtk::Window void gameOver(); sigc::connection clockSignalConn; Glib::Dispatcher m_clockDispatch; -// void OpenNearCells(int index); -// void Explode();xo -// bool AllCellsOpened(); + // void OpenNearCells(int index); + // void Explode();xo + // bool AllCellsOpened(); - public: -MainWindow(); -// void OnNewButtonClick(); +public: + MainWindow(); + // void OnNewButtonClick(); void OnCellClick(int x, int y); void OnCellRightClick(int n_press, double n_x, double n_y, int index); -// void ShowGameWonAnimation(); -// void ApplyStyles(); -// bool UpdateClockLabel(); + // void ShowGameWonAnimation(); + // void ApplyStyles(); + // bool UpdateClockLabel(); };