pokerstats 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +18 -0
  5. data/Rakefile +50 -0
  6. data/VERSION +1 -0
  7. data/bin/checkps +190 -0
  8. data/generators/pokerstats/USAGE +1 -0
  9. data/generators/pokerstats/pokerstats_generator.rb +7 -0
  10. data/generators/pokerstats/templates/create_pokerstats.rhtml +28 -0
  11. data/lib/pokerstats.rb +2 -0
  12. data/lib/pokerstats/.gitignore +0 -0
  13. data/lib/pokerstats/hand_constants.rb +27 -0
  14. data/lib/pokerstats/hand_history.rb +38 -0
  15. data/lib/pokerstats/hand_statistics.rb +225 -0
  16. data/lib/pokerstats/hand_statistics_api.rb +64 -0
  17. data/lib/pokerstats/player_statistics.rb +58 -0
  18. data/lib/pokerstats/plugins/aggression_statistics.rb +57 -0
  19. data/lib/pokerstats/plugins/blind_attack_statistics.rb +78 -0
  20. data/lib/pokerstats/plugins/cash_statistics.rb +95 -0
  21. data/lib/pokerstats/plugins/continuation_bet_statistics.rb +68 -0
  22. data/lib/pokerstats/plugins/preflop_raise_statistics.rb +50 -0
  23. data/lib/pokerstats/poker-edge.rb +57 -0
  24. data/lib/pokerstats/pokerstars_file.rb +100 -0
  25. data/lib/pokerstats/pokerstars_hand_history_parser.rb +141 -0
  26. data/pokerstats.gemspec +91 -0
  27. data/spec/file_empty.txt +0 -0
  28. data/spec/file_many_hands.txt +564 -0
  29. data/spec/file_one_hand.txt +52 -0
  30. data/spec/hand_statistics_spec.rb +846 -0
  31. data/spec/hand_statistics_spec_helper.rb +89 -0
  32. data/spec/player_statistics_spec.rb +230 -0
  33. data/spec/pokerstars_file_spec.rb +160 -0
  34. data/spec/pokerstars_hand_history_parser_spec.rb +197 -0
  35. data/spec/spec_helper.rb +9 -0
  36. data/spec/zpokerstars_hand_history_parser_integration.rb.txt +67 -0
  37. metadata +125 -0
