Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > [QUIZ SOLUTION] Euchre Hands (#55)

Reply
Thread Tools

[QUIZ SOLUTION] Euchre Hands (#55)

 
 
Robin Stocker
Guest
Posts: n/a
 
      11-20-2005
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 Stocker


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
lines = readlines

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



 
Reply With Quote
 
 
 
 
Edward Faulkner
Guest
Posts: n/a
 
      11-20-2005
--TB36FDmn/VVEgNH/
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

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 =3D %w{d c h s}.map {|s| s[0]}
CARDS =3D %w{A K Q J T 9}.map {|c| c[0]}

trump,hand =3D STDIN.readline, STDIN.readlines

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

# If the suit after the trump suit is missing, we swap it with the
# other suit of the same color. This ensures that we always have a
# correct color alternation when possible.
unless hand.find {|card| card[1] =3D=3D SUITS[(trump+1)%4]}
tmp =3D SUITS[(trump+1)%4]
SUITS[(trump+1)%4] =3D SUITS[(trump+3)%4]
SUITS[(trump+3)%4] =3D tmp
end

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


--TB36FDmn/VVEgNH/
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFDgJmYnhUz11p9MSARAt1cAKC8/V15WvvXCnxPxtmkvOPHLS9KRwCbBu4G
9+kkX5s6DVtZP4uGzXHKX5g=
=EaVm
-----END PGP SIGNATURE-----

--TB36FDmn/VVEgNH/--


 
Reply With Quote
 
 
 
 
Ryan Leavengood
Guest
Posts: n/a
 
      11-20-2005
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
bad input.

Normal code:

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

class EuchreSort
# These represent preferred sorting order
SUITS =3D %w{Diamonds Clubs Hearts Spades} # Alphabetical, then by color
CARDS =3D %w{A K Q J T 9} # Highest to lowest

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

def sort(hand)
weights =3D {}
hand.each do |card|
raise "Invalid card: #{card}" if card !~ /\A[#{CARDS.join}]{1}[dchs]{=
1}\z/
weights[card] =3D
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 =3D=3D __FILE__
hand =3D STDIN.collect {|i|i.chomp}
trump =3D hand.shift
es =3D 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):

a=3D$<.read.split("\n");s=3D%w{d c h
s};c=3D'AKQJT9';t=3Da.shift;u=3D(t[0]+32).chr;i=3Ds.index(u);v=3Ds[(i+2)%4]=
;w=3D[1];i.upto(i+3){|j|w<<s[j%4]};m=3D{};a.each{|k|m[k]=3Dk=3D~/(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


 
Reply With Quote
 
James Edward Gray II
Guest
Posts: n/a
 
      11-20-2005
On Nov 20, 2005, at 8:53 AM, Robin Stocker 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 Quiz$ ruby solutions/euchre_hand.rb >
test_hand.txt
Neo:~/Documents/Ruby/Ruby Quiz$ ruby solutions/Robin\ Stocker/
euchre_hands.rb test_hand.txt
solutions/Robin Stocker/euchre_hands.rb:58:in `-': nil can't be
coerced into Fixnum (TypeError)
from solutions/Robin Stocker/euchre_hands.rb:58:in `<=>'
from solutions/Robin Stocker/euchre_hands.rb:27:in `sort'
from solutions/Robin Stocker/euchre_hands.rb:27:in `sort!'
from solutions/Robin Stocker/euchre_hands.rb:80
Neo:~/Documents/Ruby/Ruby Quiz$ 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 Gray II



 
Reply With Quote
 
James Edward Gray II
Guest
Posts: n/a
 
      11-20-2005
On Nov 20, 2005, at 9:15 AM, Christian Neukirchen wrote:

> =begin
> Robin Stocker <> writes:
>
>> Now I'm interested to see other solutions
>> Robin Stocker

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


Hmm, is this correct:

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

I would have answered:

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



 
Reply With Quote
 
Robin Stocker
Guest
Posts: n/a
 
      11-20-2005
James Edward Gray 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 Stocker


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
lines = readlines

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


 
Reply With Quote
 
Dale Martenson
Guest
Posts: n/a
 
      11-20-2005
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',
'Ad','Kd','Qd','Td','9d',
'Ac','Kc','Qc','Jc','Tc','9c'
]

SPADES_AS_TRUMP_DECK = [
'Js','Jc','As','Ks','Qs','Ts','9s',
'Ad','Kd','Qd','Jd','Td','9d',
'Ac','Kc','Qc','Tc','9c',
'Ah','Kh','Qh','Jh','Th','9h'
]

DIAMONDS_AS_TRUMP_DECK = [
'Jd','Jh','Ad','Kd','Qd','Td','9d',
'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',
'Ad','Kd','Qd','Jd','Td','9d'
]

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

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

def test_spades_as_trump
@eh.trump = "Spades"
assert_equal( SPADES_AS_TRUMP_DECK, @eh.hand )
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, ff_suit,
:alternate_suit_2 )

@@suits = {
"Diamonds"=>Suit.new("d","c","h","s"),
"Clubs"=>Suit.new("c","h","s","d"),
"Spades"=>Suit.new("s","d","c","h"),
"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

def add_card( card )
@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
puts %w{Diamonds Clubs Spades Hearts}[rand(4)]

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
eh.add_card( card.strip )
end

puts eh.trump
puts eh.hand

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

--Dale

 
Reply With Quote
 
James Edward Gray II
Guest
Posts: n/a
 
      11-21-2005
On Nov 20, 2005, at 3:07 PM, Dale Martenson 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 Gray II

P.S. Nice proof of your solution!



 
Reply With Quote
 
Dominik Bathon
Guest
Posts: n/a
 
      11-21-2005
Here is my solution.

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

Dominik


The code:

class EuchreCard

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

attr_reader :rank, :suit

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

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

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

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


 
Reply With Quote
 
Dominik Bathon
Guest
Posts: n/a
 
      11-21-2005
Here is another solution to the problem. I got the idea for this while =20
reading Dale Martenson's solution.

It doesn't really "solve" the problem, instead it cheats , but on the=
=20
other side it is short and should be correct if the order hash is correct=
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[SUMMARY] Euchre Hands (#55) Ruby Quiz Ruby 2 11-24-2005 04:01 PM
[SOLUTION] Euchre Hands (#55) Warren Brown Ruby 2 11-23-2005 12:55 AM
[QUIZ SOLUTION] Euchre Hands (#55) Daniel Sheppard Ruby 0 11-21-2005 06:55 AM
[QUIZ] Euchre Hands (#55) Ruby Quiz Ruby 5 11-21-2005 02:23 AM
MSN Euchre through router? Suenet Computer Support 0 11-04-2005 02:46 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57