pokerstats 2.0.13 → 2.0.14

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.
@@ -0,0 +1,94 @@
1
+ module Pokerstats
2
+ class StreetStatistics < HandStatistics::Plugin
3
+
4
+ def self.report_specification
5
+ [
6
+ # [key, sql_type, function]
7
+ [:saw_flop, 'boolean', :saw_flop],
8
+ [:saw_turn, 'boolean', :saw_turn],
9
+ [:saw_river, 'boolean', :saw_river],
10
+ [:saw_showdown, 'boolean', :saw_showdown],
11
+ [:won_preflop, 'boolean', :won_preflop],
12
+ [:won_flop, 'boolean', :won_flop],
13
+ [:won_turn, 'boolean', :won_turn],
14
+ [:won_river, 'boolean', :won_river],
15
+ [:won_showdown, 'boolean', :won_showdown]
16
+ ]
17
+ end
18
+
19
+ def initialize handstatistics
20
+ super handstatistics
21
+ @saw = {}
22
+ @won = {}
23
+ end
24
+
25
+ def saw_flop player
26
+ @saw[player] && @saw[player][:flop]
27
+ end
28
+
29
+ def saw_turn player
30
+ @saw[player] && @saw[player][:turn]
31
+ end
32
+
33
+ def saw_river player
34
+ @saw[player] && @saw[player][:river]
35
+ end
36
+
37
+ def saw_showdown player
38
+ @saw[player] && @saw[player][:showdown]
39
+ end
40
+
41
+ def won_preflop player
42
+ @won[player] && @won[player][:preflop]
43
+ end
44
+
45
+ def won_flop player
46
+ @won[player] && @won[player][:flop]
47
+ end
48
+
49
+ def won_turn player
50
+ @won[player] && @won[player][:turn]
51
+ end
52
+
53
+ def won_river player
54
+ @won[player] && @won[player][:river]
55
+ end
56
+
57
+ def won_showdown player
58
+ @won[player] && @won[player][:showdown]
59
+ end
60
+
61
+ def register_player screen_name, street, player
62
+ @saw[screen_name] = {}
63
+ @won[screen_name] = {}
64
+ end
65
+
66
+ def street_transition street
67
+ end
68
+
69
+ def street_transition_for_player street, player
70
+ end
71
+
72
+ def apply_action action, street
73
+ # saw_* statistics for ftrs
74
+ screen_name = action[:screen_name]
75
+ if [:flop, :turn, :river, :showdown].include? street
76
+ @saw[screen_name][street] = true
77
+ end
78
+ # won_* statistics for pftrs
79
+ if [:preflop, :flop, :turn, :river, :showdown].include? street
80
+ if action[:result] == :win
81
+ @won[screen_name][street] = true
82
+ if street == :showdown
83
+ @won[screen_name][@hand_statistics.last_street] = true
84
+ end
85
+ elsif @won[screen_name][street].nil?
86
+ @won[screen_name][street] = false
87
+ if street == :showdown
88
+ @won[screen_name][@hand_statistics.last_street] = false
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,9 +1,57 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/hand_constants')
2
2
  require File.expand_path(File.dirname(__FILE__) + '/hand_statistics')
3
+ require 'tzinfo'
3
4
 
4
5
  module Pokerstats
5
6
  CASH = "[0-9$,.]+"
7
+
8
+ class PokerstarsTimeStringConverter
9
+ attr_accessor :timestring
10
+
11
+ def initialize(timestring)
12
+ @timestring = timestring
13
+ parse_timestring
14
+ self
15
+ end
16
+
17
+ def as_utc_datetime
18
+ time_converter.local_to_utc(DateTime.parse(@timestring))
19
+ end
6
20
 
21
+ private
22
+
23
+ def parse_timestring
24
+ if @timestring =~ /^(.*)(UTC|PT|MT|CT|ET|AT|BRT|WET|CET|EET|MSK|CCT|JST|AWST|ACST|AEST|NZT)$/
25
+ @localtime = $1
26
+ @ps_timezone_string = $2
27
+ else
28
+ raise ArgumentError, "time string does not end with a valid Pokerstars time zone suffix"
29
+ end
30
+ end
31
+
32
+ def time_converter
33
+ case @ps_timezone_string
34
+ when "ACST" then TZInfo::Timezone.get('Australia/Adelaide')
35
+ when "AEST" then TZInfo::Timezone.get('Australia/Melbourne')
36
+ when "AT" then TZInfo::Timezone.get('Canada/Atlantic')
37
+ when "AWST" then TZInfo::Timezone.get('Australia/West')
38
+ when "BRT" then TZInfo::Timezone.get('Brazil/East')
39
+ when "CCT" then TZInfo::Timezone.get('Indian/Cocos')
40
+ when "CET" then TZInfo::Timezone.get('Europe/Amsterdam')
41
+ when "CT" then TZInfo::Timezone.get('US/Central')
42
+ when "ET" then TZInfo::Timezone.get('US/Eastern')
43
+ when "EET" then TZInfo::Timezone.get('EET')
44
+ when "JST" then TZInfo::Timezone.get('Japan')
45
+ when "MSK" then TZInfo::Timezone.get('Europe/Moscow')
46
+ when "MT" then TZInfo::Timezone.get('US/Mountain')
47
+ when "NZT" then TZInfo::Timezone.get('NZ')
48
+ when "PT" then TZInfo::Timezone.get('US/Pacific')
49
+ when "UTC" then TZInfo::Timezone.get('UTC')
50
+ when "WET" then TZInfo::Timezone.get('WET')
51
+ end
52
+ end
53
+ end
54
+
7
55
  class HandHistoryParseError < RuntimeError
8
56
  end
9
57
  class PokerstarsHandHistoryParser
@@ -36,16 +84,15 @@ module Pokerstats
36
84
 
37
85
  def ignorable?(line)
