\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