Class: AcpcTableManager::Match
- Inherits:
-
Object
- Object
- AcpcTableManager::Match
- Defined in:
- lib/acpc_table_manager/match.rb
Constant Summary collapse
- UNIQUENESS_GUARANTEE_CHARACTER =
'_'
Class Method Summary collapse
- .create_with_defaults(user_name: 'Guest', game_definition_key: :two_player_limit, port_numbers: []) ⇒ Object
- .default_opponent_names(num_players) ⇒ Object
- .delete_finished_matches! ⇒ Object
- .delete_match!(match_id) ⇒ Object
-
.delete_matches_older_than!(lifespan) ⇒ Object
Deletion.
- .finished(matches = all) ⇒ Object
- .id_exists?(match_id, matches = all) ⇒ Boolean
- .include_game_definition ⇒ Object
-
.include_name ⇒ Object
Schema.
- .include_name_from_user ⇒ Object
- .include_number_of_hands ⇒ Object
- .include_opponent_names ⇒ Object
- .include_seat ⇒ Object
- .include_user_name ⇒ Object
- .kill_all_orphan_processes!(matches = all) ⇒ Object
- .kill_all_orphan_proxies!(matches = all) ⇒ Object
- .kill_process_if_running(pid) ⇒ Object
-
.new_name(user_name, game_def_key: nil, num_hands: nil, seed: nil, seat: nil, time: true) ⇒ Object
Generators.
- .new_random_seat(num_players) ⇒ Object
- .new_random_seed ⇒ Object
- .not_running(matches = all) ⇒ Object
- .ports_in_use(matches = all) ⇒ Object
- .quiet_find(match_id) ⇒ Object
-
.running(matches = all) ⇒ Object
Almost scopes.
- .started_and_unfinished ⇒ Object
- .unfinished(matches = all) ⇒ Object
Instance Method Summary collapse
- #all_slices_up_to_hand_end_viewed? ⇒ Boolean
- #all_slices_viewed? ⇒ Boolean
- #bot_special_port_requirements ⇒ Object
-
#bots(dealer_host) ⇒ Object
Schema field :port_numbers, type: Array field :random_seed, type: Integer, default: new_random_seed field :last_slice_viewed, type: Integer, default: -1 field :dealer_pid, type: Integer, default: nil field :proxy_pid, type: Integer, default: nil field :ready_to_start, type: Boolean, default: false field :unable_to_start_dealer, type: Boolean, default: false field :dealer_options, type: String, default: ( [ ‘-a’, # Append logs with the same name rather than overwrite “–t_response 80000”, # 80 seconds per action ‘–t_hand -1’, ‘–t_per_hand -1’ ].join(‘ ’) ) include_name include_name_from_user include_user_name include_game_definition include_number_of_hands include_opponent_names include_seat.
- #copy? ⇒ Boolean
- #copy_for_next_human_player(next_user_name, next_seat) ⇒ Object
- #dealer_running? ⇒ Boolean
- #defunkt? ⇒ Boolean
- #finish_starting! ⇒ Object
- #finished? ⇒ Boolean
- #game_def ⇒ Object
- #game_def_file_name_from_key ⇒ Object
- #game_def_hash_from_key ⇒ Object
-
#game_info ⇒ Object
Convenience accessors.
- #hand_number ⇒ Object
- #kill_dealer! ⇒ Object
- #kill_orphan_processes! ⇒ Object
- #kill_orphan_proxy! ⇒ Object
- #kill_proxy! ⇒ Object
- #no_limit? ⇒ Boolean
- #opponent_ports ⇒ Object
- #opponent_ports_with_condition ⇒ Object
- #opponent_ports_without_condition ⇒ Object
- #opponent_seats(opponent_name) ⇒ Object
- #opponent_seats_with_condition ⇒ Object
- #player_names ⇒ Object
- #proxy_running? ⇒ Boolean
- #rejoinable_seats(user_name) ⇒ Object
- #running? ⇒ Boolean
- #sanitized_name ⇒ Object
-
#set_dealer_options!(options) ⇒ Object
Initializers.
- #set_game_definition_file_name!(file_name = ) ⇒ Object
- #set_game_definition_hash!(hash = self.game_def_hash) ⇒ Object
- #set_name!(name_ = self.name_from_user) ⇒ Object
- #set_seat!(seat_ = self.seat) ⇒ Object
- #started? ⇒ Boolean
- #users_port ⇒ Object
Class Method Details
.create_with_defaults(user_name: 'Guest', game_definition_key: :two_player_limit, port_numbers: []) ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/acpc_table_manager/match.rb', line 170 def create_with_defaults( user_name: 'Guest', game_definition_key: :two_player_limit, port_numbers: [] ) new( name_from_user: new_name(user_name), user_name: user_name, port_numbers: port_numbers, game_definition_key: game_definition_key ).finish_starting! end |
.default_opponent_names(num_players) ⇒ Object
166 167 168 |
# File 'lib/acpc_table_manager/match.rb', line 166 def default_opponent_names(num_players) (num_players - 1).times.map { |i| "Tester" } end |
.delete_finished_matches! ⇒ Object
188 189 190 191 192 193 |
# File 'lib/acpc_table_manager/match.rb', line 188 def delete_finished_matches! finished.each do |m| m.delete if m.all_slices_viewed? end self end |
.delete_match!(match_id) ⇒ Object
194 195 196 197 198 199 200 201 202 |
# File 'lib/acpc_table_manager/match.rb', line 194 def delete_match!(match_id) begin match = find match_id rescue Mongoid::Errors::DocumentNotFound else match.delete end self end |
.delete_matches_older_than!(lifespan) ⇒ Object
Deletion
184 185 186 187 |
# File 'lib/acpc_table_manager/match.rb', line 184 def delete_matches_older_than!(lifespan) old(lifespan).delete_all self end |
.finished(matches = all) ⇒ Object
79 80 81 |
# File 'lib/acpc_table_manager/match.rb', line 79 def finished(matches=all) matches.select { |match| match.finished? } end |
.id_exists?(match_id, matches = all) ⇒ Boolean
60 61 62 |
# File 'lib/acpc_table_manager/match.rb', line 60 def id_exists?(match_id, matches=all) matches.where(id: match_id).exists? end |
.include_game_definition ⇒ Object
117 118 119 120 121 122 |
# File 'lib/acpc_table_manager/match.rb', line 117 def include_game_definition field :game_definition_key, type: Symbol validates_presence_of :game_definition_key field :game_definition_file_name field :game_def_hash, type: Hash end |
.include_name ⇒ Object
Schema
106 107 108 109 110 |
# File 'lib/acpc_table_manager/match.rb', line 106 def include_name field :name validates_presence_of :name validates_format_of :name, without: /\A\s*\z/ end |
.include_name_from_user ⇒ Object
111 112 113 114 115 116 |
# File 'lib/acpc_table_manager/match.rb', line 111 def include_name_from_user field :name_from_user validates_presence_of :name_from_user validates_format_of :name_from_user, without: /\A\s*\z/ validates_uniqueness_of :name_from_user end |
.include_number_of_hands ⇒ Object
123 124 125 126 127 |
# File 'lib/acpc_table_manager/match.rb', line 123 def include_number_of_hands field :number_of_hands, type: Integer validates_presence_of :number_of_hands validates_numericality_of :number_of_hands, greater_than: 0, only_integer: true end |
.include_opponent_names ⇒ Object
128 129 130 131 |
# File 'lib/acpc_table_manager/match.rb', line 128 def include_opponent_names field :opponent_names, type: Array validates_presence_of :opponent_names end |
.include_seat ⇒ Object
132 133 134 |
# File 'lib/acpc_table_manager/match.rb', line 132 def include_seat field :seat, type: Integer end |
.include_user_name ⇒ Object
135 136 137 138 139 |
# File 'lib/acpc_table_manager/match.rb', line 135 def include_user_name field :user_name validates_presence_of :user_name validates_format_of :user_name, without: /\A\s*\z/ end |
.kill_all_orphan_processes!(matches = all) ⇒ Object
97 98 99 |
# File 'lib/acpc_table_manager/match.rb', line 97 def kill_all_orphan_processes!(matches=all) matches.each { |m| m.kill_orphan_processes! } end |
.kill_all_orphan_proxies!(matches = all) ⇒ Object
101 102 103 |
# File 'lib/acpc_table_manager/match.rb', line 101 def kill_all_orphan_proxies!(matches=all) matches.each { |m| m.kill_orphan_proxy! } end |
.kill_process_if_running(pid) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/acpc_table_manager/match.rb', line 43 def kill_process_if_running(pid) if pid && pid > 0 begin safe_kill pid if AcpcDealer::process_exists?(pid) AcpcDealer::force_kill_process pid sleep 1 # Give the process a chance to exit if AcpcDealer::process_exists?(pid) yield if block_given? end end rescue Errno::ESRCH end end end |
.new_name(user_name, game_def_key: nil, num_hands: nil, seed: nil, seat: nil, time: true) ⇒ Object
Generators
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/acpc_table_manager/match.rb', line 142 def new_name( user_name, game_def_key: nil, num_hands: nil, seed: nil, seat: nil, time: true ) name = "match.#{user_name}" name += ".#{game_def_key}" if game_def_key name += ".#{num_hands}h" if num_hands name += ".#{seat}s" if seat name += ".#{seed}r" if seed name += ".#{Time.now_as_string}" if time name end |
.new_random_seat(num_players) ⇒ Object
163 164 165 |
# File 'lib/acpc_table_manager/match.rb', line 163 def new_random_seat(num_players) rand(num_players) + 1 end |
.new_random_seed ⇒ Object
158 159 160 161 162 |
# File 'lib/acpc_table_manager/match.rb', line 158 def new_random_seed # The ACPC dealer requires 32 bit random seeds # TODO The bound to rand is exclusive so this should be 2**33 rand(2**33 - 1) end |
.not_running(matches = all) ⇒ Object
76 77 78 |
# File 'lib/acpc_table_manager/match.rb', line 76 def not_running(matches=all) matches.select { |match| !match.running? } end |
.ports_in_use(matches = all) ⇒ Object
89 90 91 92 93 94 95 |
# File 'lib/acpc_table_manager/match.rb', line 89 def ports_in_use(matches=all) ports = [] matches.possibly_running.each do |m| ports += m.port_numbers if m.running? end ports end |
.quiet_find(match_id) ⇒ Object
64 65 66 67 68 69 70 |
# File 'lib/acpc_table_manager/match.rb', line 64 def quiet_find(match_id) begin match = Match.find match_id rescue Mongoid::Errors::DocumentNotFound nil end end |
.running(matches = all) ⇒ Object
Almost scopes
73 74 75 |
# File 'lib/acpc_table_manager/match.rb', line 73 def running(matches=all) matches.possibly_running.select { |match| match.running? } end |
.started_and_unfinished ⇒ Object
85 86 87 |
# File 'lib/acpc_table_manager/match.rb', line 85 def started_and_unfinished started.to_a.select { |match| !match.finished? } end |
.unfinished(matches = all) ⇒ Object
82 83 84 |
# File 'lib/acpc_table_manager/match.rb', line 82 def unfinished(matches=all) matches.select { |match| !match.finished? } end |
Instance Method Details
#all_slices_up_to_hand_end_viewed? ⇒ Boolean
343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/acpc_table_manager/match.rb', line 343 def all_slices_up_to_hand_end_viewed? (self.slices.length - 1).downto(0).each do |slice_index| slice = self.slices[slice_index] if slice.hand_has_ended if self.last_slice_viewed >= slice_index return true else return false end end end return all_slices_viewed? end |
#all_slices_viewed? ⇒ Boolean
340 341 342 |
# File 'lib/acpc_table_manager/match.rb', line 340 def all_slices_viewed? self.last_slice_viewed >= (self.slices.length - 1) end |
#bot_special_port_requirements ⇒ Object
359 360 361 362 363 |
# File 'lib/acpc_table_manager/match.rb', line 359 def bot_special_port_requirements ::AcpcTableManager.exhibition_config.bots(game_definition_key, *opponent_names).values.map do |bot| bot['requires_special_port'] end end |
#bots(dealer_host) ⇒ Object
Schema field :port_numbers, type: Array field :random_seed, type: Integer, default: new_random_seed field :last_slice_viewed, type: Integer, default: -1 field :dealer_pid, type: Integer, default: nil field :proxy_pid, type: Integer, default: nil field :ready_to_start, type: Boolean, default: false field :unable_to_start_dealer, type: Boolean, default: false field :dealer_options, type: String, default: (
[
'-a', # Append logs with the same name rather than overwrite
"--t_response 80000", # 80 seconds per action
'--t_hand -1',
'--t_per_hand -1'
].join(' ')
) include_name include_name_from_user include_user_name include_game_definition include_number_of_hands include_opponent_names include_seat
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/acpc_table_manager/match.rb', line 229 def bots(dealer_host) bot_info_from_config_that_match_opponents = ::AcpcTableManager.exhibition_config.bots(game_definition_key, *opponent_names) bot_opponent_ports = opponent_ports_with_condition do |name| bot_info_from_config_that_match_opponents.keys.include? name end raise unless ( port_numbers.length == player_names.length || bot_opponent_ports.length == bot_info_from_config_that_match_opponents.length ) bot_opponent_ports.zip( bot_info_from_config_that_match_opponents.keys, bot_info_from_config_that_match_opponents.values ).reduce({}) do |map, args| port_num, name, info = args map[name] = { runner: (if info['runner'] then info['runner'] else info end), host: dealer_host, port: port_num } map end end |
#copy? ⇒ Boolean
305 306 307 |
# File 'lib/acpc_table_manager/match.rb', line 305 def copy? self.name_from_user.match(/^#{UNIQUENESS_GUARANTEE_CHARACTER}+$/) end |
#copy_for_next_human_player(next_user_name, next_seat) ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/acpc_table_manager/match.rb', line 288 def copy_for_next_human_player(next_user_name, next_seat) match = dup # This match was not given a name from the user, # so set this parameter to an arbitrary character match.name_from_user = UNIQUENESS_GUARANTEE_CHARACTER while !match.save do match.name_from_user << UNIQUENESS_GUARANTEE_CHARACTER end match.user_name = next_user_name # Swap seat match.seat = next_seat match.opponent_names.insert(seat - 1, user_name) match.opponent_names.delete_at(seat - 1) match.save!(validate: false) match end |
#dealer_running? ⇒ Boolean
334 335 336 |
# File 'lib/acpc_table_manager/match.rb', line 334 def dealer_running? self.dealer_pid && self.dealer_pid > 0 && AcpcDealer::process_exists?(self.dealer_pid) end |
#defunkt? ⇒ Boolean
415 416 417 |
# File 'lib/acpc_table_manager/match.rb', line 415 def defunkt?() (started? and !running? and !finished?) || self.unable_to_start_dealer end |
#finish_starting! ⇒ Object
278 279 280 281 282 283 284 285 |
# File 'lib/acpc_table_manager/match.rb', line 278 def finish_starting! set_name!.set_seat!.set_game_definition_file_name!.set_game_definition_hash! self.opponent_names ||= self.class().default_opponent_names(game_info['num_players']) self.number_of_hands ||= 1 self.ready_to_start = true save! self end |
#finished? ⇒ Boolean
332 |
# File 'lib/acpc_table_manager/match.rb', line 332 def finished?() started? && self.slices.last.match_ended? end |
#game_def ⇒ Object
318 319 320 |
# File 'lib/acpc_table_manager/match.rb', line 318 def game_def @game_def ||= AcpcPokerTypes::GameDefinition.new(game_def_hash_from_key) end |
#game_def_file_name_from_key ⇒ Object
314 |
# File 'lib/acpc_table_manager/match.rb', line 314 def game_def_file_name_from_key() game_info['file'] end |
#game_def_hash_from_key ⇒ Object
315 316 317 |
# File 'lib/acpc_table_manager/match.rb', line 315 def game_def_hash_from_key() @game_def_hash_from_key ||= AcpcPokerTypes::GameDefinition.parse_file(game_def_file_name_from_key).to_h end |
#game_info ⇒ Object
Convenience accessors
310 311 312 |
# File 'lib/acpc_table_manager/match.rb', line 310 def game_info @game_info ||= AcpcTableManager.exhibition_config.games[self.game_definition_key.to_s] end |
#hand_number ⇒ Object
321 322 323 324 325 326 327 |
# File 'lib/acpc_table_manager/match.rb', line 321 def hand_number return nil if slices.last.nil? state = AcpcPokerTypes::MatchState.parse( slices.last.state_string ) if state then state.hand_number else nil end end |
#kill_dealer! ⇒ Object
402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/acpc_table_manager/match.rb', line 402 def kill_dealer! self.class().kill_process_if_running(self.dealer_pid) do raise( StandardError.new( "Dealer process #{self.dealer_pid} couldn't be killed!" ) ) end self.dealer_pid = nil save self end |
#kill_orphan_processes! ⇒ Object
436 437 438 439 440 441 442 |
# File 'lib/acpc_table_manager/match.rb', line 436 def kill_orphan_processes! if dealer_running? && !proxy_running? kill_dealer! elsif proxy_running && !dealer_running? kill_proxy! end end |
#kill_orphan_proxy! ⇒ Object
432 433 434 |
# File 'lib/acpc_table_manager/match.rb', line 432 def kill_orphan_proxy! kill_proxy! if proxy_running? && !dealer_running? end |
#kill_proxy! ⇒ Object
419 420 421 422 423 424 425 426 427 428 429 430 |
# File 'lib/acpc_table_manager/match.rb', line 419 def kill_proxy! self.class().kill_process_if_running(self.proxy_pid) do raise( StandardError.new( "Proxy process #{self.proxy_pid} couldn't be killed!" ) ) end self.proxy_pid = nil save self end |
#no_limit? ⇒ Boolean
328 329 330 |
# File 'lib/acpc_table_manager/match.rb', line 328 def no_limit? @is_no_limit ||= game_def.betting_type == AcpcPokerTypes::GameDefinition::BETTING_TYPES[:nolimit] end |
#opponent_ports ⇒ Object
367 368 369 370 371 |
# File 'lib/acpc_table_manager/match.rb', line 367 def opponent_ports port_numbers_ = port_numbers.dup users_port_ = port_numbers_.delete_at(seat - 1) port_numbers_ end |
#opponent_ports_with_condition ⇒ Object
380 381 382 383 384 |
# File 'lib/acpc_table_manager/match.rb', line 380 def opponent_ports_with_condition opponent_seats_with_condition { |player_name| yield player_name }.map do |opp_seat| port_numbers[opp_seat - 1] end end |
#opponent_ports_without_condition ⇒ Object
385 386 387 388 389 390 391 |
# File 'lib/acpc_table_manager/match.rb', line 385 def opponent_ports_without_condition local_opponent_ports = opponent_ports opponent_ports_with_condition { |player_name| yield player_name }.each do |port| local_opponent_ports.delete port end local_opponent_ports end |
#opponent_seats(opponent_name) ⇒ Object
377 378 379 |
# File 'lib/acpc_table_manager/match.rb', line 377 def opponent_seats(opponent_name) opponent_seats_with_condition { |player_name| player_name == opponent_name } end |
#opponent_seats_with_condition ⇒ Object
372 373 374 375 376 |
# File 'lib/acpc_table_manager/match.rb', line 372 def opponent_seats_with_condition player_names.each_index.select do |i| yield player_names[i] end.map { |s| s + 1 } - [self.seat] end |
#player_names ⇒ Object
356 357 358 |
# File 'lib/acpc_table_manager/match.rb', line 356 def player_names opponent_names.dup.insert seat-1, self.user_name end |
#proxy_running? ⇒ Boolean
337 338 339 |
# File 'lib/acpc_table_manager/match.rb', line 337 def proxy_running? self.proxy_pid && self.proxy_pid > 0 && AcpcDealer::process_exists?(self.proxy_pid) end |
#rejoinable_seats(user_name) ⇒ Object
392 393 394 395 396 397 398 |
# File 'lib/acpc_table_manager/match.rb', line 392 def rejoinable_seats(user_name) ( opponent_seats(user_name) - # Remove seats already taken by players who have already joined this match self.class().where(name: self.name).ne(name_from_user: self.name).map { |m| m.seat } ) end |
#running? ⇒ Boolean
333 |
# File 'lib/acpc_table_manager/match.rb', line 333 def running?() dealer_running? && proxy_running? end |
#sanitized_name ⇒ Object
399 400 401 |
# File 'lib/acpc_table_manager/match.rb', line 399 def sanitized_name Zaru.sanitize!(Shellwords.escape(self.name.gsub(/\s+/, '_'))) end |
#set_dealer_options!(options) ⇒ Object
Initializers
254 255 256 257 |
# File 'lib/acpc_table_manager/match.rb', line 254 def () self. = (.split(' ').map { |o| Shellwords.escape o }.join(' ') || '') self end |
#set_game_definition_file_name!(file_name = ) ⇒ Object
271 272 273 274 |
# File 'lib/acpc_table_manager/match.rb', line 271 def set_game_definition_file_name!(file_name = game_info['file']) self.game_definition_file_name = file_name self end |
#set_game_definition_hash!(hash = self.game_def_hash) ⇒ Object
275 276 277 |
# File 'lib/acpc_table_manager/match.rb', line 275 def set_game_definition_hash!(hash = self.game_def_hash) self.game_def_hash = hash || game_def_hash_from_key end |
#set_name!(name_ = self.name_from_user) ⇒ Object
258 259 260 261 262 263 |
# File 'lib/acpc_table_manager/match.rb', line 258 def set_name!(name_ = self.name_from_user) name_from_user_ = name_.strip self.name = name_from_user_ self.name_from_user = name_from_user_ self end |
#set_seat!(seat_ = self.seat) ⇒ Object
264 265 266 267 268 269 270 |
# File 'lib/acpc_table_manager/match.rb', line 264 def set_seat!(seat_ = self.seat) self.seat = seat_ || self.class().new_random_seat(game_info['num_players']) if self.seat > game_info['num_players'] self.seat = game_info['num_players'] end self end |
#started? ⇒ Boolean
331 |
# File 'lib/acpc_table_manager/match.rb', line 331 def started?() !self.slices.empty? end |
#users_port ⇒ Object
364 365 366 |
# File 'lib/acpc_table_manager/match.rb', line 364 def users_port port_numbers[seat - 1] end |