Tic-tac-toe (also known as noughts and crosses or Xs and Os) is a simple "paper and pencil" game. Two players alternate turns, with the first player marking an X on a 3-by-3 game board and the second marking an O. The goal is to make three of your marks in a row, blocking and preventing your opponent from doing the same.
Tic-tac-toe was one of the first (possibly the first) games programmers used to explore artificial intelligence. As the Wikipedia link suggests, writing a computer program to play a perfect game is relatively simple (see the section on strategy). However, at this point in our study of C++, we use tic-tac-toe only to demonstrate two-dimensional arrays and how to pass them as function arguments.
Write a C++ program that allows two players to play tic-tac-toe.
Program Requirements:
Even when a program is small and simple, programmers often have a great deal of flexibility in how they choose to implement its various parts. As the size and complexity increase, so do the number of options. The tic-tac-toe example focuses on just four choices to illustrate that some choices only work on some operating systems, and some involve tradeoffs between different parts of the program.
One of the purposes of the tic-tac-toe example is to demonstrate how to pass two-dimensional arrays to functions. That implies the program must define at least one function beyond main. But functions have value far beyond demonstrating syntax and behavior. Functions help to focus our attention on what needs to be done rather than on how to do it. Functions help us manage the complexity of increasingly large programs: they allow us to focus on one small sub-problem - one that we are more likely to understand fully, from beginning to end - while temporarily ignoring the full problem. Our first choice is how to decompose the tic-tac-toe problem into a set of mutually interoperable functions.
The second choice we explore is how we draw the game board. We play the tic-tac-toe game on a 3×3 board. As we have done all semester, we will make the board using ASCII or text characters. But even within this constraint, we still have a choice to make. As a first choice, we may use the characters '|' and '-' to represent the vertical and horizontal lines and the '+' character to represent the intersection or cross lines. Although this approach works, it doesn't result in smooth, unbroken lines. A second approach is to use the drawing symbols that are available on some systems as extended ASCII codes.
const char VERT = '|'; const char HORIZ = '-'; const char CROSS = '+'; |
const char VERT = (char)179; const char HORIZ = (char)196; const char CROSS = (char)197; |
|
(a) | (b) | (c) |
The moves a player can make at any time during gameplay depend on the previous moves made by both players: once a player makes a mark on one of the nine spaces on the board, that space is no longer available. Our tic-tac-toe program will use a two-dimensional array to represent the game board, that is, to store each player's moves. However, the array may also optionally store the characters needed to divide the board into the nine spaces where the players may make their marks. Our third choice, then, is how to draw the game board. Our first option is storing the line-drawing characters in the game board array, making the display function quite simple. Our second choice is to store the moves in the array and draw the lines in the display function, making it more complex.
(a) | (b) |
The fourth and final choice only applies to the Figure 3(b) display function, demonstrating that a given sub-problem often has many solutions. (The display function for version (a) is simple and structured very much like the multiplication table introduced in chapter 3.) The fence post problem, introduced in chapter 3, is a general problem where a loop generally performs two operations but only performs one during the first or last iteration. Examples typically print a repeating pattern. In the tic-tac-toe program, the pattern is ar,0|ar,1|ar,2
, where each ar,c
is one element of row r of the array, and |
is the vertical line that is part of the playing board. Using pseudo code, we can demonstrate three of the possible ways of structuring the inner loop of the display function:
for col = 0, 1, and 2 print a[row, col] if col < 2 print '|' print new line |
print a[row, 0] for col = 1 and 2 print '|' and a[row, col] print new line |
for col = 0 and 1 print a[row, col] and '|' print a[row, 2] and new line |
The fence post problem is more challenging when the number of repetitions is large, variable, or both. This problem fixes the number of repetitions in each loop to three, greatly simplifying the problem. The following versions of the tic-tac-toe program demonstrate a few of the many choices computer scientists may make when solving a problem with a program.
View | Download | Comments |
---|---|---|
ttt1.cpp | ttt1.cpp | moves and board characters together in the array (Figure 3(a)) |
ttt2.cpp | ttt2.cpp | moves only in the array (Figure 3(b)); has three versions of the display function:
|