Your code will involve writing a TTTLine class that represents vertical, horizontal, and diagonal lines of cells on the game board. Each will keep track of how many of its cells are owned by X and by O. The class contains a countCells() method to update the X and O counts, and a winner() method that invokes countCells() and then checks the ownership counts to return one of the TTTCell constants NO_ONE, X, O, or DRAW.
A win occurs when one of the lines contains three Xs or three Os. A draw occurs when each line contains both and X and an O. An array instance variable that contains eight TTTLines is declared in the TicTacToe class. Two methods, playerWins() and gameDrawn(), are added to the TicTacToe class to check for the win or draw conditions. These methods are invoked in the updateStatus() method to determine win or draw user messages and control enabling of the "Start New Game" button.
The TTTLine class should be defined in its own .java file. It does not need any imports. The class extends the Object class, so you can omit the extends clause.
This class uses an array instance variable cells to keep track of the cells in a line. It should be declared as follows.
TTTCell[] cells = new TTTCell[3];This creates an array that can contain three TTTCell objects. In addition, the class declares two int instance variables xOwns and oOwns. These variables will be used to record the number of cells that are marked with X and O. They do not need to be initialized.
In the declaration of the cells variable, the array itself is
initialized, but its entries are not because there is not enough
information to do so.
Java just places a
To make a TTTLine useful, you need to put cells into the array. The best place to do that is in the constructor. It should have three TTTCell parameters. The statements in the constructor should just assign the parameters to the three entries of cells, which are cells[0], cells[1], and cells[2].
There are two methods defined in the TTTLine class. The first, countCells(), has no parameters and void return type. It is only used inside the TTTLine class, so you do not need to declare the method as public. The method counts the number of cells in the line that are marked by X and O and records the counts in xOwns and oOwns. At the beginning of the method, both of these counts should be initialized to 0. Then loop through the cells in the line and increment the appropriate count, depending on the mark in the cell. This can be done with a for loop with the following structure.
for (int i = 0; i < cells.length; i++; i++) { TTTCell c = cells[i]; statements to increment the appropriate count for c }In order to increment the appropriate count, you will need if statements that test whether c.getMark() is TTTCell.X or TTTCell.O.
The second method, winner(), has no parameters. It returns one of the int constants TTTCell.X, TTTCell.O, TTTCell.NO_ONE, or TTTCell.DRAW, indicating who has won the line. Since this method is invoked by the TicTacToe class, it must be declared to be public.
The first statement of the method should invoke countCells() to set up the xOwns and oOwns counts. Then you need if statements to implement return values as indicated in the following table.
Situation | Return |
---|---|
xOwns == 3 | TTTCell.X |
oOwns == 3 | TTTCell.O |
xOwns > 0 and oOwns > 0 | TTTCell.DRAW |
all other cases | TTTCell.NO_ONE |
After completing the TTTLine class, you should compile it to check for syntax errors. You will need to wait until changes are made in the TicTacToe class are made to check for errors in logic.
To simplify setting up the entries in lines, you should write a short method getCell(). Like the method of the same name that you have already written, this method returns a TTTCell. The difference is that this method has two int parameters, r and c, that specify a row and a column in the tictactoe board. The method should just invoke the original getCell() method, returning getCell(3*r + c).
Then in the setupBoard() method, assign a new TTTLines array, with initialization, to lines. You should have three lines for rows of the game board, three for columns, and two for diagonals. In the calls to the TTTLine constructor you will need to specify three cells using getCell(). Take care when specifying the rows and columns for the cells.
for (int i = 0; i < lines.length; i++) { TTTLine line = lines[i]; if statement that returns a boolean value } default return statement
The playerWins() has an int parameter plyr for specifying a player (TTCell.X or TTTCell.O). It checks a line by sending it a winner() message. It returns true if the return value from winner() is equal to plyr. The default return statement is executed only when winner() returns something other than plyr for all lines. It should just return false.
The gameDrawn() method has no parameters. If any of the winner() returned values are not TTCell.DRAW then the method returns false. In the default case it returns true.
To detect the end of a game, you then need three if statements. The boolean conditions in the first two just invoke playerWins() with parameters myMark and userMark respectively. The boolean condition in the third just invokes gameDrawn(). Each if statement contains two nested statements, one to set the user message and one to assign true to gameOver. The user message is set by sending a setText() message to the message variable. The parameter should be one of the following.
Finally, if the gameOver local variable is true, you should exchange marks for the myMark and userMark instance variables and assign TTTCell.NO_ONE to the toPlay variable. You should also add a statement to enable the "Start New Game" button if gameOver is true and disable the button otherwise.
After you have added code to updateStatus(), recompile your TicTacToe class and run the applet with appletviewer. Your applet should look and behave like the following demonstration applet. You should play games where you win, lose, and draw, watching for the appropriate messages and chacking the enabling of the "Start New Game" button. Since the computer does not yet have a good play strategy, it will be easy to win a game. You will have to make dumb moves to lose or draw. The computer will always select its move in the lowest possible row. Within that row, it will select the rightmost cell.