The card game of Euchre has an unusual ordering of cards in the hand. This week's Ruby Quiz is to take a random Euchre hand and sort it.
The first thing you need to know is that Euchre is played with a small deck of cards. Four suits are used Diamonds (d), Clubs (c), Spades (s), and Hearts (h), but each suit has only the cards Nine (9), Ten (T), Jack (J), Queen (Q), King (K), and Ace (A). The cards are generally ordered as I just listed them, Nine being the low card and Ace the high card. The exception is the "Bowers".
When a Euchre hand is started, the first task is to select a Trump suit. How that's done is not important, just know that one suit is always different from the rest. Trump is the best suit, valued higher than the other three suits (which are basically equal). In the Trump suit, the card order changes.
The first oddity of Trump is that the Jack of the selected suit becomes the Right Bower, the highest ranked Trump card. The second oddity is that the other Jack of the same color (Diamonds and Hearts are red while Clubs and Spades are black) becomes the Left Bower, the second highest Trump card. This card is considered to be of the Trump suit for the rest of the hand. For example, if Spades is selected as Trump, the order of Spades becomes (lowest to highest): 9s, Ts, Qs, Ks, As, Jc, and Js. All other suits run Nine to Ace, save that Clubs will be short a Jack.
The three non-Trump suits are equal, but it is good interface to sort the by suit alternating red, black, red, and black, I think. Especially with a GUI, this makes it easier to understand the hand.
Input (on STDIN) will be a line containing the Trump suit, followed by five lines containing a Euchre hand. For example:
Diamonds
Kc
Jh
Kd
Td
Ah
Your script should output (to STDOUT) the Trump suit, followed by the cards in sorted order (highest card first):
Diamonds
Jh
Kd
Td
Kc
Ah
Here's a script that will feed your program random hands:
# build a Euchre deck
cards = Array.new
%w{9 T J Q K A}.each do |face|
%w{d c s h}.each do |suit|
cards << face + suit
end
end
# choose trump
puts %w{Diamonds Clubs Spades Hearts}[rand(4)]
# deal a hand
cards = cards.sort_by { rand }
puts cards[0..4]
One last thought: If accuracy is our ultimate goal here, how will you know your output is correct?
Quiz Summary
My wife can't stand how Euchre reorders the suits. "Everyone know Jacks come after Tens," she would say. Funny how most of us except the odd Ace concept where the low card is frequently the high card, or even both. Once you start moving Jacks though, a lot of people have trouble following the game.
Of course, my wife would also say, "How can you call that Jack a Club (assuming Clubs are Trump), when it has Spades printed on it?" She's got me there.
My wife's analysis of Euchre's card order is really right on: It's unusual. Because of that, the traditional sort() tool won't get you all the way to a solution. That makes us think about other options.
Don't get me wrong, many solutions bent sort() to their will. However, as I said in the quiz, I was looking as much at how to know the answer is correct as I was at just having an answer. To me, the weighted sorts are hard to prove and no one did it to my satisfaction. Given that, I want to look at the other major approach people hit on.
The following is the beginning of Bob Showalter's solution:
# Euchre hand sorter
CARDS = %w(A K Q J T 9)
suits = %w(Spades Hearts Clubs Diamonds)
# read and check input data
trump = gets.chomp
raise "Invalid Trump Suit" unless suits.include? trump
hand = []
5.times do
card = gets.chomp
raise "Invalid card #{card}" unless
card.length == 2 &&
CARDS.include?(card[0,1]) &&
suits.find { |suit| suit[0,1].downcase == card[1,1] }
raise "Duplicate card #{card}" if hand.include? card
hand << card
end
# ...
Bob begins by defining the traditional card order, and the four suits.
The next chunk of code reads the trump suit and hand into variables. This code is careful to ensure proper input. Suits must be one of the known four and cards must be two characters, have a known face and suit, and not be duplicated.
# rotate trump suit to front
suits.push(suits.shift) while suits.first != trump
# if hand is void in second suit, swap second and fourth
# (this keeps output in alternating colors)
unless hand.find { |card| card[1,1] == suits[1][0,1].downcase }
suits[1], suits[3] = suits[3], suits[1]
end
# ...
This chunk of code does two things. First, the suits are "rotated" until trump is the first suit. Note that this is why "CARDS" was declared as a constant but "suits" is just a variable. Good hints for the reader in this code.
The second thing the code does is the trick many hit on to defeat my extra red/black requirement. If we have four suits ordered red, black, red, black (or black, red, black, red), the only edge case we can do anything about is when the player doesn't have the second suit. Here's the mapping of possible orderings (assuming Spades is trump) to show what I mean:
all four suits (black, red, black, red ordering works fine)
Spades, Hearts, and Clubs (already correct)
Hearts, Clubs, and Diamonds (already correct)
Spades, Hearts, and Diamonds (can't move trump to fix)
Spades, Clubs, and Diamonds (the fixable case)
just two suits (moves won't change anything)
a single suit (already correct)
In the fixable case, swapping the (missing) second and (present) fourth suits resolves the issue. That's what you're seeing in the code above.
# generate a sort order
deck = []
suits.each do |suit|
CARDS.each do |card|
deck << card + suit[0,1].downcase
end
end
# ...
Given that the correct suit order has been established, the entire Euchre deck is built, in traditional card order.
# move bowers to front
deck.insert(0, deck.delete_at(3))
deck.insert(1, deck.delete_at(15))
# ...
With the full deck in proper suit order, the only thing that remains is to shift the bowers to the head of the pack. Their location is assured, thus the use of magic numbers above, though index() is an alternative.
# output sorted hand (n.b. Array#& (intersection) seemed to work, but
# is the order guaranteed?)
puts trump
puts deck.select { |card| hand.include? card }
With the work out of the way, showing the results is trivial. Print trump and then just walk the deck printing any card in the hand.
I think that solution is very straight-forward and easy to accept as correct, even without a large group of tests. Let's look at one more variation of the same theme, by Dominik Bathon:
"Spades" => "JsJcAsKsQsTs9sAdKdQdJdTd9dAcKcQcTc9cAhKhQhJhTh9h",
"Hearts" => "JhJdAhKhQhTh9hAsKsQsJsTs9sAdKdQdTd9dAcKcQcJcTc9c",
"Clubs" => "JcJsAcKcQcTc9cAhKhQhJhTh9hAsKsQsTs9sAdKdQdJdTd9d",
"Diamonds" => "JdJhAdKdQdTd9dAcKcQcJcTc9cAhKhQhTh9hAsKsQsJsTs9s"
}
trump = gets.strip
cards = readlines.map { |l| l.strip }
o = order[trump].dup
# do we have a card of the 2nd suit
unless cards.any? { |card| card[1] == o[15] }
# if not replace second suit by the last
o[14, 12] = o[36, 12]
end
puts trump, cards.sort_by { |card| o.index(card) }
This is basically the same thing, with prebuilt decks. The code here just selects which of the four possible decks to use base on the trump indication in the input.
Again we see the second and fourth suits swapped, if needed. This time it is done with a simple String copy.
Finally, cards are sorted, by their position in the complete deck.
My thanks to all the card sharks who solved the quiz and even taught me alternate hand ordering schemes.
Tomorrow we're off to the Pinewood Derby, so everyone pick up a car kit sometime this evening...