Bridge Hands

August 11, 2015

This problem comes to us courtesy of The Awk Programming Language by Al Aho, Brian Kernighan and Peter Weinberger, where it appears as exercise 5-3. Their solution is on the next page. We represent the deck as an array of cards, with cards as integers from 51 (ace of spades) to 0 (deuce of clubs). We begin with a function that deals the deck and arranges the four hands in order:

```(define (hands)
(let loop ((deck (shuffle (range 52)))
(hands (list)))
(if (null? deck) hands
(loop (drop 13 deck)
(cons (sort > (take 13 deck)) hands)))))```

The output of `hands` contains the four hands in order North, West, East, South. Then we write a function that prints the formatted output:

```(define (print-hands hands)
(define (blanks n) (display (make-string n #\space)))

(blanks 20) (display "NORTH") (newline)
(blanks 20) (display (cards "S" (car hands))) (newline)
(blanks 20) (display (cards "H" (car hands))) (newline)
(blanks 20) (display (cards "D" (car hands))) (newline)
(blanks 20) (display (cards "C" (car hands))) (newline)

(set! hands (cdr hands))

(display "WEST") (blanks 36) (display "EAST") (newline)
(display (cards "S" (car hands)))
(blanks (- 40 (string-length (cards "S" (car hands)))))
(display (cards "S" (cadr hands))) (newline)
(display (cards "H" (car hands)))
(blanks (- 40 (string-length (cards "H" (car hands)))))
(display (cards "H" (cadr hands))) (newline)
(display (cards "D" (car hands)))
(blanks (- 40 (string-length (cards "D" (car hands)))))
(display (cards "D" (cadr hands))) (newline)
(display (cards "C" (car hands)))
(blanks (- 40 (string-length (cards "C" (car hands)))))
(display (cards "C" (cadr hands))) (newline)

(set! hands (cddr hands))

(blanks 20) (display "SOUTH") (newline)
(blanks 20) (display (cards "S" (car hands))) (newline)
(blanks 20) (display (cards "H" (car hands))) (newline)
(blanks 20) (display (cards "D" (car hands))) (newline)
(blanks 20) (display (cards "C" (car hands))) (newline))```

The `cards` function returns a string containing the cards of the designated suit in the designated hand; it calls a function `face` to convert the card index to a face value:

```(define (cards suit hand)
(define (between lo hi) (lambda (x) (<= lo x hi)))
(cond ((string=? suit "S")
(string-join #\space (cons "S:"
(map face (filter (between 39 51) hand)))))
((string=? suit "H")
(string-join #\space (cons "H:"
(map face (filter (between 26 38) hand)))))
((string=? suit "D")
(string-join #\space (cons "D:"
(map face (filter (between 13 25) hand)))))
((string=? suit "C")
(string-join #\space (cons "C:"
(map face (filter (between  0 12) hand)))))))

(define (face card)
(case (modulo card 13)
((12) "A")
((11) "K")
((10) "Q")
((9)  "J")
(else (number->string (+ (modulo card 13) 2)))))
```

You can run the program at http://ideone.com/IcpYvT, where you will also see the contributions from the Standard Prelude. Here’s a sample run:

```> (print-hands (hands))
NORTH
S: A Q J 10 8
H: 5 4 2
D: 9
C: 10 7 6 2
WEST                                    EAST
S: 7                                    S: 6
H: Q J 7 6 3                            H: K 10 8
D: J 10 6 4 3                           D: A K Q 5
C: A 8                                  C: K 9 5 4 3
SOUTH
S: K 9 5 4 3 2
H: A 9
D: 8 7 2
C: Q J```

I have no direct experience teaching beginning programmers, but I suspect this would make a good assignment partway through a first programming course, as the imperative version of the program uses arrays, loops, random numbers and a little bit of arithmetic, data types for the cards, sorting, and formatted output, and requires no input. I know some of my readers do teach beginning programmers. What do you think?

Pages: 1 2 3

10 Responses to “Bridge Hands”

1. Paul said

In Python.

```import random
from itertools import groupby
from operator import itemgetter

VALUES = list("23456789TJQKA")
VALUES[8] = "10"

def random_hand():
cards = list(range(52))
random.shuffle(cards)
return [sorted(cards[i*13:i*13+13], reverse=True) for i in range(4)]

def nr_to_card(n):
suit, value = divmod(n, 13)
return "CDHS"[suit], VALUES[value]

def hand(side):
cards = [nr_to_card(c) for c in side]
cc = groupby(cards, key=itemgetter(0))
D = {k:"{:s}: {:s}".format(k, " ".join(v for s, v in g)) for k, g in cc}
return [D[k] if k in D else k + ":" for k in "SHDC"]

def print_bridge(hands):
output = [[" "] * 80 for _ in  range(15)]
pos = [(0, 20), (5, 0), (5, 40), (10, 20)]
seats = ["NORTH", "WEST", "EAST", "SOUTH"]
for (row, col), seat, hand_ in zip(pos, seats, hands):
output[row][col:col+len(seat)] = seat
for i, hi in enumerate(hand(hand_)):
output[row+i+1][col:col+len(hi)] = hi
for o in output:
print ("".join(o))

print_bridge(random_hand())
```
2. Rutger said

Good to see you recovered!
Hereby my Python solution..

