rpoker 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5741ebe32752dd99d446d310de326c3135044d7e
4
- data.tar.gz: 19f9e061844a3cee879bf6b8446021a4c2d885b6
3
+ metadata.gz: 796e19ad9f806076d80c6b38cb37577a60fc2e48
4
+ data.tar.gz: 0229718b26ac3c2faf86e9c5061eb575807f6684
5
5
  SHA512:
6
- metadata.gz: 30d83de20f3889b6f7136e6da6986e71d87674db9ae7f8c67414aa3c706ba2bc6679a5fdac66dd1334bb64995c2e52b62d13b7e6c287ac172e5f0ff5c2de5b01
7
- data.tar.gz: a68d3baea1a99ef7b84e668e2779651929aafbe77931ce9fa8a08cadca05864ab9612dc7d0dd49928a3ca7e9ec16e31ccb412ca437e8b2988ff9412125199689
6
+ metadata.gz: 5b5576b3369451d33a07d1cc19cee5301bf2957b149e45558238b6b567058b75ee7eb3e989df69726fb7b8209fb2c493bed40922f4f5d043af000d763ba69110
7
+ data.tar.gz: 0102369e0d2c4e4020f3a8d0cbd1c84723aac6d3a60f9ca2be4c3cbeb0d58f0bbe0dab5998edcc2808f49545d6ba333713bbcbb54689d4888e7f0fad8de62e13
data/lib/rpoker/card.rb CHANGED
@@ -1,42 +1,64 @@
1
1
  class Card
2
- attr_reader :value, :suit
3
-
4
- FACES = %w(T J Q K A)
5
- SUITS = %w(h d c s)
6
- VALUES = %w(2 3 4 5 6 7 8 9) + FACES
7
-
8
- FACE_VALUES = {
9
- "T" => 10,
10
- "J" => 11,
11
- "Q" => 12,
12
- "K" => 13,
13
- "A" => 14
2
+ attr_reader :rank, :suit
3
+
4
+ FACE_TO_NUM = {
5
+ 'T' => 10,
6
+ 'J' => 11,
7
+ 'Q' => 12,
8
+ 'K' => 13,
9
+ 'A' => 14
14
10
  }
15
11
 
16
- def initialize(string)
17
- chars = string.split("")
18
- raise ArgumentError.new("Too many characters") if chars.size > 2
12
+ def initialize(str)
13
+ validate!(str)
19
14
 
20
- @value, @suit = [chars.first.upcase, chars.last.downcase]
21
- validate!
15
+ chars = str.chars
16
+ @rank = chars.first.upcase
17
+ @suit = chars.last.downcase
22
18
  end
23
19
 
24
20
  def to_s
25
- "#{@value}#{@suit}"
21
+ "#{rank}#{suit}"
26
22
  end
27
23
 
28
- def face_card?
29
- value.to_i.to_s != value
24
+ def face?
25
+ faces.include? rank
30
26
  end
31
27
 
32
28
  def to_i
33
- face_card? ? FACE_VALUES[value] : value.to_i
29
+ face? ? FACE_TO_NUM[rank] : rank.to_i
34
30
  end
35
31
 
36
32
  private
37
33
 
38
- def validate!
39
- raise ArgumentError.new("The first character must be a card value") unless VALUES.include?(value)
40
- raise ArgumentError.new("The second character must be a suit") unless SUITS.include?(suit)
34
+ def validate!(str)
35
+ raise ArgumentError, 'Input must be a string' unless str.is_a? String
36
+ raise ArgumentError, 'Wrong number of characters' unless str.length == 2
37
+ raise ArgumentError, 'Input must start with a rank' unless rank?(str[0])
38
+ raise ArgumentError, 'Input must end with a suit' unless suit?(str[1])
39
+ end
40
+
41
+ def rank?(str)
42
+ ranks.include? str.upcase
43
+ end
44
+
45
+ def suit?(str)
46
+ suits.include? str.downcase
47
+ end
48
+
49
+ def ranks
50
+ numerics + faces
51
+ end
52
+
53
+ def numerics
54
+ %w(2 3 4 5 6 7 8 9)
55
+ end
56
+
57
+ def faces
58
+ %w(T J Q K A)
59
+ end
60
+
61
+ def suits
62
+ %w(s c d h)
41
63
  end
42
64
  end
data/lib/rpoker/hand.rb CHANGED
@@ -14,18 +14,9 @@ class Hand
14
14
  }
15
15
 
16
16
  def initialize(cards)
17
- @cards =
18
- case cards
19
- when Array
20
- cards.map { |card| card.is_a?(Card) ? card : Card.new(card) }
21
- when String
22
- cards.split(" ").map {|s| Card.new(s)}
23
- else
24
- raise ArgumentError.new("Input must be a string or array")
25
- end
17
+ @cards = parse_cards(cards)
26
18
 
27
- validate!
28
- sort_cards!
19
+ validate_cards!
29
20
  end
30
21
 
31
22
  def <=>(other_hand)
@@ -47,15 +38,15 @@ class Hand
47
38
  puts cards.join(" ")
48
39
  end
49
40
 
