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 +4 -4
- data/lib/rpoker/card.rb +46 -24
- data/lib/rpoker/hand.rb +51 -40
- data/lib/rpoker/matchup.rb +6 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 796e19ad9f806076d80c6b38cb37577a60fc2e48
|
4
|
+
data.tar.gz: 0229718b26ac3c2faf86e9c5061eb575807f6684
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 :
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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(
|
17
|
-
|
18
|
-
raise ArgumentError.new("Too many characters") if chars.size > 2
|
12
|
+
def initialize(str)
|
13
|
+
validate!(str)
|
19
14
|
|
20
|
-
|
21
|
-
|
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
|
-
"#{
|
21
|
+
"#{rank}#{suit}"
|
26
22
|
end
|
27
23
|
|
28
|
-
def
|
29
|
-
|
24
|
+
def face?
|
25
|
+
faces.include? rank
|
30
26
|
end
|
31
27
|
|
32
28
|
def to_i
|
33
|
-
|
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
|
40
|
-
raise ArgumentError
|
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
|
-
|
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
|
41
|
+
def card_suits
|
51
42
|
cards.map(&:suit)
|
52
43
|
end
|
53
44
|
|
54
|
-
def
|
55
|
-
cards.map(&:
|
45
|
+
def card_ranks
|
46
|
+
cards.map(&:rank)
|
56
47
|
end
|
57
48
|
|
58
|
-
def
|
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
|
-
|
58
|
+
card_suits.uniq.size == 1
|
68
59
|
end
|
69
60
|
|
70
61
|
def straight?
|
71
|
-
|
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
|
-
|
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 == :
|
72
|
+
form == :AAAAx
|
80
73
|
end
|
81
74
|
|
82
75
|
def full_house?
|
83
|
-
form == :
|
76
|
+
form == :AAABB
|
84
77
|
end
|
85
78
|
|
86
79
|
def three_of_a_kind?
|
87
|
-
form == :
|
80
|
+
form == :AAAxx
|
88
81
|
end
|
89
82
|
|
90
83
|
def two_pair?
|
91
|
-
form == :
|
84
|
+
form == :AABBx
|
92
85
|
end
|
93
86
|
|
94
87
|
def pair?
|
95
|
-
form == :
|
88
|
+
form == :AAxxx
|
96
89
|
end
|
97
90
|
|
98
|
-
|
99
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
111
|
-
|
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
|
115
|
-
|
116
|
-
|
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
|
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
|
-
|
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
|
-
|
130
|
-
raise ArgumentError
|
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
|
data/lib/rpoker/matchup.rb
CHANGED
@@ -12,7 +12,12 @@ class Matchup
|
|
12
12
|
|
13
13
|
private
|
14
14
|
def same_rank_winner
|
15
|
-
hand1.
|
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
|