Class: AcpcTableManager::Match
- Inherits:
-
Object
- Object
- AcpcTableManager::Match
- Includes:
- Mongoid::Document, Mongoid::Timestamps::Updated
- 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.
- .safe_kill(pid) ⇒ Object
- .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
- #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
-
#queue ⇒ Object
The matches to be started (have not been started and not currently running) ordered from newest to oldest.
- #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
191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/acpc_table_manager/match.rb', line 191 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
187 188 189 |
# File 'lib/acpc_table_manager/match.rb', line 187 def default_opponent_names(num_players) (num_players - 1).times.map { |i| "Tester" } end |
.delete_finished_matches! ⇒ Object
209 210 211 212 213 214 |
# File 'lib/acpc_table_manager/match.rb', line 209 def delete_finished_matches! finished.each do |m| m.delete if m.all_slices_viewed? end self end |
.delete_match!(match_id) ⇒ Object
215 216 217 218 219 220 221 222 223 |
# File 'lib/acpc_table_manager/match.rb', line 215 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
205 206 207 208 |
# File 'lib/acpc_table_manager/match.rb', line 205 def delete_matches_older_than!(lifespan) old(lifespan).delete_all self end |
.finished(matches = all) ⇒ Object
105 106 107 |
# File 'lib/acpc_table_manager/match.rb', line 105 def finished(matches=all) matches.select { |match| match.finished? } end |
.id_exists?(match_id, matches = all) ⇒ Boolean
86 87 88 |
# File 'lib/acpc_table_manager/match.rb', line 86 def id_exists?(match_id, matches=all) matches.where(id: match_id).exists? end |
.include_game_definition ⇒ Object
139 140 141 142 143 144 |
# File 'lib/acpc_table_manager/match.rb', line 139 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
128 129 130 131 132 |
# File 'lib/acpc_table_manager/match.rb', line 128 def include_name field :name validates_presence_of :name validates_format_of :name, without: /\A\s*\z/ end |
.include_name_from_user ⇒ Object
133 134 135 136 137 138 |
# File 'lib/acpc_table_manager/match.rb', line 133 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
145 146 147 148 149 |
# File 'lib/acpc_table_manager/match.rb', line 145 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
150 151 152 153 |
# File 'lib/acpc_table_manager/match.rb', line 150 def include_opponent_names field :opponent_names, type: Array validates_presence_of :opponent_names end |
.include_seat ⇒ Object
154 155 156 |
# File 'lib/acpc_table_manager/match.rb', line 154 def include_seat field :seat, type: Integer end |
.include_user_name ⇒ Object
157 158 159 160 161 |
# File 'lib/acpc_table_manager/match.rb', line 157 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
119 120 121 |
# File 'lib/acpc_table_manager/match.rb', line 119 def kill_all_orphan_processes!(matches=all) matches.each { |m| m.kill_orphan_processes! } end |
.kill_all_orphan_proxies!(matches = all) ⇒ Object
123 124 125 |
# File 'lib/acpc_table_manager/match.rb', line 123 def kill_all_orphan_proxies!(matches=all) matches.each { |m| m.kill_orphan_proxy! } end |
.kill_process_if_running(pid) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/acpc_table_manager/match.rb', line 69 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
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/acpc_table_manager/match.rb', line 164 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
184 185 186 |
# File 'lib/acpc_table_manager/match.rb', line 184 def new_random_seat(num_players) rand(num_players) + 1 end |
.new_random_seed ⇒ Object
180 181 182 183 |
# File 'lib/acpc_table_manager/match.rb', line 180 def new_random_seed # The ACPC dealer requires 32 bit random seeds rand(2**33 - 1) end |
.not_running(matches = all) ⇒ Object
102 103 104 |
# File 'lib/acpc_table_manager/match.rb', line 102 def not_running(matches=all) matches.select { |match| !match.running? } end |
.ports_in_use(matches = all) ⇒ Object
115 116 117 |
# File 'lib/acpc_table_manager/match.rb', line 115 def ports_in_use(matches=all) running(matches).inject([]) { |ports, m| ports += m.port_numbers } end |
.quiet_find(match_id) ⇒ Object
90 91 92 93 94 95 96 |
# File 'lib/acpc_table_manager/match.rb', line 90 def quiet_find(match_id) begin match = Match.find match_id rescue Mongoid::Errors::DocumentNotFound nil end end |
.running(matches = all) ⇒ Object
Almost scopes
99 100 101 |
# File 'lib/acpc_table_manager/match.rb', line 99 def running(matches=all) matches.possibly_running.select { |match| match.running? } end |
.safe_kill(pid) ⇒ Object
63 64 65 66 67 68 |
# File 'lib/acpc_table_manager/match.rb', line 63 def safe_kill(pid) if pid && pid > 0 AcpcDealer::kill_process pid sleep 1 # Give the process a chance to exit end end |
.started_and_unfinished ⇒ Object
111 112 113 |
# File 'lib/acpc_table_manager/match.rb', line 111 def started_and_unfinished started.to_a.select { |match| !match.finished? } end |
.unfinished(matches = all) ⇒ Object
108 109 110 |
# File 'lib/acpc_table_manager/match.rb', line 108 def unfinished(matches=all) matches.select { |match| !match.finished? } end |
Instance Method Details
#all_slices_up_to_hand_end_viewed? ⇒ Boolean
365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/acpc_table_manager/match.rb', line 365 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
362 363 364 |
# File 'lib/acpc_table_manager/match.rb', line 362 def all_slices_viewed? self.last_slice_viewed >= (self.slices.length - 1) end |
#bot_special_port_requirements ⇒ Object
381 382 383 384 385 |
# File 'lib/acpc_table_manager/match.rb', line 381 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
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/acpc_table_manager/match.rb', line 251 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
327 328 329 |
# File 'lib/acpc_table_manager/match.rb', line 327 def copy? self.name_from_user.match(/^#{UNIQUENESS_GUARANTEE_CHARACTER}+$/) end |
#copy_for_next_human_player(next_user_name, next_seat) ⇒ Object
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/acpc_table_manager/match.rb', line 310 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
356 357 358 |
# File 'lib/acpc_table_manager/match.rb', line 356 def dealer_running? self.dealer_pid && self.dealer_pid > 0 && AcpcDealer::process_exists?(self.dealer_pid) end |
#defunkt? ⇒ Boolean
437 438 439 |
# File 'lib/acpc_table_manager/match.rb', line 437 def defunkt?() (started? and !running? and !finished?) || self.unable_to_start_dealer end |
#finish_starting! ⇒ Object
300 301 302 303 304 305 306 307 |
# File 'lib/acpc_table_manager/match.rb', line 300 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
354 |
# File 'lib/acpc_table_manager/match.rb', line 354 def finished?() started? && self.slices.last.match_ended? end |
#game_def ⇒ Object
340 341 342 |
# File 'lib/acpc_table_manager/match.rb', line 340 def game_def @game_def ||= AcpcPokerTypes::GameDefinition.new(game_def_hash_from_key) end |
#game_def_file_name_from_key ⇒ Object
336 |
# File 'lib/acpc_table_manager/match.rb', line 336 def game_def_file_name_from_key() game_info['file'] end |
#game_def_hash_from_key ⇒ Object
337 338 339 |
# File 'lib/acpc_table_manager/match.rb', line 337 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
332 333 334 |
# File 'lib/acpc_table_manager/match.rb', line 332 def game_info @game_info ||= AcpcTableManager.exhibition_config.games[self.game_definition_key.to_s] end |
#hand_number ⇒ Object
343 344 345 346 347 348 349 |
# File 'lib/acpc_table_manager/match.rb', line 343 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
424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/acpc_table_manager/match.rb', line 424 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
458 459 460 461 462 463 464 |
# File 'lib/acpc_table_manager/match.rb', line 458 def kill_orphan_processes! if dealer_running? && !proxy_running? kill_dealer! elsif proxy_running && !dealer_running? kill_proxy! end end |
#kill_orphan_proxy! ⇒ Object
454 455 456 |
# File 'lib/acpc_table_manager/match.rb', line 454 def kill_orphan_proxy! kill_proxy! if proxy_running? && !dealer_running? end |
#kill_proxy! ⇒ Object
441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/acpc_table_manager/match.rb', line 441 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
350 351 352 |
# File 'lib/acpc_table_manager/match.rb', line 350 def no_limit? @is_no_limit ||= game_def.betting_type == AcpcPokerTypes::GameDefinition::BETTING_TYPES[:nolimit] end |
#opponent_ports ⇒ Object
389 390 391 392 393 |
# File 'lib/acpc_table_manager/match.rb', line 389 def opponent_ports port_numbers_ = port_numbers.dup users_port_ = port_numbers_.delete_at(seat - 1) port_numbers_ end |
#opponent_ports_with_condition ⇒ Object
402 403 404 405 406 |
# File 'lib/acpc_table_manager/match.rb', line 402 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
407 408 409 410 411 412 413 |
# File 'lib/acpc_table_manager/match.rb', line 407 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
399 400 401 |
# File 'lib/acpc_table_manager/match.rb', line 399 def opponent_seats(opponent_name) opponent_seats_with_condition { |player_name| player_name == opponent_name } end |
#opponent_seats_with_condition ⇒ Object
394 395 396 397 398 |
# File 'lib/acpc_table_manager/match.rb', line 394 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
378 379 380 |
# File 'lib/acpc_table_manager/match.rb', line 378 def player_names opponent_names.dup.insert seat-1, self.user_name end |
#proxy_running? ⇒ Boolean
359 360 361 |
# File 'lib/acpc_table_manager/match.rb', line 359 def proxy_running? self.proxy_pid && self.proxy_pid > 0 && AcpcDealer::process_exists?(self.proxy_pid) end |
#queue ⇒ Object
Returns The matches to be started (have not been started and not currently running) ordered from newest to oldest.
55 |
# File 'lib/acpc_table_manager/match.rb', line 55 scope :queue, not_started.and.ready_to_start.desc(:updated_at) |
#rejoinable_seats(user_name) ⇒ Object
414 415 416 417 418 419 420 |
# File 'lib/acpc_table_manager/match.rb', line 414 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
355 |
# File 'lib/acpc_table_manager/match.rb', line 355 def running?() dealer_running? && proxy_running? end |
#sanitized_name ⇒ Object
421 422 423 |
# File 'lib/acpc_table_manager/match.rb', line 421 def sanitized_name Zaru.sanitize!(Shellwords.escape(self.name.gsub(/\s+/, '_'))) end |
#set_dealer_options!(options) ⇒ Object
Initializers
276 277 278 279 |
# File 'lib/acpc_table_manager/match.rb', line 276 def () self. = (.split(' ').map { |o| Shellwords.escape o }.join(' ') || '') self end |
#set_game_definition_file_name!(file_name = ) ⇒ Object
293 294 295 296 |
# File 'lib/acpc_table_manager/match.rb', line 293 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
297 298 299 |
# File 'lib/acpc_table_manager/match.rb', line 297 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
280 281 282 283 284 285 |
# File 'lib/acpc_table_manager/match.rb', line 280 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
286 287 288 289 290 291 292 |
# File 'lib/acpc_table_manager/match.rb', line 286 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
353 |
# File 'lib/acpc_table_manager/match.rb', line 353 def started?() !self.slices.empty? end |
#users_port ⇒ Object
386 387 388 |
# File 'lib/acpc_table_manager/match.rb', line 386 def users_port port_numbers[seat - 1] end |