Package mines

Source Code of mines.MinesUtils$MineSweeperData

package mines;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.trolltech.qt.gui.QMessageBox;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import mines.MinefieldContainer.AROUND_FIELDS_DISTANCE;
import mines.MinefieldContainer.CENTER_OPTION;
import mines.MinefieldContainer.Coord;

/**
* Class of static methods usefull for operations with the minefield.
*/
public class MinesUtils {

  /**
   * <P>Container of the real and the viewed content of a minefield. Can be saved
   * to a file.</P>
   *
   * <P>TODO: This container is able to be stored to a file because of the serialization
   * mechanism. Currently the default serialization is used. This makes troubles, when
   * some changes in class structures are made, so it is handy to make the
   * serialization independent from class structure.</P>
   */
  public static class MineSweeperData implements Serializable {

    private static final long serialVersionUID = -2157772593193389032L;

    /** Real content of the minefield data */
    public MinefieldContainer<GUI_FIELD> mRealContent = null;
    /** Viewed content of the minefield data */
    public MinefieldContainer<GUI_FIELD> mViewedContent = null;

    public MineSweeperData() {}

    /**
     * Copy constructor.
     * @param copy Original mine sweeper data object.
     */
    public MineSweeperData(MineSweeperData copy) {
      mRealContent = new MinefieldContainer<GUI_FIELD>(copy.mRealContent);
      mViewedContent = new MinefieldContainer<GUI_FIELD>(copy.mViewedContent);
    }
  }

  /**
   * Gets coordinates of all the fields from minefield with given size.
   * @param size Size of minefield.
   * @return List of coordinates of all the fields.
   */
  public static List<Coord> getAllFieldsCoords(int size) {
    List<Coord> result = new ArrayList<Coord>();
   
    for(int i = 0; i < size; i++) {
      int columns = size - i;
      int row = i * 2;

      // down
      for (int j = 0; j < columns; j++) {

        Coord coord = new Coord(row, j);
        result.add(coord);
      }

      // up
      for (int j = 0; j < columns - 1; j++) {

        Coord coord = new Coord(row + 1, j);
        result.add(coord);
      }
    }
   
    return result;
  }

  /**
   * Founds out, if the specified row has fields with up orientation or down
   * orientation.
   * @param row Row of minefield.
   * @return True, if the row contains fields with the down orientation.
   */
  public static boolean isDownButton(int row) {
    return (row % 2 == 0);
  }

  /**
   * <P>Counts the number of fields in a minefield with the given size.</P>
   *
   * <P>Statement: The triangle minefield of size $n$ has $n^2$ fields.</P>
   *
   * <P>Proof: We will divide rows into two groups - rows with the down orientation
   * and with the up orientation. There are $2n-1$ rows - $n$ of them are down oriented
   * and $n-1$ of them are up oriented. We will count the number of fields via
   * the sums of fields contained in these groups of rows. The count of fields
   * in down rows is $\sum_{i=0}^{n} i$ and the count of fields in up rows gives
   * $\sum_{i=1}^{n-1} i$. This gives together $n^2$.</P>
   *
   * @param size Size of minefield
   * @return Count of fields in specified minefield.
   */
  public static int getFieldsCount(int size) {
    return size * size;
 

  /**
   * Counts the number of flagged covered fields on the minefield.
   * @param minefield Minefield, where the flags will be counted.
   * @return Count of flags.
   */
  public static int countFlaggedFields(MinefieldContainer<GUI_FIELD> minefield) {
    List<Coord> indexes = minefield.getAllFieldsIndexes(GUI_FIELD.FLAG_FLAG);

    return indexes.size();
  }

  /**
   * Lays randomly given number of mines on the given minefield.
   *
   * @param minefield Minefield, where the mines will be layed.
   * @param mines Count of mines to lay.
   */
  public static void layMinesRandomly(MinefieldContainer<GUI_FIELD> minefield, int mines) {
    int fieldsCount = MinesUtils.getFieldsCount(minefield.getSize());
    if (mines > fieldsCount) {
      throw new Error("Internal error, more mines than fields: " + mines);
    }

    // initialize the minefield
    minefield.initialFill(GUI_FIELD.COVERED);

    // we will take all the coordinates and remove the mined ones
    Random random = new Random();
    List<Coord> coveredFields = minefield.getAllFieldsIndexes(GUI_FIELD.ALL_FLAGS);

    for(int i = 0; i < mines; i++) {
      // take a random number from interval <0; count of covered fields - 1>
      // and lay a mine according to it

      int fieldOrder = (Math.abs(random.nextInt()) % coveredFields.size());
      Coord minedCoord = coveredFields.get(fieldOrder);

      assert(minefield.get(minedCoord).equals(GUI_FIELD.COVERED));
      minefield.set(minedCoord, GUI_FIELD.MINE);

      // removed the mined field
      coveredFields.remove(fieldOrder);
    }
  }

  /**
   * For each non mined field it counts the count of mines in the neighbourhood
   * of that field and sets the value of that field same as the count of the mines.
   * @param minefield Minefield for updating the numbered fields.
   */
  public static void updateFieldsNumbers(MinefieldContainer<GUI_FIELD> minefield) {
    List<Coord> allFieldsCoords = getAllFieldsCoords(minefield.getSize());

    for (Coord coord : allFieldsCoords) {
      GUI_FIELD current_type = minefield.get(coord);

      // ignore mined fields
      if (current_type.equals(GUI_FIELD.MINE)) {
        continue;
      }
     
      List<Coord> minedCoords = minefield.getAroundFields(coord.mX, coord.mY,
          AROUND_FIELDS_DISTANCE.ONE, GUI_FIELD.MINE_FLAG);
     
      int aroundMinesCount = minedCoords.size();
      GUI_FIELD new_type = GUI_FIELD.getButtonTypeFromMinesCount(aroundMinesCount);
     
      minefield.set(coord, new_type);
    }
  }

  /**
   * Saves given minefield to given file. Eventual exception is shown in a message box.
   * @param data Minefield to save.
   * @param filename Filename for storing the minefield.
   */
  public static void saveMinefieldToFileSafely(MineSweeperData data, String filename) {
    try {
      ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(filename));
     
      os.writeObject(data);
      os.close();
    } catch (Exception e) {
      e.printStackTrace();
     
      QMessageBox errDialog = new QMessageBox(QMessageBox.Icon.Warning, "Unable to save minefield.", e.getMessage());
      errDialog.exec();
    }
  }

