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 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] > compare_to.sorted_cards[i]
74
- return -1 if @sorted_cards[i] < compare_to.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].rank > @sorted_cards[i].rank)
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
@@ -34,7 +34,7 @@ module Holdem
34
34
  def award(amount)
35
35
  @delta += amount
36
36
  end
37
-
37
+
38
38
  # override this for subclass Players
39
39
  def act(game)
40
40
  return :neutral
data/lib/rholdem.rb CHANGED
@@ -5,5 +5,5 @@ require File.dirname(__FILE__) + "/player"
5
5
  require File.dirname(__FILE__) + "/game"
6
6
 
7
7
  module Holdem
8
- VERSION = '0.0.1'
8
+ VERSION = '0.1.0'
9
9
  end
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.should == c2
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 evaluate two cards as === if their suits and ranks match" do
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 1 when comparing higher to lower card" do
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 return 0 when cards are equal" do
89
- c1 = Card.new('8c')
90
- c2 = Card.new('8d')
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
- (c1 > c2).should == false
93
- (c1 < c2).should == false
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.1
7
- date: 2007-09-30 00:00:00 -07:00
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