Project #2 CMSC 389C
Due:  Sunday 1/22 at 11:00PM C++ Programming

Tic-Tac-Toe Virtuoso

"The Only Winning Move is Not to Play"

Objective

To practice many of the features of C++ including classes with dynamically allocated members, making use of the Standard Template Library, etc. Hopefully you'll use most of the features and concepts that we've gone over in class.

Overview

As you surely know, Tic-Tac-Toe is an amazingly interesting and difficult game. OK, not really. Just in case you somehow made it into college without learning how to play, you can read all about the game by clicking this link..

Our project will involve two phases and then an optional "extra credit phase":

  1. Write a class called BoardState that represents the state of the board at a particular moment during the game. Please note that we are not assuming the board is 3 by 3. Our board could be 2 by 2 or any larger (square) size.
  2. Write a class called CompleteStateGraph that literally contains the entire state graph for every possible game. This will be a directed graph. Each node in the graph will contain a board-state and a vector of pointers to other nodes. Important: Our graph will never contain two nodes with the same BoardState!

    Below is a diagram of the CompleteStateGraph for all possible games played on a 2 by 2 board:

  3. OPTIONAL EXTRA CREDIT: Write a class called PrunedStateGraph that represents the state graph but without any paths that result in a win for player 'X'. Note that for the 2 by 2 game, this results in an empty graph, since every possible game results in a win for player 'X'! However on a larger board there are many scenarios where 'X' does not win, and so we will construct a graph that depicts all of them.

Getting the Project Files

Go into your personal directory, and from the command prompt, type:

cp -r ../../public/P2 .
(Notice the "space dot" at the end. Also, don't forget to use the -r option.)

That should give you the necessary files in a directory called P2

Part I: BoardState Class

A BoardState object represents the state of the tic-tac-toe board at any one particular moment. Each position on the board can either be blank, contain an 'X', or contain an 'O'. The BoardState is also responsible for remembering whose turn it is. The board can be a square of any size: 2 by 2, 3 by 3, 4 by 4, 100 by 100. You'll need to allocate your data members dynamically. Note that the BoardState does not know anything about the rules of the game, so it would be perfectly happy if all of the board positions had X's in them or some other absurd configuration. Important: The BoardState is not a Node. We have a separate Node class that will be used to wrap a BoardState when we construct the graphs. The BoardState does not keep pointers to other BoardStates, it just stores the configuration of the current board.

We have included a header file (BoardState.h) that is mostly filled in. What you need to do is to fill in the private data members that you will use to store the state of a board. You are free to store your data any way you choose, but you are only allowed to use primitives or pointers (or pointers to pointers). The Board should keep track of where the X's and O's are, and should also keep track of whose turn it is. That's all.

After deciding how you will store the data for the BoardState, you will write the implementation file, BoardState.cpp

Restrictions on the BoardState class:

  1. You may not #include anything anywhere, other than #including BoardState.h in BoardState.cpp. This rule applies to both BoardState.h and BoardState.cpp. Absolutely no #includes other than #include BoardState.h
  2. You may only use primitive types or pointers (or pointers to pointers) in the implementation of the class. Arrays are fine, but no strings, no vectors, no maps, nothing like that at all.
  3. You may not add any public members to the class, and you may not modify any of the function declarations that are already there.

Here are the specifications for the functions you must implement in BoardState.cpp:

Part II: CompleteStateGraph Class

The CompleteStateGraph is a directed graph that depicts the flow of every possible game of tic-tac-toe for a given board size. The graph pictured above is the complete state graph for 2 by 2 tic-tac-toe.

We have provided the header file CompleteStateGraph.h. Do not modify it in any way! Your job will be to write the implementation file, CompleteStateGraph.cpp.

Before you get started on the Graph, you should know that we already wrote a Node class for you! Please take a look at the files Node.h and Node.cpp. They are finished -- don't change them.

A Node consists of a BoardState together with a vector of pointers to other Nodes. Sounds like a directed graph, doesn't it!? Look at the picture above that shows the CompleteStateGraph for size 2 by 2. Each of the ellipses represents a Node.

Note that the CompleteStateGraph has a the following private member:

Node const *start;

As you might guess, this will be a pointer to the Node that holds the empty board representing the beginning of the game.

Here are the specifications for the functions you must write in the CompleteStateGraph class:

EFFICIENCY REQUIREMENT
Your implementation must be reasonably efficient. If you are traversing every possible scenario over and over again, then your code will run too slowly and we will not be able to test it. If your code times out during any of the release tests, you will receive 0 points for the test.


Part III: OPTIONAL EXTRA CREDIT: PrunedStateGraph Class

This part is exactly the same as the previous part, but you will be constructing a different graph. The graph we require for this class consists of all possible games that do not result in a win for player 'X'. In other words, the terminal nodes for this graph will consist of only boards where player 'O' has won, or boards that are completely full (and 'X' has not won.) Note that for the 2 by 2 game, this is an empty graph, since 'X' always wins. However for larger sizes there are plenty of paths where either 'O' wins or nobody wins.

The interesting thing about tic-tac-toe is that constructing this pruned graph will not eliminate any of the choices that player 'X' has at any juncture! That means that tic-tac-toe is a game that player 'O' cannot lose. We use this fact together with your completed PrunedStateGraph to run a little program (driver2) that plays a flawless game of tic-tac-toe. You get to be player 'X' (so you go first) and the computer will be player 'O'. The computer will always choose moves that are on the PrunedStateGraph, so it cannot lose, no matter what moves you choose! Try out driver2 and I'm sure you'll be captivated for hours as your computer humbles you with it's flawless play.


Using the Makefile (Provided)

For this project there are three public tests.

To run one of the public tests, type either "make pub1", "make pub2", or "make pub3".

I also wrote two small programs that might be useful in testing/debugging your code:

  1. Type "make driver1" to run a program that uses your ComleteStateGraph class. It will play games of tic-tac-toe where each move is selected randomly from the possible moves at that juncture, based on your graph.
  2. Type "make driver2" to run a program that uses your PrunedStateGraph class (which only makes sense if you have implemented the optional extra credit part.) It will let you play against the computer. You get to go first, so you are player 'X'. Since the computer's moves will be selected from the pruned version of the graph, the computer cannot lose! It's really very frustrating.


Submitting


Grading

This assignment is worth 100 points. If you successfully complete the extra credit portion, you could earn as much as 15 extra points (so your grade could be as high as 115/100). Here is how the tests break down:

Public tests:

Release tests:

Web Accessibility