deck_of_cards_handler 0.1.5 → 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/lib/deck_of_cards_handler/packet/packet.rb +1 -4
- data/lib/deck_of_cards_handler/poker_hand.rb +11 -121
- 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
- metadata +11 -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
@@ -1,123 +1,13 @@
|
|
1
|
-
# typed: strict
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
class << self
|
17
|
-
sig { override.params(string: String).returns(PokerHand) }
|
18
|
-
def build_from_string(string:)
|
19
|
-
result = super
|
20
|
-
PokerHand.new(cards: result.cards)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
HAND_RANKS = T.let(
|
25
|
-
{
|
26
|
-
nothing: 0,
|
27
|
-
pair: 1,
|
28
|
-
two_pairs: 2,
|
29
|
-
three_of_a_kind: 3,
|
30
|
-
straight: 4,
|
31
|
-
flush: 5,
|
32
|
-
full_house: 6,
|
33
|
-
four_of_a_kind: 7,
|
34
|
-
straight_flush: 8
|
35
|
-
}.freeze, T::Hash[Symbol, Integer]
|
36
|
-
)
|
37
|
-
|
38
|
-
sig { returns(Integer) }
|
39
|
-
def rank # rubocop:disable Metrics
|
40
|
-
return T.must(HAND_RANKS[:straight_flush]) if straight_flush?
|
41
|
-
return T.must(HAND_RANKS[:four_of_a_kind]) if four_of_a_kind?
|
42
|
-
return T.must(HAND_RANKS[:full_house]) if full_house?
|
43
|
-
return T.must(HAND_RANKS[:flush]) if flush?
|
44
|
-
return T.must(HAND_RANKS[:straight]) if straight?
|
45
|
-
return T.must(HAND_RANKS[:three_of_a_kind]) if three_of_a_kind?
|
46
|
-
return T.must(HAND_RANKS[:two_pairs]) if two_pairs?
|
47
|
-
return T.must(HAND_RANKS[:pair]) if pair?
|
48
|
-
|
49
|
-
T.must(HAND_RANKS[:nothing])
|
50
|
-
end
|
51
|
-
|
52
|
-
sig { returns(T::Boolean) }
|
53
|
-
def pair?
|
54
|
-
validate_hand_size
|
55
|
-
|
56
|
-
counts = cards.map(&:rank).flatten.tally
|
57
|
-
counts.values.count(2) == 1
|
58
|
-
end
|
59
|
-
|
60
|
-
sig { returns(T::Boolean) }
|
61
|
-
def two_pairs?
|
62
|
-
validate_hand_size
|
63
|
-
|
64
|
-
counts = cards.map(&:rank).flatten.tally
|
65
|
-
counts.values.count(2) == 2
|
66
|
-
end
|
67
|
-
|
68
|
-
sig { returns(T::Boolean) }
|
69
|
-
def three_of_a_kind?
|
70
|
-
validate_hand_size
|
71
|
-
|
72
|
-
counts = cards.map(&:rank).flatten.tally
|
73
|
-
counts.values.count(3) == 1
|
74
|
-
end
|
75
|
-
|
76
|
-
sig { returns(T::Boolean) }
|
77
|
-
def straight? # rubocop:disable Metrics/AbcSize
|
78
|
-
validate_hand_size
|
79
|
-
|
80
|
-
ranks = cards.map(&:rank).sort
|
81
|
-
return true if ranks.each_cons(2).all? { |a, b| b == T.must(a) + 1 }
|
82
|
-
|
83
|
-
ranks = cards.map { _1.rank(low_ace: true) }
|
84
|
-
ranks.each_cons(2).all? { |a, b| b == T.must(a) + 1 }
|
85
|
-
end
|
86
|
-
|
87
|
-
sig { returns(T::Boolean) }
|
88
|
-
def flush?
|
89
|
-
validate_hand_size
|
90
|
-
|
91
|
-
suit = T.must(cards.first).suit
|
92
|
-
cards.all? { _1.suit == suit }
|
93
|
-
end
|
94
|
-
|
95
|
-
sig { returns(T::Boolean) }
|
96
|
-
def full_house?
|
97
|
-
validate_hand_size
|
98
|
-
|
99
|
-
three_of_a_kind? && pair?
|
100
|
-
end
|
101
|
-
|
102
|
-
sig { returns(T::Boolean) }
|
103
|
-
def four_of_a_kind?
|
104
|
-
validate_hand_size
|
105
|
-
|
106
|
-
counts = cards.map(&:rank).flatten.tally
|
107
|
-
counts.values.count(4) == 1
|
108
|
-
end
|
109
|
-
|
110
|
-
sig { returns(T::Boolean) }
|
111
|
-
def straight_flush?
|
112
|
-
validate_hand_size
|
113
|
-
|
114
|
-
straight? && flush?
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
sig { void }
|
120
|
-
def validate_hand_size
|
121
|
-
raise StandardError unless size == 5
|
122
|
-
end
|
123
|
-
end
|
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
|
@@ -76,6 +76,16 @@ files:
|
|
76
76
|
- lib/deck_of_cards_handler/packet/packet.rb
|
77
77
|
- lib/deck_of_cards_handler/packet/shuffles.rb
|
78
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
|
79
89
|
- lib/deck_of_cards_handler/version.rb
|
80
90
|
- sig/deck_of_cards.rbs
|
81
91
|
- sorbet/config
|