Ejemplo n.º 1
0
 def display_state(game):
     comment("displaying game info:")
     comment(
         _RENDER(
             game,
             use_debugboard=use_debugboard,
             use_colour=use_colour,
             use_unicode=use_unicode,
         ),
         depth=1,
     )
Ejemplo n.º 2
0
    def __init__(self, name, player_loc, time_limit=None, space_limit=None):
        self.name = name

        # create some context managers for resource limiting
        self.timer = _CountdownTimer(time_limit, self.name)
        if space_limit is not None:
            space_limit *= NUM_PLAYERS
        self.space = _MemoryWatcher(space_limit)

        # import the Player class from given package
        player_pkg, player_cls = player_loc
        comment(f"importing {self.name}'s player class '{player_cls}' "
                f"from package '{player_pkg}'")
        self.Player = _load_player_class(player_pkg, player_cls)
Ejemplo n.º 3
0
 def action(self):
     comment(f"asking {self.name} for next action...")
     with self.space, self.timer:
         # ask the real player
         action = self.player.action()
     comment(f"{self.name} returned action: {action!r}", depth=1)
     comment(self.timer.status(), depth=1)
     comment(self.space.status(), depth=1)
     # give back the result
     return action
Ejemplo n.º 4
0
 def update(self, opponent_action, player_action):
     comment(f"updating {self.name} with actions...")
     with self.space, self.timer:
         # forward to the real player
         self.player.update(opponent_action, player_action)
     comment(self.timer.status(), depth=1)
     comment(self.space.status(), depth=1)
Ejemplo n.º 5
0
 def init(self, colour):
     self.colour = colour
     self.name += f" ({colour})"
     player_cls = str(self.Player).strip("<class >")
     comment(f"initialising {self.colour} player as a {player_cls}")
     with self.space, self.timer:
         # construct/initialise the player class
         self.player = self.Player(colour)
     comment(self.timer.status(), depth=1)
     comment(self.space.status(), depth=1)
Ejemplo n.º 6
0
def main():
    # Parse command-line options into a namespace for use throughout this
    # program
    options = get_options()

    # Create a star-log for controlling the format of output from within this
    # program
    config(level=options.verbosity, ansi=options.use_colour)
    comment("all messages printed by the referee after this begin with *")
    comment("(any other lines of output must be from your Player class).")
    comment()

    try:
        # Import player classes
        p1 = PlayerWrapper(
            "player 1",
            options.player1_loc,
            time_limit=options.time,
            space_limit=options.space,
        )
        p2 = PlayerWrapper(
            "player 2",
            options.player2_loc,
            time_limit=options.time,
            space_limit=options.space,
        )

        # We'll start measuring space usage from now, after all
        # library imports should be finished:
        set_space_line()

        # Play the game!
        result = play(
            [p1, p2],
            delay=options.delay,
            print_state=(options.verbosity > 1),
            use_debugboard=(options.verbosity > 2),
            use_colour=options.use_colour,
            use_unicode=options.use_unicode,
            log_filename=options.logfile,
        )
        # Display the final result of the game to the user.
        comment("game over!", depth=-1)
        print(result)

    # In case the game ends in an abnormal way, print a clean error
    # message for the user (rather than a trace).
    except KeyboardInterrupt:
        _print()  # (end the line)
        comment("bye!")
    except IllegalActionException as e:
        comment("game error!", depth=-1)
        print("error: invalid action!")
        comment(e)
    except ResourceLimitException as e:
        comment("game error!", depth=-1)
        print("error: resource limit exceeded!")
        comment(e)
Ejemplo n.º 7
0
 def wait():
     comment("(press enter to continue)", end="")
     input()
