deck_of_cards_handler 0.1.4 → 0.1.6
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/CHANGELOG.md +4 -0
- data/README.md +59 -0
- data/lib/deck_of_cards_handler/card/card.rb +8 -16
- data/lib/deck_of_cards_handler/packet/packet.rb +3 -6
- data/lib/deck_of_cards_handler/poker_hand.rb +13 -0
- data/lib/deck_of_cards_handler/poker_hands/flush.rb +26 -0
- data/lib/deck_of_cards_handler/poker_hands/four_of_a_kind.rb +66 -0
- data/lib/deck_of_cards_handler/poker_hands/full_house.rb +65 -0
- data/lib/deck_of_cards_handler/poker_hands/high_card.rb +25 -0
- data/lib/deck_of_cards_handler/poker_hands/one_pair.rb +66 -0
- data/lib/deck_of_cards_handler/poker_hands/poker_hand.rb +61 -0
- data/lib/deck_of_cards_handler/poker_hands/straight.rb +50 -0
- data/lib/deck_of_cards_handler/poker_hands/straight_flush.rb +46 -0
- data/lib/deck_of_cards_handler/poker_hands/three_of_a_kind.rb +66 -0
- data/lib/deck_of_cards_handler/poker_hands/two_pairs.rb +69 -0
- data/lib/deck_of_cards_handler/version.rb +1 -1
- data/lib/deck_of_cards_handler.rb +1 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7de6705b81dcdbf0b0a3401bb40a0a343fd6794ff909994c658c8a9f3d2304c
|
|
4
|
+
data.tar.gz: 8a4e77d32f0bf80d9fb5aab2b0fb79ce88c4a00715cc0ee40a13b019e1278ba4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2493bd9f55e74662de828a3a4260bb4a0d4f638fb4e1d26326091921e654cf1f8313243dccecd0e06f152229a68f9d331429ff4a25621ea35b419d84506f3d73
|
|
7
|
+
data.tar.gz: 18f233fa7fd9626e39b49b599d39381a3a69cf2c7650e85d1b2f9b0c345a8d9be6a521fce8d05a84526c556630973b849033d62a9c64285977b63bf3a508287d
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -46,6 +46,62 @@ gem install deck_of_cards_handler
|
|
|
46
46
|
# assign a position value to the cards
|
|
47
47
|
deck.set_cards_positions
|
|
48
48
|
|
|
49
|
+
deck
|
|
50
|
+
# =>
|
|
51
|
+
# <Packet:0x000000012ae2b848
|
|
52
|
+
# @cards=
|
|
53
|
+
[#<Card:0x000000012ae2bd20 @position=1, @suit="S", @value="4">,
|
|
54
|
+
#<Card:0x000000012ae2cae0 @position=2, @suit="H", @value="2">,
|
|
55
|
+
#<Card:0x000000012ae2c220 @position=3, @suit="D", @value="7">,
|
|
56
|
+
#<Card:0x000000012ae2bd98 @position=4, @suit="S", @value="3">,
|
|
57
|
+
#<Card:0x000000012ae2c9f0 @position=5, @suit="H", @value="4">,
|
|
58
|
+
#<Card:0x000000012ae2c298 @position=6, @suit="D", @value="6">,
|
|
59
|
+
#<Card:0x000000012ae2df08 @position=7, @suit="C", @value="A">,
|
|
60
|
+
#<Card:0x000000012ae2c978 @position=8, @suit="H", @value="5">,
|
|
61
|
+
#<Card:0x000000012ae2ce00 @position=9, @suit="C", @value="9">,
|
|
62
|
+
#<Card:0x000000012ae2d260 @position=10, @suit="C", @value="2">,
|
|
63
|
+
#<Card:0x000000012ae2c630 @position=11, @suit="H", @value="Q">,
|
|
64
|
+
#<Card:0x000000012ae2c400 @position=12, @suit="D", @value="3">,
|
|
65
|
+
#<Card:0x000000012ae2b960 @position=13, @suit="S", @value="Q">,
|
|
66
|
+
#<Card:0x000000012ae2c810 @position=14, @suit="H", @value="8">,
|
|
67
|
+
#<Card:0x000000012ae2cf68 @position=15, @suit="C", @value="6">,
|
|
68
|
+
#<Card:0x000000012ae2cfe0 @position=16, @suit="C", @value="5">,
|
|
69
|
+
#<Card:0x000000012ae2c798 @position=17, @suit="H", @value="9">,
|
|
70
|
+
#<Card:0x000000012ae2b8e8 @position=18, @suit="S", @value="K">,
|
|
71
|
+
#<Card:0x000000012ae2c478 @position=19, @suit="D", @value="2">,
|
|
72
|
+
#<Card:0x000000012ae2c6a8 @position=20, @suit="H", @value="J">,
|
|
73
|
+
#<Card:0x000000012ae2d0d0 @position=21, @suit="C", @value="3">,
|
|
74
|
+
#<Card:0x000000012ae2ce78 @position=22, @suit="C", @value="8">,
|
|
75
|
+
#<Card:0x000000012ae2c900 @position=23, @suit="H", @value="6">,
|
|
76
|
+
#<Card:0x000000012ae2ba50 @position=24, @suit="S", @value="10">,
|
|
77
|
+
#<Card:0x000000012ae2c310 @position=25, @suit="D", @value="5">,
|
|
78
|
+
#<Card:0x000000012ae2bf50 @position=26, @suit="D", @value="K">,
|
|
79
|
+
#<Card:0x000000012ae2be10 @position=27, @suit="S", @value="2">,
|
|
80
|
+
#<Card:0x000000012ae2ca68 @position=28, @suit="H", @value="3">,
|
|
81
|
+
#<Card:0x000000012ae2c1a8 @position=29, @suit="D", @value="8">,
|
|
82
|
+
#<Card:0x000000012ae2bca8 @position=30, @suit="S", @value="5">,
|
|
83
|
+
#<Card:0x000000012ae2cc20 @position=31, @suit="C", @value="K">,
|
|
84
|
+
#<Card:0x000000012ae2c040 @position=32, @suit="D", @value="J">,
|
|
85
|
+
#<Card:0x000000012ae2bb40 @position=33, @suit="S", @value="8">,
|
|
86
|
+
#<Card:0x000000012ae2cd88 @position=34, @suit="C", @value="10">,
|
|
87
|
+
#<Card:0x000000012ae2c5b8 @position=35, @suit="H", @value="K">,
|
|
88
|
+
#<Card:0x000000012ae2b9d8 @position=36, @suit="S", @value="J">,
|
|
89
|
+
#<Card:0x000000012ae2cef0 @position=37, @suit="C", @value="7">,
|
|
90
|
+
#<Card:0x000000012ae2c720 @position=38, @suit="H", @value="10">,
|
|
91
|
+
#<Card:0x000000012ae2c4f0 @position=39, @suit="D", @value="A">,
|
|
92
|
+
#<Card:0x000000012ae2d058 @position=40, @suit="C", @value="4">,
|
|
93
|
+
#<Card:0x000000012ae2c888 @position=41, @suit="H", @value="7">,
|
|
94
|
+
#<Card:0x000000012ae2c388 @position=42, @suit="D", @value="4">,
|
|
95
|
+
#<Card:0x000000012ae2be88 @position=43, @suit="S", @value="A">,
|
|
96
|
+
#<Card:0x000000012ae2bac8 @position=44, @suit="S", @value="9">,
|
|
97
|
+
#<Card:0x000000012ae2cd10 @position=45, @suit="C", @value="J">,
|
|
98
|
+
#<Card:0x000000012ae2bfc8 @position=46, @suit="D", @value="Q">,
|
|
99
|
+
#<Card:0x000000012ae2bbb8 @position=47, @suit="S", @value="7">,
|
|
100
|
+
#<Card:0x000000012ae2cc98 @position=48, @suit="C", @value="Q">,
|
|
101
|
+
#<Card:0x000000012ae2c0b8 @position=49, @suit="D", @value="10">,
|
|
102
|
+
#<Card:0x000000012ae2bc30 @position=50, @suit="S", @value="6">,
|
|
103
|
+
#<Card:0x000000012ae2cb58 @position=51, @suit="H", @value="A">,
|
|
104
|
+
#<Card:0x000000012ae2c130 @position=52, @suit="D", @value="9">]>
|
|
49
105
|
```
|
|
50
106
|
|
|
51
107
|
</details>
|
|
@@ -68,6 +124,9 @@ gem install deck_of_cards_handler
|
|
|
68
124
|
deck.shuffle
|
|
69
125
|
|
|
70
126
|
hands = deck.deal_into_piles(number_of_piles: 5, number_of_cards: 5)
|
|
127
|
+
|
|
128
|
+
hands.map(&:to_s)
|
|
129
|
+
# => [["5 of D", "8 of C", "6 of S", "10 of D", "5 of C"], ["7 of C", "5 of S", "4 of C", "2 of D", "Q of D"], ["3 of S", "8 of D", "A of D", "2 of C", "7 of D"], ["Q of H", "4 of S", "3 of D", "J of S", "9 of S"], ["6 of C", "6 of H", "10 of C", "4 of D", "A of H"]]
|
|
71
130
|
```
|
|
72
131
|
|
|
73
132
|
</details>
|
|
@@ -50,28 +50,20 @@ class Card
|
|
|
50
50
|
other.suit == suit && other.value == value
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
sig {
|
|
54
|
-
def rank
|
|
53
|
+
sig { params(low_ace: T::Boolean).returns(Integer) }
|
|
54
|
+
def rank(low_ace: false)
|
|
55
55
|
case value
|
|
56
|
-
when "A" then
|
|
57
|
-
when "J" then
|
|
58
|
-
when "Q" then
|
|
59
|
-
when "K" then
|
|
60
|
-
else
|
|
56
|
+
when "A" then low_ace ? 1 : 14
|
|
57
|
+
when "J" then 11
|
|
58
|
+
when "Q" then 12
|
|
59
|
+
when "K" then 13
|
|
60
|
+
else value.to_i
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
sig { params(other: Card).returns(Integer) }
|
|
65
65
|
def <=>(other)
|
|
66
|
-
|
|
67
|
-
my_ranks = rank
|
|
68
|
-
other_ranks = other.rank
|
|
69
|
-
|
|
70
|
-
# check if any ranks overlap (Ace == 1 or 14)
|
|
71
|
-
return 0 if (my_ranks & other_ranks).any?
|
|
72
|
-
|
|
73
|
-
# otherwise compare by highest possible
|
|
74
|
-
T.must(my_ranks.max) <=> T.must(other_ranks.max)
|
|
66
|
+
rank <=> other.rank
|
|
75
67
|
end
|
|
76
68
|
|
|
77
69
|
class << self
|
|
@@ -52,9 +52,9 @@ class Packet
|
|
|
52
52
|
packet
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
sig { params(string: String).returns(Packet) }
|
|
55
|
+
sig { overridable.params(string: String).returns(Packet) }
|
|
56
56
|
def build_from_string(string:) # rubocop:disable Metrics
|
|
57
|
-
content = string.split(",")
|
|
57
|
+
content = string.split(",").map(&:strip)
|
|
58
58
|
cards = []
|
|
59
59
|
cards_set = Set.new
|
|
60
60
|
|
|
@@ -70,10 +70,7 @@ class Packet
|
|
|
70
70
|
cards << card
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
packet.set_cards_positions
|
|
75
|
-
|
|
76
|
-
packet
|
|
73
|
+
Packet.new(cards:)
|
|
77
74
|
end
|
|
78
75
|
end
|
|
79
76
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "poker_hands/poker_hand"
|
|
4
|
+
|
|
5
|
+
require_relative "poker_hands/high_card"
|
|
6
|
+
require_relative "poker_hands/one_pair"
|
|
7
|
+
require_relative "poker_hands/two_pairs"
|
|
8
|
+
require_relative "poker_hands/three_of_a_kind"
|
|
9
|
+
require_relative "poker_hands/straight"
|
|
10
|
+
require_relative "poker_hands/flush"
|
|
11
|
+
require_relative "poker_hands/full_house"
|
|
12
|
+
require_relative "poker_hands/four_of_a_kind"
|
|
13
|
+
require_relative "poker_hands/straight_flush"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class Flush < PokerHand
|
|
6
|
+
sig { override.returns(Integer) }
|
|
7
|
+
def rank
|
|
8
|
+
6
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
12
|
+
def <=>(other)
|
|
13
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
14
|
+
|
|
15
|
+
0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
20
|
+
def is?(cards)
|
|
21
|
+
suit = T.must(cards.first).suit
|
|
22
|
+
cards.all? { _1.suit == suit }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class FourOfAKind < PokerHand
|
|
6
|
+
class << self
|
|
7
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
8
|
+
def is?(cards)
|
|
9
|
+
counts = cards.map(&:rank).flatten.tally
|
|
10
|
+
counts.values.count(4) == 1
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
sig { returns(T::Array[Card]) }
|
|
15
|
+
attr_reader :four_of_a_kind
|
|
16
|
+
|
|
17
|
+
sig { returns(T::Array[Card]) }
|
|
18
|
+
attr_reader :kicker
|
|
19
|
+
|
|
20
|
+
sig { params(cards: T::Array[Card]).void }
|
|
21
|
+
def initialize(cards:)
|
|
22
|
+
super
|
|
23
|
+
@four_of_a_kind = T.let(extract_four_of_a_kind, T::Array[Card])
|
|
24
|
+
@kicker = T.let(extract_kicker, T::Array[Card])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { override.returns(Integer) }
|
|
28
|
+
def rank
|
|
29
|
+
8
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
33
|
+
def <=>(other)
|
|
34
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
35
|
+
|
|
36
|
+
comparison = four_of_a_kind_value <=> other.four_of_a_kind_value
|
|
37
|
+
return comparison unless comparison.zero?
|
|
38
|
+
|
|
39
|
+
kicker_value <=> other.kicker_value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
sig { returns(Integer) }
|
|
45
|
+
def four_of_a_kind_value
|
|
46
|
+
T.must(four_of_a_kind.first).rank
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sig { returns(Integer) }
|
|
50
|
+
def kicker_value
|
|
51
|
+
T.must(kicker.first).rank
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
sig { returns(T::Array[Card]) }
|
|
57
|
+
def extract_four_of_a_kind
|
|
58
|
+
cards.group_by(&:rank).values.select { _1.size == 4 }.flatten
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
sig { returns(T::Array[Card]) }
|
|
62
|
+
def extract_kicker
|
|
63
|
+
cards.group_by(&:rank).values.select { _1.size == 1 }.flatten
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class FullHouse < PokerHand
|
|
6
|
+
class << self
|
|
7
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
8
|
+
def is?(cards)
|
|
9
|
+
PokerHands::ThreeOfAKind.is?(cards) && PokerHands::OnePair.is?(cards)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
sig { returns(T::Array[Card]) }
|
|
14
|
+
attr_reader :three_of_a_kind
|
|
15
|
+
|
|
16
|
+
sig { returns(T::Array[Card]) }
|
|
17
|
+
attr_reader :pair
|
|
18
|
+
|
|
19
|
+
sig { params(cards: T::Array[Card]).void }
|
|
20
|
+
def initialize(cards:)
|
|
21
|
+
super
|
|
22
|
+
@three_of_a_kind = T.let(extract_three_of_a_kind, T::Array[Card])
|
|
23
|
+
@pair = T.let(extract_pair, T::Array[Card])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { override.returns(Integer) }
|
|
27
|
+
def rank
|
|
28
|
+
7
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
32
|
+
def <=>(other)
|
|
33
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
34
|
+
|
|
35
|
+
comparison = three_of_a_kind_value <=> other.three_of_a_kind_value
|
|
36
|
+
return comparison unless comparison.zero?
|
|
37
|
+
|
|
38
|
+
pair_value <=> other.pair_value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
sig { returns(Integer) }
|
|
44
|
+
def three_of_a_kind_value
|
|
45
|
+
T.must(three_of_a_kind.first).rank
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sig { returns(Integer) }
|
|
49
|
+
def pair_value
|
|
50
|
+
T.must(pair.first).rank
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
sig { returns(T::Array[Card]) }
|
|
56
|
+
def extract_three_of_a_kind
|
|
57
|
+
cards.group_by(&:rank).values.select { _1.size == 3 }.flatten
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
sig { returns(T::Array[Card]) }
|
|
61
|
+
def extract_pair
|
|
62
|
+
cards.group_by(&:rank).values.select { _1.size == 2 }.flatten
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class HighCard < PokerHand
|
|
6
|
+
sig { override.returns(Integer) }
|
|
7
|
+
def rank
|
|
8
|
+
1
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
12
|
+
def <=>(other)
|
|
13
|
+
return rank <=> other.rank if self.class != other.class
|
|
14
|
+
|
|
15
|
+
sum_cards_ranks(cards) <=> sum_cards_ranks(other.cards)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
sig { params(cards: T::Array[Card]).returns(Integer) }
|
|
21
|
+
def sum_cards_ranks(cards)
|
|
22
|
+
T.must(cards.map(&:rank).reduce(&:+))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class OnePair < PokerHand
|
|
6
|
+
class << self
|
|
7
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
8
|
+
def is?(cards)
|
|
9
|
+
counts = cards.map(&:rank).flatten.tally
|
|
10
|
+
counts.values.count(2) == 1
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
sig { returns(T::Array[Card]) }
|
|
15
|
+
attr_reader :pair
|
|
16
|
+
|
|
17
|
+
sig { returns(T::Array[Card]) }
|
|
18
|
+
attr_reader :kickers
|
|
19
|
+
|
|
20
|
+
sig { params(cards: T::Array[Card]).void }
|
|
21
|
+
def initialize(cards:)
|
|
22
|
+
super
|
|
23
|
+
@pair = T.let(extract_pair, T::Array[Card])
|
|
24
|
+
@kickers = T.let(extract_kickers, T::Array[Card])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { override.returns(Integer) }
|
|
28
|
+
def rank
|
|
29
|
+
2
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
33
|
+
def <=>(other)
|
|
34
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
35
|
+
|
|
36
|
+
pair_comparison = pair_value <=> other.pair_value
|
|
37
|
+
return pair_comparison unless pair_comparison.zero?
|
|
38
|
+
|
|
39
|
+
kickers_value <=> other.kickers_value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
sig { returns(Integer) }
|
|
45
|
+
def pair_value
|
|
46
|
+
T.must(pair.first).rank
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sig { returns(Integer) }
|
|
50
|
+
def kickers_value
|
|
51
|
+
T.must(kickers.map(&:rank).reduce(&:+))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
sig { returns(T::Array[Card]) }
|
|
57
|
+
def extract_pair
|
|
58
|
+
cards.group_by(&:rank).values.select { _1.size == 2 }.flatten
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
sig { returns(T::Array[Card]) }
|
|
62
|
+
def extract_kickers
|
|
63
|
+
cards.group_by(&:rank).values.select { _1.size == 1 }.flatten
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
# This represents a poker hands raking
|
|
6
|
+
class PokerHand
|
|
7
|
+
extend T::Sig
|
|
8
|
+
extend T::Helpers
|
|
9
|
+
include Comparable
|
|
10
|
+
abstract!
|
|
11
|
+
|
|
12
|
+
sig { returns(T::Array[Card]) }
|
|
13
|
+
attr_reader :cards
|
|
14
|
+
|
|
15
|
+
sig { params(cards: T::Array[Card]).void }
|
|
16
|
+
def initialize(cards:)
|
|
17
|
+
raise ArgumentError if cards.size != 5
|
|
18
|
+
|
|
19
|
+
@cards = cards
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
HANDS = %i[OnePair TwoPairs ThreeOfAKind Straight Flush FullHouse FourOfAKind StraightFlush].freeze
|
|
23
|
+
|
|
24
|
+
sig { abstract.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
25
|
+
def <=>(other); end
|
|
26
|
+
|
|
27
|
+
sig { abstract.returns(Integer) }
|
|
28
|
+
def rank; end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
extend T::Sig
|
|
32
|
+
sig { params(string: String).returns(PokerHand) }
|
|
33
|
+
def build_from_string(string:)
|
|
34
|
+
cards = Packet.build_from_string(string:).cards
|
|
35
|
+
PokerHand.create(cards:)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
sig do
|
|
39
|
+
params(cards: T::Array[Card]).returns(
|
|
40
|
+
T.any(
|
|
41
|
+
HighCard,
|
|
42
|
+
OnePair,
|
|
43
|
+
TwoPairs,
|
|
44
|
+
ThreeOfAKind,
|
|
45
|
+
Straight,
|
|
46
|
+
Flush,
|
|
47
|
+
FullHouse,
|
|
48
|
+
FourOfAKind,
|
|
49
|
+
StraightFlush
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
def create(cards:)
|
|
54
|
+
HANDS.reverse_each do |hand|
|
|
55
|
+
return PokerHands.const_get(hand).new(cards:) if PokerHands.const_get(hand).is?(cards)
|
|
56
|
+
end
|
|
57
|
+
HighCard.new(cards:)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class Straight < PokerHand
|
|
6
|
+
class << self
|
|
7
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
8
|
+
def is?(cards)
|
|
9
|
+
ranks = cards.map(&:rank).sort
|
|
10
|
+
return true if ranks.each_cons(2).all? { |a, b| b == T.must(a) + 1 }
|
|
11
|
+
|
|
12
|
+
ranks = cards.map { _1.rank(low_ace: true) }.sort
|
|
13
|
+
ranks.each_cons(2).all? { |a, b| b == T.must(a) + 1 }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
sig { override.returns(Integer) }
|
|
18
|
+
def rank
|
|
19
|
+
5
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
23
|
+
def <=>(other)
|
|
24
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
25
|
+
|
|
26
|
+
c1 = cards.map { card_value(_1) }
|
|
27
|
+
c2 = other.cards.map { other.card_value(_1) }
|
|
28
|
+
|
|
29
|
+
c1 <=> c2
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
protected
|
|
33
|
+
|
|
34
|
+
sig { params(card: Card).returns(Integer) }
|
|
35
|
+
def card_value(card)
|
|
36
|
+
return card.rank unless card.value == "A"
|
|
37
|
+
return 1 if low_ace?
|
|
38
|
+
|
|
39
|
+
14
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
sig { returns(T::Boolean) }
|
|
45
|
+
def low_ace?
|
|
46
|
+
ranks = cards.map { _1.rank(low_ace: true) }.sort
|
|
47
|
+
ranks.each_cons(2).all? { |a, b| b == T.must(a) + 1 }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class StraightFlush < PokerHand
|
|
6
|
+
class << self
|
|
7
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
8
|
+
def is?(cards)
|
|
9
|
+
Flush.is?(cards) && Straight.is?(cards)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
sig { override.returns(Integer) }
|
|
14
|
+
def rank
|
|
15
|
+
9
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
19
|
+
def <=>(other)
|
|
20
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
21
|
+
|
|
22
|
+
c1 = cards.map { card_value(_1) }
|
|
23
|
+
c2 = other.cards.map { other.card_value(_1) }
|
|
24
|
+
|
|
25
|
+
c1 <=> c2
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
|
|
30
|
+
sig { params(card: Card).returns(Integer) }
|
|
31
|
+
def card_value(card)
|
|
32
|
+
return card.rank unless card.value == "A"
|
|
33
|
+
return 1 if low_ace?
|
|
34
|
+
|
|
35
|
+
14
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
sig { returns(T::Boolean) }
|
|
41
|
+
def low_ace?
|
|
42
|
+
ranks = cards.map { _1.rank(low_ace: true) }.sort
|
|
43
|
+
ranks.each_cons(2).all? { |a, b| b == T.must(a) + 1 }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class ThreeOfAKind < PokerHand
|
|
6
|
+
class << self
|
|
7
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
8
|
+
def is?(cards)
|
|
9
|
+
counts = cards.map(&:rank).flatten.tally
|
|
10
|
+
counts.values.count(3) == 1
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
sig { returns(T::Array[Card]) }
|
|
15
|
+
attr_reader :three_of_a_kind
|
|
16
|
+
|
|
17
|
+
sig { returns(T::Array[Card]) }
|
|
18
|
+
attr_reader :kickers
|
|
19
|
+
|
|
20
|
+
sig { params(cards: T::Array[Card]).void }
|
|
21
|
+
def initialize(cards:)
|
|
22
|
+
super
|
|
23
|
+
@three_of_a_kind = T.let(extract_three_of_a_kind, T::Array[Card])
|
|
24
|
+
@kickers = T.let(extract_kickers, T::Array[Card])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { override.returns(Integer) }
|
|
28
|
+
def rank
|
|
29
|
+
4
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
33
|
+
def <=>(other)
|
|
34
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
35
|
+
|
|
36
|
+
comparison = three_of_a_kind_value <=> other.three_of_a_kind_value
|
|
37
|
+
return comparison unless comparison.zero?
|
|
38
|
+
|
|
39
|
+
kickers_value <=> other.kickers_value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
sig { returns(Integer) }
|
|
45
|
+
def three_of_a_kind_value
|
|
46
|
+
T.must(three_of_a_kind.first).rank
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sig { returns(Integer) }
|
|
50
|
+
def kickers_value
|
|
51
|
+
T.must(kickers.map(&:rank).reduce(&:+))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
sig { returns(T::Array[Card]) }
|
|
57
|
+
def extract_three_of_a_kind
|
|
58
|
+
cards.group_by(&:rank).values.select { _1.size == 3 }.flatten
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
sig { returns(T::Array[Card]) }
|
|
62
|
+
def extract_kickers
|
|
63
|
+
cards.group_by(&:rank).values.select { _1.size == 1 }.flatten
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
4
|
+
module PokerHands
|
|
5
|
+
class TwoPairs < PokerHand
|
|
6
|
+
sig { returns(T::Array[Card]) }
|
|
7
|
+
attr_reader :pairs
|
|
8
|
+
|
|
9
|
+
sig { returns(T::Array[Card]) }
|
|
10
|
+
attr_reader :kicker
|
|
11
|
+
|
|
12
|
+
sig { params(cards: T::Array[Card]).void }
|
|
13
|
+
def initialize(cards:)
|
|
14
|
+
super
|
|
15
|
+
@pairs = T.let(extract_pairs, T::Array[Card])
|
|
16
|
+
@kicker = T.let(extract_kicker, T::Array[Card])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
sig { override.returns(Integer) }
|
|
20
|
+
def rank
|
|
21
|
+
3
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
|
25
|
+
def <=>(other) # rubocop:disable Metrics/AbcSize
|
|
26
|
+
return rank <=> other.rank unless instance_of?(other.class)
|
|
27
|
+
|
|
28
|
+
first_pair_comparison = pair_values.first <=> other.pair_values.first
|
|
29
|
+
return first_pair_comparison unless T.must(first_pair_comparison).zero?
|
|
30
|
+
|
|
31
|
+
second_pair_comparison = pair_values.last <=> other.pair_values.last
|
|
32
|
+
return second_pair_comparison unless T.must(second_pair_comparison).zero?
|
|
33
|
+
|
|
34
|
+
kickers_value <=> other.kickers_value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
sig { params(cards: T::Array[Card]).returns(T::Boolean) }
|
|
39
|
+
def is?(cards)
|
|
40
|
+
counts = cards.map(&:rank).flatten.tally
|
|
41
|
+
counts.values.count(2) == 2
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
protected
|
|
46
|
+
|
|
47
|
+
sig { returns(T::Array[Integer]) }
|
|
48
|
+
def pair_values
|
|
49
|
+
pairs.map(&:rank).sort.reverse
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
sig { returns(Integer) }
|
|
53
|
+
def kickers_value
|
|
54
|
+
T.must(kicker.map(&:rank).reduce(&:+))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
sig { returns(T::Array[Card]) }
|
|
60
|
+
def extract_pairs
|
|
61
|
+
cards.group_by(&:rank).values.select { _1.size == 2 }.flatten
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
sig { returns(T::Array[Card]) }
|
|
65
|
+
def extract_kicker
|
|
66
|
+
cards.group_by(&:rank).values.select { _1.size == 1 }.flatten
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: deck_of_cards_handler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Bernard
|
|
@@ -75,6 +75,17 @@ files:
|
|
|
75
75
|
- lib/deck_of_cards_handler/packet/deals.rb
|
|
76
76
|
- lib/deck_of_cards_handler/packet/packet.rb
|
|
77
77
|
- lib/deck_of_cards_handler/packet/shuffles.rb
|
|
78
|
+
- lib/deck_of_cards_handler/poker_hand.rb
|
|
79
|
+
- lib/deck_of_cards_handler/poker_hands/flush.rb
|
|
80
|
+
- lib/deck_of_cards_handler/poker_hands/four_of_a_kind.rb
|
|
81
|
+
- lib/deck_of_cards_handler/poker_hands/full_house.rb
|
|
82
|
+
- lib/deck_of_cards_handler/poker_hands/high_card.rb
|
|
83
|
+
- lib/deck_of_cards_handler/poker_hands/one_pair.rb
|
|
84
|
+
- lib/deck_of_cards_handler/poker_hands/poker_hand.rb
|
|
85
|
+
- lib/deck_of_cards_handler/poker_hands/straight.rb
|
|
86
|
+
- lib/deck_of_cards_handler/poker_hands/straight_flush.rb
|
|
87
|
+
- lib/deck_of_cards_handler/poker_hands/three_of_a_kind.rb
|
|
88
|
+
- lib/deck_of_cards_handler/poker_hands/two_pairs.rb
|
|
78
89
|
- lib/deck_of_cards_handler/version.rb
|
|
79
90
|
- sig/deck_of_cards.rbs
|
|
80
91
|
- sorbet/config
|