rholdem 0.0.1 → 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/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
|