TOC PREV NEXT

Program — Flow-of-control project


This programming assignment gives you experience with defining and using functions, parameters, and classes, the fundamental construct of object-oriented programming.

Readings

A Computer Science Tapestry, sections 2.4-2.6, 3.4, 4.5, and 5.4, plus chapters 6 and 7 except sections 6.3.5, 6.3.7, and 6.5 (these cover files, which appear in the next programming assignment).

C++ Program Design, chapters 5 through 8 except 5.6 (files, which appear in the next programming assignment) and 5.8 (character type functions, useful but not covered in CS 9F).

Computing Fundamentals with C++, chapters 3 through 6 except section 4.3 (which uses the grid class provided with the diskette that accompanies the book).

Review the section "C constructs to avoid" in this study guide, which describes "C-isms" that you won't be allowed to use in CS 9F.

The sections of this document titled "Comparing C++ and non-C-based programming languages" and "Linker errors".

Miscellaneous information

Your class account should be set up to search the directory ~cs9f/lib automatically for .h files. When you give the command alias to the UNIX shell, you should see a line that looks like

		g++ g++ -g -Wall -I /home/dd/cs9f/lib

Related quizzes

Fundamentals

Problem

Do one of the next two exercises, that is, project A or project B, but not both.

CS 9F programming assignment 2
Flow of control project A

Background

The scene is an urban park; a cat watches a mouse run around the base of a statue of Bjarne Stroustrup. Over the course of a minute, the somewhat witless mouse moves one meter counterclockwise around the statue's base, which is circular and two meters in diameter. Every sixty seconds, the cat pursues the mouse as follows: If the cat can see the mouse, the cat moves one meter toward the statue. If the cat can't see the mouse, the cat circles 1.25 meters counterclockwise around the statue.

The cat plans eventually to get close enough to the mouse to make it a juicy lunch. The mouse by accident, however, may manage to keep completely out of sight of the cat, since both the cat and the mouse are moving counterclockwise. The cat will eventually tire of the chase if this happens.

Problem

Complete the program found in ~cs9f/lib/cat+mouse.cpp to produce a simulation of this situation and determine if the cat catches the mouse. Don't change any of the code already provided in that file. The program uses a Position class declared in ~cs9f/lib/positions.h; you are also to provide a file positions.cpp that correctly implements the operations of this class.

The program first calls the GetPositions function to ask the user for the initial positions of the mouse (angle in degrees) and the cat (both radius and angle in degrees). You may assume that the user provides legal position values.

The program then calls the RunChase function to simulate the chase using the rules listed above. (The cat moves first.) Your code should use member functions of the Position class where appropriate. It should print the position of the cat and mouse after each move, followed by the results of the chase (either the mouse becomes lunch, or the cat gives up after 30 minutes).

Useful information for writing positions.cpp

A number of the member functions you are to write for the Position class involve trigonometry. Given below are some formulas. All angles are in radians; you will have to convert the input angles from degrees to radians. (Derivations for all the formulas appear here.)

  1. The cat sees the mouse if
	(cat radius) * cos (cat angle - mouse angle)
is at least 1.0.
  1. When the cat circles distance d around the statue, its radius does not change, and the change in its angle can be calculated from the relationship
	d = angle * (radius of arc)
  1. The cat catches the mouse when it (the cat) moves past the mouse while at the base of the statue, i.e. when the cat radius is 1.0 and the mouse angle lies between the old cat angle and the new cat angle. An angle B is between angles A and C in the following circumstances:
	cos (B - A) > cos (C - A)
and
	cos (C - B) > cos (C - A).
The difference C - A is assumed to be less than 180°, or ¼ radians.
  1. Note that the cat cannot move inside the statue's base; hence if the cat is, say, at radius 1.7 and sees the mouse, it can move in only 0.7 meters, up to the base of the statue. While at the base of the statue, the cat continues to move, circling 1.25 meters around the statue since it's unable to see the mouse, until either it passes the mouse or gets tired as described above.
  2. Remember that angles a, a + 2¼, a + 4¼, ... are all equal.

