Tic-Tac-Toe

Due: Must be checked off or submitted on Canvas by the end of Feb 28.

Goals

  • Put together everything we've learned so far!
  • Create many standard gameplay devices: game board, game state, textual information, user interaction sensing.

Tic-Tac-Toe

tic-tac-toe game We are going to create a program to play Tic-Tac-Toe, a two-player game that is played on a 3 x 3 grid where a player wins by placing three of his or her markers in a row. If you are unfamiliar with the game or need a refresher, feel free to browse the Wikipedia article.

Step 1: Design the Game Board

Design your game layout. The image shown on the right is just an example, and we encourage you to tweak the layout and look to your liking. At minimum, you will need the following:

  1. 3 x 3 grid of squares for the game board.
  2. An indication of whose turn it is.
  3. An indication of the game result.
  4. A reset button of some sort.

We suggest that you create a separate function to draw your board, possibly named drawBoard(), that will be called from draw(). If you would like to, you can also parameterize this function to make your code more flexible should you choose to later alter your layout.

Step 2: Design Your Game State

We will need to declare and initialize some variables to keep track of the current "state" of the game. You will definitely need to track the state of the board (i.e. who has played what and where), which we suggest you do using an array of size 9. The following questions are meant to help you think about the state of the game, though the exact implementation is up to you:

  • How do you know whose turn it is?
  • How many different "states" can each square of your board be in?
  • How many different "states"/"stages" can the game be in? (e.g. game ongoing, Player 1 wins, etc.)

Step 3: Display the Board State

Before we implement user input, let's make sure we can display the correct symbols on our grid. You should have an array of size 9 that holds the current state of the board. We recommend using the following layout to relate the board to the array:

Array index:  [ 0 | 1 | 2 ]
              [ 3 | 4 | 5 ]
              [ 6 | 7 | 8 ]

That is, element 4 of your array holds the current state of the middle square of your board, and element 5 of your array holds the current state of the square at row 2, column 3.

Now we need to display symbols to show the player moves ('O' for Player 1, 'X' for Player 2). Our suggestion is to use a separate char array:

char[] symbols = {' ','O','X'};

With this in your code, then symbols[0] will display a space (no symbol), symbols[1] will display an 'oh', and symbols[2] will display an 'ex'. Make sure that this works well in combination with the values you store in your array.

To do the actual display on your drawing canvas, we recommend using the text() function inside of nested for-loops. The for-loop variables will help you calculate the correct coordinates to pass to text(), but you will also need to use them to access the correct index of your game board state.

Step 4: User Click Recognition

We can recognize user clicks using the mousePressed() function in combination with the values of mouseX and mouseY. If you are unsure about how to proceed, see the "Puzzle App" lectures or the "Birthday Visualization" lab.

It is easiest to start with the Reset button. Create a separate reset() function to call when the user clicks within the Reset button region of the drawing canvas. This should set all of the game state variables to the values they would hold at the very beginning of a game of Tic-Tac-Toe.

When the user clicks within the game board, your program should correctly identify which square (0-8) was clicked, similar to how the Birthday Visualization recognized which month and day was being hovered over. Write some test code so that you can verify correct behavior using either println() or text().

Once that is confirmed to be working correctly, you can move on to "playing that square." Remember that we only want to update our game board if (1) the user clicked on a board square and (2) that square hasn't been played previously. Similarly, when do we want to change players?

Step 5: Checking for Endgame

After each valid play, your program should check to see if the end of the game has been reached (a player wins or there is a draw). Create a separate function to do this.

Endgame conditions can be checked using just the state of the game board. There are certainly some more clever ways to do this, but we are fine with the "brute force" method of checking every possible winning combination one-by-one.

  • How many different ways can you win in Tic-Tac-Toe?
  • How do you check for a winning combination?
  • How do you differentiate between a Player 1 and Player 2 win?
  • How do you know if a Draw has been reached?

Once the endgame has been reached, your game state should be updated to reflect this. How will this affect gameplay? Should the user still be allowed to play squares on the game board? Note: we still want to allow the user to click the Reset button while at the endgame and then start a new game.

And there you have it! You've put together a working game! Play a few rounds with a partner or friend in order to make sure your program behavior matches what you expect to happen. This is a process called testing that is really important. Make sure you take your program through every possible game state!

Example Solution

Tic Tac Toe gif A working solution is shown on the right. You do not need to follow this exact layout.

In order to better visualize what's going on, extra code was included to display a mouse symbol in the lower-left corner of the image whenever a mouse button is clicked.

Here's what's being shown in the gif:

  1. Clicking all around the edges of the grid, showing that clicks outside of the grid are ignored.
  2. One 'O' played, which switches the Player and symbol text, followed by a mid-game Reset.
  3. A game is played through where Player 1 wins. A few clicks are made on the grid once the game result is displayed, showing that these clicks are ignored.
  4. After a reset, another game is played through where Player 2 wins. Again, clicks are ignored after the win condition is reached.
  5. After a reset, another game is played through that ends in a draw. Again, clicks are ignored after the win condition is reached.


Checkoff

  1. Run your program for your TA to see.
    • All required game elements (game board, turn indicator, result indicator, and reset button) are appropriately laid out on the drawing canvas.
    • Clicks on the reset button work correctly both in the middle of a game and from the endgame state.
    • Clicks on the game board are correctly detected and place a symbol in the correct position on the game board that can't be overwritten and doesn't disappear later.
    • The turn indicator and the next symbol placed change after a valid play.
    • Endgame situations (wins and draws) are correctly detected and indicated on the screen while disabling further game board clicks.
  2. Show your code to your TA.
    • Important lines or blocks of code should be commented.
    • There should be a block comment at the top describing what the program does and should include the name of your group members.
    • All functions, loops, and code blocks should be properly indented.