  /**
   * Loads serialized object from file. Eventual exception is shown in a message box.
   * @param <T> Class of the object.
   * @param filename Filename to load from.
   * @return The loaded object.
   */
  public static <T> T loadObjectFromFileSafely(String filename) {
 
    try {
      ObjectInputStream is = new ObjectInputStream(new FileInputStream(filename));

      @SuppressWarnings("unchecked")
      T result = (T) is.readObject();

      is.close();
      return result;
    } catch (Exception e) {
      e.printStackTrace();
     
      QMessageBox errDialog = new QMessageBox(QMessageBox.Icon.Warning, "Unable to load minefield.", e.getMessage());
      errDialog.exec();

      return null;
    }
  }

  /** Conversion map between {@link mines.GUI_FIELD} and {@link mines.SOLVER_FIELD}. */
  private static Map<GUI_FIELD, SOLVER_FIELD> mGUItoSolverDataConversionMap = loadGUItoSolverConversion();
  /** Conversion map between {@link mines.SOLVER_FIELD} and {@link mines.GUI_FIELD}. */
  private static Map<SOLVER_FIELD, GUI_FIELD> mSolvertoGUIDataConversionMap = loadSolvertoGUIConversion();

  /**
   * Fills the conversion map between gui and solver. There are some unconvertable
   * values.
   * @return The conversion map.
   */
  private static Map<GUI_FIELD, SOLVER_FIELD> loadGUItoSolverConversion() {
    Map<GUI_FIELD, SOLVER_FIELD> result = new HashMap<GUI_FIELD, SOLVER_FIELD>();

    result.put(GUI_FIELD.ZERO, SOLVER_FIELD.ZERO);
    result.put(GUI_FIELD.ONE, SOLVER_FIELD.ONE);
    result.put(GUI_FIELD.TWO, SOLVER_FIELD.TWO);
    result.put(GUI_FIELD.THREE, SOLVER_FIELD.THREE);
    result.put(GUI_FIELD.FOUR, SOLVER_FIELD.FOUR);
    result.put(GUI_FIELD.FIVE, SOLVER_FIELD.FIVE);
    result.put(GUI_FIELD.SIX, SOLVER_FIELD.SIX);
    result.put(GUI_FIELD.SEVEN, SOLVER_FIELD.SEVEN);
    result.put(GUI_FIELD.EIGHT, SOLVER_FIELD.EIGHT);
    result.put(GUI_FIELD.NINE, SOLVER_FIELD.NINE);
    result.put(GUI_FIELD.TEN, SOLVER_FIELD.TEN);
    result.put(GUI_FIELD.ELEVEN, SOLVER_FIELD.ELEVEN);
    result.put(GUI_FIELD.TWELVE, SOLVER_FIELD.TWELVE);

    result.put(GUI_FIELD.COVERED, SOLVER_FIELD.COVERED);
    result.put(GUI_FIELD.MARKED_CLEAR, SOLVER_FIELD.CLEAR);
    result.put(GUI_FIELD.MARKED_MINE, SOLVER_FIELD.MINE);
    result.put(GUI_FIELD.FLAG, SOLVER_FIELD.COVERED);

    return result;
  }

