Beispiel #1
0
def play_game(li, game_id, control_queue, engine_factory, user_profile,
              config):
    updates = li.get_game_stream(game_id).iter_lines()

    #Initial response of stream will be the full game info. Store it
    game = model.Game(json.loads(next(updates).decode('utf-8')),
                      user_profile["username"], li.baseUrl)
    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li, __version__)

    print("+++ {}".format(game))

    engine.pre_game(game)

    engine_cfg = config["engine"]

    if (engine_cfg["polyglot"] == True):
        board = play_first_book_move(game, engine, board, li, engine_cfg)
    else:
        board = play_first_move(game, engine, board, li)

    try:
        for binary_chunk in updates:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                board = update_board(board, moves[-1])
                if is_engine_move(game, moves):
                    best_move = None
                    if (engine_cfg["polyglot"] == True and len(moves) <=
                        (engine_cfg["polyglot_max_depth"] * 2) - 1):
                        best_move = get_book_move(board, engine_cfg)
                    if best_move == None:
                        best_move = engine.search(board, upd["wtime"],
                                                  upd["btime"], upd["winc"],
                                                  upd["binc"])
                    li.make_move(game.id, best_move)
                    game.abort_in(20)
            elif u_type == "ping":
                if game.should_abort_now() and len(game.state["moves"]) < 6:
                    print("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
    except (RemoteDisconnected, ConnectionError, ProtocolError,
            HTTPError) as exception:
        print("Abandoning game due to connection error")
        traceback.print_exception(type(exception), exception,
                                  exception.__traceback__)
    finally:
        print("--- {} Game over".format(game.url()))
        engine.quit()
        # This can raise queue.NoFull, but that should only happen if we're not processing
        # events fast enough and in this case I believe the exception should be raised
        control_queue.put_nowait({"type": "local_game_done"})
Beispiel #2
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config):
    updates = li.get_game_stream(game_id).iter_lines()

    #Initial response of stream will be the full game info. Store it
    game = model.Game(json.loads(next(updates).decode('utf-8')), user_profile["username"], li.baseUrl, config.get("abort_time", 20))
    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li, __version__)

    print("+++ {}".format(game))

    engine_cfg = config["engine"]
    polyglot_cfg = engine_cfg.get("polyglot", {})
    book_cfg = polyglot_cfg.get("book", {})

    if not polyglot_cfg.get("enabled") or not play_first_book_move(game, engine, board, li, book_cfg):
        play_first_move(game, engine, board, li)

    engine.set_time_control(game)

    try:
        for binary_chunk in updates:
            upd = json.loads(binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                board = update_board(board, moves[-1])
                if is_engine_move(game, moves):
                    if config.get("fake_think_time") and len(moves) > 9:
                        delay = min(game.clock_initial, game.my_remaining_seconds()) * 0.015
                        accel = 1 - max(0, min(100, len(moves) - 20)) / 150
                        sleep = min(5, delay * accel)
                        time.sleep(sleep)
                    best_move = None
                    if polyglot_cfg.get("enabled") and len(moves) <= polyglot_cfg.get("max_depth", 8) * 2 - 1:
                        best_move = get_book_move(board, book_cfg)
                    if best_move == None:
                        best_move = engine.search(board, upd["wtime"], upd["btime"], upd["winc"], upd["binc"])
                    li.make_move(game.id, best_move)
                    game.abort_in(config.get("abort_time", 20))
            elif u_type == "ping":
                if game.should_abort_now():
                    print("    Aborting {} by lack of activity".format(game.url()))
                    li.abort(game.id)
    except (RemoteDisconnected, ChunkedEncodingError, ConnectionError, ProtocolError, HTTPError) as exception:
        print("Abandoning game due to connection error")
        traceback.print_exception(type(exception), exception, exception.__traceback__)
    finally:
        print("--- {} Game over".format(game.url()))
        engine.quit()
        # This can raise queue.NoFull, but that should only happen if we're not processing
        # events fast enough and in this case I believe the exception should be raised
        control_queue.put_nowait({"type": "local_game_done"})
Beispiel #3
0
def play_game(li, game_id, control_queue, engine_factory):
    username = li.get_profile()["username"]
    updates = li.get_game_stream(game_id).iter_lines()

    #Initial response of stream will be the full game info. Store it
    game = model.Game(json.loads(next(updates).decode('utf-8')), username,
                      li.baseUrl)
    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li)

    print("+++ {}".format(game.show()))

    engine.pre_game(game)

    board = play_first_move(game, engine, board, li)

    try:
        for binary_chunk in updates:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd))
            elif u_type == "gameState":
                moves = upd.get("moves").split()
                board = update_board(board, moves[-1])

                if is_engine_move(game.is_white, moves):
                    best_move = engine.search(board, upd.get("wtime"),
                                              upd.get("btime"),
                                              upd.get("winc"), upd.get("binc"))
                    li.make_move(game.id, best_move)
    except (RemoteDisconnected, ConnectionError, ProtocolError,
            HTTPError) as exception:
        print("Abandoning game due to connection error")
        traceback.print_exception(type(exception), exception,
                                  exception.__traceback__)
    finally:
        print("--- {} Game over".format(game.url()))
        engine.quit()
        # This can raise queue.NoFull, but that should only happen if we're not processing
        # events fast enough and in this case I believe the exception should be raised
        control_queue.put_nowait({"type": "local_game_done"})
Beispiel #4
0
def play_game(li, game_id, engine_path, weights, threads, control_queue):
    username = li.get_profile()["username"]
    updates = li.get_game_stream(game_id).iter_lines()

    #Initial response of stream will be the full game info. Store it
    game = model.Game(json.loads(next(updates).decode('utf-8')), username,
                      li.baseUrl)
    board = setup_board(game.state)
    engine = setup_engine(engine_path, board, weights, threads)
    conversation = Conversation(game, engine, li)

    print("+++ {}".format(game.show()))

    engine.pre_game(game)

    board = play_first_move(game, engine, board, li)

    for binary_chunk in updates:
        upd = json.loads(
            binary_chunk.decode('utf-8')) if binary_chunk else None
        u_type = upd["type"] if upd else "ping"
        if u_type == "chatLine":
            conversation.react(ChatLine(upd))
        elif u_type == "gameState":
            moves = upd.get("moves").split()
            board = update_board(board, moves[-1])

            if is_engine_move(game.is_white, moves):
                best_move = engine.search(board, upd.get("wtime"),
                                          upd.get("btime"), upd.get("winc"),
                                          upd.get("binc"))
                li.make_move(game.id, best_move)
                if CONFIG.get("print_engine_stats"):
                    engine.print_stats()

    print("--- {} Game over".format(game.url()))
    engine.quit()
    # This can raise queue.NoFull, but that should only happen if we're not processing
    # events fast enough and in this case I believe the exception should be raised
    control_queue.put_nowait({"type": "local_game_done"})
