CPS 100, Spring 2003, Yahtzee Assignment


See the FAQ

The game Yahtzee (copyright Milton Bradley) can be played by one or more players. You'll work on a program that allows the user to play solitaire Yahtzee using the computer.

The Rules of Yahtzee explain things in detail. The rules are summarized below. There's also a Java Applet version you can play to get an idea of how the game is played.

You can also figure out how to play be looking at the scorecard below and compiling/running the beginning version of the game you're given.

scorecard

Rules/Scoring

You have five dice. On each turn you can roll all the dice up to three times. Each time you can "freeze" some of the dice so that you don't roll them all. This allows you to try to get four of a kind, for example by freezing some dice after the first roll, freezing some (same or different) after the second roll, then scoring the dice after rolling some/all of the dice a third time.

You score the five dice by entering the appropriate value in the scorecard. Once you enter a value in a given category that category is done for the game. When you've filled in all categories the game is over.

The upper section of the score card counts the number of 1's, 2's, ..., 5's or 6's you roll. For example, if you roll three 2's a 4 and a 5 you could score this as Twos and it would count six points, as Fours and it would count four points, as Fives and it would count five points, and as Threes and it would count zero points.

The lower section scores the categories shown. A small straight is four in a row, e.g., 1,2,3,4; or 2,3,4,5; or 3,4,5,6. A large straight is five in a row: 1,2,3,4,5 or 2,3,4,5,6. A full house is three of one kind and two of another, e.g., 2,3,3,2,2. Chance can be any roll at all. A Yahtzee is five-of-a-kid. In our game we will not score a Yahtzee bonus (more than one Yahtzee per hand).

Structure of the Program

You're given the framework of a program that will play yahtzee when you're done making additions and modifications to it. Just as the board game yahtzee uses a score-card and a cup-of-dice, so does the computer version of the program. Here's the main function from yahtzee.cpp.
    int main()
    {
        CupOfDice cup(5);
        GroupRoller gr;
        ScoreCard sc;
    
        while (! sc.allFilled()) {
    	    sc.display();
	    gr.roll(cup,3);
	    sc.score(cup);
        }
        return 0;
    }

As you can see from the code, the game is played until the score-card is filled. Each time the user takes a turn, a cup-of-dice is rolled as a group (rolled up to three times), then the dice in the cup are scored via the score-card. What you can't see in this main function is that the class ScoreCard (specified/declared in scorecard.h) uses several ScoreCardEntry classes/subclasses.

Purpose of Assignment

Your task for this assignment is to understand the relationship between all the classes, modify the code that is incorrect or not robust, and add new classes and code so that a complete game of Yahtzee results. This assignment is meant to be a re-introduction to C++, with a program that uses several classes, that uses inheritance (a potentially new topic) and that uses pointers.

Overview of What You'll Do

This assignment consists of three different programs, you'll be running all three. An outline of what you'll need to do follows, specifics are given below.
  1. You'll need to compile/run/fix the program testunfreeze. This program tests the function unfreezeAll from the class CupOfDice. In the code you're given, there's a bug in the function that the test program reveals. Your task is to fix the bug so the test program passes.

  2. You'll need to compile/run/fix the program testscoreentry which tests several ScoreEntry subclasses. Some of the classes are partially implemented, e.g., FullHouse and Chance and some are completely implemented, e.g., AboveLine and FourKind. You'll also need to implement new subclasses for SmallStraight and LargeStraight. You'll need to implement/fix these subclasses so that the test program passes its tests based on your implementations.

  3. You'll need to compile/run/fix the program yahtzee so that it plays a complete game of yahtzee with the user. This requires adding code to trap bad user-input, adding code to allow the user to play more simply, and adding code to score the entire game correctly.

Copying Files

