Client Usage

The battleship_mp.client library provides a client API in the form of a single class GameSession.

Usage Outline

Code to use the client only needs to import the GameSession.

from battleship_mp.client import GameSession

Since a GameSession also represents remote state, it cannot be directly instantiated without knowing the server/client protocol. Instead, use connect() or start() to create instances.

with GameSession.connect() as session:
    # session is a connected GameSession instance
    # ready for one round of the game
    ...

Afterwards, the session is ready to exchange ship placement with the opponent’s client via place_ships(). Ship placements express size, position and orientation of each ship; see the method doc for details.

my_ships = [
    (5, (2, 1), True),
    (3, (4, 5), False),
]
enemy_ships = session.place_ships(*my_ships)

The main game phase consists of multiple rounds of exchanging shots. Each player can either announce_shot() or expect_shot() and a player can only announce a new shot after the previous one was expected by the remote player. This allows for two styles of exchanging shots: simultaneous or alternating.

simultaneous shots

Both players announce their shot before expecting the enemy shot. This gives a play style as if players made their move at the same time.

# exchange shots with the opponent
my_shot = (..., ...)
session.announce_shot(my_shot)
enemy_shot = session.expect_shot()
# process both shots now
alternating shots

Only one of the players announces their shot and the other expects it. This gives a play style of players taking separate turns.

if my_move:
    my_shot = (..., ...)
    session.announce_shot(my_shot)
    # process my shot now
else:
    enemy_shot = session.expect_shot()
    # process enemy shot now

How players swap turns is not enforced. For example, players could swap turns after each shot or after each miss.

Once the exchange of shots has lead to the ships of one or both players being sunk, peers should end_game() and announce the determined winner. This may be one of the players’ identifiers or None in case of a draw. The server validates whether there is a consensus of who won the game. In addition, a player may end_game() and forfeit the game at any time.

Note that the client API – just like the server - generally imposes little restrictions on the game rules. If a restriction is not mentioned then the API does not impose it; for example, there is no restriction on the board size. Each client should check for itself whether all peers adhere to expected rules.

Class Definition

class battleship_mp.client.GameSession(opponent: str, first: bool, connection: Connection)[source]

Handle to an open game session at the server

As the opponent and first attributes represent the remote player they usually cannot be determined locally to create instances. Use either of connect() or start() to create instances for which these attributes are fetched from the server.

Since a session represents game state, it must be used in correct order. At least the order place_ships() -> announce_shot() | expect_shot() -> … -> announce_shot() | expect_shot() -> end_game() is enforced. Further restrictions may be applied by the peer depending on its game rules.

opponent

humanreadable identifier of the opponent

first

whether this player shoots first

classmethod connect(local_name: None | str) Generator[GameSession, None, None][source]

Create a new session with a new connection to the server

This creates a new instance based on server information. The underlying websocket is managed automatically.

classmethod start(local_name: None | str, connection: Connection) GameSession[source]

Create a new session on an established connection to the server

This creates a new instance based on server information. The underlying websocket must be managed manually.

place_ships(*ships: Tuple[int, Tuple[int, int], bool]) tuple[SHIP_PLACEMENT, ...][source]

Exchange placement of all the players’ ships

Parameters:

ships – placement of the local player’s ships

Returns:

placement of the remote player’s ships

Each placement is of the form (size, (y, x), vertical). For example, (4, (0, 2), False) encodes a) a Destroyer of size 4 b) at the top three cells to the right c) oriented to the right.

announce_shot(coord: tuple[int, int]) None[source]

Announce that a shot has been fired

expect_shot() tuple[int, int][source]

Wait for a shot to be fired and return its coordinates

end_game(winner: str | None, forfeit: bool = False) str | None[source]

End the game, announcing a winner or forfeit

Returns the winner determined by the peer. If a game is forfeit, the peer is the winner. A draw corresponds to winner=None.