# Euchre Hands (#55)

Hi

This one seemed pretty easy, so I gave it a try.

My first idea was to solve the whole sorting in the <=> method of the
Card class. But it was too confusing this way, because I had to include
all possible comparisons. Also the Card itself had to know what the
trump suit was.

Then I tried a different approach. I wrote a Card#weighting method which
returned a number, for trump jack a 41, for trump color jack a 40, and
so on. Then i sorted the card array with sort_by and the weighting. But
I wasn’t satisfied yet, because again, the card had to know what suit
was trump. And the numbering was ugly, too.

So my final idea was to just implement the <=> method for sorting
without regard to trump. Then in my Hand class, which knows about the
trump, I wrote a sort! method for the sorting with regard to trump suit.

Now I’m interested to see other solutions

Robin S.

module Euchre

class Hand
attr_accessor :cards

`````` def initialize( trump )
@cards = []
@trump = Card.new( trump )
end

def <<( card )
@cards << card
end

def sort!
@cards =
# First the trump jack..
@cards.select{ |c| trump_suit?(c) and c.jack? } |
# then the jack of the trump color..
@cards.select{ |c| trump_color?(c) and c.jack? } |
# then all the trump cards..
@cards.select{ |c| trump_suit?(c) }.sort.reverse |
# then a different color, so the colors alternate..
@cards.select{ |c| !trump_color?(c) and
c.suit =~ /d|c/ }.sort.reverse |
# then the cards with the same color as the trump..
@cards.select{ |c| trump_color?(c) }.sort.reverse |
# and finally the rest.
@cards.sort.reverse
end

def trump_suit?( card ) card.suit == @trump.suit end
def trump_color?( card ) card.color == @trump.color end

def to_s
@cards.join("\n")
end
``````

end

class Card
attr_accessor :suit, :face

`````` Suits = ['d', 'h', 'c', 's']
Faces = ['9', 'T', 'J', 'K', 'A']
Colors = {'d' => :red, 'h' => :red, 'c' => :black, 's' => :black}

def initialize( suit, face=nil )
@suit = suit.downcase
@face = face.upcase if face
end

def jack?() @face == 'J' end
def color() Colors[@suit] end

# Sort first by suit and then by face.
def <=>( other )
rel = Suits.index(@suit) - Suits.index(other.suit)
rel = Faces.index(@face) - Faces.index(other.face) if rel == 0
rel
end

def to_s
@face + @suit
end
``````

end

end

if FILE == \$0

trump = lines.shift.slice(/\w+/)
hand = Euchre::Hand.new(trump[0,1])

lines.join.scan(/(\w)(\w)/) do |face, suit|
hand << Euchre::Card.new(suit, face)
end

hand.sort!
puts trump
puts hand
end

=begin
Robin S. [email protected] writes:

Now I’m interested to see other solutions
Robin S.

This is mine, I didn’t bother putting it into a class.
=end

# Return rank of the same color.

def opposite(rank)
{?d => ?h, ?h => ?d, ?c => ?s, ?s => ?c}.fetch rank
end

# Return rank of different color.

def neighbor(rank)
{?d => ?c, ?c => ?h, ?h => ?s, ?s => ?d}.fetch rank
end

def relative_rank(trump, suit, rank)
case suit
when trump: rank == ?J ? 1000 : 500
when neighbor(trump): 400
when opposite(trump): rank == ?J ? 900 : 300
when opposite(neighbor(trump)): 200
end +
[?9, ?T, ?J, ?Q, ?K, ?A].index(rank)
end

def sort_cards(trump, cards)
cards.sort_by { |c| -relative_rank(trump, c[1], c[0]) }
end

puts sort_cards(gets.strip.downcase[0], readlines.map { |s| s.strip })

END

Here’s my minimalist solution: 20 lines. I believe it always gets
both rank and color-alternation correct. If you only want to get
alternation correct most of the time, you can cut out another five
lines.

SUITS = %w{d c h s}.map {|s| s[0]}
CARDS = %w{A K Q J T 9}.map {|c| c[0]}

puts trump
trump = SUITS.index(trump.downcase[0])

