Class: PokerHand

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/ruby-poker/poker_hand.rb

Constant Summary collapse

OPS =
[
  ['Royal Flush',     :royal_flush? ],
  ['Straight Flush',  :straight_flush? ],
  ['Four of a kind',  :four_of_a_kind? ],
  ['Full house',      :full_house? ],
  ['Flush',           :flush? ],
  ['Straight',        :straight? ],
  ['Three of a kind', :three_of_a_kind?],
  ['Two pair',        :two_pair? ],
  ['Pair',            :pair? ],
  ['Highest Card',    :highest_card? ],
]
RESOLVING_METHODS =

Resolving methods are just passed directly down to the @hand array

[:size, :+, :-]
@@allow_duplicates =

true by default

true

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cards = []) ⇒ PokerHand

Returns a new PokerHand object. Accepts the cards represented in a string or an array

PokerHand.new("3d 5c 8h Ks")   # => #<PokerHand:0x5c673c ...
PokerHand.new(["3d", "5c", "8h", "Ks"])  # => #<PokerHand:0x5c2d6c ...


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/ruby-poker/poker_hand.rb', line 14

def initialize(cards = [])
  if cards.is_a? Array
    @hand = cards.map do |card|
      if card.is_a? Card
        card
      else
        Card.new(card.to_s)
      end
    end
  elsif cards.respond_to?(:to_str)
    @hand = cards.scan(/\S{2,3}/).map { |str| Card.new(str) }
  else
    @hand = cards
  end
  
  check_for_duplicates if !@@allow_duplicates
end

Instance Attribute Details

#handObject (readonly)

Returns the value of attribute hand.



3
4
5
# File 'lib/ruby-poker/poker_hand.rb', line 3

def hand
  @hand
end

Class Method Details

.allow_duplicatesObject



6
# File 'lib/ruby-poker/poker_hand.rb', line 6

def self.allow_duplicates; @@allow_duplicates; end

.allow_duplicates=(v) ⇒ Object



7
# File 'lib/ruby-poker/poker_hand.rb', line 7

def self.allow_duplicates=(v); @@allow_duplicates = v; end

Instance Method Details

#<<(new_cards) ⇒ Object

Add a card to the hand

hand = PokerHand.new("5d")
hand << "6s"          # => Add a six of spades to the hand by passing a string
hand << ["7h", "8d"]  # => Add multiple cards to the hand using an array


303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/ruby-poker/poker_hand.rb', line 303

def << new_cards
  if new_cards.is_a?(Card) || new_cards.is_a?(String)
    new_cards = [new_cards]
  end
  
  new_cards.each do |nc|
    unless @@allow_duplicates
      raise "A card with the value #{nc} already exists in this hand. Set PokerHand.allow_duplicates to true if you want to be able to add a card more than once." if self =~ /#{nc}/
    end
    
    @hand << Card.new(nc)
  end
end

#<=>(other_hand) ⇒ Object



294
295
296
# File 'lib/ruby-poker/poker_hand.rb', line 294

def <=> other_hand
  self.score[0].compact <=> other_hand.score[0].compact
end

#=~(re) ⇒ Object

The =~ method does a regular expression match on the cards in this hand. This can be useful for many purposes. A common use is the check if a card exists in a hand.

PokerHand.new("3d 4d 5d") =~ /8h/           # => nil
PokerHand.new("3d 4d 5d") =~ /4d/           # => #<MatchData:0x615e18>


71
72
73
# File 'lib/ruby-poker/poker_hand.rb', line 71

def =~ (re)
  re.match(just_cards)
end

#by_faceObject

Returns a new PokerHand object with the cards sorted by value with the highest value first.

PokerHand.new("3d 5c 8h Ks").by_face.just_cards   # => "Ks 8h 5c 3d"


44
45
46
# File 'lib/ruby-poker/poker_hand.rb', line 44

def by_face
  PokerHand.new(@hand.sort_by { |c| [c.face, c.suit] }.reverse)
end

#by_suitObject

Returns a new PokerHand object with the cards sorted by suit The suit order is spades, hearts, diamonds, clubs

PokerHand.new("3d 5c 8h Ks").by_suit.just_cards   # => "Ks 8h 3d 5c"


36
37
38
# File 'lib/ruby-poker/poker_hand.rb', line 36

def by_suit
  PokerHand.new(@hand.sort_by { |c| [c.suit, c.face] }.reverse)
end

#delete(card) ⇒ Object

Remove a card from the hand.

