acpc_dealer_data 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ --protected
2
+ --no-private
3
+ lib/**/*.rb
4
+ ext/**/*.c
5
+ -
6
+ *.md
7
+ doc/**/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in acpc_dealer_data.gemspec
4
+ gemspec
@@ -0,0 +1,17 @@
1
+ License
2
+ =========
3
+
4
+ Copyright © 2012 by the Computer Poker Research Group, University of Alberta.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13
+
14
+ Credits
15
+ -------
16
+
17
+ Dustin Morrill: morrill@ualberta.ca
@@ -0,0 +1,45 @@
1
+ ACPC Dealer Data
2
+ ==================
3
+
4
+ The [Annual Computer Poker Competition Dealer Data][ACPC Dealer Data GitHub] gem contains utilities for extracting information from [<em>ACPC Dealer Server</em>][ACPC competition server] logs.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'acpc_dealer_data'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install acpc_dealer_data
19
+
20
+ ## Usage
21
+
22
+ See the [documentation][docs] for information on available classes, modules, and constants.
23
+
24
+ ## Contributing
25
+
26
+ See the [issue tracker](https://github.com/dmorrill10/acpc_dealer_data/issues) for currently known issues, or to log new ones.
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
33
+
34
+ Copyright
35
+ ---------
36
+ Copyright &copy; 2012 by the Computer Poker Research Group, University of Alberta. See [LICENSE](LICENSE.md) for details.
37
+
38
+ <!---
39
+ Link references
40
+ ================
41
+ -->
42
+
43
+ [ACPC Dealer Data GitHub]: https://github.com/dmorrill10/acpc_dealer_data#readme
44
+ [ACPC competition server]: http://www.computerpokercompetition.org/index.php?option=com_rokdownloads&view=folder&Itemid=59
45
+ [docs]: http://rubydoc.info/github/dmorrill10/acpc_dealer_data/frames
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "lib" << 'spec/support'
9
+ t.test_files = FileList['spec/**/*_spec.rb']
10
+ t.verbose = true
11
+ t.warning = true
12
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/acpc_dealer_data/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Dustin Morrill"]
6
+ gem.email = ["morrill@ualberta.ca"]
7
+ gem.description = %q{ACPC Dealer data}
8
+ gem.summary = %q{Gem to parse, manipulate, and use data from the ACPC Dealer program.}
9
+ gem.homepage = "https://github.com/dmorrill10/acpc_dealer_data"
10
+
11
+ gem.add_dependency 'acpc_dealer'
12
+ gem.add_dependency 'acpc_poker_types'
13
+ gem.add_dependency 'celluloid'
14
+ gem.add_dependency 'dmorrill10-utils', '~>1.0.0'
15
+
16
+
17
+ gem.add_development_dependency 'mocha'
18
+ gem.add_development_dependency 'simplecov'
19
+ gem.add_development_dependency 'turn'
20
+
21
+ gem.files = `git ls-files`.split($\)
22
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
23
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
24
+ gem.name = "acpc_dealer_data"
25
+ gem.require_paths = ["lib"]
26
+ gem.version = AcpcDealerData::VERSION
27
+ end
@@ -0,0 +1,10 @@
1
+ require "acpc_dealer_data/version"
2
+
3
+ require 'acpc_dealer_data/action_messages'
4
+ require 'acpc_dealer_data/hand_data'
5
+ require 'acpc_dealer_data/hand_results'
6
+ require 'acpc_dealer_data/poker_match_data'
7
+ require 'acpc_dealer_data/match_definition'
8
+
9
+ module AcpcDealerData
10
+ end
@@ -0,0 +1,90 @@
1
+
2
+ require 'acpc_poker_types/match_state'
3
+ require 'acpc_poker_types/poker_action'
4
+
5
+ require_relative 'match_definition'
6
+
7
+ class ActionMessages
8
+
9
+ attr_reader :data, :final_score, :match_def
10
+
11
+ def self.parse_to_message(to_message)
12
+ if to_message.strip.match(
13
+ /^TO\s*(\d+)\s*at\s*[\d\.]+\s+(\S+)$/
14
+ )
15
+ {seat: $1.to_i - 1, state: MatchState.parse($2)}
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def self.parse_from_message(from_message)
22
+ if from_message.strip.match(
23
+ /^FROM\s*(\d+)\s*at\s*[\d\.]+\s*(#{MatchState::LABEL}\S+):([#{PokerAction::LEGAL_ACPC_CHARACTERS.to_a.join('')}]\s*\d*)$/
24
+ )
25
+ {seat: $1.to_i - 1, state: MatchState.parse($2), action: PokerAction.new($3)}
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ def self.parse_score(score_string)
32
+ if score_string.strip.match(
33
+ /^SCORE:([\d\-\.|]+):([\w|]+)$/
34
+ )
35
+
36
+ stack_changes = $1.split '|'
37
+ players = $2.split '|'
38
+
39
+ players.each_index.inject({}) do |player_results, j|
40
+ player_results[players[j].to_sym] = stack_changes[j].to_r
41
+ player_results
42
+ end
43
+ else
44
+ nil
45
+ end
46
+ end
47
+
48
+ def self.parse_to_or_from_message(message)
49
+ parsed_message = ActionMessages.parse_to_message(message)
50
+ if parsed_message.nil?
51
+ ActionMessages.parse_from_message(message)
52
+ else
53
+ parsed_message
54
+ end
55
+ end
56
+
57
+ def self.parse_file(acpc_log_file_path, player_names, game_def_directory)
58
+ File.open(acpc_log_file_path, 'r') do |file|
59
+ ActionMessages.parse file, player_names, game_def_directory
60
+ end
61
+ end
62
+
63
+ alias_new :parse
64
+
65
+ def initialize(acpc_log_statements, player_names, game_def_directory)
66
+ @final_score = nil
67
+ @match_def = nil
68
+ @data = acpc_log_statements.inject([]) do |accumulating_data, log_line|
69
+ if @match_def.nil?
70
+ @match_def = MatchDefinition.parse(log_line, player_names, game_def_directory)
71
+ else
72
+ parsed_message = ActionMessages.parse_to_or_from_message(log_line)
73
+ if parsed_message
74
+ if (
75
+ accumulating_data.empty? ||
76
+ accumulating_data.last.first[:state].hand_number != parsed_message[:state].hand_number
77
+ )
78
+ accumulating_data << []
79
+ end
80
+
81
+ accumulating_data.last << parsed_message
82
+ else
83
+ @final_score = ActionMessages.parse_score(log_line) unless @final_score
84
+ end
85
+ end
86
+
87
+ accumulating_data
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,164 @@
1
+
2
+ require 'dmorrill10-utils/class'
3
+
4
+ require_relative 'match_definition'
5
+
6
+ # Monkey patch for easy boundary checking
7
+ class Array
8
+ def in_bounds?(i)
9
+ i < length
10
+ end
11
+ end
12
+
13
+ class HandData
14
+
15
+ exceptions :match_definitions_do_not_match, :invalid_data
16
+
17
+ attr_reader :chip_distribution, :match_def, :turn_number, :data, :seat
18
+
19
+ # State messages are organized by seat
20
+ Turn = Struct.new :state_messages, :action_message
21
+
22
+ def initialize(match_def, action_data, result)
23
+ @match_def = match_def
24
+
25
+ set_chip_distribution! result
26
+
27
+ set_data! action_data
28
+
29
+ @turn_number = nil
30
+ @seat = nil
31
+ end
32
+
33
+ def ==(other)
34
+ @match_def == other.match_def &&
35
+ @chip_distribution == other.chip_distribution &&
36
+ @data == other.data
37
+ end
38
+
39
+ def for_every_turn!(seat=0)
40
+ @seat = seat
41
+ @data.each_index do |i|
42
+ @turn_number = i
43
+
44
+ yield @turn_number
45
+ end
46
+
47
+ @turn_number = nil
48
+ self
49
+ end
50
+
51
+ def current_match_state(seat=@seat)
52
+ if @turn_number
53
+ @data[@turn_number].state_messages[seat]
54
+ else
55
+ nil
56
+ end
57
+ end
58
+
59
+ def next_action
60
+ if @turn_number
61
+ @data[@turn_number].action_message
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
67
+ def last_match_state(seat=@seat)
68
+ if @turn_number && @turn_number != 0
69
+ @data[@turn_number-1].state_messages[seat]
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ def last_action
76
+ if @turn_number && @turn_number != 0
77
+ @data[@turn_number-1].action_message
78
+ else
79
+ nil
80
+ end
81
+ end
82
+
83
+ def final_turn?
84
+ if @turn_number
85
+ @turn_number >= @data.length - 1
86
+ else
87
+ nil
88
+ end
89
+ end
90
+
91
+ protected
92
+
93
+ def set_chip_distribution!(result)
94
+ @chip_distribution = []
95
+ result.each do |player_name, amount|
96
+ begin
97
+ @chip_distribution[@match_def.player_names.index(player_name.to_s)] = amount
98
+ rescue TypeError
99
+ raise PlayerNamesDoNotMatch
100
+ end
101
+ end
102
+ end
103
+
104
+ def set_data!(action_data)
105
+ number_of_state_messages = @match_def.game_def.number_of_players
106
+
107
+ @data = []
108
+ message_number = 0
109
+ while message_number < action_data.length
110
+ state_messages = action_data[message_number..message_number+number_of_state_messages-1]
111
+
112
+ assert_messages_have_no_actions state_messages
113
+
114
+ state_messages = process_state_messages state_messages
115
+
116
+ assert_messages_are_well_defined state_messages
117
+
118
+ message_number += number_of_state_messages
119
+
120
+ action_message = if action_data.in_bounds?(message_number) &&
121
+ action_data[message_number][:action]
122
+
123
+ message_number += 1
124
+ action_data[message_number-1]
125
+ else
126
+ assert_message_is_from_final_turn action_data, message_number, state_messages
127
+
128
+ nil
129
+ end
130
+
131
+ @data << Turn.new(state_messages, action_message)
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ def process_state_messages(state_messages)
138
+ state_messages.inject([]) do |messages, raw_messages|
139
+ messages[raw_messages[:seat]] = raw_messages[:state]
140
+ messages
141
+ end
142
+ end
143
+
144
+ def assert_messages_have_no_actions(state_messages)
145
+ if state_messages.any? { |message| !message[:action].nil? }
146
+ raise InvalidData, state_messages.find do |message|
147
+ !message[:action].nil?
148
+ end.inspect
149
+ end
150
+ end
151
+
152
+ def assert_messages_are_well_defined(state_messages)
153
+ if state_messages.any? { |message| message.nil? }
154
+ raise InvalidData, state_messages.find { |message| message.nil? }.inspect
155
+ end
156
+ end
157
+
158
+ def assert_message_is_from_final_turn(action_data, message_number, state_messages)
159
+ if action_data.in_bounds?(message_number+1) &&
160
+ state_messages.last.round == action_data[message_number+1][:state].round
161
+ raise InvalidData, action_data[message_number].inspect
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,71 @@
1
+
2
+ require 'dmorrill10-utils/class'
3
+
4
+ require_relative 'match_definition'
5
+
6
+ class HandResults
7
+
8
+ attr_reader :data, :final_score, :match_def
9
+
10
+ def self.parse_state(state_string)
11
+ if state_string.strip.match(
12
+ /^STATE:\d+:[cfr\d\/]+:[^:]+:([\d\-\.|]+):([\w|]+)$/
13
+ )
14
+
15
+ stack_changes = $1.split '|'
16
+ players = $2.split '|'
17
+
18
+ players.each_index.inject({}) do |player_results, j|
19
+ player_results[players[j].to_sym] = stack_changes[j].to_r
20
+ player_results
21
+ end
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
27
+ def self.parse_score(score_string)
28
+ if score_string.strip.match(
29
+ /^SCORE:([\d\-\.|]+):([\w|]+)$/
30
+ )
31
+
32
+ stack_changes = $1.split '|'
33
+ players = $2.split '|'
34
+
35
+ players.each_index.inject({}) do |player_results, j|
36
+ player_results[players[j].to_sym] = stack_changes[j].to_r
37
+ player_results
38
+ end
39
+ else
40
+ nil
41
+ end
42
+ end
43
+
44
+ def self.parse_file(acpc_log_file_path, player_names, game_def_directory)
45
+ File.open(acpc_log_file_path, 'r') do |file|
46
+ HandResults.parse file, player_names, game_def_directory
47
+ end
48
+ end
49
+
50
+ alias_new :parse
51
+
52
+ def initialize(acpc_log_statements, player_names, game_def_directory)
53
+ @final_score = nil
54
+ @match_def = nil
55
+
56
+ @data = acpc_log_statements.inject([]) do |accumulating_data, log_line|
57
+ if @match_def.nil?
58
+ @match_def = MatchDefinition.parse(log_line, player_names, game_def_directory)
59
+ else
60
+ parsed_message = HandResults.parse_state(log_line)
61
+ if parsed_message
62
+ accumulating_data << parsed_message
63
+ else
64
+ @final_score = HandResults.parse_score(log_line) unless @final_score
65
+ end
66
+ end
67
+
68
+ accumulating_data
69
+ end
70
+ end
71
+ end