Computer Science 4 - Project 1

Two Person Games

copyright RIT 2003
$Id: writeup.xml,v 1.9 2006/09/14 14:41:23 cs4 Exp $

Due Dates:

Part 1 is due on 28 September 2003.

Part 2 is due on 12 October 2003.

Part 3 is due on 26 October 2003.

Part 4 is due on 9 November 2003.

Part 5 is due on 10 November 2003.

Goal

1. Design

You will improve your design skills while learning many new design techniques and styles.

2. C++ Programming

You will learn what it takes to develop a larger program in the C++ language.

3. Teamwork

You will learn how to work in a team.

4. C++ Survival

Some of the techniques you learned in more standard object-oriented languages may not apply here. In addition, C++ has some unique features that you may be able to exploit. This project should help expose you to these issues and show you how to make choices you can live with.

Overview

Abstraction as a Means of Extensible Design

Below you will read about some specific two-person games you are to analyze. However, we will also show you how these games fit into a more general analysis pattern. If you know this, you can design your program to this more abstract model, thereby allowing you to plug in new games with less effort.

Here are some simple games that all have common characteristics. If you are not familiar with any of them, try playing the game a few times. The strategy discussions will then be clearer.

Take Away

In front of both players is a pile of pennies. When it is a player's turn to play, the player must take 1, 2, or 3 pennies from the pile. At least one coin must be taken. The loser is the player who takes the last coin. A simple strategy is to try to leave your opponent 1, 5, 9, 13... coins or a multiple of four plus one. What ever your opponent does you can reduce the pile to the next number in this sequence until you leave one coin which your opponent must pick up. Think about how you would discover this strategy if you didn't know it.

Nim

Nim is played with several pebbles or stones. When it is a player's turn to play, the player may take as many stones as she wants from a single pile but she must take at least one. The winner is the player who takes the last pebble. A typical game might start with three piles of 3, 4, and 5 pebbles. What is the best strategy for each player?

Othello

Our version of Othello is played on an initially empty 3 x 4 checker board. There are two players, called "X" and "O". A player, when it is her turn to play, plases her mark (X or O) in any empty square and then checks all eight lines (horizontally, vertically, and diagonally) radiating from this square. if there is an unbroken sequence of the opponent's mark followed immediately by the player's mark then all of the opponent's marks are changed to the player's mark. It is important to notice that there can be no empty spaces in the run of opponent's marks and that a player's mark immediately follow this run of opponent marks. Only opponent marks up to the first player mark are changed. The game ends when there are no empty spaces to play in. The final score is the number of player marks minus the number of opponent marks.


Example of an Othello game

In the above diagram player "O" can play in the lower right hand corner and change the two X's in the bottom row and the X along the right hand side to O's. If O plays in the rightmost empty space in the top line then only the leftmost X in the middle row can be changed to an O. The best strategy in this game may be to make moves that do not change many squares but set up a situation where future moves can be advantageous. If O plays in the upper left corner then no X's can be changed.

The best strategy in this game may be to make moves that do not change many squares but set up a situation where future moves can be advantageous.

The specifics of these problems will be given in the activity sections.

Background

A Single Abstraction for these Problems

What do all of these games have in common? All of these games are "two player games with perfect information". They have the following properties (adapted from "Winning Ways" by Berlekamp, Conway, and Guy):

Mapping the Abstraction

Let's see how the tic tac toe game maps to this abstraction.

We will leave it as an exercise to the student to determine the mappings for the other games.

Designing A Good Game Playing Algorithm

Suppose you were a player in one of these games. How would you decide your best move? If you knew the consequences of each move (assuming best play by both players after this move) then it would be easy. You would try all possible moves and make the move that has the best outcome. If we had a program that could calculate the final score starting from any position (assuming best play) then we could use it to determine the best move. ( Note that a position also implies which player is to move.) We would try all possible moves from the current position and chose the move that leads to a position with the largest score. We could also calculate the final score resulting from the current position to be the same as the final score of the position resulting by moving our best move. (If there are several moves that result in the same final score we could chose the first one or a random "best" move for more variety.)

It is easy to calculate the score at the end of a game - just use the rules of the game. It is also easy to determine if the game is over using the rules of the game. Otherwise, the game is not over and we must evaluate each possible move in turn. By calling this routine recursively we can determine all possible games that could be played (in a depth-first search). The only tricky part of this is to realize that the two players have different goals: one player wants to maximize the score while the other player wants to minimize the score of the first player.

