package com.charlesmadere.android.classygames.models.games; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * A generic Board class. All games need to have their own Board class that * extends from this one. */ public abstract class GenericBoard { protected final static byte BOARD_INVALID = -1; protected final static byte BOARD_LOSE = 7; protected final static byte BOARD_NEW_GAME = 1; protected final static byte BOARD_NEW_MOVE = 2; protected final static byte BOARD_WIN = 15; /** * The number of positions that the board has horizontally. This can be * thought of as the board's X limit. */ protected byte lengthHorizontal; /** * The number of positions that the board has vertically. This can be * thought of as the board's Y limit. */ protected byte lengthVertical; /** * JSONObject that represents the game board. This can be null. */ private JSONObject boardJSON; /** * This board's positions. This is a two dimensional array that should be * accessed as [X][Y]. So a position on the board that is (5, 3) - (X = 5 * and Y = 3), would be [5][3]. */ private Position[][] positions; /** * Boolean indicating whether or not a piece on this board has been moved. * This does not prevent further moves on the board from being made. This * defaults to false. */ protected boolean hasMoveBeenMade; /** * Boolean indicating whether or not this board is locked. If the board is * locked, then that means that pieces can no longer be moved around on the * board. This defaults to false. */ protected boolean isBoardLocked; /** * Creates the Board object. Initializes all of the board's positions and * sets them to this board's defaults. * * @param lengthHorizontal * The <strong>X length</strong> of the game board. * * @param lengthVertical * The <strong>Y length</strong> of the game board. * * @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. */ protected GenericBoard(final byte lengthHorizontal, final byte lengthVertical) throws JSONException { this.lengthHorizontal = lengthHorizontal; this.lengthVertical = lengthVertical; reset(); } /** * Creates the Board object. Initializes all of the board's positions and * sets them to the data found in the boardJSON JSONObject. * * @param lengthHorizontal * The <strong>X length</strong> of the game board. * * @param lengthVertical * The <strong>Y length</strong> of the game board. * * @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. */ protected GenericBoard(final byte lengthHorizontal, final byte lengthVertical, final JSONObject boardJSON) throws JSONException { this.lengthHorizontal = lengthHorizontal; this.lengthVertical = lengthVertical; this.boardJSON = boardJSON; reset(); } /** * Flips both team's coordinate locations on the board. */ public void flipTeams() { final byte halfLengthVertical = (byte) (lengthVertical / 2); for (byte x = 0; x < lengthHorizontal; ++x) { for (byte y = 0; y < halfLengthVertical; ++y) { final Position position = getPosition(x, y); final Position positionInverse = getPosition(lengthHorizontal - 1 - x, lengthVertical - 1 - y); final GenericPiece piece = position.getPiece(); final GenericPiece pieceInverse = positionInverse.getPiece(); if (piece != null) { piece.switchTeam(); } if (pieceInverse != null) { pieceInverse.switchTeam(); } position.setPiece(pieceInverse); positionInverse.setPiece(piece); } } } /** * @return * Returns the number of positions that the board has horizontally. */ public byte getLengthHorizontal() { return lengthHorizontal; } /** * @return * Returns the number of positions that the board has vertically. */ public byte getLengthVertical() { return lengthVertical; } /** * Returns a specific Position on the Board. Think of X and Y as an ordered * pair. Note that <strong>bound checking is not performed</strong>, so if * you do something stupid you could get an ArrayIndexOutOfBoundsException. * * @param x * The X coordinate for the Position that you want. * * @param y * The Y coordinate for the Position that you want. * * @return * The Position as specified by the X and Y parameters. */ public Position getPosition(final byte x, final byte y) { return positions[x][y]; } /** * Returns a specific Position on the Board. Think of X and Y as an ordered * pair. Note that <strong>bound checking is not performed</strong>, so if * you do something stupid you could get an ArrayIndexOutOfBoundsException. * * @param x * The X coordinate for the Position that you want. * * @param y * The Y coordinate for the Position that you want. * * @return * The Position as specified by the X and Y parameters. */ public Position getPosition(final int x, final int y) { return getPosition((byte) x, (byte) y); } /** * Returns a specific Position on the Board. Note that <strong>bound * checking is not performed</strong>, so if you do something stupid you * could get an ArrayIndexOutOfBoundsException. * * @param coordinate * The single Coordinate object containing the X and Y positions. * * @return * The Position as specified by the Coordinate's X and Y positions. */ public Position getPosition(final Coordinate coordinate) { return getPosition(coordinate.getX(), coordinate.getY()); } /** * @return * Returns whether or not the board is in a state that could be sent to the * server. */ public boolean hasMoveBeenMade() { return hasMoveBeenMade; } /** * @return * Returns whether or not the board is currently locked. This means that * the board is in a state that could be sent to the server. */ public boolean isBoardLocked() { return isBoardLocked; } /** * Initializes the game board using data from the boardJSON variable. * * @throws JSONException * If a glitch or something happened while trying to create some JSON data * then this JSONException will be thrown. */ private void initializeBoardFromJSON() throws JSONException { final JSONObject board = boardJSON.getJSONObject("board"); final JSONArray teams = board.getJSONArray("teams"); final JSONArray teamPlayer = teams.getJSONArray(0); final JSONArray teamOpponent = teams.getJSONArray(1); initializeTeamFromJSON(teamPlayer, GenericPiece.TEAM_PLAYER); initializeTeamFromJSON(teamOpponent, GenericPiece.TEAM_OPPONENT); performGameSpecificJSONChecks(boardJSON); } /** * Initializes all of this board's positions. */ private void initializePositions() { positions = new Position[lengthHorizontal][lengthVertical]; for (byte x = 0; x < lengthHorizontal; ++x) { for (byte y = 0; y < lengthVertical; ++y) { positions[x][y] = new Position(x, y); } } } /** * Creates a team of pieces from the given JSONArray. * * @param team * JSONArray of all of this team's pieces. * * @param whichTeam * A byte that specifies which team these pieces belong to. * * @throws JSONException * If a glitch or something happened while trying to create this JSONObject * then a JSONException will be thrown. */ private void initializeTeamFromJSON(final JSONArray team, final byte whichTeam) throws JSONException { for (int i = 0; i < team.length(); ++i) { final JSONObject piece = team.getJSONObject(i); final JSONArray coordinates = piece.getJSONArray("coordinate"); final Coordinate coordinate = new Coordinate(coordinates.getInt(0), coordinates.getInt(1)); final int type = piece.getInt("type"); final GenericPiece genericPiece = buildPiece(whichTeam, type); getPosition(coordinate).setPiece(genericPiece); } } /** * Checks to see if a given position is actually located on a real, * existing, position on the board. * * @param x * The X position to be checked. * * @param y * The Y position to be checked. * * @return * True if the X, Y position passed in does exist. */ public boolean isPositionValid(final byte x, final byte y) { return x >= 0 && x < lengthHorizontal && y >= 0 && y < lengthVertical; } /** * Checks to see if a given position is actually located on a real, * existing, position on the board. * * @param x * The X position to be checked. * * @param y * The Y position to be checked. * * @return * True if the X, Y position passed in does exist. */ public boolean isPositionValid(final int x, final int y) { return isPositionValid((byte) x, (byte) y); } /** * Checks to see if a given position is actually located on a real, * existing, position on the board. * * @param coordinate * The X and Y positions to be checked. * * @return * True if the X, Y position passed in does exist. */ public boolean isPositionValid(final Coordinate coordinate) { return isPositionValid(coordinate.getX(), coordinate.getY()); } /** * Creates a JSON representation of this GenericBoard object. The * JSONObject that is created here <strong>is not stored</strong>, so * please be sure to only run this method when you really need to okay? :) * * @return * Returns a JSONObject that represents this GenericBoard object. * * @throws JSONException * If a glitch or something happened while trying to create this JSONObject * then a JSONException will be thrown. */ public JSONObject makeJSON() throws JSONException { final JSONObject teams = new JSONObject(); teams.put("teams", makeJSONTeams()); final JSONObject board = new JSONObject(); board.put("board", teams); return board; } /** * Creates a JSONArray of both teams (the player's and the opponent's) on * the board. This JSONArray is actually a JSONArray of two other * JSONArray's: one is the player's pieces and the other is the opponent's * pieces. * * @return * Returns a JSONArray that contains two other JSONArrays: one is the * player's pieces and the other is the opponent's pieces. * * @throws JSONException * If a glitch or something happened while trying to create this JSON * String then a JSONException will be thrown. */ private JSONArray makeJSONTeams() throws JSONException { final JSONArray teamPlayer = makeJSONTeam(GenericPiece.TEAM_PLAYER); final JSONArray teamOpponent = makeJSONTeam(GenericPiece.TEAM_OPPONENT); final JSONArray teams = new JSONArray(); teams.put(teamPlayer); teams.put(teamOpponent); return teams; } /** * Creates a JSONArray of one team's pieces. The team to make the pieces * for is specified by the whichTeam parameter. * * @param whichTeam * Which team do you want to make a JSONArray for? You should use one of * the GenericPiece class's TEAM_* public static members for this * parameter. * * @return * Returns a JSONArray that contains all of the given team's pieces. This * has the possibility of being an empty JSONArray. * * @throws JSONException * If a glitch or something happened while trying to create this JSON * String then a JSONException will be thrown. */ private JSONArray makeJSONTeam(final byte whichTeam) throws JSONException { final JSONArray team = new JSONArray(); for (byte x = 0; x < lengthHorizontal; ++x) { for (byte y = 0; y < lengthVertical; ++y) { final Position position = getPosition(x, y); if (position.hasPiece() && position.getPiece().isTeam(whichTeam)) { team.put(position.makeJSON()); } } } return team; } /** * Does some final JSON parsing to finish the setting up of the game board. * * @throws JSONException * If there was an error when trying to parse the JSON, then this Exception * will be thrown. */ protected void performGameSpecificJSONChecks(final JSONObject boardJSON) throws JSONException { } /** * This will reset the board back to it's original state as created * immediately after using this class's constructor. * * @throws JSONException * If a glitch or something happened while trying to create some JSON data * then this JSONException will be thrown. If the game was not resumed from * JSON data then this should never happen. */ public void reset() throws JSONException { hasMoveBeenMade = false; isBoardLocked = false; initializePositions(); resetBoard(); if (boardJSON == null) { initializeDefaultBoard(); } else { initializeBoardFromJSON(); } } /** * Creates a GenericPiece object out of the given data. * * @param whichTeam * The team that this new GenericPiece object should be on. * * @param type * The type of piece that this GenericPiece object should be. This should * be something like a king for checkers or a knight for chess. * * @return * Returns a new GenericPiece object made from the given data. */ protected abstract GenericPiece buildPiece(final byte whichTeam, final int type); /** * Checks this GenericBoard object for validity. This should treat the * board as if it's had only 1 turned played on it. * * @return * A byte that represents if the board is valid or not. */ public abstract byte checkValidity(); /** * Checks this GenericBoard object for validity against the board given in * the board parameter. * * @param board * The board to check against. This is a newer game board than the one * that's already stored in this GenericBoard object. * * @return * A byte that represents if the board is valid or not. */ public abstract byte checkValidity(final GenericBoard board); /** * Makes this GenericBoard object a default board. This should only be used * for a brand new game. If a game is being resumed then this should not be * used. This should probably be run (in the case that it needs to be) * immediately after the object is constructed. */ protected abstract void initializeDefaultBoard(); /** * Checks to see if a move can be performed given the input positions. If * the given move turns out to be valid then this method will make any * needed adjustments to the game board and then return true. * * @param previous * The previous (old) position on the game board that the user clicked on. * This selected position contains the piece that the user wants to * actually move. * * @param current * The current (new) position on the game board that the user clicked on. * This selected position is the location that the user wants to move their * selected piece to. * * @return * Returns true if the given move is a valid one. */ public abstract boolean move(final Position previous, final Position current); /** * Allows classes that extend from this one to perform some code when this * class's reset() method is run. */ protected abstract void resetBoard(); }