示例#1
0
 def __init__(self, tc, player_b=None, player_w=None, **kwargs):
     self.tc = tc
     kwargs.setdefault('board_size', 9)
     game_controller = gtp_controller.Game_controller('one', 'two')
     if player_b is None:
         player_b = gtp_engine_fixtures.Test_player()
     if player_w is None:
         player_w = gtp_engine_fixtures.Test_player()
     engine_b = gtp_engine_fixtures.make_player_engine(player_b)
     engine_w = gtp_engine_fixtures.make_player_engine(player_w)
     channel_b = gtp_controller_test_support.Testing_gtp_channel(engine_b)
     channel_w = gtp_controller_test_support.Testing_gtp_channel(engine_w)
     controller_b = gtp_controller.Gtp_controller(channel_b, 'player one')
     controller_w = gtp_controller.Gtp_controller(channel_w, 'player two')
     game_controller.set_player_controller('b', controller_b)
     game_controller.set_player_controller('w', controller_w)
     game = gtp_games.Gtp_game(game_controller, **kwargs)
     self.game_controller = game_controller
     self.game = game
     self.controller_b = controller_b
     self.controller_w = controller_w
     self.channel_b = channel_b
     self.channel_w = channel_w
     self.engine_b = channel_b.engine
     self.engine_w = channel_w.engine
     self.player_b = channel_b.engine.player
     self.player_w = channel_w.engine.player
示例#2
0
 def __init__(self, tc):
     self.tc = tc
     self.channel = gtp_engine_fixtures.get_test_channel()
     self.underlying_engine = self.channel.engine
     self.controller = gtp_controller.Gtp_controller(
         self.channel, 'testbackend')
     self.proxy = gtp_proxy.Gtp_proxy()
     self.proxy.set_back_end_controller(self.controller)
     self.engine = self.proxy.engine
     self.commands_handled = self.underlying_engine.commands_handled
示例#3
0
def test_error_from_list_commands(tc):
    channel = gtp_engine_fixtures.get_test_channel()
    channel.engine.force_error("list_commands")
    controller = gtp_controller.Gtp_controller(channel, 'testbackend')
    proxy = gtp_proxy.Gtp_proxy()
    with tc.assertRaises(BackEndError) as ar:
        proxy.set_back_end_controller(controller)
    tc.assertEqual(
        str(ar.exception), "failure response from first command "
        "(list_commands) to testbackend:\n"
        "handler forced to fail")
    tc.assertIsInstance(ar.exception.cause, BadGtpResponse)
    proxy.close()
示例#4
0
def main():
    try:
        channel = gtp_controller.Subprocess_gtp_channel(
            ["gnugo", "--mode=gtp"])
        controller = gtp_controller.Gtp_controller(channel, "gnugo")
        controller.do_command("boardsize", "19")
        controller.do_command("clear_board")
        controller.do_command("komi", "6")
        controller.do_command("play", "B", "D4")
        print controller.do_command("genmove", "W")
        print controller.do_command("showboard")
        controller.close()
    except (gtp_controller.GtpChannelError, gtp_controller.BadGtpResponse), e:
        sys.exit(str(e))
示例#5
0
文件: game_jobs.py 项目: uduse/gomill
def check_player(player_check, discard_stderr=False):
    """Do a test run of a GTP engine.

    player_check -- Player_check object

    This starts an engine subprocess, sends it some GTP commands, and ends the
    process again.

    Raises CheckFailed if the player doesn't pass the checks.

    Returns a list of warning messages.

    Currently checks:
     - any explicitly specified cwd exists and is a directory
     - the engine subprocess starts, and replies to GTP commands
     - the engine reports protocol version 2 (if it supports protocol_version)
     - the engine accepts any startup_gtp_commands
     - the engine accepts the specified board size and komi
     - the engine accepts the 'clear_board' command
     - the engine accepts 'quit' and closes down cleanly

    """
    player = player_check.player
    if player.cwd is not None and not os.path.isdir(player.cwd):
        raise CheckFailed("bad working directory: %s" % player.cwd)

    if discard_stderr:
        stderr = open(os.devnull, "w")
    else:
        stderr = None
    try:
        env = player.make_environ()
        env['GOMILL_GAME_ID'] = 'startup-check'
        try:
            channel = gtp_controller.Subprocess_gtp_channel(player.cmd_args,
                                                            env=env,
                                                            cwd=player.cwd,
                                                            stderr=stderr)
        except GtpChannelError, e:
            raise GtpChannelError("error starting subprocess for %s:\n%s" %
                                  (player.code, e))
        controller = gtp_controller.Gtp_controller(channel, player.code)
        controller.set_gtp_aliases(player.gtp_aliases)
        controller.check_protocol_version()
        for command, arguments in player.startup_gtp_commands:
            controller.do_command(command, *arguments)
        controller.do_command("boardsize", str(player_check.board_size))
        controller.do_command("clear_board")
        controller.do_command("komi", str(player_check.komi))
        controller.safe_close()
