/*
* Copyright 2012, 2013 Evan Flynn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.foobar.minesweeper.model;
import java.util.List;
public class Square {
private final int column;
private final int row;
private final Minefield minefield;
private boolean mine;
private Squares type = Squares.BLANK;
private int nearbyMines;
Square(Minefield minefield, int row, int column) {
this.minefield = minefield;
this.row = row;
this.column = column;
}
/**
* Gets the type of the Square.
*
* @return type of the Square
*/
public Squares getType() {
return type;
}
/**
* Determines whether the square can be revealed. This is equivalent to
* {@code !minefield.isGameOver() && getType() == Squares.BLANK}, where
* {@code minefield} is the field this square belongs to.
*
* @return true if the square can be revealed.
*/
public boolean isRevealable() {
return !minefield.isGameOver() && type == Squares.BLANK;
}
/**
* Gets the row of the square.
*
* @return the row of the square
*/
public int getRow() {
return row;
}
/**
* Gets the column of the square.
*
* @return the column of the square.
*/
public int getColumn() {
return column;
}
/**
* Gets the number of nearby mines.
*
*/
public int getMineCount() {
return nearbyMines;
}
/**
* Toggles the flag state of the square. If the game is over or the square
* cannot be flagged, the method returns.
*
*/
public void toggleFlag() {
if (minefield.isGameOver()) {
return;
}
if (type == Squares.FLAG) {
type = Squares.BLANK;
} else if (type == Squares.BLANK) {
type = Squares.FLAG;
} else {
return;
}
minefield.updateSquare(this);
}
/**
* Reveals this square. If the square is a mine, the game is over. If the game
* is over or the square is flagged, the method returns.
*
* <p>
* Calling this method repeatedly will have no effect until the game is
* restarted.
*/
public void reveal() {
if (type != Squares.BLANK || minefield.isGameOver()) {
return;
}
if (mine) {
type = Squares.HITMINE;
mine = false;
minefield.onGameLost();
} else {
minefield.reveal(this);
}
}
/**
* Reveals nearby squares. The square must be already exposed for this call to
* work. Otherwise, the method returns with no change.
*/
public void revealNearby() {
if (minefield.isGameOver() || type != Squares.EXPOSED) {
return;
}
List<Square> neighbors = minefield.findNeighbors(this);
int nearbyFlags = neighbors.stream()
.filter(square -> square.type == Squares.FLAG)
.mapToInt(e -> 1)
.sum();
if (nearbyFlags == nearbyMines) {
neighbors.forEach(Square::reveal);
}
}
void addNearbyMine() {
nearbyMines++;
}
boolean isMine() {
return mine;
}
void setMine(boolean isMine) {
mine = isMine;
}
void onGameLost() {
if (mine) {
type = Squares.MINE;
} else if (type == Squares.FLAG) {
type = Squares.WRONGMINE;
}
}
void onGameWon() {
if (mine) {
type = Squares.FLAG;
}
}
int visit() {
int exposed = 1;
type = Squares.EXPOSED;
if (nearbyMines == 0) {
for (Square square : minefield.findNeighbors(this)) {
if (square.type != Squares.EXPOSED) {
exposed += square.visit();
}
}
}
return exposed;
}
}