Forum: Ruby Euchre Hands (#55)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Cd49db0b676767ea4358b1047c4cddd2?d=identicon&s=25 robin (Guest)
on 2005-11-20 15:56
(Received via mailing list)
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
7264fb16beeea92b89bb42023738259d?d=identicon&s=25 chneukirchen (Guest)
on 2005-11-20 16:16
(Received via mailing list)
=begin
Robin Stocker <robin@nibor.org> writes:

> Now I'm interested to see other solutions :)
>    Robin Stocker

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__
036a1b88dafaab8ffd73a8b0a74b5b38?d=identicon&s=25 ef (Guest)
on 2005-11-20 16:44
(Received via mailing list)
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]}

trump,hand = STDIN.readline, STDIN.readlines

puts trump
trump = 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] == 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" }
4b174722d1b1a4bbd9672e1ab50c30a9?d=identicon&s=25 leavengood (Guest)
on 2005-11-20 16:48
(Received via mailing list)
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 = %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):

a=$<.read.split("\n");s=%w{d c h
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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 james (Guest)
on 2005-11-20 20:02
(Received via mailing list)
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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 james (Guest)
on 2005-11-20 20:06
(Received via mailing list)
On Nov 20, 2005, at 9:15 AM, Christian Neukirchen wrote:

> =begin
> Robin Stocker <robin@nibor.org> 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
7264fb16beeea92b89bb42023738259d?d=identicon&s=25 chneukirchen (Guest)
on 2005-11-20 20:35
(Received via mailing list)
James Edward Gray II <james@grayproductions.net> writes:

> On Nov 20, 2005, at 9:15 AM, Christian Neukirchen wrote:
>
>> =begin
>> Robin Stocker <robin@nibor.org> writes:
>>
>>> Now I'm interested to see other solutions :)
>>>    Robin Stocker
>>
>> 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.
Cd49db0b676767ea4358b1047c4cddd2?d=identicon&s=25 robin (Guest)
on 2005-11-20 21:23
(Received via mailing list)
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
A083f2c63fc0be7f8f19006c9dbdff86?d=identicon&s=25 dale.martenson (Guest)
on 2005-11-20 22:07
(Received via mailing list)
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, :off_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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 james (Guest)
on 2005-11-21 02:41
(Received via mailing list)
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!
18ca239ffade6df0b839d26062f173fb?d=identicon&s=25 dbatml (Guest)
on 2005-11-21 03:01
(Received via mailing list)
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,
         :spades => :black
     }
     SUIT_ORDER = [:diamonds, :clubs, :hearts, :spades]
     RANK_ORDER = [:nine, :ten, :jack, :queen, :king, :ace]

     attr_reader :rank, :suit

     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
18ca239ffade6df0b839d26062f173fb?d=identicon&s=25 dbatml (Guest)
on 2005-11-21 03:37
(Received via mailing list)
Here is another solution to the problem. I got the idea for this while
reading Dale Martenson's solution.

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 = {
     "Spades"   => "JsJcAsKsQsTs9sAdKdQdJdTd9dAcKcQcTc9cAhKhQhJhTh9h",
     "Hearts"   => "JhJdAhKhQhTh9hAsKsQsJsTs9sAdKdQdTd9dAcKcQcJcTc9c",
     "Clubs"    => "JcJsAcKcQcTc9cAhKhQhJhTh9hAsKsQsTs9sAdKdQdJdTd9d",
     "Diamonds" => "JdJhAdKdQdTd9dAcKcQcJcTc9cAhKhQhTh9hAsKsQsJsTs9s"
}

trump = gets.strip
puts trump, readlines.map { |l| l.strip }.sort_by { |card|
     order[trump].index(card)
}
22eddcd715e3ff602d4bec1fbd863a53?d=identicon&s=25 zed.lopez (Guest)
on 2005-11-21 03:37
(Received via mailing list)
Terse, but almost reasonable version:

class Euchre
  OTHER_COLOR = {'c' => 's', 's' => 'c', 'h' => 'd', 'd' => 'h'}
  attr_reader :trump_suit
  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(r)+s.index(z)*9}.reverse
22eddcd715e3ff602d4bec1fbd863a53?d=identicon&s=25 zed.lopez (Guest)
on 2005-11-21 04:21
(Received via mailing list)
On 11/20/05, Dominik Bathon <dbatml@gmx.de> 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.
18ca239ffade6df0b839d26062f173fb?d=identicon&s=25 dbatml (Guest)
on 2005-11-21 05:26
(Received via mailing list)
On Mon, 21 Nov 2005 04:20:16 +0100, Zed Lopez <zed.lopez@gmail.com>
wrote:

> On 11/20/05, Dominik Bathon <dbatml@gmx.de> 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
reply to Christian's solution)...

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