We could write two functions - one for each player that calculates the value of the game from that player's viewpoint. Or we could write a single function and change the sign of the final score and the identity of the two players after each move. ( In checkers, this would be like reversing the colors of all the men and turning the board around on each move. Some games (such as Take Away or Nim) only change the sign of the score while others (such as Tic Tac Toe or othello) require changing the position and/or identity of the pieces.)

If we write out a complete game tree and evaluate the position from the viewpoint of the player who is to move we note that for each level the value of the game at that level is the maximum of the negative of the child nodes of that level. (The minus sign is because we reverse our viewpoint on each move. If we don't reverse our viewpoint then alternate levels of the game tree would have to minimize and maximize.)

Here is a game tree for the game Take Away with five pennies in the pile.


Part of the game tree for Take Away

Note that these are game positions for us to evaluate with us to move. If we are left with 0 then our opponent must have taken the last stone and we have won. In all other cases we can move. Our objective is to move to a losing position for our opponent so, for example, if we are presented with 4 stones we should leave one since this is the only alternative that loses for our opponent. In general, if we can move to a losing position for our opponent then we move there and claim that the current position is a win. Otherwise, our opponent can win the game no matter what we do so we make a random move. For games with more possible scores than "win" or "lose" we would move to the position with the minimum score (from the viewpoint of our opponent).

Another way to eveluate game positions (if you find reversing viewpoints confusing) is to use two evaluation functions, one for us and one for our opponent. Then our evaluation function attempts to maximize the score and our opponent's function attempts to minimize the score. These two functions will call each other to evaluate the game tree. You might arrange for each of these two functions to call common functions for the common part of the analysis. You may use either a single evaluation function or an evaluation function for each player in your code.

Here is pseudocode for the board evaluation strategy:

Evaluate position:


    If game position is a final position then
        Return score of position
    Generate all legal moves from this position
    For each legal move
        Calculate the resulting board position
        Reverse the board (if necessary) to the other player's viewpoint
        Call Evaluate Position on this resulting position
        If this is the smallest value seen so far (our best is opponent's worst)
            remember the move as the best move
            remember the value as the best value
        end-if
    end-for
    return best move and minus best value
        

This algorithm must terminate since there are only a finite number of possible moves for any game position and a game has only a finite number of moves. It also returns the best move against a perfect opponent because no other move can guarantee a better outcome. (Do you see why?)

Did you recognize a pattern in the way the algorithm organizes and traverses its search space? It's a depth-first search of all of the possible games. Some games, like chess, could theoretically be solved with such a program but the number of positions that would have to be searched makes the running time of this simple algorithm to be longer than the age of the universe. There are ways we could speed up this algorithm:

One way to speed up this algorithm:

Playing a Game

Once you have a function that calculates the best move from a given position it is easy to add code to allow your program to play against a human opponent. You need to display the current position, input the human's move, make the human move to get the new position, calculate the best move in response given this position, make this move, and repeat. You will also need tests to determine if the game is over and code to print out the final score. You might want to be able to play a series of games and keep a running score.

One way to design this code is to write out an activity diagram or state chart containing the various possibilities. (Illegal move, game scoring, what to print when etc.)

What To Do

The activities in this project will have you design a framework that is easily adapted to all the games of the classification described above. You will then implement and test all three of the games using that design.

The general process you should follow goes something like this:


    Develop the initial framework design in the abstract.
    Submit the design to your instructor.
    Write the code for the abstract framework.
    For each problem for which you must implement a solution,
        Code the specific problem classes.
        If the previous step forced a modification of your design,
            Modify the code for the design as needed to make it work
            Modify the code for the previous problems as needed
        Submit the code for your latest design and all the problems solved so far
        

Shared Programming Responsibility

Because this is the only project you are doing in this course, and it is a team project, there is a possibility that we will not be able to accurately assess your programming abilities if your teammate does most of the programming. Therefore, each team member must be responsible for roughly half the code written in each activity, 2, 3, and 4. In the header comments, the name of the principal author should show up first, as always, in each code file. Both team members must be able to explain all code orally if asked by his/her instructor.

Part 1

Part 1 is due on 28 September 2003.

In this first activity, you are mainly concerned with the design of the framework. The term framework means a set of classes that enable implementation of solutions to certain problems. However, the framework by itself is not a complete program. You will work with abstract notions such as position, move, and find-best-move. The find-best-move should be able to solve any game that conforms to an interface that you develop in your design. Think carefully about this interface, as you will later be writing classes that conform to it to calculate moves in the three games. You should also think about the user interface for playing a game. You might want to overload the input operator ">>" to read in a move from the terminal.

Your design document is a Rational Rose UML model that contains at least use case, sequence, and class diagrams. For this particular project you will have at least two use cases: a use case that determines the best move and a use case that plays a game with a human opponent.

The more interesting work is in the class and sequence diagrams. The classes you show for this activity are only the ones of the framework, not of any particular problem. For the sequence diagrams, show some interesting scenarios involving part of the above algorithm. Although this would be illegal in real code, include objects that are instantiated from your abstractions. Note that there must be a method in some class that runs an algorithm like the one described in the Background section. If you are planning to use a templated class you might draw the diagram as though the templated class were instantiated with a specific instance.

When you design the generic configuration class, make sure you include some kind of a display function that will, when implemented, print some textual representation of the configuration to standard output. This will be of great help while you are debugging your code. The problem solver algorithm can be enhanced by a call to the display function inside the loop. Of course, the implementations of your display function will only show up in the specific problems' configuration classes. This could be accomplished conveniently by overloading the output operator "<<".

How To Submit

Call your model file game.mdl. It should contain all the diagrams you have developed.


    try cs4-grd project1-1 game.mdl
                

Part 2

Part 2 is due on 12 October 2003.

The purpose of this activity is to perform the first validation of your design. You will write the code for your design. Then you will add code for the Take Away game, put the two together, and see how they work. It is important to note that you are expected to be using a framework that is equally applicable to the other problems. (While you could conceivably use the simple "leave a multiple of four plus one" approach we discussed earlier, this isn't what we will be looking for: instead, you should be using the generic algorithm we've defined, which should generate the same results.) There are easier solutions for this game than the one we are having you build but we want you to solve it using the general framework you designed in part 1.

Now your design must get more detailed. This is probably the right time to think about exactly how you will realize your design within the constraints of the C++ language. Although you are free to make your own decisions, some suggested approaches are shown at choices.html that satisfy the requirement of a framework that adapts well to different "two-person games with perfect information".

Getting back to the Take Away game, it requires one integer as input:

This integer is to be provided on the command line. If you get the wrong number or type of arguments, or if the number is out of bounds with respect to the legal number of pennies, you should report an error on standard error and quit.

If you wish to play the game instead of calculating a single move then the first input argument should be the word "play" followed by the argument giving the number of pennies in the pile.

The program is to be called takeaway, which means the main function should be defined in a file named takeaway.cpp. As submitted, the program must print out, on standard output, the best move or the number of pennies to take or, if the play option is used, alternately allow the user to play followed by the computer playing.

You must submit a file called readme which is a text file containing instructions for running your program. It should contain the format of any input required by the program. For this submission the instructions are simple but you should get used to writing user documentation for the code that you write.

If you have modified the design, you must submit an explanation of changes as part of your readme file.

If you want to submit a custom gmake file, rename it to be called custom.mk and submit it along with the other files.

If you submit your own custom.mk gmake file, invoking gmake without any arguments must produce an executable called takeaway that calculates the best move as described here described here.

How To Submit

You must submit all the .cpp and .h files required to build the takeaway program. It must be possible to compile the takeaway program by executing gmakemake and then simply gmake or you must submit a custom gmake file called custom.mk. Your design model must also be resubmitted, augmented with the classes for the takeaway problem. If your underlying design changed, include the changes and reasons in the readme file mentioned above.


    try cs4-grd project1-2 readme takeaway.cpp other-needed-code-files game.mdl [ custom.mk ]
                

Part 3

Part 3 is due on 26 October 2003.

The purpose of this activity is to implement the solution for a game that requires a slightly more involved position analysis. Write the code for the Nim game, plug it into your framework, and see how it works.

A position in Nim consists of several piles of stones. The program will read the number of stones in each pile from the command line arguments. There will be as many arguments as there are piles of stones and each argument will specify how many stones are in the corresponding pile. Any bad command line arguments should result in an error message on standard error and the program should terminate. For convenience, the first pile is pile number 0. Alternatively, the first argument can be the word "play" followed by arguments specifying the stone piles. In this case, the program will play the Nim game with a human opponent.

The program is to be called nim, which means the main function should be defined in a file named nim.cpp. As submitted, the program must print out the best move by listing the pile number and the number of stones to remove from it.

You must submit a file called readme which is a text file containing instructions for running your program. It should contain the format of any input required by the program.

If you have modified the design, you must include an explanation of the changes in the readme file.

You must use memoization for this submission.

Configuration Design Suggestions

This game, although more complicated than takeaway, is still fairly simple. The position basically consists of a vector of ints. Be careful not to change any shared data structures - always copy a vector or structure before modifying it if there exists any other use of the vector or data structure.

How To Submit

You must submit all the .cpp and .h files required to build the takeaway program and the nim program. It must be possible to compile both programs by executing gmakemake and then simply gmake. Or submit custom.mk as before. Your design model must also be resubmitted, augmented with the classes for the nim program. If your underlying design changed, include the changes and the reasons for them in the readme file. ( If you had to change your design, then you probably need to update the takeaway program so that it continues to work.)

If you submit your own custom.mk gmake file, invoking gmake without any arguments must produce two executables: one called takeaway, and one called nim that plays the nim game.


    try cs4-grd project1-3 readme takeaway.cpp nim.cpp other-needed-code-files game.mdl [ custom.mk ]
                

Part 4

Part 4 is due on 9 November 2003.

The purpose of this activity is to play a game that is more complicated. We hope that you will be surprised at how easily your framework can play this game. Write the code for the Othello game, plug it into your framework, and see how it works. (You should assume that the user will be playing as "X", both for the "best move" functionality, and for interactive play.)

Input

Your program will need to be told the initial position to analyze. There will be command line arguments for each square of the Othello diagram with 1 for "X", -1 for "O", and 0 for an empty space. Trailing 0's may be omitted.

To play the game against the computer the first command line argument should be "play" followed by the arguments as above.

You should detect any errors in the command line arguments and terminate with an appropriate error message.

The program is to be called othello, which means the main function should be defined in a file named othello.cpp.

You must submit a file called readme which is a text file containing instructions for running your program. It should contain the format of any input required by the program.

If you have modified the design, you must submit an explanation of changes in the readme file.

Design Suggestions

Choose a simple data structure to represent the board.

How To Submit

You must submit all the .cpp and .h files required to build the othello and takeaway and nim programs. It must be possible to compile all three programs by executing gmakemake and then simply gmake. Or submit custom.mk as before. Your design model must also be resubmitted, augmented with the classes for the Othello game. If your underlying design changed, include the details in the readme file. ( If you had to change your design, then you probably need to update the other two programs so that they continue to work. )

If you submit your own custom.mk gmake file, invoking gmake without any arguments must produce three executables: one called takeaway, one called nim, and an executable called othello that plays the othello game described here.


    try cs4-grd project1-4 readme takeaway.cpp nim.cpp othello.cpp other-needed-code-files game.mdl [ custom.mk ]
                

Part 5

Part 5 is due on 10 November 2003.

The last activity, due one day after the previous one, is the team evaluation.

Only a textual evaluation form is submitted. It is available as team-evaluation in the web directory for this project.

Do not submit this from your team account.

How To Submit

From your own account, submit your evaluation as follows:


    try cs4-grd project1-5 team-evaluation
                

Grade Computation

Grade Breakdown:


Change History

$Log: writeup.xml,v $
Revision 1.9  2006/09/14 14:41:23  cs4
Fixed a spelling mistake.

Revision 1.8  2003/11/22 14:24:52  cs4
Added text to Part 4 requirements to indicate that the user will play
as "X" in both modes.

Revision 1.7  2003/11/22 14:20:01  cs4
Added text to Part 2 description, in an attempt to reinforce the
goal for this level.

Revision 1.6  2003/07/03 15:50:49  cs4
Almost ready for review (swm)

Revision 1.5  2003/06/25 19:31:19  swm
fix paragraph title problem

Revision 1.4  2003/06/25 19:29:36  swm
fixed xml syntax error

Revision 1.3  2003/06/25 19:02:53  swm
fixed xml syntax error

Revision 1.2  2003/06/25 18:55:14  swm
replaced quote characters

Revision 1.1  2003/06/25 18:37:59  swm
Initial revision