wizardwerdna-pokerstats 1.0.14 → 1.0.15
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/bin/checkps +7 -8
- data/lib/pokerstats.rb +2 -0
- data/lib/pokerstats/hand_constants.rb +25 -23
- data/lib/pokerstats/hand_history.rb +29 -26
- data/lib/pokerstats/hand_statistics.rb +173 -173
- data/lib/pokerstats/hand_statistics_api.rb +46 -44
- data/lib/pokerstats/player_statistics.rb +47 -45
- data/lib/pokerstats/plugins/aggression_statistics.rb +46 -44
- data/lib/pokerstats/plugins/blind_attack_statistics.rb +65 -63
- data/lib/pokerstats/plugins/cash_statistics.rb +82 -80
- data/lib/pokerstats/plugins/continuation_bet_statistics.rb +57 -55
- data/lib/pokerstats/plugins/preflop_raise_statistics.rb +40 -38
- data/lib/pokerstats/poker-edge.rb +39 -37
- data/lib/pokerstats/pokerstars_file.rb +77 -76
- data/lib/pokerstats/pokerstars_hand_history_parser.rb +100 -98
- data/pokerstats.gemspec +3 -4
- data/spec/hand_statistics_spec.rb +2 -0
- data/spec/player_statistics_spec.rb +1 -0
- data/spec/pokerstars_file_spec.rb +1 -1
- data/spec/pokerstars_hand_history_parser.rb +1 -1
- metadata +3 -5
- data/spec/zpokerstars_hand_history_parser_integration.txt +0 -62
@@ -1,99 +1,100 @@
|
|
1
1
|
require 'iconv'
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/hand_history')
|
3
|
+
module Pokerstats
|
4
|
+
class PokerstarsFile
|
5
|
+
include Enumerable
|
3
6
|
|
4
|
-
|
5
|
-
include Enumerable
|
7
|
+
POKERSTARS_HEADER_PATTERN = /PokerStars Game #([0-9]+)/
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
new(filename, starting_at).open(starting_at, &block)
|
11
|
-
end
|
9
|
+
def self.open(filename, starting_at = 0, &block)
|
10
|
+
new(filename, starting_at).open(starting_at, &block)
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
def initialize(filename, starting_at = 0, transliterate_from = "ISO-8859-1", transliterate_to = "ASCII//TRANSLIT//IGNORE")
|
14
|
+
@filename = File.expand_path(filename)
|
15
|
+
@lastline = nil
|
16
|
+
@lines = []
|
17
|
+
@transliterator = Iconv.new(transliterate_to, transliterate_from)
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def open_file_and_verify_first_line(starting_at = 0)
|
21
|
+
@file = File.open(@filename, "r")
|
22
|
+
self.pos=starting_at
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
def open(starting_at = 0)
|
26
|
+
open_file_and_verify_first_line(starting_at)
|
27
|
+
if block_given?
|
28
|
+
begin
|
29
|
+
yield self
|
30
|
+
ensure
|
31
|
+
close
|
32
|
+
end
|
32
33
|
end
|
34
|
+
self
|
33
35
|
end
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
def closed?
|
38
|
-
@file.closed?
|
39
|
-
end
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
37
|
+
def closed?
|
38
|
+
@file.closed?
|
39
|
+
end
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
unless @lastline && @lastline =~ POKERSTARS_HEADER_PATTERN
|
50
|
-
close
|
51
|
-
raise "hand record must begin with a valid header line"
|
41
|
+
def pos
|
42
|
+
return @file.pos if @lastline.nil?
|
43
|
+
@file.pos - @lastline.size - 1
|
52
44
|
end
|
53
|
-
@lines = [@lastline]
|
54
|
-
end
|
55
45
|
|
56
|
-
|
57
|
-
|
58
|
-
|
46
|
+
def pos=(index)
|
47
|
+
@file.pos=index unless pos == index
|
48
|
+
@lastline = read_and_chomp_next_line_from_file
|
49
|
+
unless @lastline && @lastline =~ POKERSTARS_HEADER_PATTERN
|
50
|
+
close
|
51
|
+
raise "hand record must begin with a valid header line"
|
52
|
+
end
|
53
|
+
@lines = [@lastline]
|
54
|
+
end
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
return next_handrecord
|
56
|
+
def eof?
|
57
|
+
@lastline.nil?
|
63
58
|
end
|
64
|
-
end
|
65
59
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
def first(starting_at = 0)
|
61
|
+
open(starting_at) do
|
62
|
+
return next_handrecord
|
63
|
+
end
|
64
|
+
end
|
70
65
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@lastline = read_and_chomp_next_line_from_file
|
75
|
-
break if @lastline =~ POKERSTARS_HEADER_PATTERN
|
76
|
-
@lines << @lastline unless @lastline.empty?
|
66
|
+
def each
|
67
|
+
yield next_handrecord
|
68
|
+
yield next_handrecord until @lastline.nil?
|
77
69
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
70
|
+
|
71
|
+
def next_handrecord
|
72
|
+
starting_at = pos
|
73
|
+
until @file.eof?
|
74
|
+
@lastline = read_and_chomp_next_line_from_file
|
75
|
+
break if @lastline =~ POKERSTARS_HEADER_PATTERN
|
76
|
+
@lines << @lastline unless @lastline.empty?
|
77
|
+
end
|
78
|
+
result, @lines = HandHistory.new(@lines, @filename, starting_at), [@lastline]
|
79
|
+
if @file.eof?
|
80
|
+
@lastline = nil
|
81
|
+
@index_of_last_header = nil
|
82
|
+
@lines = []
|
83
|
+
else
|
84
|
+
@index_of_last_header = @file.pos-@lastline.size-1
|
85
|
+
@lines = [@lastline]
|
86
|
+
end
|
87
|
+
result
|
86
88
|
end
|
87
|
-
result
|
88
|
-
end
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
def close
|
91
|
+
@file.close unless @file.closed?
|
92
|
+
end
|
93
93
|
|
94
|
-
|
94
|
+
private
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
def read_and_chomp_next_line_from_file
|
97
|
+
@transliterator.iconv(@file.readline.chomp!)
|
98
|
+
end
|
99
|
+
end
|
99
100
|
end
|
@@ -1,111 +1,113 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/hand_constants')
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/hand_statistics')
|
3
3
|
|
4
|
-
|
4
|
+
module Pokerstats
|
5
|
+
CASH = "[0-9$,.]+"
|
5
6
|
|
6
|
-
class PokerstarsHandHistoryParser
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class PokerstarsHandHistoryParser
|
8
|
+
def self.parse_lines(lines, stats=nil)
|
9
|
+
parser = self.new(stats)
|
10
|
+
lines.each {|line| parser.parse(line)}
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def initialize stats=nil
|
14
|
+
@stats = stats || HandStatistics.new
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
17
|
+
def ignorable?(line)
|
18
|
+
regular_expressions_for_ignorable_phrases = [
|
19
|
+
/(.*): doesn't show hand/,
|
20
|
+
/(.*) has timed out/,
|
21
|
+
/(.*) has returned/,
|
22
|
+
/(.*) leaves the table/,
|
23
|
+
/(.*) joins the table at seat #[0-9]+/,
|
24
|
+
/(.*) sits out/,
|
25
|
+
/(.*) mucks hand/,
|
26
|
+
/(.*) is sitting out/,
|
27
|
+
/(.*) is (dis)?connected/,
|
28
|
+
/(.*) said,/,
|
29
|
+
/(.*) will be allowed to play after the button/,
|
30
|
+
/(.*) was removed from the table for failing to post/,
|
31
|
+
/(.*) re-buys and receives (.*) chips for (.*)/,
|
32
|
+
/Seat [0-9]+: (.*) \(((small)|(big)) blind\) folded on the Flop/,
|
33
|
+
/Seat [0-9]+: (.*) folded on the ((Flop)|(Turn)|(River))/,
|
34
|
+
/Seat [0-9]+: (.*) folded before Flop \(didn't bet\)/,
|
35
|
+
/Seat [0-9]+: (.*) (\((small blind)|(big blind)|(button)\) )?folded before Flop( \(didn't bet\))?/,
|
36
|
+
/Seat [0-9]+: (.*) (\((small blind)|(big blind)|(button)\) )?collected (.*)/,
|
37
|
+
/^\s*$/
|
38
|
+
]
|
39
|
+
regular_expressions_for_ignorable_phrases.any?{|re| re =~ line }
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
42
|
+
def parse(line)
|
43
|
+
begin
|
44
|
+
case line
|
45
|
+
when /PokerStars Game #([0-9]+): Tournament #([0-9]+), (\$[0-9+$]+) ([^\-]*) - Level ([IVXL]+) \((#{CASH})\/(#{CASH})\) - (.*)$/
|
46
|
+
@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
|
47
|
+
when /PokerStars Game #([0-9]+): +([^(]*) \((#{CASH})\/(#{CASH})\) - (.*)$/
|
48
|
+
@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
|
49
|
+
when /PokerStars Game #([0-9]+): +([^(]*) \((#{CASH})\/(#{CASH}) USD\) - (.*)$/
|
50
|
+
@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
|
51
|
+
when /\*\*\* HOLE CARDS \*\*\*/
|
52
|
+
@stats.register_button(@stats.button)
|
53
|
+
@stats.update_hand :street => :preflop
|
54
|
+
when /\*\*\* FLOP \*\*\* \[(.*)\]/
|
55
|
+
@stats.update_hand :street => :flop
|
56
|
+
when /\*\*\* TURN \*\*\* \[([^\]]*)\] \[([^\]]*)\]/
|
57
|
+
@stats.update_hand :street => :turn
|
58
|
+
when /\*\*\* RIVER \*\*\* \[([^\]]*)\] \[([^\]]*)\]/
|
59
|
+
@stats.update_hand :street => :river
|
60
|
+
when /\*\*\* SHOW DOWN \*\*\*/
|
61
|
+
@stats.update_hand :street => :showdown
|
62
|
+
when /\*\*\* SUMMARY \*\*\*/
|
63
|
+
@stats.update_hand :street => :summary
|
64
|
+
when /PokerStars Game #([0-9]+):/
|
65
|
+
raise "invalid hand record: #{line}"
|
66
|
+
when /Dealt to ([^)]+) \[([^\]]+)\]/
|
67
|
+
@stats.register_action($1, 'dealt', :result => :cards, :data => $2)
|
68
|
+
when /(.*): shows \[(.*)\]/
|
69
|
+
@stats.register_action($1, 'shows', :result => :cards, :data => $2)
|
70
|
+
when /Board \[(.*)\]/
|
71
|
+
@stats.update_hand :board => $1
|
72
|
+
when /Total pot (#{CASH}) (((Main)|(Side)) pot(-[0-9]+)? (#{CASH}). )*\| Rake (#{CASH})/
|
73
|
+
@stats.update_hand(:total_pot => cash_to_d($1), :rake => cash_to_d($8))
|
74
|
+
when /Total pot (#{CASH}) Main pot (#{CASH}).( Side pot-[0-9]+ (#{CASH}).)* | Rake (#{CASH})/
|
75
|
+
raise "popo!"
|
76
|
+
when /Seat ([0-9]+): (.+) \(#{CASH} in chips\)( is sitting out)?/
|
77
|
+
@stats.register_player(:seat => $1.to_i, :screen_name => $2)
|
78
|
+
when /(.*): posts ((small)|(big)|(small \& big)) blind(s)? (#{CASH})/
|
79
|
+
@stats.register_action($1, 'posts', :result => :post, :amount => cash_to_d($7))
|
80
|
+
when /(.*): posts the ante (#{CASH})/
|
81
|
+
@stats.register_action($1, 'antes', :result => :post, :amount => cash_to_d($2))
|
82
|
+
when /Table '([0-9]+) ([0-9]+)' (.*) Seat #([0-9]+) is the button/
|
83
|
+
@stats.register_button($4.to_i)
|
84
|
+
when /Table '(.*)' (.*) Seat #([0-9]+) is the button/
|
85
|
+
@stats.register_button($3.to_i)
|
86
|
+
when /Uncalled bet \((.*)\) returned to (.*)/
|
87
|
+
@stats.register_action($2, 'return', :result => :win, :amount => cash_to_d($1))
|
88
|
+
when /(.+): ((folds)|(checks))/
|
89
|
+
@stats.register_action($1, $2, :result => :neutral)
|
90
|
+
when /(.+): ((calls)|(bets)) ((#{CASH})( and is all-in)?)?$/
|
91
|
+
@stats.register_action($1, $2, :result => :pay, :amount => cash_to_d($6))
|
92
|
+
when /(.+): raises (#{CASH}) to (#{CASH})( and is all-in)?$/
|
93
|
+
@stats.register_action($1, 'raises', :result => :pay_to, :amount => cash_to_d($3))
|
94
|
+
when /(.*) collected (.*) from ((side )|(main ))?pot/
|
95
|
+
@stats.register_action($1, "wins", :result => :win, :amount => cash_to_d($2))
|
96
|
+
when /Seat [0-9]+: (.*) (\((small blind)|(big blind)|(button)\) )?showed \[([^\]]+)\] and ((won) \(#{CASH}\)|(lost)) with (.*)/
|
97
|
+
when /Seat [0-9]+: (.*) mucked \[([^\]]+)\]/
|
98
|
+
else
|
99
|
+
raise "invalid line for parse: #{line}" unless ignorable?(line)
|
100
|
+
end
|
101
|
+
rescue => e
|
102
|
+
raise e
|
99
103
|
end
|
100
|
-
rescue => e
|
101
|
-
raise e
|
102
104
|
end
|
103
|
-
end
|
104
105
|
|
105
|
-
|
106
|
+
private
|
106
107
|
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
def cash_to_d(string)
|
109
|
+
string.gsub!(/[$, ]/,"")
|
110
|
+
string.to_d
|
111
|
+
end
|
110
112
|
end
|
111
113
|
end
|
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 = "1.0.
|
8
|
+
s.version = "1.0.15"
|
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-
|
12
|
+
s.date = %q{2009-09-01}
|
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}
|
@@ -53,8 +53,7 @@ Gem::Specification.new do |s|
|
|
53
53
|
"spec/player_statistics_spec.rb",
|
54
54
|
"spec/pokerstars_file_spec.rb",
|
55
55
|
"spec/pokerstars_hand_history_parser.rb",
|
56
|
-
"spec/spec_helper.rb"
|
57
|
-
"spec/zpokerstars_hand_history_parser_integration.txt"
|
56
|
+
"spec/spec_helper.rb"
|
58
57
|
]
|
59
58
|
s.homepage = %q{http://github.com/wizardwerdna/pokerstats}
|
60
59
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -2,6 +2,8 @@ require 'rubygems'
|
|
2
2
|
require 'activesupport'
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
4
4
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/hand_statistics')
|
5
|
+
|
6
|
+
include Pokerstats
|
5
7
|
require File.expand_path(File.dirname(__FILE__) + '/hand_statistics_spec_helper')
|
6
8
|
|
7
9
|
describe HandStatistics, "when created" do
|
@@ -3,6 +3,7 @@ require 'activesupport'
|
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
4
4
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/hand_statistics')
|
5
5
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/player_statistics')
|
6
|
+
include Pokerstats
|
6
7
|
|
7
8
|
TEST = [
|
8
9
|
{"andy" => {
|
@@ -3,7 +3,7 @@ require 'activesupport'
|
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
4
4
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/hand_history')
|
5
5
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/pokerstars_file')
|
6
|
-
|
6
|
+
include Pokerstats
|
7
7
|
|
8
8
|
describe PokerstarsFile, "when opened on an empty file" do
|
9
9
|
it "should complain" do
|
@@ -7,6 +7,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/hand_histo
|
|
7
7
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/hand_statistics')
|
8
8
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/pokerstars_file')
|
9
9
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/pokerstats/pokerstars_hand_history_parser')
|
10
|
+
include Pokerstats
|
10
11
|
|
11
12
|
include HandConstants
|
12
13
|
|
@@ -27,7 +28,6 @@ describe PokerstarsHandHistoryParser, "when parsing structural matter" do
|
|
27
28
|
:street => :prelude
|
28
29
|
)
|
29
30
|
@parser.parse("PokerStars Game #21650436825: Tournament #117620218, $10+$1 Hold'em No Limit - Level I (10/20) - 2008/10/31 17:25:42 ET")
|
30
|
-
puts 'foo'
|
31
31
|
puts @stats.report_hand_information
|
32
32
|
end
|
33
33
|
|