Beispiel #5
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config,
              challenge_queue, correspondence_queue, logging_queue,
              logging_configurer, logging_level):
    logging_configurer(logging_queue, logging_level)
    logger = logging.getLogger(__name__)

    response = li.get_game_stream(game_id)
    lines = response.iter_lines()

    # Initial response of stream will be the full game info. Store it
    initial_state = json.loads(next(lines).decode('utf-8'))
    game = model.Game(initial_state, user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    board = setup_board(game)
    engine = engine_factory(board)
    engine.get_opponent_info(game)
    conversation = Conversation(game, engine, li, __version__, challenge_queue)

    logger.info("+++ {}".format(game))

    is_correspondence = game.perf_name == "Correspondence"
    correspondence_cfg = config.get("correspondence", {}) or {}
    correspondence_move_time = correspondence_cfg.get("move_time", 60) * 1000

    engine_cfg = config["engine"]
    is_usi = engine_cfg["protocol"] == "usi"
    is_usi_ponder = is_usi and engine_cfg.get("ponder", False)
    move_overhead = config.get("move_overhead", 1000)
    delay_seconds = config.get("rate_limiting_delay", 0) / 1000
    polyglot_cfg = engine_cfg.get("polyglot", {})
    book_cfg = polyglot_cfg.get("book", {})

    ponder_thread = None
    deferredFirstMove = False

    ponder_usi = None

    def ponder_thread_func(game, engine, board, btime, wtime, binc, winc, byo):
        global ponder_results
        best_move, ponder_move = engine.search_with_ponder(
            game, board, btime, wtime, binc, winc, byo, True)
        ponder_results[game.id] = (best_move, ponder_move)

    logger.debug("Game state: {}".format(game.state))

    if len(board.move_stack) < 2:
        while not terminated:
            try:
                if not play_first_move(game, engine, board, li):
                    deferredFirstMove = True
                break
            except (HTTPError) as exception:
                if exception.response.status_code == 400:  # fallthrough
                    break
    else:
        moves = game.state["moves"].split()
        if not is_game_over(game) and is_engine_move(game, moves):
            best_move = None
            ponder_move = None
            btime = game.state["btime"]
            wtime = game.state["wtime"]
            start_time = time.perf_counter_ns()

            if board.turn == shogi.BLACK:
                btime = max(
                    0, btime - move_overhead - int(
                        (time.perf_counter_ns() - start_time) / 1000000))
            else:
                wtime = max(
                    0, wtime - move_overhead - int(
                        (time.perf_counter_ns() - start_time) / 1000000))
            logger.info("Searching for btime {} wtime {}".format(btime, wtime))
            best_move, ponder_move = engine.search_with_ponder(
                game, board, btime, wtime, game.state["binc"],
                game.state["winc"], game.state["byo"])
            engine.print_stats()

            if is_usi_ponder and not (ponder_move is None):
                ponder_board = copy.deepcopy(board)
                ponder_board.push(shogi.Move.from_usi(best_move))
                ponder_board.push(shogi.Move.from_usi(ponder_move))
                ponder_usi = ponder_move

                if board.turn == shogi.BLACK:
                    btime = max(
                        0, btime - move_overhead - int(
                            (time.perf_counter_ns() - start_time) / 1000000) +
                        game.state["binc"])
                else:
                    wtime = max(
                        0, wtime - move_overhead - int(
                            (time.perf_counter_ns() - start_time) / 1000000) +
                        game.state["winc"])
                logger.info("Pondering for btime {} wtime {}".format(
                    btime, wtime))
                ponder_thread = threading.Thread(
                    target=ponder_thread_func,
                    args=(game, engine, ponder_board, btime, wtime,
                          game.state["binc"], game.state["winc"],
                          game.state["byo"]))
                ponder_thread.start()
            li.make_move(game.id, best_move)
            time.sleep(delay_seconds)
        elif is_game_over(game):
            engine.report_game_result(game, board)
        elif len(board.move_stack) == 0:
            correspondence_disconnect_time = correspondence_cfg.get(
                "disconnect_time", 300)

    correspondence_disconnect_time = 0
    while not terminated:
        try:
            binary_chunk = next(lines)

            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            logger.debug("Update: {}".format(upd))
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                if len(moves) > 0 and len(moves) != len(board.move_stack):
                    board = update_board(board, moves[-1])
                if not is_game_over(game) and is_engine_move(game, moves):
                    start_time = time.perf_counter_ns()
                    fake_thinking(config, board, game)
                    print_move_number(board)
                    correspondence_disconnect_time = correspondence_cfg.get(
                        "disconnect_time", 300)

                    best_move = None
                    ponder_move = None

                    btime = upd["btime"]
                    wtime = upd["wtime"]
                    start_time = time.perf_counter_ns()

                    if not deferredFirstMove:
                        if best_move == None:
                            if board.turn == shogi.BLACK:
                                btime = max(
                                    0, btime - move_overhead - int(
                                        (time.perf_counter_ns() - start_time) /
                                        1000000))
                            else:
                                wtime = max(
                                    0, wtime - move_overhead - int(
                                        (time.perf_counter_ns() - start_time) /
                                        1000000))
                            logger.info(
                                "Searching for btime {} wtime {}".format(
                                    btime, wtime))
                            best_move, ponder_move = engine.search_with_ponder(
                                game, board, btime, wtime, upd["binc"],
                                upd["winc"], upd["byo"])
                            engine.print_stats()

                        if is_usi_ponder and not (ponder_move is None):
                            ponder_board = copy.deepcopy(board)
                            ponder_board.push(shogi.Move.from_usi(best_move))
                            ponder_board.push(shogi.Move.from_usi(ponder_move))
                            ponder_usi = ponder_move
                            if board.turn == shogi.BLACK:
                                btime = max(
                                    0, btime - move_overhead - int(
                                        (time.perf_counter_ns() - start_time) /
                                        1000000) + upd["binc"])
                            else:
                                wtime = max(
                                    0, wtime - move_overhead - int(
                                        (time.perf_counter_ns() - start_time) /
                                        1000000) + upd["winc"])
                            logger.info(
                                "Pondering for btime {} wtime {}".format(
                                    btime, wtime))
                            ponder_thread = threading.Thread(
                                target=ponder_thread_func,
                                args=(game, engine, ponder_board, btime, wtime,
                                      upd["binc"], upd["winc"], upd["byo"]))
                            ponder_thread.start()
                        li.make_move(game.id, best_move)
                    else:
                        play_first_move(game, engine, board, li)
                        deferredFirstMove = False
                wb = 'w' if board.turn == shogi.BLACK else 'b'
                game.ping(config.get("abort_time", 30),
                          (upd[f"{wb}time"] + upd[f"{wb}inc"]) / 1000 + 60,
                          correspondence_disconnect_time)
            elif u_type == "ping":
                if is_correspondence and not is_engine_move(
                        game, board) and game.should_disconnect_now():
                    break
                elif game.should_abort_now():
                    logger.info("Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
                    break
                elif game.should_terminate_now():
                    logger.info("Terminating {} by lack of activity".format(
                        game.url()))
                    if game.is_abortable():
                        li.abort(game.id)
                    break
        except (HTTPError, ReadTimeout, RemoteDisconnected,
                ChunkedEncodingError, ConnectionError, ProtocolError):
            if game.id not in (ongoing_game["gameId"]
                               for ongoing_game in li.get_ongoing_games()):
                break
        except StopIteration:
            break

    engine.stop()
    if not (ponder_thread is None):
        ponder_thread.join()

    if is_correspondence and not is_game_over(game):
        logger.info("--- Disconnecting from {}".format(game.url()))
        correspondence_queue.put(game_id)
    else:
        logger.info("--- {} Game over".format(game.url()))

    control_queue.put_nowait({"type": "local_game_done"})
Beispiel #6
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config,
              challenge_queue):
    response = li.get_game_stream(game_id)
    lines = response.iter_lines()

    #Initial response of stream will be the full game info. Store it
    initial_state = json.loads(next(lines).decode('utf-8'))
    game = model.Game(initial_state, user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    board = setup_board(game)
    engine = engine_factory(board)
    engine.get_opponent_info(game)
    conversation = Conversation(game, engine, li, __version__, challenge_queue)

    logger.info("+++ {}".format(game))

    engine_cfg = config["engine"]
    is_usi = engine_cfg["protocol"] == "usi"
    is_usi_ponder = is_usi and engine_cfg.get("ponder", False)
    move_overhead = config.get("move_overhead", 1000)
    polyglot_cfg = engine_cfg.get("polyglot", {})
    book_cfg = polyglot_cfg.get("book", {})

    ponder_thread = None
    deferredFirstMove = False

    ponder_usi = None

    def ponder_thread_func(game, engine, board, wtime, btime, winc, binc):
        global ponder_results
        best_move, ponder_move = engine.search_with_ponder(
            board, wtime, btime, winc, binc, True)
        ponder_results[game.id] = (best_move, ponder_move)

    engine.set_time_control(game)

    if len(board.move_stack) < 2:
        while not terminated:
            try:
                if not play_first_move(game, engine, board, li):
                    deferredFirstMove = True
                break
            except (HTTPError) as exception:
                if exception.response.status_code == 400:  # fallthrough
                    break
    else:
        moves = game.state["moves"].split()
        if not is_game_over(game) and is_engine_move(game, moves):
            best_move = None
            ponder_move = None
            wtime = game.state["wtime"]
            btime = game.state["btime"]
            if board.turn == shogi.BLACK:
                wtime = max(0, wtime - move_overhead)
            else:
                btime = max(0, btime - move_overhead)
            logger.info("Searching for wtime {} btime {}".format(wtime, btime))
            best_move, ponder_move = engine.search_with_ponder(
                board, wtime, btime, game.state["winc"], game.state["binc"])
            engine.print_stats()

            if is_usi_ponder and not (ponder_move is None):
                ponder_board = copy.deepcopy(board)
                ponder_board.push(shogi.Move.from_usi(best_move))
                ponder_board.push(shogi.Move.from_usi(ponder_move))
                ponder_usi = ponder_move
                logger.info("Pondering for wtime {} btime {}".format(
                    wtime, btime))
                ponder_thread = threading.Thread(
                    target=ponder_thread_func,
                    args=(game, engine, ponder_board, wtime, btime,
                          game.state["winc"], game.state["binc"]))
                ponder_thread.start()
            li.make_move(game.id, best_move)

    while not terminated:
        try:
            binary_chunk = next(lines)
        except (StopIteration):
            break
        try:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                if len(moves) > 0 and len(moves) != len(board.move_stack):
                    board = update_board(board, moves[-1])
                if not is_game_over(game) and is_engine_move(game, moves):
                    if config.get("fake_think_time") and len(moves) > 9:
                        delay = min(game.clock_initial,
                                    game.my_remaining_seconds()) * 0.015
                        accel = 1 - max(0, min(100, len(moves) - 20)) / 150
                        sleep = min(5, delay * accel)
                        time.sleep(sleep)

                    best_move = None
                    ponder_move = None

                    wtime = upd["wtime"]
                    btime = upd["btime"]
                    if board.turn == shogi.BLACK:
                        wtime = max(0, wtime - move_overhead)
                    else:
                        btime = max(0, btime - move_overhead)

                    if not deferredFirstMove:
                        if best_move == None:
                            logger.info(
                                "Searching for wtime {} btime {}".format(
                                    wtime, btime))
                            best_move, ponder_move = engine.search_with_ponder(
                                board, wtime, btime, upd["winc"], upd["binc"])
                            engine.print_stats()

                        if is_usi_ponder and not (ponder_move is None):
                            ponder_board = copy.deepcopy(board)
                            ponder_board.push(shogi.Move.from_usi(best_move))
                            ponder_board.push(shogi.Move.from_usi(ponder_move))
                            ponder_usi = ponder_move
                            logger.info(
                                "Pondering for wtime {} btime {}".format(
                                    wtime, btime))
                            ponder_thread = threading.Thread(
                                target=ponder_thread_func,
                                args=(game, engine, ponder_board, wtime, btime,
                                      upd["winc"], upd["binc"]))
                            ponder_thread.start()
                        li.make_move(game.id, best_move)
                    else:
                        play_first_move(game, engine, board, li)
                        deferredFirstMove = False
                if board.turn == shogi.BLACK:
                    game.ping(config.get("abort_time", 20),
                              (upd["wtime"] + upd["winc"]) / 1000 + 60)
                else:
                    game.ping(config.get("abort_time", 20),
                              (upd["btime"] + upd["binc"]) / 1000 + 60)
            elif u_type == "ping":
                if game.should_abort_now():
                    logger.info("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
                    break
                elif game.should_terminate_now():
                    logger.info(
                        "    Terminating {} by lack of activity".format(
                            game.url()))
                    if game.is_abortable():
                        li.abort(game.id)
                    break
        except (HTTPError, ReadTimeout, RemoteDisconnected,
                ChunkedEncodingError, ConnectionError, ProtocolError) as e:
            if game.id in (ongoing_game["gameId"]
                           for ongoing_game in li.get_ongoing_games()):
                continue
            else:
                break

    logger.info("--- {} Game over".format(game.url()))
    engine.stop()
    if not (ponder_thread is None):
        ponder_thread.join()
        ponder_thread = None

    # This can raise queue.NoFull, but that should only happen if we're not processing
    # events fast enough and in this case I believe the exception should be raised
    control_queue.put_nowait({"type": "local_game_done"})
Beispiel #7
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config,
              challenge_queue):
    response = li.get_game_stream(game_id)
    lines = response.iter_lines()

    # Initial response of stream will be the full game info. Store it
    initial_state = json.loads(next(lines).decode('utf-8'))
    game = model.Game(initial_state, user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    engine = engine_factory()
    engine.get_opponent_info(game)
    engine.set_time_control(game)
    conversation = Conversation(game, engine, li, __version__, challenge_queue)

    logger.info("+++ {}".format(game))

    engine_cfg = config["engine"]
    is_uci = engine_cfg["protocol"] == "uci"
    is_uci_ponder = is_uci and engine_cfg.get("uci_ponder", False)
    move_overhead = config.get("move_overhead", 1000)
    polyglot_cfg = engine_cfg.get("polyglot", {})

    ponder_thread = None
    ponder_uci = None

    first_move = True
    while not terminated:
        try:
            if first_move:
                upd = game.state
                first_move = False
            else:
                binary_chunk = next(lines)
                upd = json.loads(
                    binary_chunk.decode('utf-8')) if binary_chunk else None

            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                board = setup_board(game)
                if not is_game_over(game) and is_engine_move(game, board):
                    start_time = time.perf_counter_ns()
                    fake_thinking(config, board, game)

                    best_move, ponder_move = get_book_move(
                        board, polyglot_cfg), None
                    if best_move is None:
                        if len(board.move_stack) < 2:
                            best_move, ponder_move = choose_first_move(
                                engine, board)
                        else:
                            best_move, ponder_move = get_pondering_results(
                                ponder_thread, ponder_uci, game, board, engine)
                            if best_move is None:
                                best_move, ponder_move = choose_move(
                                    engine, board, game, start_time,
                                    move_overhead)
                    li.make_move(game.id, best_move)
                    ponder_thread, ponder_uci = start_pondering(
                        engine, board, game, is_uci_ponder, best_move,
                        ponder_move, start_time, move_overhead)

                wb = 'w' if board.turn == chess.WHITE else 'b'
                game.ping(config.get("abort_time", 20),
                          (upd[f"{wb}time"] + upd[f"{wb}inc"]) / 1000 + 60)
            elif u_type == "ping":
                if game.should_abort_now():
                    logger.info("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
                    break
                elif game.should_terminate_now():
                    logger.info(
                        "    Terminating {} by lack of activity".format(
                            game.url()))
                    if game.is_abortable():
                        li.abort(game.id)
                    break
        except (HTTPError, ReadTimeout, RemoteDisconnected,
                ChunkedEncodingError, ConnectionError, ProtocolError):
            if game.id not in (ongoing_game["gameId"]
                               for ongoing_game in li.get_ongoing_games()):
                break
        except StopIteration:
            break

    logger.info("--- {} Game over".format(game.url()))
    engine.stop()
    engine.quit()
    if ponder_thread is not None:
        ponder_thread.join()

    # This can raise queue.NoFull, but that should only happen if we're not processing
    # events fast enough and in this case I believe the exception should be raised
    control_queue.put_nowait({"type": "local_game_done"})
Beispiel #8
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config,
              challenge_queue, correspondence_queue, logging_queue,
              logging_configurer, logging_level):
    logging_configurer(logging_queue, logging_level)
    logger = logging.getLogger(__name__)

    response = li.get_game_stream(game_id)
    lines = response.iter_lines()

    # Initial response of stream will be the full game info. Store it
    initial_state = json.loads(next(lines).decode('utf-8'))
    game = model.Game(initial_state, user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    engine = engine_factory()
    engine.get_opponent_info(game)
    conversation = Conversation(game, engine, li, __version__, challenge_queue)

    logger.info("+++ {}".format(game))

    is_correspondence = game.perf_name == "Correspondence"
    correspondence_cfg = config.get("correspondence", {}) or {}
    correspondence_move_time = correspondence_cfg.get("move_time", 60) * 1000

    engine_cfg = config["engine"]
    ponder_cfg = correspondence_cfg if is_correspondence else engine_cfg
    can_ponder = ponder_cfg.get("uci_ponder", False) or ponder_cfg.get(
        'ponder', False)
    move_overhead = config.get("move_overhead", 1000)
    delay_seconds = config.get("rate_limiting_delay", 0) / 1000
    polyglot_cfg = engine_cfg.get("polyglot", {})

    first_move = True
    correspondence_disconnect_time = 0
    while not terminated:
        try:
            if first_move:
                upd = game.state
                first_move = False
            else:
                binary_chunk = next(lines)
                upd = json.loads(
                    binary_chunk.decode('utf-8')) if binary_chunk else None

            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                board = setup_board(game)
                if not is_game_over(game) and is_engine_move(game, board):
                    start_time = time.perf_counter_ns()
                    fake_thinking(config, board, game)
                    print_move_number(board)
                    correspondence_disconnect_time = correspondence_cfg.get(
                        "disconnect_time", 300)

                    best_move = get_book_move(board, polyglot_cfg)
                    if best_move is None:
                        if len(board.move_stack) < 2:
                            best_move = choose_first_move(engine, board)
                        elif is_correspondence:
                            best_move = choose_move_time(
                                engine, board, correspondence_move_time,
                                can_ponder)
                        else:
                            best_move = choose_move(engine, board, game,
                                                    can_ponder, start_time,
                                                    move_overhead)
                    li.make_move(game.id, best_move)
                    time.sleep(delay_seconds)
                elif is_game_over(game):
                    engine.report_game_result(game, board)
                elif len(board.move_stack) == 0:
                    correspondence_disconnect_time = correspondence_cfg.get(
                        "disconnect_time", 300)

                wb = 'w' if board.turn == chess.WHITE else 'b'
                game.ping(config.get("abort_time", 20),
                          (upd[f"{wb}time"] + upd[f"{wb}inc"]) / 1000 + 60,
                          correspondence_disconnect_time)
            elif u_type == "ping":
                if is_correspondence and not is_engine_move(
                        game, board) and game.should_disconnect_now():
                    break
                elif game.should_abort_now():
                    logger.info("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
                    break
                elif game.should_terminate_now():
                    logger.info(
                        "    Terminating {} by lack of activity".format(
                            game.url()))
                    if game.is_abortable():
                        li.abort(game.id)
                    break
        except (HTTPError, ReadTimeout, RemoteDisconnected,
                ChunkedEncodingError, ConnectionError, ProtocolError):
            if game.id not in (ongoing_game["gameId"]
                               for ongoing_game in li.get_ongoing_games()):
                break
        except StopIteration:
            break

    engine.stop()
    engine.quit()

    if is_correspondence and not is_game_over(game):
        logger.info("--- Disconnecting from {}".format(game.url()))
        correspondence_queue.put(game_id)
    else:
        logger.info("--- {} Game over".format(game.url()))

    control_queue.put_nowait({"type": "local_game_done"})
def play_game(li, game_id, control_queue, engine_factory, user_profile,
              config):
    updates = li.get_game_stream(game_id).iter_lines()

    #Initial response of stream will be the full game info. Store it
    game = model.Game(json.loads(next(updates).decode('utf-8')),
                      user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li, __version__)

    print("+++ {}".format(game))

    engine_cfg = config["engine"]
    is_uci = engine_cfg["protocol"] == "uci"
    is_uci_ponder = is_uci and engine_cfg.get("uci_ponder", False)
    polyglot_cfg = engine_cfg.get("polyglot", {})

    if not polyglot_cfg.get("enabled") or not play_first_book_move(
            game, engine, board, li, polyglot_cfg):
        play_first_move(game, engine, board, li)

    engine.set_time_control(game)

    ponder_thread = None
    ponder_uci = None

    def ponder_thread_func(game, engine, board, wtime, btime, winc, binc):
        global ponder_results
        best_move, ponder_move = engine.search_with_ponder(
            board, wtime, btime, winc, binc, True)
        ponder_results[game.id] = (best_move, ponder_move)

    try:
        for binary_chunk in updates:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                board = update_board(board, moves[-1])
                if is_engine_move(game, moves):
                    best_move = None
                    ponder_move = None
                    thinking_started_at = time.time()
                    if not (ponder_thread is None):
                        move_uci = moves[-1]
                        if ponder_uci == move_uci:
                            engine.engine.ponderhit()
                            ponder_thread.join()
                            ponder_thread = None
                            best_move, ponder_move = ponder_results[game.id]
                        else:
                            engine.engine.stop()
                            ponder_thread.join()
                            ponder_thread = None
                    ponder_uci = None

                    if config.get("fake_think_time") and len(moves) > 9:
                        delay = min(game.clock_initial,
                                    game.my_remaining_seconds()) * 0.015
                        accel = 1 - max(0, min(100, len(moves) - 20)) / 150
                        sleep = min(5, delay * accel)
                        time.sleep(sleep)

                    if (best_move is None
                        ) and polyglot_cfg.get("enabled") and len(
                            moves) <= polyglot_cfg.get("max_depth", 8) * 2 - 1:
                        best_move = get_book_move(board, polyglot_cfg)
                    if best_move == None:
                        wtime = int(upd["wtime"])
                        btime = int(upd["btime"])
                        winc = int(upd["winc"])
                        binc = int(upd["binc"])
                        best_move, ponder_move = engine.search_with_ponder(
                            board, wtime, btime, winc, binc)
                    if is_uci_ponder and not (ponder_move is None):
                        mwtime = wtime
                        mbtime = btime
                        elapsed_thinking_time_ms = int(
                            (time.time() - thinking_started_at) * 1000)
                        if game.is_white:
                            mwtime = wtime - elapsed_thinking_time_ms
                        else:
                            mbtime = btime - elapsed_thinking_time_ms
                        if (mwtime > 0) and (mbtime > 0):
                            ponder_board = board.copy()
                            ponder_board.push(best_move)
                            ponder_board.push(ponder_move)
                            ponder_uci = ponder_move.uci()
                            ponder_thread = threading.Thread(
                                target=ponder_thread_func,
                                args=(game, engine, ponder_board, mwtime,
                                      mbtime, winc, binc))
                            ponder_thread.start()
                    li.make_move(game.id, best_move)
                    game.abort_in(config.get("abort_time", 20))
            elif u_type == "ping":
                if game.should_abort_now():
                    print("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
    except (RemoteDisconnected, ChunkedEncodingError, ConnectionError,
            ProtocolError, HTTPError) as exception:
        print("Abandoning game due to connection error")
        traceback.print_exception(type(exception), exception,
                                  exception.__traceback__)
    finally:
        print("--- {} Game over".format(game.url()))
        engine.quit()
        if not (ponder_thread is None):
            ponder_thread.join()
            ponder_thread = None
        # This can raise queue.NoFull, but that should only happen if we're not processing
        # events fast enough and in this case I believe the exception should be raised
        control_queue.put_nowait({"type": "local_game_done"})
Beispiel #10
0
def play_game(li, game_id, control_queue, engine_factory, user_profile,
              config):
    #game state
    gg_said = False

    updates = li.get_game_stream(game_id).iter_lines()

    #Initial response of stream will be the full game info. Store it
    game = model.Game(json.loads(next(updates).decode('utf-8')),
                      user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li, __version__)

    print("+++ {}".format(game))

    engine_cfg = config["engine"]
    polyglot_cfg = engine_cfg.get("polyglot", {})

    if not polyglot_cfg.get("enabled") or not play_first_book_move(
            game, engine, board, li, config):
        play_first_move(game, engine, board, li)

    engine.set_time_control(game)

    save_fen(config, board)

    if board.fullmove_number == 1:
        cnt = 0
        glms = config["good_luck_messages"]
        for glm in glms:
            game_chat(li, game.id, glm, public=(cnt < (len(glms) - 1)))
            cnt += 1

    watch_engine[game.id] = True

    def print_engine_info(engine, game):
        global watch_engine
        while watch_engine[game.id]:
            try:
                if not engine.engine.idle:
                    info = engine.engine.info_handlers[0].info
                    score = info["score"][1]
                    print(
                        "{} - {}\n  {:8s} {:8s}".format(
                            game.white, game.black, str(score.cp),
                            str(score.mate)),
                        " ".join(list(map(chess.Move.uci,
                                          info["pv"][1][0:5]))))
            except:
                pass
            time.sleep(1)
        print("engine watch finished")

    th = threading.Thread(target=print_engine_info, args=(engine, game))
    th.start()

    try:
        for binary_chunk in updates:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                board = update_board(board, moves[-1])

                save_fen(config, board)

                if is_engine_move(game, moves):
                    best_move = None

                    pos_eval = 0
                    best_move = get_book_move(board, config)

                    if best_move == None:
                        print("searching for move")
                        best_move = engine.search(board, upd["wtime"],
                                                  upd["btime"], upd["winc"],
                                                  upd["binc"])
                        info = engine.engine.info_handlers[0].info
                        score = info["score"][1]
                        if score[1] == None:
                            pos_eval = score[0]
                        else:
                            mate = score[1]
                            if mate > 0:
                                pos_eval = MATE_SCORE - mate
                            else:
                                pos_eval = -MATE_SCORE - mate
                        print("best move", best_move, pos_eval)
                    else:
                        pass

                    opp_time = int(upd["wtime"])

                    if game.is_white:
                        opp_time = int(upd["btime"])

                    is_inc = (int(upd["winc"]) > 0) or (int(upd["binc"]) > 0)

                    if (pos_eval > RESIGN_SCORE) or (
                        (opp_time < TIME_TROUBLE_LIMIT) and (not is_inc)):
                        if pos_eval > WON_SCORE and not gg_said:
                            ggm = config["good_game_message"]
                            if not ggm is None:
                                game_chat(li, game.id, ggm, public=True)
                            gg_said = True
                        li.make_move(game.id, best_move)
                        game.abort_in(config.get("abort_time", 20))
                    else:
                        print("resign")
                        game_chat(li, game.id, "good game", public=True)
                        li.resign_hopeless_game(game.id)
            elif u_type == "ping":
                if game.should_abort_now():
                    print("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
    except (RemoteDisconnected, ChunkedEncodingError, ConnectionError,
            ProtocolError, HTTPError) as exception:
        print("Abandoning game due to connection error")
        traceback.print_exception(type(exception), exception,
                                  exception.__traceback__)
    finally:
        print("--- {} Game over".format(game.url()))
        engine.quit()
        watch_engine[game.id] = False
        # This can raise queue.NoFull, but that should only happen if we're not processing
        # events fast enough and in this case I believe the exception should be raised
        control_queue.put_nowait({"type": "local_game_done"})
Beispiel #11
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config,
              challenge_queue):
    response = li.get_game_stream(game_id)
    lines = response.iter_lines()

    #Initial response of stream will be the full game info. Store it
    initial_state = json.loads(next(lines).decode('utf-8'))
    game = model.Game(initial_state, user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))
    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li, __version__, challenge_queue)

    logger.info("+++ {}".format(game))

    engine_cfg = config["engine"]
    is_uci = engine_cfg["protocol"] == "uci"
    is_uci_ponder = is_uci and engine_cfg.get("uci_ponder", False)
    move_overhead = config.get("move_overhead", 1000)
    polyglot_cfg = engine_cfg.get("polyglot", {})
    book_cfg = polyglot_cfg.get("book", {})

    ponder_thread = None
    deferredFirstMove = False

    ponder_uci = None

    def ponder_thread_func(game, engine, board, wtime, btime, winc, binc):
        global ponder_results
        best_move, ponder_move = engine.search_with_ponder(
            board, wtime, btime, winc, binc, True)
        ponder_results[game.id] = (best_move, ponder_move)

    engine.set_time_control(game)
    conversation.xhr.chat(
        game.id, "player",
        f"Hi {game.opponent.name}, you are playing against:")

    conversation.xhr.chat(
        game.id, "player",
        f"{os.path.basename(engine_cfg['lczero']['weights'])}")

    if 'go_commands' in engine_cfg['uci_options']:
        if 'depth' in engine_cfg['uci_options']['go_commands']:
            conversation.xhr.chat(
                game.id, "player",
                f"It can search to a depth of: {engine_cfg['uci_options']['go_commands']['depth']}"
            )
        if 'nodes' in engine_cfg['uci_options']['go_commands']:
            conversation.xhr.chat(
                game.id, "player",
                f"It is limited to searching  {engine_cfg['uci_options']['go_commands']['nodes']} nodes"
            )
    conversation.xhr.chat(
        game.id, "player",
        f"and is using {engine_cfg['lczero']['threads']} threads")

    conversation.xhr.chat(
        game.id, "player",
        f"We are testing a lot of different networks, we'll have nicer looking names in the future"
    )

    conversation.xhr.chat(game.id, "player",
                          "Good luck and I hope you have fun")
    if len(board.move_stack) < 2:
        while not terminated:
            try:
                if not polyglot_cfg.get("enabled") or not play_first_book_move(
                        game, engine, board, li, book_cfg):
                    if not play_first_move(game, engine, board, li):
                        deferredFirstMove = True
                break
            except (HTTPError) as exception:
                if exception.response.status_code == 400:  # fallthrough
                    break
    else:
        moves = game.state["moves"].split()
        if not board.is_game_over() and is_engine_move(game, moves):
            book_move = None
            best_move = None
            ponder_move = None
            wtime = game.state["wtime"]
            btime = game.state["btime"]
            if board.turn == chess.WHITE:
                wtime = max(0, wtime - move_overhead)
            else:
                btime = max(0, btime - move_overhead)
            if polyglot_cfg.get("enabled") and len(
                    moves) <= polyglot_cfg.get("max_depth", 8) * 2 - 1:
                book_move = get_book_move(board, book_cfg)
            if book_move == None:
                logger.info("Searching for wtime {} btime {}".format(
                    wtime, btime))
                best_move, ponder_move = engine.search_with_ponder(
                    board, wtime, btime, game.state["winc"],
                    game.state["binc"])
                engine.print_stats()
            else:
                best_move = book_move

            if is_uci_ponder and not (ponder_move is None):
                ponder_board = board.copy()
                ponder_board.push(best_move)
                ponder_board.push(ponder_move)
                ponder_uci = ponder_move.uci()
                logger.info("Pondering for wtime {} btime {}".format(
                    wtime, btime))
                ponder_thread = threading.Thread(
                    target=ponder_thread_func,
                    args=(game, engine, ponder_board, wtime, btime,
                          game.state["winc"], game.state["binc"]))
                ponder_thread.start()
            li.make_move(game.id, best_move)

    while not terminated:
        try:
            binary_chunk = next(lines)
        except (StopIteration):
            break
        try:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                board = update_board(board, moves[-1])
                if not board.is_game_over() and is_engine_move(game, moves):
                    if config.get("fake_think_time") and len(moves) > 9:
                        delay = min(game.clock_initial,
                                    game.my_remaining_seconds()) * 0.015
                        accel = 1 - max(0, min(100, len(moves) - 20)) / 150
                        sleep = min(5, delay * accel)
                        time.sleep(sleep)

                    book_move = None
                    best_move = None
                    ponder_move = None
                    if not (ponder_thread is None):
                        move_uci = moves[-1]
                        if ponder_uci == move_uci:
                            engine.engine.ponderhit()
                            ponder_thread.join()
                            ponder_thread = None
                            best_move, ponder_move = ponder_results[game.id]
                            engine.print_stats()
                        else:
                            engine.engine.stop()
                            ponder_thread.join()
                            ponder_thread = None
                        ponder_uci = None

                    wtime = upd["wtime"]
                    btime = upd["btime"]
                    if board.turn == chess.WHITE:
                        wtime = max(0, wtime - move_overhead)
                    else:
                        btime = max(0, btime - move_overhead)

                    if not deferredFirstMove:
                        if polyglot_cfg.get(
                                "enabled") and len(moves) <= polyglot_cfg.get(
                                    "max_depth", 8) * 2 - 1:
                            book_move = get_book_move(board, book_cfg)
                        if best_move == None:
                            if book_move == None:
                                logger.info(
                                    "Searching for wtime {} btime {}".format(
                                        wtime, btime))
                                best_move, ponder_move = engine.search_with_ponder(
                                    board, wtime, btime, upd["winc"],
                                    upd["binc"])
                                engine.print_stats()
                            else:
                                best_move = book_move
                        else:
                            if not (book_move == None):
                                best_move = book_move
                                ponder_move = None

                        if is_uci_ponder and not (ponder_move is None):
                            ponder_board = board.copy()
                            ponder_board.push(best_move)
                            ponder_board.push(ponder_move)
                            ponder_uci = ponder_move.uci()
                            logger.info(
                                "Pondering for wtime {} btime {}".format(
                                    wtime, btime))
                            ponder_thread = threading.Thread(
                                target=ponder_thread_func,
                                args=(game, engine, ponder_board, wtime, btime,
                                      upd["winc"], upd["binc"]))
                            ponder_thread.start()
                        li.make_move(game.id, best_move)
                    else:
                        if not polyglot_cfg.get(
                                "enabled") or not play_first_book_move(
                                    game, engine, board, li, book_cfg):
                            play_first_move(game, engine, board, li)
                        deferredFirstMove = False
                if board.turn == chess.WHITE:
                    game.ping(config.get("abort_time", 20),
                              (upd["wtime"] + upd["winc"]) / 1000 + 60)
                else:
                    game.ping(config.get("abort_time", 20),
                              (upd["btime"] + upd["binc"]) / 1000 + 60)
            elif u_type == "ping":
                if game.should_abort_now():
                    logger.info("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
                    break
                elif game.should_terminate_now():
                    logger.info(
                        "    Terminating {} by lack of activity".format(
                            game.url()))
                    if game.is_abortable():
                        li.abort(game.id)
                    break
        except (HTTPError, ReadTimeout, RemoteDisconnected,
                ChunkedEncodingError, ConnectionError, ProtocolError) as e:
            ongoing_games = li.get_ongoing_games()
            game_over = True
            for ongoing_game in ongoing_games:
                if ongoing_game["gameId"] == game.id:
                    game_over = False
                    break
            if not game_over:
                continue
            else:
                break
    logger.info("--- {} Game over".format(game.url()))
    conversation.xhr.chat(game.id, "player", "GG, thanks for playing")
    conversation.xhr.chat(game.id, "spectator", "This game was against:")
    conversation.xhr.chat(
        game.id, "spectator",
        f"{os.path.basename(engine_cfg['lczero']['weights'])}")
    if 'go_commands' in engine_cfg['uci_options']:
        if 'depth' in engine_cfg['uci_options']['go_commands']:
            conversation.xhr.chat(
                game.id, "spectator",
                f"It can search to a depth of: {engine_cfg['uci_options']['go_commands']['depth']}"
            )
        if 'nodes' in engine_cfg['uci_options']['go_commands']:
            conversation.xhr.chat(
                game.id, "spectator",
                f"It is limited to searching  {engine_cfg['uci_options']['go_commands']['nodes']} nodes"
            )
    if 'sRand' in engine_cfg.get("lczero", {}):
        conversation.xhr.chat(
            game.id, "spectator",
            f"The search randomization factor is: {engine_cfg['lczero']['sRand']:.3f}"
        )
    conversation.xhr.chat(
        game.id, "spectator",
        f"and is using {engine_cfg['lczero']['threads']} threads")
    conversation.xhr.chat(game.id, "spectator",
                          f"My internal stats were: {engine.get_stats()}")
    engine.engine.stop()
    engine.quit()
    if not (ponder_thread is None):
        ponder_thread.join()
        ponder_thread = None

    # This can raise queue.NoFull, but that should only happen if we're not processing
    # events fast enough and in this case I believe the exception should be raised
    control_queue.put_nowait({"type": "local_game_done"})
Beispiel #12
0
def play_game(li, game_id, control_queue, engine_factory, user_profile, config,
              challenge_queue):
    response = li.get_game_stream(game_id)
    lines = response.iter_lines()

    #Initial response of stream will be the full game info. Store it
    initial_state = json.loads(next(lines).decode('utf-8'))
    game = model.Game(initial_state, user_profile["username"], li.baseUrl,
                      config.get("abort_time", 20))

    color_file_path = os.path.join(os.getcwd(), 'engines', 'engine_color.txt')
    print("Engine color file path: ", color_file_path)
    if os.path.exists(color_file_path):
        print("File exists, removing it....")
        os.remove(color_file_path)

    print("Trying to open ", color_file_path)
    color_file = open(color_file_path, 'w+')
    is_white_num_str = str(1 if game.is_white else 0)
    color_file.write(is_white_num_str)
    print(f"Wrote {is_white_num_str} to it")
    color_file.close()

    board = setup_board(game)
    engine = engine_factory(board)
    conversation = Conversation(game, engine, li, __version__, challenge_queue)

    logger.info("+++ {}".format(game))

    engine_cfg = config["engine"]
    is_uci = engine_cfg["protocol"] == "uci"
    is_uci_ponder = is_uci and engine_cfg.get("uci_ponder", False)
    move_overhead = config.get("move_overhead", 1000)
    polyglot_cfg = engine_cfg.get("polyglot", {})
    book_cfg = polyglot_cfg.get("book", {})

    ponder_thread = None
    deferredFirstMove = False

    ponder_uci = None

    def ponder_thread_func(game, engine, board, wtime, btime, winc, binc):
        global ponder_results
        best_move, ponder_move = engine.search_with_ponder(
            board, wtime, btime, winc, binc, True)
        ponder_results[game.id] = (best_move, ponder_move)

    engine.set_time_control(game)

    if len(board.move_stack) < 2:
        while not terminated:
            try:
                if not polyglot_cfg.get(
                        "enabled"
                ):  #or not play_first_book_move(game, engine, board, li, book_cfg):
                    if not play_first_move(game, engine, board, li):
                        deferredFirstMove = True
                break
            except (HTTPError) as exception:
                if exception.response.status_code == 400:  # fallthrough
                    break
    else:
        moves = game.state["moves"].split()
        if not board.is_game_over() and is_engine_move(game, moves):
            book_move = None
            best_move = None
            ponder_move = None
            wtime = game.state["wtime"]
            btime = game.state["btime"]
            if board.turn == chess.WHITE:
                wtime = max(0, wtime - move_overhead)
            else:
                btime = max(0, btime - move_overhead)
            # if polyglot_cfg.get("enabled") and len(moves) <= polyglot_cfg.get("max_depth", 8) * 2 - 1:
            #     book_move = get_book_move(board, book_cfg)
            # if book_move == None:
            logger.info("Searching for wtime {} btime {}".format(wtime, btime))
            best_move, ponder_move = engine.search_with_ponder(
                board, wtime, btime, game.state["winc"], game.state["binc"])
            engine.print_stats()
            # else:
            #     best_move = book_move

            if is_uci_ponder and not (ponder_move is None):
                ponder_board = board.copy()
                ponder_board.push(best_move)
                ponder_board.push(ponder_move)
                ponder_uci = ponder_move.uci()
                logger.info("Pondering for wtime {} btime {}".format(
                    wtime, btime))
                ponder_thread = threading.Thread(
                    target=ponder_thread_func,
                    args=(game, engine, ponder_board, wtime, btime,
                          game.state["winc"], game.state["binc"]))
                ponder_thread.start()
            li.make_move(game.id, best_move)

    while not terminated:
        try:
            binary_chunk = next(lines)
        except (StopIteration):
            break
        try:
            upd = json.loads(
                binary_chunk.decode('utf-8')) if binary_chunk else None
            u_type = upd["type"] if upd else "ping"
            if u_type == "chatLine":
                conversation.react(ChatLine(upd), game)
            elif u_type == "gameState":
                game.state = upd
                moves = upd["moves"].split()
                board = update_board(board, moves[-1])
                if not board.is_game_over() and is_engine_move(game, moves):
                    if config.get("fake_think_time") and len(moves) > 9:
                        delay = min(game.clock_initial,
                                    game.my_remaining_seconds()) * 0.015
                        accel = 1 - max(0, min(100, len(moves) - 20)) / 150
                        sleep = min(5, delay * accel)
                        time.sleep(sleep)

                    book_move = None
                    best_move = None
                    ponder_move = None
                    if not (ponder_thread is None):
                        move_uci = moves[-1]
                        if ponder_uci == move_uci:
                            engine.engine.ponderhit()
                            ponder_thread.join()
                            ponder_thread = None
                            best_move, ponder_move = ponder_results[game.id]
                            engine.print_stats()
                        else:
                            engine.engine.stop()
                            ponder_thread.join()
                            ponder_thread = None
                        ponder_uci = None

                    wtime = upd["wtime"]
                    btime = upd["btime"]
                    if board.turn == chess.WHITE:
                        wtime = max(0, wtime - move_overhead)
                    else:
                        btime = max(0, btime - move_overhead)

                    # if not deferredFirstMove:
                    # if polyglot_cfg.get("enabled") and len(moves) <= polyglot_cfg.get("max_depth", 8) * 2 - 1:
                    #     book_move = get_book_move(board, book_cfg)
                    # if best_move == None:
                    #     if book_move == None:
                    logger.info("Searching for wtime {} btime {}".format(
                        wtime, btime))
                    best_move, ponder_move = engine.search_with_ponder(
                        board, wtime, btime, upd["winc"], upd["binc"])
                    engine.print_stats()
                    #     else:
                    #         best_move = book_move
                    # else:
                    #     if not ( book_move == None ):
                    #         best_move = book_move
                    #         ponder_move = None

                    if is_uci_ponder and not (ponder_move is None):
                        ponder_board = board.copy()
                        ponder_board.push(best_move)
                        ponder_board.push(ponder_move)
                        ponder_uci = ponder_move.uci()
                        logger.info("Pondering for wtime {} btime {}".format(
                            wtime, btime))
                        ponder_thread = threading.Thread(
                            target=ponder_thread_func,
                            args=(game, engine, ponder_board, wtime, btime,
                                  upd["winc"], upd["binc"]))
                        ponder_thread.start()
                    li.make_move(game.id, best_move)
                    # else:
                    #     if not polyglot_cfg.get("enabled") or not play_first_book_move(game, engine, board, li, book_cfg):
                    #         play_first_move(game, engine, board, li)
                    #     deferredFirstMove = False
                if board.turn == chess.WHITE:
                    game.ping(config.get("abort_time", 20),
                              (upd["wtime"] + upd["winc"]) / 1000 + 60)
                else:
                    game.ping(config.get("abort_time", 20),
                              (upd["btime"] + upd["binc"]) / 1000 + 60)
            elif u_type == "ping":
                if game.should_abort_now():
                    logger.info("    Aborting {} by lack of activity".format(
                        game.url()))
                    li.abort(game.id)
                    break
                elif game.should_terminate_now():
                    logger.info(
                        "    Terminating {} by lack of activity".format(
                            game.url()))
                    if game.is_abortable():
                        li.abort(game.id)
                    break
        except (HTTPError, ReadTimeout, RemoteDisconnected,
                ChunkedEncodingError, ConnectionError, ProtocolError) as e:
            ongoing_games = li.get_ongoing_games()
            game_over = True
            for ongoing_game in ongoing_games:
                if ongoing_game["gameId"] == game.id:
                    game_over = False
                    break
            if not game_over:
                continue
            else:
                break

    logger.info("--- {} Game over".format(game.url()))
    engine.engine.stop()
    engine.quit()
    if not (ponder_thread is None):
        ponder_thread.join()
        ponder_thread = None

    # This can raise queue.NoFull, but that should only happen if we're not processing
    # events fast enough and in this case I believe the exception should be raised
    control_queue.put_nowait({"type": "local_game_done"})