CSCI 102 - Fall 2023 Fundamentals of Computation

HW 10

You will write a program that utilizes functions. However, this assignment is also a cumulative assignment that should push you to use all of what you have learned over the semester. It can be difficult so please start early and DO NOT succumb to any temptation to look for solutions or share code with others.

Writing Your Code

This assignment will again be submitted through Vocareum. To develop your code you can use any text editor/compiler you like. We recommend you use the virtual machine and get used to using Linux and developing, compiling and testing code on it. However, you can use the web-based editors/compilers we have referenced in the past.

3x3 (and eventually NxN) Tic-Tac-Toe

Description & Requirements

In this program, you will write a program to allow two players to play the classic game of Tic-Tac-Toe. The first player will be represented with Xs and the second player with Os. In this program we will provide you a correct and complete implementation of main(). As you study it you will see that main simply implements the high-level logic for the game but leaves the basic tasks to various functions (e.g. printTTT(), checkForWinner(), getInputAndUpdateGrid(), and checkForDraw()). Your only task in this assignment is to complete the 7 functions prototyped below. The main() function is complete and should NOT be changed.

void printTTT(char grid[], int dim);
int checkForWinner(char grid[], int dim);
bool getInputAndUpdateGrid(char grid[], int dim, char player);
bool checkForDraw(char grid[], int dim);

// 3 small helper functions for converting 1D <=> 2D indices
char getRCEntry(char grid[], int dim, int r, int c);
int idxToRow(int idx);
int idxToCol(int idx);

In all these functions, we pass the array (named tttdata in main() but named grid in all the input arguments) storing the Xs, Os, and blanks spots so that the functions can examine the game state and modify it if needed (e.g. getInputAndUpdateGrid() will actually change the contents of grid). Remember, you can modify the value of an array directly, unlike trying to modify a primitive like an int (which is passed by value). Refer to the class notes or your lab exercises for more details. We will use the character ? to represent blank spots, so that the user sees they are available. You must follow the guidelines and produce the return values that are provided in the comments above each prototype in the skeleton code. Those comments act as requirements just as much as this webpage does.

Getting the skeleton

We have provided a starter/skeleton code on Vocareum.

Winning

To win, a player must completely fill any row, column, or one of the two diagonals with their symbol (i.e. Xs or Os). As soon as this condition occurs for any player, the program should stop and print the correct message, either:

X player wins!

or

O player wins!

The program should then immediately exit.

Draws

We want to determine if the game will end in a draw and potentially end early, declaring a draw, without needing all squares to be filled in. We will define a draw as occurring when each row, column, and each of the two diagonals have at least one X and one O. This should be checked after each turn and, if satisfied, the game should stop and the following message should be printed:

Draw...game over!

The program should then immediately exit.

Importnat: While we could determine a draw even earlier (before one of each symbol exists in each row/column/diagonal), please just implement this algorithm as described above.

Parts of the Program & Grid Size

To start you can attempt to write the code for only a 3x3 tic-tac-toe grid. Get this working first. However, this part will only be worth 14 of 20 points. To be eligible for the full 20 points you must make your code work for a generic n x n tic-tac-toe grid (where n is an odd integer from the set 3, 5, 7, 9, or 11). The largest grid will be 11x11 and thus we will need an array of at most 121 locations (this array of 121 locations is declared at the start of main()).

Grid size

We want you to try the n x n version of the program to have you practice developing loops that generalize patterns. We will use the variable name dim to specify n, which stands for the dimension of one side of the square grid. And all of your code should be generalized to work for a given value of dim. Thus, a majority of your code will use values that are some function of dim.

While we allocate the tttdata/grid array for size 121 (i.e. 11x11), we will not use the entire array for smaller dimensions. The user will enter the dimension via cin at the start of the program and then main will create the initial array contents for that size. For example, if the user enters a dimension of 3 then your could should only use entries 0 through 8 in the tttdata/grid array. When you test your code you will always need to enter that dimension value first before the game can truly start.

Output

Your grid must be drawn according to the examples shown below:

 X | O | ?
-----------
 ? | O | X
-----------
 ? | ? | X
 X | O | ? | ? | ?
-------------------
 ? | O | X | X | ?
-------------------
 ? | ? | X | O | ?
-------------------
 ? | X | O | O | ?
-------------------
 ? | ? | X | O | ?

1D to 2D Array access

