rora 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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