/*
* TickTackDoh - a cross'n'naughts style game for Android
* Copyright (C) 2008 Karo Launonen
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ticktackdoh.app;
public class Game {
public final static String GAME_TAG = "Game";
public final static byte MIN_DIMENSION = 10;
public final static byte MAX_DIMENSION = 100;
public final static byte PLAYER_O = 1;
public final static byte PLAYER_X = 2;
public final static byte MOVE_ILLEGAL = 1;
public final static byte MOVE_LEGAL = 2;
public final static byte STATE_PLAYER_O_WINS = 1;
public final static byte STATE_PLAYER_X_WINS = 2;
public final static byte STATE_GAME_DRAW = 3;
public final static byte STATE_GAME_ONGOING = 4;
public final static byte MARKS_IN_A_ROW_TO_WIN = 5;
private byte mBoardColumns = 0;
private byte mBoardRows = 0;
private byte mBoard[];
private int mMovesMade = 0;
private int mMovesAvailable = 0;
private byte mWhoseMove = 0;
private byte mGameState = STATE_GAME_ONGOING;
Game(byte boardColumns, byte boardRows, byte startingPlayer) throws Exception
{
if (boardColumns < MIN_DIMENSION || boardRows < MIN_DIMENSION) {
throw new Exception("Columns and/or rows less than " + MIN_DIMENSION);
} else if (boardColumns > MAX_DIMENSION || boardRows > MAX_DIMENSION) {
throw new Exception("Columns and/or rows greater than " + MAX_DIMENSION);
}
if (startingPlayer != PLAYER_O && startingPlayer != PLAYER_X) {
throw new Exception("Invalid starting player");
}
mBoardColumns = boardColumns;
mBoardRows = boardRows;
mMovesAvailable = boardColumns * boardRows;
mBoard = new byte[mMovesAvailable];
mWhoseMove = startingPlayer;
}
public byte getWhoseMove() {
return mWhoseMove;
}
public void setWhoseMove(byte player) {
mWhoseMove = player;
}
public int getMoves() {
return mMovesMade;
}
public byte getCell(byte column, byte row) throws Exception {
if (column < 0 || column >= mBoardColumns ||
row < 0 || row >= mBoardRows) {
throw new Exception("Out-of-range dimensions");
}
return mBoard[row * mBoardColumns + column];
}
public byte tryMove(byte column, byte row) {
if (mGameState != STATE_GAME_ONGOING || mMovesMade == mMovesAvailable) {
return MOVE_ILLEGAL;
}
int cell = row * mBoardColumns + column;
if (mBoard[cell] != 0) {
return MOVE_ILLEGAL;
}
makeMove(column, row);
/* Turn change */
mWhoseMove = (mWhoseMove == PLAYER_O ? PLAYER_X : PLAYER_O);
return MOVE_LEGAL;
}
public byte getGameState() {
return mGameState;
}
private void makeMove(byte column, byte row) {
int marksInARow = 1;
int cell = row * mBoardColumns + column;
int x, y, checkCell, xBound, yBound;
mBoard[cell] = mWhoseMove;
mMovesMade++;
/* Check the game state */
/* Check horizontal, first to left and then right */
checkCell = cell - 1;
x = column - 1;
xBound = column - MARKS_IN_A_ROW_TO_WIN + 1;
if (xBound < 0) {
xBound = 0;
}
while (x >= xBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
x--;
checkCell--;
}
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
x = column + 1;
checkCell = cell + 1;
xBound = column + MARKS_IN_A_ROW_TO_WIN - 1;
if (xBound > mBoardColumns) {
xBound = mBoardColumns;
}
/* To the right */
while (x < xBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
x++;
checkCell++;
}
}
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
/* No win, reset counter */
marksInARow = 1;
/* Check vertical, first up */
y = row - 1;
checkCell = cell - mBoardColumns;
yBound = row - MARKS_IN_A_ROW_TO_WIN + 1;
if (yBound < 0) {
yBound = 0;
}
while (y >= yBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
y--;
checkCell -= mBoardColumns;
}
}
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
/* No win, check down */
y = row + 1;
checkCell = cell + mBoardColumns;
yBound = row + MARKS_IN_A_ROW_TO_WIN - 1;
if (yBound > mBoardRows) {
yBound = mBoardRows;
}
while (y < yBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
y++;
checkCell += mBoardColumns;
}
}
/* Check diagonals, first from top-left to bottom-right */
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
marksInARow = 1;
xBound = column - MARKS_IN_A_ROW_TO_WIN + 1;
if (xBound < 0) {
xBound = 0;
}
yBound = row - MARKS_IN_A_ROW_TO_WIN + 1;
if (yBound < 0) {
yBound = 0;
}
x = column - 1;
y = row - 1;
checkCell = cell - mBoardColumns - 1;
while (x >= xBound && y >= yBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
x--;
y--;
checkCell -= (mBoardColumns + 1);
}
}
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
xBound = column + MARKS_IN_A_ROW_TO_WIN - 1;
if (xBound > mBoardColumns) {
xBound = mBoardColumns;
}
yBound = row + MARKS_IN_A_ROW_TO_WIN - 1;
if (yBound > mBoardRows) {
yBound = mBoardRows;
}
x = column + 1;
y = row + 1;
checkCell = cell + mBoardColumns + 1;
while(x < xBound && y < yBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
x++;
y++;
checkCell += (mBoardColumns + 1);
}
}
/* Check diagonal top-right to bottom-left */
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
marksInARow = 1;
xBound = column + MARKS_IN_A_ROW_TO_WIN - 1;
if (xBound > mBoardColumns) {
xBound = mBoardColumns;
}
yBound = row - MARKS_IN_A_ROW_TO_WIN + 1;
if (yBound < 0) {
yBound = 0;
}
x = column + 1;
y = row - 1;
checkCell = cell - mBoardColumns + 1;
while (x < xBound && y >= yBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
x++;
y--;
checkCell -= (mBoardColumns - 1);
}
}
if (marksInARow < MARKS_IN_A_ROW_TO_WIN) {
xBound = column - MARKS_IN_A_ROW_TO_WIN + 1;
if (xBound < 0) {
xBound = 0;
}
yBound = row + MARKS_IN_A_ROW_TO_WIN - 1;
if (yBound >= mBoardRows) {
yBound = mBoardRows;
}
x = column - 1;
y = row + 1;
checkCell = cell + mBoardColumns - 1;
while (x >= xBound && y < yBound && mBoard[checkCell] == mWhoseMove) {
marksInARow++;
x--;
y++;
checkCell += (mBoardColumns - 1);
}
}
if (marksInARow == MARKS_IN_A_ROW_TO_WIN) {
/* Win! */
mGameState = (mWhoseMove == PLAYER_O ? STATE_PLAYER_O_WINS : STATE_PLAYER_X_WINS);
} else if (mMovesMade == mMovesAvailable) {
mGameState = STATE_GAME_DRAW;
}
}
}
|