コード例 #1
0
def resign(user_id: int, game_id: int) -> None:
    """Ends the game due to one player's resignation"""
    game = Game.get(game_id)

    # If there is no moves in the game, just cancel it.
    if not game.raw_moves:
        end_game.delay(game_id, '-', 'Game canceled.', update_stats=False)
        return

    user_white = user_id == game.white_user.id

    result: str
    reason: str
    if user_white:
        result = "0-1"
        reason = "White resigned. Black won."
    else:
        result = "1-0"
        reason = "Black resigned. White won."

    end_game.delay(game_id, result, reason)
コード例 #2
0
def get_rating_changes(game_id: int) -> Dict[str, RatingChange]:
    '''Returns rating changes for game in dict.
        Example: {"w": RatingChange, "b": RatingChange}'''
    game = Game.get(game_id)
    r_white = game.white_user.rating
    r_black = game.black_user.rating

    R_white = 10**(r_white / 400)
    R_black = 10**(r_black / 400)

    R_sum = R_white + R_black

    E_white = R_white / R_sum
    E_black = R_black / R_sum

    k_factor_white = game.white_user.k_factor
    k_factor_black = game.black_user.k_factor

    rating_change_white = RatingChange.from_formula(k_factor_white, E_white)
    rating_change_black = RatingChange.from_formula(k_factor_black, E_black)

    return {"w": rating_change_white, "b": rating_change_black}
コード例 #3
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,
        )
コード例 #4
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)
コード例 #5
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
コード例 #6
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)
コード例 #7
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,
            )
コード例 #8
0
    def test_total_time_and_black_clock_and_white_clock(self):
        game = Game()
        game.save()
        self.used_game_ids.append(game.id)

        total_time = timedelta(seconds=30, microseconds=13231)
        game.total_time = total_time
        black_clock = timedelta(seconds=310, microseconds=321312)
        game.black_clock = black_clock
        white_clock = timedelta(seconds=312, microseconds=31231)
        game.white_clock = white_clock

        game.save()
        game.refresh()

        self.assertEqual(game.total_time, total_time)
        self.assertEqual(game.black_clock, black_clock)
        self.assertEqual(game.white_clock, white_clock)