The grid argument to each function is a 1D array. However, we can visualize it as a 2D array if we let the first dim elements be the first row, the next dim elements be the 2nd row, and so on. For a 3x3 grid, the array contents:

0 1 2 3 4 5 6 7 8
X O ? ? O X ? ? X

would correspond to a tic-tac-toe grid of:

 X | O | ?
-----------
 ? | O | X
-----------
 ? | ? | X

Put more concretely, below we show the array index correspondence to the given locations in a 3x3 grid:

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

A 5x5 grid would have this correspondence to the 1D-array of 25 elements:

  0 |  1 |  2 |  3 |  4
------------------------
  5 |  6 |  7 |  8 |  9
------------------------
 10 | 11 | 12 | 13 | 14
------------------------
 15 | 16 | 17 | 18 | 19
------------------------
 20 | 21 | 22 | 23 | 24

Getting Input

At each turn you should prompt the user to enter the square number that they want to choose for their location. Use the corresponding indices shown above (i.e. 0 is the upper-left corner, etc.).

Important: If they type in a value that is out of range (i.e. not between 0 to dim^2-1) then return true from the getInputAndUpdateGrid function (and false otherwise). Similarly, if they type in a square that is already occupied with an X or O, then ask the user to choose again (and continue doing so until they choose a blank square or given an invalid location). Read the comments for getInputAndUpdateGrid.

Using math to determine row and column indexing

  1. Consider the problem of iterating through row 0 of the grid. (Let us assume we count rows and columns starting at 0 like an array not 1).

For a 3x3 grid:

For a 5x5 grid:

  1. Can you generalize the pattern to enumerate the array indices that form the i-th row? The i-th row starts at what index in the grid array? Put your answer in terms of i and dim.

Now consider the problem of iterating down a column of the grid.

Can you generalize the pattern? To iterate down column i, we should start at what index in the array? Then we should take steps of what size? Put your answer in terms of i and dim.

  1. Finally, consider how, if you had a given row and column location (i.e. row and col) you might determine what index in the 1D-array corresponds to that location.

Can you generalize the pattern? Given row and col numbers could you find a mathematical expression to convert to the 1D index of the square that corresponds to that row and column? Note that this relationship should help you visit the diagonals because it might be easier to consider generating two separate indices: row and col and then converting those two values to the corresponding 1D array index.

  1. To summarize you should be able to complete the following conversions using simple mathematical expressions:

Use the answers you found above to complete the functions idxToRow(int i, int dim); and idxToCol(int i, int dim);. With those functions, you can call them anywhere in your code that you may want to conver a 1D index to a 2D row/column index, if it is helpful.

Use the answer you just found to complete the function getRCEntry(char grid[], int dim, int r, int c);. Call this function anywhere you might find it convenient to generate two separate indices (row, column) and then to access the corresponding character (X, O, etc.) from the grid at that row/column index.

Be sure to indent your code correctly and add comments describing major chunks of your code.

Hints

Build and Run

Since we have taught you to compile your code using g++ and run your code on the command line, we have removed the Build and Run Scripts. You will need to compile and test your code on your own in the Terminal window/area at the bottom of the Vocareum Editor window. The executable program must be named tttn. The Submit button will also NOT compile your code but expect that it is already compiled successfully.

Testing for 5x5 and Beyond

When you click Submit we will not run any tests for 5x5 or above when you submit, only when we grade. So that means you must test these larger (5x5 and greater) cases yourselves for the various options such as X winning, O winning, draws, etc.

Submitting your code

When you believe your program is finished login to Vocareum, start the assignment, and in the upper-left of the resulting window, click New..File. A textbox will appear where you can type in the filename (leave the work/ portion there and) just type tttn.cpp. In the left window pane you should now see tttn.cpp appear (it must be named tttn.cpp) and you can click on it to open it. A blank window should appear on the right. Cut/paste your code from your other tab/window to this window and you can click Submit. Alternatively, rather than clicking New..File, you can just use the Upload button to upload the file if you have it on your VM (but the file must be named tttn.cpp).

When you click submit it should run a few automated tests to let you know if it compiled and passed some basic sanity checks. Look at the output to ensure things are working. If not, you’ll need to modify your code in Vocareum or back in your VM/PC and then re-upload or update the code back in Vocareum.

Be sure you click submit otherwise your code will just be saved, but not submitted!!

Please post any questions about using Vocareum on EdStem.