rora 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +210 -20
- data/lib/rora/model/board.rb +10 -0
- data/lib/rora/model/card.rb +1 -5
- data/lib/rora/model/game.rb +70 -2
- data/lib/rora/model/hand.rb +1 -2
- data/lib/rora/model/player.rb +70 -5
- data/lib/rora/model/pot.rb +7 -6
- data/lib/rora/model/rank.rb +2 -2
- data/lib/rora/model/seat.rb +31 -0
- data/lib/rora/model/starting_hand.rb +4 -0
- data/lib/rora/model/suit.rb +1 -1
- data/lib/rora/model/table.rb +107 -14
- data/lib/rora/utils/game_logger.rb +19 -0
- data/lib/rora.rb +7 -2
- data/test/rora/model/board_test.rb +14 -12
- data/test/rora/model/card_test.rb +16 -7
- data/test/rora/model/game_test.rb +23 -0
- data/test/rora/model/hand_type_test.rb +3 -3
- data/test/rora/model/player_test.rb +108 -0
- data/test/rora/model/pot_test.rb +15 -8
- data/test/rora/model/rank_test.rb +6 -6
- data/test/rora/model/seat_test.rb +41 -0
- data/test/rora/model/suit_test.rb +1 -1
- data/test/rora/model/table_test.rb +230 -0
- data/test/rora_test.rb +2 -1
- metadata +48 -6
data/README.md
CHANGED
@@ -33,7 +33,7 @@ A complete deck of 52 cards will contain exactly thirteen ranks - numerical valu
|
|
33
33
|
## Cards
|
34
34
|
|
35
35
|
### Overview
|
36
|
-
A playing card is constructed with one suit and one rank. A card can be created
|
36
|
+
A playing card is constructed with exactly one suit and one rank. A card can be created by using the Suit and Rank classes as described above, or their equivalent character values.
|
37
37
|
|
38
38
|
# Example 1: Creating a card with Rank and Suit instances.
|
39
39
|
card = Card.new Rank::ACE, Suit::SPADE
|
@@ -46,7 +46,7 @@ A playing card is constructed with one suit and one rank. A card can be created
|
|
46
46
|
|
47
47
|
### Creating multiple cards at once
|
48
48
|
|
49
|
-
You can create multiple cards at once by using the to_cards class method. The to_cards takes a series suit-rank characters seperated by commas or white spaces and returns an
|
49
|
+
You can create multiple cards at once by using the to_cards class method. The to_cards takes a series suit-rank characters seperated by commas or white spaces and returns an array of cards.
|
50
50
|
|
51
51
|
# Example 1: Create an array of cards by providing a comma-seperated string.
|
52
52
|
cards = Card.to_cards "AS,KS,QS,JS"
|
@@ -60,7 +60,7 @@ You can create multiple cards at once by using the to_cards class method. The to
|
|
60
60
|
## Hands
|
61
61
|
|
62
62
|
### Overview
|
63
|
-
A hand consists of exactly five cards, and can be constructed by providing an
|
63
|
+
A hand consists of exactly five cards, and can be constructed by providing an array of cards or an equivalent sequence of suit-rank character values.
|
64
64
|
|
65
65
|
# Example 1: Creating a hand with a comma-seperated string.
|
66
66
|
hand = Hand.new "AS,KS,QS,JS,TS"
|
@@ -71,7 +71,7 @@ A hand consists of exactly five cards, and can be constructed by providing an Ar
|
|
71
71
|
# Example 3: Creating a hand with an Array of cards.
|
72
72
|
hand = Hand.new [Card.new("AS"), Card.new("KS"), Card.new("QS"), Card.new("JS"), Card.new("TS")]
|
73
73
|
|
74
|
-
# Example 4: Creating a hand with an
|
74
|
+
# Example 4: Creating a hand with an array of cards, using Card.to_cards
|
75
75
|
hand = Hand.new Card.to_cards("AS,KS,QS,JS,TS")
|
76
76
|
|
77
77
|
### Hand Score
|
@@ -92,7 +92,7 @@ Each hand in poker has a rank, or score. While many software libraries provide t
|
|
92
92
|
puts seven_high.name => 'High Card'
|
93
93
|
puts seven_high.probability => 0.50 // Probability of receiving any High Card
|
94
94
|
|
95
|
-
### Hand Detection
|
95
|
+
### Hand Type Detection
|
96
96
|
|
97
97
|
You can query a hand to determine what kind of hand it is.
|
98
98
|
|
@@ -111,13 +111,20 @@ Hand type detection extends to the following methods:
|
|
111
111
|
- two_pair?
|
112
112
|
- one_pair?
|
113
113
|
- high_card?
|
114
|
+
|
115
|
+
### Hand Type Probability
|
116
|
+
|
117
|
+
You can query a hand to determine the probability of drawing that type of hand.
|
118
|
+
|
119
|
+
# Example 1: Determine the probability of a Royal Flush
|
120
|
+
puts Hand.new("AS,KS,QS,JS,TS").probability => 0.0015
|
114
121
|
|
115
122
|
## Starting Hands
|
116
123
|
|
117
124
|
Beyond the code samples presented below, the following links provide more information about [texas hold'em starting hands](http://www.moralesce.com/2012/01/21/holdem-starting-hands/) and [poker hand evaluation](http://www.moralesce.com/2011/11/26/poker-hand-evaluation/).
|
118
125
|
|
119
126
|
### Overview
|
120
|
-
A starting hand is also referred to as a
|
127
|
+
A starting hand is also referred to as a player's pocket cards, or hole cards. A starting hand consists of exactly two cards, and can be constructed by providing an array of cards or an equivalent sequence of suit-rank character values.
|
121
128
|
|
122
129
|
# Example 1: Creating a starting hand with a comma-seperated string.
|
123
130
|
starting_hand = StartingHand.new "AS,KS"
|
@@ -133,7 +140,9 @@ A starting hand is also referred to as a players pocket cards, or hole cards. A
|
|
133
140
|
|
134
141
|
### Creating an array of all starting hands
|
135
142
|
|
136
|
-
|
143
|
+
Given a deck of 52 cards, choosing any two card yields 1326 distinct 2 card combinations
|
144
|
+
|
145
|
+
# Returns an array of all 1326 starting hands.
|
137
146
|
all = StartingHand.all_starting_hands
|
138
147
|
|
139
148
|
|
@@ -188,27 +197,35 @@ The remove method takes a variety of arguments.
|
|
188
197
|
deck.remove starting_hand
|
189
198
|
|
190
199
|
### Inspecting the Deck
|
191
|
-
You can query the deck to determine whether it contains a specific card or at least one card in a group.
|
192
|
-
|
193
|
-
# Determines if the Ace of Spades is in the deck.
|
194
|
-
deck.contains Card.new("AS")
|
200
|
+
You can query the deck to determine whether it contains a specific card or at least one card in a group.
|
195
201
|
|
196
202
|
# Determines if the Ace of Spades is in the deck.
|
197
203
|
deck.contains "AS"
|
198
204
|
|
199
|
-
# Determines if
|
200
|
-
deck.contains
|
205
|
+
# Determines if the Ace of Spades is in the deck.
|
206
|
+
deck.contains Card.new("AS")
|
201
207
|
|
202
208
|
# Determines if any Ace is in the deck.
|
203
|
-
deck.
|
209
|
+
deck.contains_any "AS,AH,AD,AC"
|
210
|
+
|
211
|
+
# Determines if any Ace is in the deck.
|
212
|
+
deck.contains_any [Card.new("AS"), Card.new("AH"), Card.new("AD"), Card.new("AC")]
|
213
|
+
|
214
|
+
# Determines if there are any cards in the deck
|
215
|
+
deck.empty?
|
216
|
+
|
217
|
+
# Returns the number of Aces left in the deck
|
218
|
+
deck.count_cards_with_rank Rank::ACE
|
219
|
+
|
220
|
+
# Returns the number of Clubs left in the deck
|
221
|
+
deck.count_cards_with_suit Suit::CLUB
|
204
222
|
|
205
223
|
|
206
224
|
### Combinations
|
207
225
|
The combination method allows you to enumerate through card subsets. Given a combination value greater than 1, the method will return a two-dimensional array.
|
208
226
|
|
209
|
-
|
210
227
|
# Chooses every possible 2 card combination from the deck. Assuming the deck contains 52
|
211
|
-
# cards, this example will return
|
228
|
+
# cards, this example will return 1326 2-card combinations.
|
212
229
|
cards = deck.combination 2
|
213
230
|
|
214
231
|
# Chooses every possible 5 card combination from the deck. Assuming the deck contains 52
|
@@ -218,7 +235,7 @@ The combination method allows you to enumerate through card subsets. Given a com
|
|
218
235
|
## Boards
|
219
236
|
|
220
237
|
### Overview
|
221
|
-
A board represents the logical table area where community cards are dealt. A board can be setup with either 0, 3, 4 or 5 cards to represent and empty board, the flop, turn, or river (respectively).
|
238
|
+
A board represents the logical table area where community cards are dealt. A board can be setup with either 0, 3, 4 or 5 unique cards to represent and empty board, the flop, turn, or river (respectively).
|
222
239
|
|
223
240
|
# Creates an empty board.
|
224
241
|
board = Board.new
|
@@ -233,7 +250,7 @@ A board represents the logical table area where community cards are dealt. A boa
|
|
233
250
|
board = Board.new "KS,QS,7H,4C,3H"
|
234
251
|
|
235
252
|
### Subsequent Betting Rounds
|
236
|
-
An empty board can be populated
|
253
|
+
An empty board can be populated after constrution as well - indeed, this is the most common usage.
|
237
254
|
|
238
255
|
board = Board.new
|
239
256
|
board.flop = "AS,KS,QS"
|
@@ -252,7 +269,180 @@ You can query the board to determine whether it contains a specific card or at l
|
|
252
269
|
board.contains "AS"
|
253
270
|
|
254
271
|
# Determines if any Ace is on the board.
|
255
|
-
board.
|
272
|
+
board.contains_any [Card.new("AS"), Card.new("AH"), Card.new("AD"), Card.new("AC")]
|
256
273
|
|
257
274
|
# Determines if any Ace is on the board.
|
258
|
-
board.
|
275
|
+
board.contains_any "AS,AH,AD,AC"
|
276
|
+
|
277
|
+
# You can query to the board for flop, turn and river cards.
|
278
|
+
puts board.flop => '2H', '3C', 'JH'
|
279
|
+
puts board.turn => 'AS'
|
280
|
+
puts board.river => 'JS'
|
281
|
+
|
282
|
+
# You can query the board to return all cards on the board
|
283
|
+
puts board.cards => '2H', '3C', 'JH', 'AS', 'JS'
|
284
|
+
|
285
|
+
## Pots
|
286
|
+
|
287
|
+
A pot represents the sum of money that players compete for during a hand of poker.
|
288
|
+
|
289
|
+
# Creates a new pot.
|
290
|
+
pot = Pot.new
|
291
|
+
|
292
|
+
### Adding Money to the Pot
|
293
|
+
|
294
|
+
You can add positive sums of money to the pot. At the moment the pot assumes only one kind of (implicit) currency.
|
295
|
+
|
296
|
+
# Add money to the pot
|
297
|
+
pot.add(5)
|
298
|
+
puts pot.value => '5'
|
299
|
+
|
300
|
+
# Chain add operations
|
301
|
+
pot.add(5).add(5).add(5)
|
302
|
+
puts pot.value => '15'
|
303
|
+
|
304
|
+
# Add decimal values to the pot
|
305
|
+
pot.add(2.50).add(1.25)
|
306
|
+
puts pot.value => '3.75'
|
307
|
+
|
308
|
+
# The to_s representation returns formatted monetary amount
|
309
|
+
pot.add(300)
|
310
|
+
puts pot.to_s => '$300.00'
|
311
|
+
|
312
|
+
## Tables
|
313
|
+
|
314
|
+
A poker table where players compete for pots. A newly created table will have one deck of 52 cards, one pot with zero dollars and one empty board. You can specify the number of seats at the table - if not provided, a table will be created with 9 seats. A table must have a minimum of 2 seats.
|
315
|
+
|
316
|
+
# Creates a table with 9 seats
|
317
|
+
table = Table.new
|
318
|
+
|
319
|
+
# Creates a table with 6 seats
|
320
|
+
table = Table.new 6
|
321
|
+
|
322
|
+
### Swapping pots, boards and decks
|
323
|
+
|
324
|
+
You can provide a table with different decks, boards and pots. This configuration is chainable, as shown in the example below:
|
325
|
+
|
326
|
+
deck = Deck.new
|
327
|
+
pot = Pot.new
|
328
|
+
board = Board.new
|
329
|
+
…
|
330
|
+
# Creates a table, and configures the table to use the specified board, pot and deck.
|
331
|
+
table = Table.new
|
332
|
+
table.with(deck).with(board).with(pot)
|
333
|
+
|
334
|
+
### Managing players
|
335
|
+
A poker table won't do us any good unless we have a few players sitting in on a game. We can add players to the table using the add method:
|
336
|
+
|
337
|
+
table = Table.new
|
338
|
+
james = Player.new("James")
|
339
|
+
sally = Player.new("Sally")
|
340
|
+
|
341
|
+
# james will automatcially take seat number 1
|
342
|
+
table.add james
|
343
|
+
|
344
|
+
# sally will automatcially take seat number 2
|
345
|
+
table.add sally
|
346
|
+
|
347
|
+
At a real poker table, players are free to sit at any available seat at the table. You can specify which seat a player sits at:
|
348
|
+
|
349
|
+
table = Table.new
|
350
|
+
james = Player.new("James")
|
351
|
+
sally = Player.new("Sally")
|
352
|
+
|
353
|
+
# james will sit at seat number 2
|
354
|
+
table.add james 2
|
355
|
+
|
356
|
+
# sally will sit at seat number 6
|
357
|
+
table.add sally 6
|
358
|
+
|
359
|
+
This index is not zero based. In other words, seat 1 is the first seat, you cannot specify to sit at 'seat 0', there's no such thing! If the specified seat is already taken by another player an exception is raised. If the specified seat doesn't exist (you specify to seat a player at seat 11 at a 9-seated table) and exception will be raised.
|
360
|
+
|
361
|
+
You can remove players by using the tables' remove method:
|
362
|
+
|
363
|
+
# Create a table and a couple of players
|
364
|
+
table = Table.new
|
365
|
+
james = Player.new("James")
|
366
|
+
sally = Player.new("Sally")
|
367
|
+
|
368
|
+
# james will automatcially take seat number 1
|
369
|
+
table.add james
|
370
|
+
|
371
|
+
# james will be removed, seat number 1 will be free
|
372
|
+
table.remove james
|
373
|
+
|
374
|
+
### Inspecting the table
|
375
|
+
|
376
|
+
# Returns the number of seats at the table
|
377
|
+
table.size
|
378
|
+
|
379
|
+
# Determines if all of the seats at the table are occupied
|
380
|
+
table.full?
|
381
|
+
|
382
|
+
# Determines if none of the seats at the table are occupied
|
383
|
+
table.empty?
|
384
|
+
|
385
|
+
# Returns all players sitting at the table as an array
|
386
|
+
table.players
|
387
|
+
|
388
|
+
# Returns seat number 3
|
389
|
+
third_seat = table.seat(3)
|
390
|
+
|
391
|
+
### Poker positions at the table
|
392
|
+
|
393
|
+
There are a few positions that are relevant in a poker game, and the rora api makes it trivial to identify key positions. The dealer, or 'button' is a rotating position - after every hand a new dealer is chosen by moving the 'button' or 'puck' in a clockwise fashion around the table.
|
394
|
+
|
395
|
+
# Returns the seat with the button.
|
396
|
+
table.the_button
|
397
|
+
|
398
|
+
The small blind sits to the immediate left of the dealer, and is required to post one half sized bet before before a hand begins.
|
399
|
+
|
400
|
+
# Returns the seat with the small blind.
|
401
|
+
table.the_small_blind
|
402
|
+
|
403
|
+
# In a heads up (2-player) game, the small blind is also the button!
|
404
|
+
table = Table.new
|
405
|
+
james = Player.new("James")
|
406
|
+
sally = Player.new("Sally")
|
407
|
+
|
408
|
+
puts table.the_button.player.name => 'James'
|
409
|
+
puts table.the_small_blind.player.name => 'James'
|
410
|
+
puts table.the_big_blind.player.name => 'Sally'
|
411
|
+
|
412
|
+
The big blind sits to the immediate left of the small blind, and must post one full sized bet before a hand begins.
|
413
|
+
|
414
|
+
# Returns the seat with the big blind.
|
415
|
+
table.the_big_blind
|
416
|
+
|
417
|
+
UTG sits to the immediate left of the big blind, and is the first player to act in the pre-flop (i.e. first) betting round. The player to the left the big blind is always the first player to act in the preflop betting round. In a heads up (i.e. 2-player game), the 'next' player is our other player, the small blind.
|
418
|
+
|
419
|
+
# Returns the seat that is first to act, or 'under the gun'
|
420
|
+
table.under_the_gun
|
421
|
+
|
422
|
+
# In a heads up (2-player) game, the small blind is also the first to act (under the gun)!
|
423
|
+
table = Table.new
|
424
|
+
james = Player.new("James")
|
425
|
+
sally = Player.new("Sally")
|
426
|
+
|
427
|
+
puts table.the_small_blind.player.name => 'James'
|
428
|
+
puts table.the_big_blind.player.name => 'Sally'
|
429
|
+
puts table.under_the_gun.player.name => 'James'
|
430
|
+
|
431
|
+
|
432
|
+
### Gaps in player positions
|
433
|
+
There is no need for players to sit directly beside each other. Rora maintains an internal linked list of players and their relative positions, so there can be gaps between seated players.
|
434
|
+
|
435
|
+
# Create a table with three players
|
436
|
+
table = Table.new
|
437
|
+
james = Player.new("James")
|
438
|
+
sally = Player.new("Sally")
|
439
|
+
frank = Player.new("Frank")
|
440
|
+
|
441
|
+
# Seats 1, 3, 4, 5 and 7 will be unoccupied, that's ok!
|
442
|
+
table.add james 2
|
443
|
+
table.add sally 6
|
444
|
+
table.add frank 8
|
445
|
+
|
446
|
+
puts table.the_small_blind.player.name => 'James'
|
447
|
+
puts table.the_big_blind.player.name => 'Sally'
|
448
|
+
puts table.under_the_gun.player.name => 'Frank'
|
data/lib/rora/model/board.rb
CHANGED
@@ -65,6 +65,12 @@ class Board
|
|
65
65
|
@river = cd
|
66
66
|
end
|
67
67
|
|
68
|
+
# Returns the number of community cards that have been dealt.
|
69
|
+
def size
|
70
|
+
cards.size
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the community cards that have been dealt
|
68
74
|
def cards
|
69
75
|
cds = Array.new
|
70
76
|
cds += @flop if !@flop.nil?
|
@@ -92,4 +98,8 @@ class Board
|
|
92
98
|
false
|
93
99
|
end
|
94
100
|
|
101
|
+
def to_s
|
102
|
+
"Board: flop=#{@flop.map { |card| "#{card.key}" }.join(",")}"
|
103
|
+
end
|
104
|
+
|
95
105
|
end
|
data/lib/rora/model/card.rb
CHANGED
@@ -37,10 +37,6 @@ class Card
|
|
37
37
|
@rank.key + @suit.key
|
38
38
|
end
|
39
39
|
|
40
|
-
def value
|
41
|
-
@rank.value + @suit.value
|
42
|
-
end
|
43
|
-
|
44
40
|
def name
|
45
41
|
"#{@rank.value} of #{@suit.value}s"
|
46
42
|
end
|
@@ -68,7 +64,7 @@ class Card
|
|
68
64
|
end
|
69
65
|
|
70
66
|
def to_s
|
71
|
-
"Card:
|
67
|
+
"Card: #{name}"
|
72
68
|
end
|
73
69
|
|
74
70
|
end
|
data/lib/rora/model/game.rb
CHANGED
@@ -1,8 +1,76 @@
|
|
1
1
|
class Game
|
2
|
-
attr_reader :table, :
|
2
|
+
attr_reader :table, :buy_in, :started
|
3
3
|
|
4
|
-
def initialize
|
4
|
+
def initialize arguments=nil
|
5
|
+
@table = arguments.nil? ? Table.new : (arguments[:table].nil? ? Table.new : arguments[:table])
|
6
|
+
@buy_in = arguments.nil? ? 50 : (arguments[:buy_in].nil ? 20 : arguments[:buy_in])
|
7
|
+
@log = GameLogger.new
|
8
|
+
@started = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
@started = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def add player
|
16
|
+
raise ArgumentError, "#{player.name} cannot join game, stack of #{player.stack} is less than the buy in amount #{@buy_in}" if player.stack < @buy_in
|
17
|
+
raise ArgumentError, "#{player.name} cannot join game, the game has already started" if @started
|
18
|
+
player.join_game self
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove player
|
22
|
+
player.leave_game
|
23
|
+
end
|
24
|
+
|
25
|
+
def pot
|
26
|
+
@table.pot
|
27
|
+
end
|
28
|
+
|
29
|
+
def shuffle_deck
|
30
|
+
@log.debug("Shuffling the deck")
|
31
|
+
@table.deck.shuffle
|
32
|
+
end
|
33
|
+
|
34
|
+
def assign_button
|
35
|
+
@table.pass_the_buck
|
36
|
+
@log.info("#{@table.the_button.player.name} will be the dealer for this hand");
|
37
|
+
end
|
38
|
+
|
39
|
+
def post_small_blind
|
40
|
+
@table.the_small_blind.player.post_small_blind
|
41
|
+
@log.info("#{@table.the_small_blind.player.name} posts the small blind");
|
42
|
+
end
|
43
|
+
|
44
|
+
def post_big_blind
|
45
|
+
@table.the_big_blind.player.post_big_blind
|
46
|
+
@log.info("#{@table.the_big_blind.player.name} posts the big blind");
|
47
|
+
@log.info("The pot is at #{@table.pot.value}");
|
48
|
+
end
|
49
|
+
|
50
|
+
def deal_pocket_cards
|
51
|
+
@table.players.each do |player|
|
52
|
+
player.add table.deck.deal
|
53
|
+
end
|
54
|
+
@table.players.each do |player|
|
55
|
+
player.add table.deck.deal
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def place_bet amount
|
60
|
+
@table.pot.add amount
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_preflop_betting_round
|
64
|
+
@log.info("-----------------------------------------------------------------")
|
65
|
+
first_to_act = @table.under_the_gun
|
66
|
+
first_to_act.player.act
|
67
|
+
number = first_to_act.number
|
68
|
+
seat_number = @table.the_seat_after(first_to_act).number
|
5
69
|
|
70
|
+
while seat_number != number
|
71
|
+
@table.seat(seat_number).player.act
|
72
|
+
seat_number = @table.the_seat_after(@table.seat(seat_number)).number
|
73
|
+
end
|
6
74
|
end
|
7
75
|
|
8
76
|
end
|
data/lib/rora/model/hand.rb
CHANGED
@@ -10,12 +10,11 @@
|
|
10
10
|
# hand = Hand.new("ACKCJS9H4H")
|
11
11
|
#
|
12
12
|
class Hand
|
13
|
-
attr_reader :cards
|
13
|
+
attr_reader :cards
|
14
14
|
|
15
15
|
def initialize cards
|
16
16
|
@hand_repository = HandRepository.instance
|
17
17
|
@cards = cards.kind_of?(Array) ? cards : Card.to_cards(cards)
|
18
|
-
|
19
18
|
raise ArgumentError, "Exactly 5 cards are required to create a hand, #{cards.size} provided" if @cards.size != 5
|
20
19
|
raise ArgumentError, "The hand contains duplicate cards" if @cards.uniq.length != @cards.length
|
21
20
|
end
|
data/lib/rora/model/player.rb
CHANGED
@@ -1,8 +1,73 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
|
1
|
+
#
|
2
|
+
# A player in a Texas Hold'em game.
|
3
|
+
#
|
4
4
|
class Player
|
5
|
-
|
6
|
-
|
5
|
+
attr_reader :name, :stack, :game
|
6
|
+
|
7
|
+
def initialize name, stack
|
8
|
+
raise ArgumentError, "The player name cannot be nil" if name.nil?
|
9
|
+
@name = name
|
10
|
+
@stack = stack.nil? ? 0 : stack
|
11
|
+
@sitting_out = true
|
12
|
+
@table = nil
|
13
|
+
@cards = []
|
14
|
+
@log = GameLogger.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def join_game game, seat=nil
|
18
|
+
raise ArgumentError, "#{@name} cannot join this game, the table is full" if game.table.full?
|
19
|
+
raise ArgumentError, "#{@name} cannot join this game, this player is already playing another game" if !@game.nil?
|
20
|
+
game.table.add self, seat
|
21
|
+
@game = game
|
22
|
+
end
|
23
|
+
|
24
|
+
def leave_game
|
25
|
+
end_session
|
26
|
+
@game = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def start_session
|
30
|
+
raise ArgumentError, "#{@name} is not sitting at a table" if @game.nil?
|
31
|
+
@sitting_out = false
|
32
|
+
end
|
33
|
+
|
34
|
+
def end_session
|
35
|
+
@sitting_out = true
|
36
|
+
end
|
37
|
+
|
38
|
+
def sitting_out?
|
39
|
+
@sitting_out
|
40
|
+
end
|
41
|
+
|
42
|
+
def add card
|
43
|
+
@cards << card
|
44
|
+
end
|
45
|
+
|
46
|
+
def starting_hand
|
47
|
+
StartingHand.new(@cards)
|
48
|
+
end
|
49
|
+
|
50
|
+
def post_small_blind
|
51
|
+
raise_error_if_sitting_out "cannot post the small blind"
|
52
|
+
@game.place_bet @game.buy_in / 200.00
|
53
|
+
end
|
54
|
+
|
55
|
+
def post_big_blind
|
56
|
+
raise_error_if_sitting_out "cannot post the big blind"
|
57
|
+
@game.place_bet @game.buy_in / 100.00
|
7
58
|
end
|
59
|
+
|
60
|
+
def act
|
61
|
+
raise_error_if_sitting_out "cannot act"
|
62
|
+
action = "folds"
|
63
|
+
@log.info("#{name} #{action}")
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def raise_error_if_sitting_out message
|
69
|
+
raise ArgumentError, "#{name} has not joined a game, #{message}" if game.nil?
|
70
|
+
raise ArgumentError, "#{name} is sitting out, #{message}" if sitting_out?
|
71
|
+
end
|
72
|
+
|
8
73
|
end
|
data/lib/rora/model/pot.rb
CHANGED
@@ -2,20 +2,21 @@
|
|
2
2
|
# The sum of money that players wager during a single hand or game.
|
3
3
|
#
|
4
4
|
class Pot
|
5
|
-
|
6
|
-
attr_reader :total
|
5
|
+
attr_reader :value
|
7
6
|
|
8
7
|
def initialize
|
9
|
-
@
|
8
|
+
@value = 0
|
10
9
|
end
|
11
|
-
|
10
|
+
|
11
|
+
# Adds the specified bet to the pot
|
12
12
|
def add bet
|
13
|
-
|
13
|
+
raise ArgumentError if bet < 0
|
14
|
+
@value += bet
|
14
15
|
self
|
15
16
|
end
|
16
17
|
|
17
18
|
def to_s
|
18
|
-
"Pot: " + sprintf("$%.02f" , @
|
19
|
+
"Pot: " + sprintf("$%.02f" , @value)
|
19
20
|
end
|
20
21
|
|
21
22
|
end
|
data/lib/rora/model/rank.rb
CHANGED
@@ -25,11 +25,11 @@ class Rank
|
|
25
25
|
self.values.each do |rank|
|
26
26
|
return rank if rank.key.casecmp(key) == 0
|
27
27
|
end
|
28
|
-
raise ArgumentError, "No rank exists for key "
|
28
|
+
raise ArgumentError, "No rank exists for key '#{key}'"
|
29
29
|
end
|
30
30
|
|
31
31
|
def to_s
|
32
|
-
"Rank:
|
32
|
+
"Rank: #{@value}"
|
33
33
|
end
|
34
34
|
|
35
35
|
class << self
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# A seat at a poker table.
|
3
|
+
#
|
4
|
+
# Once a table is constructed, each seat at the table will contain a reference to
|
5
|
+
# the seat before it and the seat after it. In effect, the seat implements a very
|
6
|
+
# basic doubly-linked list.
|
7
|
+
#
|
8
|
+
class Seat
|
9
|
+
attr_accessor :prev, :next, :player, :button
|
10
|
+
attr_reader :number
|
11
|
+
|
12
|
+
def initialize number
|
13
|
+
raise ArgumentError, "Must create a seat number with a value of 1 or greater" if number < 1
|
14
|
+
@number = number
|
15
|
+
end
|
16
|
+
|
17
|
+
# Determines whether the seat is taken.
|
18
|
+
def taken?
|
19
|
+
return !@player.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Determines whether this seat has the dealer button.
|
23
|
+
def button?
|
24
|
+
return !button.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"Seat: #{@number}"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|