Ejemplo n.º 1
0
def start_game(game_id: int) -> None:
    '''Marks game as started, sends game info for players,
    emits first_move_waiting signal to white player'''
    game = Game.get(game_id)
    with rom.util.EntityLock(game, 10, 10):
        game.is_started = 1

        eta = datetime.utcnow() + timedelta(seconds=FIRST_MOVE_TIME_OUT)
        task = on_first_move_timed_out.apply_async(
            args=(game_id, ),
            eta=eta,
        )

        game.first_move_timed_out_task_id = task.id
        game.first_move_timed_out_task_eta = eta

        game.save()

    send_game_info.delay(game_id, game.white_user.sid, True)
    send_game_info.delay(game_id, game.black_user.sid, True)
    sio.emit(
        'first_move_waiting',
        {'wait_time': FIRST_MOVE_TIME_OUT},
        room=game.white_user.sid,
    )
Ejemplo n.º 2
0
def search_game(user_id: int, seconds: int) -> None:
    '''If there is appropriate game request, it starts a new game.
       Else it makes the game request and adds it to the database.'''
    user = User.get(user_id)
    with rom.util.EntityLock(user, 10, 10):
        game_requests = \
                rom.query.Query(GameRequest).filter(time=seconds).all()

        added_to_existed = False
        if game_requests:
            accepted_request = \
                min(game_requests,
                    key=lambda x: abs(User.get(x.user_id).rating - user.rating))
            if abs(user.rating -
                   User.get(accepted_request.user_id).rating) <= 200:
                added_to_existed = True

                accepted_request.delete()
                user_to_play_with = User.get(accepted_request.user_id)

                game = Game(
                    white_user=user,
                    black_user=user_to_play_with,
                    white_rating=user.rating,
                    black_rating=user_to_play_with.rating,
                    is_started=0,
                )
                tdelta = timedelta(seconds=seconds)
                game.total_clock = tdelta
                game.white_clock = tdelta
                game.black_clock = tdelta
                game.save()

                user.cur_game_id = game.id
                user.save()

                with rom.util.EntityLock(user_to_play_with, 10, 10):
                    user_to_play_with.cur_game_id = game.id
                    user_to_play_with.in_search = False
                    user_to_play_with.save()

                sio.emit(
                    'redirect',
                    {'url': f'/game/{game.id}'},
                    room=user.sid,
                )
                sio.emit(
                    'redirect',
                    {'url': f'/game/{game.id}'},
                    room=user_to_play_with.sid,
                )
                start_game.delay(game.id)

        if added_to_existed is False:
            user.in_search = True
            user.save()

            game_request = GameRequest(time=seconds, user_id=user_id)
            game_request.save()
Ejemplo n.º 3
0
def send_game_info(game_id: int, room_id: int, is_player: bool):
    request_datetime = datetime.utcnow()
    game = Game.get(game_id)

    data = {
        "black_user": {
            "nickname": game.black_user.login,
            "rating": game.black_rating
        },
        "white_user": {
            "nickname": game.white_user.login,
            "rating": game.white_rating
        },
        "moves": game.raw_moves,
        "is_player": is_player,
    }

    if is_player:
        data['color'] = 'w' if game.white_user.sid == room_id else 'b'

    if not game.is_finished:
        black_clock = game.black_clock
        white_clock = game.white_clock
        if game.raw_moves:
            next_to_move = game.get_next_to_move()
            if next_to_move == chess.WHITE:
                white_clock -= request_datetime - game.last_move_datetime
            else:
                black_clock -= request_datetime - game.last_move_datetime

        data["black_clock"] = int(black_clock.total_seconds())
        data["white_clock"] = int(white_clock.total_seconds())
        if is_player:
            rating_changes = get_rating_changes(game_id)
            if game.draw_offer_sender is None and game.get_moves_cnt() != 0:
                data["can_send_draw_offer"] = True
            else:
                data["can_send_draw_offer"] = False
            data['rating_changes'] = rating_changes[data['color']].to_dict()
    else:
        data["result"] = game.result

    sio.emit('game_started', data, room=room_id)
Ejemplo n.º 4
0
def on_disconnect(user_id: int, game_id: int) -> None:
    '''Schedules on_disconnect_timed_out_task, adds it to database.
       Emits 'opp_disconnected' to the opponent'''

    eta = datetime.utcnow() + timedelta(seconds=DISCONNECT_TIME_OUT)
    task = on_disconnect_timed_out.apply_async(
        args=(user_id, game_id),
        eta=eta,
    )

    game = Game.get(game_id)

    if game.draw_offer_sender:
        #  We aren't checking user_id != draw_offer_sender
        #  It'll be checked in decline_draw_offer func
        decline_draw_offer.delay(user_id, game_id)

    is_user_white = user_id == game.white_user.id

    opp_sid: Optional[int]
    with rom.util.EntityLock(game, 10, 10):
        if is_user_white:
            game.white_disconnect_timed_out_task_id = task.id
            game.white_disconnect_timed_out_task_eta = eta
            opp_sid = game.black_user.sid
        else:
            game.black_disconnect_timed_out_task_id = task.id
            game.black_disconnect_timed_out_task_eta = eta
            opp_sid = game.white_user.sid

        game.save()

    if opp_sid:
        sio.emit(
            'opp_disconnected',
            {'wait_time': DISCONNECT_TIME_OUT},
            room=opp_sid,
        )