hand = PokerHand.new("5d Jd")
hand.delete("Jd")           # => #<Card:0x5d0674 @value=23, @face=10, @suit=1>
hand.just_cards             # => "5d"


322
323
324
# File 'lib/ruby-poker/poker_hand.rb', line 322

def delete card
  @hand.delete(Card.new(card))
end

#face_valuesObject

Returns an array of the card values in the hand. The values returned are 1 less than the value on the card. For example: 2’s will be shown as 1.

PokerHand.new(["3c", "Kh"]).face_values     # => [2, 12]


61
62
63
# File 'lib/ruby-poker/poker_hand.rb', line 61

def face_values
  @hand.map { |c| c.face }
end

#flush?Boolean

Returns:

  • (Boolean)


127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ruby-poker/poker_hand.rb', line 127

def flush?
  if (md = (by_suit =~ /(.)(.) (.)\2 (.)\2 (.)\2 (.)\2/))
    [
      [
        6,
        Card::face_value(md[1]),
        *(md[3..6].map { |f| Card::face_value(f) })
      ],
      arrange_hand(md)
    ]
  else
    false
  end
end

#four_of_a_kind?Boolean

Returns:

  • (Boolean)


94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/ruby-poker/poker_hand.rb', line 94

def four_of_a_kind?
  if (md = (by_face =~ /(.). \1. \1. \1./))
    # get kicker
    (md.pre_match + md.post_match).match(/(\S)/)
    [
      [8, Card::face_value(md[1]), Card::face_value($1)],
      arrange_hand(md)
    ]
  else
    false
  end
end

#full_house?Boolean

Returns:

  • (Boolean)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ruby-poker/poker_hand.rb', line 107

def full_house?
  if (md = (by_face =~ /(.). \1. \1. (.*)(.). \3./))
    arranged_hand = arrange_hand(md[0] + ' ' +
        md.pre_match + ' ' + md[2] + ' ' + md.post_match)
    [
      [7, Card::face_value(md[1]), Card::face_value(md[3])],
      arranged_hand
    ]
  elsif (md = (by_face =~ /((.). \2.) (.*)((.). \5. \5.)/))
    arranged_hand = arrange_hand(md[4] + ' '  + md[1] + ' ' +
        md.pre_match + ' ' + md[3] + ' ' + md.post_match)
    [
      [7, Card::face_value(md[5]), Card::face_value(md[2])],
      arranged_hand
    ]
  else
    false
  end
end

#hand_ratingObject Also known as: rank

Returns the verbose hand rating

PokerHand.new("4s 5h 6c 7d 8s").hand_rating     # => "Straight"


250
251
252
253
254
# File 'lib/ruby-poker/poker_hand.rb', line 250

def hand_rating
  OPS.map { |op|
    (method(op[1]).call()) ? op[0] : false
  }.find { |v| v }
end

#highest_card?Boolean

Returns:

  • (Boolean)


229
230
231
232
# File 'lib/ruby-poker/poker_hand.rb', line 229

def highest_card?
  result = by_face
  [[1, *result.face_values[0..4]], result.hand.join(' ')]
end

#just_cardsObject Also known as: cards

Returns string representation of the hand without the rank

PokerHand.new(["3c", "Kh"]).just_cards     # => "3c Kh"


51
52
53
# File 'lib/ruby-poker/poker_hand.rb', line 51

def just_cards
  @hand.join(" ")
end

#pair?Boolean

Returns:

  • (Boolean)


209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/ruby-poker/poker_hand.rb', line 209

def pair?
  if (md = (by_face =~ /(.). \1./))
    # get kicker
    arranged_hand = arrange_hand(md)
    arranged_hand.match(/(?:\S\S ){2}(\S)\S\s+(\S)\S\s+(\S)/)
    [
      [
        2,
        Card::face_value(md[1]),
        Card::face_value($1),
        Card::face_value($2),
        Card::face_value($3)
      ],
      arranged_hand
    ]
  else
    false
  end
end

#royal_flush?Boolean

Returns:

  • (Boolean)


75
76
77
78
79
80
81
# File 'lib/ruby-poker/poker_hand.rb', line 75

def royal_flush?
  if (md = (by_suit =~ /A(.) K\1 Q\1 J\1 T\1/))
    [[10], arrange_hand(md)]
  else
    false
  end
end

#scoreObject



258
259
260
261
262
263
264
265
266
267
# File 'lib/ruby-poker/poker_hand.rb', line 258

