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.
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):
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.
This assignment will require you to study and work with the following concepts:
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:
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.
See image. Figure 12.