Ejemplo n.º 5
0
def make_draw_offer(user_id: int, game_id: int):
    '''Makes draw offer, if it's possible.'''
    game = Game.get(game_id)

    with rom.util.EntityLock(game, 10, 10):
        if game.get_moves_cnt() == 0:
            #  Do not make draw offer, if game isn't started.
            return
        if game.draw_offer_sender and game.draw_offer_sender != user_id:
            #  Accept draw offer, if it's already exist
            accept_draw_offer.delay(user_id, game_id)
        elif game.draw_offer_sender:
            return

        game.draw_offer_sender = user_id
        game.save()

    opp_sid: str
    if user_id == game.white_user.id:
        opp_sid = game.black_user.sid
    else:
        opp_sid = game.white_user.sid

    sio.emit('draw_offer', room=opp_sid)
Ejemplo n.º 6
0
def make_move(user_id: int, game_id: int, move_san: str) -> None:
    '''Updates game state by user's move.
       Calls end_game(...) if the game is ended.'''

    request_datetime = datetime.utcnow()

    game = Game.get(game_id)

    if game.is_finished or\
            user_id not in (game.white_user.id, game.black_user.id):
        return

    board = game.get_board()
    is_user_white = user_id == game.white_user.id

    if (is_user_white and board.turn == chess.BLACK) or\
            (not is_user_white and board.turn == chess.WHITE):
        return

    try:
        with rom.util.EntityLock(game, 10, 10):
            board.push_san(move_san)
            game.append_move(move_san)

            if game.first_move_timed_out_task_id:
                revoke(game.first_move_timed_out_task_id)
                game.first_move_timed_out_task_id = None

            if is_user_white:
                revoke(game.white_time_is_up_task_id)
            else:
                revoke(game.black_time_is_up_task_id)

            if game.draw_offer_sender and game.draw_offer_sender != user_id:
                # Decline draw offer only if it was asked by the opp
                # This call is waiting because of an entity lock
                decline_draw_offer.delay(user_id, game_id)
                game.draw_offer_sender = None

            if is_user_white:
                if game.get_moves_cnt() != 1:
                    game.white_clock -=\
                        request_datetime - game.last_move_datetime

                eta = datetime.utcnow() + game.black_clock

                task = on_time_is_up.apply_async(
                    args=(game.black_user.id, game_id),
                    eta=eta,
                )

                game.black_time_is_up_task_id = task.id
                game.black_time_is_up_task_eta = eta
            else:
                if game.get_moves_cnt() != 1:
                    game.black_clock -=\
                            request_datetime - game.last_move_datetime

                eta = datetime.utcnow() + game.white_clock

                task = on_time_is_up.apply_async(
                    args=(game.white_user.id, game_id),
                    eta=eta,
                )

                game.white_time_is_up_task_id = task.id
                game.white_time_is_up_task_eta = eta

            game.last_move_datetime = request_datetime

            if board.fullmove_number == 1:  # and board.turn == chess.BLACK
                sio.emit('first_move_waiting',
                         {'wait_time': FIRST_MOVE_TIME_OUT},
                         room=game.black_user.sid)

                eta = \
                    datetime.utcnow() + timedelta(seconds=FIRST_MOVE_TIME_OUT)
                task = on_first_move_timed_out.\
                    apply_async((game_id, ), eta=eta)

                game.first_move_timed_out_task_id = task.id
                game.first_move_timed_out_task_eta = eta

            game.save()

        data = {
            'san': move_san,
            'black_clock': int(game.black_clock.total_seconds()),
            'white_clock': int(game.white_clock.total_seconds())
        }

        sio.emit('game_updated', data, room=game_id)
        # DO NOT REMOVE NEXT STRING. SIO CAN'T EMIT TO SPECTATORS WITHOUT
        # THIS :/
        data = data
        sio.emit('game_updated', data, room=game.black_user.sid)
        sio.emit('game_updated', data, room=game.white_user.sid)

        result = board.result()
        if result != '*':
            reason: str
            if result == '1/2-1/2':
                reason = "Draw"
            elif result == '1-0':
                reason = "Checkmate. White won."
            else:
                reason = "Checkmate. Black won."

            end_game.delay(game_id, result, reason)
    except ValueError:
        pass
