package com.cast.gwt.TicTacToe.client; import com.cast.gwt.receiver.client.console; import com.google.gwt.canvas.dom.client.Context; import com.google.gwt.canvas.dom.client.Context2d; /* * This class represents a TicTacToe board object, with all needed drawing and * state update functions. */ public class Board { private Context2d mContext; private String mGameResult; private int mWinningLocation; Integer mBoard[][]; private int dimension; private int margin = 50; private int pieceMargin = 20; private int X; private int Y; private int cellWidth; /** * Creates an empty board object with no location * * @param {CanvasRenderingContext2D} context the 2D context of the canvas that * the board is drawn on. */ public Board(Context context) { this.mContext = (Context2d) context; this.mBoard = new Integer[3][3]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { this.mBoard[i][j] = STATE.EMPTY.value; } } } /** * Calculates board dimensions. */ public void clear() { this.boardCalcDimensions(); } /** * Calculates and sets internally the board's width, height, x, and y. */ private void boardCalcDimensions() { if (this.mContext.getCanvas().getWidth() > this.mContext.getCanvas() .getHeight()) { this.dimension = this.mContext.getCanvas().getHeight() - 2 * this.margin; } else { this.dimension = this.mContext.getCanvas().getWidth() - 2 * this.margin; } this.X = (this.mContext.getCanvas().getWidth() - this.dimension) / 2; this.Y = (this.mContext.getCanvas().getHeight() - this.dimension) / 2; this.cellWidth = this.dimension / 3; } public void drawGrid() { this.boardDrawGrid(); } /** * Draws the hashmark-shaped grid for TicTacToe. */ private void boardDrawGrid() { this.mContext.setFillStyle("#BDBDBD"); this.mContext.setStrokeStyle("#000000"); this.mContext.fillRect(this.X, this.Y, this.dimension, this.dimension); // draw grid this.mContext.setLineWidth(5); this.mContext.moveTo(this.X + this.cellWidth, this.Y); this.mContext.lineTo(this.X + this.cellWidth, this.Y + this.dimension); this.mContext.stroke(); this.mContext.moveTo(this.X + this.cellWidth * 2, this.Y); this.mContext.lineTo(this.X + this.cellWidth * 2, this.Y + this.dimension); this.mContext.stroke(); this.mContext.moveTo(this.X, this.Y + this.cellWidth); this.mContext.lineTo(this.X + this.dimension, this.Y + this.cellWidth); this.mContext.stroke(); this.mContext.moveTo(this.X, this.Y + this.cellWidth * 2); this.mContext.lineTo(this.X + this.dimension, this.Y + this.cellWidth * 2); this.mContext.stroke(); } /** * Draws an O symbol in the given row and column. * * @param {number} row the row the piece should be placed in. * @param {number} col the column the piece should be placed in. * @this {board} * @return {boolean} true if the selected row and column is a valid square to * put a piece in. */ public boolean boardDrawNaught(int row, int col) { if (this.mBoard[row][col] != STATE.EMPTY.value) { console.info("Invalid position: " + row + " " + col + " val:" + this.mBoard[row][col]); return false; } this.mBoard[row][col] = STATE.NAUGHT.value; this.mContext.setLineWidth(8); this.mContext.setStrokeStyle("#FFFF00"); this.mContext.beginPath(); this.mContext.arc(this.X + this.cellWidth * (col + 0.5), this.Y + this.cellWidth * (row + 0.5), this.cellWidth / 2 - this.pieceMargin, 0, 360); this.mContext.stroke(); return true; } /** * Draws an X symbol in the given row and column. * * @param {number} row the row the piece should be placed in. * @param {number} col the column the piece should be placed in. * @this {board} * @return {boolean} true if the selected row and column is a valid square to * put a piece in. */ public boolean boardDrawCross(int row, int col) { if (this.mBoard[row][col] != STATE.EMPTY.value) { console.info("Invalid position: " + row + " " + col + " val:" + this.mBoard[row][col]); return false; } this.mBoard[row][col] = STATE.CROSS.value; this.mContext.setLineWidth(8); this.mContext.setStrokeStyle("#0000FF"); this.mContext.beginPath(); this.mContext.moveTo(this.X + this.cellWidth * col + this.pieceMargin, this.Y + this.cellWidth * row + this.pieceMargin); this.mContext.lineTo( this.X + this.cellWidth * (col + 1) - this.pieceMargin, this.Y + this.cellWidth * (row + 1) - this.pieceMargin); this.mContext.stroke(); this.mContext.moveTo( this.X + this.cellWidth * (col + 1) - this.pieceMargin, this.Y + this.cellWidth * row + this.pieceMargin); this.mContext.lineTo(this.X + this.cellWidth * col + this.pieceMargin, this.Y + this.cellWidth * (row + 1) - this.pieceMargin); this.mContext.stroke(); return true; } public boolean drawCross(double row, double col) { return boardDrawCross((int) row, (int) col); } public boolean drawNaught(double row, double col) { return boardDrawNaught((int) row, (int) col); } /** * Determines whether the game is over, whether by winning or draw. * * @return {@link Boolean} true if the game ended via a win or a draw. */ public boolean isGameOver() { return boardIsGameOver(); } /** * Determines whether the game is over, whether by winning or draw. * * @return {@link Boolean} true if the game ended via a win or a draw. */ private boolean boardIsGameOver() { boolean isBoardFull = true; this.printBoard(); // Check the rows for (int i = 0; i < this.mBoard.length; i++) { if ((this.mBoard[i][0] != STATE.EMPTY.value) && (this.mBoard[i][1] == this.mBoard[i][0]) && (this.mBoard[i][2] == this.mBoard[i][0])) { this.mGameResult = GAME_RESULT.O_WON.value; if (this.mBoard[i][0] == STATE.CROSS.value) { this.mGameResult = GAME_RESULT.X_WON.value; } if (i == 0) { this.mWinningLocation = WINNING_LOCATION.ROW_0.value; } else if (i == 1) { this.mWinningLocation = WINNING_LOCATION.ROW_1.value; } else { this.mWinningLocation = WINNING_LOCATION.ROW_2.value; } } if ((isBoardFull == true) && ((this.mBoard[i][0] == STATE.EMPTY.value) || (this.mBoard[i][1] == STATE.EMPTY.value) || (this.mBoard[i][2] == STATE.EMPTY.value))) { isBoardFull = false; } } this.printBoard(); // Check the columns for (int j = 0; j < this.mBoard[0].length; j++) { if ((this.mBoard[0][j] != STATE.EMPTY.value) && (this.mBoard[1][j] == this.mBoard[0][j]) && (this.mBoard[2][j] == this.mBoard[0][j])) { this.mGameResult = GAME_RESULT.O_WON.value; if (this.mBoard[0][j] == STATE.CROSS.value) { this.mGameResult = GAME_RESULT.X_WON.value; } if (j == 0) { this.mWinningLocation = WINNING_LOCATION.COLUMN_0.value; } else if (j == 1) { this.mWinningLocation = WINNING_LOCATION.COLUMN_1.value; } else { this.mWinningLocation = WINNING_LOCATION.COLUMN_2.value; } break; } } this.printBoard(); // Check diagonals if ((this.mBoard[0][0] != STATE.EMPTY.value) && (this.mBoard[1][1] == this.mBoard[0][0]) && (this.mBoard[2][2] == this.mBoard[0][0])) { this.mWinningLocation = WINNING_LOCATION.DIAGONAL_TOPLEFT.value; this.mGameResult = GAME_RESULT.O_WON.value; if (this.mBoard[0][0] == STATE.CROSS.value) { this.mGameResult = GAME_RESULT.X_WON.value; } } else if ((this.mBoard[0][2] != STATE.EMPTY.value) && (this.mBoard[1][1] == this.mBoard[0][2]) && (this.mBoard[2][0] == this.mBoard[0][2])) { this.mWinningLocation = WINNING_LOCATION.DIAGONAL_BOTTOMLEFT.value; this.mGameResult = GAME_RESULT.O_WON.value; if (this.mBoard[0][2] == STATE.CROSS.value) { this.mGameResult = GAME_RESULT.X_WON.value; } } // Check whether the game was won or drawn if ((this.mGameResult == GAME_RESULT.X_WON.value) || (this.mGameResult == GAME_RESULT.O_WON.value)) { this.drawWinningLocation(); return true; } if (isBoardFull == true) { this.mGameResult = GAME_RESULT.DRAW.value; return true; } return false; } private void drawWinningLocation() { boardDrawWinningLocation(); } /** * Draws the line connecting the winning three pieces. */ private void boardDrawWinningLocation() { double xStart = -1, yStart = -1, xEnd = -1, yEnd = -1; if (this.mWinningLocation == WINNING_LOCATION.ROW_0.value) { xStart = 0.05; xEnd = 2.95; yStart = yEnd = 0.5; } else if (this.mWinningLocation == WINNING_LOCATION.ROW_1.value) { xStart = 0.05; xEnd = 2.95; yStart = yEnd = 1.5; } else if (this.mWinningLocation == WINNING_LOCATION.ROW_2.value) { xStart = 0.05; xEnd = 2.95; yStart = yEnd = 2.5; } else if (this.mWinningLocation == WINNING_LOCATION.COLUMN_0.value) { yStart = 0.05; yEnd = 2.95; xStart = xEnd = 0.5; } else if (this.mWinningLocation == WINNING_LOCATION.COLUMN_1.value) { yStart = 0.05; yEnd = 2.95; xStart = xEnd = 1.5; } else if (this.mWinningLocation == WINNING_LOCATION.COLUMN_2.value) { yStart = 0.05; yEnd = 2.95; xStart = xEnd = 2.5; } else if (this.mWinningLocation == WINNING_LOCATION.DIAGONAL_TOPLEFT.value) { xStart = yStart = 0.05; xEnd = yEnd = 2.95; } else if (this.mWinningLocation == WINNING_LOCATION.DIAGONAL_BOTTOMLEFT.value) { xStart = yEnd = 2.95; yStart = xEnd = 0.05; } else { console.log("Unknown winning location: " + this.mWinningLocation); } this.mContext.setLineWidth(10); this.mContext.setStrokeStyle("#FF0000"); this.mContext.beginPath(); this.mContext.moveTo(this.X + this.cellWidth * xStart, this.Y + this.cellWidth * yStart); this.mContext.lineTo(this.X + this.cellWidth * xEnd, this.Y + this.cellWidth * yEnd); this.mContext.stroke(); } /** * Logs the current state of the board's pieces and results. */ private void printBoard() { boardPrintBoard(); } /** * Logs the current state of the board's pieces and results. */ private void boardPrintBoard() { for (int i = 0; i < this.mBoard.length; i++) { console.log("[ " + this.mBoard[i][0] + ", " + this.mBoard[i][1] + ", " + this.mBoard[i][2] + " ]"); } console.log("gameResult: " + this.mGameResult); console.log("winningLoc: " + this.mWinningLocation); } public int getWinningLocation() { return this.mWinningLocation; } public String getGameResult() { return this.mGameResult; } /** * Updates this game's result to abandoned. */ public void setGameAbandoned() { this.mGameResult = GAME_RESULT.ABANDONED.value; } /** * Resets the board to a starting state. */ public void reset() { this.mGameResult = ""; this.mWinningLocation = -1; this.mContext.beginPath(); this.mContext.clearRect(this.X, this.Y, this.mContext.getCanvas() .getWidth(), this.mContext.getCanvas().getHeight()); this.drawGrid(); for (int i = 0; i < this.mBoard.length; i++) { for (int j = 0; j < this.mBoard[0].length; j++) { this.mBoard[i][j] = STATE.EMPTY.value; } } } }