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"})
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"})
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"})
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"})
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"})
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"})
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"})
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"})
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"})
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"})
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"})