Ejemplo n.º 7
0
def end_game(game_id: int,
             result: str,
             reason: str,
             update_stats=True) -> None:
    '''Marks game as finished, emits 'game_ended' signal to users,
     closes the room,
     recalculates ratings and k-factors if update_stats is True'''

    game = Game.get(game_id)

    if game.is_finished:
        return

    if game.first_move_timed_out_task_id:
        revoke(game.first_move_timed_out_task_id)

    if game.white_disconnect_timed_out_task_id:
        revoke(game.white_disconnect_timed_out_task_id)

    if game.black_disconnect_timed_out_task_id:
        revoke(game.black_disconnect_timed_out_task_id)

    if game.white_time_is_up_task_id:
        revoke(game.white_time_is_up_task_id)

    if game.black_time_is_up_task_id:
        revoke(game.black_time_is_up_task_id)

    data = {'result': result}
    sio.emit('game_ended', data, room=game_id)  # Emit to spectators

    data['reason'] = reason
    sio.emit('game_ended', data, room=game.white_user.sid)
    sio.emit('game_ended', data, room=game.black_user.sid)

    with rom.util.EntityLock(game, 10, 10):
        game.is_finished = 1
        game.result = result
        with rom.util.EntityLock(game.white_user, 10, 10):
            game.white_user.cur_game_id = None
            game.white_user.save()

        with rom.util.EntityLock(game.black_user, 10, 10):
            game.black_user.cur_game_id = None
            game.black_user.save()

        game.save()

    if update_stats is False:
        return

    rating_changes = get_rating_changes(game_id)

    with rom.util.EntityLock(game.white_user, 10, 10):
        game.white_user.games_played += 1
        game.white_user.save()

    with rom.util.EntityLock(game.black_user, 10, 10):
        game.black_user.games_played += 1
        game.black_user.save()

    if result == "1-0":
        update_rating.delay(game.white_user.id, rating_changes["w"].win)
        update_rating.delay(game.black_user.id, rating_changes["b"].lose)
    elif result == "1/2-1/2":
        update_rating.delay(game.white_user.id, rating_changes["w"].draw)
        update_rating.delay(game.black_user.id, rating_changes["b"].draw)
    elif result == "0-1":
        update_rating.delay(game.white_user.id, rating_changes["w"].lose)
        update_rating.delay(game.black_user.id, rating_changes["b"].win)

    update_k_factor.delay(game.white_user.id)
    update_k_factor.delay(game.black_user.id)
Ejemplo n.º 8
0
def reconnect(user_id: int, game_id: int) -> None:
    '''Sends game info to reconnected player
    Emits 'opp_reconnected' to the opponent.'''

    game = Game.get(game_id)

    next_to_move = game.get_next_to_move()

    is_user_white = user_id == game.white_user.id

    if is_user_white:
        # Not ".delay()", because of bad emition order
        send_game_info(game_id, game.white_user.sid, True)
        if game.white_disconnect_timed_out_task_id:
            revoke(game.white_disconnect_timed_out_task_id)
            with rom.util.EntityLock(game, 10, 10):
                game.white_disconnect_timed_out_task_id = None
                game.save()

        sio.emit('opp_reconnected', room=game.black_user.sid)

    else:
        send_game_info.delay(game_id, game.black_user.sid, True)

        if game.black_disconnect_timed_out_task_id:
            revoke(game.black_disconnect_timed_out_task_id)
            with rom.util.EntityLock(game, 10, 10):
                game.black_disconnect_timed_out_task_id = None
                game.save()

        sio.emit('opp_reconnected', room=game.white_user.sid)

        if is_user_white:
            if game.first_move_timed_out_task_id and\
                    next_to_move == chess.WHITE:
                wait_time = (game.first_move_timed_out_task_eta -
                             datetime.utcnow()).seconds
                sio.emit(
                    'first_move_waiting',
                    {'wait_time': wait_time},
                    room=game.white_user.sid,
                )
            elif game.first_move_timed_out_task_id and\
                    next_to_move == chess.BLACK:
                wait_time = (game.first_move_timed_out_task_eta -
                             datetime.utcnow()).seconds
                sio.emit(
                    'first_move_waiting',
                    {'wait_time': wait_time},
                    room=game.black_user.sid,
                )

        if is_user_white and game.black_disconnect_timed_out_task_id:
            wait_time = (game.black_disconnect_timed_out_task_eta -
                         datetime.utcnow()).seconds
            sio.emit(
                'opp_disconnected',
                {'wait_time': wait_time},
                room=game.white_user.sid,
            )
        elif not is_user_white and game.white_disconnect_timed_out_task_id:
            wait_time = (game.white_disconnect_timed_out_task_eta -
                         datetime.utcnow()).seconds
            sio.emit(
                'opp_disconnected',
                {'wait_time': wait_time},
                room=game.black_user.sid,
            )