/**
* Copyright (C) 2010, LGPL
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* @author Matfyz, fyzmat@gmail.com
*/
package mines;
import com.trolltech.qt.QThread;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.trolltech.qt.core.QObject;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import mines.MinefieldContainer.Coord;
import mines.MinesUtils.MineSweeperData;
import mines.SettingsDialog.SettingsData;
import mines.gui.generated.Ui_Mines;
import mines.gui.generated.Ui_MinesControlPanel;
/**
* Main class of the program. Contains the application logic and connects
* the GUI parts. Works as a singleton.
*/
public class MineSweeperLogic extends QObject{
/**
* Class for measurring the speed of solver. It runs the solver on a randomly
* generated minefield with size MEASURING_MINEFIELD_SIZE and MEASURING_MINES
* mines for MEASURINGS_COUNT times. One zero field is revealed, each time before
* the solver is runned. Solver marks determinable mined and numbered fields.
* When the solver marks the numbered field, then the field is immedeately revealed.
* This is done repeateadly until some covered field can be marked. The measurrer
* logs how long it takes to solve one minefield. The minefields for the MEASURINGS_SAVED
* worst times are saved.
*/
private class Measurrer extends QObject {
/**
* Container for minefield data and solver running time.
*/
private class MeasurrerData implements Comparable<MeasurrerData>{
/**
* The minefield content - real uncovered fields and, what is seen
* by the user.
*/
MineSweeperData mSweeperData;
/**
* Time, until the solver has marked all the determinable fields.
*/
long mTime;
/**
* Used to sort measurrer data according to the time of solver running.
* @param o MeasurrerData object to compare.
* @return 1 if given time is bigger, 0 if they equals and -1 otherwise.
*/
public int compareTo(MeasurrerData o) {
MeasurrerData compared = (MeasurrerData) o;
if(this.mTime > compared.mTime) {
return -1;
}
if(this.mTime < compared.mTime) {
return 1;
}
return 0;
}
}
/** Number of generated minefields and solver runs. */
static final int MEASURINGS_COUNT = 242;
/** Size of generated minefields */
static final int MEASURING_MINEFIELD_SIZE = 30;
/** Number of mines, which is layed on generated minefields */
static final int MEASURING_MINES = 140;
/** Number of the saved minefields, which had the worst solver running time */
static final int MEASURINGS_SAVED = 20;
/** Filename for the log of solver running times */
static final String OUTPUT_FILE_PATH = MineSolver.MINESOLVER_DATA_DIR + "/measuringOut.txt";
/** Filename prefix for the saved minefields */
static final String BAD_DATA_FILENAME = "/bad_data";
/** For counting measurements */
int mMeasureCounter = 0;
/** For log of solver running times */
PrintStream mOutputFile = null;
/** For remembering the start of last measurring; UNIX timestamp */
long mLastMeasureStartTime = 0;
/** All the generated minefields with appropriate solver running times */
List<MeasurrerData> mMeasuredData = new ArrayList<MeasurrerData>();
/** Last measurred minefield with appropriate time */
MeasurrerData mLastMeasuredData = null;
/**
* Tries to open the file with measurements results. Continues if failes.
*/
public Measurrer() {
try {
mOutputFile = new PrintStream(OUTPUT_FILE_PATH);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
* Generates new minefield and reveals one zero field. The revealed zero
* field is the middle of all the zeroes fields.
*/
void prepareNewMinefieldForMeasuring() {
// set the minefield parameters for measuring
mMinefieldSize = MEASURING_MINEFIELD_SIZE;
mMines = MEASURING_MINES;
// we will need some zero in the generated minefield, so continue,
// until there is at least one
List<Coord> zeroCoords = null;
while(true) {
// generate new minefield
restart();
zeroCoords = mRealMinefield.getAllFieldsIndexes(GUI_FIELD.ZERO_NUMBER_FLAG);
if(zeroCoords.size() != 0) {
break;
}
}
// we will take the zero hopefully somewhere in the midle of the minefield
int takenZeroIndex = zeroCoords.size() / 2;
revealField(zeroCoords.get(takenZeroIndex));
}
/**
* Runs next iteration of measurement.
*/
void startNextMeasuring(){
// if some measurement has already been made, store its solver running time
if(mMeasureCounter != 0) {
long measuringLength = System.currentTimeMillis() - mLastMeasureStartTime;
mOutputFile.println("" + mMeasureCounter + "\t" + measuringLength);
mLastMeasuredData.mTime = measuringLength;
mMeasuredData.add(mLastMeasuredData);
}
// stop when the measurements are all done
if(mMeasureCounter >= MEASURINGS_COUNT) {
mMeasureCounter = 0;
processMeasuringResults();
return;
}
mMeasureCounter++;
prepareNewMinefieldForMeasuring();
runSolverMeasuring();
}
/**
* Stores the defined number of the minefields with the worst solver
* running times.
*/
void processMeasuringResults() {
Collections.sort(mMeasuredData);
for (int i = 0; i < MEASURINGS_SAVED; i++) {
MeasurrerData data = mMeasuredData.get(i);
String dataFilename = MineSolver.MINESOLVER_DATA_DIR + BAD_DATA_FILENAME +
"_" + i + "_" + data.mTime + ".msd";
MinesUtils.saveMinefieldToFileSafely(data.mSweeperData, dataFilename);
}
}
/**
* Creates a new thread runner for solver and runs it.
*/
void runSolverMeasuring() {
// prepare the thread data
mSolverThread.setMode(MineSolverThread.MODE.SOLVE_AND_REVEAL);
mSolverThread.setContent(mRealMinefield, mViewedMinefield, mSolver);
// create new thread runner
QThread threadRunner = createSolverThreadRunner();
threadRunner.finished.connect(this, "startNextMeasuring()");
// remember the initial conditions
mLastMeasureStartTime = System.currentTimeMillis();
MinesUtils.MineSweeperData data = new MinesUtils.MineSweeperData();
data.mRealContent = mRealMinefield;
data.mViewedContent = mViewedMinefield;
mLastMeasuredData = new MeasurrerData();
mLastMeasuredData.mSweeperData = data;
// run the measurement
threadRunner.start();
}
}
/** The singleton object */
static MineSweeperLogic mTheLogic = null;
/** The singleton constructor
* @return The singleton of mine shg\weeper logic.
*/
public static MineSweeperLogic getTheMineSweeperLogic() {
if (mTheLogic == null) {
mTheLogic = new MineSweeperLogic();
}
return mTheLogic;
}
/** Maximum allowed size of minefield */
public static final int MAXIMUM_MINEFIELD_SIZE = 100;
/** Default size of the minefield */
public static final int DEFAULT_MINEFIELD_SIZE = 10;
/** Default mines count on the minefield */
public static final int DEFAULT_MINES = 10;
/** Current size of minefield */
int mMinefieldSize = DEFAULT_MINEFIELD_SIZE;
/** Current count of mines on the minefield */
int mMines = DEFAULT_MINES;
/** Current count of flags, which are marked by the user */
int mFlaggedMines = 0;
/** Flag, if the user uncovered a mined field */
boolean mMineFired = false;
/** Flag, if the coordinates on the main window are visible. */
boolean mCoordinatesVisible = true;
/** The solver object. There is only one instance used for whole application.
* The content of solver is changed with changes in minefield.
*/
final MineSolver mSolver = new MineSolver();
/** The solver thread object. It is used to make the solver compute in a
* different thread. This is the only one instance for the whole program.
* This thread is runned by QThread. This object is also used for synchronization
* between the main and the solver thread.
*/
final MineSolverThread mSolverThread = new MineSolverThread();
/** Object for making measurements of the solver speed. */
final Measurrer mMeasurrer = new Measurrer();
/** Container of current minefield (uncovered fields). It contains number and mine fields. */
MinefieldContainer<GUI_FIELD> mRealMinefield = new MinefieldContainer<GUI_FIELD>(mMinefieldSize);
/** Container of current minefield (fields, which the user see). It can containe covered fields, numbers
* fields, mines and a fired mine (if the user fire some mine), flags marked by the user and
* fields, which are marked by the solver (must be mine, cannot be mine). */
MinefieldContainer<GUI_FIELD> mViewedMinefield = new MinefieldContainer<GUI_FIELD>(mMinefieldSize);
/** Signals, that the whole minefield content, which is seen by user, should be
* painted from scratch. Gives the minefield data structure to be painted. */
public Signal1<MinefieldContainer<GUI_FIELD>> mMinefieldPaintSignal = new Signal1<MinefieldContainer<GUI_FIELD>>();
/** Signals, that some parts of minefield should be repaint.
* Gives the minefield data structure to be painted. */
public Signal1<MinefieldContainer<GUI_FIELD>> mMinefieldRepaintSignal = new Signal1<MinefieldContainer<GUI_FIELD>>();
/** Signals, that user has flagged/unflagged some field. Gives the
* count of mines, which are remaining to be flagged.
*/
public Signal1<Integer> mFlaggedFieldsCountChangeSignal = new Signal1<Integer>();
/** Signals, that coordinates should (not) be visible. Gives true, if the
* coordinates should be visible */
public Signal1<Boolean> mCoordinatesVisibilityChangeSignal = new Signal1<Boolean>();
/** Signals, that the solver is runnning */
public Signal1<Boolean> mSolverActivitySignal = new Signal1<Boolean>();
/** Signals, that the solver is not runnning */
public Signal1<Boolean> mSolverNoActivitySignal = new Signal1<Boolean>();
/** Signals, that the game has been restarted */
public Signal0 mRestartedSignal = new Signal0();
/** Signals, that the user has uncovered some mined field */
public Signal0 mMineFiredSignal = new Signal0();
/** Signals, that appearance of some field should be changed. Gives
the coordinates of that field and the new appearance. */
public Signal2<Coord, GUI_FIELD> mFieldValueChangedSignal =
new Signal2<Coord, GUI_FIELD>();
/** Signals, that the settings dialog to show itself. Gives the current
settings data. */
public Signal1<SettingsData> mSettingShowSignal = new Signal1<SettingsData>();
/** Signals, that the mines editor to show itself. Gives the current
minefield data. */
public Signal1<MineSweeperData> mEditorShowSignal = new Signal1<MineSweeperData>();
/** Inicialization of the mine sweeper logic singleton. It only connects
solver threads to show the results of the solver. */
private MineSweeperLogic() {
mSolverThread.mFieldMarkMineSignal.connect(this, "markSolvedMineField(mines.MinefieldContainer$Coord)");
mSolverThread.mFieldMarkClearSignal.connect(this, "markSolvedClearField(mines.MinefieldContainer$Coord)");
mSolverThread.mFieldRevealedSignal.connect(this, "revealSolvedField(mines.MinefieldContainer$Coord)");
}
/**
* Connects the logic of main window with all the GUI parts. All the interaction
* between GUI and logic is done throught signals.
* @param mainWindow Main form of the application.
* @param settingsDialog Instance of the settings dialog.
* @param editor Instance of the editor window.
* @param about Instance of the about dialog.
*/
public void connectWithGUI(Mines mainWindow, SettingsDialog settingsDialog,
MinesEditor editor, AboutDialog about) {
Ui_Mines GUI = mainWindow.ui;
mMinefieldPaintSignal.connect(GUI.mMinefieldView, "createMinefield(MinefieldContainer)");
mMinefieldRepaintSignal.connect(GUI.mMinefieldView, "repaintMinefield(MinefieldContainer)");
GUI.mMinefieldView.connectToButtonsLeftClick(this, "onMinebuttonLeftClicked(MinefieldContainer$Coord)");
GUI.mMinefieldView.connectToButtonsRightClick(this, "onMinebuttonRightClicked(MinefieldContainer$Coord)");
GUI.mMinefieldView.connectToButtonsMouseOver(GUI.mCoordinatesPanel, "showCoordinates(MinefieldContainer$Coord)");
mFieldValueChangedSignal.connect(GUI.mMinefieldView,
"onButtonChanged(MinefieldContainer$Coord, GUI_FIELD)");
Ui_MinesControlPanel controlPanelGUI = GUI.mControlPanel.ui;
mRestartedSignal.connect(controlPanelGUI.mAgainButton, "gameRestarted()");
mMineFiredSignal.connect(controlPanelGUI.mAgainButton, "mineFired()");
controlPanelGUI.mAgainButton.clicked.connect(this, "restart()");
GUI.mEditorMenu.triggered.connect(this, "runEditor()");
GUI.mSettingsMenu.triggered.connect(this, "runSettingsDialog()");
mFlaggedFieldsCountChangeSignal.connect(controlPanelGUI.mMinesCountLabel, "setMinesCount(int)");
mCoordinatesVisibilityChangeSignal.connect(GUI.mCoordinatesPanel, "setVisibility(boolean)");
GUI.mActionShow_debug_data_viewer.triggered.connect(this, "openDebugDataViewer()");
GUI.mActionStart_meassuring.triggered.connect(mMeasurrer, "startNextMeasuring()");
GUI.mActionSolve.triggered.connect(this, "runSolver()");
GUI.mAction_Abort_solver.triggered.connect(this, "abortSolver()");
GUI.mAction_About.triggered.connect(about, "show()");
mSolverNoActivitySignal.connect(GUI.mControlPanel.ui.mBusyIndicator, "onSolverRun(boolean)");
mSolverNoActivitySignal.connect(GUI.mMinefieldView, "setEnabled(boolean)");
mSolverNoActivitySignal.connect(GUI.mControlPanel.ui.mAgainButton, "setEnabled(boolean)");
mSolverNoActivitySignal.connect(GUI.mActionSolve, "setEnabled(boolean)");
mSolverActivitySignal.connect(GUI.mAction_Abort_solver, "setEnabled(boolean)");
mSolverNoActivitySignal.connect(GUI.mEditorMenu, "setEnabled(boolean)");
mSolverNoActivitySignal.connect(GUI.mSettingsMenu, "setEnabled(boolean)");
mSolverNoActivitySignal.connect(GUI.mActionStart_meassuring, "setEnabled(boolean)");
mSettingShowSignal.connect(settingsDialog, "runDialog(SettingsDialog$SettingsData)");
settingsDialog.mAcceptedSignal.connect(this, "newSettingsAccepted(SettingsDialog$SettingsData)");
mEditorShowSignal.connect(editor, "runDialog(MinesUtils$MineSweeperData)");
editor.mAcceptedSignal.connect(this, "editorAccepted(MinesUtils$MineSweeperData)");
}
/**
* Creates and open viewer for debug data.
*/
void openDebugDataViewer() {
MinefieldDebugDataViewer viewer = new MinefieldDebugDataViewer(null);
viewer.show();
}
/**
* Signal handler for solver start event. It sends appropriate signals
* for GUI parts.
*/
void onSolverThreadStart() {
mSolverActivitySignal.emit(true);
mSolverNoActivitySignal.emit(false);
}
/**
* Signal handler for solver finish event. It sends appropriate signals
* for GUI parts.
*/
void onSolverThreadFinish() {
mSolverActivitySignal.emit(false);
mSolverNoActivitySignal.emit(true);
}
/**
* This creates new thread runner for solver thread and connects its
* events with sweeper logic.
* @return New thread runner for the solver thread.
*/
QThread createSolverThreadRunner() {
QThread threadRunner = new QThread(mSolverThread);
threadRunner.starting.connect(this, "onSolverThreadStart()");
threadRunner.finished.connect(this, "onSolverThreadFinish()");
return threadRunner;
}
/**
* Signal handler, when the solver finds out, that there has to be a mine on
* some field.
* @param coveredCoord Coordinates of the mined field.
*/
void markSolvedMineField(Coord coveredCoord) {
assert(mRealMinefield.get(coveredCoord).equals(GUI_FIELD.MINE));
assert(mViewedMinefield.get(coveredCoord).equals(GUI_FIELD.COVERED));
mViewedMinefield.set(coveredCoord, GUI_FIELD.MARKED_MINE);
mFieldValueChangedSignal.emit(coveredCoord, GUI_FIELD.MARKED_MINE);
synchronized(mSolverThread) {
mSolverThread.notify();
}
}
/**
* Signal handler, when the solver marks, that there cannot be a mine on some
* field.
* @param coveredCoord Coordinates of the numbered field.
*/
void markSolvedClearField(Coord coveredCoord) {
assert(!mRealMinefield.get(coveredCoord).equals(GUI_FIELD.MINE));
assert(mViewedMinefield.get(coveredCoord).equals(GUI_FIELD.COVERED));
mViewedMinefield.set(coveredCoord, GUI_FIELD.MARKED_CLEAR);
mFieldValueChangedSignal.emit(coveredCoord, GUI_FIELD.MARKED_CLEAR);
synchronized(mSolverThread) {
mSolverThread.notify();
}
}
/**
* Signal handler, when the solver finds out, that there is some number on
* some field.
* @param coveredCoord Coordinates of the numbered field.
*/
void revealSolvedField(Coord coveredCoord) {
revealField(coveredCoord);
synchronized(mSolverThread) {
mSolverThread.notify();
}
}
/**
* Aborts execution of the solver thread. Does not stops the thread directly,
* but set the abort flag and then the thread finishes itself.
*/
void abortSolver() {
synchronized(mSolverThread) {
mSolver.abort();
}
}
/**
* Signal handler, when the user wants the solver to help. Runs the solver
* thread to mark determinable mined and numbered fields.
*/
void runSolver() {
mSolverThread.setMode(MineSolverThread.MODE.SOLVE_ONLY);
mSolverThread.setContent(mRealMinefield, mViewedMinefield, mSolver);
QThread threadRunner = createSolverThreadRunner();
threadRunner.start();
}
/**
* Signal handler, when the user wants to open the settings dialog.
*/
void runSettingsDialog() {
SettingsData data = new SettingsData();
data.mMinefieldSize = mMinefieldSize;
data.mMinesCount = mMines;
data.mCoordinatesVisible = mCoordinatesVisible;
mSettingShowSignal.emit(data);
}
/**
* Signal handler, when the user accepts new settings in the settings dialog.
* It applies new settings and restarts the game.
* @param data Accepted settings.
*/
void newSettingsAccepted(SettingsData data) {
mMinefieldSize = data.mMinefieldSize;
mMines = data.mMinesCount;
mCoordinatesVisible = data.mCoordinatesVisible;
mCoordinatesVisibilityChangeSignal.emit(mCoordinatesVisible);
restart();
}
/**
* Signal handler, when the user wants to open the mines editor.
*/
void runEditor(){
MineSweeperData data = new MineSweeperData();
data.mRealContent = mRealMinefield;
data.mViewedContent = mViewedMinefield;
mEditorShowSignal.emit(data);
}
/**
* Signal handler, when the user closes the editor. It sets the content
* of the game according to current data in the editor.
* @param data Currently edited data.
*/
void editorAccepted(MineSweeperData data) {
setContent(data.mRealContent, data.mViewedContent);
}
/**
* Restarts the game. The minefield is randomly generated and covered. The
* size of minefield and count of mines stays same.
*/
public void restart() {
GenerateNewMinefield(mMinefieldSize, mMines);
}
/**
* Sets the current content of minefield.
* @param real Minefield with uncovered values of fields.
* @param viewed Minefield seen by the user.
*/
void setContent(MinefieldContainer<GUI_FIELD> real, MinefieldContainer<GUI_FIELD> viewed) {
mRealMinefield = real;
mViewedMinefield = viewed;
// set the content of the solver
MinefieldContainer<SOLVER_FIELD> solverData =
MinesUtils.convertGUIToSolverMinefield(mViewedMinefield);
mSolver.setContent(solverData);
// set the number of flags, which remains to mark
int flaggedMines = MinesUtils.countFlaggedFields(mViewedMinefield);
setFlaggedMinesCount(flaggedMines);
mMineFired = false;
// emit appropriate signals
mMinefieldPaintSignal.emit(mViewedMinefield);
mRestartedSignal.emit();
}
/**
* Randomly generates new minefield.
* @param size Size of new minefield.
* @param mines Count of mines on new minefield.
*/
void GenerateNewMinefield(int size, int mines) {
// generate the real content of minefield
MinefieldContainer<GUI_FIELD> realMinefield = new MinefieldContainer<GUI_FIELD>(size);
MinesUtils.layMinesRandomly(realMinefield, mines);
MinesUtils.updateFieldsNumbers(realMinefield);
// generate, what is seen by the user - all minefield covered
MinefieldContainer<GUI_FIELD> viewedMinefield = new MinefieldContainer<GUI_FIELD>(size);
viewedMinefield.initialFill(GUI_FIELD.COVERED);
setContent(realMinefield, viewedMinefield);
}
/**
* Flags/unflags given covered field.
* @param coord Coordinates of covered field, which should be flaged/unflaged.
* @param makeFlag True, if the field should be flagged.
*/
void changeFieldFlag(Coord coord, boolean makeFlag) {
GUI_FIELD type = mViewedMinefield.get(coord);
if (makeFlag) {
assert(type.equals(GUI_FIELD.COVERED));
mViewedMinefield.set(coord, GUI_FIELD.FLAG);
mFieldValueChangedSignal.emit(coord, GUI_FIELD.FLAG);
setFlaggedMinesCount(mFlaggedMines + 1);
return;
}
assert(type.equals(GUI_FIELD.FLAG));
mViewedMinefield.set(coord, GUI_FIELD.COVERED);
mFieldValueChangedSignal.emit(coord, GUI_FIELD.COVERED);
setFlaggedMinesCount(mFlaggedMines - 1);
return;
}
/**
* Signal handler, when the user right clicks some button on the minefield.
* It flags the covered field and unflags the flagged field.
*
* @param coord Coordinates of clicked button.
*/
public void onMinebuttonRightClicked(Coord coord) {
GUI_FIELD type = mViewedMinefield.get(coord);
if (type.equals(GUI_FIELD.COVERED)) {
changeFieldFlag(coord, true);
return;
}
if (type.equals(GUI_FIELD.FLAG)) {
changeFieldFlag(coord, false);
return;
}
}
/**
* Signal handler, when the user left clicks on some field on the minefield.
* This click is ignored, if some mine has been fired, the clicked field is flagged
* or uncovered. If the clicked field is not zero, the whole continuous area
* of zeros and neighbourhood is uncovered. If the clicked field is a non zero
* number, then only the clicked field is uncovered.
*
* @param clickedCoord Coordinates of field, which was clicked.
*/
public void onMinebuttonLeftClicked(Coord clickedCoord) {
GUI_FIELD realType = mRealMinefield.get(clickedCoord);
GUI_FIELD viewType = mViewedMinefield.get(clickedCoord);
if (mMineFired) {
return;
}
if (viewType.equals(GUI_FIELD.FLAG)) {
return;
}
if (realType.equals(GUI_FIELD.MINE)) {
fireMine(clickedCoord);
return;
}
// here we get the area, which is to be uncovered; if the clicked field
// is a nonzero number, we get only the clicked field; if the clicked
// field is zero, then we get the whole area of zeros and its neighbourhood;
// otherwise we get nothing to uncover
Set<Coord> area = new HashSet<Coord>();
MinesUtils.getRevealArea(clickedCoord, area, mViewedMinefield, mRealMinefield);
for(Coord coord : area) {
GUI_FIELD revealedViewField = mViewedMinefield.get(coord);
if (revealedViewField.equals(GUI_FIELD.FLAG)) {
changeFieldFlag(coord, false);
}
revealField(coord);
}
}
/**
* Uncovers given covered field.
* @param revealedCoord Coordinates of the field, which should be uncovered.
*/
void revealField(Coord revealedCoord) {
// get the real value of field
GUI_FIELD realValue = mRealMinefield.get(revealedCoord);
assert(!realValue.equals(GUI_FIELD.MINE));
// check, if the field is covered
GUI_FIELD viewValue = mViewedMinefield.get(revealedCoord);
assert(viewValue.isFiltered(GUI_FIELD.COVERED_AND_MARKED_CLEAR_FLAGS));
// make the field uncovered
mViewedMinefield.set(revealedCoord, realValue);
mFieldValueChangedSignal.emit(revealedCoord, realValue);
SOLVER_FIELD revealedValue = MinesUtils.convertGUIToSolverData(realValue);
mSolver.revealField(revealedCoord, revealedValue);
}
/**
* It makes the clicked field red, shows real values on all the other fields
* and emits mine fired signal.
* @param coord
*/
void fireMine(Coord coord) {
mMineFired = true;
mMinefieldRepaintSignal.emit(mRealMinefield);
mFieldValueChangedSignal.emit(coord, GUI_FIELD.FIRED_MINE);
mMineFiredSignal.emit();
}
/**
* Set the flagged fields counter and emits signal, that number
* of flagged fields has changed.
* @param newCount Count of currently flagged fields.
*/
void setFlaggedMinesCount(int newCount) {
assert(newCount < MinesUtils.getFieldsCount(mMinefieldSize));
assert(newCount >= 0);
mFlaggedMines = newCount;
int remainingMines = mMines - mFlaggedMines;
mFlaggedFieldsCountChangeSignal.emit(remainingMines);
}
}