50
- def suits
41
+ def card_suits
51
42
  cards.map(&:suit)
52
43
  end
53
44
 
54
- def values
55
- cards.map(&:value)
45
+ def card_ranks
46
+ cards.map(&:rank)
56
47
  end
57
48
 
58
- def nums
49
+ def card_values
59
50
  wheel? ? [5,4,3,2,1] : cards.map(&:to_i)
60
51
  end
61
52
 
@@ -64,70 +55,90 @@ class Hand
64
55
  end
65
56
 
66
57
  def flush?
67
- suits.uniq.size == 1
58
+ card_suits.uniq.size == 1
68
59
  end
69
60
 
70
61
  def straight?
71
- wheel? || (nums.first == nums.last + 4 && form == :abcde)
62
+ return true if wheel?
63
+ sorted_values = card_values.sort
64
+ sorted_values.last - sorted_values.first == 4 && form == :xxxxx
72
65
  end
73
66
 
74
67
  def wheel?
75
- values == %w(A 5 4 3 2)
68
+ card_ranks.sort == %w(A 2 3 4 5).sort
76
69
  end
77
70
 
78
71
  def four_of_a_kind?
79
- form == :aaaab
72
+ form == :AAAAx
80
73
  end
81
74
 
82
75
  def full_house?
83
- form == :aaabb
76
+ form == :AAABB
84
77
  end
85
78
 
86
79
  def three_of_a_kind?
87
- form == :aaabc
80
+ form == :AAAxx
88
81
  end
89
82
 
90
83
  def two_pair?
91
- form == :aabbc
84
+ form == :AABBx
92
85
  end
93
86
 
94
87
  def pair?
95
- form == :aabcd
88
+ form == :AAxxx
96
89
  end
97
90
 
98
- private
99
- # sort cards by their value multiplicity in descending order
100
- # e.g. sort Js 2s Jh 4s 2c as Js Jh 2s 2c 4s
101
- def sort_cards!
102
- @cards.sort_by! { |card| [-value_count[card.value], -card.to_i] }
91
+ def form
92
+ @form ||= compute_form
103
93
  end
104
94
 
105
- def value_count
106
- @value_count ||=
107
- values.each_with_object(Hash.new(0)) { |v, hsh| hsh[v] += 1 }
95
+ # sort cards by their rank multiplicity in descending order
96
+ # e.g. sort 2s Jh 4s Js 2c as Jh Js 2s 2c 4s
97
+ def sort!
98
+ @cards.sort_by! { |card| [-card_rank_to_count[card.rank], -card.to_i] }
108
99
  end
109
100
 
110
- def counts
111
- value_count.values.sort_by { |count| -count }
101
+ private
102
+
103
+ def card_rank_to_count
104
+ @card_rank_to_count ||=
105
+ card_ranks.each_with_object(Hash.new(0)) { |v, hsh| hsh[v] += 1 }
112
106
  end
113
107
 
114
- def form
115
- @form ||=
116
- counts.zip(%w(a b c d e)).map { |count, l| l*count }.join.to_sym
108
+ def compute_form
109
+ card_rank_counts = card_rank_to_count.values.sort.reverse
110
+ counts_for_duplicates = card_rank_counts.select { |c| c > 1 }
111
+
112
+ counts_for_duplicates.zip(['A', 'B'])
113
+ .map { |count, letter| letter * count }.join.ljust(5, 'x').to_sym
117
114
  end
118
115
 
119
- def validate!
116
+ def parse_cards(cards)
117
+ case cards
118
+ when Array then cards.map { |card| card.is_a?(Card) ? card : Card.new(card) }
119
+ when String then cards.split(' ').map { |s| Card.new(s) }
120
+ else raise ArgumentError, 'Input must be a string or array'
121
+ end
122
+ end
123
+
124
+ def validate_cards!
120
125
  validate_length!
121
126
  check_for_duplicates!
122
127
  end
123
128
 
124
129
  def validate_length!
125
- raise ArgumentError.new("A hand must contain 5 cards") unless cards.size == 5
130
+ unless cards.size == 5
131
+ raise ArgumentError, 'A hand must contain 5 cards'
132
+ end
126
133
  end
127
134
 
128
135
  def check_for_duplicates!
129
- unless cards.map(&:to_s).uniq.size == 5
130
- raise ArgumentError.new("A hand cannot contain duplicate cards")
136
+ if duplicates?
137
+ raise ArgumentError, 'A hand cannot contain duplicate cards'
131
138
  end
132
139
  end
140
+
141
+ def duplicates?
142
+ cards.map(&:to_s).uniq.size < 5
143
+ end
133
144
  end
@@ -12,7 +12,12 @@ class Matchup
12
12
 
13
13
  private
14
14
  def same_rank_winner
15
- hand1.nums.zip(hand2.nums).each do |v1, v2|
15
+ hand1.sort!
16
+ hand2.sort!
17
+
18
+ value_pairs = hand1.card_values.zip(hand2.card_values)
19
+
20
+ value_pairs.each do |v1, v2|
16
21
  return hand1 if v1 > v2
17
22
  return hand2 if v2 > v1
18
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpoker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Cornelis