Computer Science 120
Introduction to Programming
Spring 2012, Siena College
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 1849 2012-04-11 20:27:21Z terescoj $ */
/**
* TicTacToe game to demonstrate two-dimensional arrays.
*
* Jim Teresco, CSIS 120, Siena College
* Based on example from CS 134, Williams College
*
*/
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 clicked
*/
public void onMouseClick(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;
}
}