Java Programming Challenge: Adding Arrays to the Simple Tic-Tac-Toe Program

By Doug Lowe

This Java programming challenge builds on the previous challenges in this Tic-Tac-Toe series and tests your ability to use arrays — two of them, in fact.

In Java Programming Challenge: A Simple Tic-Tac-Toe Game and Java Programming Challenge: Adding Class to the Simple Tic-Tac-Toe Program you are challenged to write a program to play the simple game of Tic-Tac-Toe.

As a game, Tic-Tac-Toe cries out for the use of an array to represent the status of the game. Without arrays, you must use a separate variable to represent each square of the board. With an array, you can use a single variable to represent all nine squares.

This programming challenge is simple: Write an improved version of the program that makes use of arrays. You must use at least two arrays in your solution:

  1. You must use an array to represent the board. Most likely, you will want to use a one-dimensional array with nine elements, as follows:

     0 | 1 | 2
    ---|---|---
     3 | 4 | 5
    ---|---|---
     6 | 7 | 8

    In other words, the top left square (A1) is stored in array element 0, and the bottom right square (C3) is stored in array element 8.

  2. You must also use an array to represent the eight possible three-in-a-row vectors.

    You can then use this array to determine whether either player has won the game. Most likely, you will want to use a two-dimensional array for this task. The array will hold eight, three-element arrays, each of which represents the three indexes of a particular three-in-a-row vector.

    The complete array would contain the following data:

       0 1 2
       3 4 5
       6 7 8
       0 3 6
       1 4 7
       2 5 8
       0 4 8
       2 4 6 

     

One additional requirement of this program is that the TicTacToeBoard class you create for this challenge must be completely compatible with the class you created for the preceding challenge. In other words, it must implement the exact same methods. For your convenience, these methods are repeated in the following table.

Because these methods refer to the squares of the tic-tac-toe board using row-column designations such as A1 or B2, your implementation will need to map these designations to index numbers. For example, if the string A1 is passed to the playAt method, the program must mark the play at index 0 in the array.

The TicTacToeBoard Class
Constructor Description
TicTacToeBoard
Creates a new TicTacToeBoard with all squares empty.
Method Description
void reset() Resets the status of each square to empty.
void playAt(String square, int player) Marks the specified square (A1, A2, A3, B1, B2, B3, C1, C2, or C3) for the specified player (1 for X, 2 for O). Throws IllegalArgumentException if square is not one of the allowable values, player is not 1 or 2, or the specified square is not empty.
int isGameOver() Determines whether the game is over. Returns 0 if the game is not over, 1 if X has won the game, 2 if O has won the game, and 3 if the game is a draw. The game ending conditions are as follows:
1: If any row, column, or diagonal contains all X’s.
2: If any row, column, or diagonal contains all O’s.
3: If there are no empty squares and neither X nor O has won.
int getNextMove() Returns an integer representing the next move for the computer opponent. This method should make a rudimentary effort to select a good move, according to the following strategy:
* If the center (square B2) is empty, play the center square.
* If the center is not empty but any of the four corners (squares A1, A3, C1, or C3) are empty, play one of the corners (it doesn’t matter which).
* If the center is not empty and no corners are empty, play one of the edges (squares A2, B1, B3, or C2).
String toString() Returns a string that represents the current status of the board. The string includes new-line characters to display the rows as well as separator lines on separate console lines, as in this example:
O | | O
—|—|—
| X |
—|—|—
| X |

As a further challenge, for this version of the TicTacToeBoard challenge, the computer player should use a more intelligent strategy against the human opponent. Determine the computer’s play as follows:

  1. If it is possible for the computer to win on its next play, the computer should play in the winning square.

  2. If it is possible for the human opponent to win on his or her next play, the computer should play in the human opponent’s winning square to block the win.

  3. If the center square is available, the computer should take the center square.

  4. If any corner square is available, the computer should play in one of the available corners.

  5. The computer should play in an available edge square.

Note that to implement this strategy, you’ll need to develop a routine that can determine if either player can win on his or her next move. To do so, you’ll have to look at each of the eight, three-in-a-row vectors to determine if the vector contains one empty square and if the each of the other two squares contain marks for the same opponent (that is, two X’s or two O’s).

You can do that by using 0 to represent an empty square, 1 to represent an X, and 2 to represent an O. But that would require pretty complicated logic – something like this would be required, assuming that s1, s2, and s3 are integers containing the contents of the three squares of one of the eight, three-in-a-row vectors:

if (s1 == 0 & s2 == 1 & s3 == 1)
   // X can win by playing in s1
if (s2 == 0 & s1 == 1 & s3 == 1)
   // X can win by playing in s2
if (s3 == 0 & s1 == 1 & s2 == 1)
   // X can win by playing in s3

So here’s a tip: Instead of using 0, 1, and 2 to represent an empty square, an X, and an O, use the prime numbers 2, 3, and 5 instead. Then, to determine whether a player can win on a given vector, simply multiply the three values for that vector. If the result is 18, X can win (233=18). If the result is 50, O can win (255=50).

Note also that although this strategy is an improvement over the strategy employed for previous versions of the program, it is still not a perfect strategy: You can still beat the computer with the right sequence of plays. If you want an additional challenge, consider what additional strategy would be necessary to make the game unwinnable, and then devise a way to implement the new strategy.

You can find the solution to this challenge on the Downloads tab of the Java All-in-One For Dummies, 4th Edition product page.

Good luck!