  /**
   * Fills the conversion map between solver and gui.
   * @return The conversion map.
   */
  private static Map<SOLVER_FIELD, GUI_FIELD> loadSolvertoGUIConversion() {
    Map<SOLVER_FIELD, GUI_FIELD> result = new HashMap<SOLVER_FIELD, GUI_FIELD>();

    result.put(SOLVER_FIELD.ZERO, GUI_FIELD.ZERO);
    result.put(SOLVER_FIELD.ONE, GUI_FIELD.ONE);
    result.put(SOLVER_FIELD.TWO, GUI_FIELD.TWO);
    result.put(SOLVER_FIELD.THREE, GUI_FIELD.THREE);
    result.put(SOLVER_FIELD.FOUR, GUI_FIELD.FOUR);
    result.put(SOLVER_FIELD.FIVE, GUI_FIELD.FIVE);
    result.put(SOLVER_FIELD.SIX, GUI_FIELD.SIX);
    result.put(SOLVER_FIELD.SEVEN, GUI_FIELD.SEVEN);
    result.put(SOLVER_FIELD.EIGHT, GUI_FIELD.EIGHT);
    result.put(SOLVER_FIELD.NINE, GUI_FIELD.NINE);
    result.put(SOLVER_FIELD.TEN, GUI_FIELD.TEN);
    result.put(SOLVER_FIELD.ELEVEN, GUI_FIELD.ELEVEN);
    result.put(SOLVER_FIELD.TWELVE, GUI_FIELD.TWELVE);

    result.put(SOLVER_FIELD.COVERED, GUI_FIELD.COVERED);
    result.put(SOLVER_FIELD.CLEAR, GUI_FIELD.MARKED_CLEAR);
    result.put(SOLVER_FIELD.MINE, GUI_FIELD.MARKED_MINE);

    return result;
  }

  /**
   * Converts the value of solver field to gui field.
   * @param data Value to convert.
   * @return Appropriate gui field value.
   */
  public static GUI_FIELD convertSolverToGUIData(SOLVER_FIELD data) {
    assert(mSolvertoGUIDataConversionMap.containsKey(data));

    return mSolvertoGUIDataConversionMap.get(data);
  }

  /**
   * Converts the value of gui field to solver field.
   * @param data Value to convert.
   * @return Appropriate solver field value.
   */
  public static SOLVER_FIELD convertGUIToSolverData(GUI_FIELD data) {
    assert(mGUItoSolverDataConversionMap.containsKey(data));

    return mGUItoSolverDataConversionMap.get(data);
  }

  /**
   * Converts all the gui minefield to the solver minefield.
   * @param GUIMinefield GUI minefield to convert.
   * @return Appropriate solver minefield.
   */
  public static MinefieldContainer<SOLVER_FIELD> convertGUIToSolverMinefield(
      MinefieldContainer<GUI_FIELD> GUIMinefield) {

    int minefieldSize = GUIMinefield.getSize();
    MinefieldContainer<SOLVER_FIELD> solverMinefield = new MinefieldContainer<SOLVER_FIELD>(minefieldSize);

    List<Coord> allCoords = MinesUtils.getAllFieldsCoords(minefieldSize);

    // set the solver fields according to the appropriate gui fields
    for(Coord coord : allCoords) {
      GUI_FIELD guiValue = GUIMinefield.get(coord);
      SOLVER_FIELD solverValue = convertGUIToSolverData(guiValue);

      solverMinefield.set(coord, solverValue);
    }

    return solverMinefield;
  }

