texas_holdem 0.1.0
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.
- data/.autotest +5 -0
- data/.document +5 -0
- data/.gitignore +21 -0
- data/Gemfile +0 -0
- data/LICENSE +20 -0
- data/README +0 -0
- data/README.rdoc +17 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/lib/betting_round.rb +83 -0
- data/lib/deck.rb +35 -0
- data/lib/game.rb +20 -0
- data/lib/hand.rb +78 -0
- data/lib/player.rb +42 -0
- data/lib/players_hand.rb +132 -0
- data/lib/texas_holdem.rb +8 -0
- data/test/helper.rb +57 -0
- data/test/test_betting_round.rb +140 -0
- data/test/test_deck.rb +37 -0
- data/test/test_game.rb +30 -0
- data/test/test_hand.rb +158 -0
- data/test/test_player.rb +32 -0
- data/test/test_players_hand.rb +117 -0
- data/test/test_texas_holdem.rb +9 -0
- metadata +99 -0
data/.autotest
ADDED
data/.document
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Keith McDonnell
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
File without changes
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= texas_holdem
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Keith McDonnell. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "texas_holdem"
|
8
|
+
gem.summary = %Q{Ruby classes modelling a game Texas Holdem poker}
|
9
|
+
gem.description = %Q{Ruby classes modelling a game Texas Holdem poker}
|
10
|
+
gem.email = "keith@dancingtext.com"
|
11
|
+
gem.homepage = "http://github.com/kmcd/texas_holdem"
|
12
|
+
gem.authors = ["Keith McDonnell"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/test_*.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
task :test => :check_dependencies
|
28
|
+
|
29
|
+
task :default => :test
|
30
|
+
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
Rake::RDocTask.new do |rdoc|
|
33
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
34
|
+
|
35
|
+
rdoc.rdoc_dir = 'rdoc'
|
36
|
+
rdoc.title = "texas_holdem #{version}"
|
37
|
+
rdoc.rdoc_files.include('README*')
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Create class & unit test'
|
42
|
+
task :class, :name do |task,args|
|
43
|
+
class_name = args.name.capitalize
|
44
|
+
file_name = args.name.downcase
|
45
|
+
|
46
|
+
write_to "lib/#{file_name}.rb", %Q{class TexasHoldem::#{class_name}
|
47
|
+
end}
|
48
|
+
|
49
|
+
write_to "test/test_#{file_name}.rb", %Q{require 'helper'
|
50
|
+
class #{class_name}Test < Test::Unit::TestCase
|
51
|
+
def setup
|
52
|
+
end
|
53
|
+
|
54
|
+
test "should " do
|
55
|
+
flunk
|
56
|
+
end
|
57
|
+
end}
|
58
|
+
end
|
59
|
+
|
60
|
+
def write_to(local_filename,text)
|
61
|
+
File.open(local_filename, 'w') {|f| f.write(text) }
|
62
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class BettingRound
|
4
|
+
attr_accessor :max_wait_time_for_bet
|
5
|
+
|
6
|
+
def initialize(hand)
|
7
|
+
@hand = hand
|
8
|
+
@betting_circle = players.enum_for
|
9
|
+
players.each {|player| player.add_observer self }
|
10
|
+
end
|
11
|
+
|
12
|
+
def next_player
|
13
|
+
@current_player = @betting_circle.next
|
14
|
+
rescue StopIteration
|
15
|
+
@betting_circle.rewind and retry
|
16
|
+
end
|
17
|
+
|
18
|
+
def max_wait_time_for_bet(seconds)
|
19
|
+
Timeout::timeout(seconds) { yield }
|
20
|
+
rescue Timeout::Error
|
21
|
+
fold @current_player and next_player
|
22
|
+
end
|
23
|
+
|
24
|
+
def current_player
|
25
|
+
@current_player || next_player
|
26
|
+
end
|
27
|
+
|
28
|
+
def players
|
29
|
+
@players ||= @hand.players
|
30
|
+
end
|
31
|
+
|
32
|
+
def bets
|
33
|
+
@bets ||= Hash.new(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
def minimum_bet
|
37
|
+
bets.values.empty? ? @hand.minimum_bet : amount_to_match_pot
|
38
|
+
end
|
39
|
+
|
40
|
+
def minimum_raise
|
41
|
+
minimum_bet + @hand.minimum_bet
|
42
|
+
end
|
43
|
+
|
44
|
+
def amount_bet
|
45
|
+
bets.values.compact.reduce(:+) || 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def finished?
|
49
|
+
players.size == 1 || everyone_bet_equal_amount?
|
50
|
+
end
|
51
|
+
|
52
|
+
def update(args)
|
53
|
+
if args[:fold]
|
54
|
+
fold @current_player
|
55
|
+
else
|
56
|
+
bet, player = args[:bet][:amount], args[:bet][:player]
|
57
|
+
return unless valid? bet, player
|
58
|
+
bets[player] += bet
|
59
|
+
@hand.pot += bet
|
60
|
+
end
|
61
|
+
next_player
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def valid?(bet,player)
|
67
|
+
return unless player.eql? current_player
|
68
|
+
bet == minimum_bet || bet >= minimum_raise || bet == 0
|
69
|
+
end
|
70
|
+
|
71
|
+
def everyone_bet_equal_amount?
|
72
|
+
bets.values.uniq.size == 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def amount_to_match_pot
|
76
|
+
bets.values.max - bets[current_player]
|
77
|
+
end
|
78
|
+
|
79
|
+
def fold(player)
|
80
|
+
players.delete player
|
81
|
+
@hand.fold player
|
82
|
+
end
|
83
|
+
end
|
data/lib/deck.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class TexasHoldem::Deck
|
2
|
+
FACES, SUITS = "AKQJT98765432", "cdhs"
|
3
|
+
|
4
|
+
attr_reader :cards
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
build
|
8
|
+
shuffle
|
9
|
+
end
|
10
|
+
|
11
|
+
def next_card
|
12
|
+
@cards.pop
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build
|
18
|
+
@cards = []
|
19
|
+
|
20
|
+
FACES.each_byte do |f|
|
21
|
+
SUITS.each_byte {|s| @cards.push(f.chr + s.chr) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def shuffle
|
26
|
+
3.times do
|
27
|
+
shuf = []
|
28
|
+
@cards.each do |c|
|
29
|
+
loc = rand(shuf.size + 1)
|
30
|
+
shuf.insert(loc, c)
|
31
|
+
end
|
32
|
+
@cards = shuf.reverse
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/game.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class TexasHoldem::Game
|
2
|
+
SMALL_BLIND_PERCENTAGE = 0.0125
|
3
|
+
attr_reader :players, :entrance_fee
|
4
|
+
|
5
|
+
def initialize(players,entrance_fee)
|
6
|
+
@players, @entrance_fee = players, entrance_fee
|
7
|
+
end
|
8
|
+
|
9
|
+
def finished?
|
10
|
+
players.size == 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def winner
|
14
|
+
players.first if finished?
|
15
|
+
end
|
16
|
+
|
17
|
+
def small_blind
|
18
|
+
@entrance_fee * SMALL_BLIND_PERCENTAGE
|
19
|
+
end
|
20
|
+
end
|
data/lib/hand.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'enumerated_attribute'
|
2
|
+
|
3
|
+
class TexasHoldem::Hand
|
4
|
+
enum_attr :round, %w( ^pocket flop turn river showdown )
|
5
|
+
attr_reader :players, :community_cards
|
6
|
+
attr_accessor :pot
|
7
|
+
|
8
|
+
def initialize(players, small_blind_amount=1)
|
9
|
+
@players = players
|
10
|
+
@deck = TexasHoldem::Deck.new
|
11
|
+
@community_cards = []
|
12
|
+
@small_blind_amount = small_blind_amount
|
13
|
+
@pot = 0 # Should this be an Observer?
|
14
|
+
end
|
15
|
+
|
16
|
+
def dealer
|
17
|
+
@players.first
|
18
|
+
end
|
19
|
+
|
20
|
+
def minimum_bet
|
21
|
+
@small_blind_amount * 2
|
22
|
+
end
|
23
|
+
|
24
|
+
def small_blind
|
25
|
+
return dealer if players_remaining? 2
|
26
|
+
@players[2]
|
27
|
+
end
|
28
|
+
|
29
|
+
def big_blind
|
30
|
+
return @players.last if players_remaining? 2
|
31
|
+
@players[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def winner
|
35
|
+
if finished?
|
36
|
+
@players.first.take_winnings @pot
|
37
|
+
@players.first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def finished?
|
42
|
+
players_remaining? 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def fold(player)
|
46
|
+
@players.delete player
|
47
|
+
end
|
48
|
+
|
49
|
+
def deal
|
50
|
+
case round
|
51
|
+
when :pocket : deal_pocket_cards && deduct_blinds # Move to BettingRound?
|
52
|
+
when :flop : deal_community_cards 3
|
53
|
+
when :turn : deal_community_cards 1
|
54
|
+
when :river : deal_community_cards 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def players_remaining?(number)
|
61
|
+
@players.size == number
|
62
|
+
end
|
63
|
+
|
64
|
+
def deal_pocket_cards
|
65
|
+
@players.each do |player|
|
66
|
+
2.times { player.cards << @deck.next_card }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def deal_community_cards(number)
|
71
|
+
number.times { @community_cards << @deck.next_card }
|
72
|
+
end
|
73
|
+
|
74
|
+
def deduct_blinds
|
75
|
+
@pot += small_blind.bet(@small_blind_amount)
|
76
|
+
@pot += big_blind.bet(@small_blind_amount * 2)
|
77
|
+
end
|
78
|
+
end
|
data/lib/player.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class NotEnoughCashError < StandardError
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'observer'
|
5
|
+
|
6
|
+
class TexasHoldem::Player
|
7
|
+
attr_reader :name, :cards, :cash
|
8
|
+
include Observable
|
9
|
+
|
10
|
+
def initialize(name,cash)
|
11
|
+
@name, @cash = name, cash
|
12
|
+
@cards = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def bet(amount)
|
16
|
+
# TODO: add remaining cash instead of raising an error
|
17
|
+
raise NotEnoughCashError unless enough_cash_for?(amount)
|
18
|
+
@cash -= amount
|
19
|
+
changed true
|
20
|
+
notify_observers :bet => { :player => self, :amount => amount }
|
21
|
+
amount
|
22
|
+
end
|
23
|
+
|
24
|
+
def check
|
25
|
+
bet 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def fold
|
29
|
+
changed true
|
30
|
+
notify_observers :fold => true
|
31
|
+
end
|
32
|
+
|
33
|
+
def take_winnings(amount)
|
34
|
+
@cash += amount
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def enough_cash_for?(amount)
|
40
|
+
@cash > amount
|
41
|
+
end
|
42
|
+
end
|
data/lib/players_hand.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# TODO: rename to PlayerHand or Player::Hand
|
2
|
+
class TexasHoldem::PlayersHand
|
3
|
+
attr_reader :cards
|
4
|
+
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
# TODO: dry up [scdh] references
|
8
|
+
MATCHES = {
|
9
|
+
'one pair' => /(\d{1,2})[scdh] (?:\s\1[scdh]){1}/x,
|
10
|
+
'two pair' => /(\d{1,2})[scdh] \1[scdh].*(\d{1,2})[scdh] \2[scdh]/,
|
11
|
+
'three of a kind' => /(\d{1,2})[scdh] (?:\s\1[scdh]){2}/x,
|
12
|
+
'four of a kind' => /(\d{1,2})[scdh] (?:\s\1[scdh]){3}/x,
|
13
|
+
}
|
14
|
+
|
15
|
+
def initialize(cards)
|
16
|
+
@cards = cards
|
17
|
+
@cards.gsub!(/J/, '11')
|
18
|
+
@cards.gsub!(/Q/, '12')
|
19
|
+
@cards.gsub!(/K/, '13')
|
20
|
+
@cards.gsub!(/A/, '14')
|
21
|
+
@cards = @cards.split.sort_by {|c| c.gsub(/\D/,'').to_i }.join ' '
|
22
|
+
end
|
23
|
+
|
24
|
+
def <=>(players_hand)
|
25
|
+
if score == players_hand.score
|
26
|
+
remaining_cards <=> players_hand.remaining_cards
|
27
|
+
else
|
28
|
+
score <=> players_hand.score
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def name
|
33
|
+
# TODO: refactor to polymorphic type?
|
34
|
+
if four_of_a_kind?
|
35
|
+
'four of a kind'
|
36
|
+
elsif full_house?
|
37
|
+
'full house'
|
38
|
+
elsif flush?
|
39
|
+
'flush'
|
40
|
+
elsif straight?
|
41
|
+
'straight'
|
42
|
+
elsif three_of_a_kind?
|
43
|
+
'three of a kind'
|
44
|
+
elsif two_pair?
|
45
|
+
'two pair'
|
46
|
+
elsif one_pair?
|
47
|
+
'one pair'
|
48
|
+
else
|
49
|
+
'high card'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def one_pair?
|
54
|
+
@cards.match MATCHES['one pair']
|
55
|
+
end
|
56
|
+
|
57
|
+
def two_pair?
|
58
|
+
@cards.match MATCHES['two pair']
|
59
|
+
end
|
60
|
+
|
61
|
+
def straight?
|
62
|
+
card_sequence = face_values.split.map {|d| d.to_i }
|
63
|
+
(card_sequence.first..card_sequence.last).to_a == card_sequence
|
64
|
+
end
|
65
|
+
|
66
|
+
def flush?
|
67
|
+
@cards.match /\d{1,2}([csdh]) (?:\s\d{1,2}\1){4} /x
|
68
|
+
end
|
69
|
+
|
70
|
+
def three_of_a_kind?
|
71
|
+
@cards.match MATCHES['three of a kind']
|
72
|
+
end
|
73
|
+
|
74
|
+
def full_house?
|
75
|
+
# TODO: change this to [csdh]
|
76
|
+
@cards.gsub(/\D/,'').match /^(?:(\d) \1{2} (\d) \2|(\d) \3 (\d) \4{2})/x
|
77
|
+
end
|
78
|
+
|
79
|
+
def four_of_a_kind?
|
80
|
+
@cards.match MATCHES['four of a kind']
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def score
|
86
|
+
base_score + relative_score
|
87
|
+
end
|
88
|
+
|
89
|
+
def base_score
|
90
|
+
case name
|
91
|
+
when /one pair/ : 1
|
92
|
+
when /two pair/ : 2
|
93
|
+
when /three/ : 3
|
94
|
+
when /straight/ : 4
|
95
|
+
when /flush/ : 5
|
96
|
+
when /full house/ : 6
|
97
|
+
when /four/ : 7
|
98
|
+
else
|
99
|
+
0
|
100
|
+
end * 1000
|
101
|
+
end
|
102
|
+
|
103
|
+
def relative_score
|
104
|
+
case name
|
105
|
+
when /one pair/ : @cards[MATCHES['one pair'],1].to_i * 2
|
106
|
+
when /two pair/ : @cards[MATCHES['two pair'],2].to_i * 2
|
107
|
+
when /three/ : @cards[MATCHES['three of a kind'],1].to_i * 3
|
108
|
+
when /straight/ : face_values.split.last.to_i
|
109
|
+
when /flush/ : face_values.split.sort.last.to_i
|
110
|
+
when /full house/ : @cards[MATCHES['three of a kind'],1].to_i
|
111
|
+
when /four/ : @cards[MATCHES['four of a kind'],1].to_i
|
112
|
+
else
|
113
|
+
0
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def remaining_cards
|
118
|
+
case name
|
119
|
+
when /high card/ : @cards
|
120
|
+
when /one pair/ : @cards.gsub(MATCHES['one pair'], '')
|
121
|
+
when /two pair/ : @cards.gsub(MATCHES['two pair'], '')
|
122
|
+
else
|
123
|
+
''
|
124
|
+
end.gsub /[a-z]/, ''
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def face_values
|
130
|
+
@cards.gsub(/[scdh]/,'')
|
131
|
+
end
|
132
|
+
end
|
data/lib/texas_holdem.rb
ADDED
data/test/helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_support/testing/declarative'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
# TODO: create a rake task to load irb environment
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
9
|
+
require 'texas_holdem'
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
extend ActiveSupport::Testing::Declarative
|
13
|
+
|
14
|
+
def assert_cards(number,players)
|
15
|
+
assert players.all? {|player| player.cards.size == number }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class String
|
20
|
+
include Test::Unit::Assertions
|
21
|
+
|
22
|
+
def hand_name(name)
|
23
|
+
assert_equal name, TexasHoldem::PlayersHand.new(self).name
|
24
|
+
end
|
25
|
+
|
26
|
+
def beats(loser)
|
27
|
+
winner, loser = TexasHoldem::PlayersHand.new(self), TexasHoldem::PlayersHand.new(loser)
|
28
|
+
assert( winner > loser )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class TexasHoldem::Hand
|
33
|
+
def self.factory(small_blind=1.25)
|
34
|
+
players = %w( Amy Bill Carl ).map {|name| TexasHoldem::Player.factory name }
|
35
|
+
new players, small_blind
|
36
|
+
end
|
37
|
+
|
38
|
+
def advance_to_round(number)
|
39
|
+
(number - 1).times do
|
40
|
+
deal
|
41
|
+
round_next
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class TexasHoldem::Player
|
47
|
+
def self.factory(name, cash=100)
|
48
|
+
new name, cash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module HandTestHelper
|
53
|
+
def setup
|
54
|
+
@hand = TexasHoldem::Hand.factory
|
55
|
+
@amy, @bill, @carl = *@hand.players
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class BettingRoundTest < Test::Unit::TestCase
|
4
|
+
include HandTestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
@betting_round = BettingRound.new @hand
|
9
|
+
@hand.deal
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_current_player(player)
|
13
|
+
assert_equal player, @betting_round.current_player
|
14
|
+
end
|
15
|
+
|
16
|
+
test "player to the left of big blind is first to bet" do
|
17
|
+
assert_current_player @amy
|
18
|
+
end
|
19
|
+
|
20
|
+
test "play continues clockwise around the table" do
|
21
|
+
assert_current_player @amy
|
22
|
+
@betting_round.next_player
|
23
|
+
assert_current_player @bill
|
24
|
+
@betting_round.next_player
|
25
|
+
assert_current_player @carl
|
26
|
+
@betting_round.next_player
|
27
|
+
assert_current_player @amy
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should track amount bet" do
|
31
|
+
@amy.bet 20
|
32
|
+
assert_equal 20, @betting_round.amount_bet
|
33
|
+
@bill.bet 20
|
34
|
+
assert_equal 40, @betting_round.amount_bet
|
35
|
+
@carl.bet 20
|
36
|
+
assert_equal 60, @betting_round.amount_bet
|
37
|
+
end
|
38
|
+
|
39
|
+
test "should only accept bet from current player" do
|
40
|
+
assert_current_player @amy
|
41
|
+
@carl.bet 20
|
42
|
+
assert_equal 0, @betting_round.amount_bet
|
43
|
+
assert_current_player @amy
|
44
|
+
end
|
45
|
+
|
46
|
+
test "should finish if all players check" do
|
47
|
+
@betting_round.players.each {|player| player.check }
|
48
|
+
assert @betting_round.finished?
|
49
|
+
end
|
50
|
+
|
51
|
+
test "should finish if all players check or fold" do
|
52
|
+
@amy.check
|
53
|
+
@bill.check
|
54
|
+
@carl.fold
|
55
|
+
assert @betting_round.finished?
|
56
|
+
end
|
57
|
+
|
58
|
+
test "should remove player from hand if they fold" do
|
59
|
+
player = @betting_round.current_player
|
60
|
+
player.fold
|
61
|
+
assert_nil @hand.players.find {|p| p == player }
|
62
|
+
end
|
63
|
+
|
64
|
+
test "should finish when all but one fold" do
|
65
|
+
@amy.fold
|
66
|
+
@carl.fold
|
67
|
+
assert @betting_round.finished?
|
68
|
+
end
|
69
|
+
|
70
|
+
test "should have minimum raises equal to big blind" do
|
71
|
+
@amy.bet @hand.minimum_bet - 1
|
72
|
+
assert_equal 0, @betting_round.amount_bet
|
73
|
+
assert_current_player @amy
|
74
|
+
end
|
75
|
+
|
76
|
+
test "betting finishes when everyone puts in same amount" do
|
77
|
+
@betting_round.players.each {|player| player.bet 10 }
|
78
|
+
assert @betting_round.finished?
|
79
|
+
end
|
80
|
+
|
81
|
+
test "should calculate minimum bet for current player" do
|
82
|
+
@amy.bet 20
|
83
|
+
|
84
|
+
assert_current_player @bill
|
85
|
+
assert_equal @betting_round.minimum_bet, 20
|
86
|
+
@bill.bet 30
|
87
|
+
|
88
|
+
assert_current_player @carl
|
89
|
+
assert_equal @betting_round.minimum_bet, 30
|
90
|
+
@carl.bet 40
|
91
|
+
|
92
|
+
assert_current_player @amy
|
93
|
+
assert_equal @betting_round.minimum_bet, 20
|
94
|
+
@amy.bet 20
|
95
|
+
|
96
|
+
assert_current_player @bill
|
97
|
+
assert_equal @betting_round.minimum_bet, 10
|
98
|
+
@bill.bet 10
|
99
|
+
end
|
100
|
+
|
101
|
+
test "betting finishes when everyone matches amount previously put in" do
|
102
|
+
@amy.bet 20
|
103
|
+
@bill.bet 30
|
104
|
+
@carl.bet 40
|
105
|
+
assert !@betting_round.finished?
|
106
|
+
@amy.bet 20
|
107
|
+
assert !@betting_round.finished?
|
108
|
+
@bill.bet 10
|
109
|
+
assert @betting_round.finished?
|
110
|
+
end
|
111
|
+
|
112
|
+
test "should enforce (big blind) minumim bet raise" do
|
113
|
+
@amy.bet 10
|
114
|
+
assert_equal 10, @betting_round.minimum_bet
|
115
|
+
assert_equal 12.5, @betting_round.minimum_raise
|
116
|
+
|
117
|
+
@bill.bet 12
|
118
|
+
assert_current_player @bill
|
119
|
+
|
120
|
+
@bill.bet 12.5
|
121
|
+
assert_current_player @carl
|
122
|
+
end
|
123
|
+
|
124
|
+
test "should add bets to the pot" do
|
125
|
+
@amy.bet 20
|
126
|
+
@bill.bet 20
|
127
|
+
@carl.bet 20
|
128
|
+
blinds = 3.75
|
129
|
+
assert_equal 60 + blinds, @hand.pot
|
130
|
+
end
|
131
|
+
|
132
|
+
test "should kick player out of the hand if they take too long to bet" do
|
133
|
+
@betting_round.max_wait_time_for_bet(0.09) do
|
134
|
+
assert_current_player @amy
|
135
|
+
sleep 0.1
|
136
|
+
assert !@betting_round.players.include?(@amy)
|
137
|
+
assert_current_player @bill
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/test/test_deck.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
SORTED_FULL_DECK = %w[ 2c 2d 2h 2s 3c 3d 3h 3s 4c 4d 4h 4s 5c 5d 5h 5s 6c 6d 6h 6s
|
4
|
+
7c 7d 7h 7s 8c 8d 8h 8s 9c 9d 9h 9s Ac Ad Ah As Jc Jd Jh Js Kc Kd Kh Ks Qc
|
5
|
+
Qd Qh Qs Tc Td Th Ts]
|
6
|
+
|
7
|
+
class NewDeckTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@deck = TexasHoldem::Deck.new
|
10
|
+
end
|
11
|
+
|
12
|
+
test "should have 52 cards initially" do
|
13
|
+
assert_equal 52, @deck.cards.size
|
14
|
+
end
|
15
|
+
|
16
|
+
test "should have 52 unique cards" do
|
17
|
+
assert_equal 52, @deck.cards.uniq.size
|
18
|
+
end
|
19
|
+
|
20
|
+
test "should have 2 - Ace of Clubs, Spades, Diamonds & Hearts" do
|
21
|
+
assert_equal SORTED_FULL_DECK, @deck.cards.sort
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class DeckDealTest < Test::Unit::TestCase
|
26
|
+
def setup
|
27
|
+
@deck = TexasHoldem::Deck.new
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should deal the next card" do
|
31
|
+
delt_card = @deck.next_card
|
32
|
+
|
33
|
+
assert_equal 51, @deck.cards.size
|
34
|
+
assert SORTED_FULL_DECK.include?(delt_card)
|
35
|
+
assert_equal SORTED_FULL_DECK - [delt_card], @deck.cards.sort
|
36
|
+
end
|
37
|
+
end
|
data/test/test_game.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class GameTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@game = TexasHoldem::Game.new [], 100
|
6
|
+
end
|
7
|
+
|
8
|
+
test "should not have a winner at game start" do
|
9
|
+
assert_nil @game.winner
|
10
|
+
end
|
11
|
+
|
12
|
+
test "should have players" do
|
13
|
+
assert_equal [], @game.players
|
14
|
+
end
|
15
|
+
|
16
|
+
test "should have small blind as 1.25% of entrance fee" do
|
17
|
+
assert_equal 1.25, @game.small_blind
|
18
|
+
end
|
19
|
+
|
20
|
+
test "should know when finshed" do
|
21
|
+
@game.stubs(:players).returns [TexasHoldem::Player.new('Foo', 100)]
|
22
|
+
assert_equal true, @game.finished?
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should have a winner when finished" do
|
26
|
+
winner = TexasHoldem::Player.new 'Foo', 100
|
27
|
+
@game.stubs(:players).returns [winner]
|
28
|
+
assert_equal winner, @game.winner
|
29
|
+
end
|
30
|
+
end
|
data/test/test_hand.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HandTest < Test::Unit::TestCase
|
4
|
+
include HandTestHelper
|
5
|
+
|
6
|
+
test "should not have a winner initially" do
|
7
|
+
assert_nil @hand.winner
|
8
|
+
end
|
9
|
+
|
10
|
+
test "should have a winner if everyone else folds" do
|
11
|
+
@hand.deal
|
12
|
+
@hand.fold @bill
|
13
|
+
@hand.fold @carl
|
14
|
+
|
15
|
+
assert_equal @hand.winner, @amy
|
16
|
+
end
|
17
|
+
|
18
|
+
test "automatically deduct blinds after dealing pocket cards" do
|
19
|
+
assert_equal 100, @hand.small_blind.cash
|
20
|
+
assert_equal 100, @hand.big_blind.cash
|
21
|
+
|
22
|
+
@hand.deal
|
23
|
+
|
24
|
+
assert @hand.pocket?
|
25
|
+
assert_equal 98.75, @hand.small_blind.cash
|
26
|
+
assert_equal 97.5, @hand.big_blind.cash
|
27
|
+
assert_equal 2.5 + 1.25, @hand.pot
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should know when finished" do
|
31
|
+
@hand.deal
|
32
|
+
@hand.fold @bill
|
33
|
+
@hand.fold @carl
|
34
|
+
|
35
|
+
assert_equal true, @hand.finished?
|
36
|
+
end
|
37
|
+
|
38
|
+
test "pay out winnings when only one player remains" do
|
39
|
+
@hand.deal
|
40
|
+
@hand.fold @hand.small_blind
|
41
|
+
@hand.fold @hand.big_blind
|
42
|
+
|
43
|
+
assert_equal 103.75, @hand.winner.cash
|
44
|
+
end
|
45
|
+
|
46
|
+
test "minimum bet is double the small blind" do
|
47
|
+
assert_equal 2.5, @hand.minimum_bet
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class PocketHandTest < Test::Unit::TestCase
|
52
|
+
include HandTestHelper
|
53
|
+
|
54
|
+
test "first round should be pocket/hole cards" do
|
55
|
+
assert @hand.pocket?
|
56
|
+
end
|
57
|
+
|
58
|
+
test "first round should deal two cards to all players" do
|
59
|
+
@hand.deal
|
60
|
+
assert_cards 2, @hand.players
|
61
|
+
end
|
62
|
+
|
63
|
+
test "should have a dealer" do
|
64
|
+
assert_equal @amy, @hand.dealer
|
65
|
+
end
|
66
|
+
|
67
|
+
test "should have a big blind player" do
|
68
|
+
assert_equal @bill, @hand.big_blind
|
69
|
+
end
|
70
|
+
|
71
|
+
test "should have a small blind player" do
|
72
|
+
assert_equal @carl, @hand.small_blind
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class FlopTest < Test::Unit::TestCase
|
77
|
+
include HandTestHelper
|
78
|
+
|
79
|
+
def setup
|
80
|
+
super
|
81
|
+
@hand.advance_to_round 2
|
82
|
+
@hand.deal
|
83
|
+
end
|
84
|
+
|
85
|
+
test "second round should be the flop" do
|
86
|
+
assert @hand.flop?
|
87
|
+
end
|
88
|
+
|
89
|
+
test "second round should deal no new cards to any players" do
|
90
|
+
assert_cards 2, @hand.players
|
91
|
+
end
|
92
|
+
|
93
|
+
test "second round should deal three community cards" do
|
94
|
+
assert_equal 3, @hand.community_cards.size
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class TurnTest < Test::Unit::TestCase
|
99
|
+
include HandTestHelper
|
100
|
+
|
101
|
+
def setup
|
102
|
+
super
|
103
|
+
@hand.advance_to_round 3
|
104
|
+
@hand.deal
|
105
|
+
end
|
106
|
+
|
107
|
+
test "third round should be the turn" do
|
108
|
+
assert @hand.turn?
|
109
|
+
end
|
110
|
+
|
111
|
+
test "third round should deal no new cards to any players" do
|
112
|
+
assert_cards 2, @hand.players
|
113
|
+
end
|
114
|
+
|
115
|
+
test "second round should deal one extra community card" do
|
116
|
+
assert_equal 4, @hand.community_cards.size
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class RiverTest < Test::Unit::TestCase
|
121
|
+
include HandTestHelper
|
122
|
+
|
123
|
+
def setup
|
124
|
+
super
|
125
|
+
@hand.advance_to_round 4
|
126
|
+
@hand.deal
|
127
|
+
end
|
128
|
+
|
129
|
+
test "third round should be the river" do
|
130
|
+
assert @hand.river?
|
131
|
+
end
|
132
|
+
|
133
|
+
test "third round should deal no new cards to any players" do
|
134
|
+
assert_cards 2, @hand.players
|
135
|
+
end
|
136
|
+
|
137
|
+
test "second round should deal one extra community card" do
|
138
|
+
assert_equal 5, @hand.community_cards.size
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class TwoPlayerHandTest < Test::Unit::TestCase
|
143
|
+
include HandTestHelper
|
144
|
+
|
145
|
+
def setup
|
146
|
+
super
|
147
|
+
@hand = TexasHoldem::Hand.new [@bill, @carl]
|
148
|
+
end
|
149
|
+
|
150
|
+
test "should have the dealer as the small blind" do
|
151
|
+
assert_equal @hand.dealer, @bill
|
152
|
+
assert_equal @hand.small_blind, @bill
|
153
|
+
end
|
154
|
+
|
155
|
+
test "should have second player as big blind" do
|
156
|
+
assert_equal @hand.big_blind, @carl
|
157
|
+
end
|
158
|
+
end
|
data/test/test_player.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class PlayerTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@entrance_fee = 100
|
6
|
+
@player = TexasHoldem::Player.new 'Carl', @entrance_fee
|
7
|
+
end
|
8
|
+
|
9
|
+
test "should have a name" do
|
10
|
+
assert_equal 'Carl', @player.name
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should have no cards initially" do
|
14
|
+
assert @player.cards.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
test "should have cash" do
|
18
|
+
assert_equal @entrance_fee, @player.cash
|
19
|
+
end
|
20
|
+
|
21
|
+
test "should place a bet" do
|
22
|
+
@player.bet 99
|
23
|
+
assert_equal 1, @player.cash
|
24
|
+
end
|
25
|
+
|
26
|
+
test "should NOT bet more than available cash" do
|
27
|
+
# FIXME: treat as all in insead of raising an error
|
28
|
+
assert_raises(NotEnoughCashError) do
|
29
|
+
@player.bet(101)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HandTest < Test::Unit::TestCase
|
4
|
+
test "should always have cards sorted in ascending order by face value" do
|
5
|
+
hand = TexasHoldem::PlayersHand.new('Ad As 5c 6c Jd')
|
6
|
+
assert_equal "5c 6c 11d 14s 14d", hand.cards
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class HandIdentificationTest < Test::Unit::TestCase
|
11
|
+
# TODO: add more non-sequential matches
|
12
|
+
|
13
|
+
test "should recognise a high card" do
|
14
|
+
'2d 3s 5c 6c Ad'.hand_name 'high card'
|
15
|
+
end
|
16
|
+
|
17
|
+
test "should recognise a pair" do
|
18
|
+
'2d 2s 5c 6c Ad'.hand_name 'one pair'
|
19
|
+
end
|
20
|
+
|
21
|
+
test "should recognise two pair" do
|
22
|
+
'2d 2s 5c 5d Ad'.hand_name 'two pair'
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should recognise three of a kind" do
|
26
|
+
'2d 2s 2h 5d Ad'.hand_name 'three of a kind'
|
27
|
+
end
|
28
|
+
|
29
|
+
test "should recognise a straight" do
|
30
|
+
'2d 3s 4h 5d 6d'.hand_name 'straight'
|
31
|
+
'10c Jd Qs Kd As'.hand_name 'straight'
|
32
|
+
end
|
33
|
+
|
34
|
+
test "should recognise a flush" do
|
35
|
+
'2h 3h 4h 5h 7h'.hand_name 'flush'
|
36
|
+
end
|
37
|
+
|
38
|
+
test "should recognise a full house" do
|
39
|
+
'2d 2s 2h 4d 4c'.hand_name 'full house'
|
40
|
+
'2d 2s 4h 4d 4c'.hand_name 'full house'
|
41
|
+
'2d 4s 2h 4d 4c'.hand_name 'full house'
|
42
|
+
end
|
43
|
+
|
44
|
+
test "should recognise four of a kind" do
|
45
|
+
'2d 2c 2s 2h 3c'.hand_name 'four of a kind'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class WinningHandsTest < Test::Unit::TestCase
|
50
|
+
test "highest card should win if no scoring poker hands" do
|
51
|
+
'2d 3s 5c 6c Ad'.beats '2c 3d 5s 6h Kd'
|
52
|
+
end
|
53
|
+
|
54
|
+
test "one pair beats no matches" do
|
55
|
+
'2d 2s 5c 6c Ad'.beats '2c 3d 5s 6h Kd'
|
56
|
+
end
|
57
|
+
|
58
|
+
test "high pair beats low pair" do
|
59
|
+
'Ad As 5c 6c Jd'.beats 'Kc Kd 5s 6h Qd'
|
60
|
+
end
|
61
|
+
|
62
|
+
test "highest card wins when pairs are equal" do
|
63
|
+
'Ad As 5c 6c Kd'.beats 'Ac Ad 5s 6h Qd'
|
64
|
+
end
|
65
|
+
|
66
|
+
test "two pair beats a pair" do
|
67
|
+
'Ad As 5c 5d Kd'.beats 'Ac Ad 5s 6h Qd'
|
68
|
+
end
|
69
|
+
|
70
|
+
test "highest pair wins if both two pairs" do
|
71
|
+
'2d 2s 4c 4d Kd'.beats '2c 2d 3s 3h Qd'
|
72
|
+
'Ad As 4c 4d Kd'.beats 'Kc Kd Qs Qh Jd'
|
73
|
+
end
|
74
|
+
|
75
|
+
test "highest card wins if both hands have identincal two pairs" do
|
76
|
+
'2d 2s 4c 4d 6d'.beats '2d 2s 4c 4d 5d'
|
77
|
+
end
|
78
|
+
|
79
|
+
test "three of a kind beats two pair" do
|
80
|
+
'Ad As Ac 5d Kd'.beats 'Kc Kd 5s 5h Qd'
|
81
|
+
end
|
82
|
+
|
83
|
+
test "stright beats three of a kind" do
|
84
|
+
'2d 3c 4h 5s 6d'.beats 'Ad As Ac 5d Kd'
|
85
|
+
end
|
86
|
+
|
87
|
+
test "higher stright beats lower straight" do
|
88
|
+
'3c 4h 5s 6d 7d'.beats '2d 3c 4h 5s 6d'
|
89
|
+
'10c Jd Qs Kd As'.beats '9h 10c Jd Qs Kd'
|
90
|
+
end
|
91
|
+
|
92
|
+
test "flush beats a straight" do
|
93
|
+
'2h 3h 4h 5h 7h'.beats '9h 10c Jd Qs Kd'
|
94
|
+
end
|
95
|
+
|
96
|
+
test "high card determines winner of flushes" do
|
97
|
+
'2h 3h 4h 5h 8h'.beats '2d 3d 4d 5d 7d'
|
98
|
+
end
|
99
|
+
|
100
|
+
test "full house beats a flush" do
|
101
|
+
'2d 2s 2h 4d 4c'.beats '2d 3d 4d 5d 7d'
|
102
|
+
end
|
103
|
+
|
104
|
+
test "higher three of a kind determines full house winner" do
|
105
|
+
'3d 3s 3h 4d 4c'.beats '2s 2h 2c Ad Ac'
|
106
|
+
'Ad As Ah 4d 4c'.beats 'Ks Kh Kc Ad Ac'
|
107
|
+
end
|
108
|
+
|
109
|
+
test "four of a kind beats a full house" do
|
110
|
+
'2d 2c 2s 2h 3c'.beats '3d 3s 3h 4d 4c'
|
111
|
+
end
|
112
|
+
|
113
|
+
test "higher card determines four of a kind winner" do
|
114
|
+
'3d 3c 3s 3h 4c'.beats '2d 2c 2s 2h 5c'
|
115
|
+
'Ad Ac As Ah 4c'.beats 'Kd Kc Ks Kh 5c'
|
116
|
+
end
|
117
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: texas_holdem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Keith McDonnell
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-29 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Ruby classes modelling a game Texas Holdem poker
|
23
|
+
email: keith@dancingtext.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- LICENSE
|
30
|
+
- README
|
31
|
+
- README.rdoc
|
32
|
+
files:
|
33
|
+
- .autotest
|
34
|
+
- .document
|
35
|
+
- .gitignore
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE
|
38
|
+
- README
|
39
|
+
- README.rdoc
|
40
|
+
- Rakefile
|
41
|
+
- VERSION
|
42
|
+
- lib/betting_round.rb
|
43
|
+
- lib/deck.rb
|
44
|
+
- lib/game.rb
|
45
|
+
- lib/hand.rb
|
46
|
+
- lib/player.rb
|
47
|
+
- lib/players_hand.rb
|
48
|
+
- lib/texas_holdem.rb
|
49
|
+
- test/helper.rb
|
50
|
+
- test/test_betting_round.rb
|
51
|
+
- test/test_deck.rb
|
52
|
+
- test/test_game.rb
|
53
|
+
- test/test_hand.rb
|
54
|
+
- test/test_player.rb
|
55
|
+
- test/test_players_hand.rb
|
56
|
+
- test/test_texas_holdem.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/kmcd/texas_holdem
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --charset=UTF-8
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.3.7
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Ruby classes modelling a game Texas Holdem poker
|
91
|
+
test_files:
|
92
|
+
- test/test_game.rb
|
93
|
+
- test/test_player.rb
|
94
|
+
- test/test_betting_round.rb
|
95
|
+
- test/test_hand.rb
|
96
|
+
- test/test_deck.rb
|
97
|
+
- test/test_players_hand.rb
|
98
|
+
- test/helper.rb
|
99
|
+
- test/test_texas_holdem.rb
|