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?
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())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()))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"))
@Paul, @Rutger: could you please tell me how do you insert your code in such a beauty way? Which website do you use?
@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.
@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.
@Paul Thanks it works fine!
A Haskell version.
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') import Control.Monad (liftM) 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 where suitify (rss@((r, _) : _)) = (r, map snd rss) 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 :: [String] -> [String] padR xs = let maxLen = maximum $ map length xs in map (padTo maxLen) 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 printHand $ padL 20 n printHand $ padR w `followedBy` padL 30 e printHand $ padL 20 s$ ./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[…] Bridge hands problem in C++ […]
[…] bridge hands problem on programming praxis in Common […]