Ejemplo n.º 8
0
def play(
    players,
    delay=0,
    print_state=True,
    use_debugboard=False,
    use_colour=False,
    use_unicode=False,
    log_filename=None,
    out_function=comment,
):
    """
    Coordinate a game, return a string describing the result.

    Arguments:
    * players        -- A list of Player wrappers supporting init, action
                        and update methods.
    * delay          -- Time in seconds to wait between turns, or negative
                        to wait for user input.
    * print_state    -- If True, print a picture of the board after each
                        update.
    * use_debugboard -- If True, use a larger board during updates (if
                        print_state is also True).
    * use_colour     -- Use ANSI colour codes for output.
    * use_unicode    -- Use unicode symbols for output.
    * log_filename   -- If not None, log all game actions to this path.
    * out_function   -- Use this function (instead of default 'comment')
                        for all output messages.
    """
    # Configure behaviour of this function depending on parameters:
    if delay > 0:

        def wait():
            time.sleep(delay)

    elif delay < 0:

        def wait():
            comment("(press enter to continue)", end="")
            input()

    else:

        def wait():
            pass

    if print_state:

        def display_state(game):
            comment("displaying game info:")
            comment(
                _RENDER(
                    game,
                    use_debugboard=use_debugboard,
                    use_colour=use_colour,
                    use_unicode=use_unicode,
                ),
                depth=1,
            )

    else:

        def display_state(game):
            pass

    # Set up a new game and initialise the players (constructing the
    # Player classes including running their .__init__() methods).
    game = Game(log_filename=log_filename)
    comment("initialising players", depth=-1)
    for player, colour in zip(players, COLOURS):
        # NOTE: `player` here is actually a player wrapper. Your program
        # should still implement a method called `__init__()`, not one
        # called `init()`:
        player.init(colour)

    # Display the initial state of the game.
    comment("game start!", depth=-1)
    display_state(game)

    # Repeat the following until the game ends
    # SIMULTANEOUS PLAY VERSION:
    # all players choose an action, then the board and players get updates:
    turn = 1
    player_1, player_2 = players
    while not game.over():
        comment(f"Turn {turn}", depth=-1)

        # Ask both players for their next action (calling .action() methods)
        action_1 = player_1.action()
        action_2 = player_2.action()

        # Validate both actions and apply them to the game if they are
        # allowed. Display the resulting game state
        game.update(action_1, action_2)
        display_state(game)

        # Notify both players of the actions (via .update() methods)
        player_1.update(opponent_action=action_2, player_action=action_1)
        player_2.update(opponent_action=action_1, player_action=action_2)

        # Next turn!
        turn += 1
        wait()

    # After that loop, the game has ended (one way or another!)
    result = game.end()
    return result