def test_nontgtp_backend(tc):
    channel = gtp_controller_test_support.Preprogrammed_gtp_channel(
        "Usage: randomprogram [options]\n\nOptions:\n"
        "--help   show this help message and exit\n")
    controller = gtp_controller.Gtp_controller(channel, 'testbackend')
    proxy = gtp_proxy.Gtp_proxy()
    with tc.assertRaises(BackEndError) as ar:
        proxy.set_back_end_controller(controller)
    tc.assertEqual(str(ar.exception),
                   "GTP protocol error reading response to first command "
                   "(list_commands) from testbackend:\n"
                   "engine isn't speaking GTP: first byte is 'U'")
    tc.assertIsInstance(ar.exception.cause, GtpProtocolError)
    proxy.close()
示例#7
0
class Game(object):
    """A single game between two GTP engines.

    Instantiate with:
      board_size -- int
      komi       -- float (default 0.0)
      move_limit -- int   (default 1000)

    The 'commands' values are lists of strings, as for subprocess.Popen.

    Normal use:

      game = Game(...)
      game.set_player_code('b', ...)
      game.set_player_code('w', ...)
      game.use_internal_scorer() or game.allow_scorer(...) [optional]
      game.set_move_callback...() [optional]
      game.set_player_subprocess('b', ...) or set_player_controller('b', ...)
      game.set_player_subprocess('w', ...) or set_player_controller('w', ...)
      game.request_engine_descriptions() [optional]
      game.ready()
      game.set_handicap(...) [optional]
      game.run()
      game.close_players()
      game.make_sgf() or game.write_sgf(...) [optional]

    then retrieve the Game_result and moves.

    If neither use_internal_scorer() nor allow_scorer() is called, the game
    won't be scored.

    Public attributes for reading:
      players               -- map colour -> player code
      game_id               -- string or None
      result                -- Game_result (None before the game is complete)
      moves                 -- list of tuples (colour, move, comment)
                               move is a pair (row, col), or None for a pass
      player_scores         -- map player code -> string or None
      engine_names          -- map player code -> string
      engine_descriptions   -- map player code -> string

   player_scores values are the response to the final_score GTP command (if the
   player was asked).

   Methods which communicate with engines may raise BadGtpResponse if the
   engine returns a failure response.

   Methods which communicate with engines will normally raise GtpChannelError
   if there is trouble communicating with the engine. But after the game result
   has been decided, they will set these errors aside; retrieve them with
   describe_late_errors().

   This enforces a simple ko rule, but no superko rule. It accepts self-capture
   moves.

   """
    def __init__(self, board_size, komi=0.0, move_limit=1000):
        self.players = {'b': 'b', 'w': 'w'}
        self.game_id = None
        self.controllers = {}
        self.claim_allowed = {'b': False, 'w': False}
        self.after_move_callback = None
        self.board_size = board_size
        self.komi = komi
        self.move_limit = move_limit
        self.allowed_scorers = []
        self.internal_scorer = False
        self.handicap_compensation = "no"
        self.handicap = 0
        self.first_player = "b"
        self.engine_names = {}
        self.engine_descriptions = {}
        self.moves = []
        self.player_scores = {'b': None, 'w': None}
        self.additional_sgf_props = []
        self.late_errors = []
        self.handicap_stones = None
        self.result = None
        self.board = boards.Board(board_size)
        self.simple_ko_point = None

    ## Configuration methods (callable before set_player_...)

    def set_player_code(self, colour, player_code):
        """Specify a player code.

        player_code -- short ascii string

        The player codes are used to identify the players in game results, sgf
        files, and the error messages.

        Setting these is optional but strongly encouraged. If not explicitly
        set, they will just be 'b' and 'w'.

        Raises ValueError if both players are given the same code.

        """
        s = str(player_code)
        if self.players[opponent_of(colour)] == s:
            raise ValueError("player codes must be distinct")
        self.players[colour] = s

    def set_game_id(self, game_id):
        """Specify a game id.

        game_id -- string

        The game id is reported in the game result, and used as a default game
        name in the SGF file.

        If you don't set it, it will have value None.

        """
        self.game_id = str(game_id)

    def use_internal_scorer(self, handicap_compensation='no'):
        """Set the scoring method to internal.

        The internal scorer uses area score, assuming all stones alive.

        handicap_compensation -- 'no' (default), 'short', or 'full'.

        If handicap_compensation is 'full', one point is deducted from Black's
        score for each handicap stone; if handicap_compensation is 'short', one
        point is deducted from Black's score for each handicap stone except the
        first. (The number of handicap stones is taken from the parameter to
        set_handicap().)

        """
        self.internal_scorer = True
        if handicap_compensation not in ('no', 'short', 'full'):
            raise ValueError("bad handicap_compensation value: %s" %
                             handicap_compensation)
        self.handicap_compensation = handicap_compensation

    def allow_scorer(self, colour):
        """Allow the specified player to score the game.

        If this is called for both colours, both are asked to score.

        """
        self.allowed_scorers.append(colour)

    def set_claim_allowed(self, colour, b=True):
        """Allow the specified player to claim a win.

        This will have no effect if the engine doesn't implement
        gomill-genmove_ex.

        """
        self.claim_allowed[colour] = bool(b)

    def set_move_callback(self, fn):
        """Specify a callback function to be called after every move.

        This function is called after each move is played, including passes but
        not resignations, and not moves which triggered a forfeit.

        It is passed three parameters: colour, move, board
          move is a pair (row, col), or None for a pass

        Treat the board parameter as read-only.

        Exceptions raised from the callback will be propagated unchanged out of
        run().

        """
        self.after_move_callback = fn

    ## Channel methods

    def set_player_controller(self,
                              colour,
                              controller,
                              check_protocol_version=True):
        """Specify a player using a Gtp_controller.

        controller             -- Gtp_controller
        check_protocol_version -- bool (default True)

        By convention, the channel name should be 'player <player code>'.

        If check_protocol_version is true, rejects an engine that declares a
        GTP protocol version <> 2.

        Propagates GtpChannelError if there's an error checking the protocol
        version.

        """
        self.controllers[colour] = controller
        if check_protocol_version:
            controller.check_protocol_version()

    def set_player_subprocess(self,
                              colour,
                              command,
                              check_protocol_version=True,
                              **kwargs):
        """Specify the a player as a subprocess.

        command                -- list of strings (as for subprocess.Popen)
        check_protocol_version -- bool (default True)

        Additional keyword arguments are passed to the Subprocess_gtp_channel
        constructor.

        If check_protocol_version is true, rejects an engine that declares a
        GTP protocol version <> 2.

        Propagates GtpChannelError if there's an error creating the
        subprocess or checking the protocol version.

        """
        try:
            channel = gtp_controller.Subprocess_gtp_channel(command, **kwargs)
        except GtpChannelError, e:
            raise GtpChannelError(
                "error starting subprocess for player %s:\n%s" %
                (self.players[colour], e))
        controller = gtp_controller.Gtp_controller(
            channel, "player %s" % self.players[colour])
        self.set_player_controller(colour, controller, check_protocol_version)
