rora 0.0.5

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.
@@ -0,0 +1,95 @@
1
+ #
2
+ # The logical table area where community cards are dealt.
3
+ #
4
+ # A board can be initialized with flop, turn and river cards.
5
+ #
6
+ # # A board with the flop set
7
+ # board_one = Board.new "AS,KS,QS"
8
+ #
9
+ # # A board with the flop and turn set
10
+ # board_two = Board.new "AS,KS,QS,JS"
11
+ #
12
+ # # A board with the flop, turn and river set.
13
+ # board_three = Board.new "AS,KS,QS,JS,TS"
14
+ #
15
+ # # An empty baord can be created as well.
16
+ # board_four = Board.new
17
+ #
18
+ # Boards may also be populated programmatically using the flop, turn and
19
+ # river methods respectively:
20
+ #
21
+ # board = Board.new
22
+ # board.flop = "AS,KS,QS"
23
+ # board.turn = "JS"
24
+ # board.river = "TS"
25
+ #
26
+ class Board
27
+ attr_reader :flop, :turn, :river
28
+
29
+ def initialize cards=nil
30
+ return if cards.nil?
31
+
32
+ cds = cards.kind_of?(Array) ? cards : Card.to_cards(cards)
33
+ raise ArgumentError, "3 to 5 cards are required to create a board, #{cds.size} cards provided" if cds.size < 3 || cds.size > 5
34
+ raise ArgumentError, "The board contains duplicate cards" if cds.uniq.length != cds.length
35
+
36
+ @flop = cds[0..2]
37
+ @turn = cds[3] if cds.size >= 4
38
+ @river = cds[4] if cds.size == 5
39
+ end
40
+
41
+ def flop
42
+ @flop.dup if !@flop.nil?
43
+ end
44
+
45
+ def flop= cards
46
+ raise ArgumentError, "Cannot deal a flop with empty array of cards" if cards.nil? || cards.empty?
47
+
48
+ cds = cards.kind_of?(Array) ? cards : Card.to_cards(cards)
49
+ raise ArgumentError, "3 cards are required on the flop, #{cds.size} cards provided" if cds.size !=3
50
+ raise ArgumentError, "The flop contains duplicate cards" if cds.uniq.length != cds.length
51
+ @flop = cds[0..2]
52
+ end
53
+
54
+ def turn= card
55
+ cd = card.kind_of?(Card) ? card : Card.new(card)
56
+ raise RuntimeError, "The flop must be dealt before the turn card is dealt" if @flop.nil?
57
+ raise ArgumentError, "The board already contains the #{cd.name}" if cards.include? cd
58
+ @turn = cd
59
+ end
60
+
61
+ def river= card
62
+ cd = card.kind_of?(Card) ? card : Card.new(card)
63
+ raise RuntimeError, "The turn card must be dealt before the river card is dealt" if @turn.nil?
64
+ raise ArgumentError, "The board already contains the #{cd.name}" if cards.include? cd
65
+ @river = cd
66
+ end
67
+
68
+ def cards
69
+ cds = Array.new
70
+ cds += @flop if !@flop.nil?
71
+ cds.push(@turn) if !@turn.nil?
72
+ cds.push(@river) if !@river.nil?
73
+ cds
74
+ end
75
+
76
+ # Determines if the board contains the given card.
77
+ def contains? argument
78
+ if argument.kind_of? Card
79
+ return cards.include?(argument)
80
+ end
81
+ cards.include? Card.new(argument)
82
+ end
83
+
84
+ # Determines if the board contains any of the given cards.
85
+ def contains_any? argument
86
+ if argument.kind_of? Array
87
+ argument.each {|card| return true if cards.include? card}
88
+ end
89
+ if argument.kind_of? String
90
+ Card.to_cards(argument).each {|card| return true if cards.include? card}
91
+ end
92
+ false
93
+ end
94
+
95
+ end
@@ -0,0 +1,74 @@
1
+ #
2
+ # One of 52 of cards that are used to play card games.
3
+ #
4
+ # A card is comprised of one rank and one suit, with 13 ranks and 4 suits there
5
+ # are 52 unique playing cards. A card can be created in one of two ways:
6
+ #
7
+ # The first method uses the Rank and Suit enumerators
8
+ # card = Card.new(Rank::KING, Suit::HEART)
9
+ #
10
+ # The second method uses rank and suit values.
11
+ # card = Card.new("KH")
12
+ #
13
+ # Rank and suit values are case insensitive, so these work as well
14
+ # card = Card.new("Kh")
15
+ # card = Card.new("kH")
16
+ #
17
+ class Card
18
+ attr_reader :rank, :suit
19
+
20
+ def initialize(*args)
21
+ if(args.size == 2)
22
+ @rank = args[0]
23
+ @suit = args[1]
24
+ end
25
+ if(args.size == 1)
26
+ raise ArgumentError, "#{args} is an invalid card sequence" if args[0].length != 2
27
+ @rank = Rank.get(args[0][0])
28
+ @suit = Suit.get(args[0][1])
29
+ end
30
+ end
31
+
32
+ def id
33
+ @rank.id * @suit.id
34
+ end
35
+
36
+ def key
37
+ @rank.key + @suit.key
38
+ end
39
+
40
+ def value
41
+ @rank.value + @suit.value
42
+ end
43
+
44
+ def name
45
+ "#{@rank.value} of #{@suit.value}s"
46
+ end
47
+
48
+ def eql? card
49
+ self == card
50
+ end
51
+
52
+ def == card
53
+ self.id == card.id
54
+ end
55
+
56
+ def hash
57
+ return self.id
58
+ end
59
+
60
+ def self.to_cards string
61
+ cards = Array.new
62
+ if !string.include?(",") && !string.include?(" ")
63
+ string.scan(/../).each { |chars| cards << Card.new(chars) }
64
+ else
65
+ string.split(string.include?(",") ? "," : " ").each { |chars| cards << Card.new(chars) }
66
+ end
67
+ cards
68
+ end
69
+
70
+ def to_s
71
+ "Card: name='#{name}' value='#{@rank.key}#{@suit.key}' id=#{id}"
72
+ end
73
+
74
+ end
@@ -0,0 +1,109 @@
1
+ #
2
+ # A complete set of 52 playing cards.
3
+ #
4
+ # A deck of cards may be used for playing a great variety of card games, with
5
+ # varying elements of skill and chance, some of which are played for money.
6
+ #
7
+ class Deck
8
+ attr_reader :cards
9
+
10
+ def initialize
11
+ @cards = Array.new
12
+ Suit.values.each do |suit|
13
+ Rank.values.each do |rank|
14
+ @cards << Card.new(rank, suit)
15
+ end
16
+ end
17
+ end
18
+
19
+ # Shuffles all cards in the deck.
20
+ def shuffle
21
+ @cards.shuffle!
22
+ self
23
+ end
24
+
25
+ # Returns the number of cards in the deck.
26
+ def size
27
+ @cards.size
28
+ end
29
+
30
+ # Deals a single card from the deck.
31
+ def deal
32
+ @cards.delete_at 0
33
+ end
34
+
35
+ # Determines if the deck is empty.
36
+ def empty?
37
+ @cards.empty?
38
+ end
39
+
40
+ # Returns a shallow copy of the cards in the deck.
41
+ def cards
42
+ @cards.dup
43
+ end
44
+
45
+ # Returns the total number of cards with the given rank in the deck.
46
+ def count_cards_with_rank rank
47
+ count = 0;
48
+ @cards.each do |card|
49
+ count +=1 if card.rank.eql?(rank)
50
+ end
51
+ count
52
+ end
53
+
54
+ # Returns the total number of cards with the given suit in the deck.
55
+ def count_cards_with_suit suit
56
+ count = 0;
57
+ @cards.each do |card|
58
+ count +=1 if card.suit.eql?(suit)
59
+ end
60
+ count
61
+ end
62
+
63
+ # Removes cards from the deck.
64
+ #
65
+ # This method can remove a Card, an Enumerable (an Array or Hash
66
+ # of Cards) or a StartingHand from the deck.
67
+ def remove argument
68
+ if argument.kind_of? Card
69
+ @cards.delete argument
70
+ end
71
+ if argument.kind_of? Enumerable
72
+ argument.each { |card| @cards.delete card }
73
+ end
74
+ if argument.kind_of? StartingHand
75
+ argument.cards.each { |card| @cards.delete card }
76
+ end
77
+ if argument.kind_of? Board
78
+ argument.cards.each { |card| @cards.delete card }
79
+ end
80
+ if argument.kind_of? String
81
+ Card.to_cards(argument).each { |card| @cards.delete card }
82
+ end
83
+ self
84
+ end
85
+
86
+ # Determines if the deck contains the given card.
87
+ def contains? argument
88
+ if argument.kind_of? Card
89
+ return @cards.include?(argument)
90
+ end
91
+ @cards.include? Card.new(argument)
92
+ end
93
+
94
+ # Determines if the deck contains any of the given cards.
95
+ def contains_any? argument
96
+ if argument.kind_of? Array
97
+ argument.each {|card| return true if @cards.include? card}
98
+ end
99
+ if argument.kind_of? String
100
+ Card.to_cards(argument).each {|card| return true if @cards.include? card}
101
+ end
102
+ false
103
+ end
104
+
105
+ def combination number
106
+ cards.combination(number).to_a
107
+ end
108
+
109
+ end
@@ -0,0 +1,8 @@
1
+ class Game
2
+ attr_reader :table, :players, :dealer
3
+
4
+ def initialize
5
+
6
+ end
7
+
8
+ end
@@ -0,0 +1,126 @@
1
+ #
2
+ # A subset of cards held at one time by a player during a game.
3
+ #
4
+ # A hand consists of exactly five cards, and can be created in one of two ways.
5
+ #
6
+ # A hand can be created with an array of cards.
7
+ # hand = Hand.new([c1,c2,c3,c4,c5])
8
+ #
9
+ # A hand can be created with string representation of each card.
10
+ # hand = Hand.new("ACKCJS9H4H")
11
+ #
12
+ class Hand
13
+ attr_reader :cards, :hand_repository
14
+
15
+ def initialize cards
16
+ @hand_repository = HandRepository.instance
17
+ @cards = cards.kind_of?(Array) ? cards : Card.to_cards(cards)
18
+
19
+ raise ArgumentError, "Exactly 5 cards are required to create a hand, #{cards.size} provided" if @cards.size != 5
20
+ raise ArgumentError, "The hand contains duplicate cards" if @cards.uniq.length != @cards.length
21
+ end
22
+
23
+ # Returns the hand id.
24
+ def id
25
+ i = 1
26
+ @cards.each { |card| i = i * card.id }
27
+ i
28
+ end
29
+
30
+ # Returns the hand hash key.
31
+ def hash_key
32
+ j = 1
33
+ @cards.each { |card| j = j * card.rank.id }
34
+ flush? ? (j * 67) : j
35
+ end
36
+
37
+ # Returns the hand value.
38
+ def value
39
+ @cards.map { |card| "#{card.key}" }.join(",")
40
+ end
41
+
42
+ # Returns the hand strength, from 1 (strongest) to 7462 (weakest).
43
+ def score
44
+ resolve_hand_attribute(0)
45
+ end
46
+
47
+ # Returns the probability of this hand being dealt.
48
+ def probability
49
+ resolve_hand_attribute(3)
50
+ end
51
+
52
+ # Returns the hand name.
53
+ def name
54
+ resolve_hand_attribute(2)
55
+ end
56
+
57
+ # Returns the hand type.
58
+ def type
59
+ HandType.get resolve_hand_attribute(1)
60
+ end
61
+
62
+ # Returns all cards contained in the hand.
63
+ def cards
64
+ @cards.dup
65
+ end
66
+
67
+ # Determines if this hand is a flush.
68
+ def flush?
69
+ for i in 0..@cards.size - 2 do
70
+ return false if @cards[i].suit != @cards[i+1].suit
71
+ end
72
+ true
73
+ end
74
+
75
+ # Determines if this hand is a straight flush.
76
+ def straight_flush?
77
+ type == HandType::STRAIGHT_FLUSH
78
+ end
79
+
80
+ # Determines if this hand is four of a kind.
81
+ def four_of_a_kind?
82
+ type == HandType::FOUR_OF_A_KIND
83
+ end
84
+
85
+ # Determines if this hand is a full house.
86
+ def full_house?
87
+ type == HandType::FULL_HOUSE
88
+ end
89
+
90
+ # Determines if this hand is a straight.
91
+ def straight?
92
+ type == HandType::STRAIGHT
93
+ end
94
+
95
+ # Determines if this hand is a three of a kind.
96
+ def three_of_a_kind?
97
+ type == HandType::THREE_OF_A_KIND
98
+ end
99
+
100
+ # Determines if this hand is two pair.
101
+ def two_pair?
102
+ type == HandType::TWO_PAIR
103
+ end
104
+
105
+ # Determines if this hand has one pair.
106
+ def one_pair?
107
+ type == HandType::ONE_PAIR
108
+ end
109
+
110
+ # Determines if this hand is a high card.
111
+ def high_card?
112
+ type == HandType::HIGH_CARD
113
+ end
114
+
115
+ # Determines if this hand contains the given cards.
116
+ def contains cards
117
+ @cards.contains cards
118
+ end
119
+
120
+ private
121
+
122
+ def resolve_hand_attribute value
123
+ @hand_repository.find(hash_key)[value]
124
+ end
125
+
126
+ end
@@ -0,0 +1,53 @@
1
+ #
2
+ # The nine different types of 5-card poker hand.
3
+ #
4
+ # A 5-card poker hand will be one of the following hand types:
5
+ #
6
+ # 1. High Card
7
+ # 2. One Pair
8
+ # 3. Two Pair
9
+ # 4. Three of a Kind
10
+ # 5. Straight
11
+ # 6. Flush
12
+ # 7. Full House
13
+ # 8. Four of a Kind
14
+ # 9. Straight Flush
15
+ #
16
+ class HandType
17
+ attr_reader :key, :value, :order
18
+
19
+ def initialize(key, value, order)
20
+ @key = key
21
+ @value = value
22
+ @order = order
23
+ end
24
+
25
+ def self.values
26
+ [HIGH_CARD, ONE_PAIR, TWO_PAIR, THREE_OF_A_KIND, STRAIGHT, FLUSH, FULL_HOUSE, FOUR_OF_A_KIND, STRAIGHT_FLUSH]
27
+ end
28
+
29
+ def self.get(key)
30
+ self.values.each do |type|
31
+ return type if type.key.casecmp(key) == 0
32
+ end
33
+ raise ArgumentError, "No hand type exists for key " + key
34
+ end
35
+
36
+ def to_s
37
+ "HandType: key='#{@key}', value='#{@value}'"
38
+ end
39
+
40
+ class << self
41
+ private :new
42
+ end
43
+
44
+ HIGH_CARD = new("HC", "High Card", 1)
45
+ ONE_PAIR = new("1P", "One Pair", 2)
46
+ TWO_PAIR = new("2P", "Two Pair", 3)
47
+ THREE_OF_A_KIND = new("3K", "Three of a Kind", 4)
48
+ STRAIGHT = new("ST", "Straight", 5)
49
+ FLUSH = new("FL", "Flush", 6)
50
+ FULL_HOUSE = new("FH", "Full House", 7)
51
+ FOUR_OF_A_KIND = new("4K", "Four of a Kind", 8)
52
+ STRAIGHT_FLUSH = new("SF", "Straight Flush", 9)
53
+ end
@@ -0,0 +1,8 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+
4
+ class Player
5
+ def initialize
6
+
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # The sum of money that players wager during a single hand or game.
3
+ #
4
+ class Pot
5
+
6
+ attr_reader :total
7
+
8
+ def initialize
9
+ @total = 0
10
+ end
11
+
12
+ def add bet
13
+ @total += bet
14
+ self
15
+ end
16
+
17
+ def to_s
18
+ "Pot: " + sprintf("$%.02f" , @total)
19
+ end
20
+
21
+ end
@@ -0,0 +1,52 @@
1
+ #
2
+ # The thirteen different hierarchical values in a deck of cards.
3
+ #
4
+ # A deck of cards has thirteen ranks: 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen,
5
+ # King, Ace.
6
+ #
7
+ # The ranks have a natural order, with the weakest value (0) assigned to the 2
8
+ # and the strongest value (12) assigned to the ace.
9
+ #
10
+ class Rank
11
+ attr_reader :id, :key, :value, :order
12
+
13
+ def initialize(id, key, value, order)
14
+ @id = id
15
+ @key = key
16
+ @value = value
17
+ @order = order
18
+ end
19
+
20
+ def self.values
21
+ [TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE]
22
+ end
23
+
24
+ def self.get(key)
25
+ self.values.each do |rank|
26
+ return rank if rank.key.casecmp(key) == 0
27
+ end
28
+ raise ArgumentError, "No rank exists for key " + key
29
+ end
30
+
31
+ def to_s
32
+ "Rank: id=#{@id}, key='#{@key}', value='#{@value}'"
33
+ end
34
+
35
+ class << self
36
+ private :new
37
+ end
38
+
39
+ TWO = new(2, "2", "Two", 0)
40
+ THREE = new(3, "3", "Three", 1)
41
+ FOUR = new(5, "4", "Four", 2)
42
+ FIVE = new(7, "5", "Five", 3)
43
+ SIX = new(11, "6", "Six", 4)
44
+ SEVEN = new(13, "7", "Seven", 5)
45
+ EIGHT = new(17, "8", "Eight", 6)
46
+ NINE = new(19, "9", "Nine", 7)
47
+ TEN = new(23, "T", "Ten", 8)
48
+ JACK = new(29, "J", "Jack", 9)
49
+ QUEEN = new(31, "Q", "Queen", 10)
50
+ KING = new(37, "K", "King", 11)
51
+ ACE = new(41, "A", "Ace", 12)
52
+ end
@@ -0,0 +1,102 @@
1
+ #
2
+ # Two cards, also known as hole cards or pocket cards, which belong solely to
3
+ # one player and remain hidden from the other players.
4
+ #
5
+ class StartingHand
6
+ attr_reader :id, :key, :cards
7
+
8
+ def initialize cards
9
+ @cards = Array.new, @id = 1, @key = 1
10
+
11
+ @cards = cards.kind_of?(Array) ? cards : Card.to_cards(cards)
12
+ raise ArgumentError, "Exactly 2 cards are required to create a starting hand, #{@cards.size} provided" if @cards.size != 2
13
+ raise ArgumentError, "The hand contains duplicate cards" if @cards.uniq.length != @cards.length
14
+
15
+ @cards.each {|card| @id *= card.id}
16
+ @cards.each {|card| @key *= card.rank.id}
17
+ @key = suited? ? @key * 67 : @key
18
+ end
19
+
20
+ # Returns all cards contained in the starting hand.
21
+ def cards
22
+ @cards.dup
23
+ end
24
+
25
+ # Returns the starting hand value.
26
+ def value
27
+ @cards.map { |card| "#{card.key}" }.join(",")
28
+ end
29
+
30
+ # Returns the shorthand notation for the starting hand.
31
+ #
32
+ # It is often desirable to have a short hand notation for starting hands,
33
+ # ignoring card suits and simply describing whether the starting hand is
34
+ # suited or not. The shorthand notation removes suit characters, and
35
+ # appends an 'o' (offsuit) or 's' (suited) to the card ranks.
36
+ #
37
+ # Starting hand consisting of the Ace of Clubs and Jack of Clubs:
38
+ # card.value == 'AC,JC'
39
+ # card.short_value == 'JCs'
40
+ #
41
+ # Starting hand consisting of the Ten of Hearts and Eight of Clubs:
42
+ # card.value == 'TH,8C',
43
+ # card.short_value == 'T8o'
44
+ def short_value
45
+ @cards[0].rank.key + @cards[1].rank.key + (suited? ? "s" : "o")
46
+ end
47
+
48
+ # Determines if the starting hand is a pocket pair.
49
+ def pocket_pair?
50
+ cards[0].rank == cards[1].rank
51
+ end
52
+
53
+ # Determines if the starting hand is suited.
54
+ def suited?
55
+ for i in 0..@cards.size - 2 do
56
+ return false if @cards[i].suit != @cards[i+1].suit
57
+ end
58
+ true
59
+ end
60
+
61
+ # Returns all possible starting hands.
62
+ #
63
+ # There are exaclty 1,326 (52c2) starting hands. This method returns
64
+ # a list containing every possible starting hand.
65
+ def self.all_starting_hands
66
+ StartingHandRepository.instance.all_starting_hands
67
+ end
68
+
69
+ # Returns all distinct starting hands.
70
+ #
71
+ # While there are 1,324 starting hands, many of these starting hands have
72
+ # the same value in poker. To elaborate on this a bit, consider the
73
+ # number of hands a player could have containing a Jack and a Seven:
74
+ #
75
+ # J♣ 7♦
76
+ # J♠ 7♦
77
+ # J♥ 7♦
78
+ # J♦ 7♦
79
+ # J♣ 7♠
80
+ # J♠ 7♠
81
+ # J♥ 7♠
82
+ # J♦ 7♠
83
+ # J♣ 4♥
84
+ # J♠ 4♥
85
+ #
86
+ # This list goes on a bit further further. You might be surprised to know
87
+ # that there are 16 starting hand combinations that contain exactly one
88
+ # Jack and one Seven. Out of this list of 16, only two card values have any
89
+ # relevance in a poker game - Jack-Seven suited and Jack-Seven unsuited.
90
+ #
91
+ # This exercise demonstrates that while there are 1,324 starting hands to
92
+ # contend with, the number of distinct starting hands is dramatically
93
+ # lower. This is because card suits don’t tend to affect the score of the
94
+ # hand.
95
+ #
96
+ # Once all 1,324 poker hands are collapsed into distinct values, we end up
97
+ # with just 169 starting hands!
98
+ def self.distinct_starting_hands
99
+ StartingHandRepository.instance.distinct_starting_hands
100
+ end
101
+
102
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # The four categories in a deck of cards.
3
+ #
4
+ # Each card bears one of four symbols showing which suit it belongs to. A deck
5
+ # of cards has four suits: hearts, clubs, spades, and diamonds.
6
+ #
7
+ class Suit
8
+ attr_reader :id, :key, :value
9
+
10
+ def initialize(id, key, value)
11
+ @id = id
12
+ @key = key
13
+ @value = value
14
+ end
15
+
16
+ def self.values
17
+ [HEART, SPADE, CLUB, DIAMOND]
18
+ end
19
+
20
+ def self.get(key)
21
+ self.values.each do |suit|
22
+ return suit if suit.key.casecmp(key) == 0
23
+ end
24
+ raise ArgumentError, "No suit exists for key " + key
25
+ end
26
+
27
+ def to_s
28
+ "Suit: id=#{@id}, key='#{@key}', value='#{@value}'"
29
+ end
30
+
31
+ def == suit
32
+ self.id == suit.id
33
+ end
34
+
35
+ class << self
36
+ private :new
37
+ end
38
+
39
+ HEART = new(43, "H", "Heart")
40
+ SPADE = new(47, "S", "Spade")
41
+ CLUB = new(53, "C", "Club")
42
+ DIAMOND = new(59, "D", "Diamond")
43
+ end