Package mines

Source Code of mines.MineSweeperLogic$Measurrer$MeasurrerData

/**
* 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);
  }
}
TOP

Related Classes of mines.MineSweeperLogic$Measurrer$MeasurrerData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.