Example #1
0
 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)
Example #2
0
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)