[QUIZ] Euchre Hands (#55)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

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:

  #!/usr/local/bin/ruby -w
  
  # 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?

Hello James,

I have to admit that I do not totally understand. Why is in the
example the King of Clubs higher than the Ace of hearts. I thought an
Ace would be higher than a King and Clubs and Hearts are equal except
for the Jack if the trump is Diamond.

And secondly I do not understand the paragraph about the red-black sort order.

cheers,

Brian

···

On 18/11/05, Ruby Quiz <james@grayproductions.net> wrote:

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

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:

        #!/usr/local/bin/ruby -w

        # 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?

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

Hmm. My original solution was to assign integer values to each card
and suit, and sort_by that mapping.

#!/usr/bin/ruby -w

NUMS = %w(9 T J Q K A)
SUITS = %w(h c d s)
COLOURS = Hash[*%w(s B d R c B h R)]

def card_value(card, trump)
  num, suit = card.split('')

  value = NUMS.index(num)
  value += 10 * SUITS.index(suit)
  if num == 'J'
    value += 150 if COLOURS[suit] == COLOURS[trump]
    value += 10 if suit == trump
  elsif suit == trump
    value += 100
  end
  
  return value
end

trump = gets.chomp
tsuit = trump[0,1].downcase

puts trump
puts $stdin.readlines.sort_by { |card| -card_value(card,tsuit) }

When I started to think about it though, that seemed a little silly to
me. If you ever use more than ten numbers, or more than 5 cards,
things start to get messy and sneaky bugs pop up. I realised that what
I was really doing was creating an array of base-10 numbers to
represent the cards. Why not just create an actual array and be done
with it?

So, here's my version 2:

#!/usr/bin/ruby -w

NUMS = %w(9 T J Q K A)
SUITS = %w(h c d s)
COLOURS = Hash[*%w(s B d R c B h R)]

def card_value(card, trump)
  num, suit = card.split('')
  
  value = Array.new
  
  value << (num == 'J' && value[3] == 1 ? 1 : 0)
  value << (num == 'J' && COLOURS[suit] == COLOURS[trump] ? 1 : 0)
  value << (suit == trump ? 1 : 0)
  value << SUITS.index(suit)
  value << NUMS.index(num)
end

trump = gets.chomp
tsuit = trump[0,1].downcase

puts trump
puts $stdin.readlines.sort_by { |card| card_value(card,tsuit) }.reverse

Though, incidentally, I'm not sure how the performance of the two
would compare. I also wish I could think of a neater way to include
those boolean values than the ? 1 : 0 thing. I considered defining <=>
for TrueClass and FalseClass, but thought that might be a little icky.

Sam

···

On 11/18/05, Ruby Quiz <james@grayproductions.net> wrote:

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.

I seriously debated with myself whether it was worth submitting my
solution. It takes a whole 100 lines of code! I decided in the end that
I would post it anyway.

I made the opposite decision to Robin Stocker and decided to stick with
the <=> approach. The cards do have to know about the trump suit, but
the deck class automatically handles that.

Here it is:

class Card
  @@values = {'A' => :ace, 'K' => :king, 'Q' => :queen, 'J' => :jack,
'T' => :ten, '9' => :nine}
  @@value_values = {:ace => 6, :king => 5, :queen => 4, :jack => 3, :ten
=> 2, :nine => 1}
  @@suits = {'h' => :hearts, 'd' => :diamonds, 'c' => :clubs, 's' =>
:spades}
  @@suit_values = {:hearts => 2, :diamonds => -2, :clubs => 1, :spades
=> -1}

  # The suit_values is used to compactly calculate the order suits
should be sorted in

  def initialize(string = "Jh", trump = :hearts)
    @suit = :hearts
    @value = :ace
    @trump = trump
    if string
      self.parse(string)
    end
  end

  def parse(string)
    cardarray = string.upcase.scan(/[AKQJT9][HDCS]/)
    if cardarray.length > 0
      @value = @@values[cardarray[0][0,1]]
      @suit = @@suits[(cardarray[0][1,1]).downcase]
    end
  end

  def rank_value
    case @suit
      when @trump
        return @value == :jack ? 40 : (@@value_values[@value] + 30)
      when @@suit_values.index(@@suit_values[@trump] * -1)
      # True when card is of the trump's suit's colour companion
        return @value == :jack ? 39 : (@@value_values[@value] + 10)
      when @@suit_values.index((@@suit_values[@trump] % 2) + 1)
      # Triggers for clubs if trump is red or hearts if trump is black
        return @@value_values[@value] + 20
      else
      # The last remaining suit comes last
        return @@value_values[@value]
    end
  end

  def <=>(other)
    self.rank_value <=> other.rank_value
  end

  def printable
    @@values.index(@value) + @@suits.index(@suit)
  end

  include Comparable

  attr_accessor :trump, :value, :suit
end

class Hand
  def initialize(trump = :hearts, *cards)
    @cards = Array.new
    @trump = trump
    cards.each do |cs|
      @cards << Card.new(cs, trump)
    end
    @cards.sort!
  end

  def add_card(card)
    @cards << Card.new(card, @trump)
  end

  def display
    @cards.sort!
    @cards.reverse.each do |card|
      puts card.printable
    end
  end

  def inspect
    handstring = String.new
    @cards.each do |card|
      handstring << card.printable << ', '
    end
    handstring.chop!
    handstring.chop!
  end

  def trump=(newtrump)
    @trump = newtrump
    @cards.each {|c| c.trump = newtrump}
  end

  attr_accessor :cards
  attr_reader :trump
end

if __FILE__ == $0

  until (trumpline = readline()) != ""
  end

  case trumpline.downcase[0,1]
    when 'h'
      trump = :hearts
    when 'd'
      trump = :diamonds
    when 'c'
      trump = :clubs
    when 's'
      trump = :spades
    else
      raise StandardError, "Bad Input"
  end

  h = Hand.new(trump)

  5.times do |t|
    cardline = readline()
    h.add_card(cardline)
  end
  
  puts trump.to_s.capitalize
  h.display

end

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

Hello James,

Hello Brian.

I have to admit that I do not totally understand.

That's probably just my terrible explination. Let's see if I can clear it up...

Why is in the example the King of Clubs higher than the Ace of hearts.

Cards are sorted first buy suit, then by value. Trump is a better suit then the rest, but the others are equal and can come in any order. The suit order above is Diamonds (Trump), Clubs, then Hearts. Inside those suits, cards are sorted by face value.

And secondly I do not understand the paragraph about the red-black sort order.

Technically, this is another (correct) answer to the above:

  Diamonds
  Jh
  Kd
  Td
  Ah
  Kc

The cards are still sorted in order of value. However, now we have put the red suits together. I think it's better to break them up, when we can. That's why my example shows Clubs between the two suits.

Does that help?

James Edward Gray II

···

On Nov 19, 2005, at 1:31 PM, Brian Schröder wrote:

>> 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

> Hello James,

Hello Brian.

> I have to admit that I do not totally understand.

That's probably just my terrible explination. Let's see if I can
clear it up...

> Why is in the example the King of Clubs higher than the Ace of hearts.

Cards are sorted first buy suit, then by value. Trump is a better
suit then the rest, but the others are equal and can come in any
order. The suit order above is Diamonds (Trump), Clubs, then
Hearts. Inside those suits, cards are sorted by face value.

Now I understand. I always sort my cards first by value then by suite.
That was the problem.

cheers,

Brian

> [snip]

Does that help?

Yes

···

On 19/11/05, James Edward Gray II <james@grayproductions.net> wrote:

On Nov 19, 2005, at 1:31 PM, Brian Schröder wrote:

James Edward Gray II

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/