First, you'll need to create a directory named yahtzee in your cps100 directory then copy the files from ~ola/cps100/yahtzee as shown below.
 cd
 cd cps100
 mkdir yahtzee
 cd yahtzee
 cp ~ola/cps100/yahtzee/* .

Don't forget the trailing dot on the last line when copying files. You should see many files, individually they're accessible here, but you should copy them from the acpub account.


Testing unfreezeAll

You should compile and run this program as shown below, user-entered input in italics and red. As the run shows (your run should be the same) many of the tests fail. This failure is due to a bug in the function CupOfDice::unfreezeAll. You should look at the code in that function (in cupofdice.cpp), find the bug, fix it, recompile and re-run the test program so that it prints that each dice passes the tests.
prompt> make testunfreeze

compiler output shows up here

prompt> testunfreeze

checking unfreeze on group size = 1
dice 0 passed
checking unfreeze on group size = 2
dice 0 passed
dice 1 failed
checking unfreeze on group size = 3
dice 0 passed
dice 1 failed
dice 2 failed
checking unfreeze on group size = 4
dice 0 passed
dice 1 failed
dice 2 failed
dice 3 failed
checking unfreeze on group size = 5
dice 0 passed
dice 1 failed
dice 2 failed
dice 3 failed
dice 4 failed
checking unfreeze on group size = 6
dice 0 passed
dice 1 failed
dice 2 failed
dice 3 failed
dice 4 failed
dice 5 failed

prompt> 

Testing ScoreEntry subclasses

The class ScoreEntry in scoreentry.h is the base class of an inheritance hierarchy of score-card entries. The idea is that each of the 13 possible score-card entries is represented by a different object or class (e.g., each the above-the-line entries is represented by an instance of the AboveLine subclass, but a full-house is represented by the separate class FullHouse).

The test program testscoreentry.cpp is designed to test all the subclasses. Currently the classes Chance and FullHouse are partially implemented; for each of these the function calcScore isn't correct and you'll need to implement these functions (FullHouse::calcScore and Chance::calcScore) so that compiling and running testscoreentry.cpp passes all the tests by producing NO output. As given to you, here's what the program produces as output.

prompt> make testscoreentry
  
   lots of compiler output follows

prompt> testscoreentry

failed with chance          (all) dice = '11222' score = 0 TARGET = 8
failed with chance          (all) dice = '66566' score = 0 TARGET = 29
failed with chance          (all) dice = '12345' score = 0 TARGET = 15
failed with full house      (25)  dice = '11112' score = 25 TARGET = 0
failed with full house      (25)  dice = '66666' score = 25 TARGET = 0
As the output shows, each test of Chance produces 0 as the score when the target for the correct score varies as shown in the output above. The tests for FullHouse show that the fail tests produce 25 as output for dice rolls that aren't full houses and so should be scored as 0.

You should change the implementation of Chance::calcScore and FullHouse::calcScore so that when you recompile and re-run the test program it produces no output. When nothing is printed, all the tests pass.

Implementing New Subclasses

Currently there are no classes for SmallStraight and LargeStraight. You must implement these two classes and provide tests for them in testscoreentry.cpp. This requires the following steps.

  1. Create .h and .cpp files for both classes, i.e., smallstraight.h, smallstraight.cpp, largestraight.h, and largestraight.cpp. You should model these files on the existing subclasses of ScoreEntry.

  2. Add smallstraight.cpp and largestraight.cpp to the Makefile for the target program testscoreentry as shown below, on the last line following fullhouse.cpp.

      EXEC_TESTSCORE = testscoreentry
      SRC_TESTSCORE  = scoreentry.cpp fixeddice.cpp testscoreentry.cpp \
                       aboveline.cpp \
                       threekind.cpp fourkind.cpp fivekind.cpp dicegroup.cpp \
                       chance.cpp fullhouse.cpp
    
    After changing the Makefile, add appropriate #include lines for the .h files you've made to testscoreentry.cpp. Then do the following to recompile and test your new classes:
      make depend
      make testscoreentry
    
    You only need to run make depend if you've modified the Makefile or included any extra header files. So you'll most likely need to type make testscoreentry several times, but you'll run make depend only a few times.

  3. Add appropriate tests to testscoreentry.cpp for testing your classes SmallStraight and LargeStraight. You should include tests for dice that do form straights and for dice that do not form straights. When you have written your tests and fixed your classes so that tests pass you're ready for the final step of this program.

Playing Yahtzee

If you compile/run the program yahtzee.cpp as shown below, you can play most of a game of yahtzee, though as given small straight and large straight are missing.

prompt> make yahtzee
  
  lots of compiler output

prompt> yahtzee
1.	1 : (score # 1's x 1)	*
2.	2 : (score # 2's x 2)	*
3.	3 : (score # 3's x 3)	*
4.	4 : (score # 4's x 4)	*
5.	5 : (score # 5's x 5)	*
6.	6 : (score # 6's x 6)	*
---------

7.	three of a kind (all)	*
8.	four of a kind  (all)	*
9.	full house      (25)   	*
10.	YAHTZEE         (50)	*
11.	chance          (all)	*

+---+	+---+	+---+	+---+	+---+	
| 5 |	| 3 |	| 5 |	| 3 |	| 3 |	
+---+	+---+	+---+	+---+	+---+	
  0	  1	  2	  3	  4	


Pressing return freezes NO dice.

freeze which dice [e.g., "034"] ? 1 3 4
+---+	+---+	+---+	+---+	+---+	
| 2 |	| 3 |	| 1 |	| 3 |	| 3 |	
+---+	+---+	+---+	+---+	+---+	
  0	  1	  2	  3	  4	


freeze which dice [e.g., "034"] ? 1 3 4
+---+	+---+	+---+	+---+	+---+	
| 6 |	| 3 |	| 1 |	| 3 |	| 3 |	
+---+	+---+	+---+	+---+	+---+	
  0	  1	  2	  3	  4	


1.	1 : (score # 1's x 1)	*
2.	2 : (score # 2's x 2)	*
3.	3 : (score # 3's x 3)	*
4.	4 : (score # 4's x 4)	*
5.	5 : (score # 5's x 5)	*
6.	6 : (score # 6's x 6)	*
---------

7.	three of a kind (all)	*
8.	four of a kind  (all)	*
9.	full house      (25)   	*
10.	YAHTZEE         (50)	*
11.	chance          (all)	*

score by which number 7

1.	1 : (score # 1's x 1)	*
2.	2 : (score # 2's x 2)	*
3.	3 : (score # 3's x 3)	*
4.	4 : (score # 4's x 4)	*
5.	5 : (score # 5's x 5)	*
6.	6 : (score # 6's x 6)	*
---------

7.	three of a kind (all)	16
8.	four of a kind  (all)	*
9.	full house      (25)   	*
10.	YAHTZEE         (50)	*
11.	chance          (all)	*

game continues here

You must do the following to make the game of yahtzee complete.

  1. Uncomment the lines in scorecard.cpp where SmallStraight and LargeStraight score-card entries are added to the ScoreCard (in the constructor). You'll also need to add proper #include statements to get access to the straight subclasses.

  2. Add entries to the Makefile under the yahtzee section for smallstraight.cpp and largestraight.cpp, then re-run make depend since you modified the Makefile.

  3. Compile/run/test the yahtzee program to see if small straight/large straight show up in the scorecard. You should be confident that these classes work since they passed tests to show this.

  4. Currently, if the user selects a scorecard number like 52, the program will abort since 52 isn't a legal scoreentry index (the legal indexes are 1-11 as written and 1-13 after adding straights). Fix this problem by modifying code in scorecard.cpp in the function ScoreCard::score.

  5. Currently, the user can score the same entry several times. For example, the user can score above-the-line 2's more than once since nothing stops the user from using 2 as a scoring choice more than once. You should modify the code in scorecard.cpp so that this isn't possible. Note that each ScoreEntry subclass returns the value -1 via when getScore is called if the entry hasn't yet been scored, and returns 0 or a positive number if the entry has been scored.

  6. No final score is given. The final score is the sum of all entries in the ScoreCard, plus a 35 point bonus if the above the line score totals 63 or more (see the scoring details for how this works). You should add methods to the appropriate classes and call them when the game is done so that the user is shown the final score when the game is over.

  7. The order in which score-card entries are printed, e.g., by the function ScoreCard::display must match exactly the order in which the entries are shown on the Yahtzee scorecard shown here.

    This means, for example, that the user should enter 1 to score above-the-line 1's, 2 to score above-the-line 2's, etc., 7 for three-of-a-kind, 8 for four-of-a-kind, 9 for full-house, 10 for small-straight, 11 for large-straight, 12 for yahtzee, and 13 for chance.

  8. The user should get exactly thirteen attempts to roll the cup-of-dice, i.e., should have exactly thirteen times all the dice are rolled/re-rolled and scored. This means for each time ScoreCard::score is called, if the user picks a score-card entry (number) that's already taken, the user should be re-prompted to choose a score-card entry that's not taken.

Extra Credit

Add functions/code to the class ScoreCard so that the user is shown what the value of each score-card entry would be if chosen for the cup-of-dice that's about to be scored. An example of what this might look like is shown below.

+---+	+---+	+---+	+---+	+---+	
| 4 |	| 6 |	| 1 |	| 5 |	| 1 |	
+---+	+---+	+---+	+---+	+---+	
  0	  1	  2	  3	  4	


when freezing dice do NOT enter spaces

freeze which dice [e.g., "034"] ? 24
+---+	+---+	+---+	+---+	+---+	
| 3 |	| 5 |	| 1 |	| 5 |	| 1 |	
+---+	+---+	+---+	+---+	+---+	
  0	  1	  2	  3	  4	


freeze which dice [e.g., "034"] ? 1234
+---+	+---+	+---+	+---+	+---+	
| 1 |	| 5 |	| 1 |	| 5 |	| 1 |	
+---+	+---+	+---+	+---+	+---+	
  0	  1	  2	  3	  4	


             *** possible scoring
            1.	1 : (score # 1's x 1)	3
            2.	2 : (score # 2's x 2)	0
            3.	3 : (score # 3's x 3)	0
            4.	4 : (score # 4's x 4)	0
            5.	5 : (score # 5's x 5)	10
            6.	6 : (score # 6's x 6)	0
            ---------

            7.	three of a kind (all)	13
            8.	four of a kind  (all)	0
            9.	full house      (25)   	25
            10.	small straight  (30)   	0
            11.	large straight  (40)   	0
            12.	YAHTZEE         (50)	0
            13.	chance          (all)	13

score by which number 9

 program continues

A value of -1 should be shown for entries already scored (since the user can't rescore an already-scored entry). Worth 6 points.

Design Extra

The classes ThreeKind, FourKind, and FiveKind have similar/redundant code. Write one class to take the place of these classes. The new class should have a constructor that indicates how many-of-a-kind an instance/object will represent, similarly to how the AboveLine class works to represent more than one score-card entry. You may need to pass additional parameters to the constructor, e.g., for scoring purposes.

If you make this change, you'll need to modify your scorecard.cpp file and your testscoreentry.cpp file (and perhaps others). You'll need to change the Makefile too.

Worth 4 points.


Grading

This assignment is worth 40 points. The points will be awarded roughly as follows:
Criteria Points
Fix bug in unfreezeAll 3
Fix chance/full house 6
Straight classes/tests 10
Yahtzee works in general/robust 8
Scoring Game 3
Style of code (comments etc) 8
README 2
Extra Credit 10

Submit

You should turn every .h and .cpp files used to make the yahtzee executable you worked on. You must turn in the Makefile too.

You must submit a README that includes information about how much time you spent on the assignment, and a list of people with whom you discussed the program. You should also include your thoughts about the assignment.

Be sure that every file you modify includes your name in a comment near the top of the file.

To submit use

 submit_cps100 yahtzee README Makefile *.h *.cpp

If submit_cps100 doesn't work, try ~ola/bin/submit_cps100.


Owen L. Astrachan
Last modified: Thu Jan 23 11:34:03 EST 2003