  /**
   * Converts all the solver minefield to the gui minefield.
   * @param solverMinefield Solver minefield to convert.
   * @return Appropriate gui minefield.
   */
  public static MinefieldContainer<GUI_FIELD> convertSolverToGUIMinefield(
      MinefieldContainer<SOLVER_FIELD> solverMinefield) {

    int minefieldSize = solverMinefield.getSize();
    MinefieldContainer<GUI_FIELD> GUIMinefield = new MinefieldContainer<GUI_FIELD>(minefieldSize);

    List<Coord> allCoords = MinesUtils.getAllFieldsCoords(minefieldSize);

    // set the gui fields according to the appropriate solver fields
    for(Coord coord : allCoords) {
      SOLVER_FIELD solverValue = solverMinefield.get(coord);
      GUI_FIELD guiValue = MinesUtils.convertSolverToGUIData(solverValue);

      GUIMinefield.set(coord, guiValue);
    }

    return GUIMinefield;
  }

  /**
   * <P>Gets the indepent area of fields, which can affect, whether on the given covered
   * field must or must not be a mine. Used for reducing solver search complexity.</P>
   *
   * <P>Observation: The continous area of covered fields with the minimum width of two
   * fields separates uncovered numbered fields to areas, which does not affect each other.
   * That means, that minefield consistency of one such area does not depend on
   * values of numbers in all the other areas.</P>
   *
   * <P>This function get union of all such areas, which are at boundary (distance 2)
   * of given covered field.</P>
   *
   * @param minefield Minefield used for getting the affected fields.
   * @param coveredField Field, where it is required to check, if it can be mined
   * of numbered.
   * @return Coordinates of uncovered numbered fields, which can affected the
   * mine consistency of the searched area.
   */
  public static List<Coord> getAffectedFields(MinefieldContainer<SOLVER_FIELD> minefield,
      Coord coveredField) {

    List<Coord> result = new ArrayList<Coord>();
    Set<Coord> affectedFieldsSet = new HashSet<Coord>();
    Stack<Coord> areaSearchStack = new Stack<Coord>();

    // wave algorithm; start with the covered field, but do not add it to the
    // result; in each iteration there will be added uncovered numbered fields
    // with distance two of the taken field to the result; no coordinates
    // are added twice

    areaSearchStack.push(coveredField);

    while(!areaSearchStack.isEmpty()) {
      // remove taken field from stack and add it to the set to prevent
      // the duplicities
      Coord areaField = areaSearchStack.pop();
      affectedFieldsSet.add(areaField);

      List<Coord> aroundFields = minefield.getAroundFields(areaField, AROUND_FIELDS_DISTANCE.TWO,
          SOLVER_FIELD.NON_ZERO_NUMBER_FLAG, CENTER_OPTION.EXCLUDE_CENTER);

      // add new uncovered numbered fields to the stack and the result
      for(Coord aroundField : aroundFields) {
        if(affectedFieldsSet.contains(aroundField)) {
          // around field has already been added
          continue;
        }

        affectedFieldsSet.add(aroundField);
        areaSearchStack.push(aroundField);
        result.add(aroundField);
      }
    }

    return result;
  }

  /**
   * Gets the continous area of zero fields and non-zero fields, which are in the
   * neighbourhood of that area. Usefull when the user reveals the zero field.
   * Works recursively.
   *
   * @param searchedCoord Coordinates of next expansion of area to be revealed.
   * @param alreadyFoundFields Coordinates of fields, which has already been
   * added in the searched area.
   * @param viewedMinefield To prevent revealing the uncovered.
   * @param realMinefield The real content of minefield.
   */
  public static void getRevealArea(Coord searchedCoord, Set<Coord> alreadyFoundFields,
      MinefieldContainer<GUI_FIELD> viewedMinefield,
      MinefieldContainer<GUI_FIELD> realMinefield) {

    // ignore not covered fields
    GUI_FIELD viewedValue = viewedMinefield.get(searchedCoord);
    if (!viewedValue.isFiltered(GUI_FIELD.COVERED_AND_MARKED_CLEAR_FLAGS)) {
      return;
    }

    // add nonzero covered fields
    GUI_FIELD type = realMinefield.get(searchedCoord.mX, searchedCoord.mY);
    if (!type.equals(GUI_FIELD.ZERO)) {
      alreadyFoundFields.add(searchedCoord);
      return;
    }

    // add zero covered fields and all the covered around fields
    alreadyFoundFields.add(searchedCoord);

    List<Coord> aroundCoords = realMinefield.getAroundFields(searchedCoord.mX, searchedCoord.mY);
    for (Coord coord : aroundCoords) {
      if (!alreadyFoundFields.contains(coord)) {
        getRevealArea(coord, alreadyFoundFields, viewedMinefield,
            realMinefield);
      }
    }
  }
}
TOP

Related Classes of mines.MinesUtils$MineSweeperData

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.