#include "minefield.hpp" 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++) { std::shared_ptr cell = std::make_shared(); m_cells.push_back(cell); } } void MineField::timerTick() { auto start = std::chrono::system_clock::now(); while((m_exploded == false) && (m_gameWon == false)) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); 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(); } //I should use std::duration to represent the time instead } void MineField::initBombs(int x, int y) { int remainingMines = m_totalMines; int startPos = x + y * m_rows; srand(time(NULL)); //initialize rand() while(remainingMines > 0) { int position = rand() % (m_cols * m_rows); if(isBomb(position % m_cols, position / m_cols) || position == startPos) { continue; } m_cells.at(position)->isBomb = true; --remainingMines; } //init the timer to zero and start the timer thread m_time = 0; timerThread = std::thread(&MineField::timerTick, this); 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; gameOverSignal.emit(); return false; } setOpenCell(x, y); 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; } } } } 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); } } } } } } bool MineField::isOpened(int x, int y) { return m_cells.at(x + y * m_rows)->isCleared; } bool MineField::isFlagged(int x, int y) { return m_cells.at(x + y * m_rows)->isFlagged; } 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) { computeBombsNearby(x, y); } return m_cells.at(x + y * m_rows)->bombsNearby; } void MineField::setOpenCell(int x, int y) { m_cells.at(x + y * m_rows)->isCleared = true; openCellSignal.emit(x, y); if((++m_openCells == (m_cols * m_rows - m_totalMines)) && (m_exploded == false)) { m_gameWon = true; gameWonSignal.emit(); } } 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) { m_cells.at(x + y * m_rows)->isFlagged = true; --m_remainingFlags; remainingFlagsSignal.emit(m_remainingFlags); return true; } return false; }