Ejemplo n.º 9
0
def connect_and_play(
    player,
    name,
    channel,
    host,
    port,
    log_filename=None,
    out_function=None,
    print_state=True,
    use_debugboard=False,
    use_colour=False,
    use_unicode=False,
):
    """
    Connect to and coordinate a game with a server, return a string describing
    the result.

    Parameters:
    * player         -- Your player's wrapped object (supporting 'init',
                        'update' and 'action' methods).
    * name           -- Your player's name on the server
    * channel        -- The matchmaking channel string
    * host           -- The server address
    * port           -- The server port
    * log_filename   -- If not None, log all game actions to this path.
    * print_state    -- If True, print a picture of the board after each
                        update.
    * use_debugboard -- If True, use a larger board during updates (if
                        print_state is also True).
    * use_colour     -- Use ANSI colour codes for output.
    * use_unicode    -- Use unicode symbols for output.
    """
    # Configure behaviour of this function depending on parameters:
    if print_state:

        def display_state(players_str, game):
            comment("displaying game info:")
            comment(
                _RENDER(
                    game,
                    message=players_str,
                    use_debugboard=use_debugboard,
                    use_colour=use_colour,
                    use_unicode=use_unicode,
                ),
                depth=1,
            )

    else:

        def display_state(players_str, game):
            pass

    # Set up a connection with the server
    comment("connecting to battleground", depth=-1)
    comment("attempting to connect to the server...")
    server = Server.from_address(host, port)
    comment("connection established!")

    # Wait for some matching players
    comment("looking for a game", depth=-1)
    channel_str = f"channel '{channel}'" if channel else "open channel"
    comment(f"submitting game request as '{name}' in {channel_str}...")
    server.send(M.PLAY, name=name, channel=channel)
    server.recv(M.OKAY)
    comment("game request submitted.")
    comment(f"waiting for opponents in {channel_str}...")
    comment("(press ^C to stop waiting)")
    # (wait through some OKAY-OKAY msg exchanges until a GAME message comes---
    # the server is asking if we are still here waiting, or have disconnected)
    gamemsg = server.recv(M.OKAY | M.GAME)
    while gamemsg["mtype"] is not M.GAME:
        server.send(M.OKAY)
        gamemsg = server.recv(M.OKAY | M.GAME)
    # when we get a game message, it's time to play!
    comment("setting up game", depth=-1, clear=True)
    comment("opponents found!")
    for colour in COLOURS:
        comment(f"{colour} player:", gamemsg[colour])

    # Initialise the player
    comment("initialising player", depth=-1)
    comment("waiting for colour assignment...")
    initmsg = server.recv(M.INIT)
    comment("playing as", initmsg["colour"], depth=1)
    comment("initialising your player class...")
    player.init(initmsg["colour"])
    comment("ready to play!")
    server.send(M.OKAY)

    # Set up a new game and display the initial state and players
    comment("game start", depth=-1)
    players_str = format_players_str(gamemsg, player.colour)
    game = Game(log_filename)
    display_state(players_str, game)

    # Now wait for messages from the sever and respond accordingly
    while True:
        msg = server.recv(M.TURN | M.UPD8 | M.OVER | M.ERRO)
        if msg["mtype"] is M.TURN:
            # TODO: For simultaneous play, there's no need to display the
            # state again at the start of the turn...
            # comment("your turn!", depth=-1, clear=True)
            # display_state(players_str, game)
            # decide on action and submit it to server
            action = player.action()
            server.send(M.ACTN, action=action)

        elif msg["mtype"] is M.UPD8:
            player_action = msg["player_action"]
            opponent_action = msg["opponent_action"]
            comment("receiving update", depth=-1, clear=True)
            if player.colour == "upper":
                game.update(
                    upper_action=player_action,
                    lower_action=opponent_action,
                )
            else:
                game.update(
                    upper_action=opponent_action,
                    lower_action=player_action,
                )
            display_state(players_str, game)
            player.update(
                player_action=player_action,
                opponent_action=opponent_action,
            )
            # then notify server we are ready to continue:
            server.send(M.OKAY)

        elif msg["mtype"] is M.OVER:
            # the game ended!
            return msg["result"]

        elif msg["mtype"] is M.ERRO:
            # seems like the server encountered an error, but not
            # with our connection
            raise ServerEncounteredError(msg["reason"])
Ejemplo n.º 10
0
def main():
    # Parse command-line options into a namespace for use throughout this
    # program
    options = get_options()

    # Create a star-log for controlling the format of output from within this
    # program
    out = config(level=options.verbosity, ansi=options.use_colour)
    comment("all messages printed by the client after this begin with a *")
    comment("(any other lines of output must be from your Player class).")
    comment()

    try:
        # Import player classes
        player = PlayerWrapper(
            "your player",
            options.player_loc,
        )

        # Even though we're not limiting space, the display
        # may still be useful for some users
        set_space_line()

        # Play the game, catching any errors and displaying them to the
        # user:
        result = connect_and_play(
            player=player,
            name=options.name,
            channel=options.channel,
            host=options.host,
            port=options.port,
            log_filename=options.logfile,
            print_state=(options.verbosity > 1),
            use_debugboard=(options.verbosity > 2),
            use_colour=options.use_colour,
            use_unicode=options.use_unicode,
        )
        comment("game over!", depth=-1)
        print(result)
    except KeyboardInterrupt:
        _print()  # (end the line)
        comment("bye!")
    except ConnectingException as e:
        print("error connecting to server")
        comment(e)
    except DisconnectException as e:
        print("connection lost", depth=-1)
        comment(e)
    except ProtocolException as e:
        print("protocol error!", depth=-1)
        comment(e)
    except ServerEncounteredError as e:
        print("server encountered error!", depth=-1)
        comment(e)