```import random

suits = ["S", "H", "D", "C"]
values = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]

# returns tuples of (suit, value)
def deck(shuffle=True):
def card_number_to_card_tuple(n):
suit, value = divmod(n, 13)
return suits[suit], values[value]

all_cards = range(52)
if shuffle:
random.shuffle(all_cards)
return map(card_number_to_card_tuple, all_cards)

# returns list of 4 hands, with each hand as a dict with k=suit , v=list of values for suit k
def four_hands(deck):
hands = []
for hand in [deck[i*13:i*13+13] for i in range(4)]:
d = dict(zip(suits, [[],[],[],[]]))
for suit, value in hand:
d.setdefault(suit, []).append(value)
hands.append(d)
return hands

# prints output. Spent most time in that python format mini language..
def print_formatted(four_hands):
def hand_to_string(hand):
return [suit + ': ' + " ".join(hand[suit]) for suit in suits]

print ' '*20 + 'NORTH'
for north in hand_to_string(four_hands[0]):
print ' '*20 + north
print '{:<40}{:}'.format('WEST', 'EAST')
for west, east in zip(hand_to_string(four_hands[1]), hand_to_string(four_hands[2])):
print '{:<40}{:}'.format(west, east)
for south in hand_to_string(four_hands[3]):
print ' '*20 + south

print_formatted(four_hands(deck()))
```
3. Andras said

Scala:
import scala.util.Random
type Card = Tuple2[String, String]
val color: Seq[String] = List(“S”, “H”, “D”, “C”)
val numbers: Seq[String] = (2 to 10 map { _.toString }) ++ List(“J”, “Q”, “K”, “A”)
def numberSorter(a: String, b: String): Boolean = numbers.indexOf(a) > numbers.indexOf(b)
val cards: Seq[Card] = Random.shuffle(for (c <- color; n List(filter(x, “S”), filter(x, “H”), filter(x, “D”), filter(x, “C”)))
val blockLines = blocks.map(x => (List(x._1) ++ x._2).map(x => x + ” ” * (20 – x.length())))
val north = for (i <- 0 to 4) yield " " * 20 + blockLines(0)(i) + " " * 20
val west_east = for (i <- 0 to 4) yield blockLines(1)(i) + " " * 20 + blockLines(2)(i)
val south = for (i <- 0 to 4) yield " " * 20 + blockLines(3)(i) + " " * 20
println((north ++ west_east ++ south).mkString("\r\n"))

4. Andras said

@Paul, @Rutger: could you please tell me how do you insert your code in such a beauty way? Which website do you use?

5. Paul said

@Andras.I am using method Third as mentioned in the “Howto: Posting Source Code”. You have to know the mnemonic for Scala (I guess it is scala). Further the tag is usefull if you want to use a link in your post. I hope this helps.

6. Paul said

@Andras, the “a href” tag messed up part of my reply. You only have to google for this tag and see how it is used.

7. Andras said

@Paul Thanks it works fine!

8. Globules said

```import Data.Function (on)
import Data.List (groupBy, intercalate, sortOn)
import Data.List.Split (chunksOf)
import Data.Maybe (fromMaybe)
import Data.Ord (Down(..))
import System.Random
import System.Random.Shuffle (shuffle')

shuffle :: (RandomGen r) => [a] -> r -> [a]
shuffle xs = shuffle' xs (length xs)

-- A standard deck of 52 cards, where each card is a pair consisting of a suit
-- and a rank.
deck :: [(Int, Int)]
deck = [(s, r) | s <- [0..3], r <- [0..12]]

-- Deal hands of 13 cards.  Each hand consists of ordered lists of ranks grouped
-- by suit.
deal :: [(Int, Int)] -> [[(Int, [Int])]]
deal = map (map suitify . groupBy ((==) `on` fst) . sortOn Down) . chunksOf 13

showSuit :: Int -> String
showSuit = (["S", "H", "D", "C"] !!)

showRank :: Int -> String
showRank = (ranks !!)
where ranks = [ "2", "3",  "4", "5", "6", "7",
"8", "9", "10", "J", "Q", "K", "A"]

-- Each element of the result is one suit letter followed by the card ranks.
showHand :: [(Int, [Int])] -> [String]
showHand cs = map (\s -> showSuit s ++ ": " ++ showRanks (ranks s)) [0..3]
where ranks s = fromMaybe [] \$ lookup s cs
showRanks = unwords . map showRank

printHand :: [String] -> IO ()
printHand = putStrLn . intercalate "\n"

-- Pad all the strings to the length of the longest.
padR xs = let maxLen = maximum \$ map length xs
where padTo mx s = s ++ replicate (mx - length s) ' '

-- Prepend all the strings with spaces.
padL :: Int -> [String] -> [String]
padL n = map (replicate n ' ' ++)

-- Append corresponding elements of the lists.
followedBy :: [[a]] -> [[a]] -> [[a]]
followedBy = zipWith (++)

main :: IO ()
main = do
hands <- liftM (map showHand . deal . shuffle deck) getStdGen
let [n, s, e, w] = zipWith (:) ["NORTH", "SOUTH", "EAST", "WEST"] hands
```
```\$ ./bridge
NORTH
S: K Q 9 5
H: Q 10
D: K 9 8 3
C: 6 5 4
WEST                                     EAST
S: A 10 8 7                              S: J 6 3
H: K 8 2                                 H: J 9 7 6 3
D: 6 4                                   D: Q J 7
C: 9 8 3 2                               C: A Q
SOUTH
S: 4 2
H: A 5 4
D: A 10 5 2
C: K J 10 7
```
9. […] Bridge hands problem in C++ […]

10. […] bridge hands problem on programming praxis in Common […]