def score
  # OPS.map returns an array containing the result of calling each OPS method again
  # the poker hand. The non-nil cell closest to the front of the array represents
  # the highest ranking.
  # find([0]) returns [0] instead of nil if the hand does not match any of the rankings
  # which is not likely to occur since every hand should at least have a highest card
  OPS.map { |op|
    method(op[1]).call()
  }.find([0]) { |score| score }
end

#sort_using_rankObject

Returns a string of the hand arranged based on its rank. Usually this will be the same as by_face but there are some cases where it makes a difference.

ph = PokerHand.new("As 3s 5s 2s 4s")
ph.sort_using_rank        # => "5s 4s 3s 2s As"
ph.by_face.just_cards       # => "As 5s 4s 3s 2s"


275
276
277
# File 'lib/ruby-poker/poker_hand.rb', line 275

def sort_using_rank
  score[1]
end

#straight?Boolean

Returns:

  • (Boolean)


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/ruby-poker/poker_hand.rb', line 142

def straight?
  result = false
  if hand.size >= 5
    transform = delta_transform
    # note we can have more than one delta 0 that we
    # need to shuffle to the back of the hand
    i = 0
    until transform.match(/^\S{3}( [1-9x]\S\S)+( 0\S\S)*$/) or i >= hand.size  do
      # only do this once per card in the hand to avoid entering an
      # infinite loop if all of the cards in the hand are the same
      transform.gsub!(/(\s0\S\S)(.*)/, "\\2\\1")    # moves the front card to the back of the string
      i += 1
    end
    if (md = (/.(.). 1.. 1.. 1.. 1../.match(transform)))
      high_card = Card::face_value(md[1])
      arranged_hand = fix_low_ace_display(md[0] + ' ' + md.pre_match + ' ' + md.post_match)
      result = [[5, high_card], arranged_hand]
    end
  end
end

#straight_flush?Boolean

Returns:

  • (Boolean)


83
84
85
86
87
88
89
90
91
92
# File 'lib/ruby-poker/poker_hand.rb', line 83

def straight_flush?
  if (md = (/.(.)(.)(?: 1.\2){4}/.match(delta_transform(true))))
    high_card = Card::face_value(md[1])
    arranged_hand = fix_low_ace_display(md[0] + ' ' +
        md.pre_match + ' ' + md.post_match)
    [[9, high_card], arranged_hand]
  else
    false
  end
end

#three_of_a_kind?Boolean

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/ruby-poker/poker_hand.rb', line 163

def three_of_a_kind?
  if (md = (by_face =~ /(.). \1. \1./))
    # get kicker
    arranged_hand = arrange_hand(md)
    arranged_hand.match(/(?:\S\S ){3}(\S)\S (\S)/)
    [
      [
        4,
        Card::face_value(md[1]),
        Card::face_value($1),
        Card::face_value($2)
      ],
      arranged_hand
    ]
  else
    false
  end
end

#to_aObject Also known as: to_ary

Returns an array of ‘Card` objects that make up the `PokerHand`.



288
289
290
# File 'lib/ruby-poker/poker_hand.rb', line 288

def to_a
  @hand
end

#to_sObject

Returns string with a listing of the cards in the hand followed by the hand’s rank.

h = PokerHand.new("8c 8s")
h.to_s                      # => "8c 8s (Pair)"


283
284
285
# File 'lib/ruby-poker/poker_hand.rb', line 283

def to_s
  just_cards + " (" + hand_rating + ")"
end

#two_pair?Boolean

Returns:

  • (Boolean)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/ruby-poker/poker_hand.rb', line 182

def two_pair?
  # \1 is the face value of the first pair
  # \2 is the card in between the first pair and the second pair
  # \3 is the face value of the second pair
  if (md = (by_face =~ /(.). \1.(.*?) (.). \3./))
    # to get the kicker this does the following
    # md[0] is the regex matched above which includes the first pair and
    # the second pair but also some cards in the middle so we sub them out
    # then we add on the cards that came before the first pair, the cards
    # that were in-between, and the cards that came after.
    arranged_hand = arrange_hand(md[0].sub(md[2], '') + ' ' +
        md.pre_match + ' ' + md[2] + ' ' + md.post_match)
    arranged_hand.match(/(?:\S\S ){4}(\S)/)
    [
      [
        3,
        Card::face_value(md[1]),    # face value of the first pair
        Card::face_value(md[3]),    # face value of the second pair
        Card::face_value($1)        # face value of the kicker
      ],
      arranged_hand
    ]
  else
    false
  end
end

#uniqObject

Same concept as Array#uniq



327
328
329
# File 'lib/ruby-poker/poker_hand.rb', line 327

def uniq
  PokerHand.new(@hand.uniq)
end