The cos function appears in the C++ math function library. Note that the argument to this function must be specified in radians, not degrees. A source file that uses any of the math functions must include the line

		#include <cmath>

or, in an old C++ environment,

		#include <math.h>

It's legal and appropriate to assign one object to another, for instance by saying

oldCatPosition = newCatPosition;

Note also that an object's member function (e.g. IsBetween) can access the private data of any other object of the same class.

Miscellaneous requirements

You should test the member functions of the Position class in isolation, using appropriate test data, to see if they work independently before calling them from the RunChase function. Tutors may ask to see the results of these tests. Your test data should include the following values:
cat radius
cat angle
mouse angle
test case features
1.0
35.0°
396.0°
angle > 360°;
mouse caught immediately
3.2
0.0°
-57.0°
negative angle;
cat radius has fractional part;
mouse caught at time 17

plus the following cases:

Be sure to check that the output makes sense for these tests, since the output is what most tutors will check first. Students are sometimes embarrassed to have a tutor point out that their programs have the cat moving deeper and deeper inside the statue, or moving away from the mouse instead of approaching it.

Your code should call Position member functions where appropriate; code in cat+mouse.cpp should not duplicate their functionality. You should not change the public interface of the Position class; you may, however, provide additional private member functions. (Don't forget to provide the function prototypes for any functions you provide.) Your program should also adhere to standards described in the section "Style guidelines" of this document.

Checklist

Correctly working code.
No changes made to functions already provided in cat+mouse.cpp, or to the public interface in positions.h.
Sufficient testing, with output sufficient to verify test correctness:
tests on specified values for cat and mouse positions;
evidence of independent tests of all functions in positions.cpp;
tests sufficient to exercise all statements in the program.
Printed listings of program text, tests, and test results.
Adherence to CS 9F style standards:
appropriate use of indenting and white space;
avoidance of "forbidden C++";
variable and function names that reflect their use;
informative comments at the head of each function;
no routine more than twenty-four lines of code.
Clean case analysis and simple loop structuring.
Suitable input prompts and informative output, including at least the positions of the cat and mouse after each move, and the final result of the chase.

cat+mouse.cpp framework

#include <iostream>
#include <cmath>
using namespace std;

#include "positions.h"

// You define the GetPositions function.
// It should read legal cat and mouse positions from the user
// and return the position values in its two arguments.
void GetPositions ( ... [you fill these in] ) {
	... [you fill this in]
}

// You define the RunChase function.
// Given initialized cat and mouse positions,
// it should simulate the cat chasing the mouse, printing the 
// result of each movement of cat and mouse.  Either the cat will 
// catch the mouse, or 30 time units will go by and the cat will 
// give up.
void RunChase ( ... [you fill this in] ) {
	... [you fill this in]
}

int main () {
	Position cat, mouse;
	GetPositions (cat, mouse);
	RunChase (cat, mouse);
	return 0;
}

positions.h

#ifndef POSITIONS_H
#define POSITIONS_H

class Position {
public:
	// Initialize a position. It consists of a radius expressed in meters
	// and an angle expressed in radians.
	Position ();	// radius = 1, angle = 0.
	Position (float r);	// angle = 0.
	Position (float r, float thetaInRadians);

	// Reinitialize this position.
	void SetAbsolutePosition (float r, float thetaInRadians);

	// Change this position, incrementing the radius by rChange
	// and incrementing the angle by angularDistChange/myRadius.
	// angularDistChange is expressed in meters.
	// One of the two arguments must be 0.
	// Negative radius values represent movement toward the statue.
	// Positive distance changes represent counterclockwise motion; 
	// negative values are clockwise.
	void IncrementPosition (float rChange, float angularDistChange);

	// Print this position.
	void Print ( );

	// Return true if someone at this position can see someone or
	// something at the argument position (i.e. the statue does
	// not block one's view), and return false otherwise.
	// The argument position must be at the statue.
	bool Sees (Position pos);

	// Return true if this position is at the base of the statue,
	// i.e. its radius is 1, and return false otherwise.
	bool IsAtStatue ( );

	// Return true if this position is between the first argument
	// position and the second.  Precondition: the counterclockwise
	// difference between the first and second argument positions 
	// is less than ¼ radians, and the radii of all the positions 
	// are the same.
	bool IsBetween (Position pos1, Position pos2);

private:
	float myRadius;
	float myAngleInRadians;
};

#endif

CS 9F programming assignment 2
Flow of control project B

Background

For this assignment, you complete a program to play the game of "Twenty-one" (also called "Blackjack").

The general idea of Twenty-one is as follows. There are two players, the customer and the dealer. The dealer deals a card face down to the customer, then a card face up to herself (the up-card), then another card to the customer and another card to herself, both face down. In most cases, the customer may then ask for more cards, one at a time. The dealer does the same, and the hand is resolved. The object of the game is to be dealt a set of cards whose total value is as close to 21 as possible without going over.

Cards are dealt from a deck of 52. There are four cards in the deck with value 2, four with value 3, and so on up to value 9; there are sixteen cards with value 10 (four 10's and twelve face cards: four jacks, four queens, and four kings); and there are four cards ("aces") whose value may be either 1 or 11 depending on the value of a player's remaining cards. For both players, an ace counts 11 unless the hand total is more than 21, in which case it counts 1.

For our purposes, the actual play of the game proceeds as follows. Each player receives two cards as described above. If the dealer's cards total 21—that is, one has value 10 and the other is an ace—the dealer has a "blackjack" and wins the hand. Otherwise, if the customer's cards total 21, she has a blackjack and wins the hand. Otherwise, the customer may ask for more cards one by one, until either she goes over 21—in which case she loses, or "busts"—or has a total that she thinks is high enough to beat the dealer. After the customer has stopped with a total at most 21, the dealer draws, taking another card if her total is 16 or less, and stopping if her total is over 16. If the dealer busts, she loses. If neither player has bust, the totals of the two sets of cards are compared. If the customer's is higher, she wins; otherwise, the dealer wins.

Productive strategies in Twenty-one involve taking account of the dealer's up-card. For instance, it will be easier for the dealer to win if her up-card is an ace or a face card.

Examples

Some annotated sample hands are shown below. Only the values of the cards are listed, except for aces.

Customer hand = 10 ace
Dealer hand = ace 10
Dealer has blackjack and wins immediately, even though the customer also has blackjack.
Customer hand = 10 ace
Dealer hand = 4 9
Customer has blackjack and wins immediately.
Customer hand = 4 ace
Dealer hand = 4 9
Customer continues, and is dealt a 7; her hand now totals 12.
Customer continues, and is dealt a 10; dealer wins.
Customer hand = 4 ace
Dealer hand = 4 9
Customer continues, and is dealt a 7; her hand now totals 12.
Customer continues, and is dealt an ace; her hand now totals 13.
Customer continues, and is dealt a 5; her hand now totals 18.
Dealer draws a 10 and loses.
Customer hand = 4 ace
Dealer hand = 4 9
Customer continues, and is dealt a 7; her hand now totals 12.
Customer continues, and is dealt an ace; her hand now totals 13.
Customer continues, and is dealt a 5; her hand now totals 18.
Dealer's total is less than 17, so she continues, drawing a 3; her hand now totals 16.
Dealer's total is less than 17, so she continues, drawing an ace; her hand now totals 17.
Dealer must stand. Customer has more than dealer, so customer wins.
Customer hand = 4 ace
Dealer hand = 4 9
Customer continues, and is dealt a 3; her hand now totals 18.
Dealer's total is less than 17, so she continues, drawing a 3; her hand now totals 16.
Dealer's total is less than 17, so she continues, drawing a 5; her hand now totals 21.
Dealer must stand. Dealer has more than customer, so dealer wins.

Problem

Complete the program in the file ~cs9f/lib/blackjack.cpp to play games of Twenty-one. The program begins each game by calling the DealFirstFourCards function, which initializes the customer and dealer hands as described above. After checking for blackjacks, it calls the ResultOfPlay function to play the game. You supply the code for these two functions. Don't change any of the code that's provided in the file.

The program uses two classes, declared in ~cs9f/lib/hands.h, to represent the dealer and customer hands; you are also to provide two versions of a file hands.cpp that correctly implements the operations of these classes. These versions will differ only in the CanDraw member function of the CustomerHand class. The idea is to to test two strategies for the customer, namely the dealer's strategy—draw to a total of 16 or under—and the following alternative:

Take a card if and only if your hand total is less than 17 and the dealer's up card is a 7, 8, 9, 10, or ace, or if your hand total is less than 12 and the dealer's up card is a 2, 3, 4, 5, or 6. (The idea is that in the second case, the dealer is much more likely to bust since there are more 10's than any other card.)

Your program will play fifty games of Twenty-one with each strategy and report how many games were won by dealer and customer in each run. A blackjack should count as two wins.

System information

Your program will include calls to member functions of Deck and Card classes that we supply in the file ~cs9f/lib/cards.h. The Deck constructor may be given a boolean argument that says whether or not to produce debugging output when the deck is shuffled or a card is drawn; the code in blackjack.cpp enables this deck option. One of the functions, Shuffle, should be called at the start of each game; another, Deal, should be called to deal a card from the deck. The Value and IsAce functions will help in evaluating a hand. The Name function should be used when printing a hand, for instance as follows:

	cout << card.Name ();

The file cards.cpp is available in ~cs9f/lib for inclusion in the executable version of your program. (The techniques it uses won't be covered in CS 9F until the "Vectors" segment.) A command to create an executable version of the program might then be

	g++ -Wall -g blackjack.cpp hands.cpp ~cs9f/lib/cards.cpp

You can abbreviate this command using the make program described later in this document; a "make file" for this assignment is available in ~cs9f/lib/p2b.makefile. You may also download this file in case you're using a C++ environment on some other computer.

Miscellaneous requirements

You do not need to store the individual cards in the hands (that requires an array). You only need to maintain each hand's point total.

You should test the member functions of the ...Hand classes in isolation, using appropriate test data, to see if they work independently before calling them from the program in blackjack.cpp. Tutors may ask to see the results of these tests. Tests should also include whatever other values are necessary to ensure that all statements in your program have been executed at least once. Be sure to check that your output makes sense, since the output is what most tutors will check first. Students are sometimes embarrassed to have a tutor point out that their programs have the customer never drawing a card, or always valuing an ace as 1.

Some other useful tests involve the possibilities for valuing an ace. Test your program on the following situations:

a hand containing a single ace whose final value is 11;
a hand containing a single ace that initially is valued at 11, but whose value must change to 1 because of subsequently drawn cards;
a hand containing a single ace that initially is valued at 1;
a hand containing more than one ace, one of which has a final value of 11;
a hand containing more than one ace, the first of which initially is valued at 11 but whose value much change to 1 because of subsequently drawn cards;
a hand containing more than one ace, all of which have initial values of 1.

Your program runs must also provide enough evidence that your simulation is accurate: that hands are dealt and evaluated correctly, and that the rules and strategies described above are correctly implemented. Include in your program sufficient output for the tutor to be able to verify its correctness quickly. (One way to improve the readability of your output is to indent the intermediate output and not to indent the result of each game. For instance:

	Dealing 9 of spades
	Dealing 9 of clubs
	Dealing 4 of hearts
	Dealing 5 of clubs
	Customer hand: 2 cards, 0 aces counting 11, 18 total.
	Dealer hand: 2 cards, 0 aces counting 11, up card = 9 of clubs, total = 9
	Customer stands.
	Dealing 3 of spades
	Dealing 3 of hearts
	Dealing 9 of hearts
WIN:  Dealer bust with 24

Your output should include examples of each case of the dealer's strategy, as well as the customer's strategy as displayed in the following table:
your total dealer's up card your total dealer's up card
< 17 7, 8, 9, 10, A < 12 2 through 6
= 17 7, 8, 9, 10, A = 12 2 through 6
> 17 7, 8, 9, 10, A > 12 2 through 6
< 17 2 through 6 < 12 7, 8, 9, 10, A
= 17 2 through 6 = 12 7, 8, 9, 10, A
> 17 2 through 6 > 12 7, 8, 9, 10, A

It is probably easier to provide this output by constructing cards by hand using the ChangeCard function rather than relying on a shuffled deck to produce the right numbers.

Your code should call ...Hand member functions where appropriate; code in blackjack.cpp should not duplicate their functionality. You should not change the public interface of the ...Hand classes; you may, however, provide additional private member functions. (Don't forget to provide the function prototypes for any functions you provide.) Your program should also adhere to standards described in the section "Style guidelines" of this document. You don't need arrays to solve this problem

Checklist

Correctly working code:
completion of blackjack.cpp without duplicating functionality in ...Hand member functions;
completion of two versions of hands.cpp (one for each player strategy mentioned in the assignment), without adding any public member functions to the ...Hand classes.
Sufficient testing, with output sufficient to verify test correctness:
tests on specified strategy situations;
tests of hands that include aces as specified;
evidence of independent tests of all functions in hands.cpp;
tests sufficient to exercise all statements in the program.
Printed listings of program text, tests, and test results.
Adherence to CS 9F style standards:
appropriate use of indenting and white space;
avoidance of "forbidden C++"
variable and function names that reflect their use;
informative comments at the head of each function;
no routine more than twenty-four lines of code.
Clean case analysis and simple loop structuring.
Suitable input prompts and informative output, including at least the final result of each game and the total number of wins for a given strategy.

blackjack.cpp framework

#include <iostream>
using namespace std;

#include "cards.h"
#include "hands.h"

// You complete this function, which takes two hands and a deck as arguments. 
// It returns the result of dealing two cards each to the dealer hand and 
// the customer hand arguments.  The first card in the deck argument goes 
// to the customer, the second to the dealer, the third to the customer, and the fourth to 
// the dealer. DealFirstFourCards should also print appropriate debugging output.
void DealFirstFourCards ( ... [you fill this in] ) {
	... [you fill this in]
}

// You complete this function, which takes two hands and a deck as arguments. 
// On entry, each hand has already been dealt two cards, and neither hand has 
// a blackjack (a total of 21). ResultOfPlay returns the result of playing the hand. 
// First, the customer draws cards until he/she either goes past 21 (a "bust") 
// or reaches a suitable total less than or equal to 21. If the customer has not bust, 
// then the dealer draws cards until reaching a total of 17 or more. 
// ResultOfPlay returns 0 if the customer busts or if the customer's total is 
// less than or equal to the dealer's and the dealer hasn't bust. 
// ResultOfPlay returns 1 if the dealer busts or if the customer's total 
// exceeds the dealer's and the customer has not bust.
// ResultOfPlay should also print appropriate debugging output.
int ResultOfPlay ( ... [you fill this in] ) {
	... [you fill this in]
}
int main () {
	Deck d(true);
	int numGames, numWins;
	DealerHand dealerHand;
	CustomerHand ourHand;
	cout << "How many games? ";
	cin >> numGames;
	numWins = 0;
	for (int k=1; k<=numGames; k++) {
		DealFirstFourCards (dealerHand, ourHand, d);
		if (dealerHand.Total()==21) {
			cout << "LOSS: Dealer has blackjack" << endl;
		} else if (ourHand.Total()==21) {
			numWins += 2;
			cout << "WIN:  We have blackjack" << endl;
		} else {
			numWins += ResultOfPlay (dealerHand, ourHand, d);
		}
	}
	cout << "We won " << numWins << " out of " << numGames << endl;
	return 0;
}

cards.h

#ifndef _CARDS
#define _CARDS
#include <iostream>
#include <vector>
#include <string>

class Card {
public:
	// Construct a card initialized to the ace of clubs.
	Card ();
	
	// Change the card to the nth card in the sequence
	// ace of clubs, 2 of diamonds, 3 of hearts, 4 of spades,
	// 5 of clubs, ..., queen of spades,
	// king of clubs, ace of diamonds, 2 of hearts, 3 of spades,
	// 4 of clubs, ..., jack of spades,
	// queen of clubs, king of diamonds, ace of hearts, 2 of 
	// spades, etc.
	void ChangeCard (int n);
	
	// Return the card's value in blackjack: 11 if it's an ace,
	// 10 if it's a face card (king, queen, or jack), and the 
	// card's face value (2, 3, ..., or 10) otherwise.
	int Value ();
	
	// Return the name of the card.
	string Name ();
	
	// Return true if the card is an ace, and return false 
	// otherwise.
	bool IsAce ();
	
	// Return true if the card is a face card (king, queen, or 
	// jack), and return false otherwise.
	bool IsFaceCard ();
	
	// Return true if the card outranks the argument card.
	// One card outranks another if it has a higher value.
	// Any face card outranks a 10; a king outranks a queen or 
	// jack; and a queen outranks a jack.
	bool Outranks (Card c);
	
	// Return true if the card and the argument card are equal 
	// in value.
	bool EqualsInRank (Card c);
	
	// Return true if the card and the argument card are the 
	// same suit.
	bool IsSameSuit (Card c);
	
private:
	int myRank;
	int mySuit;
};

class Deck {
public:
	// Construct a deck of 52 cards.
	// If a true argument is given, it causes debugging output
	// to be printed to cerr every time a card is dealt or the deck
	// shuffled.
	Deck (bool debug=false);
	
	// Shuffle the deck, i.e. randomly permute its cards.
	void Shuffle ();
	
	// Print the contents of the deck.
	void Print (ostream &output);
	
	// Return the top card of the deck, and remove that card from
	// the deck.  If the deck is empty, first shuffle the already-
	// dealt cards and deal from them.
	Card Deal ();
	
	// Return true if the deck is empty and false otherwise.
	bool IsEmpty ();
	
private:
	vector <Card> myCards;
	int myTop;
	bool debugging;
};
#endif

hands.h

#ifndef _HANDS
#define _HANDS
#include "cards.h"

class DealerHand {
public:
	// Construct an empty hand for the dealer.
	DealerHand ();
	// Reinitialize the hand to contain no cards.
	void Reset ();
	// Add the given card to the hand.
	void AddCard (Card c);
	// Return true if, according to the dealer's strategy, another
	// card should be drawn.  (The dealer's strategy is to draw a
	// card when his/her total is less than or equal to 16.)
	bool CanDraw ();
	// Return the dealer's "up card" (the first card dealt to the
	// dealer at the start of each game).
	Card UpCard ();
	// Return the dealer's total hand value.
	int Total ();
	// Print available information about the hand.
	void Print ();
private:
	Card myUpCard;
	int myCardCount;
	int myAceAs11Count;
	int myTotal;
};

class CustomerHand {
public:
	// Construct an empty hand for the customer.
	CustomerHand ();
	
	// Reinitialize the hand to contain no cards.
	void Reset ();
	
	// Add the given card to the hand.
	void AddCard (Card c);
	
	// Return true if, according to the player's strategy, another
	// card should be drawn.
	bool CanDraw (Card dealerUpCard);
	
	// Return the total hand value.
	int Total ();
	
	// Print available information about the hand.
	void Print ();
	
private:
	int myCardCount;
	int myAceAs11Count;
	int myTotal;
};
#endif

TOC PREV NEXT