Tic-Tac-Toe, also called noughts and crosses, is a pencil-and-paper game for two players, X and O, who take turns marking spaces in a 3x3 grid. Player X usually goes first. The player who succeeds in placing three respective marks in a horizontal, vertical, or diagonal row wins the game. Figure 1.

Tic-Tac-Toe leads always to a draw, if both players play reasonable. Its apparent simplicity makes it easy to implement. Yet, a more detailed analysis reveals that Tic-Tac-Toe offers some interesting challenges not only in terms of combinatorics, but also in terms of automation when we wish the computer to take the role of the opposing player. Consider, for example, Figure 2 that shows a scenario in which player X has the opportunity to win through placing respective marks in the North-East (NE) corner of the board. Player O must devise a suitable defense against this threat. Figure 2.

Such a defense is shown in Figure 3. Selecting the NE corner is a weak move. Attack yields a much better option here. By choosing one of the options shown in Figure 3, player O forces player X to respond with a defensive rather than an aggressive move. From player O’s perspective, attack is the best defense. A proper implementation of Tic-Tac-Toe must contain a suitable strategy for all corners of the board. Otherwise, the computer may take an unreasonable move and lose the game. Figure 3.

Architecture

This assignment asks you to implement a console application for Tic-Tac-Toe in C++. The design of the application has to follow the Model-View-Controller pattern (MVC). This design pattern can be realized quite differently, but the flow of information is generally as follows (cf. Figure 4):

  • The user interacts with the user interface in some way (e.g., console input).
  • The controller handles the input and maps it to user actions understandable by the model.
  • The controller notifies the model of the user action (e.g., pushing data into the model), forcing the model to take appropriate actions (e.g., changing its state).
  • The model notifies view when its state has changed (e.g., new data is available). In response, the view pulls data from the model and performs associated user interface operations (e.g., displaying the new board).
  • The user interface waits for further user interactions to start the control cycle in step 1 again. Figure 4.

MVC enforces a separation of concerns. The model is independent of the view. This architectural feature allows us to build several different types of application (e.g., console app, Web app, Windows app) for the same underlying model or game, in this case.

The key element for the success of this approach is the use of an interface that both the model and the view use to interact. C++ does not offer an interface concept per se. But, we can technically model an interface by an abstract class, here called TicTacToeView. More precisely, TicTacToeView is a class that only defines pure virtual abstract methods (see below). For the TicTacToe game, this interface class defines just two methods: printBoard, to display the current board in the view and declareWinner, to provide information to the view about the winner of the game. A concrete view implementation uses inheritance to define a subclass of TicTacToeView, here TicTacToeConsoleView, that provides suitable definitions for the view functions.

The game implementation requires four classes, four objects, and one main function. The general structure of the required classes is shown below. Except for private methods, these classes contain everything needed to implement TicTacToe. No additional fields (i.e., member variables) are required. However, you can add as many private member functions as you see fit. Private member functions do not alter the public protocol of a class, but help to organize your code. Divide and conquer. Structure your solution is a way so that you can incrementally build up the game. TicTacToe requires user input, readable output, and at least support to enable two players to play the game with a proper declaration of the winner at the end. Of course, the goal of this assignment is to add also a lightweight decision procedure to the game to allow for a reasonable computer player to automatically respond to user moves.

Key Techniques Used

This assignment will require you to study and work with the following concepts:

  • Mutual dependent classes
  • Forward declaration
  • MVC
  • Boolean Logic
  • Algorithmic problem solving (i.e., find best possible move)
  • Interface-based programming
  • Separation of concerns
  • C++ I/O
  • Value-based objects
  • Object composition
  • Exceptions
  • Public inheritance
  • Friends

Classes & Main

Player: Figure 5.

The game requires three instances of class Player. There is a player NIL, a special placeholder when the games ends with a draw. You always need a proper player object to announce the winner even when there is none. This is the purpose of NIL. Each player has a name and a dedicated player symbol. We use the characters ‘_’ for NIL, ‘X’ for the first player, and ‘O’ for the second player.

The type std::string is the C++ version of strings (i.e., sequences of characters) in C++. See http://www.cplusplus.com/reference/string/ for more information.

TicTacToe: Figure 6

TicTacToe is the model class for the game. It implements the game logic and the decision procedures to determine the winner and the best move for player A in response to player B’s move.

The specification of class TicTicToe requires a so-called forward declaration. This mechanism is used to break the circular dependency between class TicTacToe and class TicTacToeView. This is a built-in feature in C++. It does not exist in C. Forward declarations introduce class names to the current compilation unit. The actual definition of the class TicTacToeView is not necessary for the specification of class TicTacToe. The forward declaration just tells the C++ compiler that TicTacToeView is required and its implementation will be supplied later.

The class TicTacToe also declares a friend, operator<<. This corresponds to the standard philosophy of defining I/O primitives in C++. The operator << is associated with an output stream. The friend declaration states that the operator << has access to the private member variables to class TicTacToe. Figure 7.

The game requires a 3x3 grid. We can number the grid as shown in Figure 5. We can use any order of moves to play the game, but there exists a simple heuristics that defines a sequence of optimal moves: 4, 0, 2, 6, 8, 1, 3, 5, and 7. Use this sequence to compute computer moves. To determine the optimal moves, you can use the Minimaxing algorithm1 for depth 2. But, for this assignment we just use the following simple process consisting of three loops:

  • If player A can win by selecting an optimal move, then player A picks it.
  • If player B can win next by selecting an optimal move, then player A has to take it in order to defend against this threat.
  • If neither player A or player B can win in one move, then player A can pick any optimal move as long as it yields a proper defense against the scenario illustrated in Figure 2 and Figure 3.

You need to devise a proper use for all public methods of this class. To facilitate the implementation, you can define as many private member functions as you see fit.

The member function setField must check that the field to be set is not yet marked. Initially, all fields are initialized with ‘_’, the marker for player NIL. If setField finds the target field occupied, then an exception (using a string value) has to be thrown. A proper try-catch block is required in the calling context of setField.

TicTacToeView: Figure 8.

This class defines the interface between model and view. You can use it as is. No additional code is required.

TicTacToeConsoleView: Figure 9.

This class implements both the controller and the view for the game. The controller has to set up the game, ask for player names, and run the game loop. The controller functionality is embedded in the console view. So, the class TicTacToeConsoleView has to provide implementations for the view methods.

The flag array fComputerPlayer can be used to mark one of both players as computer players. To make a player a computer player, use the string “COMPUTER” and the name of that player. (Both players can be computer players.)

All inputs have to be checked for validity. To mark a field, the user has to type a two- digit number RC, where R is the row (1-3) and C is the column (1-3). If the input is faulty, then the user has to specify the field again: Figure 10.

Main function: Figure 11.

Use this main method for the application.

The solution requires three non-abstract classes: Player, TicTacToe, and TicTacToeConsoleView. Define all in a separate compilation unit (i.e., a corresponding .cpp file). Start the project with the prototype files given on Blackboard. You can alter all header files to add private member functions as you see fit. Do not add any more public methods. Also, the provided class specifications contain all the necessary fields to complete the game.

How does it work?

See image. Figure 12.

Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.