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 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