rholdem 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -0
- data/lib/card.rb +16 -7
- data/lib/deck.rb +6 -2
- data/lib/hand.rb +51 -3
- data/lib/player.rb +1 -1
- data/lib/rholdem.rb +1 -1
- data/spec/card_spec.rb +75 -31
- data/spec/deck_spec.rb +18 -0
- data/spec/hand_spec.rb +73 -0
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.1.0 / 2007-10-07
|
2
|
+
|
3
|
+
* Changed definition of == in Card class to include rank and suit comparison
|
4
|
+
* Removed definition of === in Card class
|
5
|
+
* Changed the way that cards are compared for Hand class per changes above, ignores suit
|
6
|
+
* Added helper methods Hand#outs and Hand#odds for future pot odds calculations
|
7
|
+
|
1
8
|
== 0.0.1 / 2007-09-30
|
2
9
|
|
3
10
|
* First release of library
|
data/lib/card.rb
CHANGED
@@ -47,20 +47,15 @@ module Holdem
|
|
47
47
|
|
48
48
|
def ==(compare_to)
|
49
49
|
if(compare_to.is_a?(Card))
|
50
|
-
return self.rank == compare_to.rank
|
50
|
+
return self.rank == compare_to.rank && self.suit == compare_to.suit
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def ===(compare_to)
|
55
|
-
return false unless self == compare_to
|
56
|
-
return true if self.suit == compare_to.suit
|
57
|
-
false
|
58
|
-
end
|
59
|
-
|
60
54
|
def <=>(compare_to)
|
61
55
|
if(compare_to.is_a?(Card))
|
62
56
|
return 0 if self == compare_to
|
63
57
|
return -1 if self.rank < compare_to.rank
|
58
|
+
return -1 if self.rank == compare_to.rank && self.suit < compare_to.suit
|
64
59
|
return 1
|
65
60
|
end
|
66
61
|
end
|
@@ -73,5 +68,19 @@ module Holdem
|
|
73
68
|
def <(compare_to)
|
74
69
|
return compare_to > self
|
75
70
|
end
|
71
|
+
|
72
|
+
def equiv?(compare_to)
|
73
|
+
return true if self.rank == compare_to.rank
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
def higher?(compare_to)
|
78
|
+
return true if self.rank > compare_to.rank
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
def lower?(compare_to)
|
83
|
+
return compare_to.higher?(self)
|
84
|
+
end
|
76
85
|
end
|
77
86
|
end
|
data/lib/deck.rb
CHANGED
@@ -8,7 +8,7 @@ module Holdem
|
|
8
8
|
@cards = @cards.sort_by { rand }
|
9
9
|
end
|
10
10
|
|
11
|
-
def initialize
|
11
|
+
def initialize(shuffle_cards=true)
|
12
12
|
@cards = []
|
13
13
|
Card::SUITS.each_byte do |suit|
|
14
14
|
Card::RANKS.each_byte do |rank|
|
@@ -16,11 +16,15 @@ module Holdem
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
shuffle
|
19
|
+
shuffle if shuffle_cards
|
20
20
|
end
|
21
21
|
|
22
22
|
def deal
|
23
23
|
@cards.pop
|
24
24
|
end
|
25
|
+
|
26
|
+
def remove(card)
|
27
|
+
@cards.delete_at(@cards.index(card))
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
data/lib/hand.rb
CHANGED
@@ -70,13 +70,61 @@ module Holdem
|
|
70
70
|
# otherwise must compare by cards.
|
71
71
|
i = @sorted_cards.length-1
|
72
72
|
while(i >= 0)
|
73
|
-
return 1 if @sorted_cards[i]
|
74
|
-
return -1 if @sorted_cards[i]
|
73
|
+
return 1 if @sorted_cards[i].higher?(compare_to.sorted_cards[i])
|
74
|
+
return -1 if @sorted_cards[i].lower?(compare_to.sorted_cards[i])
|
75
75
|
i -= 1
|
76
76
|
end
|
77
77
|
|
78
78
|
return 0
|
79
79
|
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Information Calculators
|
83
|
+
#
|
84
|
+
|
85
|
+
def outs
|
86
|
+
return nil if @cards.length >= 7
|
87
|
+
|
88
|
+
deck = Deck.new(false)
|
89
|
+
@cards.each { |c| deck.remove(c) }
|
90
|
+
unknown_cards = deck.cards.length
|
91
|
+
results = Hash.new
|
92
|
+
|
93
|
+
deck.cards.length.times do
|
94
|
+
card = deck.deal
|
95
|
+
new_hand = Hand.new(@cards + [card])
|
96
|
+
if(RANKINGS.index(new_hand.ranking) < RANKINGS.index(self.ranking))
|
97
|
+
# an improved hand... find ranking difference
|
98
|
+
results[new_hand.ranking] ||= 0
|
99
|
+
results[new_hand.ranking] += 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
results
|
104
|
+
end
|
105
|
+
|
106
|
+
def odds
|
107
|
+
odds = outs
|
108
|
+
return nil if odds.nil?
|
109
|
+
|
110
|
+
unknown_cards = 52 - @cards.length
|
111
|
+
odds.each_key do |key|
|
112
|
+
odds[key] = (odds[key].to_f / unknown_cards.to_f)
|
113
|
+
end
|
114
|
+
|
115
|
+
odds
|
116
|
+
end
|
117
|
+
|
118
|
+
def on_board?(*hole_cards)
|
119
|
+
hole_cards.flatten!
|
120
|
+
raise "Need to receive 2 hole cards, got #{hole_cards.lenth}" if hole_cards.length != 2
|
121
|
+
unless hole_cards[0].is_a?(Card) && hole_cards[0].is_a?(Card)
|
122
|
+
raise "Need to receive Card objects, got #{hole_cards[0].class} and #{hole_cards[0].class}"
|
123
|
+
end
|
124
|
+
|
125
|
+
return false if @sorted_cards.include?(hole_cards[0]) || @sorted_cards.include?(hole_cards[1])
|
126
|
+
true
|
127
|
+
end
|
80
128
|
|
81
129
|
private
|
82
130
|
|
@@ -106,7 +154,7 @@ module Holdem
|
|
106
154
|
update_ranking(new_rank, cards)
|
107
155
|
elsif(RANKINGS.index(new_rank) == RANKINGS.index(@ranking))
|
108
156
|
(0...cards.length).each do |i|
|
109
|
-
if(cards[i].
|
157
|
+
if(cards[i].higher?(@sorted_cards[i]))
|
110
158
|
update_ranking(new_rank, cards)
|
111
159
|
break
|
112
160
|
end
|
data/lib/player.rb
CHANGED
data/lib/rholdem.rb
CHANGED
data/spec/card_spec.rb
CHANGED
@@ -34,15 +34,13 @@ describe Card do
|
|
34
34
|
c2 = Card.new('3c')
|
35
35
|
|
36
36
|
c1.should == c2
|
37
|
-
c1.should eql?(c2)
|
38
37
|
end
|
39
38
|
|
40
|
-
it "should evaluate two cards as equal if their ranks match" do
|
39
|
+
it "should not evaluate two cards as equal if their ranks match" do
|
41
40
|
c1 = Card.new('3d')
|
42
41
|
c2 = Card.new('3c')
|
43
42
|
|
44
|
-
c1.
|
45
|
-
c1.should eql?(c2)
|
43
|
+
c1.should_not == c2
|
46
44
|
end
|
47
45
|
|
48
46
|
it "should evaluate two cards as not equal if their ranks don't match" do
|
@@ -51,45 +49,91 @@ describe Card do
|
|
51
49
|
c1.should_not == c2
|
52
50
|
end
|
53
51
|
|
54
|
-
it "should
|
55
|
-
c1 = Card.new('3d')
|
56
|
-
c2 = Card.new('3d')
|
57
|
-
c1.should === c2
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should evaluate two cards as not === if their suits don't match" do
|
61
|
-
c1 = Card.new('3d')
|
62
|
-
c2 = Card.new('3h')
|
63
|
-
c1.should_not === c2
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should evaluate two cards as not === if their ranks don't match" do
|
67
|
-
c1 = Card.new('5d')
|
68
|
-
c2 = Card.new('3d')
|
69
|
-
c1.should_not === c2
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should return -1 when comparing lower to higher card" do
|
52
|
+
it "should return < when comparing lower to higher card" do
|
73
53
|
c1 = Card.new('3c')
|
74
54
|
c2 = Card.new('4d')
|
75
|
-
(c1 <=> c2).should == -1
|
76
55
|
(c1 > c2).should == false
|
77
56
|
(c1 < c2).should == true
|
78
57
|
end
|
79
58
|
|
80
|
-
it "should return
|
59
|
+
it "should return > when comparing higher to lower card" do
|
81
60
|
c1 = Card.new('8c')
|
82
61
|
c2 = Card.new('4d')
|
83
|
-
(c1 <=> c2).should == 1
|
84
62
|
(c1 > c2).should == true
|
85
63
|
(c1 < c2).should == false
|
86
64
|
end
|
87
65
|
|
88
|
-
it "should
|
89
|
-
c1 = Card.new('
|
90
|
-
c2 = Card.new('
|
66
|
+
it "should evaluate <=> as 1 if card 1 is greater than card 2 by rank" do
|
67
|
+
c1 = Card.new('4s')
|
68
|
+
c2 = Card.new('3c')
|
69
|
+
(c1 <=> c2).should == 1
|
70
|
+
(c2 <=> c1).should == -1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should evaluate <=> as 1 if card 1 is greater than card 2 by suit with equal ranks" do
|
74
|
+
c1 = Card.new('3s')
|
75
|
+
c2 = Card.new('3c')
|
76
|
+
(c1 <=> c2).should == 1
|
77
|
+
(c2 <=> c1).should == -1
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should evaluate <=> as 0 if card 1 has the same rank and suit as card 2" do
|
81
|
+
c1 = Card.new('3c')
|
82
|
+
c2 = Card.new('3c')
|
91
83
|
(c1 <=> c2).should == 0
|
92
|
-
|
93
|
-
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should return evaluate equiv as true if two cards have the same rank and suit" do
|
87
|
+
c1 = Card.new('Ac')
|
88
|
+
c2 = Card.new('Ac')
|
89
|
+
c1.should be_equiv(c2)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return evaluate equiv as true if two cards have the same rank and different suits" do
|
93
|
+
c1 = Card.new('Ac')
|
94
|
+
c2 = Card.new('As')
|
95
|
+
c1.should be_equiv(c2)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should return evaluate equiv as false if two cards do not have the same rank and suit" do
|
99
|
+
c1 = Card.new('Ac')
|
100
|
+
c2 = Card.new('Kc')
|
101
|
+
c1.should_not be_equiv(c2)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return higher when rank is higher" do
|
105
|
+
c1 = Card.new('Kc')
|
106
|
+
c2 = Card.new('Qs')
|
107
|
+
c1.should be_higher(c2)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should not return higher when rank is same" do
|
111
|
+
c1 = Card.new('Tc')
|
112
|
+
c2 = Card.new('Ts')
|
113
|
+
c1.should_not be_higher(c2)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should not return higher when rank is lower" do
|
117
|
+
c1 = Card.new('Tc')
|
118
|
+
c2 = Card.new('2s')
|
119
|
+
c2.should_not be_higher(c1)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should return lower when rank is lower" do
|
123
|
+
c1 = Card.new('Kc')
|
124
|
+
c2 = Card.new('Qs')
|
125
|
+
c2.should be_lower(c1)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should not return lower when rank is same" do
|
129
|
+
c1 = Card.new('Tc')
|
130
|
+
c2 = Card.new('Ts')
|
131
|
+
c1.should_not be_lower(c2)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should not return lower when rank is higher" do
|
135
|
+
c1 = Card.new('Tc')
|
136
|
+
c2 = Card.new('2s')
|
137
|
+
c1.should_not be_lower(c2)
|
94
138
|
end
|
95
139
|
end
|
data/spec/deck_spec.rb
CHANGED
@@ -50,4 +50,22 @@ describe Deck do
|
|
50
50
|
@d.deal
|
51
51
|
@d.cards.length.should == 51
|
52
52
|
end
|
53
|
+
|
54
|
+
it "should remove a card from the deck" do
|
55
|
+
card = Card.new('As')
|
56
|
+
@d.remove(card)
|
57
|
+
@d.cards.each { |c| c.should_not === card }
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should return an unshuffled deck if desired" do
|
61
|
+
ordered_cards = []
|
62
|
+
Card::SUITS.each_byte do |suit|
|
63
|
+
Card::RANKS.each_byte do |rank|
|
64
|
+
ordered_cards << Card.new(rank.chr + suit.chr)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
deck = Deck.new(false)
|
69
|
+
deck.cards.should == ordered_cards
|
70
|
+
end
|
53
71
|
end
|
data/spec/hand_spec.rb
CHANGED
@@ -347,4 +347,77 @@ describe Hand, "comparing to other hands" do
|
|
347
347
|
(h1 < h2).should be_false
|
348
348
|
(h2 < h1).should be_true
|
349
349
|
end
|
350
|
+
|
351
|
+
it "should not compare by suit" do
|
352
|
+
h1 = Hand.new('Ac Kc Qc Jc Tc')
|
353
|
+
h2 = Hand.new('As Ks Qs Js Ts')
|
354
|
+
h1.should == h2
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
describe Hand, "odds calculations" do
|
359
|
+
it "should compute numer of improving cards as a ranking => num 'outs' hash" do
|
360
|
+
# flush: any available club gives a flush, 9 cards
|
361
|
+
# straight: any 6 (not 6c) gives a straight, 3 cards
|
362
|
+
# pair: any 3, 4, 5, 7 (not 7c), 8 gives a pair, 14 cards
|
363
|
+
# 5 known cards, 47 unknown cards
|
364
|
+
h = Hand.new('3c 4c 5c 7s 8c')
|
365
|
+
|
366
|
+
outs = h.outs
|
367
|
+
outs[:pair].should == 14
|
368
|
+
outs[:straight].should == 3
|
369
|
+
outs[:flush].should == 9
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should be able to give outs for hands under 5 cards" do
|
373
|
+
h = Hand.new('Ac As')
|
374
|
+
lambda { h.outs }.should_not raise_error
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should have nil outs if a hand is not possible" do
|
378
|
+
h = Hand.new('3h 4d 6c Ts Kh')
|
379
|
+
h.outs[:flush].should be_nil
|
380
|
+
end
|
381
|
+
|
382
|
+
it "should return nil 'outs' if there are no cards to come" do
|
383
|
+
h = Hand.new('3c 4c 5s 6s 7s 8h 9h')
|
384
|
+
h.outs.should be_nil
|
385
|
+
end
|
386
|
+
|
387
|
+
it "should figure out the odds of hands to come" do
|
388
|
+
h = Hand.new('Kc Jd Qs Qh Ks Tc')
|
389
|
+
|
390
|
+
odds = h.odds
|
391
|
+
odds[:full_house].should == 4.0/46
|
392
|
+
odds[:straight].should == 8.0/46
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should have nil odds if there are no outs for hand" do
|
396
|
+
h = Hand.new('3c 4s')
|
397
|
+
h.odds[:flush].should be_nil
|
398
|
+
end
|
399
|
+
|
400
|
+
it "should return nil for pot odds if there are no cards to come" do
|
401
|
+
h = Hand.new('3c 4c 5s 6s 7s 8h 9h')
|
402
|
+
h.odds.should be_nil
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
describe Hand, "knowledge about hand" do
|
407
|
+
it "should know if best hand is made of at least one hole card" do
|
408
|
+
h = Hand.new('6c 7c 8c 9c Tc Jh Ks')
|
409
|
+
h.on_board?(Card.new('9c'), Card.new('Ks')).should be_false
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should know when a hand is on the board and requires no hole cards" do
|
413
|
+
h = Hand.new('6c 7c 8c 9c Tc Jc Ks')
|
414
|
+
h.on_board?(Card.new('6c'), Card.new('Ks')).should be_true
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should raise an error unless receiving two hole cards" do
|
418
|
+
h = Hand.new('6c 7c 8c 9c Tc Jh Ks')
|
419
|
+
lambda { h.on_board?(Card.new('9c')) }.should raise_error
|
420
|
+
lambda { h.on_board?([Card.new('9c')]) }.should raise_error
|
421
|
+
lambda { h.on_board?(Object.new, Object.new) }.should raise_error
|
422
|
+
end
|
350
423
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rholdem
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date: 2007-
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-10-07 00:00:00 -07:00
|
8
8
|
summary: Ruby Texas Hold'em Simulations Library
|
9
9
|
require_paths:
|
10
10
|
- lib
|