示例#8
0
def main():
    ##    usage = "%prog [options] --black='<command>' --white='<command>'"
    ##    parser = OptionParser(usage=usage)
    ##    parser.add_option("--black", help=SUPPRESS_HELP)
    ##    parser.add_option("--white", help=SUPPRESS_HELP)
    ##    parser.add_option("--komi", type="float", default=7.5)
    ##    parser.add_option("--size", type="int", default=19)
    ##    parser.add_option("--games", type="int", default=1, metavar="COUNT")
    ##    parser.add_option("--verbose", type="choice", choices=('0','1','2'),
    ##                      default=0, metavar="0|1|2")
    ##    parser.add_option("--sgfbase", type="string", metavar="FILENAME-PREFIX")
    ##
    ##    (options, args) = parser.parse_args()
    ##    if args:
    ##        parser.error("too many arguments")
    ##    if not options.black or not options.white:
    ##        parser.error("players not specified")
    ##
    ##    black_command = shlex.split(options.black)
    ##    white_command = shlex.split(options.white)
    ##    b_code = black_command[0]
    ##    w_code = white_command[0]
    ##    if b_code == w_code:
    ##        b_code += '-b'
    ##        w_code += '-w'
    #these used to be in an options object
    verbose = '2'
    size = 13
    komi = 5.5

    b_code = 'b'
    w_code = 'w'
    white_command = 'gnugo\gnugo --mode gtp --silent --color white'

    #video_source = 'tests/go game.mp4'
    video_source = 0

    game_controller = gtp_controller.Game_controller(b_code, w_code)
    try:
        player = video_player.VideoPlayer("camera_params.npz",
                                          video_source,
                                          size,
                                          "Gowatcher",
                                          rotate_pic=True,
                                          debug=True)
        engine = video_player.make_engine(player)
        channel = gtp_controller.Internal_gtp_channel(engine)
        black_controller = gtp_controller.Gtp_controller(channel, 'b')
        game_controller.set_player_controller('b', black_controller)
        game_controller.set_player_subprocess('w', white_command)
    except GtpChannelError as e:
        game_controller.close_players()
        sys.exit("error creating players:\n%s\n" % e)

    eds = game_controller.engine_descriptions
    if verbose == '1':
        print('Black: %s' % eds['b'].get_short_description())
        print('White: %s' % eds['w'].get_short_description())
    elif verbose == '2':
        print('Black: %s\n' % eds['b'].get_long_description())
        print('White: %s\n' % eds['w'].get_long_description())

    game = gtp_games.Gtp_game(game_controller,
                              board_size=size,
                              komi=komi,
                              move_limit=1000)

    if verbose == '1':
        game.set_move_callback(print_move)
    elif verbose == '2':
        game.set_move_callback(print_board)
    game.allow_scorer('b')
    game.allow_scorer('w')

    try:
        game.prepare()
        game.run()
    except (GtpChannelError, BadGtpResponse) as e:
        game_controller.close_players()
        sys.exit("aborting run due to error:\n%s\n" % e)
    print(game.result.describe())

    ##        if options.sgfbase is not None:
    ##            sgf_game = game.make_sgf()
    ##            sgf_game.get_root().set("AP", ("Gomill twogtp", __version__))
    ##            try:
    ##                write_sgf(sgf_game, game_number, options.sgfbase)
    ##            except EnvironmentError, e:
    ##                sys.exit("error writing SGF file: %s" % e)

    game_controller.close_players()
    late_error_messages = game_controller.describe_late_errors()
    if late_error_messages:
        sys.exit(late_error_messages)
示例#9
0
        command -- list of strings (as for subprocess.Popen)

        Additional keyword arguments are passed to the Subprocess_gtp_channel
        constructor.

        Raises BackEndError if it can't communicate with the back end.

        """
        try:
            channel = gtp_controller.Subprocess_gtp_channel(command, **kwargs)
        except GtpChannelError, e:
            # Probably means exec failure
            raise BackEndError("can't launch back end command\n%s" % e,
                               cause=e)
        controller = gtp_controller.Gtp_controller(channel, "back end")
        self.set_back_end_controller(controller)

    def close(self):
        """Close the channel to the back end.

        It's safe to call this at any time after set_back_end_... (including
        after receiving a BackEndError).

        It's not strictly necessary to call this if you're going to exit from
        the parent process anyway, as that will naturally close the command
        channel. But some engines don't behave well if you don't send 'quit',
        so it's safest to close the proxy explicitly.

        This will send 'quit' if low-level errors have not previously been seen
        on the channel, unless expect_back_end_exit() has been called.