package com.charlesmadere.android.classygames.models.games.checkers;
import com.charlesmadere.android.classygames.models.games.Coordinate;
import com.charlesmadere.android.classygames.models.games.GenericBoard;
import com.charlesmadere.android.classygames.models.games.Position;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Class representing a Checkers board. This board is made up of a bunch of
* positions. Checkers is 8 by 8, so that's 64 positions.
*/
public final class Board extends GenericBoard
{
private final static byte LENGTH_HORIZONTAL = 8;
private final static byte LENGTH_VERTICAL = 8;
private final static byte MAX_TEAM_SIZE = 12;
/**
* Holds a handle to the piece that the user last moved. This is needed for
* multijump purposes.
*/
private Piece lastMovedPiece;
/**
* Creates a Checkers board object.
*
* @throws JSONException
* If a glitch or something happened while trying to create some JSON data
* then this JSONException will be thrown. When using this particular
* constructor this should never happen.
*/
public Board() throws JSONException
{
super(LENGTH_HORIZONTAL, LENGTH_VERTICAL);
}
/**
* Creates a Checkers board object using the given JSON String.
*
* @param boardJSON
* JSONObject that represents the board.
*
* @throws JSONException
* If a glitch or something happened while trying to create some JSON data
* then this JSONException will be thrown.
*/
public Board(final JSONObject boardJSON) throws JSONException
{
super(LENGTH_HORIZONTAL, LENGTH_VERTICAL, boardJSON);
}
@Override
protected Piece buildPiece(final byte whichTeam, final int type)
{
return new Piece(whichTeam, type);
}
@Override
public byte checkValidity()
{
byte piecesCountOpponent = 0;
byte piecesCountPlayer = 0;
for (byte x = 0; x < lengthHorizontal; ++x)
{
for (byte y = 0; y < lengthVertical; ++y)
{
final Coordinate coordinate = new Coordinate(x, y);
final Position position = getPosition(coordinate);
final Piece piece = (Piece) position.getPiece();
if (piece != null && coordinate.areBothEitherEvenOrOdd())
// check to see if this piece is in an invalid position on the
// board
{
return BOARD_INVALID;
}
if (piece != null)
// count the size of the teams
{
if (piece.isTeamOpponent())
{
++piecesCountOpponent;
}
else if (piece.isTeamPlayer())
{
++piecesCountPlayer;
}
}
if (!coordinate.areBothEitherEvenOrOdd())
{
if (y > 4)
{
if (piece == null)
{
return BOARD_INVALID;
}
else if (piece.isTeamPlayer())
{
return BOARD_INVALID;
}
else if (piece.isTypeKing())
{
return BOARD_INVALID;
}
}
else if (y == 2)
{
if (piece != null)
{
if (piece.isTeamOpponent())
{
return BOARD_INVALID;
}
else if (piece.isTypeKing())
{
return BOARD_INVALID;
}
}
}
else if (y < 2)
{
if (piece == null)
{
return BOARD_INVALID;
}
else if (piece.isTeamOpponent())
{
return BOARD_INVALID;
}
else if (piece.isTypeKing())
{
return BOARD_INVALID;
}
}
}
if (y == 7 && x % 2 == 0 || y == 6 && x % 2 != 0 || y == 5 && x % 2 == 0 || y == 1 && x % 2 == 0 || y == 0 && x % 2 != 0)
// only the first row should have a piece moved from a valid
// spot at first
{
if (piece == null)
{
return BOARD_INVALID;
}
}
if (y == 3)
// make sure that the first move came from a valid place
{
if (piece != null)
{
if (x == 0)
// check that farthest left piece moved to this position
{
if (getPosition(x + 1, y - 1).hasPiece())
{
return BOARD_INVALID;
}
}
else if (x % 2 == 0)
// check the rest of the pieces for a valid first turn
// move
{
if (getPosition(x - 1, y - 1).hasPiece() && getPosition(x + 1, y - 1).hasPiece())
{
return BOARD_INVALID;
}
}
}
}
}
}
if (piecesCountOpponent != MAX_TEAM_SIZE || piecesCountPlayer != MAX_TEAM_SIZE)
{
return BOARD_INVALID;
}
return BOARD_NEW_GAME;
}
@Override
public byte checkValidity(final GenericBoard board)
{
byte piecesCountOpponent = 0;
byte piecesCountPlayer = 0;
for (byte x = 0; x < lengthHorizontal; ++x)
{
for (byte y = 0; y < lengthVertical; ++y)
{
final Coordinate coordinate = new Coordinate(x, y);
final Position positionNew = board.getPosition(coordinate);
final Piece pieceNew = (Piece) positionNew.getPiece();
if (coordinate.areBothEitherEvenOrOdd())
// check to see if this piece is in an invalid position on
// the board
{
if (pieceNew != null)
{
return BOARD_INVALID;
}
}
if (pieceNew != null)
// count the size of the teams
{
if (pieceNew.isTeamOpponent())
{
++piecesCountOpponent;
}
else if (pieceNew.isTeamPlayer())
{
++piecesCountPlayer;
}
}
if (piecesCountOpponent > MAX_TEAM_SIZE || piecesCountPlayer > MAX_TEAM_SIZE)
{
return BOARD_INVALID;
}
}
}
if (piecesCountOpponent == 0)
{
return BOARD_WIN;
}
return BOARD_NEW_MOVE;
}
@Override
protected void initializeDefaultBoard()
{
// player team
getPosition(1, 0).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(3, 0).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(5, 0).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(7, 0).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(0, 1).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(2, 1).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(4, 1).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(6, 1).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(1, 2).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(3, 2).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(5, 2).setPiece(new Piece(Piece.TEAM_PLAYER));
getPosition(7, 2).setPiece(new Piece(Piece.TEAM_PLAYER));
// opponent team
getPosition(0, 7).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(2, 7).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(4, 7).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(6, 7).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(1, 6).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(3, 6).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(5, 6).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(7, 6).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(0, 5).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(2, 5).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(4, 5).setPiece(new Piece(Piece.TEAM_OPPONENT));
getPosition(6, 5).setPiece(new Piece(Piece.TEAM_OPPONENT));
}
@Override
public boolean move(final Position previous, final Position current)
{
boolean isMoveValid = false;
if (isBoardLocked)
// If the board is locked, then that means that no piece is allowed to
// move around.
{
return false;
}
else if (current.hasPiece())
// If the second position that the user selected has a piece on it then
// this is an invalid move.
{
return false;
}
else if (previous.hasPiece() && previous.getPiece().isTeamOpponent())
// If the first position that the user selected has an opponent's piece
// on it then this is an invalid move.
{
return false;
}
else if (previous.hasPiece() && previous.getPiece().isTeamPlayer() && !current.hasPiece())
// This is the one way that a checkers move can actually be valid. The
// first selected position must have a friendly piece and the second
// position not have a piece.
{
final Piece piece = (Piece) previous.getPiece();
// The if statements that we're about to see perform calculations
// on the player's attempted move. We're trying to see how far the
// jump is in order to travel from the previous position to the
// current one.
if (((previous.getCoordinate().getX() == current.getCoordinate().getX() - 1)
|| (previous.getCoordinate().getX() == current.getCoordinate().getX() + 1))
&& !hasMoveBeenMade)
// Checks to see if the move from the previous position to the
// current one has a difference of just 1 when comparing the two X
// values. Then it also checks to make sure that a move has not yet
// been made on the board.
{
switch (piece.getType())
// Depending on the type of Piece that this is (Normal or King)
// we will perform various move calculations.
{
case Piece.TYPE_KING:
if (previous.getCoordinate().getY() == current.getCoordinate().getY() + 1)
// Checks to see if the move from the previous position
// to the current one has a difference of just 1 when
// comparing the two Y values. Note the + 1 at the end
// of the if statement and the lack of a break at the
// end of this case statement; a King piece can move
// either up or down on the game board.
{
isMoveValid = true;
isBoardLocked = true;
}
case Piece.TYPE_NORMAL:
if (previous.getCoordinate().getY() == current.getCoordinate().getY() - 1)
// Checks to see if the move from the previous position
// to the current one has a difference of just 1 when
// comparing the two Y values. Note the - 1 at the end
// of the if statement; a Normal piece can only move up
// on the game board.
{
isMoveValid = true;
isBoardLocked = true;
}
}
}
else if ((previous.getCoordinate().getX() == current.getCoordinate().getX() - 2)
|| (previous.getCoordinate().getX() == current.getCoordinate().getX() + 2))
// Checks to see if the move from the previous position to the
// current one has a difference of 2 when comparing the two X
// values. Note that here, unlike in the if statement directly
// above this one, we're not checking the state of the
// hasMoveBeenMade variable. This is for double jumping purposes.
// Also note how a difference of 2 is being checked this time. This
// should be thought of as the jump condition. In order for a
// checkers piece to jump over another, their X and Y coordinates
// must both change by 2.
{
boolean isJumpValid = false;
switch (piece.getType())
// Depending on the type of Piece that this is (Normal or King)
// we will perform various move calculations.
{
case Piece.TYPE_KING:
if (previous.getCoordinate().getY() == current.getCoordinate().getY() + 2)
// Check to see if this King piece has moved down 2
// positions. Note how we purposely left the break
// command out. That is because a King piece has its
// own set of jump rules in addition to having the
// exact jump rules that a Normal piece has.
{
isJumpValid = true;
}
case Piece.TYPE_NORMAL:
if (previous.getCoordinate().getY() == current.getCoordinate().getY() - 2)
// Check to see if this Normal (or King) piece has
// moved up 2 positions.
{
isJumpValid = true;
}
}
if (isJumpValid)
// If the jump was valid then we still need to make sure that
// the actual move was valid. A valid move is one where the
// checkers piece has actually passed over the top of an
// opponent's checkers piece.
{
// Calculate the X and Y coordinates for the position that
// is between the previous position and the current
// position. Note that we're using Math.abs(), that method
// insures that the resulting value is definitely going to
// be a positive number.
final byte middleX = (byte) Math.abs((previous.getCoordinate().getX() + current.getCoordinate().getX()) / 2);
final byte middleY = (byte) Math.abs((previous.getCoordinate().getY() + current.getCoordinate().getY()) / 2);
// This is the position on the checkers board that is
// between the previous position and the current position.
final Position middlePosition = getPosition(middleX, middleY);
if (middlePosition.hasPiece() && middlePosition.getPiece().isTeamOpponent())
// Check to see that this middle position has a piece and
// that the piece is an opponent's.
{
if (!hasMoveBeenMade)
// Here we check to see if a move has already been
// made. This is for double jumping purposes.
{
lastMovedPiece = piece;
middlePosition.removePiece();
isMoveValid = true;
}
else if (lastMovedPiece == piece)
// A move has already been made and as such we need to
// verify that the piece that is being moved right now
// is the same piece that moved last time.
{
middlePosition.removePiece();
isMoveValid = true;
}
}
}
}
if (isMoveValid)
// This jump move has been calculated as being valid. That means
// that the user jumped one of the opposing team's pieces. Let's
// kill that piece and set the piece that the user jumped with as
// the last moved piece (this is for double jump purposes).
{
current.setPiece(new Piece(piece));
previous.removePiece();
// Grab a reference to the current position's piece. This is
// the last moved piece.
lastMovedPiece = (Piece) current.getPiece();
if (current.getCoordinate().getY() == lengthVertical - 1)
// Check to see if the position that we just now landed on is
// the top of the board. In checkers, if a piece lands at the
// top of the board then that means that the piece becomes a
// King.
{
((Piece) current.getPiece()).ascendToKing();
}
}
}
if (isMoveValid)
// The move has been calculated as being valid. Mark that a move has
// been made.
{
hasMoveBeenMade = true;
}
return isMoveValid;
}
@Override
protected void resetBoard()
{
lastMovedPiece = null;
}
}