\input bkmacs \projchap{Project: Scoring Bridge Hands} \write\toc{\begingroup\blskip{1}} %%% ends in leap.tex At the beginning of a game of bridge, each player assigns a value to his or her hand by counting {\it points.\/} Bridge players use these points in the first part of the game, the ``bidding,'' to decide how high to bid. (A bid is a promise about how well you'll do in the rest of the game. If you succeed in meeting your bid you win, and if you don't meet the bid, you lose.) For example, if you have fewer than six points, you generally don't bid anything at all. You're going to write a computer program to look at a bridge hand and decide how many points it's worth. You won't have to know anything about the rest of the game; we'll tell you the rules for counting points. A bridge hand contains thirteen cards. Each ace in the hand is worth four \justidx{bridge points} \justidx{points, bridge} points, each king is worth three points, each queen two points, and each jack one. The other cards, twos through tens, have no point value. So if your hand has two aces, a king, two jacks, and eight other cards, it's worth thirteen points. A bridge hand might also have some ``distribution'' points, which are points having to do with the distribution of the thirteen cards among the four suits. If your hand has only two cards of a particular suit, then it is worth an extra point. If it has a ``singleton,'' only one card of a particular suit, that's worth two extra points. A ``void,'' no cards in a particular suit, is worth three points. In our program, we'll represent a card by a word like {\tt h5} (five of hearts) or {\tt dk} (king of diamonds).\footnt{Why not {\tt 5h}? Scheme words that begin with a digit but aren't numbers have to be surrounded with double-quote marks. Putting the suit first avoids that.} A hand will be a sentence of cards, like this: {\prgex% (sa s10 s7 s6 s2 hq hj h9 ck c4 dk d9 d3) } This hand is worth 14 points:\ ace of spades (4), plus queen of hearts (2), plus jack of hearts (1), plus king of clubs (3), plus king of diamonds (3), plus one more for having only two clubs. To find the suit of a card, we take its {\tt first}, and to find the rank, we take the {\tt butfirst}. (Why not the {\tt last}?) We have a particular program structure in mind. We'll describe all of the procedures you need to write; if you turn each description into a working procedure, then you should have a complete program. In writing each procedure, take advantage of the ones you've already written. Our descriptions are ordered {\it \idx{bottom-up}\/}, which means that for each procedure you will already have written the helper procedures you need. (This ordering will help you write the project, but it means that we're beginning with small details. If we were describing a project to help you understand its structure, we'd do it in {\it \idx{top-down}\/} order, starting with the most general procedures. We'll do that in the next chapter, in which we present a tic-tac-toe program as a larger Scheme programming example.) \function{Card-val} Write a procedure {\tt \ufun{card-val}} that takes a single card as its argument and returns the value of that card. {\prgex% > (card-val 'cq) 2 > (card-val 's7) 0 > (card-val 'ha) 4 } \solution {\prgex% (define (card-val card) (cond ((equal? (bf card) 'a) 4) ((equal? (bf card) 'k) 3) ((equal? (bf card) 'q) 2) ((equal? (bf card) 'j) 1) (else 0))) } @ \function{High-card-points} Write a procedure {\tt \ufun{high-card-points}} that takes a hand as its argument and returns the total number of points from high cards in the hand. (This procedure does {\it not\/} count distribution points.) {\prgex% > (high-card-points '(sa s10 hq ck c4)) 9 > (high-card-points '(sa s10 s7 s6 s2 hq hj h9 ck c4 dk d9 d3)) 13 } \solution {\prgex% (define (high-card-points hand) (accumulate + (every card-val hand))) } @ \function{Count-suit} Write a procedure {\tt \ufun{count-suit}} that takes a suit and a hand as arguments and returns the number of cards in the hand with the given suit. {\prgex% > (count-suit 's '(sa s10 hq ck c4)) 2 > (count-suit 'c '(sa s10 s7 s6 s2 hq hj h9 ck c4 dk d9 d3)) 2 > (count-suit 'd '(h3 d7 sk s3 c10 dq d8 s9 s4 d10 c7 d4 s2)) 5 } \solution {\prgex% (define (count-suit suit hand) (count (keep (lambda (card) (equal? (first card) suit)) hand))) } Thinking about domain and range really helps in this problem; there are many ways to get confused about what to use as argument to which helper function. In particular, it's important to distinguish among the data types suit, card, and hand. @ \function{Suit-counts} Write a procedure {\tt \ufun{suit-counts}} that takes a hand as its argument and returns a sentence containing the number of spades, the number of hearts, the number of clubs, and the number of diamonds in the hand. {\prgex% > (suit-counts '(sa s10 hq ck c4)) (2 1 2 0) > (suit-counts '(sa s10 s7 s6 s2 hq hj h9 ck c4 dk d9 d3)) (5 3 2 3) > (suit-counts '(h3 d7 sk s3 c10 dq d8 s9 s4 d10 c7 d4 s2)) (5 1 2 5) } \solution {\prgex% (define (suit-counts hand) (every (lambda (suit) (count-suit suit hand)) '(s h c d))) } This solution is not obvious, because the second argument to {\tt every} is a constant, rather than one of the original arguments. Students may find it easier to understand this less elegant alternative: {\prgex% (define (suit-counts hand) (se (count-suit 's hand) (count-suit 'h hand) (count-suit 'c hand) (count-suit 'd hand))) } @ \function{Suit-dist-points} Write {\tt \ufun{suit-dist-points}} that takes a number as its argument, interpreting it as the number of cards in a suit. The procedure should return the number of distribution points your hand gets for having that number of cards in a particular suit. {\prgex% > (suit-dist-points 2) 1 > (suit-dist-points 7) 0 > (suit-dist-points 0) 3 } \solution {\prgex% (define (suit-dist-points n) (if (<= n 2) (- 3 n) 0)) } \noindent or, the slightly less elegant way: {\prgex% (define (suit-dist-points n) (cond ((= n 0) 3) ((= n 1) 2) ((= n 2) 1) (else 0))) } @ \function{Hand-dist-points} Write {\tt \ufun{hand-dist-points}}, which takes a hand as its argument and returns the number of distribution points the hand is worth. {\prgex% > (hand-dist-points '(sa s10 s7 s6 s2 hq hj h9 ck c4 dk d9 d3)) 1 > (hand-dist-points '(h3 d7 sk s3 c10 dq d8 s9 s4 d10 c7 d4 s2)) 3 } \solution {\prgex% (define (hand-dist-points hand) (accumulate + (every suit-dist-points (suit-counts hand)))) } @ \function{Bridge-val} Write a procedure {\tt \ufun{bridge-val}} that takes a hand as its argument and returns the total number of points that the hand is worth. {\prgex% > (bridge-val '(sa s10 s7 s6 s2 hq hj h9 ck c4 dk d9 d3)) 14 > (bridge-val '(h3 d7 sk s3 c10 dq d8 s9 s4 d10 c7 d4 s2)) 8 } \solution {\prgex% (define (bridge-val hand) (+ (high-card-points hand) (hand-dist-points hand))) } @ \bye