Thanks,
Dominik
4b174722d1b1a4bbd9672e1ab50c30a9?d=identicon&s=25 leavengood (Guest)
on 2005-11-21 06:18
(Received via mailing list)
Thanks to inspiration from reading Zed Lopez'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:

a=$<.read.split("\n");s=%w{d c h
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
B5e329ffa0cc78efbfc7ae2d084c149f?d=identicon&s=25 dbalmain.ml (Guest)
on 2005-11-21 06:34
(Received via mailing list)
Ryan, how about changing /(J#{u})|(J#{v})/ to /J[#{u}#{v}]/? That'll
save you a couple more characters. :p
B5e329ffa0cc78efbfc7ae2d084c149f?d=identicon&s=25 dbalmain.ml (Guest)
on 2005-11-21 06:46
(Received via mailing list)
Here's my terribly hacky version of Ryans;

a=$<.read.split("\n");s=%w{d c h
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])}
B5e329ffa0cc78efbfc7ae2d084c149f?d=identicon&s=25 dbalmain.ml (Guest)
on 2005-11-21 07:14
(Received via mailing list)
On 11/21/05, David Balmain <dbalmain.ml@gmail.com> wrote:
> Ryan, how about changing /(J#{u})|(J#{v})/ to /J[#{u}#{v}]/? That'll
> save you a couple more characters. :p
>

Damn, now I can see why you did it that way. Sorry. Please disregard.
93d566cc26b230c553c197c4cd8ac6e4?d=identicon&s=25 pit (Guest)
on 2005-11-21 07:39
(Received via mailing list)
David Balmain schrieb:
> On 11/21/05, David Balmain <dbalmain.ml@gmail.com> wrote:
>>Ryan, how about changing /(J#{u})|(J#{v})/ to /J[#{u}#{v}]/? That'll
>>save you a couple more characters. :p
>
> 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
Ddbfebb47432f6599da361df6a135c7c?d=identicon&s=25 adam.shelly (Guest)
on 2005-11-21 07:51
(Received via mailing list)
Here's mine. I believe it always handles red-black ordering correctly
even
with empty suits.

class EuchreHand
SUIT = [?c,?d,?s,?h]
VAL = [?A,?Q,?K,?J,?T,?9]
def initialize input
@trump = input.shift
@hand = {}
4.times {|s| @hand[SUIT[s]]=[]}
input.each{|card| @hand[card[1]] << card[0] }
end
def display
puts @trump
t=SUIT.index(@trump.downcase[0])
get_jacks(SUIT[t])
get_jacks(SUIT[(t+2)%4])
if @hand[SUIT[(t+1)%4]].empty?
t.downto(0){|s| show_suit s}
(3).downto(t+1) {|s| show_suit s}
else
(t..3).each {|s| show_suit s}
(0...t).each {|s| show_suit s}
end
end
def get_jacks suit
if @hand[suit].include? ?J
puts [?J,suit].pack('c*')
@hand[suit].delete(?J)
end
end
def show_suit s
suit = SUIT[s]
@hand[suit].sort{|a,b| VAL.index(a)<=>VAL.index(b)}.each{|v|
puts [v,suit].pack('c*')
}
end
end

if __FILE__ == $0
input =readlines
e = EuchreHand.new(input)
e.display
end

----
-Adam
B5e329ffa0cc78efbfc7ae2d084c149f?d=identicon&s=25 dbalmain.ml (Guest)
on 2005-11-21 07:51
(Received via mailing list)
On 11/21/05, Pit Capitain <pit@capitain.de> wrote:
> Regards,
> Pit

This is the last time I play golf. It's addictive and I should be
working.

a=$<.read.split("\n");s=%w{d c h
s};c='AKQJT9';t=a.shift;u=(t[0]+32).chr;i=s.index(u);v=s[i-2];w=[1];puts
t,a.sort_by{|k|(k=~/J[#{u+v}]/?0:99)+(s.index(k[1,1])-i)%4*10+c.index(k[0,1])}

184 characters. And this time I think it works.
B5e329ffa0cc78efbfc7ae2d084c149f?d=identicon&s=25 dbalmain.ml (Guest)
on 2005-11-21 08:11
(Received via mailing list)
Got to get back to work. 166 chars;
a=$<.readlines;s='dchs';c='AKQJT9';t=a.shift;u=(t[0]+32).chr;i=s.index(u);v=s[i-2].chr;puts
t,a.sort_by{|k|(k=~/J[#{u+v}]/?0:50)+(s.index(k[1])-i)%4*10+c.index(k[0])}
Ddbfebb47432f6599da361df6a135c7c?d=identicon&s=25 adam.shelly (Guest)
on 2005-11-21 08:15
(Received via mailing list)
OOPS! I was just looking at ways I could golf my solution and I realized
I
had a big bug:
replace
VAL = [?A,?Q,?K,?J,?T,?9]
with
VAL = [?A,?K,?Q,?J,?T,?9]

Doh!
4b174722d1b1a4bbd9672e1ab50c30a9?d=identicon&s=25 leavengood (Guest)
on 2005-11-21 18:41
(Received via mailing list)
On 11/21/05, David Balmain <dbalmain.ml@gmail.com> wrote:
> Got to get back to work. 166 chars;
> 
a=$<.readlines;s='dchs';c='AKQJT9';t=a.shift;u=(t[0]+32).chr;i=s.index(u);v=s[i-2].chr;puts
> t,a.sort_by{|k|(k=~/J[#{u+v}]/?0:50)+(s.index(k[1])-i)%4*10+c.index(k[0])}

Brilliant. I'm extremely impressed. I learned quite a bit from it too,
which is one of the reasons I like code golfing so much. Plus it is
fun and rather addictive, as you have learned.

For those wondering, here is what I learned from David's changes to my
code:

1. The extra newlines don't need to be cleaned out from the input
since "puts" seems to ignore them.
2. For compactness it is better to use strings rather than %w{} arrays.
3. Ruby's array and string indexing already takes care of
"wrap-around" when indexing since negative indexes work. So given a
string s of length 4, s[(i+2)%4] and s[i-2] are equivalent for values
of i from 0 to 5.
4. You can get the index of either a string or a fixnum representing a
character using String#index.
5. Using % on a negative number yields a positive number, which is
cleverly used above in calculating the weight. My math is rusty I
guess, or in fact, I'm not sure I ever learned what performing modulus
on a negative number is supposed to return. But I Googled it and
learned now.

But there is a problem in my code (both the above and the longer
version) which I just noticed (and that Zed brought up earlier): if a
suit is missing in certain cases the suits are not interlaced
properly. I'm wondering if I will bother going back to the drawing
board or not :)

Ryan
18ca239ffade6df0b839d26062f173fb?d=identicon&s=25 dbatml (Guest)
on 2005-11-21 21:18
(Received via mailing list)
Here is a corrected version, that hopefully gets the color alternation
right.

Dominik


order = {
     "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) }
37794819c7007e228675aecbb28bdc0d?d=identicon&s=25 lbstoldt (Guest)
on 2005-11-21 22:19
(Received via mailing list)
Here is mine.  Some parts shamefully stolen from above (input using
..split and using negative array indexes to get the complement suit)

class Hand
	attr :cards
	SUITS 	= %w{s h c d} # should be ordered by alternating colors
	RANKS 	= %w{9 T J Q K A} # must be ordered low to high

	def initialize(cards, trump)
		@trump_c = trump[0,1].downcase
		@complement = SUITS[SUITS.index(@trump_c) - 2]
		first_off_suit = SUITS.find{|suit|
![@trump_c,@complement].include?(suit) &&
cards.find{|card|card[1,1]==suit}}
		@suit_values = {@trump_c => 100, first_off_suit => 50, @complement =>
25}
		@cards = cards.sort_by{|card| get_val(card) * -1}.unshift(trump)
	end

	private

	def get_val(card)
		val = RANKS.index(card[0,1]) + (@suit_values[card[1,1]] ||= 0)
		return ["J#{@trump_c}", "J#{@complement}"].include?(card) ? val+500 :
val
	end
end

cards = STDIN.read.split
puts Hand.new(cards, cards.shift).cards
123320fdc17940dfc8e365edb48fbff2?d=identicon&s=25 bob_showalter (Guest)
on 2005-11-22 14:25
(Received via mailing list)
Zed Lopez wrote:
> 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.
 > ...
> Ordering the hand can't come without considering what suits are present.

I addressed this by generating a suit order in alternating colors
starting with the trump suit. Then some hand checking of the various
possibilities showed me that the only adjustment needed was to swap the
second and fourth suits if the hand was void in the second suit. Given
that adjustment, any combination of suits in the hand should yield
properly alternating colors.
22eddcd715e3ff602d4bec1fbd863a53?d=identicon&s=25 zed.lopez (Guest)
on 2005-11-23 07:32
(Received via mailing list)
On 11/22/05, Bob Showalter <bob_showalter@taylorwhite.com> wrote:
> second and fourth suits if the hand was void in the second suit. Given
> that adjustment, any combination of suits in the hand should yield
> properly alternating colors.

I did it kind of bottom-up to your approach's top-down -- starting
with array of the suits present in the hand, in arbitrary order, I
moved the trump suit to the front. Then, if there were three or more
suits, if the second suit was the same color as the trump suit, I
swapped the second and third suits.
This topic is locked and can not be replied to.