ruby_holdem 0.0.1
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 +7 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +48 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile.rb +6 -0
- data/example_game.rb +44 -0
- data/lib/ruby_holdem/dealer.rb +28 -0
- data/lib/ruby_holdem/deck.rb +51 -0
- data/lib/ruby_holdem/errors.rb +7 -0
- data/lib/ruby_holdem/game.rb +5 -0
- data/lib/ruby_holdem/round.rb +154 -0
- data/lib/ruby_holdem/round_player.rb +11 -0
- data/lib/ruby_holdem/version.rb +3 -0
- data/lib/ruby_holdem.rb +7 -0
- data/ruby_holdem.gemspec +28 -0
- data/spec/dealer_spec.rb +5 -0
- data/spec/round_spec.rb +249 -0
- data/spec/spec_helper.rb +10 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 29fbffb1efbe129bc60cc9b38ef8f048c2e18ab9
|
4
|
+
data.tar.gz: 0b99f0a5ebf3996ae2fe11b169647cf1dd74384b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4b072056f11a740fe63f29deceb02a692114c152b6532ccb83364757b83728f7cb4425f063f8baeee3373672a31eaf6944a748b42dc9ee35a3c155b8b4bd172
|
7
|
+
data.tar.gz: 409610a9e35b4ec60cfb4730edea0ca4d7958f53682e20b039a502d2d2e3264f89c4d8e26327f577f023324facf3112340c77b4fa922d4bc046068cb76036e8e
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ruby_holdem (0.0.1)
|
5
|
+
ruby-poker
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.1.0)
|
11
|
+
diff-lcs (1.2.5)
|
12
|
+
method_source (0.8.2)
|
13
|
+
pry (0.10.1)
|
14
|
+
coderay (~> 1.1.0)
|
15
|
+
method_source (~> 0.8.1)
|
16
|
+
slop (~> 3.4)
|
17
|
+
rake (10.4.0)
|
18
|
+
rspec (3.1.0)
|
19
|
+
rspec-core (~> 3.1.0)
|
20
|
+
rspec-expectations (~> 3.1.0)
|
21
|
+
rspec-mocks (~> 3.1.0)
|
22
|
+
rspec-core (3.1.7)
|
23
|
+
rspec-support (~> 3.1.0)
|
24
|
+
rspec-expectations (3.1.2)
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
+
rspec-support (~> 3.1.0)
|
27
|
+
rspec-its (1.1.0)
|
28
|
+
rspec-core (>= 3.0.0)
|
29
|
+
rspec-expectations (>= 3.0.0)
|
30
|
+
rspec-mocks (3.1.3)
|
31
|
+
rspec-support (~> 3.1.0)
|
32
|
+
rspec-support (3.1.2)
|
33
|
+
ruby-poker (1.0.1)
|
34
|
+
slop (3.6.0)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 1.7)
|
41
|
+
pry
|
42
|
+
rake (~> 10.0)
|
43
|
+
rspec
|
44
|
+
rspec-its
|
45
|
+
ruby_holdem!
|
46
|
+
|
47
|
+
BUNDLED WITH
|
48
|
+
1.12.5
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016, Evan Rolfe
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# RubyHoldem
|
2
|
+
RubyHoldem is a set of classes which track the game state of a texas holdem poker game.
|
3
|
+
|
4
|
+
### Installation
|
5
|
+
```
|
6
|
+
git clone git@github.com:evanrolfe/ruby-holdem.git
|
7
|
+
cd ruby-holdem/
|
8
|
+
gem install ruby_holdem
|
9
|
+
```
|
10
|
+
|
11
|
+
### Usage
|
12
|
+
```ruby
|
13
|
+
require 'ruby_holdem'
|
14
|
+
|
15
|
+
players = ["Jack", "Joe", "Jil"]
|
16
|
+
poker_round = RubyHoldem::Round.new(players, 2, 4)
|
17
|
+
|
18
|
+
poker_round.make_move('bet', 2) # Jack bets small blinds
|
19
|
+
poker_round.make_move('bet', 4) # Joe bets big blinds
|
20
|
+
poker_round.make_move('fold') # Jil folds
|
21
|
+
poker_round.make_move('call') # Jack calls
|
22
|
+
poker_round.make_move('call') # Joe calls
|
23
|
+
poker_round.next_stage
|
24
|
+
|
25
|
+
puts poker_round.community_cards.join(' ')
|
26
|
+
# => i.e. 3s Js Qd
|
27
|
+
|
28
|
+
puts poker_round.pot_amount
|
29
|
+
# => 8
|
30
|
+
|
31
|
+
poker_round.make_move('bet', 3) # Jack bets 3
|
32
|
+
poker_round.make_move('fold') # Joe folds
|
33
|
+
|
34
|
+
puts poker_round.winner
|
35
|
+
# => Jack
|
36
|
+
```
|
37
|
+
|
38
|
+
### License
|
39
|
+
|
40
|
+
RubyHoldem uses the MIT license. Please check the [LICENSE](https://github.com/evanrolfe/ruby-holdem/blob/master/LICENSE) file for more details.
|
data/Rakefile.rb
ADDED
data/example_game.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ruby_holdem'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
line_break = "\n----------------------\n"
|
5
|
+
|
6
|
+
player1 = OpenStruct.new(name: "Player #1")
|
7
|
+
player2 = OpenStruct.new(name: "Player #2")
|
8
|
+
player3 = OpenStruct.new(name: "Player #3")
|
9
|
+
players = [player1, player2, player3]
|
10
|
+
|
11
|
+
poker_round = RubyHoldem::Round.new(players, 2, 4)
|
12
|
+
|
13
|
+
puts "Game started"
|
14
|
+
|
15
|
+
until poker_round.has_winner?
|
16
|
+
puts "#{line_break}#{poker_round.current_stage} , pot: $#{poker_round.pot_amount}"
|
17
|
+
if poker_round.community_cards.any?
|
18
|
+
puts "Community cards: #{poker_round.community_cards.join(' ')}#{line_break}"
|
19
|
+
end
|
20
|
+
|
21
|
+
until poker_round.ready_for_next_stage?
|
22
|
+
next_move_args = [
|
23
|
+
['bet', 4],
|
24
|
+
['call'],
|
25
|
+
['fold']
|
26
|
+
].sample
|
27
|
+
|
28
|
+
poker_round.make_move(*next_move_args)
|
29
|
+
|
30
|
+
if poker_round.last_move[:move] == 'bet'
|
31
|
+
puts "Player #{poker_round.last_move[:player].name} raised by #{poker_round.last_move[:amount]}"
|
32
|
+
elsif poker_round.last_move[:move] == 'call'
|
33
|
+
puts "Player #{poker_round.last_move[:player].name} called by #{poker_round.last_move[:amount]}"
|
34
|
+
else
|
35
|
+
puts "Player #{poker_round.last_move[:player].name} folded"
|
36
|
+
end
|
37
|
+
|
38
|
+
sleep 0.3
|
39
|
+
end
|
40
|
+
|
41
|
+
poker_round.next_stage
|
42
|
+
end
|
43
|
+
|
44
|
+
puts "The winner is #{poker_round.winner.name}!"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RubyHoldem
|
2
|
+
class Dealer
|
3
|
+
attr_reader :community_cards
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@deck = Deck.new
|
7
|
+
@deck.shuffle
|
8
|
+
@community_cards = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def deal_hole_cards(players)
|
12
|
+
raise ArgumentError unless players.map { |player| player.hole_cards.count == 0 }.all?
|
13
|
+
players.each do |player|
|
14
|
+
2.times { player.hole_cards << @deck.deal }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def deal_community_cards(stage)
|
19
|
+
raise ArgumentError unless %w(pre_flop flop turn river show_down).include?(stage)
|
20
|
+
|
21
|
+
if stage == 'flop'
|
22
|
+
3.times { community_cards << @deck.deal }
|
23
|
+
elsif %w(turn river).include?(stage)
|
24
|
+
community_cards << @deck.deal
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Code taken from: https://github.com/robolson/ruby-poker/blob/master/examples/deck.rb
|
2
|
+
|
3
|
+
module RubyHoldem
|
4
|
+
class Deck
|
5
|
+
def initialize
|
6
|
+
@cards = []
|
7
|
+
Card::SUITS.each_byte do |suit|
|
8
|
+
# careful not to double include the aces...
|
9
|
+
Card::FACES[1..-1].each_byte do |face|
|
10
|
+
@cards.push(Card.new(face.chr, suit.chr))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
shuffle
|
14
|
+
end
|
15
|
+
|
16
|
+
def shuffle
|
17
|
+
@cards = @cards.sort_by { rand }
|
18
|
+
return self
|
19
|
+
end
|
20
|
+
|
21
|
+
# removes a single card from the top of the deck and returns it
|
22
|
+
# synonymous to poping off a stack
|
23
|
+
def deal
|
24
|
+
@cards.pop
|
25
|
+
end
|
26
|
+
|
27
|
+
# delete an array or a single card from the deck
|
28
|
+
# converts a string to a new card, if a string is given
|
29
|
+
def burn(burn_cards)
|
30
|
+
return false if burn_cards.is_a?(Integer)
|
31
|
+
if burn_cards.is_a?(Card) || burn_cards.is_a?(String)
|
32
|
+
burn_cards = [burn_cards]
|
33
|
+
end
|
34
|
+
|
35
|
+
burn_cards.map! do |c|
|
36
|
+
c = Card.new(c) unless c.class == Card
|
37
|
+
@cards.delete(c)
|
38
|
+
end
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
# return count of the remaining cards
|
43
|
+
def size
|
44
|
+
@cards.size
|
45
|
+
end
|
46
|
+
|
47
|
+
def empty?
|
48
|
+
@cards.empty?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module RubyHoldem
|
4
|
+
class Round
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :players,
|
8
|
+
:small_blinds,
|
9
|
+
:big_blinds,
|
10
|
+
:pot_amount,
|
11
|
+
:current_stage,
|
12
|
+
:action_history,
|
13
|
+
:dealer,
|
14
|
+
:turns_played
|
15
|
+
|
16
|
+
def_delegator :@dealer, :community_cards
|
17
|
+
|
18
|
+
STAGES = %w(pre_flop flop turn river show_down)
|
19
|
+
|
20
|
+
def initialize(players, small_blinds, big_blinds)
|
21
|
+
@small_blinds, @big_blinds = small_blinds, big_blinds
|
22
|
+
@current_stage = STAGES[0]
|
23
|
+
@pot_amount = 0
|
24
|
+
@action_history = []
|
25
|
+
|
26
|
+
@players = players.map { |player| RoundPlayer.new(player) }
|
27
|
+
@dealer = Dealer.new
|
28
|
+
@dealer.deal_hole_cards(@players)
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: Extract the code relating to making a move into its own class to separate the logic
|
32
|
+
# behind making a move and the game state methods
|
33
|
+
def make_move(move, amount=nil)
|
34
|
+
if turns_played == 0
|
35
|
+
apply_bet(small_blinds)
|
36
|
+
elsif turns_played == 1
|
37
|
+
apply_bet(big_blinds)
|
38
|
+
elsif move == 'bet'
|
39
|
+
apply_bet(amount)
|
40
|
+
elsif move == 'call'
|
41
|
+
apply_call
|
42
|
+
elsif move == 'fold'
|
43
|
+
apply_fold
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def next_stage
|
48
|
+
raise StandardError unless ready_for_next_stage? && @current_stage != 'show_down'
|
49
|
+
|
50
|
+
@current_stage = STAGES[STAGES.index(@current_stage)+1]
|
51
|
+
@dealer.deal_community_cards(@current_stage)
|
52
|
+
end
|
53
|
+
|
54
|
+
def ready_for_next_stage?
|
55
|
+
return false unless every_player_has_called? && turns_played_in_stage > 0
|
56
|
+
|
57
|
+
players_still_in_round.map { |player| (player.current_bet_amount == highest_bet_placed) }.all?
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_winner?
|
61
|
+
(current_stage == 'show_down' || players_still_in_round.count == 1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def winner
|
65
|
+
return players_still_in_round[0] if players_still_in_round.count == 1
|
66
|
+
return players_still_in_round[2]
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO: Refactor this method to make it more readable
|
70
|
+
def player_in_turn #The player whose turn it is to make a move
|
71
|
+
return players[0] if action_history.length == 0
|
72
|
+
|
73
|
+
last_player_index = players.index(action_history.last[:player])
|
74
|
+
player_found = false
|
75
|
+
increment=1
|
76
|
+
|
77
|
+
until player_found
|
78
|
+
next_player = players[(last_player_index + increment) % players.length] #Wrap around the array once end reached
|
79
|
+
player_found = true if players_still_in_round.include?(next_player)
|
80
|
+
increment += 1
|
81
|
+
end
|
82
|
+
|
83
|
+
next_player
|
84
|
+
end
|
85
|
+
|
86
|
+
def players_still_in_round
|
87
|
+
players.select do |round_player|
|
88
|
+
folds = action_history.select { |action| action[:move] == 'fold' && action[:player] == round_player }
|
89
|
+
(folds.length == 0)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def highest_bet_placed
|
94
|
+
players_still_in_round.max_by(&:current_bet_amount).current_bet_amount
|
95
|
+
end
|
96
|
+
|
97
|
+
def last_move
|
98
|
+
action_history.last
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def turns_played
|
104
|
+
action_history.length
|
105
|
+
end
|
106
|
+
|
107
|
+
def turns_played_in_stage
|
108
|
+
action_history.select { |action| action[:stage] == @current_stage }.length
|
109
|
+
end
|
110
|
+
|
111
|
+
def every_player_has_called?
|
112
|
+
players_num_calls = players_still_in_round.map do |round_player|
|
113
|
+
calls = action_history.select { |action| action[:stage] == @current_stage && action[:move] == 'call' && action[:player] == round_player }
|
114
|
+
calls.length
|
115
|
+
end
|
116
|
+
|
117
|
+
players_num_calls.map { |num_calls| num_calls >= 1 }.all?
|
118
|
+
end
|
119
|
+
|
120
|
+
def apply_bet(amount)
|
121
|
+
raise MinBetNotMeet if amount < min_bet_amount_for_player(player_in_turn)
|
122
|
+
|
123
|
+
#TODO: Go all in instead of raising an error
|
124
|
+
raise NotEnoughMoney unless player_can_afford_bet?(player_in_turn, amount)
|
125
|
+
|
126
|
+
@pot_amount += amount
|
127
|
+
player_in_turn.current_bet_amount += amount
|
128
|
+
action_history << { player: player_in_turn, stage: current_stage, move: 'bet', amount: amount}
|
129
|
+
end
|
130
|
+
|
131
|
+
def apply_call
|
132
|
+
amount = min_bet_amount_for_player(player_in_turn)
|
133
|
+
raise NotEnoughMoney unless player_can_afford_bet?(player_in_turn, amount)
|
134
|
+
|
135
|
+
@pot_amount += amount
|
136
|
+
player_in_turn.current_bet_amount += amount
|
137
|
+
|
138
|
+
action_history << { player: player_in_turn, stage: current_stage, move: 'call', amount: amount}
|
139
|
+
end
|
140
|
+
|
141
|
+
def apply_fold
|
142
|
+
action_history << { player: player_in_turn, stage: current_stage, move: 'fold', amount: 0}
|
143
|
+
end
|
144
|
+
|
145
|
+
def min_bet_amount_for_player(player)
|
146
|
+
highest_bet_placed - player.current_bet_amount
|
147
|
+
end
|
148
|
+
|
149
|
+
# TODO:
|
150
|
+
def player_can_afford_bet?(player, bet_amount)
|
151
|
+
true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/ruby_holdem.rb
ADDED
data/ruby_holdem.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'ruby_holdem/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'ruby_holdem'
|
9
|
+
spec.version = RubyHoldem::VERSION
|
10
|
+
spec.authors = ['Evan Rolfe']
|
11
|
+
spec.email = ['evanrolfe@gmail.com']
|
12
|
+
spec.summary = 'A gem for playing texas-holdem poker'
|
13
|
+
spec.homepage = 'https://github.com/evanrolfe/ruby-holdem'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = []
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'ruby-poker'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'rspec'
|
26
|
+
spec.add_development_dependency 'pry'
|
27
|
+
spec.add_development_dependency 'rspec-its'
|
28
|
+
end
|
data/spec/dealer_spec.rb
ADDED
data/spec/round_spec.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe RubyHoldem::Round do
|
5
|
+
let(:player1) { OpenStruct.new(name: "Player #1") }
|
6
|
+
let(:player2) { OpenStruct.new(name: "Player #2") }
|
7
|
+
let(:player3) { OpenStruct.new(name: "Player #3") }
|
8
|
+
let(:players) { [player1, player2, player3] }
|
9
|
+
|
10
|
+
let(:round) { RubyHoldem::Round.new(players, 1, 2) }
|
11
|
+
|
12
|
+
describe '#initialize' do
|
13
|
+
subject { round }
|
14
|
+
|
15
|
+
# TODO: Convert all instances of "should" to "is_expected"
|
16
|
+
its(:small_blinds) { should eq(1) }
|
17
|
+
its(:big_blinds) { should eq(2) }
|
18
|
+
its(:current_stage) { should eq('pre_flop') }
|
19
|
+
its(:action_history) { should eq([]) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#next_stage' do
|
23
|
+
before do
|
24
|
+
allow(round).to receive(:action_history).and_return(action_history)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'on the pre_flop' do
|
28
|
+
# TODO: Convert the stubbing of the action_history to a sequence of make_move calls in a
|
29
|
+
# before block
|
30
|
+
let(:action_history) do
|
31
|
+
[
|
32
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
33
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' },
|
34
|
+
{ stage: 'pre_flop', player: round.players[2], amount: 1, move: 'call' },
|
35
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 0, move: 'call' }
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
before do
|
40
|
+
round.next_stage
|
41
|
+
end
|
42
|
+
|
43
|
+
it { expect(round.current_stage).to eq('flop') }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#ready_for_next_stage?' do
|
48
|
+
before do
|
49
|
+
allow(round).to receive(:action_history).and_return(action_history)
|
50
|
+
end
|
51
|
+
|
52
|
+
subject { round.ready_for_next_stage? }
|
53
|
+
|
54
|
+
context 'on game start' do
|
55
|
+
let(:action_history) { [] }
|
56
|
+
|
57
|
+
it { should eq(false) }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'on the pre_flop' do
|
61
|
+
|
62
|
+
context 'situation 1' do
|
63
|
+
let(:action_history) do
|
64
|
+
[
|
65
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
66
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' }
|
67
|
+
]
|
68
|
+
end
|
69
|
+
it { should eq(false) }
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'situation 2' do
|
73
|
+
let(:action_history) do
|
74
|
+
[
|
75
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
76
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' },
|
77
|
+
{ stage: 'pre_flop', player: round.players[2], amount: 1, move: 'call' },
|
78
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 0, move: 'call' }
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
it { should eq(true) }
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'on the flop' do
|
88
|
+
|
89
|
+
context 'situation 1' do
|
90
|
+
let(:action_history) do
|
91
|
+
[
|
92
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
93
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' },
|
94
|
+
{ stage: 'pre_flop', player: round.players[2], amount: 1, move: 'call' },
|
95
|
+
{ stage: 'flop', player: round.players[0], amount: 1, move: 'bet' }
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
before do
|
100
|
+
allow(round).to receive(:current_stage).and_return('flop')
|
101
|
+
end
|
102
|
+
|
103
|
+
it { should eq(false) }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'situation 2' do
|
107
|
+
let(:action_history) do
|
108
|
+
[
|
109
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
110
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' },
|
111
|
+
{ stage: 'pre_flop', player: round.players[2], amount: 1, move: 'call' },
|
112
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 0, move: 'call' },
|
113
|
+
{ stage: 'flop', player: round.players[0], amount: 3, move: 'bet' },
|
114
|
+
{ stage: 'flop', player: round.players[2], amount: 3, move: 'call' },
|
115
|
+
{ stage: 'flop', player: round.players[0], amount: 0, move: 'call' }
|
116
|
+
]
|
117
|
+
end
|
118
|
+
|
119
|
+
before do
|
120
|
+
allow(round).to receive(:current_stage).and_return('flop')
|
121
|
+
end
|
122
|
+
|
123
|
+
it { should eq(true) }
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe '#player_in_turn' do
|
130
|
+
let(:action_history) do
|
131
|
+
[
|
132
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
133
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' }
|
134
|
+
]
|
135
|
+
end
|
136
|
+
|
137
|
+
before do
|
138
|
+
allow(round).to receive(:action_history).and_return(action_history)
|
139
|
+
end
|
140
|
+
|
141
|
+
subject { round.send(:player_in_turn) }
|
142
|
+
|
143
|
+
it { should == round.players[2] }
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#players_still_in_round' do
|
147
|
+
let(:action_history) do
|
148
|
+
[
|
149
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
150
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 0, move: 'fold' }
|
151
|
+
]
|
152
|
+
end
|
153
|
+
|
154
|
+
before do
|
155
|
+
allow(round).to receive(:action_history).and_return(action_history)
|
156
|
+
end
|
157
|
+
|
158
|
+
subject { round.send(:players_still_in_round) }
|
159
|
+
|
160
|
+
its([0]) { should eq(round.players[0]) }
|
161
|
+
its([1]) { should eq(round.players[2]) }
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#highest_bet_placed' do
|
165
|
+
before do
|
166
|
+
round.make_move('call')
|
167
|
+
round.make_move('call')
|
168
|
+
round.make_move('bet', 4)
|
169
|
+
end
|
170
|
+
|
171
|
+
subject { round.send(:highest_bet_placed) }
|
172
|
+
|
173
|
+
it { should eq(4) }
|
174
|
+
end
|
175
|
+
|
176
|
+
describe '#has_winner?' do
|
177
|
+
subject { round.has_winner? }
|
178
|
+
|
179
|
+
context 'round won after folds' do
|
180
|
+
let(:action_history) do
|
181
|
+
[
|
182
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 1, move: 'bet' },
|
183
|
+
{ stage: 'pre_flop', player: round.players[1], amount: 4, move: 'bet' },
|
184
|
+
{ stage: 'pre_flop', player: round.players[2], amount: 0, move: 'fold'},
|
185
|
+
{ stage: 'pre_flop', player: round.players[0], amount: 3, move: 'call' },
|
186
|
+
{ stage: 'flop', player: round.players[1], amount: 0, move: 'call' },
|
187
|
+
{ stage: 'flop', player: round.players[0], amount: 0, move: 'fold' }
|
188
|
+
]
|
189
|
+
end
|
190
|
+
|
191
|
+
before do
|
192
|
+
allow(round).to receive(:action_history).and_return(action_history)
|
193
|
+
end
|
194
|
+
|
195
|
+
it { should eq(true) }
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'showdown between remaining players' do
|
199
|
+
before do
|
200
|
+
allow(round).to receive(:current_stage).and_return('show_down')
|
201
|
+
end
|
202
|
+
|
203
|
+
it { should eq(true) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#winner' do
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
describe '#apply_bet' do
|
212
|
+
context 'at the start of the round' do
|
213
|
+
before do
|
214
|
+
round.send(:apply_bet, 1)
|
215
|
+
end
|
216
|
+
|
217
|
+
subject { round }
|
218
|
+
|
219
|
+
its(:action_history) { should eq([{ player: round.players[0], stage: 'pre_flop', move: 'bet', amount: 1 }]) }
|
220
|
+
its(:pot_amount) { should eq(1) }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#apply_call' do
|
225
|
+
context 'at the start of the round' do
|
226
|
+
before do
|
227
|
+
round.send(:apply_call)
|
228
|
+
end
|
229
|
+
|
230
|
+
subject { round }
|
231
|
+
|
232
|
+
its(:action_history) { should eq([{ player: round.players[0], stage: 'pre_flop', move: 'call', amount: 0 }]) }
|
233
|
+
its(:pot_amount) { should eq(0) }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe '#apply_fold' do
|
238
|
+
context 'at the start of the round' do
|
239
|
+
before do
|
240
|
+
round.send(:apply_fold)
|
241
|
+
end
|
242
|
+
|
243
|
+
subject { round }
|
244
|
+
|
245
|
+
its(:action_history) { should eq([{ player: round.players[0], stage: 'pre_flop', move: 'fold', amount: 0 }]) }
|
246
|
+
its(:pot_amount) { should eq(0) }
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_holdem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Evan Rolfe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-poker
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-its
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- evanrolfe@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".rspec"
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- LICENSE
|
108
|
+
- README.md
|
109
|
+
- Rakefile.rb
|
110
|
+
- example_game.rb
|
111
|
+
- lib/ruby_holdem.rb
|
112
|
+
- lib/ruby_holdem/dealer.rb
|
113
|
+
- lib/ruby_holdem/deck.rb
|
114
|
+
- lib/ruby_holdem/errors.rb
|
115
|
+
- lib/ruby_holdem/game.rb
|
116
|
+
- lib/ruby_holdem/round.rb
|
117
|
+
- lib/ruby_holdem/round_player.rb
|
118
|
+
- lib/ruby_holdem/version.rb
|
119
|
+
- ruby_holdem.gemspec
|
120
|
+
- spec/dealer_spec.rb
|
121
|
+
- spec/round_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
homepage: https://github.com/evanrolfe/ruby-holdem
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.5.1
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: A gem for playing texas-holdem poker
|
147
|
+
test_files:
|
148
|
+
- spec/dealer_spec.rb
|
149
|
+
- spec/round_spec.rb
|
150
|
+
- spec/spec_helper.rb
|