def _run(self): warnings = [] log_entries = [] try: game = gtp_games.Game(self.board_size, self.komi, self.move_limit) game.set_player_code('b', self.player_b.code) game.set_player_code('w', self.player_w.code) game.set_game_id(self.game_id) except ValueError, e: raise job_manager.JobFailed("error creating game: %s" % e)
class Game_job(object): """A game to be played in a worker process. A Game_job is designed to be used a job object for the job manager. That is, its public interface is the run() method. When the job is run, it plays a GTP game as described by its attributes, and optionally writes an SGF file. The job result is a Game_job_result object. required attributes: game_id -- short string player_b -- Player player_w -- Player board_size -- int komi -- float move_limit -- int optional attributes (default None unless otherwise stated): game_data -- arbitrary pickleable data handicap -- int handicap_is_free -- bool (default False) use_internal_scorer -- bool (default True) internal_scorer_handicap_compensation -- 'no' , 'short', or 'full' (default 'no') sgf_filename -- filename for the SGF file sgf_dirname -- directory pathname for the SGF file void_sgf_dirname -- directory pathname for the SGF file for void games sgf_game_name -- string to show as SGF Game Name (default game_id) sgf_event -- string to show as SGF EVent sgf_note -- multiline string to put into SGF root comment gtp_log_pathname -- pathname to use for the GTP log stderr_pathname -- pathname to send players' stderr to The game_id will be returned in the job result, so you can tell which game you're getting the result for. It also appears in the SGF file as a comment and the GN property. game_data is returned in the job result. It's provided as a convenient way to pass a small amount of information from get_job() to process_response(). If use_internal_scorer is False, the Players' is_reliable_scorer attributes are used to determine who scores the game (see errors.rst). If sgf_dirname and sgf_filename are set, an SGF file will be written after the game is over. If void_sgf_dirname and sgf_filename are set, an SGF file will be written for any void games (games which were aborted due to unhandled errors) which have at least one move. The leaf directory will be created if necessary. If gtp_log_pathname is set, all GTP messages to and from both players will be logged (this doesn't append; any existing file will be overwritten). If stderr_pathname is set, the specified file will be opened in append mode and both players' standard error streams will be sent there. Otherwise the players' standard error streams will be left as the standard error of the calling process. But if a player has discard_stderr=True then its standard error is sent to os.devnull instead. Game_jobs are suitable for pickling. """ def __init__(self): self.handicap = None self.handicap_is_free = False self.sgf_filename = None self.sgf_dirname = None self.void_sgf_dirname = None self.sgf_game_name = None self.sgf_event = None self.sgf_note = None self.use_internal_scorer = True self.internal_scorer_handicap_compensation = 'no' self.game_data = None self.gtp_log_pathname = None self.stderr_pathname = None # The code here has to be happy to run in a separate process. def run(self, worker_id=None): """Run the job. This method is called by the job manager. worker_id -- int or None Returns a Game_job_result, or raises JobFailed. """ self._worker_id = worker_id self._files_to_close = [] try: return self._run() finally: # These files are all either flushed after every write, or not # written to at all from this process, so there shouldn't be any # errors from close(). for f in self._files_to_close: try: f.close() except EnvironmentError: pass def _start_player(self, game_controller, game, colour, player, gtp_log_file): if player.discard_stderr: stderr_pathname = os.devnull else: stderr_pathname = self.stderr_pathname if stderr_pathname is not None: stderr = open(stderr_pathname, "a") self._files_to_close.append(stderr) else: stderr = None if not self.use_internal_scorer and player.is_reliable_scorer: game.allow_scorer(colour) if player.allow_claim: game.set_claim_allowed(colour) env = player.make_environ() env['GOMILL_GAME_ID'] = self.game_id if self._worker_id is not None: env['GOMILL_SLOT'] = str(self._worker_id) game_controller.set_player_subprocess(colour, player.cmd_args, env=env, cwd=player.cwd, stderr=stderr) controller = game_controller.get_controller(colour) controller.set_gtp_aliases(player.gtp_aliases) if gtp_log_file is not None: controller.channel.enable_logging(gtp_log_file, prefix="%s: " % colour) for command, arguments in player.startup_gtp_commands: game_controller.send_command(colour, command, *arguments) def _run(self): warnings = [] log_entries = [] try: game_controller = gtp_controller.Game_controller( self.player_b.code, self.player_w.code) game = gtp_games.Gtp_game(game_controller, self.board_size, self.komi, self.move_limit) game.set_game_id(self.game_id) except ValueError, e: raise job_manager.JobFailed("error creating game: %s" % e) if self.use_internal_scorer: game.use_internal_scorer( self.internal_scorer_handicap_compensation) if self.gtp_log_pathname is not None: gtp_log_file = open(self.gtp_log_pathname, "w") self._files_to_close.append(gtp_log_file) else: gtp_log_file = None try: self._start_player(game_controller, game, 'b', self.player_b, gtp_log_file) self._start_player(game_controller, game, 'w', self.player_w, gtp_log_file) game.prepare() if self.handicap: try: game.set_handicap(self.handicap, self.handicap_is_free) except ValueError: raise BadGtpResponse("invalid handicap") game.run() except (GtpChannelError, BadGtpResponse), e: game_controller.close_players() msg = "aborting game due to error:\n%s" % e self._record_void_game(game_controller, game, msg) late_error_messages = game_controller.describe_late_errors() if late_error_messages is not None: msg += "\nalso:\n" + late_error_messages raise job_manager.JobFailed(msg)