Computer Science 252
Problem Solving with Java
Spring 2014, The College of Saint Rose
TicTacToe Demo
A working demo of TicTacToe will appear below. Click inside the applet to interact with it.
TicTacToe BlueJ Project
Click here to download a BlueJ project for TicTacToe.
TicTacToe Source Code
The Java source code for TicTacToe is below. Click on a file name to download it.
import java.awt.Color; import objectdraw.*; /* $Id: TicTacToe.java 2264 2013-11-26 04:31:39Z terescoj $ */ /** * TicTacToe game to demonstrate two-dimensional arrays. * * Jim Teresco, CSIS 120, Siena College * Based on example from CS 134, Williams College * Updated for CSC 252, The College of Saint Rose * */ public class TicTacToe extends WindowController { // Top, left corner of grid private static final int TOP = 40; private static final int LEFT = 40; // Number of rows in the grid private static final int NUM_ROWS = 3; private static final int NUM_COLS = NUM_ROWS; // Size of each grid square private static final int CELL_SIZE = 40; // Size of the marks to draw in the grid and offset from square edge private static final int MARK_SIZE = CELL_SIZE * 3 / 4; private static final int MARK_OFFSET = (CELL_SIZE - MARK_SIZE) / 2; // Length of the lines making up the grid private static final int LINE_LENGTH = CELL_SIZE * NUM_ROWS; // Denote value of a square to show who has played there. private static final int EMPTY = 0; private static final int X_MARK = 1; private static final int O_MARK = 2; // The grid contents private int[][] marks = new int[NUM_ROWS][NUM_COLS]; // Whose turn it is next private int nextMark = X_MARK; // Whether the game has been won or not private boolean gameOver = false; // Next player label private Text theLabel; /** * Begin execution by drawing an empty grid */ public void begin() { for (int i = 1; i < NUM_ROWS; i++) { new Line(LEFT, TOP + i * CELL_SIZE, LEFT + LINE_LENGTH, TOP + i * CELL_SIZE, canvas); new Line(LEFT + i * CELL_SIZE, TOP, LEFT + i * CELL_SIZE, TOP + LINE_LENGTH, canvas); } theLabel = new Text("X's Turn", LEFT, (NUM_ROWS + 2) * CELL_SIZE, canvas); theLabel.setFontSize(32); } /** * Draw an X with the given upper left hand corner to fill * a space MARK_SIZE x MARK_SIZE in the Tic-Tac-Toe board * * @param left the x coordinate of the upper-left corner of the X * @param top the y coordinate of the upper-left corner of the X */ private void drawX(int left, int top) { new Line(left, top, left + MARK_SIZE, top + MARK_SIZE, canvas); new Line(left + MARK_SIZE, top, left, top + MARK_SIZE, canvas); } /** * Draw an O with the given upper left hand corner to fill * a space MARK_SIZE x MARK_SIZE in the Tic-Tac-Toe board * * @param left the x coordinate of the upper-left corner of the O * @param top the y coordinate of the upper-left corner of the O */ private void drawO(int left, int top) { new FramedOval(left, top, MARK_SIZE, MARK_SIZE, canvas); } /** * Draw the appropriate mark in the appropriate row/col in * the Tic-Tac-Toe board * * @param row the row number (0, 1, 2) of the cell to mark * @param col the column number (0, 1, 2) of the cell to mark * @param mark what to mark, X_MARK or O_MARK */ private void drawMark(int row, int col, int mark) { if (mark == X_MARK) { drawX(LEFT + col * CELL_SIZE + MARK_OFFSET, TOP + row * CELL_SIZE + MARK_OFFSET); } else { drawO(LEFT + col * CELL_SIZE + MARK_OFFSET, TOP + row * CELL_SIZE + MARK_OFFSET); } } /** * Place the next mark at the click point and check for a win. * If the click is within a cell of the Tic-Tac-Toe board and * that cell is not already occupied, the current player's * mark is placed in that cell and we check to see if the player * has now won. * * @param point the Location where the mouse was pressed */ public void onMousePress(Location point) { double x = point.getX(); double y = point.getY(); // Ignore clicks outside the grid if (x > LEFT && x < LEFT + LINE_LENGTH && y > TOP && y < TOP + LINE_LENGTH) { // Figure out which cell in the grid was clicked in int col = (int) ((x - LEFT) / CELL_SIZE); int row = (int) ((y - TOP) / CELL_SIZE); // Make sure the grid is empty before adding a mark. // Also make sure the game is not already over if (marks[row][col] == EMPTY && !gameOver) { // Add the mark marks[row][col] = nextMark; drawMark(row, col, nextMark); // See if the game is won if (checkGameWon(row, col, nextMark)) { if (nextMark == X_MARK) { theLabel.setText("X Wins!"); } else { theLabel.setText("O Wins!"); } gameOver = true; } // see if it's a tie if (!gameOver && checkAllFilled()) { theLabel.setText("It's a tie!"); gameOver = true; } if (!gameOver) { // switch players if (nextMark == X_MARK) { nextMark = O_MARK; theLabel.setText("O's Turn"); } else { nextMark = X_MARK; theLabel.setText("X's Turn"); } } } } } /** * Check to see if the game is won as a result of a mark being placed * at position row, col. The game is over if an entire row, column, or * diagonal has the same pieces. * * @param row the row number (0, 1, 2) of the cell just marked * @param col the column number (0, 1, 2) of the cell just marked * @param matchMark the mark, X_MARK or O_MARK, just placed * * @return true if the current player now has won the game, false otherwise */ private boolean checkGameWon(int row, int col, int matchMark) { return checkForRowWin(row, matchMark) || checkForColWin(col, matchMark) || checkForDiagWin(matchMark) || checkForDiag2Win(matchMark); } /** * Check to see if the game is a tie: all positions are occupied, * but no one has won. * * @return true if all positions are occupied */ private boolean checkAllFilled() { for (int row = 0; row < NUM_ROWS; row++) { for (int col = 0; col < NUM_COLS; col++) { if (marks[row][col] == EMPTY) { return false; } } } return true; } /** * Check for a win in the given row by the player with the * given mark. * * @param row the row number to check * @param matchMark the mark, X_MARK or O_MARK, just placed * * @return whether the given row contains all matchMark entries */ private boolean checkForRowWin(int row, int matchMark) { for (int col = 0; col < NUM_COLS; ++col) { if (marks[row][col] != matchMark) { return false; } } return true; } /** * Check for a win in the given column by the player with the * given mark. * * @param col the column number to check * @param matchMark the mark, X_MARK or O_MARK, just placed * * @return whether the given column contains all matchMark entries */ private boolean checkForColWin(int col, int matchMark) { for (int row=0; row < NUM_ROWS; ++row) { if (marks[row][col] != matchMark) { return false; } } return true; } /** * Check for a win in the main diagonal by the player with the * given mark. This is the diagonal from the top left to bottom right. * * @param matchMark the mark, X_MARK or O_MARK, just placed * * @return whether the main diagonal contains all matchMark entries */ private boolean checkForDiagWin(int matchMark) { for (int i = 0; i < NUM_COLS; ++i) { if (marks[i][i] != matchMark) { return false; } } return true; } /** * Check for a win in the off diagonal by the player with the * given mark. This is the diagonal from the bottom left to top right. * * @param matchMark the mark, X_MARK or O_MARK, just placed * * @return whether the off diagonal contains all matchMark entries */ private boolean checkForDiag2Win(int matchMark) { for (int i = 0; i < NUM_ROWS; ++i) { if (marks[i][NUM_ROWS-i-1] != matchMark) { return false; } } return true; } }