38
86
  regular_expressions_for_ignorable_phrases = [
39
- /(.*): doesn't show hand/,
40
87
  /(.*) has timed out/,
41
88
  /(.*) has returned/,
42
89
  /(.*) leaves the table/,
43
90
  /(.*) joins the table at seat #[0-9]+/,
44
91
  /(.*) sits out/,
45
- /(.*) mucks hand/,
46
92
  /(.*) is sitting out/,
47
93
  /(.*) is (dis)?connected/,
48
94
  /(.*) said,/,
95
+ /(.*): doesn't show hand/,
49
96
  /(.*) will be allowed to play after the button/,
50
97
  /(.*) was removed from the table for failing to post/,
51
98
  /(.*) re-buys and receives (.*) chips for (.*)/,
@@ -61,12 +108,24 @@ module Pokerstats
61
108
 
62
109
  def parse(line)
63
110
  case line
64
- when /PokerStars Game #([0-9]+): Tournament #([0-9]+), (\$[0-9+$]+) ([^\-]*) - Level ([IVXL]+) \((#{CASH})\/(#{CASH})\) - (.*)$/
65
- @stats.update_hand :name => "PS#{$1}", :description=> "#{$2}, #{$3} #{$4}", :tournament=> $2, :sb=> $6.to_d, :bb=> $7.to_d, :played_at=> Time.parse($8), :street => :prelude, :board => ""
66
- when /PokerStars Game #([0-9]+): +([^(]*) \((#{CASH})\/(#{CASH})\) - (.*)$/
67
- @stats.update_hand :name => "PS#{$1}", :description=> "#{$2} (#{$3}/#{$4})", :tournament=> nil, :sb=> cash_to_d($3), :bb=> cash_to_d($4), :played_at=> Time.parse($5), :street => :prelude, :board => ""
68
- when /PokerStars Game #([0-9]+): +([^(]*) \((#{CASH})\/(#{CASH}) USD\) - (.*)$/
69
- @stats.update_hand :name => "PS#{$1}", :description=> "#{$2} (#{$3}/#{$4})", :tournament=> nil, :sb=> cash_to_d($3), :bb=> cash_to_d($4), :played_at=> Time.parse($5), :street => :prelude, :board => ""
111
+ when /PokerStars Game #([0-9]+): Tournament #([0-9]+), (#{CASH})\+(#{CASH}) Hold'em No Limit - Level ([IVXL]+) \((#{CASH})\/(#{CASH})\) - (.*)$/
112
+ @stats.update_hand :name => "PS#{$1}", :description=> "#{$2}, #{$3}+#{$4} Hold'em No Limit", :tournament=> $2,
113
+ :sb=> $6.to_d, :bb=> $7.to_d, :ante => "0.0".to_d,
114
+ :played_at=> PokerstarsTimeStringConverter.new($8).as_utc_datetime,
115
+ :street => :prelude, :board => "", :max_players => 0, :number_players => 0, :table_name => "",
116
+ :game_type => "Hold'em", :limit_type => "No Limit", :stakes_type => cash_to_d($3)
117
+ when /PokerStars Game #([0-9]+): +([^(]*)Hold'em No Limit \((#{CASH})\/(#{CASH})\) - (.*)$/
118
+ @stats.update_hand :name => "PS#{$1}", :description=> "#{$2}Hold'em No Limit (#{$3}/#{$4})", :tournament=> nil,
119
+ :sb=> cash_to_d($3), :bb=> cash_to_d($4), :ante => "0.0".to_d,
120
+ :played_at=> PokerstarsTimeStringConverter.new($5).as_utc_datetime,
121
+ :street => :prelude, :board => "", :max_players => 0, :number_players => 0, :table_name => "",
122
+ :game_type => "Hold'em", :limit_type => "No Limit", :stakes_type => cash_to_d($4)
123
+ when /PokerStars Game #([0-9]+): +([^(]*)Hold'em No Limit \((#{CASH})\/(#{CASH}) USD\) - (.*)$/
124
+ @stats.update_hand :name => "PS#{$1}", :description=> "#{$2}Hold'em No LImit (#{$3}/#{$4})", :tournament=> nil,
125
+ :sb=> cash_to_d($3), :bb=> cash_to_d($4), :ante => "0.0".to_d,
126
+ :played_at=> PokerstarsTimeStringConverter.new($5).as_utc_datetime,
127
+ :street => :prelude, :board => "", :max_players => 0, :number_players => 0, :table_name => "",
128
+ :game_type => "Hold'em", :limit_type => "No Limit", :stakes_type => cash_to_d($4)
70
129
  when /PokerStars Game #([0-9]+):/
71
130
  raise HandHistoryParseError, "invalid hand record: #{line}"
72
131
  when /\*\*\* HOLE CARDS \*\*\*/
@@ -92,16 +151,18 @@ module Pokerstats
92
151
  @stats.update_hand(:total_pot => cash_to_d($1), :rake => cash_to_d($8))
93
152
  when /Total pot (#{CASH}) Main pot (#{CASH}).( Side pot-[0-9]+ (#{CASH}).)* | Rake (#{CASH})/
94
153
  raise HandHistoryParseError, "popo!"
95
- when /Seat ([0-9]+): (.+) \(#{CASH} in chips\)( is sitting out)?/
96
- @stats.register_player(:seat => $1.to_i, :screen_name => $2)
154
+ when /Seat ([0-9]+): (.+) \((#{CASH}) in chips\)( is sitting out)?/
155
+ @stats.register_player(:seat => $1.to_i, :screen_name => $2, :starting_stack => cash_to_d($3))
97
156
  when /(.*): posts ((small)|(big)|(small \& big)) blind(s)? (#{CASH})/
98
157
  @stats.register_action($1, 'posts', :result => :post, :amount => cash_to_d($7))
99
158
  when /(.*): posts the ante (#{CASH})/
100
159
  @stats.register_action($1, 'antes', :result => :post, :amount => cash_to_d($2))
101
- when /Table '([0-9]+) ([0-9]+)' (.*) Seat #([0-9]+) is the button/
102
- @stats.register_button($4.to_i)
103
- when /Table '(.*)' (.*) Seat #([0-9]+) is the button/
160
+ @stats.update_hand(:ante => [cash_to_d($2), @stats.hand_information(:ante)].max)
161
+ # when /Table '([0-9]+) ([0-9]+)' ([0-9]+)-max Seat #([0-9]+) is the button/
162
+ # @stats.register_button($4.to_i)
163
+ when /Table '(.*)' ([0-9]+)-max Seat #([0-9]+) is the button/
104
164
  @stats.register_button($3.to_i)
165
+ @stats.update_hand(:table_name => $1, :max_players => $2.to_i)
105
166
  when /Uncalled bet \((.*)\) returned to (.*)/
106
167
  @stats.register_action($2, 'return', :result => :win, :amount => cash_to_d($1))
107
168
  when /(.+): ((folds)|(checks))/
@@ -112,6 +173,8 @@ module Pokerstats
112
173
  @stats.register_action($1, 'raises', :result => :pay_to, :amount => cash_to_d($3))
113
174
  when /(.*) collected (.*) from ((side )|(main ))?pot/
114
175
  @stats.register_action($1, "wins", :result => :win, :amount => cash_to_d($2))
176
+ when /(.*) mucks hand/
177
+ @stats.register_action($1, 'mucks', :result => :neutral)
115
178
  when /Seat [0-9]+: (.*) (\((small blind)|(big blind)|(button)\) )?showed \[([^\]]+)\] and ((won) \(#{CASH}\)|(lost)) with (.*)/
116
179
  when /Seat [0-9]+: (.*) mucked \[([^\]]+)\]/
117
180
  else
data/pokerstats.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{pokerstats}
8
- s.version = "2.0.13"
8
+ s.version = "2.0.14"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Andrew C. Greenberg"]
12
- s.date = %q{2009-11-09}
12
+ s.date = %q{2009-11-18}
13
13
  s.default_executable = %q{checkps}
14
14
  s.description = %q{a library for extracting, computing and reporting statistics of poker hands parsed from hand history files}
15
15
  s.email = %q{wizardwerdna@gmail.com}
@@ -42,6 +42,8 @@ Gem::Specification.new do |s|
42
42
  "lib/pokerstats/plugins/cash_statistics.rb",
43
43
  "lib/pokerstats/plugins/continuation_bet_statistics.rb",
44
44
  "lib/pokerstats/plugins/preflop_raise_statistics.rb",
45
+ "lib/pokerstats/plugins/street_bet_statistics.rb",
46
+ "lib/pokerstats/plugins/street_statistics.rb",
45
47
  "lib/pokerstats/poker-edge.rb",
46
48
  "lib/pokerstats/pokerstars_file.rb",
47
49
  "lib/pokerstats/pokerstars_hand_history_parser.rb",
@@ -23,838 +23,1834 @@ Spec::Matchers.define :have_all_and_only_keys do |keylist|
23
23
  end
24
24
  end
25
25
 
26
- describe HandStatistics, "when created" do
27
- before(:each) do
28
- @stats = HandStatistics.new
29
- end
30
-
31
- it "should return an empty player list" do
32
- @stats.should have(0).players
33
- end
34
-
35
- it "should properly chain updates of hand record information" do
36
- sample_hand.each{|key, value| @stats.update_hand(key => value)}
37
- @stats.hand_record.should == sample_hand
38
- end
39
-
40
- it "should not complain when asked to generate hand record with all HAND_INFORMATION_KEYS filled out" do
41
- @stats.update_hand(sample_hand)
42
- lambda{@stats.hand_record}.should_not raise_error(/#{HAND_RECORD_INCOMPLETE_MESSAGE}/)
43
- end
44
-
45
- it "should complain when asked to generate hand record if no information is given" do
46
- lambda{@stats.hand_record}.should raise_error(/#{HAND_RECORD_INCOMPLETE_MESSAGE}/)
47
- end
48
-
49
- HandStatistics::HAND_INFORMATION_KEYS.each do |thing|
50
- it "should complain when asked to generate hand record without a #{thing.to_s}" do
51
- @stats.update_hand(sample_hand.except(thing))
52
- lambda{@stats.hand_record}.should raise_error(/#{HAND_RECORD_INCOMPLETE_MESSAGE}/)
26
+ Spec::Matchers.define :have_won_street_statistics_specified_by do |hash_of_hashes|
27
+ match do |hand_statistics|
28
+ @errors = []
29
+ hash_of_hashes.keys.each do |match_player|
30
+ hash_of_hashes[match_player].keys.each do |match_street|
31
+ expected_result = hash_of_hashes[match_player][match_street]
32
+ actual_result = case match_street
33
+ when :preflop then hand_statistics.won_preflop(match_player)
34
+ when :flop then hand_statistics.won_flop(match_player)
35
+ when :turn then hand_statistics.won_turn(match_player)
36
+ when :river then hand_statistics.won_river(match_player)
37
+ when :showdown then hand_statistics.won_showdown(match_player)
38
+ end
39
+ unless actual_result == expected_result
40
+ @errors << {:player => match_player, :street => match_street, :expected => expected_result, :actual => actual_result}
41
+ end
42
+ end
43
+ end
44
+ @errors.empty?
45
+ end
46
+ failure_message_for_should do |hand_statistics|
47
+ @errors.collect do |each|
48
+ "expected won_#{each[:street]}(#{each[:player].inspect}) to be #{each[:expected].inspect}, obtained #{each[:actual].inspect}"
49
+ end.join("; \n")
50
+ end
51
+ failure_message_for_should_not do |hand_statistics|
52
+ "the actual results conformed to the expected results"
53
+ end
54
+ description do
55
+ "have conforming won_street statistics"
53
56
  end
54
- end
55
-
56
- it "should not produce hand records having extra keys" do
57
- @stats.update_hand(sample_hand)
58
- @stats.update_hand(:street => :river)
59
- @stats.hand_record.should have_all_and_only_keys HAND_INFORMATION_KEYS
60
- end
61
57
  end
62
58
 
63
- describe HandStatistics, "when registering game activity" do
59
+ Spec::Matchers.define :have_saw_street_statistics_specified_by do |hash_of_hashes|
60
+ match do |hand_statistics|
61
+ @errors = []
62
+ hash_of_hashes.keys.each do |match_player|
63
+ hash_of_hashes[match_player].keys.each do |match_street|
64
+ expected_result = hash_of_hashes[match_player][match_street]
65
+ actual_result = case match_street
66
+ when :flop then hand_statistics.saw_flop(match_player)
67
+ when :turn then hand_statistics.saw_turn(match_player)
68
+ when :river then hand_statistics.saw_river(match_player)
69
+ when :showdown then hand_statistics.saw_showdown(match_player)
70
+ end
71
+ unless actual_result == expected_result
72
+ @errors << {:player => match_player, :street => match_street, :expected => expected_result, :actual => actual_result}
73
+ end
74
+ end
75
+ end
76
+ @errors.empty?
77
+ end
78
+ failure_message_for_should do |hand_statistics|
79
+ @errors.collect do |each|
80
+ "expected saw_#{each[:street]}(#{each[:player].inspect}) to be #{each[:expected].inspect}, obtained #{each[:actual].inspect}"
81
+ end.join("; \n")
82
+ end
83
+ failure_message_for_should_not do |hand_statistics|
84
+ "the actual results conformed to the expected results"
85
+ end
86
+ description do
87
+ "have conforming saw_street statistics"
88
+ end
89
+ end
64
90
 
65
- before(:each) do
66
- @stats = HandStatistics.new
67
- end
91
+ def bet_statistics_proc_symbol(street, bet, prefix = "")
92
+ "#{prefix}#{street}_#{bet}bet".to_s
93
+ end
68
94
 
69
- it "should allow you to register a player" do
70
- @stats.register_player sample_player
71
- @stats.should have(1).player_records_without_validation
72
- @stats.player_records_without_validation.first[:screen_name].should == sample_player[:screen_name]
73
- @stats.player_records_without_validation.first[:seat].should == sample_player[:seat]
74
- end
75
-
76
- it "should complain when registering a player twice" do
77
- @stats.register_player sample_player
78
- lambda{@stats.register_player sample_player}.should raise_error(/#{PLAYER_RECORDS_DUPLICATE_PLAYER_NAME}/)
79
- end
80
-
81
- it "should allow you to register a button" do
82
- @stats.button.should be_nil
83
- @stats.register_button(3)
84
- @stats.button.should == 3
85
- end
86
-
87
- it "should complain when registering action for an unregistered player" do
88
- lambda {
89
- @stats.register_action sample_action[:screen_name], sample_action[:action], sample_action
90
- }.should raise_error(/#{PLAYER_RECORDS_UNREGISTERED_PLAYER}/)
91
- end
95
+ Spec::Matchers.define :have_street_bet_statistics_specified_by do |street_hash, prefix|
96
+ match do |hand_statistics|
97
+ @errors = []
98
+ for match_street in street_hash.keys
99
+ for match_bet in 1..4
100
+ unless match_street == :preflop && match_bet == 1 # no such thing as a preflop 1-bet
101
+ for match_player in street_hash[match_street][match_bet].keys
102
+ expected_result = street_hash[match_street][match_bet][match_player]
103
+ proc_symbol = bet_statistics_proc_symbol(match_street, match_bet, prefix)
104
+ actual_result = hand_statistics.send(proc_symbol, match_player)
105
+ unless actual_result == expected_result
106
+ @errors << {:symbol => proc_symbol, :player => match_player, :expected => expected_result, :actual => actual_result}
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ @errors.empty?
113
+ end
92
114
 
93
- it "should allow you to register an action" do
94
- @stats.register_player sample_player
95
- lambda{@stats.register_action sample_action[:screen_name], sample_action[:action], sample_action}.should_not raise_error
96
- end
97
-
98
- it "should complain when asked to generate player records without a registered player" do
99
- @stats.register_button(3)
100
- lambda{@stats.player_records}.should raise_error(PLAYER_RECORDS_NO_PLAYER_REGISTERED)
101
- end
102
-
103
- it "should complain when asked to generate player records without a registered button" do
104
- @stats.register_player sample_player
105
- lambda{@stats.player_records}.should raise_error(PLAYER_RECORDS_NO_BUTTON_REGISTERED)
106
- end
107
- end
115
+ failure_message_for_should do |hand_statistics|
116
+ @errors.collect do |each|
117
+ "expected #{each[:symbol]}(#{each[:player].inspect}) to be #{each[:expected].inspect}, obtained #{each[:actual].inspect}"
118
+ end.join("; \n")
119
+ end
108
120
 
109
- describe HandStatistics, "when managing street state" do
110
- before(:each) do
111
- @stats = HandStatistics.new
112
- end
113
-
114
- it "should initially have street state set to :prelude" do
115
- @stats.street.should == :prelude
116
- end
117
-
118
- it "should change street state on an explicit call to #street_transition" do
119
- @stats.street_transition(:foo)
120
- @stats.street.should == :foo
121
- end
122
-
123
- it "should not transition when hand_update does not change street state" do
124
- @stats.should_not_receive(:street_transition)
125
- @stats.update_hand(:street => :prelude)
126
- end
127
-
128
- it "should transition when hand_update chances to a new state" do
129
- @stats.should_receive(:street_transition).with(:preflop)
130
- @stats.update_hand(:street => :preflop)
131
- end
132
- end
121
+ failure_message_for_should_not do |hand_statistics|
122
+ "the actual results conformed to the expected results"
123
+ end
133
124
 
134
- describe HandStatistics, "when evaluating position with three players" do
135
- before(:each) do
136
- @stats = HandStatistics.new
137
- @stats.register_player @seat2 = next_sample_player(:seat => 2, :screen_name => "seat2")
138
- @stats.register_player @seat4 = next_sample_player(:seat => 4, :screen_name => "seat4")
139
- @stats.register_player @seat6 = next_sample_player(:seat => 6, :screen_name => "seat6")
140
- end
141
- it "should correctly identify position with the button on a chair" do
142
- @stats.register_button(6)
143
- @stats.should_not be_cutoff("seat4")
144
- @stats.should be_button("seat6")
145
- @stats.should be_sbpos("seat2")
146
- @stats.should be_bbpos("seat4")
147
- end
148
- it "should correctly identify position with the button to the left of the first chair" do
149
- @stats.register_button(1)
150
- @stats.should_not be_cutoff("seat4")
151
- @stats.should be_button("seat6")
152
- @stats.should be_sbpos("seat2")
153
- @stats.should be_bbpos("seat4")
154
- end
155
- it "should correctly identify position with the button to the right of the last chair" do
156
- @stats.register_button(9)
157
- @stats.should_not be_cutoff("seat6")
158
- @stats.should be_button("seat6")
159
- @stats.should be_sbpos("seat2")
160
- @stats.should be_bbpos("seat4")
161
- end
162
- it "should correctly identify position with the button between two middle chairs" do
163
- @stats.register_button(5)
164
- @stats.should_not be_cutoff("seat2")
165
- @stats.should be_button("seat4")
166
- @stats.should be_sbpos("seat6")
167
- @stats.should be_bbpos("seat2")
168
- end
125
+ description do
126
+ "have conforming #{prefix}street_bet statistics"
127
+ end
169
128
  end
170
129
 
171
- describe HandStatistics, "when evaluating position with four players" do
172
- before(:each) do
173
- @stats = HandStatistics.new
174
- @stats.register_player @seat2 = next_sample_player(:seat => 2, :screen_name => "seat2")
175
- @stats.register_player @seat4 = next_sample_player(:seat => 4, :screen_name => "seat4")
176
- @stats.register_player @seat6 = next_sample_player(:seat => 6, :screen_name => "seat6")
177
- @stats.register_player @seat8 = next_sample_player(:seat => 8, :screen_name => "seat8")
178
- end
179
- it "should correctly identify position with the button on a chair" do
180
- @stats.register_button(6)
181
- @stats.should be_cutoff("seat4")
182
- @stats.should be_button("seat6")
183
- @stats.should be_sbpos("seat8")
184
- @stats.should be_bbpos("seat2")
185
- end
186
- it "should correctly identify position with the button to the left of the first chair" do
187
- @stats.register_button(1)
188
- @stats.should be_cutoff("seat6")
189
- @stats.should be_button("seat8")
190
- @stats.should be_sbpos("seat2")
191
- @stats.should be_bbpos("seat4")
192
- end
193
- it "should correctly identify position with the button to the right of the last chair" do
194
- @stats.register_button(9)
195
- @stats.should be_cutoff("seat6")
196
- @stats.should be_button("seat8")
197
- @stats.should be_sbpos("seat2")
198
- @stats.should be_bbpos("seat4")
199
- end
200
- it "should correctly identify position with the button between two middle chairs" do
201
- @stats.register_button(5)
202
- @stats.should be_cutoff("seat2")
203
- @stats.should be_button("seat4")
204
- @stats.should be_sbpos("seat6")
205
- @stats.should be_bbpos("seat8")
206
- end
207
- end
130
+ Spec::Matchers.define :have_last_aggr do |street, screen_name|
131
+ match do |hand_statistics|
132
+ @errors = []
133
+ for player in hand_statistics.players
134
+ actual = hand_statistics.send("last_aggr_#{street}", player)
135
+ expected = if screen_name != player
136
+ nil
137
+ else
138
+ true
139
+ end
140
+ @errors << {:player => player, :expected => expected, :actual => actual} unless expected == actual
141
+ end
142
+ @errors.empty?
143
+ end
208
144
 
209
- describe HandStatistics, "when managing plugins" do
210
- before(:each) do
211
- @hand_statistics = HandStatistics.new
212
- @plugins = @hand_statistics.plugins
213
- end
214
- it "should install specific plugins upon initialization" do
215
- HandStatistics.new.plugins.map{|each| each.class}.should include(CashStatistics)
216
- end
217
- it "should install all the plugins identified upon initialization" do
218
- HandStatistics.new.plugins.map{|each| each.class}.should include(*HandStatistics.plugin_factory)
219
- end
220
- it "should notify plugins whenever there is a street transition" do
221
- @plugins.each{|each_plugin| each_plugin.should_receive(:street_transition).with(:foo)}
222
- @hand_statistics.street_transition(:foo)
223
- end
224
- it "should notify plugins whenever there is a street transition for a player" do
225
- @plugins.each{|each_plugin| each_plugin.should_receive(:street_transition_for_player).with(:foo, :bar)}
226
- @hand_statistics.street_transition_for_player(:foo, :bar)
227
- end
228
- it "should notify plugins whenever a player is registered" do
229
- @plugins.each{|each_plugin| each_plugin.should_receive(:register_player).with("andy", :prelude)}
230
- @hand_statistics.register_player({:screen_name => "andy"})
231
- end
145
+ failure_message_for_should do |hand_statistics|
146
+ @errors.collect do |each|
147
+ "expected last_aggr_#{street}(#{each[:player].inspect}) to be #{each[:expected].inspect}, obtained #{each[:actual].inspect}"
148
+ end.join("; \n")
149
+ end
150
+
151
+ failure_message_for_should_not do |hand_statistics|
152
+ "the actual results conformed to the expected results"
153
+ end
154
+
155
+ description do
156
+ "have conforming last_aggression_#{street} statistics"
157
+ end
232
158
  end
233
159
 
234
- describe HandStatistics, "when registering standard actions" do
235
- before(:each) do
236
- @stats = HandStatistics.new
237
- @stats.update_hand sample_hand
238
- @stats.register_player sample_player
239
- @stats.street_transition :preflop
240
- end
241
-
242
- it "should post correctly" do
243
- post_action = lambda{register_post(sample_player, "5".to_d)}
244
- post_action.should change{@stats.posted(sample_player[:screen_name])}.by("5".to_d)
245
- post_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d * -1)
246
- post_action.should change{@stats.posted_in_bb(sample_player[:screen_name])}.by("2.5".to_d)
247
- post_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("2.5".to_d * -1)
248
- post_action.should_not change{@stats.paid(sample_player[:screen_name])}
249
- post_action.should_not change{@stats.won(sample_player[:screen_name])}
250
- end
160
+ describe HandStatistics do
161
+ context "when created" do
162
+ before(:each) do
163
+ @stats = HandStatistics.new
164
+ end
251
165
 
252
- it "should ante correctly" do
253
- ante_action = lambda{register_ante(sample_player, "5".to_d)}
254
- ante_action.should change{@stats.posted(sample_player[:screen_name])}.by("5".to_d)
255
- ante_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d * -1)
256
- ante_action.should change{@stats.posted_in_bb(sample_player[:screen_name])}.by("2.5".to_d)
257
- ante_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("2.5".to_d * -1)
258
- ante_action.should_not change{@stats.paid(sample_player[:screen_name])}
259
- ante_action.should_not change{@stats.won(sample_player[:screen_name])}
260
- end
261
-
262
- it "should pay correctly" do
263
- pay_action = lambda{register_bet(sample_player, "5".to_d)}
264
- pay_action.should change{@stats.paid(sample_player[:screen_name])}.by("5".to_d)
265
- pay_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d * -1)
266
- pay_action.should change{@stats.paid_in_bb(sample_player[:screen_name])}.by("2.5".to_d)
267
- pay_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("2.5".to_d * -1)
268
- pay_action.should_not change{@stats.posted(sample_player[:screen_name])}
269
- pay_action.should_not change{@stats.won(sample_player[:screen_name])}
270
- end
166
+ it "should return an empty player list" do
167
+ @stats.should have(0).players
168
+ end
271
169
 
272
- it "should win correctly" do
273
- win_action = lambda{register_win(sample_player, "5".to_d)}
274
- win_action.should change{@stats.won(sample_player[:screen_name])}.by("5".to_d)
275
- win_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d)
276
- win_action.should change{@stats.won_in_bb(sample_player[:screen_name])}.by("2.5".to_d)
277
- win_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("2.5".to_d)
278
- win_action.should_not change{@stats.paid(sample_player[:screen_name])}
279
- win_action.should_not change{@stats.posted(sample_player[:screen_name])}
280
- end
170
+ it "should properly chain updates of hand record information" do
171
+ sample_hand.each{|key, value| @stats.update_hand(key => value)}
172
+ @stats.hand_record.should == sample_hand
173
+ end
281
174
 
282
- it "should check correctly" do
283
- lambda {
284
- register_check(sample_player)
285
- }.should_not change{@stats}
286
- end
175
+ it "should not complain when asked to generate hand record with all HAND_INFORMATION_KEYS filled out" do
176
+ @stats.update_hand(sample_hand)
177
+ lambda{@stats.hand_record}.should_not raise_error(/#{HAND_RECORD_INCOMPLETE_MESSAGE}/)
178
+ end
179
+
180
+ it "should complain when asked to generate hand record if no information is given" do
181
+ lambda{@stats.hand_record}.should raise_error(/#{HAND_RECORD_INCOMPLETE_MESSAGE}/)
182
+ end
287
183
 
288
- it "should fold correctly" do
289
- lambda {
290
- register_fold(sample_player)
291
- }.should_not change{@stats}
292
- end
184
+ HandStatistics::HAND_INFORMATION_KEYS.each do |thing|
185
+ unless [:ante, :number_players].include?(thing)
186
+ it "should complain when asked to generate hand record without a #{thing.to_s}" do
187
+ @stats.update_hand(sample_hand.except(thing))
188
+ lambda{@stats.hand_record}.should raise_error(/#{HAND_RECORD_INCOMPLETE_MESSAGE}/)
189
+ end
190
+ end
191
+ end
293
192
 
294
- it "should show cards correctly" do
295
- register_cards(sample_player, "AH KH")
296
- @stats.cards(sample_player[:screen_name]).should == "AH KH"
297
- puts "<<#{@stats.card_category_index(sample_player[:screen_name])}>>"
298
- @stats.card_category_index(sample_player[:screen_name]).should == Pokerstats::class_index_from_hand_string("AH KH")
299
- end
300
- end
193
+ it "should not produce hand records having extra keys" do
194
+ @stats.update_hand(sample_hand)
195
+ @stats.update_hand(:street => :river)
196
+ @stats.hand_record.should have_all_and_only_keys HAND_INFORMATION_KEYS
197
+ end
198
+ end
301
199
 
302
- describe HandStatistics, "when registering pay_to actions" do
303
- before(:each) do
304
- @stats = HandStatistics.new
305
- @stats.update_hand sample_hand
306
- @stats.register_player @first_player = next_sample_player
307
- @stats.register_player @second_player = next_sample_player
308
- @stats.register_button @first_player[:seat]
309
- register_post @first_player, 1
310
- end
200
+ context "when managing game activity registration" do
201
+
202
+ before(:each) do
203
+ @stats = HandStatistics.new
204
+ end
205
+
206
+ it "should allow you to register a player" do
207
+ @stats.register_player sample_player
208
+ @stats.should have(1).player_records_without_validation
209
+ @stats.hand_information(:number_players).should == 1
210
+ @stats.player_records_without_validation.first[:screen_name].should == sample_player[:screen_name]
211
+ @stats.player_records_without_validation.first[:seat].should == sample_player[:seat]
212
+ @stats.player_records_without_validation.first[:starting_stack].should == sample_player[:starting_stack]
213
+ end
311
214
 
312
- it "should pay_to correctly for regular raise" do
313
- register_street :preflop
314
- lambda{
315
- register_raise_to @second_player, 7
316
- }.should change{@stats.paid(@second_player[:screen_name])}.by(7)
317
- end
215
+ it "should complain when registering a player twice" do
216
+ @stats.register_player sample_player
217
+ lambda{@stats.register_player sample_player}.should raise_error(/#{PLAYER_RECORDS_DUPLICATE_PLAYER_NAME}/)
218
+ end
318
219
 
319
- it "should pay_to correctly for re-raise" do
320
- @stats.register_action @second_player[:screen_name], "**************", :result => :neutral
321
- register_post(@second_player, 2)
322
- register_street :preflop
323
- lambda{
324
- register_street :preflop
325
- register_raise_to @first_player, "7".to_d
326
- }.should change{@stats.paid(@first_player[:screen_name])}.by("6".to_d)
327
- end
220
+ it "should allow you to register a button" do
221
+ @stats.button.should be_nil
222
+ @stats.register_button(3)
223
+ @stats.button.should == 3
224
+ end
328
225
 
329
- it "should pay_to correctly for re-raise after new phase" do
330
- register_post @second_player, "2".to_d
331
- register_street :flop
332
- register_bet @first_player, "3".to_d
333
- lambda{
334
- register_raise_to @second_player, "7".to_d
335
- }.should change{@stats.paid(@second_player[:screen_name])}.by("7".to_d)
336
- lambda{
337
- #TODO FIX THIS TEST
338
- @stats.register_action @first_player[:screen_name], "sample_reraise", :result => :pay_to, :amount => "14".to_d
339
- }.should change{@stats.paid(@first_player[:screen_name])}.by("11".to_d)
340
- end
341
- end
226
+ it "should complain when registering action for an unregistered player" do
227
+ lambda {
228
+ @stats.register_action sample_action[:screen_name], sample_action[:action], sample_action
229
+ }.should raise_error(/#{PLAYER_RECORDS_UNREGISTERED_PLAYER}/)
230
+ end
342
231
 
343
- describe HandStatistics, "when registering pay_to actions after antes" do
344
- before(:each) do
345
- @stats = HandStatistics.new
346
- @stats.update_hand sample_hand
347
- @stats.register_player @first_player = next_sample_player
348
- @stats.register_player @second_player = next_sample_player
349
- register_post @first_player, 1
350
- register_ante @first_player, 1
351
- register_ante @second_player, 1
352
- end
353
-
354
- it "should pay_to correctly for regular raise" do
355
- register_street :preflop
356
- lambda{
357
- register_raise_to @second_player, 7
358
- }.should change{@stats.paid(@second_player[:screen_name])}.by(7)
359
- end
360
-
361
- it "should pay_to correctly for re-raise" do
362
- @stats.register_action @second_player[:screen_name], "**************", :result => :neutral
363
- register_post(@second_player, 2)
364
- register_street :preflop
365
- lambda{
366
- register_street :preflop
367
- register_raise_to @first_player, "7".to_d
368
- }.should change{@stats.paid(@first_player[:screen_name])}.by("6".to_d)
369
- end
232
+ it "should allow you to register an action" do
233
+ @stats.register_player sample_player
234
+ lambda{@stats.register_action sample_action[:screen_name], sample_action[:action], sample_action}.should_not raise_error
235
+ end
236
+
237
+ it "should complain when asked to generate player records without a registered player" do
238
+ @stats.register_button(3)
239
+ lambda{@stats.player_records}.should raise_error(PLAYER_RECORDS_NO_PLAYER_REGISTERED)
240
+ end
370
241
 
371
- it "should pay_to correctly for re-raise after new phase" do
372
- register_post @second_player, "2".to_d
373
- register_street :flop
374
- register_bet @first_player, "3".to_d
375
- lambda{
376
- register_raise_to @second_player, "7".to_d
377
- }.should change{@stats.paid(@second_player[:screen_name])}.by("7".to_d)
378
- lambda{
379
- #TODO FIX THIS TEST
380
- @stats.register_action @first_player[:screen_name], "sample_reraise", :result => :pay_to, :amount => "14".to_d
381
- }.should change{@stats.paid(@first_player[:screen_name])}.by("11".to_d)
382
- end
383
- end
242
+ it "should complain when asked to generate player records without a registered button" do
243
+ @stats.register_player sample_player
244
+ lambda{@stats.player_records}.should raise_error(PLAYER_RECORDS_NO_BUTTON_REGISTERED)
245
+ end
246
+ end
384
247
 
385
- describe HandStatistics, "when measuring pfr" do
386
- before(:each) do
387
- @stats = HandStatistics.new
388
- @stats.register_player @first_player = next_sample_player
389
- @stats.register_player @second_player = next_sample_player
390
- register_post(@first_player, 1)
391
- register_post(@second_player, 2)
392
- register_street :preflop
393
- end
248
+ context "when managing street state" do
249
+ before(:each) do
250
+ @stats = HandStatistics.new
251
+ end
252
+
253
+ it "should initially have street state set to :prelude" do
254
+ @stats.street.should == :prelude
255
+ end
256
+
257
+ it "should change street state on an explicit call to #street_transition" do
258
+ @stats.street_transition(:foo)
259
+ @stats.street.should == :foo
260
+ end
261
+
262
+ it "should not transition when hand_update does not change street state" do
263
+ @stats.should_not_receive(:street_transition)
264
+ @stats.update_hand(:street => :prelude)
265
+ end
266
+
267
+ it "should transition when hand_update chances to a new state" do
268
+ @stats.should_receive(:street_transition).with(:preflop)
269
+ @stats.update_hand(:street => :preflop)
270
+ end
271
+
272
+ it "should remember the last street" do
273
+ @stats.last_street.should be_nil
274
+ @stats.street_transition(:foo)
275
+ @stats.last_street.should == :prelude
276
+ @stats.street_transition(:bar)
277
+ @stats.last_street.should == :foo
278
+ end
279
+ end
280
+
281
+ context "when managing plugins" do
282
+ before(:each) do
283
+ @hand_statistics = HandStatistics.new
284
+ @plugins = @hand_statistics.plugins
285
+ end
286
+ it "should install specific plugins upon initialization" do
287
+ HandStatistics.new.plugins.map{|each| each.class}.should include(CashStatistics)
288
+ end
289
+ it "should install all the plugins identified upon initialization" do
290
+ HandStatistics.new.plugins.map{|each| each.class}.should include(*HandStatistics.plugin_factory)
291
+ end
292
+ it "should notify plugins whenever there is a street transition" do
293
+ @plugins.each{|each_plugin| each_plugin.should_receive(:street_transition).with(:foo)}
294
+ @hand_statistics.street_transition(:foo)
295
+ end
296
+ it "should notify plugins whenever there is a street transition for a player" do
297
+ @plugins.each{|each_plugin| each_plugin.should_receive(:street_transition_for_player).with(:foo, :bar)}
298
+ @hand_statistics.street_transition_for_player(:foo, :bar)
299
+ end
300
+ it "should notify plugins whenever a player is registered" do
301
+ @plugins.each{|each_plugin| each_plugin.should_receive(:register_player).with("andy", :prelude, {:screen_name => "andy"})}
302
+ @hand_statistics.register_player({:screen_name => "andy"})
303
+ end
304
+ end
305
+
306
+ context "when registering standard actions" do
307
+ before(:each) do
308
+ @stats = HandStatistics.new
309
+ @stats.update_hand sample_hand
310
+ @stats.register_player sample_player
311
+ @stats.street_transition :preflop
312
+ end
313
+
314
+ it "should post correctly" do
315
+ post_action = lambda{register_post(sample_player, "5".to_d)}
316
+ post_action.should change{@stats.posted(sample_player[:screen_name])}.by("5".to_d)
317
+ post_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d * -1)
318
+ post_action.should change{@stats.posted_in_bb(sample_player[:screen_name])}.by("1.25".to_d)
319
+ post_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("1.25".to_d * -1)
320
+ post_action.should_not change{@stats.paid(sample_player[:screen_name])}
321
+ post_action.should_not change{@stats.won(sample_player[:screen_name])}
322
+ end
323
+
324
+ it "should ante correctly" do
325
+ ante_action = lambda{register_ante(sample_player, "5".to_d)}
326
+ ante_action.should change{@stats.posted(sample_player[:screen_name])}.by("5".to_d)
327
+ ante_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d * -1)
328
+ ante_action.should change{@stats.posted_in_bb(sample_player[:screen_name])}.by("1.25".to_d)
329
+ ante_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("1.25".to_d * -1)
330
+ ante_action.should_not change{@stats.paid(sample_player[:screen_name])}
331
+ ante_action.should_not change{@stats.won(sample_player[:screen_name])}
332
+ end
333
+
334
+ it "should pay correctly" do
335
+ pay_action = lambda{register_bet(sample_player, "5".to_d)}
336
+ pay_action.should change{@stats.paid(sample_player[:screen_name])}.by("5".to_d)
337
+ pay_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d * -1)
338
+ pay_action.should change{@stats.paid_in_bb(sample_player[:screen_name])}.by("1.25".to_d)
339
+ pay_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("1.25".to_d * -1)
340
+ pay_action.should_not change{@stats.posted(sample_player[:screen_name])}
341
+ pay_action.should_not change{@stats.won(sample_player[:screen_name])}
342
+ end
343
+
344
+ it "should win correctly" do
345
+ win_action = lambda{register_win(sample_player, "5".to_d)}
346
+ win_action.should change{@stats.won(sample_player[:screen_name])}.by("5".to_d)
347
+ win_action.should change{@stats.profit(sample_player[:screen_name])}.by("5".to_d)
348
+ win_action.should change{@stats.won_in_bb(sample_player[:screen_name])}.by("1.25".to_d)
349
+ win_action.should change{@stats.profit_in_bb(sample_player[:screen_name])}.by("1.25".to_d)
350
+ win_action.should_not change{@stats.paid(sample_player[:screen_name])}
351
+ win_action.should_not change{@stats.posted(sample_player[:screen_name])}
352
+ end
353
+
354
+ it "should check correctly" do
355
+ lambda {
356
+ register_check(sample_player)
357
+ }.should_not change{@stats}
358
+ end
359
+
360
+ it "should fold correctly" do
361
+ lambda {
362
+ register_fold(sample_player)
363
+ }.should_not change{@stats}
364
+ end
365
+
366
+ it "should show cards correctly" do
367
+ register_cards(sample_player, "AH KH")
368
+ @stats.cards(sample_player[:screen_name]).should == "AH KH"
369
+ @stats.card_category_index(sample_player[:screen_name]).should == Pokerstats::class_index_from_hand_string("AH KH")
370
+ end
371
+ end
372
+
373
+ context "when registering pay_to actions" do
374
+ before(:each) do
375
+ @stats = HandStatistics.new
376
+ @stats.update_hand sample_hand
377
+ @stats.register_player @first_player = next_sample_player
378
+ @stats.register_player @second_player = next_sample_player
379
+ @stats.register_button @first_player[:seat]
380
+ register_post @first_player, 1
381
+ end
382
+
383
+ it "should pay_to correctly for regular raise" do
384
+ register_street :preflop
385
+ lambda{
386
+ register_raise_to @second_player, 7
387
+ }.should change{@stats.paid(@second_player[:screen_name])}.by(7)
388
+ end
389
+
390
+ it "should pay_to correctly for re-raise" do
391
+ @stats.register_action @second_player[:screen_name], "**************", :result => :neutral
392
+ register_post(@second_player, 2)
393
+ register_street :preflop
394
+ lambda{
395
+ register_street :preflop
396
+ register_raise_to @first_player, "7".to_d
397
+ }.should change{@stats.paid(@first_player[:screen_name])}.by("6".to_d)
398
+ end
399
+
400
+ it "should pay_to correctly for re-raise after new phase" do
401
+ register_post @second_player, "2".to_d
402
+ register_street :flop
403
+ register_bet @first_player, "3".to_d
404
+ lambda{
405
+ register_raise_to @second_player, "7".to_d
406
+ }.should change{@stats.paid(@second_player[:screen_name])}.by("7".to_d)
407
+ lambda{
408
+ #TODO FIX THIS TEST
409
+ @stats.register_action @first_player[:screen_name], "sample_reraise", :result => :pay_to, :amount => "14".to_d
410
+ }.should change{@stats.paid(@first_player[:screen_name])}.by("11".to_d)
411
+ end
412
+ end
413
+
414
+ context "when registering pay_to actions after antes" do
415
+ before(:each) do
416
+ @stats = HandStatistics.new
417
+ @stats.update_hand sample_hand
418
+ @stats.register_player @first_player = next_sample_player
419
+ @stats.register_player @second_player = next_sample_player
420
+ register_post @first_player, 1
421
+ register_ante @first_player, 1
422
+ register_ante @second_player, 1
423
+ end
424
+
425
+ it "should pay_to correctly for regular raise" do
426
+ register_street :preflop
427
+ lambda{
428
+ register_raise_to @second_player, 7
429
+ }.should change{@stats.paid(@second_player[:screen_name])}.by(7)
430
+ end
431
+
432
+ it "should pay_to correctly for re-raise" do
433
+ @stats.register_action @second_player[:screen_name], "**************", :result => :neutral
434
+ register_post(@second_player, 2)
435
+ register_street :preflop
436
+ lambda{
437
+ register_street :preflop
438
+ register_raise_to @first_player, "7".to_d
439
+ }.should change{@stats.paid(@first_player[:screen_name])}.by("6".to_d)
440
+ end
441
+
442
+ it "should pay_to correctly for re-raise after new phase" do
443
+ register_post @second_player, "2".to_d
444
+ register_street :flop
445
+ register_bet @first_player, "3".to_d
446
+ lambda{
447
+ register_raise_to @second_player, "7".to_d
448
+ }.should change{@stats.paid(@second_player[:screen_name])}.by("7".to_d)
449
+ lambda{
450
+ #TODO FIX THIS TEST
451
+ @stats.register_action @first_player[:screen_name], "sample_reraise", :result => :pay_to, :amount => "14".to_d
452
+ }.should change{@stats.paid(@first_player[:screen_name])}.by("11".to_d)
453
+ end
454
+ end
455
+
456
+ context "when evaluating position" do
457
+ context "with three players" do
458
+ before(:each) do
459
+ @stats = HandStatistics.new
460
+ @stats.register_player @seat2 = next_sample_player(:seat => 2, :screen_name => "seat2")
461
+ @stats.register_player @seat4 = next_sample_player(:seat => 4, :screen_name => "seat4")
462
+ @stats.register_player @seat6 = next_sample_player(:seat => 6, :screen_name => "seat6")
463
+ end
464
+ it "should correctly identify position with the button on a chair" do
465
+ @stats.register_button(6)
466
+ @stats.should_not be_cutoff("seat4")
467
+ @stats.should be_button("seat6")
468
+ @stats.should be_sbpos("seat2")
469
+ @stats.should be_bbpos("seat4")
470
+ @stats.should be_betting_order("seat2", "seat4")
471
+ @stats.should be_betting_order("seat4", "seat6")
472
+ end
473
+ it "should correctly identify position with the button to the left of the first chair" do
474
+ @stats.register_button(1)
475
+ @stats.should_not be_cutoff("seat4")
476
+ @stats.should be_button("seat6")
477
+ @stats.should be_sbpos("seat2")
478
+ @stats.should be_bbpos("seat4")
479
+ end
480
+ it "should correctly identify position with the button to the right of the last chair" do
481
+ @stats.register_button(9)
482
+ @stats.should_not be_cutoff("seat6")
483
+ @stats.should be_button("seat6")
484
+ @stats.should be_sbpos("seat2")
485
+ @stats.should be_bbpos("seat4")
486
+ end
487
+ it "should correctly identify position with the button between two middle chairs" do
488
+ @stats.register_button(5)
489
+ @stats.should_not be_cutoff("seat2")
490
+ @stats.should be_button("seat4")
491
+ @stats.should be_sbpos("seat6")
492
+ @stats.should be_bbpos("seat2")
493
+ end
494
+ end
495
+
496
+ context "with four players" do
497
+ before(:each) do
498
+ @stats = HandStatistics.new
499
+ @stats.register_player @seat2 = next_sample_player(:seat => 2, :screen_name => "seat2")
500
+ @stats.register_player @seat4 = next_sample_player(:seat => 4, :screen_name => "seat4")
501
+ @stats.register_player @seat6 = next_sample_player(:seat => 6, :screen_name => "seat6")
502
+ @stats.register_player @seat8 = next_sample_player(:seat => 8, :screen_name => "seat8")
503
+ end
504
+ it "should correctly identify position with the button on a chair" do
505
+ @stats.register_button(6)
506
+ @stats.should be_cutoff("seat4")
507
+ @stats.should be_button("seat6")
508
+ @stats.should be_sbpos("seat8")
509
+ @stats.should be_bbpos("seat2")
510
+ @stats.should be_betting_order("seat8", "seat2")
511
+ @stats.should be_betting_order("seat2", "seat4")
512
+ @stats.should be_betting_order("seat4", "seat6")
513
+ end
514
+ it "should correctly identify position with the button to the left of the first chair" do
515
+ @stats.register_button(1)
516
+ @stats.should be_cutoff("seat6")
517
+ @stats.should be_button("seat8")
518
+ @stats.should be_sbpos("seat2")
519
+ @stats.should be_bbpos("seat4")
520
+ end
521
+ it "should correctly identify position with the button to the right of the last chair" do
522
+ @stats.register_button(9)
523
+ @stats.should be_cutoff("seat6")
524
+ @stats.should be_button("seat8")
525
+ @stats.should be_sbpos("seat2")
526
+ @stats.should be_bbpos("seat4")
527
+ end
528
+ it "should correctly identify position with the button between two middle chairs" do
529
+ @stats.register_button(5)
530
+ @stats.should be_cutoff("seat2")
531
+ @stats.should be_button("seat4")
532
+ @stats.should be_sbpos("seat6")
533
+ @stats.should be_bbpos("seat8")
534
+ end
535
+ end
536
+ end
537
+
538
+ context "when measuring statistics:" do
539
+ context "starting stack data" do
540
+ context "without an ante" do
541
+ before(:each) do
542
+ @stats = HandStatistics.new
543
+ @stats.update_hand(sample_hand.merge(:sb => "10".to_d, :bb => "20".to_d, :ante => "0".to_d))
544
+ @stats.register_player @me = next_sample_player.merge(:screen_name => "me", :starting_stack => "300".to_d)
545
+ end
546
+ it "should compute starting stack statistics correctly" do
547
+ @stats.starting_stack("me").should == "300".to_d
548
+ @stats.starting_stack_in_bb("me").should == "7.5".to_d
549
+ @stats.starting_pot.should == "30".to_d
550
+ @stats.starting_stack_as_M("me").should == "10".to_d
551
+ @stats.starting_stack_as_M_class("me").should == "yellow"
552
+ end
553
+ end
554
+ context "with an ante" do
555
+ before(:each) do
556
+ @stats = HandStatistics.new
557
+ @stats.update_hand(sample_hand.merge(:sb => "10".to_d, :bb => "20".to_d, :ante => "2".to_d, :number_players => "9".to_d))
558
+ @stats.register_player @me = next_sample_player.merge(:screen_name => "me", :starting_stack => "300".to_d)
559
+ end
560
+ it "should compute starting stack statistics correctly" do
561
+ @stats.starting_stack("me").should == "300".to_d
562
+ @stats.starting_stack_in_bb("me").should == "7.5".to_d
563
+ @stats.starting_pot.should == "50".to_d
564
+ @stats.starting_stack_as_M("me").should == "6.0".to_d
565
+ @stats.starting_stack_as_M_class("me").should == "orange"
566
+ end
567
+ end
568
+ end
569
+
570
+ context "pfr" do
571
+ before(:each) do
572
+ @stats = HandStatistics.new
573
+ @stats.register_player @first_player = next_sample_player
574
+ @stats.register_player @second_player = next_sample_player
575
+ register_post(@first_player, 1)
576
+ register_post(@second_player, 2)
577
+ register_street :preflop
578
+ end
394
579
 
395
- it "should find a pfr opportunity if first actors limped" do
396
- register_call(@first_player, 1)
397
- register_check(@second_player)
398
- @stats.should be_pfr_opportunity(@first_player[:screen_name])
399
- @stats.should_not be_pfr_opportunity_taken(@first_player[:screen_name])
400
- @stats.should be_pfr_opportunity(@second_player[:screen_name])
401
- @stats.should_not be_pfr_opportunity_taken(@second_player[:screen_name])
402
- end
580
+ it "should find a pfr opportunity if first actors limped" do
581
+ register_call(@first_player, 1)
582
+ register_check(@second_player)
583
+ @stats.should be_pfr_opportunity(@first_player[:screen_name])
584
+ @stats.should_not be_pfr_opportunity_taken(@first_player[:screen_name])
585
+ @stats.should be_pfr_opportunity(@second_player[:screen_name])
586
+ @stats.should_not be_pfr_opportunity_taken(@second_player[:screen_name])
587
+ end
403
588
 
404
- it "should not find a pfr opportunity if another player raises first" do
405
- register_raise_to(@first_player, 5)
406
- register_call(@second_player, 3)
407
- @stats.should be_pfr_opportunity(@first_player[:screen_name])
408
- @stats.should be_pfr_opportunity_taken(@first_player[:screen_name])
409
- @stats.should_not be_pfr_opportunity(@second_player[:screen_name])
410
- end
589
+ it "should not find a pfr opportunity if another player raises first" do
590
+ register_raise_to(@first_player, 5)
591
+ register_call(@second_player, 3)
592
+ @stats.should be_pfr_opportunity(@first_player[:screen_name])
593
+ @stats.should be_pfr_opportunity_taken(@first_player[:screen_name])
594
+ @stats.should_not be_pfr_opportunity(@second_player[:screen_name])
595
+ end
411
596
 
412
- it "should not find a pfr opportunity if player made a raise other than the first raise preflpp" do
413
- register_raise_to(@first_player, 5)
414
- register_raise_to(@second_player, 10)
415
- register_call(@first_player, 5)
416
- @stats.should be_pfr_opportunity(@first_player[:screen_name])
417
- @stats.should be_pfr_opportunity_taken(@first_player[:screen_name])
418
- @stats.should_not be_pfr_opportunity(@second_player[:screen_name])
419
- end
597
+ it "should not find a pfr opportunity if player made a raise other than the first raise preflpp" do
598
+ register_raise_to(@first_player, 5)
599
+ register_raise_to(@second_player, 10)
600
+ register_call(@first_player, 5)
601
+ @stats.should be_pfr_opportunity(@first_player[:screen_name])
602
+ @stats.should be_pfr_opportunity_taken(@first_player[:screen_name])
603
+ @stats.should_not be_pfr_opportunity(@second_player[:screen_name])
604
+ end
420
605
 
421
- it "should be unaffected by postflop bets and raises" do
422
- register_call(@first_player, 1)
423
- register_check(@second_player)
424
- register_street :flop
425
- register_bet(@first_player, 4)
426
- register_bet(@second_player, 10)
427
- register_call(@first_player, 6)
428
- @stats.should be_pfr_opportunity(@first_player[:screen_name])
429
- @stats.should be_pfr_opportunity(@second_player[:screen_name])
430
- end
431
- end
606
+ it "should be unaffected by postflop bets and raises" do
607
+ register_call(@first_player, 1)
608
+ register_check(@second_player)
609
+ register_street :flop
610
+ register_bet(@first_player, 4)
611
+ register_bet(@second_player, 10)
612
+ register_call(@first_player, 6)
613
+ @stats.should be_pfr_opportunity(@first_player[:screen_name])
614
+ @stats.should be_pfr_opportunity(@second_player[:screen_name])
615
+ end
616
+ end
432
617
 
433
- describe HandStatistics, "when measuring aggression" do
434
- before(:each) do
435
- @stats = HandStatistics.new
436
- @stats.register_player @first_player = next_sample_player
437
- @stats.register_player @second_player = next_sample_player
438
- end
618
+ context "aggression" do
619
+ before(:each) do
620
+ @stats = HandStatistics.new
621
+ @stats.register_player @first_player = next_sample_player
622
+ @stats.register_player @second_player = next_sample_player
623
+ end
439
624
 
440
- it "should all have zero values when no actions have been taken" do
441
- @stats.preflop_passive(@first_player[:screen_name]).should be_zero
442
- @stats.postflop_passive(@first_player[:screen_name]).should be_zero
443
- @stats.preflop_aggressive(@first_player[:screen_name]).should be_zero
444
- @stats.postflop_aggressive(@first_player[:screen_name]).should be_zero
445
- end
625
+ it "should all have zero values when no actions have been taken" do
626
+ @stats.preflop_passive(@first_player[:screen_name]).should be_zero
627
+ @stats.postflop_passive(@first_player[:screen_name]).should be_zero
628
+ @stats.preflop_aggressive(@first_player[:screen_name]).should be_zero
629
+ @stats.postflop_aggressive(@first_player[:screen_name]).should be_zero
630
+ end
446
631
 
447
- it "should treat a call preflop as a passive move" do
448
- register_street :preflop
449
- lambda{register_call(@first_player, 1)}.should change{@stats.preflop_passive(@first_player[:screen_name])}.by(1)
450
- end
632
+ it "should treat a call preflop as a passive move" do
633
+ register_street :preflop
634
+ lambda{register_call(@first_player, 1)}.should change{@stats.preflop_passive(@first_player[:screen_name])}.by(1)
635
+ end
451
636
 
452
- it "should treat a call postflop as a passive move" do
453
- register_street :flop
454
- lambda{register_call(@first_player, 1)}.should change{@stats.postflop_passive(@first_player[:screen_name])}.by(1)
455
- end
637
+ it "should treat a call postflop as a passive move" do
638
+ register_street :flop
639
+ lambda{register_call(@first_player, 1)}.should change{@stats.postflop_passive(@first_player[:screen_name])}.by(1)
640
+ end
456
641
 
457
- it "should treat a raise preflop as an aggressive move" do
458
- register_street :preflop
459
- lambda{register_raise_to(@first_player, 7)}.should change{@stats.preflop_aggressive(@first_player[:screen_name])}.by(1)
460
- end
642
+ it "should treat a raise preflop as an aggressive move" do
643
+ register_street :preflop
644
+ lambda{register_raise_to(@first_player, 7)}.should change{@stats.preflop_aggressive(@first_player[:screen_name])}.by(1)
645
+ end
461
646
 
462
- it "should treat a raise postflop as an aggressive move" do
463
- register_street :flop
464
- lambda{register_raise_to(@first_player, 7)}.should change{@stats.postflop_aggressive(@first_player[:screen_name])}.by(1)
465
- end
647
+ it "should treat a raise postflop as an aggressive move" do
648
+ register_street :flop
649
+ lambda{register_raise_to(@first_player, 7)}.should change{@stats.postflop_aggressive(@first_player[:screen_name])}.by(1)
650
+ end
466
651
 
467
- it "should treat a bet postflop as an aggressive move" do
468
- register_street :preflop
469
- lambda{register_bet(@first_player, 5)}.should change{@stats.preflop_aggressive(@first_player[:screen_name])}.by(1)
470
- end
652
+ it "should treat a bet postflop as an aggressive move" do
653
+ register_street :preflop
654
+ lambda{register_bet(@first_player, 5)}.should change{@stats.preflop_aggressive(@first_player[:screen_name])}.by(1)
655
+ end
471
656
 
472
- it "should not treat a check as an aggressive or a passive move" do
473
- register_street :preflop
474
- lambda{register_check(@first_player)}.should_not change{@stats.preflop_aggressive(@first_player[:screen_name])}
475
- lambda{register_check(@first_player)}.should_not change{@stats.preflop_passive(@first_player[:screen_name])}
476
- lambda{register_check(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
477
- lambda{register_check(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
478
- end
657
+ it "should not treat a check as an aggressive or a passive move" do
658
+ register_street :preflop
659
+ lambda{register_check(@first_player)}.should_not change{@stats.preflop_aggressive(@first_player[:screen_name])}
660
+ lambda{register_check(@first_player)}.should_not change{@stats.preflop_passive(@first_player[:screen_name])}
661
+ lambda{register_check(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
662
+ lambda{register_check(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
663
+ end
479
664
 
480
- it "should not treat a fold as an aggressive or a passive move" do
481
- register_street :preflop
482
- lambda{register_fold(@first_player)}.should_not change{@stats.preflop_aggressive(@first_player[:screen_name])}
483
- lambda{register_fold(@first_player)}.should_not change{@stats.preflop_passive(@first_player[:screen_name])}
484
- lambda{register_fold(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
485
- lambda{register_fold(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
486
- end
487
- end
665
+ it "should not treat a fold as an aggressive or a passive move" do
666
+ register_street :preflop
667
+ lambda{register_fold(@first_player)}.should_not change{@stats.preflop_aggressive(@first_player[:screen_name])}
668
+ lambda{register_fold(@first_player)}.should_not change{@stats.preflop_passive(@first_player[:screen_name])}
669
+ lambda{register_fold(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
670
+ lambda{register_fold(@first_player)}.should_not change{@stats.postflop_aggressive(@first_player[:screen_name])}
671
+ end
672
+ end
488
673
 
489
- describe HandStatistics, "when measuring c-bets" do
490
- before(:each) do
491
- @stats = HandStatistics.new
492
- @stats.register_player @button = next_sample_player(:screen_name => "button")
493
- @stats.register_player @sb = next_sample_player(:screen_name => "small blind")
494
- @stats.register_player @bb = next_sample_player(:screen_name => "big blind")
495
- @stats.register_button @button[:seat]
496
- register_street :prelude
497
- register_post @sb, 1
498
- register_post @bb, 2
499
- register_street :preflop
500
- end
501
- it "should not find preflop opportunity without a preflop raise" do
502
- register_call @button, 2
503
- register_call @sb, 1
504
- register_check @bb
505
- register_street :flop
506
- register_bet @button, 2
507
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
508
- @stats.should_not be_cbet_opportunity(@sb[:screen_name])
509
- @stats.should_not be_cbet_opportunity(@bb[:screen_name])
510
- end
511
- it "should not find c-bet opportunity with two preflop raises" do
512
- register_raise_to @button, 7
513
- register_raise_to @sb, 14
514
- register_fold @bb
515
- register_street :flop
516
- register_bet @button, 2
517
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
518
- @stats.should_not be_cbet_opportunity(@sb[:screen_name])
519
- @stats.should_not be_cbet_opportunity(@bb[:screen_name])
520
- end
521
- it "should not find c-bet opportunity if player raise is not the preflop raiser" do
522
- register_call @button, 2
523
- register_raise_to @sb, 7
524
- register_fold @bb
525
- register_call @button, 5
526
- register_street :flop
527
- register_bet @button, 2
528
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
529
- end
530
- it "should not find c-bet opportunity if non-raiser acts first" do
531
- register_call @button, 2
532
- register_raise_to @sb, 7
533
- register_fold @bb
534
- register_call @button, 5
535
- register_street :flop
536
- register_bet @button, 2
537
- register_call @sb, 2
538
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
539
- end
674
+ context "c-bets" do
675
+ before(:each) do
676
+ @stats = HandStatistics.new
677
+ @stats.register_player @button = next_sample_player(:screen_name => "button")
678
+ @stats.register_player @sb = next_sample_player(:screen_name => "small blind")
679
+ @stats.register_player @bb = next_sample_player(:screen_name => "big blind")
680
+ @stats.register_button @button[:seat]
681
+ register_street :prelude
682
+ register_post @sb, 1
683
+ register_post @bb, 2
684
+ register_street :preflop
685
+ end
686
+ it "should not find preflop opportunity without a preflop raise" do
687
+ register_call @button, 2
688
+ register_call @sb, 1
689
+ register_check @bb
690
+ register_street :flop
691
+ register_bet @button, 2
692
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
693
+ @stats.should_not be_cbet_opportunity(@sb[:screen_name])
694
+ @stats.should_not be_cbet_opportunity(@bb[:screen_name])
695
+ end
696
+ it "should not find c-bet opportunity with two preflop raises" do
697
+ register_raise_to @button, 7
698
+ register_raise_to @sb, 14
699
+ register_fold @bb
700
+ register_street :flop
701
+ register_bet @button, 2
702
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
703
+ @stats.should_not be_cbet_opportunity(@sb[:screen_name])
704
+ @stats.should_not be_cbet_opportunity(@bb[:screen_name])
705
+ end
706
+ it "should not find c-bet opportunity if player raise is not the preflop raiser" do
707
+ register_call @button, 2
708
+ register_raise_to @sb, 7
709
+ register_fold @bb
710
+ register_call @button, 5
711
+ register_street :flop
712
+ register_bet @button, 2
713
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
714
+ end
715
+ it "should not find c-bet opportunity if non-raiser acts first" do
716
+ register_call @button, 2
717
+ register_raise_to @sb, 7
718
+ register_fold @bb
719
+ register_call @button, 5
720
+ register_street :flop
721
+ register_bet @button, 2
722
+ register_call @sb, 2
723
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
724
+ end
540
725
 
541
- it "should find c-bet opportunity taken when lone pre-flop raiser makes first-in bet post-flop in position" do
542
- register_fold @button
543
- register_call @sb, 1
544
- register_raise_to @bb, 7
545
- register_call @sb, 5
546
- register_street :flop
547
- register_check @sb
548
- register_bet @bb, 7
549
- @stats.should be_cbet_opportunity(@bb[:screen_name])
550
- @stats.should be_cbet_opportunity_taken(@bb[:screen_name])
551
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
552
- @stats.should_not be_cbet_opportunity(@sb[:screen_name])
553
- end
554
- it "should find c-bet opportunity taken when lone pre-flop raiser makes first-in bet post-flop out of position" do
555
- register_fold @button
556
- register_raise_to @sb, 7
557
- register_call @bb, 5
558
- register_street :flop
559
- register_bet @sb, 7
560
- @stats.should be_cbet_opportunity(@sb[:screen_name])
561
- @stats.should be_cbet_opportunity_taken(@sb[:screen_name])
562
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
563
- @stats.should_not be_cbet_opportunity(@bb[:screen_name])
564
- end
565
- it "should find c-bet opportunity declined when lone pre-flop raiser does not make first-in bet post-flop in position" do
566
- register_fold @button
567
- register_call @sb, 1
568
- register_raise_to @bb, 7
569
- register_call @sb, 5
570
- register_street :flop
571
- register_check @sb
572
- register_check @bb
573
- @stats.should be_cbet_opportunity(@bb[:screen_name])
574
- @stats.should_not be_cbet_opportunity_taken(@bb[:screen_name])
575
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
576
- @stats.should_not be_cbet_opportunity(@sb[:screen_name])
577
- end
578
- it "should find c-bet opportunity declined when lone pre-flop raiser does not make first-in bet post-flop out of position" do
579
- register_fold @button
580
- register_raise_to @sb, 7
581
- register_call @bb, 5
582
- register_street :flop
583
- register_check @sb
584
- register_check @bb
585
- @stats.should be_cbet_opportunity(@sb[:screen_name])
586
- @stats.should_not be_cbet_opportunity_taken(@sb[:screen_name])
587
- @stats.should_not be_cbet_opportunity(@button[:screen_name])
588
- @stats.should_not be_cbet_opportunity(@bb[:screen_name])
589
- end
590
- end
726
+ it "should find c-bet opportunity taken when lone pre-flop raiser makes first-in bet post-flop in position" do
727
+ register_fold @button
728
+ register_call @sb, 1
729
+ register_raise_to @bb, 7
730
+ register_call @sb, 5
731
+ register_street :flop
732
+ register_check @sb
733
+ register_bet @bb, 7
734
+ @stats.should be_cbet_opportunity(@bb[:screen_name])
735
+ @stats.should be_cbet_opportunity_taken(@bb[:screen_name])
736
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
737
+ @stats.should_not be_cbet_opportunity(@sb[:screen_name])
738
+ end
739
+ it "should find c-bet opportunity taken when lone pre-flop raiser makes first-in bet post-flop out of position" do
740
+ register_fold @button
741
+ register_raise_to @sb, 7
742
+ register_call @bb, 5
743
+ register_street :flop
744
+ register_bet @sb, 7
745
+ @stats.should be_cbet_opportunity(@sb[:screen_name])
746
+ @stats.should be_cbet_opportunity_taken(@sb[:screen_name])
747
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
748
+ @stats.should_not be_cbet_opportunity(@bb[:screen_name])
749
+ end
750
+ it "should find c-bet opportunity declined when lone pre-flop raiser does not make first-in bet post-flop in position" do
751
+ register_fold @button
752
+ register_call @sb, 1
753
+ register_raise_to @bb, 7
754
+ register_call @sb, 5
755
+ register_street :flop
756
+ register_check @sb
757
+ register_check @bb
758
+ @stats.should be_cbet_opportunity(@bb[:screen_name])
759
+ @stats.should_not be_cbet_opportunity_taken(@bb[:screen_name])
760
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
761
+ @stats.should_not be_cbet_opportunity(@sb[:screen_name])
762
+ end
763
+ it "should find c-bet opportunity declined when lone pre-flop raiser does not make first-in bet post-flop out of position" do
764
+ register_fold @button
765
+ register_raise_to @sb, 7
766
+ register_call @bb, 5
767
+ register_street :flop
768
+ register_check @sb
769
+ register_check @bb
770
+ @stats.should be_cbet_opportunity(@sb[:screen_name])
771
+ @stats.should_not be_cbet_opportunity_taken(@sb[:screen_name])
772
+ @stats.should_not be_cbet_opportunity(@button[:screen_name])
773
+ @stats.should_not be_cbet_opportunity(@bb[:screen_name])
774
+ end
775
+ end
591
776
 
592
- describe HandStatistics, "when measuring blind attacks" do
593
- before(:each) do
594
- @stats = HandStatistics.new
595
- register_street :prelude
596
- @stats.register_player @utg = next_sample_player(:screen_name => "utg", :seat => 2)
597
- @stats.register_player @cutoff = next_sample_player(:screen_name => "cutoff", :seat => 4)
598
- @stats.register_player @button = next_sample_player(:screen_name => "button", :seat => 6)
599
- @stats.register_player @sb = next_sample_player(:screen_name => "sb", :seat => 8)
600
- @stats.register_player @bb = next_sample_player(:screen_name => "bb", :seat => 10)
601
- @stats.register_button(@button[:seat])
602
- register_post(@sb, 1)
603
- register_post(@bb, 2)
604
- register_street :preflop
605
- end
777
+ context "blind attacks" do
778
+ before(:each) do
779
+ @stats = HandStatistics.new
780
+ register_street :prelude
781
+ @stats.register_player @utg = next_sample_player(:screen_name => "utg", :seat => 2)
782
+ @stats.register_player @cutoff = next_sample_player(:screen_name => "cutoff", :seat => 4)
783
+ @stats.register_player @button = next_sample_player(:screen_name => "button", :seat => 6)
784
+ @stats.register_player @sb = next_sample_player(:screen_name => "sb", :seat => 8)
785
+ @stats.register_player @bb = next_sample_player(:screen_name => "bb", :seat => 10)
786
+ @stats.register_button(@button[:seat])
787
+ register_post(@sb, 1)
788
+ register_post(@bb, 2)
789
+ register_street :preflop
790
+ end
606
791
 
607
- it "should identify cutoff opportunities if everybody folds to him" do
608
- register_fold(@utg)
609
- register_call(@cutoff, 2)
610
- register_call(@button, 2)
611
- register_call(@sb, 1)
612
- register_check(@bb)
613
- @stats.should be_blind_attack_opportunity("cutoff")
614
- @stats.should_not be_blind_attack_opportunity_taken("cutoff")
615
- @stats.should_not be_blind_attack_opportunity("button")
616
- @stats.should_not be_blind_attack_opportunity("utg")
617
- @stats.should_not be_blind_attack_opportunity("sb")
618
- @stats.should_not be_blind_attack_opportunity("bb")
619
- end
792
+ it "should identify cutoff opportunities if everybody folds to him" do
793
+ register_fold(@utg)
794
+ register_call(@cutoff, 2)
795
+ register_call(@button, 2)
796
+ register_call(@sb, 1)
797
+ register_check(@bb)
798
+ @stats.should be_blind_attack_opportunity("cutoff")
799
+ @stats.should_not be_blind_attack_opportunity_taken("cutoff")
800
+ @stats.should_not be_blind_attack_opportunity("button")
801
+ @stats.should_not be_blind_attack_opportunity("utg")
802
+ @stats.should_not be_blind_attack_opportunity("sb")
803
+ @stats.should_not be_blind_attack_opportunity("bb")
804
+ end
620
805
 
621
- it "should properly identify cutoff and button opportunities if everybody folds to button" do
622
- register_fold(@utg)
623
- register_fold(@cutoff)
624
- register_call(@button, 2)
625
- register_call(@sb, 1)
626
- register_check(@bb)
627
- @stats.should be_blind_attack_opportunity("cutoff")
628
- @stats.should_not be_blind_attack_opportunity_taken("cutoff")
629
- @stats.should be_blind_attack_opportunity("button")
630
- @stats.should_not be_blind_attack_opportunity_taken("button")
631
- @stats.should_not be_blind_attack_opportunity("utg")
632
- @stats.should_not be_blind_attack_opportunity("sb")
633
- @stats.should_not be_blind_attack_opportunity("bb")
634
- end
806
+ it "should properly identify cutoff and button opportunities if everybody folds to button" do
807
+ register_fold(@utg)
808
+ register_fold(@cutoff)
809
+ register_call(@button, 2)
810
+ register_call(@sb, 1)
811
+ register_check(@bb)
812
+ @stats.should be_blind_attack_opportunity("cutoff")
813
+ @stats.should_not be_blind_attack_opportunity_taken("cutoff")
814
+ @stats.should be_blind_attack_opportunity("button")
815
+ @stats.should_not be_blind_attack_opportunity_taken("button")
816
+ @stats.should_not be_blind_attack_opportunity("utg")
817
+ @stats.should_not be_blind_attack_opportunity("sb")
818
+ @stats.should_not be_blind_attack_opportunity("bb")
819
+ end
635
820
 
636
- it "should identify cutoff and button attack opportunities if button raises first-in" do
637
- register_fold(@utg)
638
- register_fold(@cutoff)
639
- register_raise_to(@button, 7)
640
- register_call(@sb, 6)
641
- register_fold(@bb)
642
- register_fold(@utg)
643
- register_fold(@cutoff)
644
- @stats.should be_blind_attack_opportunity("cutoff")
645
- @stats.should_not be_blind_attack_opportunity_taken("cutoff")
646
- @stats.should be_blind_attack_opportunity("button")
647
- @stats.should be_blind_attack_opportunity_taken("button")
648
- @stats.should_not be_blind_attack_opportunity("utg")
649
- @stats.should_not be_blind_attack_opportunity("sb")
650
- @stats.should_not be_blind_attack_opportunity("bb")
651
- end
821
+ it "should identify cutoff and button attack opportunities if button raises first-in" do
822
+ register_fold(@utg)
823
+ register_fold(@cutoff)
824
+ register_raise_to(@button, 7)
825
+ register_call(@sb, 6)
826
+ register_fold(@bb)
827
+ register_fold(@utg)
828
+ register_fold(@cutoff)
829
+ @stats.should be_blind_attack_opportunity("cutoff")
830
+ @stats.should_not be_blind_attack_opportunity_taken("cutoff")
831
+ @stats.should be_blind_attack_opportunity("button")
832
+ @stats.should be_blind_attack_opportunity_taken("button")
833
+ @stats.should_not be_blind_attack_opportunity("utg")
834
+ @stats.should_not be_blind_attack_opportunity("sb")
835
+ @stats.should_not be_blind_attack_opportunity("bb")
836
+ end
652
837
 
653
- it "should identify no attack opportunities if utg raises first in" do
654
- register_raise_to(@utg, 7)
655
- register_call(@cutoff, 7)
656
- register_call(@button, 7)
657
- register_call(@sb, 6)
658
- register_fold(@bb)
659
- @stats.should_not be_blind_attack_opportunity("cutoff")
660
- @stats.should_not be_blind_attack_opportunity("button")
661
- @stats.should_not be_blind_attack_opportunity("utg")
662
- @stats.should_not be_blind_attack_opportunity("sb")
663
- @stats.should_not be_blind_attack_opportunity("bb")
664
- end
838
+ it "should identify no attack opportunities if utg raises first in" do
839
+ register_raise_to(@utg, 7)
840
+ register_call(@cutoff, 7)
841
+ register_call(@button, 7)
842
+ register_call(@sb, 6)
843
+ register_fold(@bb)
844
+ @stats.should_not be_blind_attack_opportunity("cutoff")
845
+ @stats.should_not be_blind_attack_opportunity("button")
846
+ @stats.should_not be_blind_attack_opportunity("utg")
847
+ @stats.should_not be_blind_attack_opportunity("sb")
848
+ @stats.should_not be_blind_attack_opportunity("bb")
849
+ end
665
850
 
666
- it "should identify attack opportunities only for button if cutoff raises first in" do
667
- register_raise_to(@utg, 7)
668
- register_call(@cutoff, 7)
669
- register_call(@button, 7)
670
- register_call(@sb, 6)
671
- register_fold(@bb)
672
- @stats.should_not be_blind_attack_opportunity("cutoff")
673
- @stats.should_not be_blind_attack_opportunity("button")
674
- @stats.should_not be_blind_attack_opportunity("utg")
675
- @stats.should_not be_blind_attack_opportunity("sb")
676
- @stats.should_not be_blind_attack_opportunity("bb")
677
- end
678
- end
851
+ it "should identify attack opportunities only for button if cutoff raises first in" do
852
+ register_raise_to(@utg, 7)
853
+ register_call(@cutoff, 7)
854
+ register_call(@button, 7)
855
+ register_call(@sb, 6)
856
+ register_fold(@bb)
857
+ @stats.should_not be_blind_attack_opportunity("cutoff")
858
+ @stats.should_not be_blind_attack_opportunity("button")
859
+ @stats.should_not be_blind_attack_opportunity("utg")
860
+ @stats.should_not be_blind_attack_opportunity("sb")
861
+ @stats.should_not be_blind_attack_opportunity("bb")
862
+ end
863
+ end
679
864
 
680
- describe HandStatistics, "measuring blind defense" do
681
- before(:each) do
682
- @stats = HandStatistics.new
683
- register_street :prelude
684
- @stats.register_player @utg = next_sample_player(:screen_name => "utg", :seat => 2)
685
- @stats.register_player @cutoff = next_sample_player(:screen_name => "cutoff", :seat => 4)
686
- @stats.register_player @button = next_sample_player(:screen_name => "button", :seat => 6)
687
- @stats.register_player @sb = next_sample_player(:screen_name => "sb", :seat => 8)
688
- @stats.register_player @bb = next_sample_player(:screen_name => "bb", :seat => 10)
689
- @stats.register_button(@button[:seat])
690
- register_post(@sb, 1)
691
- register_post(@bb, 2)
692
- register_street :preflop
693
- end
865
+ context "blind defense" do
866
+ before(:each) do
867
+ @stats = HandStatistics.new
868
+ register_street :prelude
869
+ @stats.register_player @utg = next_sample_player(:screen_name => "utg", :seat => 2)
870
+ @stats.register_player @cutoff = next_sample_player(:screen_name => "cutoff", :seat => 4)
871
+ @stats.register_player @button = next_sample_player(:screen_name => "button", :seat => 6)
872
+ @stats.register_player @sb = next_sample_player(:screen_name => "sb", :seat => 8)
873
+ @stats.register_player @bb = next_sample_player(:screen_name => "bb", :seat => 10)
874
+ @stats.register_button(@button[:seat])
875
+ register_post(@sb, 1)
876
+ register_post(@bb, 2)
877
+ register_street :preflop
878
+ end
694
879
 
695
- it "should not identify a blind attack when nobody raises" do
696
- register_fold(@utg)
697
- register_call(@cutoff, 2)
698
- register_call(@button, 2)
699
- register_call(@sb, 1)
700
- register_check(@bb)
701
- @stats.should_not be_blind_defense_opportunity("sb")
702
- @stats.should_not be_blind_defense_opportunity("bb")
703
- @stats.should_not be_blind_defense_opportunity("utg")
704
- @stats.should_not be_blind_defense_opportunity("cutoff")
705
- @stats.should_not be_blind_defense_opportunity("button")
706
- end
880
+ it "should not identify a blind attack when nobody raises" do
881
+ register_fold(@utg)
882
+ register_call(@cutoff, 2)
883
+ register_call(@button, 2)
884
+ register_call(@sb, 1)
885
+ register_check(@bb)
886
+ @stats.should_not be_blind_defense_opportunity("sb")
887
+ @stats.should_not be_blind_defense_opportunity("bb")
888
+ @stats.should_not be_blind_defense_opportunity("utg")
889
+ @stats.should_not be_blind_defense_opportunity("cutoff")
890
+ @stats.should_not be_blind_defense_opportunity("button")
891
+ end
707
892
 
708
- it "should not identify a blind attack when a non-attacker raises first" do
709
- register_raise_to(@utg, 7)
710
- register_fold(@cutoff)
711
- register_call(@button, 7)
712
- register_call(@sb, 6)
713
- register_check(@bb)
714
- @stats.should_not be_blind_defense_opportunity("sb")
715
- @stats.should_not be_blind_defense_opportunity("bb")
716
- @stats.should_not be_blind_defense_opportunity("utg")
717
- @stats.should_not be_blind_defense_opportunity("cutoff")
718
- @stats.should_not be_blind_defense_opportunity("button")
719
- end
893
+ it "should not identify a blind attack when a non-attacker raises first" do
894
+ register_raise_to(@utg, 7)
895
+ register_fold(@cutoff)
896
+ register_call(@button, 7)
897
+ register_call(@sb, 6)
898
+ register_check(@bb)
899
+ @stats.should_not be_blind_defense_opportunity("sb")
900
+ @stats.should_not be_blind_defense_opportunity("bb")
901
+ @stats.should_not be_blind_defense_opportunity("utg")
902
+ @stats.should_not be_blind_defense_opportunity("cutoff")
903
+ @stats.should_not be_blind_defense_opportunity("button")
904
+ end
720
905
 
721
- it "should not identify a blind attack when a non-attacker raises, even if attacker re-raises" do
722
- register_raise_to(@utg, 7)
723
- register_fold(@cutoff)
724
- register_raise_to(@button, 15)
725
- register_call(@sb, 14)
726
- register_call(@bb, 13)
727
- register_call(@utg, 8)
728
- @stats.should_not be_blind_defense_opportunity("sb")
729
- @stats.should_not be_blind_defense_opportunity("bb")
730
- @stats.should_not be_blind_defense_opportunity("utg")
731
- @stats.should_not be_blind_defense_opportunity("cutoff")
732
- @stats.should_not be_blind_defense_opportunity("button")
733
- end
906
+ it "should not identify a blind attack when a non-attacker raises, even if attacker re-raises" do
907
+ register_raise_to(@utg, 7)
908
+ register_fold(@cutoff)
909
+ register_raise_to(@button, 15)
910
+ register_call(@sb, 14)
911
+ register_call(@bb, 13)
912
+ register_call(@utg, 8)
913
+ @stats.should_not be_blind_defense_opportunity("sb")
914
+ @stats.should_not be_blind_defense_opportunity("bb")
915
+ @stats.should_not be_blind_defense_opportunity("utg")
916
+ @stats.should_not be_blind_defense_opportunity("cutoff")
917
+ @stats.should_not be_blind_defense_opportunity("button")
918
+ end
734
919
 
735
- it "should identify a blind attack when button raises first-in, taken when blind calls" do
736
- register_fold(@utg)
737
- register_fold(@cutoff)
738
- register_raise_to(@button, 7)
739
- register_call(@sb, 6)
740
- register_fold(@bb)
741
- register_fold(@utg)
742
- register_fold(@cutoff)
743
- @stats.should be_blind_defense_opportunity("sb")
744
- @stats.should be_blind_defense_opportunity_taken("sb")
745
- @stats.should_not be_blind_defense_opportunity("bb")
746
- @stats.should_not be_blind_defense_opportunity("utg")
747
- @stats.should_not be_blind_defense_opportunity("cutoff")
748
- @stats.should_not be_blind_defense_opportunity("button")
749
- end
920
+ it "should identify a blind attack when button raises first-in, taken when blind calls" do
921
+ register_fold(@utg)
922
+ register_fold(@cutoff)
923
+ register_raise_to(@button, 7)
924
+ register_call(@sb, 6)
925
+ register_fold(@bb)
926
+ register_fold(@utg)
927
+ register_fold(@cutoff)
928
+ @stats.should be_blind_defense_opportunity("sb")
929
+ @stats.should be_blind_defense_opportunity_taken("sb")
930
+ @stats.should_not be_blind_defense_opportunity("bb")
931
+ @stats.should_not be_blind_defense_opportunity("utg")
932
+ @stats.should_not be_blind_defense_opportunity("cutoff")
933
+ @stats.should_not be_blind_defense_opportunity("button")
934
+ end
750
935
 
751
- it "should identify a blind attack when cutoff raises first-in and button folds, not taken on fold, and taken on raise" do
752
- register_fold(@utg)
753
- register_raise_to(@cutoff, 7)
754
- register_fold(@button)
755
- register_fold(@sb)
756
- register_raise_to(@bb,200)
757
- register_fold(@utg)
758
- register_fold(@cutoff)
759
- @stats.should be_blind_defense_opportunity("sb")
760
- @stats.should_not be_blind_defense_opportunity_taken("sb")
761
- @stats.should be_blind_defense_opportunity("bb")
762
- @stats.should be_blind_defense_opportunity_taken("bb")
763
- @stats.should_not be_blind_defense_opportunity("utg")
764
- @stats.should_not be_blind_defense_opportunity("cutoff")
765
- @stats.should_not be_blind_defense_opportunity("button")
766
- end
936
+ it "should identify a blind attack when cutoff raises first-in and button folds, not taken on fold, and taken on raise" do
937
+ register_fold(@utg)
938
+ register_raise_to(@cutoff, 7)
939
+ register_fold(@button)
940
+ register_fold(@sb)
941
+ register_raise_to(@bb,200)
942
+ register_fold(@utg)
943
+ register_fold(@cutoff)
944
+ @stats.should be_blind_defense_opportunity("sb")
945
+ @stats.should_not be_blind_defense_opportunity_taken("sb")
946
+ @stats.should be_blind_defense_opportunity("bb")
947
+ @stats.should be_blind_defense_opportunity_taken("bb")
948
+ @stats.should_not be_blind_defense_opportunity("utg")
949
+ @stats.should_not be_blind_defense_opportunity("cutoff")
950
+ @stats.should_not be_blind_defense_opportunity("button")
951
+ end
767
952
 
768
- it "should not identify a blind attack when cutoff raises first-in and button calls" do
769
- register_fold(@utg)
770
- register_raise_to(@cutoff, 7)
771
- register_call(@button, 7)
772
- register_call(@sb, 6)
773
- register_fold(@bb)
774
- register_fold(@utg)
775
- @stats.should_not be_blind_defense_opportunity("sb")
776
- @stats.should_not be_blind_defense_opportunity("bb")
777
- @stats.should_not be_blind_defense_opportunity("utg")
778
- @stats.should_not be_blind_defense_opportunity("cutoff")
779
- @stats.should_not be_blind_defense_opportunity("button")
780
- end
953
+ it "should not identify a blind attack when cutoff raises first-in and button calls" do
954
+ register_fold(@utg)
955
+ register_raise_to(@cutoff, 7)
956
+ register_call(@button, 7)
957
+ register_call(@sb, 6)
958
+ register_fold(@bb)
959
+ register_fold(@utg)
960
+ @stats.should_not be_blind_defense_opportunity("sb")
961
+ @stats.should_not be_blind_defense_opportunity("bb")
962
+ @stats.should_not be_blind_defense_opportunity("utg")
963
+ @stats.should_not be_blind_defense_opportunity("cutoff")
964
+ @stats.should_not be_blind_defense_opportunity("button")
965
+ end
781
966
 
782
- it "should not identify a blind attack when cutoff raises first-in and button re-raises" do
783
- register_fold(@utg)
784
- register_raise_to(@cutoff, 7)
785
- register_raise_to(@button, 15)
786
- register_call(@sb, 6)
787
- register_fold(@bb)
788
- register_fold(@utg)
789
- @stats.should_not be_blind_defense_opportunity("sb")
790
- @stats.should_not be_blind_defense_opportunity("bb")
791
- @stats.should_not be_blind_defense_opportunity("utg")
792
- @stats.should_not be_blind_defense_opportunity("cutoff")
793
- @stats.should_not be_blind_defense_opportunity("button")
794
- end
795
- end
967
+ it "should not identify a blind attack when cutoff raises first-in and button re-raises" do
968
+ register_fold(@utg)
969
+ register_raise_to(@cutoff, 7)
970
+ register_raise_to(@button, 15)
971
+ register_call(@sb, 6)
972
+ register_fold(@bb)
973
+ register_fold(@utg)
974
+ @stats.should_not be_blind_defense_opportunity("sb")
975
+ @stats.should_not be_blind_defense_opportunity("bb")
976
+ @stats.should_not be_blind_defense_opportunity("utg")
977
+ @stats.should_not be_blind_defense_opportunity("cutoff")
978
+ @stats.should_not be_blind_defense_opportunity("button")
979
+ end
980
+ end
796
981
 
797
- describe HandStatistics, "when reporting statistics" do
798
- before(:each) do
799
- @stats = HandStatistics.new
800
- @stats.register_player @seat2 = next_sample_player(:seat => 2, :screen_name => "seat2")
801
- @stats.register_player @seat4 = next_sample_player(:seat => 4, :screen_name => "seat4")
802
- @stats.register_player @seat6 = next_sample_player(:seat => 6, :screen_name => "seat6")
803
- @stats.register_player @seat8 = next_sample_player(:seat => 8, :screen_name => "seat8")
804
- @blind_attack_plugin = @stats.plugins.find{|each| each.is_a? BlindAttackStatistics}
805
- @cash_plugin = @stats.plugins.find{|each| each.is_a? CashStatistics}
806
- @continuation_bet_plugin = @stats.plugins.find{|each| each.is_a? ContinuationBetStatistics}
807
- @aggression_plugin = @stats.plugins.find{|each| each.is_a? AggressionStatistics}
808
- @preflop_raise_plugin = @stats.plugins.find{|each| each.is_a? PreflopRaiseStatistics}
809
- @reports = {}
810
- end
982
+ context "per street" do
983
+ before(:each) do
984
+ @stats = HandStatistics.new
985
+ register_street :prelude
986
+ @stats.register_player @utg = next_sample_player(:screen_name => "utg", :seat => 2)
987
+ @stats.register_player @cutoff = next_sample_player(:screen_name => "cutoff", :seat => 4)
988
+ @stats.register_player @button = next_sample_player(:screen_name => "button", :seat => 6)
989
+ @stats.register_player @sb = next_sample_player(:screen_name => "sb", :seat => 8)
990
+ @stats.register_player @bb = next_sample_player(:screen_name => "bb", :seat => 10)
991
+ @players = [@utg, @cutoff, @button, @sb, @bb]
992
+ @player_names = @players.collect{|each| each[:screen_name]}
993
+ @stats.register_button(@button[:seat])
994
+ register_post(@sb, 1)
995
+ register_post(@bb, 2)
996
+ @players.each{|each| register_ante(each, 0.25)}
997
+ register_street :preflop
998
+ @nil_saw_street_results = {:flop => nil, :turn => nil, :river => nil, :showdown => nil}
999
+ @nil_won_street_results = @nil_saw_street_results.merge(:preflop => nil)
1000
+ @saw_street_expected_results = {
1001
+ "utg" => @nil_saw_street_results.clone,
1002
+ "cutoff" => @nil_saw_street_results.clone,
1003
+ "button" => @nil_saw_street_results.clone,
1004
+ "sb" => @nil_saw_street_results.clone,
1005
+ "bb" => @nil_saw_street_results.clone
1006
+ }
1007
+ @won_street_expected_results = {
1008
+ "utg" => @nil_won_street_results.clone,
1009
+ "cutoff" => @nil_won_street_results.clone,
1010
+ "button" => @nil_won_street_results.clone,
1011
+ "sb" => @nil_won_street_results.clone,
1012
+ "bb" => @nil_won_street_results.clone
1013
+ }
1014
+ end
1015
+ context "with no action" do
1016
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1017
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1018
+ end
1019
+ context "with preflop action" do
1020
+ before(:each) do
1021
+ register_raise_to(@utg, 4)
1022
+ register_raise_to(@cutoff, 8)
1023
+ register_call(@button, 8)
1024
+ register_fold(@sb)
1025
+ for player in ["utg", "cutoff", "button", "sb", "bb"]
1026
+ @won_street_expected_results[player].update(:preflop => false)
1027
+ end
1028
+ end
1029
+ context "but no winner" do
1030
+ before(:each) do
1031
+ register_fold(@bb)
1032
+ register_call(@utg,4)
1033
+ end
1034
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1035
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1036
+ end
1037
+ context "and an uncontested winner" do
1038
+ before(:each) do
1039
+ register_raise_to(@bb, 100)
1040
+ register_fold(@utg)
1041
+ register_fold(@cutoff)
1042
+ register_fold(@button)
1043
+ register_win(@bb, 9.25)
1044
+ @won_street_expected_results["bb"][:preflop] = true
1045
+ end
1046
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1047
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1048
+ end
1049
+ context "and a showdown" do
1050
+ before(:each) do
1051
+ register_raise_to(@bb, 100)
1052
+ register_fold(@utg)
1053
+ register_fold(@cutoff)
1054
+ register_call(@button, 100)
1055
+ register_street(:showdown)
1056
+ register_muck(@button)
1057
+ register_win(@bb, 9.25)
1058
+ @won_street_expected_results["bb"][:preflop] = true
1059
+ @won_street_expected_results["bb"][:showdown] = true
1060
+ @won_street_expected_results["button"][:showdown] = false
1061
+ @saw_street_expected_results["bb"][:showdown] = true
1062
+ @saw_street_expected_results["button"][:showdown] = true
1063
+ end
1064
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1065
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1066
+ end
1067
+ context ", flop action" do
1068
+ before(:each) do
1069
+ register_call(@bb, 6)
1070
+ register_call(@utg, 4)
1071
+ register_street(:flop)
1072
+ register_check(@utg)
1073
+ register_bet(@cutoff, 10)
1074
+ register_fold(@button)
1075
+ ["utg", "cutoff", "button", "bb"].each{|each|@saw_street_expected_results[each][:flop] = true}
1076
+ ["utg", "cutoff", "button", "bb"].each{|each|@won_street_expected_results[each][:flop] = false}
1077
+ end
1078
+ context "but no winner" do
1079
+ before(:each) do
1080
+ register_raise_to(@bb, 20)
1081
+ register_call(@utg, 14)
1082
+ register_call(@cutoff, 10)
1083
+ end
1084
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1085
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1086
+ end
1087
+ context "and an uncontested winner" do
1088
+ before(:each) do
1089
+ register_fold(@bb)
1090
+ register_fold(@utg)
1091
+ register_win(@cutoff, 20)
1092
+ ["utg", "cutoff", "button", "bb"].each{|each|@won_street_expected_results[each][:flop] = (each=="cutoff")}
1093
+ end
1094
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1095
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1096
+ end
1097
+ context "and a showdown" do
1098
+ before(:each) do
1099
+ register_fold(@bb)
1100
+ register_call(@utg, 10)
1101
+ register_street(:showdown)
1102
+ register_muck(@cutoff)
1103
+ register_win(@button, 20)
1104
+ ["utg", "cutoff", "button", "bb"].each{|each|@won_street_expected_results[each][:flop] = (each=="button")}
1105
+ ["cutoff", "button"].each{|each|@saw_street_expected_results[each][:showdown] = true}
1106
+ ["cutoff", "button"].each{|each|@won_street_expected_results[each][:showdown] = (each=="button")}
1107
+ end
1108
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1109
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1110
+ end
1111
+ context ", turn action" do
1112
+ before(:each) do
1113
+ register_call(@bb, 10)
1114
+ register_call(@utg, 10)
1115
+ register_street(:turn)
1116
+ register_check(@bb)
1117
+ register_bet(@utg, 10)
1118
+ register_fold(@cutoff)
1119
+ ["utg", "cutoff", "bb"].each{|each|@saw_street_expected_results[each][:turn] = true}
1120
+ ["utg", "cutoff", "bb"].each{|each|@won_street_expected_results[each][:turn] = false}
1121
+ end
1122
+ context "but no winner" do
1123
+ before(:each) do
1124
+ register_call(@bb, 10)
1125
+ end
1126
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1127
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1128
+ end
1129
+ context "and an uncontested winner" do
1130
+ before(:each) do
1131
+ register_fold(@bb)
1132
+ register_win(@utg, 20)
1133
+ @won_street_expected_results["utg"][:turn] = true
1134
+ end
1135
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1136
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1137
+ end
1138
+ context "and a showdown" do
1139
+ before(:each) do
1140
+ register_call(@bb, 10)
1141
+ register_street(:showdown)
1142
+ register_muck(@utg)
1143
+ register_win(@bb, 20)
1144
+ @won_street_expected_results["bb"][:turn] = true
1145
+ @saw_street_expected_results["bb"][:showdown] = true
1146
+ @saw_street_expected_results["utg"][:showdown] = true
1147
+ @won_street_expected_results["bb"][:showdown] = true
1148
+ @won_street_expected_results["utg"][:showdown] = false
1149
+ end
1150
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1151
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1152
+ end
1153
+ context ", river action" do
1154
+ before(:each) do
1155
+ register_call(@bb, 10)
1156
+ register_street(:river)
1157
+ register_check(@bb)
1158
+ register_bet(@utg, 10)
1159
+ ["utg", "cutoff", "bb"].each{|each|@saw_street_expected_results[each][:river] = true}
1160
+ ["utg", "cutoff", "bb"].each{|each|@won_street_expected_results[each][:river] = false}
1161
+ end
1162
+ context "but no winner" do
1163
+ before(:each) do
1164
+ register_call(@cutoff, 10)
1165
+ end
1166
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1167
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1168
+ end
1169
+ context "and an uncontested winner" do
1170
+ before(:each) do
1171
+ register_fold(@cutoff)
1172
+ register_win(@utg, 10)
1173
+ @won_street_expected_results["utg"][:river] = true
1174
+ @won_street_expected_results["cutoff"][:river] = false
1175
+ end
1176
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1177
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1178
+ end
1179
+ context "and a showdown" do
1180
+ before(:each) do
1181
+ register_call(@cutoff, 10)
1182
+ register_street(:showdown)
1183
+ register_muck(@cutoff)
1184
+ register_win(@utg, 10)
1185
+ @won_street_expected_results["utg"][:river] = true
1186
+ @won_street_expected_results["cutoff"][:river] = false
1187
+ @saw_street_expected_results["utg"][:showdown] = true
1188
+ @saw_street_expected_results["cutoff"][:showdown] = true
1189
+ @won_street_expected_results["utg"][:showdown] = true
1190
+ @won_street_expected_results["cutoff"][:showdown] = false
1191
+ end
1192
+ it {@stats.should have_saw_street_statistics_specified_by @saw_street_expected_results}
1193
+ it {@stats.should have_won_street_statistics_specified_by @won_street_expected_results}
1194
+ end
1195
+ end
1196
+ end
1197
+ end
1198
+ end
1199
+ end
811
1200
 
812
- it "should report blind attack statistics for each player" do
813
- @blind_attack_plugin.should_receive(:blind_attack_opportunity?).exactly(@stats.players.size)
814
- @blind_attack_plugin.should_receive(:blind_attack_opportunity_taken?).exactly(@stats.players.size)
815
- @blind_attack_plugin.should_receive(:blind_defense_opportunity?).exactly(@stats.players.size)
816
- @blind_attack_plugin.should_receive(:blind_defense_opportunity_taken?).exactly(@stats.players.size)
817
- @reports = @stats.reports
818
- @stats.players.each{|each| @reports[each].should include(
819
- :is_blind_attack_opportunity,
820
- :is_blind_attack_opportunity_taken,
821
- :is_blind_defense_opportunity,
822
- :is_blind_defense_opportunity_taken
823
- )}
824
- end
1201
+ context "bet and raises by street" do
1202
+ before(:each) do
1203
+ @stats = HandStatistics.new
1204
+ register_street :prelude
1205
+ @stats.register_player @utg = next_sample_player(:screen_name => "utg", :seat => 2)
1206
+ @stats.register_player @cutoff = next_sample_player(:screen_name => "cutoff", :seat => 4)
1207
+ @stats.register_player @button = next_sample_player(:screen_name => "button", :seat => 6)
1208
+ @stats.register_player @sb = next_sample_player(:screen_name => "sb", :seat => 8)
1209
+ @stats.register_player @bb = next_sample_player(:screen_name => "bb", :seat => 10)
1210
+ @players = [@utg, @cutoff, @button, @sb, @bb]
1211
+ @player_names = @players.collect{|each| each[:screen_name]}
1212
+ @bet_specification = {}
1213
+ @call_bet_specification = {}
1214
+ @fold_to_bet_specification = {}
1215
+ for street in [:preflop, :flop, :turn, :river]
1216
+ @bet_specification[street] = []
1217
+ @call_bet_specification[street] = []
1218
+ @fold_to_bet_specification[street] = []
1219
+ for bet in 1..4
1220
+ @bet_specification[street][bet] = {}
1221
+ @call_bet_specification[street][bet] = {}
1222
+ @fold_to_bet_specification[street][bet] = {}
1223
+ for player in @player_names
1224
+ @bet_specification[street][bet][player] = nil
1225
+ @call_bet_specification[street][bet][player] = nil
1226
+ @fold_to_bet_specification[street][bet][player] = nil
1227
+ end
1228
+ end
1229
+ end
1230
+ @stats.register_button(@button[:seat])
1231
+ register_post(@sb, 1)
1232
+ register_post(@bb, 2)
1233
+ @players.each{|each| register_ante(each, 0.25)}
1234
+ register_street :preflop
1235
+ end
1236
+ context "with no raising" do
1237
+ before(:each) do
1238
+ [:flop, :turn, :river].each do |street|
1239
+ register_street street
1240
+ register_check(@sb)
1241
+ register_check(@bb)
1242
+ register_bet(@utg, 5)
1243
+ register_call(@cutoff, 5)
1244
+ register_fold(@button)
1245
+ @bet_specification[street][1]["sb"] = false
1246
+ @bet_specification[street][1]["bb"] = false
1247
+ @bet_specification[street][1]["utg"] = true
1248
+ @call_bet_specification[street][1]["cutoff"] = true
1249
+ @call_bet_specification[street][1]["button"] = false
1250
+ @fold_to_bet_specification[street][1]["cutoff"] = false
1251
+ @fold_to_bet_specification[street][1]["button"] = true
1252
+ @bet_specification[street][2]["cutoff"] = false
1253
+ @bet_specification[street][2]["button"] = false
1254
+ @stats.should have_last_aggr(street, "utg")
1255
+ end
1256
+ end
1257
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1258
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1259
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1260
+ end
1261
+
1262
+ context "after 1 raise" do
1263
+ context "preflop" do
1264
+ before(:each) do
1265
+ register_call(@utg, 2)
1266
+ register_fold(@cutoff)
1267
+ register_raise_to(@button, 5)
1268
+ register_call(@sb, 4)
1269
+ register_fold(@bb)
1270
+ @call_bet_specification[:preflop][1]["utg"] = true
1271
+ @fold_to_bet_specification[:preflop][1]["utg"] = false
1272
+ @bet_specification[:preflop][2]["utg"] = false
1273
+ @call_bet_specification[:preflop][1]["cutoff"] = false
1274
+ @fold_to_bet_specification[:preflop][1]["cutoff"] = true
1275
+ @bet_specification[:preflop][2]["cutoff"] = false
1276
+ @call_bet_specification[:preflop][1]["button"] = false
1277
+ @fold_to_bet_specification[:preflop][1]["button"] = false
1278
+ @bet_specification[:preflop][2]["button"] = true
1279
+ @call_bet_specification[:preflop][2]["sb"] = true
1280
+ @fold_to_bet_specification[:preflop][2]["sb"] = false
1281
+ @bet_specification[:preflop][3]["sb"] = false
1282
+ @call_bet_specification[:preflop][2]["bb"] = false
1283
+ @fold_to_bet_specification[:preflop][2]["bb"] = true
1284
+ @bet_specification[:preflop][3]["bb"] = false
1285
+ @stats.should have_last_aggr(:preflop, "button")
1286
+ end
1287
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1288
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1289
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1290
+ end
1291
+ context "other streets" do
1292
+ before(:each) do
1293
+ [:flop, :turn, :river].each do |street|
1294
+ register_street street
1295
+ register_bet(@utg, 5)
1296
+ register_raise_to(@cutoff, 10)
1297
+ register_fold(@button)
1298
+ register_call(@sb, 10)
1299
+ register_raise_to(@bb, 20)
1300
+ @bet_specification[street][1]["utg"] = true
1301
+ @call_bet_specification[street][1]["cutoff"] = false
1302
+ @fold_to_bet_specification[street][1]["cutoff"] = false
1303
+ @bet_specification[street][2]["cutoff"] = true
1304
+ @call_bet_specification[street][2]["button"] = false
1305
+ @fold_to_bet_specification[street][2]["button"] = true
1306
+ @bet_specification[street][3]["button"] = false
1307
+ @call_bet_specification[street][2]["sb"] = true
1308
+ @fold_to_bet_specification[street][2]["sb"] = false
1309
+ @bet_specification[street][3]["sb"] = false
1310
+ @call_bet_specification[street][2]["bb"] = false
1311
+ @fold_to_bet_specification[street][2]["bb"] = false
1312
+ @bet_specification[street][3]["bb"] = true
1313
+ @stats.should have_last_aggr(street, "bb")
1314
+ end
1315
+ end
1316
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1317
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1318
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1319
+ end
1320
+ end
1321
+
1322
+ context "after 2 raises" do
1323
+ context "preflop" do
1324
+ before(:each) do
1325
+ register_raise_to(@utg, 10)
1326
+ register_raise_to(@cutoff, 20)
1327
+ register_fold(@button)
1328
+ register_call(@sb, 19)
1329
+ register_raise_to(@bb, 50)
1330
+ @call_bet_specification[:preflop][1]["utg"] = false
1331
+ @fold_to_bet_specification[:preflop][1]["utg"] = false
1332
+ @bet_specification[:preflop][2]["utg"] = true
1333
+ @call_bet_specification[:preflop][2]["cutoff"] = false
1334
+ @fold_to_bet_specification[:preflop][2]["cutoff"] = false
1335
+ @bet_specification[:preflop][3]["cutoff"] = true
1336
+ @call_bet_specification[:preflop][3]["button"] = false
1337
+ @fold_to_bet_specification[:preflop][3]["button"] = true
1338
+ @bet_specification[:preflop][4]["button"] = false
1339
+ @call_bet_specification[:preflop][3]["sb"] = true
1340
+ @fold_to_bet_specification[:preflop][3]["sb"] = false
1341
+ @bet_specification[:preflop][4]["sb"] = false
1342
+ @call_bet_specification[:preflop][3]["bb"] = false
1343
+ @fold_to_bet_specification[:preflop][3]["bb"] = false
1344
+ @bet_specification[:preflop][4]["bb"] = true
1345
+ @stats.should have_last_aggr(:preflop, "bb")
1346
+ end
1347
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1348
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1349
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1350
+ end
1351
+ context "other streets" do
1352
+ before(:each) do
1353
+ [:flop, :turn, :river].each do |street|
1354
+ register_street street
1355
+ register_bet(@sb, 5)
1356
+ register_raise_to(@bb, 10)
1357
+ register_raise_to(@utg, 50)
1358
+ register_fold(@cutoff)
1359
+ register_call(@button, 50)
1360
+ register_raise_to(@sb, 250)
1361
+ @bet_specification[street][1]["sb"] = true
1362
+ @call_bet_specification[street][1]["bb"] = false
1363
+ @fold_to_bet_specification[street][1]["bb"] = false
1364
+ @bet_specification[street][2]["bb"] = true
1365
+ @call_bet_specification[street][2]["utg"] = false
1366
+ @fold_to_bet_specification[street][2]["utg"] = false
1367
+ @bet_specification[street][3]["utg"] = true
1368
+ @call_bet_specification[street][3]["cutoff"] = false
1369
+ @fold_to_bet_specification[street][3]["cutoff"] = true
1370
+ @bet_specification[street][4]["cutoff"] = false
1371
+ @call_bet_specification[street][3]["button"] = true
1372
+ @fold_to_bet_specification[street][3]["button"] = false
1373
+ @bet_specification[street][4]["button"] = false
1374
+ @call_bet_specification[street][3]["sb"] = false
1375
+ @fold_to_bet_specification[street][3]["sb"] = false
1376
+ @bet_specification[street][4]["sb"] = true
1377
+ @stats.should have_last_aggr(street,"sb")
1378
+ end
1379
+ end
1380
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1381
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1382
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1383
+ end
1384
+ end
1385
+
1386
+ context "after 3 raises" do
1387
+ context "preflop" do
1388
+ before(:each) do
1389
+ register_raise_to(@utg, 10)
1390
+ register_raise_to(@cutoff, 50)
1391
+ register_raise_to(@button, 150)
1392
+ register_fold(@sb)
1393
+ register_call(@bb, 148)
1394
+ register_raise_to(@utg, 480)
1395
+ @call_bet_specification[:preflop][1]["utg"] = false
1396
+ @fold_to_bet_specification[:preflop][1]["utg"] = false
1397
+ @bet_specification[:preflop][2]["utg"] = true
1398
+ @call_bet_specification[:preflop][2]["cutoff"] = false
1399
+ @fold_to_bet_specification[:preflop][2]["cutoff"] = false
1400
+ @bet_specification[:preflop][3]["cutoff"] = true
1401
+ @call_bet_specification[:preflop][3]["button"] = false
1402
+ @fold_to_bet_specification[:preflop][3]["button"] = false
1403
+ @bet_specification[:preflop][4]["button"] = true
1404
+ @call_bet_specification[:preflop][4]["sb"] = false
1405
+ @call_bet_specification[:preflop][4]["bb"] = true
1406
+ @call_bet_specification[:preflop][4]["utg"] = false
1407
+ @fold_to_bet_specification[:preflop][4]["sb"] = true
1408
+ @fold_to_bet_specification[:preflop][4]["bb"] = false
1409
+ @fold_to_bet_specification[:preflop][4]["utg"] = false
1410
+ @stats.should have_last_aggr(:preflop, "utg")
1411
+ end
1412
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1413
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1414
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1415
+ end
1416
+ context "other streets" do
1417
+ before(:each) do
1418
+ [:flop, :turn, :river].each do |street|
1419
+ register_street street
1420
+ register_bet(@sb, 5)
1421
+ register_raise_to(@bb, 10)
1422
+ register_raise_to(@utg, 50)
1423
+ register_raise_to(@cutoff, 150)
1424
+ register_fold(@button)
1425
+ register_call(@sb, 145)
1426
+ register_raise_to(@bb, 450)
1427
+ @bet_specification[street][1]["sb"] = true
1428
+ @call_bet_specification[street][1]["bb"] = false
1429
+ @fold_to_bet_specification[street][1]["bb"] = false
1430
+ @bet_specification[street][2]["bb"] = true
1431
+ @call_bet_specification[street][2]["utg"] = false
1432
+ @fold_to_bet_specification[street][2]["utg"] = false
1433
+ @bet_specification[street][3]["utg"] = true
1434
+ @call_bet_specification[street][3]["cutoff"] = false
1435
+ @fold_to_bet_specification[street][3]["cutoff"] = false
1436
+ @bet_specification[street][4]["cutoff"] = true
1437
+ @call_bet_specification[street][4]["button"] = false
1438
+ @call_bet_specification[street][4]["sb"] = true
1439
+ @call_bet_specification[street][4]["bb"] = false
1440
+ @fold_to_bet_specification[street][4]["button"] = true
1441
+ @fold_to_bet_specification[street][4]["sb"] = false
1442
+ @fold_to_bet_specification[street][4]["bb"] = false
1443
+ @stats.should have_last_aggr(street, "bb")
1444
+ end
1445
+ end
1446
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1447
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1448
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1449
+ end
1450
+ end
1451
+
1452
+ context "after 4 raises" do
1453
+ context "preflop" do
1454
+ before(:each) do
1455
+ register_raise_to(@utg, 10)
1456
+ register_raise_to(@cutoff, 50)
1457
+ register_raise_to(@button, 150)
1458
+ register_raise_to(@sb, 500)
1459
+ register_fold(@bb)
1460
+ register_call(@utg, 490)
1461
+ register_raise_to(@utg, 1000)
1462
+ @call_bet_specification[:preflop][1]["utg"] = false
1463
+ @fold_to_bet_specification[:preflop][1]["utg"] = false
1464
+ @bet_specification[:preflop][2]["utg"] = true
1465
+ @call_bet_specification[:preflop][2]["cutoff"] = false
1466
+ @fold_to_bet_specification[:preflop][2]["cutoff"] = false
1467
+ @bet_specification[:preflop][3]["cutoff"] = true
1468
+ @call_bet_specification[:preflop][3]["button"] = false
1469
+ @fold_to_bet_specification[:preflop][3]["button"] = false
1470
+ @bet_specification[:preflop][4]["button"] = true
1471
+ @call_bet_specification[:preflop][4]["sb"] = false
1472
+ @fold_to_bet_specification[:preflop][4]["sb"] = false
1473
+ @stats.should have_last_aggr(:preflop, "utg")
1474
+ end
1475
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1476
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1477
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1478
+ end
1479
+ context "other streets" do
1480
+ before(:each) do
1481
+ [:flop, :turn, :river].each do |street|
1482
+ register_street street
1483
+ register_bet(@sb, 5)
1484
+ register_raise_to(@bb, 10)
1485
+ register_raise_to(@utg, 50)
1486
+ register_raise_to(@cutoff, 150)
1487
+ register_raise_to(@button, 500)
1488
+ register_fold(@sb)
1489
+ register_call(@bb, 490)
1490
+ register_raise_to(@utg,1000)
1491
+ @bet_specification[street][1]["sb"] = true
1492
+ @call_bet_specification[street][1]["bb"] = false
1493
+ @fold_to_bet_specification[street][1]["bb"] = false
1494
+ @bet_specification[street][2]["bb"] = true
1495
+ @call_bet_specification[street][2]["utg"] = false
1496
+ @fold_to_bet_specification[street][2]["utg"] = false
1497
+ @bet_specification[street][3]["utg"] = true
1498
+ @call_bet_specification[street][3]["cutoff"] = false
1499
+ @fold_to_bet_specification[street][3]["cutoff"] = false
1500
+ @bet_specification[street][4]["cutoff"] = true
1501
+ @call_bet_specification[street][4]["button"] = false
1502
+ @fold_to_bet_specification[street][4]["button"] = false
1503
+ @stats.should have_last_aggr(street, "utg")
1504
+ end
1505
+ end
1506
+ it {@stats.should have_street_bet_statistics_specified_by @bet_specification, ""}
1507
+ it {@stats.should have_street_bet_statistics_specified_by @call_bet_specification, "call_"}
1508
+ it {@stats.should have_street_bet_statistics_specified_by @fold_to_bet_specification, "fold_to_"}
1509
+ end
1510
+ end
1511
+ end
1512
+
1513
+ context "cbets and dbets by street" do
1514
+ before(:each) do
1515
+ @stats = HandStatistics.new
1516
+ @stats.register_player @button = next_sample_player(:screen_name => "button")
1517
+ @stats.register_player @sb = next_sample_player(:screen_name => "sb")
1518
+ @stats.register_player @bb = next_sample_player(:screen_name => "bb")
1519
+ @stats.register_button @button[:seat]
1520
+ register_street :prelude
1521
+ register_post @sb, 1
1522
+ register_post @bb, 2
1523
+ register_street :preflop
1524
+ end
1525
+
1526
+ it "should not find cbet or dbet opportunity without a raise on the prior street" do
1527
+ register_call @button, 2
1528
+ register_call @sb, 1
1529
+ register_check @bb
1530
+ register_street :flop
1531
+ register_check @sb
1532
+ register_check @bb
1533
+ register_bet @button, 2
1534
+ puts "last_aggr_player = #{@stats.last_aggr_player.inspect}"
1535
+ ["sb", "bb", "button"].each do |player|
1536
+ @stats.cbet_flop(player).should be_nil
1537
+ @stats.call_cbet_flop(player).should be_nil
1538
+ @stats.fold_to_cbet_flop(player).should be_nil
1539
+ @stats.dbet_flop(player).should be_nil
1540
+ @stats.call_dbet_flop(player).should be_nil
1541
+ @stats.fold_to_dbet_flop(player).should be_nil
1542
+ end
1543
+ end
1544
+
1545
+ context "when button last bets each street and bets first in on the next" do
1546
+ before(:each) do
1547
+ [:flop, :turn, :river].each do |street|
1548
+ if street == :flop
1549
+ register_raise_to(@button, 100)
1550
+ register_call(@sb, 99)
1551
+ register_call(@bb, 98)
1552
+ end
1553
+ register_street street
1554
+ register_check(@sb)
1555
+ register_check(@bb)
1556
+ register_bet(@button, 100)
1557
+ register_call(@sb, 100)
1558
+ register_fold(@bb)
1559
+ end
1560
+ end
1561
+ it "should correctly compute cbet stats" do
1562
+ [:flop, :turn, :river].each do |street|
1563
+ @stats.send("cbet_#{street}", "sb").should be_nil
1564
+ @stats.send("cbet_#{street}", "bb").should be_nil
1565
+ @stats.send("cbet_#{street}", "button").should be_true
1566
+ @stats.send("call_cbet_#{street}", "sb").should be_true
1567
+ @stats.send("call_cbet_#{street}", "bb").should be_false
1568
+ @stats.send("call_cbet_#{street}", "button").should be_nil
1569
+ @stats.send("fold_to_cbet_#{street}", "sb").should be_false
1570
+ @stats.send("fold_to_cbet_#{street}", "bb").should be_true
1571
+ @stats.send("fold_to_cbet_#{street}", "button").should be_nil
1572
+ @stats.send("dbet_#{street}", "sb").should be_false
1573
+ @stats.send("dbet_#{street}", "bb").should be_false
1574
+ @stats.send("dbet_#{street}", "button").should be_nil
1575
+ [:sb, :bb, :button].each do |player|
1576
+ @stats.send("call_dbet_#{street}", "sb").should be_nil
1577
+ @stats.send("fold_to_dbet_#{street}", "bb").should be_nil
1578
+ end
1579
+ end
1580
+ end
1581
+ end
1582
+
1583
+ context "when button last bets each street and bb donk bets" do
1584
+ before(:each) do
1585
+ [:flop, :turn, :river].each do |street|
1586
+ if street == :flop
1587
+ register_raise_to(@button, 100)
1588
+ register_call(@sb, 99)
1589
+ register_call(@bb, 98)
1590
+ end
1591
+ register_street street
1592
+ register_check(@sb)
1593
+ register_bet(@bb, 100)
1594
+ register_raise_to(@button, 200)
1595
+ register_fold(@sb)
1596
+ register_call(@sb, 100)
1597
+ end
1598
+ end
1599
+ it "should correctly compute cbet stats" do
1600
+ [:flop, :turn, :river].each do |street|
1601
+ [:sb, :bb, :button].each do |player|
1602
+ @stats.send("cbet_#{street}", player).should be_nil
1603
+ @stats.send("call_cbet_#{street}", player).should be_nil
1604
+ @stats.send("fold_to_cbet_#{street}", player).should be_nil
1605
+ end
1606
+ @stats.send("dbet_#{street}", "sb").should be_false
1607
+ @stats.send("dbet_#{street}", "bb").should be_true
1608
+ @stats.send("dbet_#{street}", "button").should be_nil
1609
+ @stats.send("call_dbet_#{street}", "sb").should be_nil
1610
+ @stats.send("call_dbet_#{street}", "bb").should be_nil
1611
+ @stats.send("call_dbet_#{street}", "button").should be_false
1612
+ @stats.send("fold_to_dbet_#{street}", "sb").should be_nil
1613
+ @stats.send("fold_to_dbet_#{street}", "bb").should be_nil
1614
+ @stats.send("fold_to_dbet_#{street}", "button").should be_false
1615
+ end
1616
+ end
1617
+ end
1618
+
1619
+ context "when big blind last bets each street and bets first in on the next" do
1620
+ before(:each) do
1621
+ [:flop, :turn, :river].each do |street|
1622
+ if street == :flop
1623
+ register_call(@button, 2)
1624
+ register_call(@sb, 1)
1625
+ register_raise_to(@bb, 100)
1626
+ register_call(@button, 98)
1627
+ register_call(@sb, 98)
1628
+ end
1629
+ register_street street
1630
+ register_check(@sb)
1631
+ register_bet(@bb, 100)
1632
+ register_call(@button, 100)
1633
+ register_fold(@sb)
1634
+ end
1635
+ end
1636
+ it "should correctly compute cbet stats" do
1637
+ [:flop, :turn, :river].each do |street|
1638
+ @stats.send("cbet_#{street}", "sb").should be_nil
1639
+ @stats.send("cbet_#{street}", "bb").should be_true
1640
+ @stats.send("cbet_#{street}", "button").should be_nil
1641
+ @stats.send("call_cbet_#{street}", "sb").should be_false
1642
+ @stats.send("call_cbet_#{street}", "bb").should be_nil
1643
+ @stats.send("call_cbet_#{street}", "button").should be_true
1644
+ @stats.send("fold_to_cbet_#{street}", "sb").should be_true
1645
+ @stats.send("fold_to_cbet_#{street}", "bb").should be_nil
1646
+ @stats.send("fold_to_cbet_#{street}", "button").should be_false
1647
+ @stats.send("dbet_#{street}", "sb").should be_false
1648
+ @stats.send("dbet_#{street}", "bb").should be_nil
1649
+ @stats.send("dbet_#{street}", "button").should be_nil
1650
+ [:sb, :bb, :button].each do |player|
1651
+ @stats.send("call_dbet_#{street}", "sb").should be_nil
1652
+ @stats.send("fold_to_dbet_#{street}", "bb").should be_nil
1653
+ end
1654
+ end
1655
+ end
1656
+ end
1657
+
1658
+ context "when button is last aggressor preflop and checks on flop" do
1659
+ before(:each) do
1660
+ register_raise_to(@button, 100)
1661
+ register_call(@sb, 99)
1662
+ register_call(@bb, 98)
1663
+ register_street :flop
1664
+ register_check(@sb)
1665
+ register_check(@bb)
1666
+ register_check(@button)
1667
+ end
1668
+
1669
+ it "correctly compute cbet stats" do
1670
+ @stats.cbet_flop("sb").should be_nil
1671
+ @stats.cbet_flop("bb").should be_nil
1672
+ @stats.cbet_flop("button").should be_false
1673
+ ["sb", "bb", "button"].each do |player|
1674
+ @stats.send("call_cbet_flop", player).should be_nil
1675
+ @stats.send("fold_to_cbet_flop", player).should be_nil
1676
+ end
1677
+ @stats.dbet_flop("sb").should be_false
1678
+ @stats.dbet_flop("bb").should be_false
1679
+ @stats.dbet_flop("button").should be_nil
1680
+ [:sb, :bb, :button].each do |player|
1681
+ @stats.dbet_flop(player).should be_nil
1682
+ end
1683
+ end
1684
+ end
1685
+
1686
+ context "when button is last aggressor flop and checks on turn" do
1687
+ before(:each) do
1688
+ register_street :flop
1689
+ register_check(@sb)
1690
+ register_check(@bb)
1691
+ register_bet(@button, 100)
1692
+ register_call(@sb, 100)
1693
+ register_call(@bb, 100)
1694
+ register_street :turn
1695
+ register_check(@sb)
1696
+ register_check(@bb)
1697
+ register_check(@button)
1698
+ end
1699
+
1700
+ it "correctly compute cbet stats" do
1701
+ @stats.cbet_turn("sb").should be_nil
1702
+ @stats.cbet_turn("bb").should be_nil
1703
+ @stats.cbet_turn("button").should be_false
1704
+ ["sb", "bb", "button"].each do |player|
1705
+ @stats.send("call_cbet_turn", player).should be_nil
1706
+ @stats.send("fold_to_cbet_turn", player).should be_nil
1707
+ end
1708
+ @stats.dbet_turn("sb").should be_false
1709
+ @stats.dbet_turn("bb").should be_false
1710
+ @stats.dbet_turn("button").should be_nil
1711
+ [:sb, :bb, :button].each do |player|
1712
+ @stats.dbet_turn(player).should be_nil
1713
+ end
1714
+ end
1715
+ end
1716
+
1717
+ context "when button is last aggressor turn and checks on river" do
1718
+ before(:each) do
1719
+ register_street :turn
1720
+ register_check(@sb)
1721
+ register_check(@bb)
1722
+ register_bet(@button, 100)
1723
+ register_call(@sb, 100)
1724
+ register_call(@bb, 100)
1725
+ register_street :river
1726
+ register_check(@sb)
1727
+ register_check(@bb)
1728
+ register_check(@button)
1729
+ end
1730
+
1731
+ it "correctly compute cbet stats" do
1732
+ @stats.cbet_river("sb").should be_nil
1733
+ @stats.cbet_river("bb").should be_nil
1734
+ @stats.cbet_river("button").should be_false
1735
+ ["sb", "bb", "button"].each do |player|
1736
+ @stats.send("call_cbet_river", player).should be_nil
1737
+ @stats.send("fold_to_cbet_river", player).should be_nil
1738
+ end
1739
+ @stats.dbet_river("sb").should be_false
1740
+ @stats.dbet_river("bb").should be_false
1741
+ @stats.dbet_river("button").should be_nil
1742
+ [:sb, :bb, :button].each do |player|
1743
+ @stats.dbet_river(player).should be_nil
1744
+ end
1745
+ end
1746
+ end
1747
+ end
1748
+ end
1749
+
1750
+ context "when reporting statistics" do
1751
+ before(:each) do
1752
+ @stats = HandStatistics.new
1753
+ @stats.register_player @seat2 = next_sample_player(:seat => 2, :screen_name => "seat2")
1754
+ @stats.register_player @seat4 = next_sample_player(:seat => 4, :screen_name => "seat4")
1755
+ @stats.register_player @seat6 = next_sample_player(:seat => 6, :screen_name => "seat6")
1756
+ @stats.register_player @seat8 = next_sample_player(:seat => 8, :screen_name => "seat8")
1757
+ @stats.register_button 1
1758
+ @blind_attack_plugin = @stats.plugins.find{|each| each.is_a? BlindAttackStatistics}
1759
+ @cash_plugin = @stats.plugins.find{|each| each.is_a? CashStatistics}
1760
+ @continuation_bet_plugin = @stats.plugins.find{|each| each.is_a? ContinuationBetStatistics}
1761
+ @aggression_plugin = @stats.plugins.find{|each| each.is_a? AggressionStatistics}
1762
+ @preflop_raise_plugin = @stats.plugins.find{|each| each.is_a? PreflopRaiseStatistics}
1763
+ @street_plugin = @stats.plugins.find{|each| each.is_a? StreetStatistics}
1764
+ @street_bet_plugin = @stats.plugins.find{|each| each.is_a? StreetBetStatistics}
1765
+ @reports = {}
1766
+ end
1767
+
1768
+ it "should report blind attack statistics for each player" do
1769
+ @blind_attack_plugin.should_receive(:blind_attack_opportunity?).exactly(@stats.players.size)
1770
+ @blind_attack_plugin.should_receive(:blind_attack_opportunity_taken?).exactly(@stats.players.size)
1771
+ @blind_attack_plugin.should_receive(:blind_defense_opportunity?).exactly(@stats.players.size)
1772
+ @blind_attack_plugin.should_receive(:blind_defense_opportunity_taken?).exactly(@stats.players.size)
1773
+ @reports = @stats.reports
1774
+ @stats.players.each{|each| @reports[each].should include(
1775
+ :is_blind_attack_opportunity,
1776
+ :is_blind_attack_opportunity_taken,
1777
+ :is_blind_defense_opportunity,
1778
+ :is_blind_defense_opportunity_taken
1779
+ )}
1780
+ end
825
1781
 
826
- it "should report continuation bet statistics for each player" do
827
- @continuation_bet_plugin.should_receive(:cbet_opportunity?).exactly(@stats.players.size)
828
- @continuation_bet_plugin.should_receive(:cbet_opportunity_taken?).exactly(@stats.players.size)
829
- @reports = @stats.reports
830
- @stats.players.each{|each| @reports[each].should include(:is_cbet_opportunity)}
831
- @stats.players.each{|each| @reports[each].should include(:is_cbet_opportunity_taken)}
832
- end
1782
+ it "should report continuation bet statistics for each player" do
1783
+ @continuation_bet_plugin.should_receive(:cbet_opportunity?).exactly(@stats.players.size)
1784
+ @continuation_bet_plugin.should_receive(:cbet_opportunity_taken?).exactly(@stats.players.size)
1785
+ @reports = @stats.reports
1786
+ @stats.players.each{|each| @reports[each].should include(:is_cbet_opportunity)}
1787
+ @stats.players.each{|each| @reports[each].should include(:is_cbet_opportunity_taken)}
1788
+ end
833
1789
 
834
- it "should report cash statistics for each player" do
835
- report_items = [:cards, :card_category_index, :posted, :paid, :won, :profit, :posted_in_bb, :paid_in_bb, :won_in_bb, :profit_in_bb]
836
- report_items.each{|report_item| @cash_plugin.should_receive(report_item).exactly(@stats.players.size)}
837
- @reports = @stats.reports
838
- report_items.each{|report_item|@stats.players.each{|each| @reports[each].should include(report_item)}}
839
- end
1790
+ it "should report cash statistics for each player" do
1791
+ report_items = [
1792
+ :seat, :position, :cards, :card_category_index,
1793
+ :posted, :paid, :won, :profit,
1794
+ :posted_in_bb, :paid_in_bb, :won_in_bb, :profit_in_bb,
1795
+ :starting_stack, :starting_stack_in_bb, :starting_stack_as_M, :starting_stack_as_M_class
1796
+ ]
1797
+ report_items.each{|report_item| @cash_plugin.should_receive(report_item).exactly(@stats.players.size)}
1798
+ @reports = @stats.reports
1799
+ report_items.each{|report_item|@stats.players.each{|each| @reports[each].should include(report_item)}}
1800
+ end
840
1801
 
841
- it "should report aggression statistics for each player" do
842
- @aggression_plugin.should_receive(:preflop_passive).exactly(@stats.players.size)
843
- @aggression_plugin.should_receive(:preflop_aggressive).exactly(@stats.players.size)
844
- @aggression_plugin.should_receive(:postflop_passive).exactly(@stats.players.size)
845
- @aggression_plugin.should_receive(:postflop_aggressive).exactly(@stats.players.size)
846
- @reports = @stats.reports
847
- @stats.players.each{|each| @reports[each].should include(:preflop_passive)}
848
- @stats.players.each{|each| @reports[each].should include(:preflop_aggressive)}
849
- @stats.players.each{|each| @reports[each].should include(:postflop_passive)}
850
- @stats.players.each{|each| @reports[each].should include(:postflop_aggressive)}
851
- end
1802
+ it "should report aggression statistics for each player" do
1803
+ @aggression_plugin.should_receive(:preflop_passive).exactly(@stats.players.size)
1804
+ @aggression_plugin.should_receive(:preflop_aggressive).exactly(@stats.players.size)
1805
+ @aggression_plugin.should_receive(:postflop_passive).exactly(@stats.players.size)
1806
+ @aggression_plugin.should_receive(:postflop_aggressive).exactly(@stats.players.size)
1807
+ @reports = @stats.reports
1808
+ @stats.players.each{|each| @reports[each].should include(:preflop_passive)}
1809
+ @stats.players.each{|each| @reports[each].should include(:preflop_aggressive)}
1810
+ @stats.players.each{|each| @reports[each].should include(:postflop_passive)}
1811
+ @stats.players.each{|each| @reports[each].should include(:postflop_aggressive)}
1812
+ end
852
1813
 
853
- it "should report preflop raise statistics for each player" do
854
- @preflop_raise_plugin.should_receive(:pfr_opportunity?).exactly(@stats.players.size)
855
- @preflop_raise_plugin.should_receive(:pfr_opportunity_taken?).exactly(@stats.players.size)
856
- @reports = @stats.reports
857
- @stats.players.each{|each| @reports[each].should include(:is_pfr_opportunity)}
858
- @stats.players.each{|each| @reports[each].should include(:is_pfr_opportunity_taken)}
859
- end
1814
+ it "should report preflop raise statistics for each player" do
1815
+ @preflop_raise_plugin.should_receive(:pfr_opportunity?).exactly(@stats.players.size)
1816
+ @preflop_raise_plugin.should_receive(:pfr_opportunity_taken?).exactly(@stats.players.size)
1817
+ @reports = @stats.reports
1818
+ @stats.players.each{|each| @reports[each].should include(:is_pfr_opportunity)}
1819
+ @stats.players.each{|each| @reports[each].should include(:is_pfr_opportunity_taken)}
1820
+ end
1821
+
1822
+ it "should report street statistics for each player" do
1823
+ report_items = [
1824
+ :saw_flop,:saw_turn,:saw_river,:saw_showdown,
1825
+ :won_preflop, :won_flop, :won_turn, :won_river, :won_showdown
1826
+ ]
1827
+ report_items.each{|report_item| @street_plugin.should_receive(report_item).exactly(@stats.players.size)}
1828
+ @reports = @stats.reports
1829
+ report_items.each{|report_item|@stats.players.each{|each| @reports[each].should include(report_item)}}
1830
+ end
1831
+
1832
+ it "should report street bet statistics for each player" do
1833
+ report_items = [
1834
+ :preflop_2bet, :preflop_3bet, :preflop_4bet,
1835
+ :flop_1bet, :flop_2bet, :flop_3bet, :flop_4bet,
1836
+ :turn_1bet, :turn_2bet, :turn_3bet, :turn_4bet,
1837
+ :river_1bet, :river_2bet, :river_3bet, :river_4bet,
1838
+ :call_flop_1bet, :call_flop_2bet, :call_flop_3bet, :call_flop_4bet,
1839
+ :call_turn_1bet, :call_turn_2bet, :call_turn_3bet, :call_turn_4bet,
1840
+ :call_river_1bet, :call_river_2bet, :call_river_3bet, :call_river_4bet,
1841
+ :fold_to_preflop_1bet, :fold_to_preflop_2bet, :fold_to_preflop_3bet, :fold_to_preflop_4bet,
1842
+ :fold_to_flop_1bet, :fold_to_flop_2bet, :fold_to_flop_3bet, :fold_to_flop_4bet,
1843
+ :fold_to_turn_1bet, :fold_to_turn_2bet, :fold_to_turn_3bet, :fold_to_turn_4bet,
1844
+ :fold_to_river_1bet, :fold_to_river_2bet, :fold_to_river_3bet, :fold_to_river_4bet,
1845
+ :last_aggr_preflop, :last_aggr_flop, :last_aggr_turn, :last_aggr_river,
1846
+ :cbet_flop, :cbet_turn, :cbet_river,
1847
+ :fold_to_cbet_flop, :fold_to_cbet_turn, :fold_to_cbet_river,
1848
+ :dbet_flop, :dbet_turn, :dbet_river,
1849
+ :fold_to_dbet_flop, :fold_to_dbet_turn, :fold_to_dbet_river
1850
+ ]
1851
+ report_items.each{|report_item| @street_bet_plugin.should_receive(report_item).exactly(@stats.players.size)}
1852
+ @reports = @stats.reports
1853
+ report_items.each{|report_item|@stats.players.each{|each| @reports[each].should include(report_item)}}
1854
+ end
1855
+ end
860
1856
  end