# correct color alternation when possible.

unless hand.find {|card| card[1] == SUITS[(trump+1)%4]}
tmp = SUITS[(trump+1)%4]
SUITS[(trump+1)%4] = SUITS[(trump+3)%4]
SUITS[(trump+3)%4] = tmp
end

hand.map { |card|
suit = (SUITS.index(card[1]) - trump)%4
num = CARDS.index(card[0])
if num==3 && suit==2
suit,num = 0,-1 # Left bower
elsif num==3 && suit==0
num = -2 # Right bower
end
[suit,num,card.chomp]
}.sort.each{|c| puts “#{c[2]}\n” }

I made two solutions, both of which use the same basic algorithm of
applying weights (where smaller is better) to the cards and then
sorting by weight. The comments in the first “normal” version should
explain things.

The second version is my highly golfed version which I think is about
as small as my algorithm can get. It is 242 bytes. I would be very
interested to see if anyone can go smaller, either by tweaking mine
more, or more likely, by golfing another algorithm. Obviously the
golfed code has zero error handling, so it will probably choke on any

Normal code:

class String
def to_suit
self[0…0].downcase
end
end

class EuchreSort

# These represent preferred sorting order

SUITS = %w{Diamonds Clubs Hearts Spades} # Alphabetical, then by color
CARDS = %w{A K Q J T 9} # Highest to lowest

def initialize(trump)
@trump = trump
trump_index = SUITS.index(trump)
raise “Invalid trump suit: #{trump}” unless trump_index
@right_bower = “J#{trump.to_suit}”
# The ordering used in SUITS ensures this works
@left_bower = “J#{SUITS[(trump_index+2)%4].to_suit}”
# Apply weights to suits starting from the trump, wrapping
# around as needed
@suit_weights = {}
weight = 10
trump_index.upto(trump_index+3) do |i|
@suit_weights[SUITS[i%4].to_suit] = weight
weight += 10
end
end

def sort(hand)
weights = {}
hand.each do |card|
raise “Invalid card: #{card}” if card !~
/\A[#{CARDS.join}]{1}[dchs]{1}\z/
weights[card] =
case card
when @right_bower: 0
when @left_bower: 1
else
@suit_weights[card[1…1]] + CARDS.index(card[0…0])
end
end
hand.sort_by {|c| weights[c]}
end
end

if \$0 == FILE
hand = STDIN.collect {|i|i.chomp}
trump = hand.shift
es = EuchreSort.new(trump)
puts trump
puts es.sort(hand)
end

Golfed code (I’m not sure how Gmail will wrap this, but it should be
all on one line):

s};c=‘AKQJT9’;t=a.shift;u=(t[0]+32).chr;i=s.index(u);v=s[(i+2)%4];w=[1];i.upto(i+3){|j|w<<s[j%4]};m={};a.each{|k|m[k]=k=~/(J#{u})|(J#{v})/?\$1?0:1:w.index(k[1…1])*10+c.index(k[0…0])};puts
t,a.sort_by{|k|m[k]}

Ryan

On Nov 20, 2005, at 8:53 AM, Robin S. wrote:

Hi

This one seemed pretty easy, so I gave it a try.

Just FYI, this solution seems to have trouble with certain inputs:

Neo:~/Documents/Ruby/Ruby Q.\$ ruby solutions/euchre_hand.rb >
test_hand.txt
Neo:~/Documents/Ruby/Ruby Q.\$ ruby solutions/Robin\ Stocker/
euchre_hands.rb test_hand.txt
solutions/Robin S./euchre_hands.rb:58:in `-': nil can't be coerced into Fixnum (TypeError) from solutions/Robin S./euchre_hands.rb:58:in`<=>’
from solutions/Robin S./euchre_hands.rb:27:in `sort' from solutions/Robin S./euchre_hands.rb:27:in`sort!’
from solutions/Robin S./euchre_hands.rb:80
Neo:~/Documents/Ruby/Ruby Q.\$ cat test_hand.txt
hearts
9s
9d
Kd
Ah
Qd

That’s really not meant as a complaint. I appreciate you sharing
your solution. It works more often than not and always seems to
produce correct answers when it does. I just thought you might like
to know.

James Edward G. II

On Nov 20, 2005, at 9:15 AM, Christian N. wrote:

=begin
Robin S. [email protected] writes:

Now I’m interested to see other solutions
Robin S.

This is mine, I didn’t bother putting it into a class.

Hmm, is this correct:

Neo:~/Documents/Ruby/Ruby Q.\$ ruby solutions/euchre_hand.rb >
test_hand.txtNeo:~/Documents/Ruby/Ruby Q.\$ ruby solutions/Christian
\ Neukirchen/euchre_hands.rb test_hand.txt
Qh
9h
Qd
9d
Qc
Neo:~/Documents/Ruby/Ruby Q.\$ cat test_hand.txt
hearts
Qd
9h
9d
Qc
Qh

hearts
Qh
9h
Qc
Qd
9d

Which doesn’t put two red suits together. Getting back to the
question of the quiz, how do we know when we’re right?

James Edward G. II

James Edward G. II [email protected] writes:

On Nov 20, 2005, at 9:15 AM, Christian N. wrote:

=begin
Robin S. [email protected] writes:

Now I’m interested to see other solutions
Robin S.

This is mine, I didn’t bother putting it into a class.

question of the quiz, how do we know when we’re right?
Difficult. Mine doesn’t (yet?) reorder if there are no cards of every
color.

James Edward G. II wrote:

Just FYI, this solution seems to have trouble with certain inputs:

Ah, thank you… I just noticed that I forgot the queen in my Faces
array

I also updated the script to get the alternating colors right.

Thanks for finding this (stupid) error.

Robin S.

module Euchre

class Hand
attr_accessor :cards

`````` def initialize( trump )
@cards = []
@trump = Card.new( trump )
end

def <<( card )
@cards << card
end

def sort!
second_suit = @cards.find{ |c| !trump_color?(c) }.suit
@cards =
# First the trump jack..
@cards.select{ |c| trump_suit?(c) and c.jack? } |
# then the jack of the trump color..
@cards.select{ |c| trump_color?(c) and c.jack? } |
# then all the trump cards..
@cards.select{ |c| trump_suit?(c) }.sort.reverse |
# then a different color, so the colors alternate..
@cards.select{ |c| c.suit == second_suit }.sort.reverse |
# then the cards with the same color as the trump..
@cards.select{ |c| trump_color?(c) }.sort.reverse |
# and finally the rest.
@cards.sort.reverse
end

def trump_suit?( card ) card.suit == @trump.suit end
def trump_color?( card ) card.color == @trump.color end

def to_s
@cards.join("\n")
end
``````

end

class Card
attr_accessor :suit, :face

`````` Suits = ['d', 'h', 'c', 's']
Faces = ['9', 'T', 'Q', 'J', 'K', 'A']
Colors = {'d' => :red, 'h' => :red, 'c' => :black, 's' => :black}

def initialize( suit, face=nil )
@suit = suit.downcase
@face = face.upcase if face
end

def jack?() @face == 'J' end
def color() Colors[@suit] end

# Sort first by suit and then by face.
def <=>( other )
rel = Suits.index(@suit) - Suits.index(other.suit)
rel = Faces.index(@face) - Faces.index(other.face) if rel == 0
rel
end

def to_s
@face + @suit
end
``````

end

end

if FILE == \$0

trump = lines.shift.slice(/\w+/)
hand = Euchre::Hand.new(trump[0,1])

lines.join.scan(/(\w)(\w)/) do |face, suit|
hand << Euchre::Card.new(suit, face)
end

hand.sort!
puts trump
puts hand
end

On Nov 20, 2005, at 3:07 PM, Dale M. wrote:

I used to play euchre a lot in college so this was fun. It might be
fun
writing an entire euchre game.

[snip]

It would be a fun to attempt to create a EuchreBot to actually play.

I agree. Maybe if you and I got a server together, building AI
players would make a good quiz for it? Drop me and email if this
interests you…

James Edward G. II

P.S. Nice proof of your solution!

I used to play euchre a lot in college so this was fun. It might be fun
writing an entire euchre game.

What I did was first think about the best test to prove my solution. If
you have a completely shuffled euchre deck for a specfic trump, the
entire deck would take on a known order. So I wrote four tests:

require ‘test/unit’
require ‘euchre’

HEARTS_AS_TRUMP_DECK = [
‘Jh’,‘Jd’,‘Ah’,‘Kh’,‘Qh’,‘Th’,‘9h’,
‘As’,‘Ks’,‘Qs’,‘Js’,‘Ts’,‘9s’,
‘Ac’,‘Kc’,‘Qc’,‘Jc’,‘Tc’,‘9c’
]

‘Js’,‘Jc’,‘As’,‘Ks’,‘Qs’,‘Ts’,‘9s’,
‘Ac’,‘Kc’,‘Qc’,‘Tc’,‘9c’,
‘Ah’,‘Kh’,‘Qh’,‘Jh’,‘Th’,‘9h’
]

DIAMONDS_AS_TRUMP_DECK = [
‘Ac’,‘Kc’,‘Qc’,‘Jc’,‘Tc’,‘9c’,
‘Ah’,‘Kh’,‘Qh’,‘Th’,‘9h’,
‘As’,‘Ks’,‘Qs’,‘Js’,‘Ts’,‘9s’,
]

CLUBS_AS_TRUMP_DECK = [
‘Jc’,‘Js’,‘Ac’,‘Kc’,‘Qc’,‘Tc’,‘9c’,
‘Ah’,‘Kh’,‘Qh’,‘Jh’,‘Th’,‘9h’,
‘As’,‘Ks’,‘Qs’,‘Ts’,‘9s’,
]

class TestEuchre < Test::Unit::TestCase
def setup
@ed = EuchreDeck.new
@eh = EuchreHand.new
@ed.shuffle
while( card = @ed.deal )
end
end

def test_hearts_as_trump
@eh.trump = “Hearts”
assert_equal( HEARTS_AS_TRUMP_DECK, @eh.hand )
end

end

def test_diamonds_as_trump
@eh.trump = “Diamonds”
assert_equal( DIAMONDS_AS_TRUMP_DECK, @eh.hand )
end

def test_clubs_as_trump
@eh.trump = “Clubs”
assert_equal( CLUBS_AS_TRUMP_DECK, @eh.hand )
end
end

I took the original input program and created a EuchreDeck class:

class EuchreDeck
def initialize
# 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
end

def shuffle
@cards = @cards.sort_by { rand }
end

def deal
@cards.shift
end
end

I wrote a EuchreHand class that would take cards dealt to it and
originize them by a computed card value using sort.

class EuchreHand
Suit = Struct.new( :suit, :alternate_suit_1, :off_suit,
:alternate_suit_2 )

@@suits = {
“Diamonds”=>Suit.new(“d”,“c”,“h”,“s”),
“Clubs”=>Suit.new(“c”,“h”,“s”,“d”),
“Hearts”=>Suit.new(“h”,“s”,“d”,“c”)
}

@@face_values_trump = {
“J” => 6,
“A” => 4,
“K” => 3,
“Q” => 2,
“T” => 1,
“9” => 0
}

@@face_values_regular = {
“A” => 5,
“K” => 4,
“Q” => 3,
“J” => 2,
“T” => 1,
“9” => 0
}

MAX_CARDS_PER_SUIT = 7

def initialize
@trump = nil
@hand = []
end

def left_brower?( card )
card == “J#{@trump.off_suit}”
end

def trump?( card )
card[1].chr == @trump.suit
end

def trump=( suit_string )
@trump = @@suits[ suit_string ]
end

def trump
@@suits.index(@trump)
end

@hand.push( card )
end

def card_value( card )
face = card[0].chr
suit = card[1].chr

``````if left_brower?(card) then
suit_value = @trump.to_a.reverse.index( @trump.suit ) *
``````

MAX_CARDS_PER_SUIT
face_value = @@face_values_trump[ face ] - 1
elsif trump?(card) then
suit_value = @trump.to_a.reverse.index( @trump.suit ) *
MAX_CARDS_PER_SUIT
face_value = @@face_values_trump[ face ]
else
suit_value = @trump.to_a.reverse.index( suit ) *
MAX_CARDS_PER_SUIT
face_value = @@face_values_regular[ face ]
end

``````suit_value + face_value
``````

end

def hand
@hand.sort {|x,y| card_value(y)<=>card_value(x) }
end
end

Once my tests passed, I rewrote the original input program as:

require ‘euchre’

# choose trump

ed = EuchreDeck.new
ed.shuffle
5.times{ puts ed.deal }

And the sort program as:

require ‘euchre’

eh = EuchreHand.new

eh.trump = gets.strip

while card = gets
end

puts eh.trump
puts eh.hand

It would be a fun to attempt to create a EuchreBot to actually play.

–Dale

Here is another solution to the problem. I got the idea for this while

It doesn’t really “solve” the problem, instead it cheats , but on
the
other side it is short and should be correct if the order hash is
correct.

Dominik

order = {
}

trump = gets.strip
puts trump, readlines.map { |l| l.strip }.sort_by { |card|
order[trump].index(card)
}

Here is my solution.

It also sorts the the cards by a score computed depending on the trump
suit, lower is better.

Dominik

The code:

class EuchreCard

`````` SUIT_COLOR = {
:diamonds => :red,
:hearts => :red,
:clubs => :black,
}
SUIT_ORDER = [:diamonds, :clubs, :hearts, :spades]
RANK_ORDER = [:nine, :ten, :jack, :queen, :king, :ace]

def initialize(str)
str = str.to_s.downcase
@rank =
if str[0] == ?9
:nine
else
RANK_ORDER.find { |rank| rank.to_s[0] == str[0] }
end
@suit = SUIT_ORDER.find { |suit| suit.to_s[0] == str[1] }
raise "unknown card rank" unless rank
raise "unknown card suit" unless suit
end

def to_s
unless rank == :nine
rank.to_s[0, 1].upcase
else
"9"
end + suit.to_s[0, 1]
end

def sort_score(trump)
if rank == :jack && suit == trump
0
elsif rank == :jack && SUIT_COLOR[suit] == SUIT_COLOR[trump]
1
else
ti = SUIT_ORDER.index(trump)
raise "unknown trump suit: #{trump}" unless ti
suit_score = (SUIT_ORDER.index(suit) - ti) % 4
10 + suit_score * 10 - RANK_ORDER.index(rank)
end
end
``````

end

if \$0 == FILE
trump = gets.strip.downcase.to_sym
unless EuchreCard::SUIT_COLOR.has_key? trump
warn “unknown trump suit: #{trump}”
exit 1
end
cards = readlines.map { |line| EuchreCard.new(line.strip) }
cards = cards.sort_by { |card| card.sort_score(trump) }
puts trump.to_s.capitalize, cards
end

On 11/20/05, Dominik B. [email protected] wrote:

It doesn’t really “solve” the problem, instead it cheats , but on the
other side it is short and should be correct if the order hash is correct.

A couple of my early attempts depended on the idea that the trump suit
absolutely determined the ranking of the cards, independent of knowing
anything else. It doesn’t work, though.

On this input:
Hearts
Qd
9h
9d
Qc
Qh

That code produces:
Hearts
Qh
9h
Qd
9d
Qc

(diamonds next to hearts) when it should be:
Hearts
Qh
9h
Qc
Qd
9d

(clubs between hearts and diamonds.) Ordering the hand can’t come
without considering what suits are present.

Terse, but almost reasonable version:

class Euchre
OTHER_COLOR = {‘c’ => ‘s’, ‘s’ => ‘c’, ‘h’ => ‘d’, ‘d’ => ‘h’}
def initialize(suit_name)
@trump_suit = suit_name
@trump = @trump_suit[0,1].downcase
end
def <<(card)
(@hand ||= []) << card
end
def hand
suits = @hand.map {|card| card[1,1]}.uniq
i = suits.index(@trump) and suits.push(suits.slice!(i))
suits[-3],suits[-2] = suits[-2],suits[-3] if suits.length > 2 and
OTHER_COLOR[suits[-1]] == suits[-2]
@hand.sort_by do |x|
rank, suit = x.split(’’)
if rank == ‘J’ and @trump == suit : 50
elsif rank == ‘J’ and OTHER_COLOR[suit] == @trump : 40
else ‘9TJQKA’.index(rank) + suits.index(suit)*10
end
end.reverse
end
end

euchre = Euchre.new(gets.strip)
5.times { euchre << gets.strip }
puts euchre.trump_suit, euchre.hand

Golfed, but weighing in a good 80 characters more than Ryan’s (but,
hey, it’s my first golf outing in Ruby.) Like his, it should be one
line.

a=[];6.times{a<<gets.strip};puts t=a.shift;o=Hash[*%w{C S S C H D D
H}];t=t[0,1];s=a.map{|c|c[1,1].upcase}.uniq;i=s.index(t)and
s.push(s.slice!(i));s.length>2&&o[s[-1]]==s[-2]&&(s[-3],s[-2]=s[-2],s[-3]);puts
a.sort_by{|c|r,z=c.upcase.split(’’);r==‘J’&&t==z&&50||r==‘J’&&o[z]==t&&40||‘9TJQKA’.index®+s.index(z)*9}.reverse

Thanks to inspiration from reading Zed L.'s code, I’ve been able to
cut my golfed solution from 242 bytes to 214. Unfortunately for you
Zed, this now means your is now 108 bytes longer

The fixes were:

1. Using s[x,1] instead of s[x…x] to get a single character from a
string. This saved 2 bytes.
2. Calculating the weights INSIDE the sort_by, instead of using a
separate hash. This seems so obvious now I slapped my head when I saw
Zed’s code doing this. This saved a whopping 26 bytes.

So, if anyone is interested:

s};c=‘AKQJT9’;t=a.shift;u=(t[0]+32).chr;i=s.index(u);v=s[(i+2)%4];w=[1];i.upto(i+3){|j|w<<s[j%4]};puts
t,a.sort_by{|k|k=~/(J#{u})|(J#{v})/?\$1?0:1:w.index(k[1,1])*10+c.index(k[0,1])}

I will be highly impressed if someone can go shorter than this. Also
if anyone wants it I can post a message “decoding” the above.

Ryan

Ryan, how about changing /(J#{u})|(J#{v})/ to /J[#{u}#{v}]/? That’ll
save you a couple more characters.

On Mon, 21 Nov 2005 04:20:16 +0100, Zed L. [email protected]
wrote:

On 11/20/05, Dominik B. [email protected] wrote:

It doesn’t really “solve” the problem, instead it cheats , but on
the
other side it is short and should be correct if the order hash is
correct.

A couple of my early attempts depended on the idea that the trump suit
absolutely determined the ranking of the cards, independent of knowing
anything else. It doesn’t work, though.

Indeed, I didn’t realize that till now (even though James wrote a
similar

So my first solution is “wrong”, too. I will post new versions.

Thanks,
Dominik

On 11/21/05, David B. [email protected] wrote:

Ryan, how about changing /(J#{u})|(J#{v})/ to /J[#{u}#{v}]/? That’ll
save you a couple more characters.

Damn, now I can see why you did it that way. Sorry. Please disregard.

Here’s my terribly hacky version of Ryans;

s};c=‘AKQJT9’;t=a.shift;u=(t[0]+32).chr;i=s.index(u);v=s[i-2];w=[1];4.times{|j|w<<s[j-i]};puts
t,a.sort_by{|k|k=~/J[#{u}#{v}]/?\$1?0:1:w.index(k[1,1])*10+c.index(k[0,1])}

David B. schrieb:

On 11/21/05, David B. [email protected] wrote:

Ryan, how about changing /(J#{u})|(J#{v})/ to /J[#{u}#{v}]/? That’ll
save you a couple more characters.

Damn, now I can see why you did it that way. Sorry. Please disregard.

No, your idea is right: he could still save one character using
/J(#{u})|(#{v})/

Regards,
Pit