@@ -0,0 +1,64 @@
1
+ module Pokerstats
2
+ module HandStatisticsAPI
3
+ def initialize handstatistics
4
+ @hand_statistics = handstatistics
5
+ end
6
+
7
+ def self.exposed_methods
8
+ self.public_instance_methods - StatisticsHolder.public_instance_methods
9
+ end
10
+
11
+ def report screen_name
12
+ automatic_report screen_name
13
+ end
14
+
15
+ def register_player screen_name, street
16
+ end
17
+
18
+ def street_transition street
19
+ end
20
+
21
+ def street_transition_for_player street, player
22
+ end
23
+
24
+ def apply_action action, street
25
+ end
26
+
27
+ def automatic_report screen_name
28
+ result = {}
29
+ self.class.report_specification.each do |each|
30
+ result[each[0]] = send(each[2], screen_name)
31
+ end
32
+ result
33
+ end
34
+
35
+ private
36
+
37
+ module ClassMethods
38
+ def report_specification
39
+ [
40
+ # [key, sql_type, function]
41
+ ]
42
+ end
43
+
44
+ def rails_migration_segment_for_player_data
45
+ prefix = "\n" + " " * 10
46
+ result = "#{prefix}# FROM #{self.to_s}"
47
+ report_specification.each do |each|
48
+ result += "#{prefix}t.#{each[1]}\t#{each[0].inspect}"
49
+ end
50
+ result += "\n"
51
+ end
52
+
53
+ def rails_generator_command_line_for_player_data
54
+ report_specification.inject("") do |string, each|
55
+ string + "#{each[0]}:#{each[1]} "
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.included(klass)
61
+ klass.extend ClassMethods
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,58 @@
1
+ module Pokerstats
2
+ class PlayerStatistics
3
+
4
+ def initialize
5
+ @aggregate_statistics = {}
6
+ end
7
+
8
+ def reports
9
+ @aggregate_statistics
10
+ end
11
+
12
+ def aggregate_numeric_statistic each_player, reports, aggregate_stat, hand_stat
13
+ @aggregate_statistics[each_player][aggregate_stat] ||= 0
14
+ @aggregate_statistics[each_player][aggregate_stat] += reports[each_player][hand_stat]
15
+ end
16
+
17
+ def aggregate_boolean_statistic each_player, reports, aggregate_stat, hand_stat
18
+ @aggregate_statistics[each_player][aggregate_stat] ||= 0
19
+ @aggregate_statistics[each_player][aggregate_stat] += 1 if reports[each_player][hand_stat]
20
+ end
21
+
22
+ def aggregate_statistic each_player, reports, aggregate_stat, hand_stat
23
+ @aggregate_statistics[each_player][aggregate_stat] ||= 0
24
+ if /^is_/ =~ hand_stat.to_s
25
+ @aggregate_statistics[each_player][aggregate_stat] += 1 if reports[each_player][hand_stat]
26
+ else
27
+ @aggregate_statistics[each_player][aggregate_stat] += reports[each_player][hand_stat]
28
+ end
29
+ end
30
+
31
+ def record hand_statistics
32
+ reports = hand_statistics.reports
33
+ reports.keys.each do |each_player|
34
+ @aggregate_statistics[each_player] ||= {}
35
+
36
+ @aggregate_statistics[each_player][:t_hands] ||= 0
37
+ @aggregate_statistics[each_player][:t_hands] += 1
38
+ @aggregate_statistics[each_player][:t_vpip] ||= 0
39
+ @aggregate_statistics[each_player][:t_vpip] += 1 unless reports[each_player][:paid].zero?
40
+ aggregate_numeric_statistic each_player, reports, :t_posted, :posted
41
+ aggregate_numeric_statistic each_player, reports, :t_paid, :paid
42
+ aggregate_numeric_statistic each_player, reports, :t_won, :won
43
+ aggregate_numeric_statistic each_player, reports, :t_preflop_passive, :preflop_passive
44
+ aggregate_numeric_statistic each_player, reports, :t_preflop_aggressive, :preflop_aggressive
45
+ aggregate_numeric_statistic each_player, reports, :t_postflop_passive, :postflop_passive
46
+ aggregate_numeric_statistic each_player, reports, :t_postflop_aggressive, :postflop_aggressive
47
+ aggregate_boolean_statistic each_player, reports, :t_blind_attack_opportunity, :is_blind_attack_opportunity
48
+ aggregate_boolean_statistic each_player, reports, :t_blind_attack_opportunity_taken, :is_blind_attack_opportunity_taken
49
+ aggregate_boolean_statistic each_player, reports, :t_blind_defense_opportunity, :is_blind_defense_opportunity
50
+ aggregate_boolean_statistic each_player, reports, :t_blind_defense_opportunity_taken, :is_blind_defense_opportunity_taken
51
+ aggregate_boolean_statistic each_player, reports, :t_pfr_opportunity, :is_pfr_opportunity
52
+ aggregate_boolean_statistic each_player, reports, :t_pfr_opportunity_taken, :is_pfr_opportunity_taken
53
+ aggregate_boolean_statistic each_player, reports, :t_cbet_opportunity, :is_cbet_opportunity
54
+ aggregate_boolean_statistic each_player, reports, :t_cbet_opportunity_taken, :is_cbet_opportunity_taken
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,57 @@
1
+ module Pokerstats
2
+ class AggressionStatistics < HandStatistics::Plugin
3
+
4
+ def self.report_specification
5
+ [
6
+ # [key, sql_type, function]
7
+ [:preflop_passive, 'integer', :preflop_passive],
8
+ [:postflop_passive, 'integer', :postflop_passive],
9
+ [:preflop_aggressive, 'integer', :preflop_aggressive],
10
+ [:postflop_aggressive, 'integer', :postflop_aggressive]
11
+ ]
12
+ end
13
+
14
+ def initialize handstatistics
15
+ super handstatistics
16
+ @preflop_passive = {}
17
+ @preflop_aggressive = {}
18
+ @postflop_passive = {}
19
+ @postflop_aggressive = {}
20
+ end
21
+
22
+ def preflop_passive(screen_name)
23
+ @preflop_passive[screen_name]
24
+ end
25
+
26
+ def postflop_passive(screen_name)
27
+ @postflop_passive[screen_name]
28
+ end
29
+
30
+ def preflop_aggressive(screen_name)
31
+ @preflop_aggressive[screen_name]
32
+ end
33
+
34
+ def postflop_aggressive(screen_name)
35
+ @postflop_aggressive[screen_name]
36
+ end
37
+
38
+ def register_player screen_name, street
39
+ @preflop_passive[screen_name] = 0
40
+ @preflop_aggressive[screen_name] = 0
41
+ @postflop_passive[screen_name] = 0
42
+ @postflop_aggressive[screen_name] = 0
43
+ end
44
+
45
+ def apply_action action, street
46
+ aggression = action[:aggression]
47
+ player = action[:screen_name]
48
+ if [:prelude, :preflop].member?(street)
49
+ @preflop_aggressive[player] +=1 if aggression == :aggressive
50
+ @preflop_passive[player] += 1 if aggression == :passive
51
+ else
52
+ @postflop_aggressive[player] +=1 if aggression == :aggressive
53
+ @postflop_passive[player] +=1 if aggression == :passive
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,78 @@
1
+ module Pokerstats
2
+ class BlindAttackStatistics < HandStatistics::Plugin
3
+ def initialize handstatistics
4
+ super(handstatistics)
5
+ @blind_attack_state = :no_action_taken
6
+ @blind_attack_opportunity = {}
7
+ @blind_attack_opportunity_taken ={}
8
+ @blind_defense_opportunity = {}
9
+ @blind_defense_opportunity_taken = {}
10
+ end
11
+
12
+ def blind_attack_opportunity?(screen_name)
13
+ @blind_attack_opportunity[screen_name]
14
+ end
15
+
16
+ def blind_attack_opportunity_taken?(screen_name)
17
+ @blind_attack_opportunity_taken[screen_name]
18
+ end
19
+
20
+ def blind_defense_opportunity?(screen_name)
21
+ @blind_defense_opportunity[screen_name]
22
+ end
23
+
24
+ def blind_defense_opportunity_taken?(screen_name)
25
+ @blind_defense_opportunity_taken[screen_name]
26
+ end
27
+
28
+ def self.report_specification
29
+ [
30
+ # [key, sql_type, function]
31
+ [:is_blind_attack_opportunity, 'integer', :blind_attack_opportunity?],
32
+ [:is_blind_attack_opportunity_taken, 'integer', :blind_attack_opportunity_taken?],
33
+ [:is_blind_defense_opportunity, 'integer', :blind_defense_opportunity?],
34
+ [:is_blind_defense_opportunity_taken, 'integer', :blind_defense_opportunity_taken?]
35
+ ]
36
+ end
37
+
38
+ # def report(screen_name)
39
+ # {
40
+ # :is_blind_attack_opportunity => blind_attack_opportunity?(screen_name),
41
+ # :is_blind_attack_opportunity_taken => blind_attack_opportunity_taken?(screen_name),
42
+ # :is_blind_defense_opportunity => blind_defense_opportunity?(screen_name),
43
+ # :is_blind_defense_opportunity_taken => blind_defense_opportunity_taken?(screen_name)
44
+ # }
45
+ # end
46
+
47
+ def apply_action action, street
48
+ player = action[:screen_name]
49
+ aggression = action[:aggression]
50
+
51
+ return if aggression == :neutral || street != :preflop
52
+
53
+ @blind_defense_opportunity[player] ||= @hand_statistics.blind?(player) && @blind_attack_state == :attacker_raised_first_in
54
+ @blind_defense_opportunity_taken[player] ||= @blind_defense_opportunity[player] && aggression != :fold
55
+ case @blind_attack_state
56
+ when :no_action_taken
57
+ @blind_attack_opportunity[player] ||= @hand_statistics.attacker?(player)
58
+ @blind_attack_opportunity_taken[player] ||= @hand_statistics.attacker?(player) && aggression == :aggressive
59
+ @blind_attack_state = case aggression
60
+ when :aggressive
61
+ @hand_statistics.attacker?(player) ? :attacker_raised_first_in : :responsive_action_taken
62
+ when :passive
63
+ :responsive_action_taken
64
+ else
65
+ :no_action_taken
66
+ end
67
+ when :attacker_raised_first_in
68
+ @blind_attack_opportunity[player] ||= false
69
+ @blind_attack_opportunity_taken[player] ||= false
70
+ @blind_attack_state = :responsive_action_taken unless [:check, :fold].member?(aggression)
71
+ when :responsive_action_taken
72
+ @blind_attack_opportunity[player] ||= false
73
+ @blind_attack_opportunity_taken[player] ||= false
74
+ else raise "invalid state: #{@blind_attack_state}"
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,95 @@
1
+ module Pokerstats
2
+ class CashStatistics < HandStatistics::Plugin
3
+ def initialize handstatistics
4
+ super handstatistics
5
+ @posted = {}
6
+ @paid = {}
7
+ @paid_this_round = {}
8
+ @won = {}
9
+ @cards = {}
10
+ @stats = {:posted => @posted, :paid => @paid, :won => @won, :cards => @cards}
11
+ end
12
+
13
+ def posted(player)
14
+ @posted[player]
15
+ end
16
+
17
+ def paid(player)
18
+ @paid[player]
19
+ end
20
+
21
+ def paid_this_round(player)
22
+ @paid_this_round[player]
23
+ end
24
+
25
+ def won(player)
26
+ @won[player]
27
+ end
28
+
29
+ def cards(player)
30
+ @cards[player]
31
+ end
32
+
33
+ def self.report_specification
34
+ [
35
+ # [key, sql_type, function]
36
+ [:posted, 'decimal', :posted],
37
+ [:paid, 'decimal', :paid],
38
+ [:won, 'decimal', :won],
39
+ [:cards, 'string', :cards]
40
+ ]
41
+ end
42
+
43
+ # def report(screen_name)
44
+ # {
45
+ # :posted => posted(screen_name),
46
+ # :paid => paid(screen_name),
47
+ # :won => won(screen_name),
48
+ # :cards => cards(screen_name)
49
+ # }
50
+ # end
51
+
52
+ def stats(player=nil)
53
+ return @stats unless player
54
+ # @stats.inject({}){|last, pair| last.merge(pair[0] => pair[1][player])}
55
+ end
56
+
57
+ def register_player(screen_name, street)
58
+ @posted[screen_name] = 0
59
+ @paid[screen_name] = 0
60
+ @won[screen_name] = 0
61
+ end
62
+
63
+ def street_transition_for_player street, player
64
+ @paid_this_round[player] = 0 unless street == :preflop
65
+ end
66
+
67
+ def apply_action action, street
68
+ player = action[:screen_name]
69
+ description = action[:description]
70
+ result = action[:result]
71
+ amount = action[:amount]
72
+ data = action[:data]
73
+ case result
74
+ when :post
75
+ @posted[player] += amount
76
+ @paid_this_round[player] += amount unless description == "antes"
77
+ when :pay
78
+ @paid[player] ||= 0
79
+ @paid[player] += amount
80
+ @paid_this_round[player] += amount
81
+ when :pay_to
82
+ net_amount_paid = amount - @paid_this_round[player]
83
+ action[:net_amount_paid] = net_amount_paid
84
+ @paid[player] += net_amount_paid
85
+ @paid_this_round[player] += net_amount_paid
86
+ when :win
87
+ @won[player] += amount
88
+ when :neutral
89
+ when :cards
90
+ @cards[player] = data
91
+ else raise "invalid action result: #{result.inspect}"
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,68 @@
1
+ module Pokerstats
2
+ class ContinuationBetStatistics < HandStatistics::Plugin
3
+ def initialize handstatistics
4
+ super(handstatistics)
5
+ @last_preflop_raiser = nil
6
+ @number_preflop_raises = 0
7
+ @first_aggressor = {}
8
+ @first_aggression_opportunity = {}
9
+ @first_aggression_opportunity_taken = {}
10
+ end
11
+
12
+ def cbet_opportunity?(screen_name, street = :flop)
13
+ # puts "cbet_opportunity?(#{screen_name}, #{street})"
14
+ # puts @first_aggression_opportunity.inspect
15
+ @number_preflop_raises==1 && @last_preflop_raiser==screen_name && @first_aggression_opportunity[street] && @first_aggression_opportunity[street][screen_name]
16
+ end
17
+
18
+ def cbet_opportunity_taken?(screen_name, street = :flop)
19
+ cbet_opportunity?(screen_name) && @first_aggression_opportunity_taken[street][screen_name]
20
+ end
21
+
22
+ def self.report_specification
23
+ [
24
+ # [key, sql_type, function]
25
+ [:is_cbet_opportunity, 'integer', :cbet_opportunity?],
26
+ [:is_cbet_opportunity_taken, 'integer', :cbet_opportunity_taken?]
27
+ ]
28
+ end
29
+
30
+ #
31
+ # def report(screen_name)
32
+ # {
33
+ # :is_cbet_opportunity => cbet_opportunity?(screen_name),
34
+ # :is_cbet_opportunity_taken => cbet_opportunity_taken?(screen_name)
35
+ # }
36
+ # end
37
+
38
+ def street_transition street
39
+ @first_aggressor[street] = nil
40
+ @first_aggression_opportunity[street] = {}
41
+ @first_aggression_opportunity_taken[street] = {}
42
+ super(street)
43
+ end
44
+
45
+ def street_transition_for_player street, player
46
+ @first_aggression_opportunity[street][player] = nil
47
+ @first_aggression_opportunity_taken[street][player] = false
48
+ end
49
+
50
+ def apply_action action, street
51
+ player = action[:screen_name]
52
+ aggression = action[:aggression]
53
+ result = action[:result]
54
+ if street == :preflop and result == :pay_to
55
+ @number_preflop_raises += 1
56
+ @last_preflop_raiser = player
57
+ end
58
+ if aggression != :neutral && @first_aggressor[street].nil? && @first_aggression_opportunity[street][player].nil?
59
+ @first_aggression_opportunity[street][player] = true
60
+ if aggression == :aggressive
61
+ @first_aggressor[street] = player
62
+ @first_aggression_opportunity_taken[street][player] = true
63
+ @hand_statistics.players.each {|player| @first_aggression_opportunity[street][player] ||= false}
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,50 @@
1
+ module Pokerstats
2
+ class PreflopRaiseStatistics < HandStatistics::Plugin
3
+
4
+ def initialize handstatistics
5
+ super handstatistics
6
+ @pfr_opportunity = {}
7
+ @pfr_opportunity_taken = {}
8
+ @pfr_size_in_bb = {}
9
+ end
10
+
11
+ def pfr_opportunity?(screen_name)
12
+ @pfr_opportunity[screen_name]
13
+ end
14
+
15
+ def pfr_opportunity_taken?(screen_name)
16
+ @pfr_opportunity[screen_name] && @pfr_opportunity_taken[screen_name]
17
+ end
18
+
19
+ def self.report_specification
20
+ [
21
+ # [key, sql_type, function]
22
+ [:is_pfr_opportunity, 'integer', :pfr_opportunity?],
23
+ [:is_pfr_opportunity_taken, 'integer', :pfr_opportunity_taken?]
24
+ ]
25
+ end
26
+
27
+ # def report screen_name
28
+ # {
29
+ # :is_pfr_opportunity => pfr_opportunity?(screen_name),
30
+ # :is_pfr_opportunity_taken => pfr_opportunity_taken?(screen_name)
31
+ # }
32
+ # end
33
+
34
+ def apply_action action, street
35
+ # puts "pfr: apply_action #{street}, #{action.inspect}"
36
+ result = action[:result]
37
+ player = action[:screen_name]
38
+ if street == :preflop and [:pay, :pay_to, :neutral].member?(result) and @pfr_opportunity[player].nil?
39
+ case result
40
+ when :pay, :neutral
41
+ @pfr_opportunity[player] = true
42
+ @pfr_opportunity_taken[player] = false
43
+ when :pay_to
44
+ @hand_statistics.players.each {|each_player| @pfr_opportunity[each_player] ||= (each_player == player)}
45
+ @pfr_opportunity_taken[player] = true
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end