pokerstats 2.0.13 → 2.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -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