示例#1
0
    def post(self, game_id, player_id):
        """
            Input:
                {
                "column" : 2
                }
            Output:
                {
                "move": "{gameId}/moves/{move_number}"
                }
            Status codes:
                • 200 - OK. On success
                • 400 - Malformed input. Illegal move
                • 404 - Game not found or player is not a part of it.
                • 409 - Player tried to post when it’s not their turn.
                • 410 - Game is already in DONE state. (additional requirement, noticed while testing)
        """
        args = moves_post_parser.parse_args()
        request_column = args['column']

        # get the game object
        try:
            g = GameModel.objects(id=game_id).get()
        except (DoesNotExist, ValidationError):
            abort(404, message=f"Game {game_id} not found.")

        # check if player is part of the game
        try:
            p = g.players.get(name=player_id)
        except DoesNotExist:
            abort(
                404,
                message=f"Player {player_id} does not belong to game {game_id}."
            )

        # check if game is already DONE
        if g.state == 'DONE':
            abort(410, message=f"The game is already DONE.")

        # check if it's player's turn
        if g.current_token != p.token:
            abort(
                409,
                message=
                f"Player {player_id} tried to post when it’s not their turn.")

        # build GameBoard
        gb = GameBoard(g.num_cols, g.num_rows)
        players_to_token_map = {p.name: p.token for p in g.players}
        moves = [{
            'token': players_to_token_map[m.player_name],
            'column': m.column
        } for m in g.moves if m.move_type == 'MOVE']
        gb.apply_moves(moves)

        # test if next move is legal
        if not gb.can_drop(request_column):
            abort(
                400,
                message=
                f"Illegal move. Unable to drop token in column {request_column}"
            )

        # apply move and check winning condition
        row = gb.drop_token(request_column, p.token)

        if gb.check_win(request_column, row):
            g.state = 'DONE'
            g.winner = p.name
        # can also be done if board is full
        elif g.moves.count() + 1 == g.num_cols * g.num_rows:
            g.state = 'DONE'
            g.winner = None

        move_number = len(moves) + 1
        g.moves.create(turn=move_number,
                       move_type='MOVE',
                       player_name=p.name,
                       column=request_column)

        token_list = list(players_to_token_map.values())
        g.current_token = get_next_token(token_list, p.token)

        # TODO: possibly use conditional save here, to make sure we're updating the latest tamestamp seen
        g.save()

        # success
        return {
            "move": f"{game_id}/moves/{move_number}"
        }  # TODO: use @marshal_with, fields.Url('endpoint_resource')
示例#2
0
def test_cannot_drop_token_into_nonexisting_column():
    nc, nr = 1, 3
    game = GameBoard(nc, nr)
    assert not game.can_drop(0)
示例#3
0
def test_can_drop_token_into_almost_full_column():
    nc, nr = 1, 3
    game = GameBoard(nc, nr)
    game.board[0] = [1, 1, None]
    assert game.can_drop(1)
示例#4
0
def test_cannot_drop_token_into_full_column():
    nc, nr = 1, 3
    game = GameBoard(nc, nr)
    game.board[0] = [1, 1, 1]
    assert not game.can_drop(1)
示例#5
0
def test_can_drop_token_into_empty_column():
    nc, nr = 1, 3
    game = GameBoard(nc, nr)
    assert game.can_drop(1)