acpc_table_manager 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +58 -0
- data/Rakefile +10 -0
- data/acpc_table_manager.gemspec +55 -0
- data/bin/console +16 -0
- data/bin/setup +7 -0
- data/exe/acpc_table_manager +63 -0
- data/lib/acpc_table_manager.rb +17 -0
- data/lib/acpc_table_manager/config.rb +180 -0
- data/lib/acpc_table_manager/dealer.rb +57 -0
- data/lib/acpc_table_manager/match.rb +350 -0
- data/lib/acpc_table_manager/match_slice.rb +196 -0
- data/lib/acpc_table_manager/match_view.rb +203 -0
- data/lib/acpc_table_manager/monkey_patches.rb +19 -0
- data/lib/acpc_table_manager/opponents.rb +39 -0
- data/lib/acpc_table_manager/param_retrieval.rb +32 -0
- data/lib/acpc_table_manager/proxy.rb +276 -0
- data/lib/acpc_table_manager/simple_logging.rb +54 -0
- data/lib/acpc_table_manager/table_manager.rb +260 -0
- data/lib/acpc_table_manager/table_queue.rb +379 -0
- data/lib/acpc_table_manager/utils.rb +34 -0
- data/lib/acpc_table_manager/version.rb +3 -0
- metadata +311 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ae4fbf6d36f0f5d968cc7c54909698a162beab9b
|
4
|
+
data.tar.gz: ab4447f63da11d537944cc16d0b86c40da872139
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 108ec4ded055368856ceb64720d600ba48fd1ad27f4d1ec9f436ea134846120ea6c0944a54475a076756849dabcc79dcc88eec698c38be79afbbc54ab08a5742
|
7
|
+
data.tar.gz: 3cf8b04a4187e248b44386f7473c4a49365274e11584fd72f018461788a7407e78c160b42371306fbf780ddc98c5bcbcb373ab8a9999572d4fe4e80cfe8483ca
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Dustin Morrill
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# AcpcTableManager
|
2
|
+
|
3
|
+
Must be able to accomplish the following tasks:
|
4
|
+
|
5
|
+
- Start a dealer
|
6
|
+
- Start a bot and have it connect to the dealer
|
7
|
+
- Start multiple bots and have them connect to the dealer
|
8
|
+
- Start a proxy and connect it to the dealer
|
9
|
+
- Send actions to the proxy for them to be played
|
10
|
+
- Ensure dealer processes are killed when matches are finished
|
11
|
+
- Ensure the number of matches being run is less than set maximum
|
12
|
+
- Manage a queue of matches
|
13
|
+
- Start the next match in the queue when one finishes
|
14
|
+
- Manage a pool of port on which remote bots can connect to dealers
|
15
|
+
|
16
|
+
The following tasks can be done in parallel:
|
17
|
+
|
18
|
+
- Playing actions
|
19
|
+
- Starting proxies
|
20
|
+
- Starting bots
|
21
|
+
|
22
|
+
Everything else must be done sequentially.
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'acpc_table_manager'
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install acpc_table_manager
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
TODO: Write usage instructions here
|
43
|
+
|
44
|
+
## Development
|
45
|
+
|
46
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
47
|
+
|
48
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
49
|
+
|
50
|
+
## Contributing
|
51
|
+
|
52
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/acpc_table_manager.
|
53
|
+
|
54
|
+
|
55
|
+
## License
|
56
|
+
|
57
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
58
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'acpc_table_manager/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "acpc_table_manager"
|
8
|
+
spec.version = AcpcTableManager::VERSION
|
9
|
+
spec.authors = ["Dustin Morrill"]
|
10
|
+
spec.email = ["dmorrill10@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Backend components to the ACPC Poker GUI Client}
|
13
|
+
spec.description = %q{Backend components to the ACPC Poker GUI Client. Includes a player that saves states from the dealer to persistent storage, and components to start, stop, and manage match components.}
|
14
|
+
spec.homepage = "https://github.com/dmorrill10/acpc_table_manager"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# To send emails
|
23
|
+
spec.add_dependency "pony"
|
24
|
+
|
25
|
+
# For persistence
|
26
|
+
spec.add_dependency 'origin', '~>1.0'
|
27
|
+
spec.add_dependency 'moped', '~>1.4'
|
28
|
+
spec.add_dependency "mongoid", '~> 3.1'
|
29
|
+
|
30
|
+
# For message passing
|
31
|
+
spec.add_dependency 'redis', '~> 3.2'
|
32
|
+
|
33
|
+
# For poker logic
|
34
|
+
spec.add_dependency "acpc_poker_types"
|
35
|
+
spec.add_dependency 'acpc_dealer', '~> 2.0'
|
36
|
+
spec.add_dependency 'acpc_poker_player_proxy', '~> 1.1'
|
37
|
+
|
38
|
+
# Simple exception email notifications
|
39
|
+
spec.add_dependency 'rusen'
|
40
|
+
|
41
|
+
# To run processes asynchronously
|
42
|
+
spec.add_dependency 'process_runner', '~> 0.0'
|
43
|
+
|
44
|
+
# For better errors
|
45
|
+
spec.add_dependency 'contextual_exceptions'
|
46
|
+
|
47
|
+
# For better logging
|
48
|
+
spec.add_dependency 'awesome_print'
|
49
|
+
|
50
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
51
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
52
|
+
spec.add_development_dependency "minitest"
|
53
|
+
spec.add_development_dependency "mocha"
|
54
|
+
spec.add_development_dependency "pry"
|
55
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "acpc_table_manager"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
12
|
+
rescue LoadError
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
15
|
+
end
|
16
|
+
|
data/bin/setup
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'acpc_table_manager'
|
4
|
+
require 'redis'
|
5
|
+
require 'json'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
|
9
|
+
ARGV << '-h' if ARGV.empty?
|
10
|
+
|
11
|
+
options = {}
|
12
|
+
OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: #{$0} [options]"
|
14
|
+
|
15
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
16
|
+
puts opts
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("-t", "--table_manager TABLE MANAGER CONFIG", "Table manager configuration file.") do |c|
|
21
|
+
options[:table_manager_config] = File.expand_path c, Dir.pwd
|
22
|
+
end
|
23
|
+
opts.on("-r", "--redis REDIS CONFIG", "Redis configuration file.") do |c|
|
24
|
+
options[:redis_config] = File.expand_path c, Dir.pwd
|
25
|
+
end
|
26
|
+
opts.on("-e", "--env [ENVIRONMENT MODE]", "The environment mode to run in, such as 'development' or 'production'.") do |e|
|
27
|
+
options[:environment_mode] = e if e && !e.empty?
|
28
|
+
end
|
29
|
+
end.parse!
|
30
|
+
|
31
|
+
raise OptionParser::MissingArgument.new('TABLE MANAGER CONFIG') unless options[:table_manager_config]
|
32
|
+
raise OptionParser::MissingArgument.new('REDIS CONFIG') unless options[:redis_config]
|
33
|
+
|
34
|
+
raise OptionParser::ArgumentError.new("#{options[:table_manager_config]} doesn't exist.") unless File.exist?(options[:table_manager_config])
|
35
|
+
raise OptionParser::ArgumentError.new("#{options[:redis_config]} doesn't exist.") unless File.exist?(options[:redis_config])
|
36
|
+
|
37
|
+
REDIS_CONFIG = YAML.load_file(options[:redis_config]).symbolize_keys
|
38
|
+
DFLT = REDIS_CONFIG[:default].symbolize_keys
|
39
|
+
redis = Redis.new(
|
40
|
+
if options[:environment_mode] && REDIS_CONFIG[options[:environment_mode].to_sym]
|
41
|
+
DFLT.merge(REDIS_CONFIG[options[:environment_mode].to_sym].symbolize_keys)
|
42
|
+
else
|
43
|
+
DFLT
|
44
|
+
end
|
45
|
+
)
|
46
|
+
|
47
|
+
CONFIG_FILE = options[:table_manager_config]
|
48
|
+
|
49
|
+
AcpcTableManager.load! CONFIG_FILE
|
50
|
+
table_manager = AcpcTableManager::TableManager.new
|
51
|
+
loop do
|
52
|
+
message = redis.blpop("backend", :timeout => AcpcTableManager.config.maintenance_interval_s)
|
53
|
+
if message
|
54
|
+
data = JSON.parse message[1]
|
55
|
+
if data['request'] == 'reload'
|
56
|
+
AcpcTableManager.load! CONFIG_FILE
|
57
|
+
else
|
58
|
+
table_manager.perform! data['request'], data['params']
|
59
|
+
end
|
60
|
+
else
|
61
|
+
table_manager.maintain!
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative "acpc_table_manager/version"
|
2
|
+
require_relative "acpc_table_manager/config"
|
3
|
+
require_relative "acpc_table_manager/match"
|
4
|
+
require_relative "acpc_table_manager/match_slice"
|
5
|
+
require_relative "acpc_table_manager/match_view"
|
6
|
+
require_relative "acpc_table_manager/monkey_patches"
|
7
|
+
require_relative "acpc_table_manager/opponents"
|
8
|
+
require_relative "acpc_table_manager/dealer"
|
9
|
+
require_relative "acpc_table_manager/param_retrieval"
|
10
|
+
require_relative "acpc_table_manager/proxy"
|
11
|
+
require_relative "acpc_table_manager/simple_logging"
|
12
|
+
require_relative "acpc_table_manager/table_manager"
|
13
|
+
require_relative "acpc_table_manager/table_queue"
|
14
|
+
require_relative "acpc_table_manager/utils"
|
15
|
+
|
16
|
+
module AcpcTableManager
|
17
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'json'
|
3
|
+
require 'mongoid'
|
4
|
+
require 'rusen'
|
5
|
+
require 'contextual_exceptions'
|
6
|
+
require 'acpc_dealer'
|
7
|
+
|
8
|
+
require_relative 'simple_logging'
|
9
|
+
using SimpleLogging::MessageFormatting
|
10
|
+
|
11
|
+
require_relative 'utils'
|
12
|
+
|
13
|
+
module AcpcTableManager
|
14
|
+
class Config
|
15
|
+
include SimpleLogging
|
16
|
+
|
17
|
+
THIS_MACHINE = Socket.gethostname
|
18
|
+
DEALER_HOST = THIS_MACHINE
|
19
|
+
|
20
|
+
attr_reader :file, :log_directory, :my_log_directory, :match_log_directory
|
21
|
+
|
22
|
+
def initialize(file_path, log_directory_, match_log_directory_, interpolation_hash)
|
23
|
+
@file = file_path
|
24
|
+
JSON.parse(File.read(file_path)).each do |constant, val|
|
25
|
+
define_singleton_method(constant.to_sym) do
|
26
|
+
::AcpcTableManager.interpolate_all_strings(val, interpolation_hash)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@log_directory = log_directory_
|
30
|
+
@match_log_directory = match_log_directory_
|
31
|
+
@my_log_directory = File.join(@log_directory, 'acpc_table_manager')
|
32
|
+
@logger = Logger.from_file_name(File.join(@my_log_directory, 'config.log'))
|
33
|
+
end
|
34
|
+
|
35
|
+
def this_machine() THIS_MACHINE end
|
36
|
+
def dealer_host() DEALER_HOST end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ExhibitionConfig
|
40
|
+
include SimpleLogging
|
41
|
+
|
42
|
+
attr_reader :file
|
43
|
+
|
44
|
+
def initialize(file_path, interpolation_hash, logger = Logger.new(STDOUT))
|
45
|
+
@logger = logger
|
46
|
+
@file = file_path
|
47
|
+
JSON.parse(File.read(file_path)).each do |constant, val|
|
48
|
+
interpolated_val = ::AcpcTableManager.interpolate_all_strings(val, interpolation_hash)
|
49
|
+
log(__method__, {adding: {method: constant, value: interpolated_val}})
|
50
|
+
|
51
|
+
instance_variable_set("@#{constant}".to_sym, interpolated_val)
|
52
|
+
define_singleton_method(constant.to_sym) do
|
53
|
+
instance_variable_get("@#{constant}".to_sym)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
unless @special_ports_to_dealer
|
57
|
+
@special_ports_to_dealer = []
|
58
|
+
log(__method__, {adding: {method: 'special_ports_to_dealer', value: @special_ports_to_dealer}})
|
59
|
+
define_singleton_method(:special_ports_to_dealer) do
|
60
|
+
instance_variable_get(:@special_ports_to_dealer)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<Class>] Returns only the names that correspond to bot runner
|
66
|
+
# classes as those classes.
|
67
|
+
def bots(game_def_key, *player_names)
|
68
|
+
game_def_key = game_def_key.to_s
|
69
|
+
if @games[game_def_key]
|
70
|
+
if @games[game_def_key]['opponents']
|
71
|
+
player_names.reduce({}) do |bot_map, name|
|
72
|
+
bot_map[name] = @games[game_def_key]['opponents'][name] if @games[game_def_key]['opponents'][name]
|
73
|
+
bot_map
|
74
|
+
end
|
75
|
+
else
|
76
|
+
log(__method__, {warning: "Game '#{game_def_key}' has no opponents."}, Logger::Severity::WARN)
|
77
|
+
{}
|
78
|
+
end
|
79
|
+
else
|
80
|
+
log(__method__, {warning: "Unrecognized game, '#{game_def_key}'."}, Logger::Severity::WARN)
|
81
|
+
{}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class UninitializedError < StandardError
|
87
|
+
include ContextualExceptions::ContextualError
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.raise_uninitialized
|
91
|
+
raise UninitializedError.new(
|
92
|
+
"Unable to complete with AcpcTableManager uninitialized. Please initialize AcpcTableManager with configuration settings by calling AcpcTableManager.load! with a (YAML) configuration file name."
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
@@config = nil
|
97
|
+
|
98
|
+
def self.config
|
99
|
+
if @@config
|
100
|
+
@@config
|
101
|
+
else
|
102
|
+
raise_uninitialized
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
@@exhibition_config = nil
|
107
|
+
def self.exhibition_config
|
108
|
+
if @@exhibition_config
|
109
|
+
@@exhibition_config
|
110
|
+
else
|
111
|
+
raise_uninitialized
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
@@is_initialized = false
|
116
|
+
|
117
|
+
def self.load_config!(config_data, yaml_directory = File.pwd)
|
118
|
+
interpolation_hash = {
|
119
|
+
pwd: yaml_directory,
|
120
|
+
home: Dir.home,
|
121
|
+
:~ => Dir.home,
|
122
|
+
dealer_directory: AcpcDealer::DEALER_DIRECTORY
|
123
|
+
}
|
124
|
+
config = interpolate_all_strings(config_data, interpolation_hash)
|
125
|
+
|
126
|
+
@@config = Config.new(
|
127
|
+
config['table_manager_constants'],
|
128
|
+
config['log_directory'],
|
129
|
+
config['match_log_directory'],
|
130
|
+
interpolation_hash
|
131
|
+
)
|
132
|
+
@@exhibition_config = ExhibitionConfig.new(
|
133
|
+
config['exhibition_constants'],
|
134
|
+
interpolation_hash,
|
135
|
+
Logger.from_file_name(File.join(@@config.my_log_directory, 'exhibition_config.log'))
|
136
|
+
)
|
137
|
+
|
138
|
+
Mongoid.logger = Logger.from_file_name(File.join(@@config.log_directory, 'mongoid.log'))
|
139
|
+
Mongoid.load!(config['mongoid_config'], config['mongoid_env'].to_sym)
|
140
|
+
|
141
|
+
if config['error_report']
|
142
|
+
Rusen.settings.sender_address = config['error_report']['sender']
|
143
|
+
Rusen.settings.exception_recipients = config['error_report']['recipients']
|
144
|
+
|
145
|
+
Rusen.settings.outputs = config['error_report']['outputs'] || [:email]
|
146
|
+
Rusen.settings.sections = config['error_report']['sections'] || [:backtrace]
|
147
|
+
Rusen.settings.email_prefix = config['error_report']['email_prefix'] || '[ERROR] '
|
148
|
+
Rusen.settings.smtp_settings = config['error_report']['smtp']
|
149
|
+
else
|
150
|
+
@@config.log(__method__, {warning: "Email reporting disabled. Please set email configuration to enable this feature."}, Logger::Severity::WARN)
|
151
|
+
end
|
152
|
+
|
153
|
+
@@is_initialized = true
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.load!(config_file_path)
|
157
|
+
load_config! YAML.load_file(config_file_path), File.dirname(config_file_path)
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.notify(exception)
|
161
|
+
Rusen.notify exception
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.initialized?
|
165
|
+
@@is_initialized
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.raise_if_uninitialized
|
169
|
+
raise_uninitialized unless initialized?
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.new_log(log_file_name)
|
173
|
+
raise_if_uninitialized
|
174
|
+
Logger.from_file_name(File.join(@@config.my_log_directory, log_file_name)).with_metadata!
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.unload!
|
178
|
+
@@is_initialized = false
|
179
|
+
end
|
180
|
+
end
|