Lab 01: Tic-Tac-Toe

Problem overview

Tic-Tac-Toe is a simple game that usually is based on a 3X3 grid (but not always, see below). Although simple, Tic-Tac-Toe is known as a "zero sum game" since its impossible for two informed players to win a game; the ideal strategy always results in a tie.

That said, with two smaller children who often play this game on kids menus (pre March 2020), one can win more often that you'd think against a non-informed player. The goal of this lab is to implement both an interactive and automatic Tic-Tac-Toe program to see for yourself.

Inspiration

Note, this problem is very loosely based on the Checkboard Lab 1 from Dr. Plank here, although the syntax goals are pretty different. By starting with an intuitive game, hopefully it will help with the more automated version that will be required for Part 2 of the lab. It is also based on what will be useful for Lab 3 (a similar but different game).

Input / Output

You will ultimately be given an integer and a series of characters from standard input in the follow format:

N
grid size N
X X O
...

For example, here is a 3X3 grid where X wins:

3
X X X
O X O
O O -

Your program should output the following:

X wins

For another example, here is a 4X4 grid where no one wins:

4
X X X O
O X O X
O O X O
X O X O

Your program should output the following:

Tie

Part 1

Develop a simple C++ program called "toe.cpp" that asks a user for the grid size, and then allows two players to interactively play Tic-Tac-Toe. This program must be able to:

  1. Use formatting (printf/cout) to provide an intitutive and well formatted board each turn to the players
  2. Prevent a player from entering an invalid cell
  3. Prevent a player from changing a previously chosen cell
  4. Determine if a player has won or not after each choice
  5. If no cells remain, report there has been a tie.
For purposes of this practice each run of "toe" will allow the players only one potential game. Subsequent games will require running "toe" again.

Part 2

Develop a simple C++ program called "toecheck.cpp" that will read in a sample input file as described above from standard in and report either "X wins," "O wins", or "Tie." We will provide sample files and a slightly different way to test for correctness that we'll cover later in lab.


Hints

  1. Although it is possible to complete this lab using a 1D array, you are allowed to use a 2D array to make accessing the rows and columns more intuitive. The second element in the 2nd row is found at A[c + 2] but the same memory address can be achieved using A[1][1] (we use 1 since we count from zero in C/C++).

Rubric

We will grade your submission relative to the rubric below.

+2    Code is well formatted, commented (inc. name, assignment, and overview), with reasonable variable names
+3    Uses formatting (printf/cout) to provide an intitutive and well formatted board each turn
+3    Prevents a player from entering an invalid cell
+3    Prevents a player from changing a previously chosen cell
+3    Works as intended on a 4X4 board (4 in a row wins)
+3    Works as intended on a 5X5 board (5 in a row wins)
+5    Correctly determines if a player has won or not after each choice
+8    Test cases successfully solved (1 point each)

We realize that this is quite a step up from the simple prelab, so please start early. We also realize that come edge conditions are harder than others, e.g., detecting a verticle win. Our test cases will be somewhat evenly split between horizontal, vertical, diagonal wins as well as ties. So if you are not able to get certain tests to work you still should receive most of the points if other cases work in your interactive and tester programs.

As potential hints, this is what my program does on a high level:

  1. Uses a fixed 2D array of chars, each of which are initialized to ' '
  2. Has a "turn" counter to keep track of how many cells have been chosen. You can use the mod operator (%) on a similar variable to see if its even (e.g., X) or odd (e.g., O).
  3. I honestly made my code a little more complex than it needed to by testing all "win" conditions using just a single nested for loop (vs. checking for horizontal, then vertical, then diagonal). I assume many of you will do each of these separately and that is totally OK as long as it works.
  4. There are a few methods, but I think the simplest is to look for a "run" of size equal to the board. For example, lets consider the first row. For a win to exist the first character (A[0][0]) must be filled ** AND ** the n-1 cells to its right must also be equal to that first character, aka A[0][0]). In my implementation if A[0][0] was filled I initialized a variable "run" to 1, and incremented run if A[0][j] (for j = 1 to < size) was equal to A[0][0]. If run was 4 the player who placed A[0][0] won. Its also fine to initialize run to 0 and use board_size - 1 to determine a win. Let us know if you have questions/concerns.
  5. Although somewhat counter-intuitive, the left top to right bottom diagonal is the easiest since A[i][i] must equal A[0][0] for all i = 1 < size. So the diagonal check is only a single for loop. Think on your own about how to change this for the top right to bottom left diagnonal.

Testing your code prior to submission

To faciliate testing, you were previously asked to clone the course Github repository as follows:

git clone https://github.com/semrich/CS140-21.git cs140

For this assignment, update this clone by using the following:

git pull

To test your solution against ours, type:

make test

Submission

To submit your solution, you must submit a single archive (.zip or .tar.gz) on Canvas prior to the deadline.

Note: Although submission will be faciliated by Canvas, we will compile and test on EECS lab machines!

If you develop your solution elsewhere please make sure it works on the lab computers prior to the deadline