Coursework: Week 6, Oxo

The coursework this week counts 25% towards your credit for the unit. The main purpose is to get to grips with structures, passed using pointers. You may need information from the lecture notes up to the objects chapter. Optionally, you may need to look ahead to the I/O chapter for input/output issues.

understand the problem and skeleton

Your task is to write a program which plays the game Noughts and Crosses (also known as Tic-Tac-Toe). Here is the skeleton for you to start from:

oxo.c
Makefile

Compile and run

As usual:

make
./oxo

Using make is highly recommended because (except on Windows) it will add the extra debugging option -fsanitize=undefined which will detect array indexes which are out of bounds. That option will be used for marking.

Two stages are recommended

There are logic and user interface functions. You need to develop both sets to make a playable game. It is strongly recommended that you first develop the logic functions, getting all the tests to pass as usual, then optionally tackle the user interface. (You may, however, want to develop the display function during stage one, to help with visualization.)

Only stage one is assessed

Assessment is by automarking, which only tests the logic functions. Getting the user interface to work is an optional extra. It is recommended that you submit stage one for marks, before starting on stage two. It is up to you whether you submit stage two - it would just be to archive your program as part of your portfolio.

The main function supports the stages

The program can be run with no arguments, ./oxo, to run the tests. Later, the user interface functions can be filled in, and the program can be run with ./oxo X or ./oxo O to play the game, with the given player having the first move.

Named constant types are used

Constants X, O, N are set up to represent which player owns a cell. These constants have the type enum player, and an abbreviation player is set up for this.

The type enum player is a synonym for int. It makes the program sightly more readable by documenting the fact that a particular integer variable is intended only to hold player constants. But the -fsanitize=undefined option may at least be able to detect if you use anything outside the range 0..2.

Constants like these can be set up in at least three ways:

#define X 0
#define O 1
#define N 2

const char X = 0, O = 1, N = 2;

enum { X, O, N };

Using #define works, but (a) it is clumsy, with one line per constant (b) it uses textual substitution, so the names are not known to the compiler, which makes errors and warnings poorer and (c) using #define for anything other than constants is extremely dangerous, so it is a good idea not to use it for anything (normally). Using const is confusing, because it means read-only (you can't explicitly change the values) not constant from the compiler's point of view, so, for example, the names can't be used in switch statements. Using an enumerated type seems best, for integer-compatible types.

Moves are counted

The game structure contains a count of the number of moves made, mostly in order to tell when the game is over. Technically, this is redundant information, because the number of moves can be deduced from the grid, but it seems a little more convenient to count the moves rather than analyze the grid.

The game structure is reused

The function newGame completely re-initializes an existing game structure so that a single structure could, in principle, be reused for a sequence of games.

Validity checking is split

Validity checking is carried out by the two functions valid and printInvalid. The valid function does the logic, returning a constant, and is tested. The printInvalid function handles the printing, including defining the strings to be displayed. A change of language from English to something else would not affect the valid function or its testing.

The line function provides support

The function line seems trivial and unnecessary. However, it can be used to make the win function slightly more readable, if you want.

The win function is troublesome

Checking for a winning line is probably the hardest part of the assignment. There isn't sufficient regularity in the eight winning lines for there to be much gain in trying to handle them with a loop and a formula, and the result probably wouldn't be very readable.

The best bet is probably to explicitly handle each of the eight lines one by one, but try to deal with each in one line of code, to keep the win function short.

There are three user interface functions to write

The functions main, show, printInvalid have been provided. The display, ask and play functions need to be filled in to make the game playable.

There are no tests for these. The comments are all there is to specify what they do.

develop the program

You know what to do by now.

write something extra

Develop your own extra program as usual, for the second half of the marks. Ideally, you should use the same development technique. Design ultra-short functions, with logic separated from input output. You will have to develop your own tests for the logic functions. It is normal to write the tests for a function at the same time as the function itself. Don't leave the testing until afterwards. You don't need as many tests as in assignment skeletons, because it is only your confidence in correctness that is at stake.

submit

Submit oxo.c and your extra program, if any, along with a brief readme file.