def play_game(ws): board = generate_board(10) ws.send(json.dumps({"player": "random", "type": "player"})) engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish") for state in board.states: analysis = engine.analyse(board, chess.engine.Limit(time=MOVE_GAP / 3)) ws.send( json.dumps({ "pieces": state, "score": analysis.score.white().score(), "type": "update" })) ws.send(json.dumps({"player": "nonrandom", "type": "player"})) while not board.is_game_over(claim_draw=True): result = engine.play(board, chess.engine.Limit(time=MOVE_GAP)) analysis = engine.analyse(board, chess.engine.Limit(time=MOVE_GAP)) print("score is:", analysis.score, "fen is", board.fen()) board.push(result.move) ws.send( json.dumps({ "pieces": board.get_pieces(), "score": analysis.score.white().score(), "type": "update" })) if board.is_checkmate(): ws.send(json.dumps({"winner": not board.turn(), "type": "winner"})) else: ws.send(json.dumps({"winner": None, "type": "winner"})) engine.quit()
def compute_move_scores(game_str, engine): # we are black, crucially... moves = game_str.split(' ') board = chess.Board() bot_scores = [] comp_scores = [] for i, move in enumerate(moves): if i % 2 == 0: # this is a computer move, do not analyse it board.push_san(move) else: # this is our move, we need to provide analysis board.push_san(move) bot_score_tup = engine.analyse(board, chess.engine.Limit(time=0.1), game='key1') board.pop() computer_move = engine.play(board, chess.engine.Limit(time=0.05)) board.push(computer_move.move) comp_score_tup = engine.analyse(board, chess.engine.Limit(time=0.1), game='key2') board.pop() board.push_san(move) if not bot_score_tup['score'].is_mate() and not comp_score_tup['score'].is_mate(): bot_scores.append(bot_score_tup['score'].black().score()) comp_scores.append(comp_score_tup['score'].black().score()) bot_scores = np.array(bot_scores) comp_scores = np.array(comp_scores) # split the games up into early, mid and late-game early_game_cutoff = 12 mid_game_cutoff = 24 bot_early, comp_early = bot_scores[:early_game_cutoff], comp_scores[:early_game_cutoff] bot_mid, comp_mid = bot_scores[early_game_cutoff:mid_game_cutoff], comp_scores[early_game_cutoff:mid_game_cutoff] bot_late, comp_late = bot_scores[mid_game_cutoff:], comp_scores[mid_game_cutoff:] assert (len(bot_early) + len(bot_mid) + len(bot_late)) == len(bot_scores) assert (len(comp_early) + len(comp_mid) + len(comp_late)) == len(comp_scores) # compute MPS across full-game and across regions full_diff = bot_scores - comp_scores early_diff = bot_early - comp_early mid_diff = bot_mid - comp_mid late_diff = bot_late - comp_late norm_full = np.mean(np.abs(comp_scores)) norm_early = np.mean(np.abs(comp_early)) norm_mid = np.mean(np.abs(comp_mid)) norm_late = np.mean(np.abs(comp_late)) MPS_full = np.mean(full_diff / norm_full) MPS_early = np.mean(early_diff / norm_early) MPS_mid = np.mean(mid_diff / norm_mid) MPS_late = np.mean(late_diff / norm_late) return (bot_scores, comp_scores, MPS_full, MPS_early, MPS_mid, MPS_late)
def coach_opinion(board, curr_move): engine = chess.engine.SimpleEngine.popen_uci("stockfish_20011801_x64.exe") curr_analyse = engine.analyse(board, chess.engine.Limit(depth=20)) curr_score = int(str(curr_analyse.score)) / 100 board.pop() prev_analyse = engine.analyse(board, chess.engine.Limit(depth=20), multipv=3) prev_score = int(str(prev_analyse[0].score)) / 100 best_move = prev_analyse[0]['pv'][0] very_good_move = prev_analyse[1]['pv'][0] good_move = prev_analyse[2]['pv'][0] result = "" final_result = "" if str(curr_move) == str(best_move): result = "Best Move!" elif str(curr_move) == str(very_good_move): result = "Very Good Move!" elif str(curr_move) == str(good_move): result = "Good Move!" else: if board.turn: if prev_score >= 0: if 0 < curr_score < prev_score: result = "Bad Move!" elif curr_score == 0: result = "Very Bad Move!" else: result = "Losing Move!" elif curr_score - prev_score >= -1.00: result = "Bad Move!" elif curr_score - prev_score >= -3.00: result = "Very Bad Move!" else: result = "Losing Move!" else: if prev_score < 0: if 0 > curr_score > prev_score: result = "Bad Move!" elif curr_score == 0: result = "Very Bad Move!" else: result = "Losing Move!" elif curr_score - prev_score <= 1.00: result = "Bad Move!" elif curr_score - prev_score <= 3.00: result = "Very Bad Move!" else: result = "Losing Move!" result += "(" + str(curr_score) + ")" final_result = result + " (The Best Move was " + str( best_move) + ", " + str( int(str(prev_analyse[0]['score'])) / 100) + " )" engine.close() board.push_uci(curr_move) return final_result
def __move_piece(self, engine, book, side): score_change = 0 moves_mate = math.inf promote_to_Q = False if len(self.moves_game) != 0: self.board.push_san(self.moves_game[-1]) if sum(1 for _ in book.find_all(self.board)) != 0: for entry in book.find_all(self.board): next_move = entry.move self.board.push(next_move) break else: if self.elapsed_time < 50: score_prior = engine.analyse(self.board, chess.engine.Limit(time=0.2)) engine_move = engine.play(self.board, chess.engine.Limit(time=0.1)) next_move = engine_move.move self.board.push(next_move) if len(str(next_move)) == 5 and str(next_move)[-1] == 'q': promote_to_Q = True if not promote_to_Q: score_after = engine.analyse(self.board, chess.engine.Limit(time=0.2)) if str(score_after["score"])[0] == '#': moves_mate = int(str(score_after["score"])[2:]) else: score_change = (int(str(score_after["score"])) - int(str(score_prior["score"]))) / 100 else: engine_move = engine.play(self.board, chess.engine.Limit(time=0.1)) next_move = engine_move.move self.board.push(next_move) start_click = str(next_move)[0:2] end_click = str(next_move)[2:4] if side == 'white': start_pos = self.pos_w[start_click] end_pos = self.pos_w[end_click] else: start_pos = self.pos_b[start_click] end_pos = self.pos_b[end_click] time.sleep( self.__wait_time(side, score_change, moves_mate, end_click, promote_to_Q)) pyautogui.click(start_pos[0], start_pos[1]) pyautogui.click(end_pos[0], end_pos[1]) if promote_to_Q: pyautogui.click(end_pos[0], end_pos[1])
def complexity(board, engine, minDepth=2, maxDepth=16): c = 0 bestMove = engine.analyse(board, chess.engine.Limit(depth=minDepth))["pv"][0] for i in range(minDepth + 2, maxDepth + 1, 2): analysis = engine.analyse(board, chess.engine.Limit(depth=i), multipv=2) if analysis[0]["pv"][0] != bestMove: bestMove = analysis[0]["pv"][0] c += analysis[0]["score"].relative.score( ) - analysis[1]["score"].relative.score() return c
def cruncher(thread_id: int): eval_nb = 0 db = pymongo.MongoClient()['puzzler'] bad_coll = db['puzzle2_bad_maybe'] play_coll = db['puzzle2_puzzle'] engine = SimpleEngine.popen_uci('./stockfish') engine.configure({'Threads': 4}) for doc in bad_coll.find({"bad": {"$exists": False}}): try: if ord(doc["_id"][4]) % threads != thread_id: continue doc = play_coll.find_one({'_id': doc['_id']}) if not doc: continue puzzle = read(doc) board = puzzle.mainline[len(puzzle.mainline) - 2].board() info = engine.analyse( board, multipv=5, limit=chess.engine.Limit(nodes=30_000_000)) bad = False for score in [pv["score"].pov(puzzle.pov) for pv in info]: if score < Mate(1) and score > Cp(250): bad = True # logger.info(puzzle.id) bad_coll.update_one({"_id": puzzle.id}, {"$set": { "bad": bad }}) except Exception as e: logger.error(e)
def run(self): log_interval = 1 engine = chess.engine.SimpleEngine.popen_uci(self.engine_path) with open(self.pgn_path) as pgn, open( os.path.splitext(self.pgn_path)[0] + '-dataset.csv', 'w', newline='') as out: outwriter = csv.writer(out) gn = 0 while game := chess.pgn.read_game(pgn): gn += 1 if gn % log_interval == 0: print('\rParsing Game #' + str(gn), end='') board = chess.Board() for move in game.mainline_moves(): board.push(move) fen = board.fen() info = engine.analyse(board, chess.engine.Limit(depth=8)) value = info['score'].white().score(mate_score=10000) if board.turn == chess.BLACK: value *= -1 value = self.normalize_cp(value) outwriter.writerow([fen, value])
def compute_score_a_by_depth(engine, board, depth): info = engine.analyse(board, chess.engine.Limit(depth=depth), info=Info.ALL) if info.get("score"): score = info.get("score").white().score() else: score = 0 return score
def compute_score_a(engine, board, time): info = engine.analyse(board, chess.engine.Limit(time=time), info=Info.ALL) if info.get("score"): score = info.get("score").white().score() else: score = 0 return score
def game_analyzer(flat_list = ['rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1']): current_fen = [] player = [] move = [] piece = [] score = [] mate = [] for fen in flat_list: board = chess.Board(fen) for el in board.legal_moves: info = engine.analyse(board, chess.engine.Limit(time=.1), root_moves=[el]) t = str(info["score"]) price = re.search('-?\d+\.?\d*',t)[0] current_fen.append(fen) move.append(str(board.san(el))) score.append(round(int(price)/100.,2)) mate.append(str(board.san(el)).endswith('+')) if "BLACK" in str(info["score"]): player.append("BLACK") else: player.append("WHITE") if len(board.san(el).replace("x","").replace("+","")) == 2: piece.append("P") else: piece.append(board.san(el).replace("x","").replace("+","")[0].upper()) # engine.quit() df2 = pd.DataFrame({'FEN':current_fen,'Player':player, 'Move':move, 'Score':score, 'Mate':mate}) return df2
def stockfish_info(fen, move, engine, depth, multipv=None): board = chess.Board(fen) move = [chess.Move.from_uci(move)] if move else None return engine.analyse(board, root_moves=move, multipv=multipv, limit=chess.engine.Limit(depth=depth))
def parser(row): (fen,) = row board = chess.Board(fen=fen) ev = engine.analyse(board, chess.engine.Limit(depth=depth)) if not 'pv' in ev: return None score = ev['score'].white() pv = ev['pv'][0].uci() if isinstance(score, chess.engine.Mate) or isinstance(score, chess.engine.MateGivenType): y = None # Ignore mates else: y = float(str(score)) if not y: return None arr = [] posx = parsef(board) moves = [] for move in board.legal_moves: if pv != move.uci(): moves.append(move.uci()) if not moves: return None mx = parsemove(board, chess.Move.from_uci(pv)) arr.append(((*posx, mx), 1)) mx = parsemove(board, chess.Move.from_uci(random.choice(moves))) arr.append(((*posx, mx), 0)) return arr
def find_mate(boardFEN): #Should only be used if mate is possible board = chess.Board(boardFEN) engine = chess.engine.SimpleEngine.popen_uci(engine_name("stockfish")) info = engine.analyse(board, chess.engine.Limit( depth=20)) #Limit the possible checkmate found to be under 20 moves board_states = [] #List of every board state in FEN notation during mate board_states.append(boardFEN) game = chess.pgn.Game() game.setup(boardFEN) x = 0 if not (info["score"].is_mate()): engine.quit() return None #The score given is not a mate, therefore, the function cannot return a mate. Therefore, it returns None for i in info["pv"]: #Loop through every move and add them to the lists if x == 0: node = game.add_main_variation( i) #If first loop, create the main line found by engine x = x + 1 else: node = node.add_main_variation( i) #Add to the main line if not first loop board.push(i) board_states.append( board.fen()) #Add the FEN board state after the move was made engine.quit() return game
def get_raw_analysis(engine, board, analysis_limit): """ Analyze a position with Stockfish. :param engine: a SimpleEngine object, probably from load_engine() :param board: a python-chess Board object :param analysis_limit: a chess.engine.Limit object describing how deeply to read :return: raw output from engine analysis: list of dict objects like { 'depth': 14, 'seldepth': 27, 'multipv': 1, 'score': PovScore(Cp(-20), WHITE), 'nodes': 3702548, 'nps': 1161765, 'tbhits': 0, 'time': 3.187, 'pv': [Move.from_uci('b1c3'), ... Move.from_uci('g1f1')] } Note: analysis limit can be in seconds: chess.engine.Limit(time=3) or in search depth: chess.engine.Limit(depth=14) """ # Run analysis for all possible variations info = engine.analyse(board, analysis_limit, multipv=500) return info
def get_optimality_scores(board, engine): # Dict with results scores = {} # Get scores for given board for el in board.legal_moves: score_info = engine.analyse(board, chess.engine.Limit(time=.01), root_moves=[el])["score"] # Calculate score score = 0.0 if (board.turn): score = score_info.white().score(mate_score=1000000) if (score_info.white().is_mate()): score -= score_info.white().mate() * 100 else: score = score_info.black().score(mate_score=1000000) if (score_info.black().is_mate()): score -= score_info.black().mate() * 100 score = round(score / 100, 2) # Store in dict scores[el] = score # Return scores return scores
def pick_move(root): ''' root is a MoveNode Pick a move that is best to play for self.player ''' # Two cases: # Either the "root" is a leaf, i.e depth = 0 if root.depth == 0: # Here there is no decision to make, just return the move and the engine's evaluation score = engine.analyse( root.board, chess.engine.Limit(time=ANALYSIS_TIME))["score"] return (score, root) else: # The "root" is somewhere else in the tree, in which case it will choose the best child. # resolve all the children, and take the best move opt_child = root.children[ 0] # know it has kids because depth is non-0 opt_score = MoveSelector.pick_move(opt_child)[0] for i in range(1, len(root.children)): child = root.children[i] result = MoveSelector.pick_move(child) if MoveSelector.compare_scores(result[0], opt_score, root.player): opt_score = result[0] opt_child = result[1] return (opt_score, opt_child)
def main(): train_data = [] for x in range(num_games): print("Starting game:" + str(x)) board = chess.Board() while not board.is_game_over() and board.fullmove_number < 20: legal_moves = board.legal_moves data = [] if board.turn == chess.BLACK: for move in legal_moves: board.push(move) # Estimate moves info = engine.analyse( board, chess.engine.Limit(time=0.0100, depth=6)) board.pop() data.append((move, str(info["score"]))) train_data.append(([to_X(board)], to_Y(data))) # Make best move result = engine.play(board, chess.engine.Limit(time=0.100)) board.push(result.move) # Make random move # board.push(random.choice(data)[0]) if (x + 1) % 96 == 0: train_data_formatted = split_data(train_data) model.fit([train_data_formatted[0]], [train_data_formatted[1]], epochs=1050, batch_size=32, callbacks=callbacks, verbose=2) train_data = [] model.save("checkpoints/" + model_name + datetime.now().strftime("D%d-H%H-M%M") + ".h5") model.save("models/" + model_name + ".h5") engine.quit()
def AI_move(): """ #get move from AI board.export_fen() """ #move = random.choice(board.get_valid_moves()) print(board.export_fen()) chess_board = chess.Board(board.export_fen()) print(chess.Board()) print(chess_board) try: move = board.move_from_san( engine.analyse(chess_board, chess.engine.Limit(time=.1))["pv"][0]) except: # while this block can definitely be reached on not moving the king out of danger, # there may be other, unintended ways to get here that I do not fully understand print("stupid move") return board.to_play #move = AI_board.search()[0] if move[2] == 6: print("King captured") return board.to_play else: won = None AI_board.make_move(move) board.make_move(move) AI_board.evaluate()
def getEngineEval(): if token.validate_token(request.args.get('token', False)): __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) __filename__ = 'stockfish-prod' if os.environ.get( "Production", False) else 'stockfish-dev' # Assign game Position with chess.engine.SimpleEngine.popen_uci( os.path.join(__location__, __filename__)) as engine: try: fen_notation = request.args.get('fen_notation', "") board = chess.Board(fen_notation) except ValueError: return jsonify({ "fen_notation_error": "Please supply a valid fen notation", }) strength = request.args.get('strength', 1) time = min(1, 0.01 * 10**int(strength)) res = engine.play(board, chess.engine.Limit(time=time, depth=20)) info = engine.analyse(board, chess.engine.Limit(time=time, depth=20)) engine.close() return jsonify({ "best_move": str(res.move), "ponder": str(res.ponder), "evaluation": str(info["score"].white()), }) return jsonify({ "access_token_error": "There seems to be a problem with your access token", })
def make_move(): # extract FEN string from HTTP POST request body fen = request.form.get('fen') # init python chess board instance board = chess.Board(fen) # search for best move info = engine.analyse(board, chess.engine.Limit(time=0.1)) # extract best move from PV best_move = info['pv'][0] # update internal python chess board state board.push(best_move) # extract FEN from current board state fen = board.fen() return { 'fen': fen, 'best_move': str(best_move), 'score': str(info['score']), 'pv': ' '.join([str(move) for move in info['pv']]), 'nodes': info['nodes'], 'time': info['time'] }
def get(self): ret_dict = {} parser = reqparse.RequestParser() parser.add_argument('fen', type=str) parser.add_argument('svg', type=str) args = parser.parse_args() engine = chess.engine.SimpleEngine.popen_uci(stockfish_path) board = chess.Board(fen=args['fen']) info = engine.analyse(board, chess.engine.Limit(time=0.100)) next_move_limit = chess.engine.Limit(time=2.0) next_move = engine.play(board, next_move_limit) engine.quit() ret_dict = { 'current_score': str(info["score"]), 'next_move_suggestion': str(next_move.move), 'next_move_suggestions': str(next_move.info), 'board_before': str(board), } if ('svg' in args and args['svg'] == '1'): ret_dict['svg'] = chess.svg.board(board=board, lastmove=next_move.move) return ret_dict
def cruncher(thread_id: int): eval_nb = 0 build_coll = pymongo.MongoClient()['puzzler']['puzzle2'] engine = SimpleEngine.popen_uci('./stockfish') engine.configure({'Threads': 4}) engine_limit = chess.engine.Limit(depth=30, time=15, nodes=12_000_000) for doc in build_coll.find({"cp": None}): try: if ord(doc["_id"][4]) % threads != thread_id: continue puzzle = read(doc) board = puzzle.game.end().board() if board.is_checkmate(): cp = 999999999 eval_nb += 1 logger.info(f'{thread_id} {eval_nb} {puzzle.id}: mate') else: info = engine.analyse(board, limit=engine_limit) score = info["score"].pov(puzzle.pov) score_cp = score.score() cp = 999999999 if score.is_mate() else ( 999999998 if score_cp is None else score_cp) eval_nb += 1 logger.info( f'{thread_id} {eval_nb} {puzzle.id}: {cp} knps: {int(info["nps"] / 1000)} kn: {int(info["nodes"] / 1000)} depth: {info["depth"]} time: {info["time"]}' ) build_coll.update_one({"_id": puzzle.id}, {"$set": { "cp": cp }}) except Exception as e: print(doc) logger.error(e)
def make_move(): # extract FEN string from HTTP POST request body fen = request.form.get('fen') # extract fixed depth value fixed_depth = request.form.get('fixed_depth') # extract move time value move_time = request.form.get('move_time') # init python chess board instance board = chess.Board(fen) # if move time is available if move_time != '0': if move_time == 'instant': # search for best move instantly info = engine.analyse(board, chess.engine.Limit(time=0.1)) else: # search for best move with fixed move time info = engine.analyse(board, chess.engine.Limit(time=int(move_time))) # if fixed depth is available if fixed_depth != '0': # search for best move instantly info = engine.analyse(board, chess.engine.Limit(depth=int(fixed_depth))) # extract best move from PV best_move = info['pv'][0] # update internal python chess board state board.push(best_move) # extract FEN from current board state fen = board.fen() return { 'fen': fen, 'best_move': str(best_move), 'score': str(info['score']), 'depth': info['depth'], 'pv': ' '.join([str(move) for move in info['pv']]), 'nodes': info['nodes'], 'time': info['time'] }
def run(self, reduced: Callable, square_filter: Callable, echo: int = 1, depth: int = 20) -> pd.DataFrame: engine = self.engine_maker() result = pd.DataFrame(columns=[ 'SideThatMoved', 'SideToMove', 'Score', 'PovScore', 'HalfMoveConfiguration', 'FEN', 'LastWhiteMove', 'LastBlackMove', 'ViewForWhite', 'ViewForBlack', 'Zeros', 'ZerosWithEcho' ]) for curr_move in range(0, len(self.moves)): bm_offset = (curr_move + 1) % 2 adjusted_wm = (curr_move // 2) * 2 adjusted_bm = curr_move - bm_offset side = bool( bm_offset) # white: 1, black: 0, same as in python-chess if not self.first_move_by_white: adjusted_wm, adjusted_bm = adjusted_bm, adjusted_wm # not tested! last_white_move = self.moves[adjusted_wm] last_black_move = self.moves[ adjusted_bm] if adjusted_bm > -1 else None white = self.msw(range(adjusted_wm - (echo * 2), adjusted_wm + 1), chess.WHITE, square_filter) black = self.msb(range(adjusted_bm - (echo * 2), adjusted_bm + 1), chess.BLACK, square_filter) zeros_white = reduced( self.msz(range(curr_move, curr_move + 1), chess.WHITE, square_filter).result) zeros_black = reduced( self.msz(range(curr_move, curr_move + 1), chess.BLACK, square_filter).result) reduced_white = reduced(white.result) reduced_black = reduced(black.result) # would be nice if engine.analyse could be made to run async/in parallel current_board = white.board if side else black.board score = engine.analyse(current_board, chess.engine.Limit(depth=depth))['score'] result.loc[curr_move] = [ 'White' if side else 'Black', 'Black' if side else 'White', int(score.pov(chess.WHITE).__str__()), int(score.pov(score.turn).__str__()), (curr_move, adjusted_wm, adjusted_bm), current_board.fen(), last_white_move, last_black_move, reduced_white, reduced_black, cda.countZeros(np.logical_or(zeros_white, zeros_black)), cda.countZeros(np.logical_or(reduced_white, reduced_black)) ] engine.quit( ) # This is important, otherwise we leak opened file objects! return result
def stockfish(board, pov): scores = [ engine.analyse(board, chess.engine.Limit( depth=d))["score"].pov(pov).score(mate_score=10000) for d in (5, 10, 15) ] return sum(a * b for a, b in zip(scores, (3, 2, 1)))
def evalBoard(board): global count print(count) count += 1 k = engine.analyse( board, chess.engine.Limit(time=0.05))['score'].relative.__str__() print(k) return k
def compute_best_move_score(engine, board, move, time): board.push(move) info = engine.analyse(board=board, limit=chess.engine.Limit(time=time), info=Info.ALL) board.pop() score = info.get('score').white().score() if score is None: score = 0 return score
def get_engine_moves(fen, engine): engine_moves = [] board = chess.Board(fen) info = engine.analyse(board, chess.engine.Limit(depth=15), multipv=3) for i in range(len(info)): engine_moves.append(board.san(info[i]['pv'][0])) return engine_moves
def evalBoard(board): global count print(count) count += 1 k = engine.analyse( board, chess.engine.Limit(time=0.1))['score'].relative.__str__() k = replaceForcedMate(k) return k
def analyse_position(eval_cursor, eval_board, root_moves=None, extended=False): """ Commit analysis to DB. Can confine to given root moves, or search for new moves with extended=True """ if root_moves: time_limit = chess.engine.Limit(time=min(5, max(2, len(root_moves)))) lines = engine.analyse(eval_board, time_limit, root_moves=root_moves, multipv=len(root_moves)) else: if extended: taken_ucis = list( map(lambda m: m['uci'], eval_cursor['theory'] + eval_cursor['moves'])) scout_lines = engine.analyse(eval_board, chess.engine.Limit(time=0.3), multipv=len(taken_ucis) + 3) scouted_moves = list(map(lambda line: line['pv'][0], scout_lines)) new_moves = [m for m in scouted_moves if m.uci() not in taken_ucis] lines = engine.analyse(eval_board, chess.engine.Limit(time=3), root_moves=new_moves, multipv=len(new_moves)) else: lines = engine.analyse(eval_board, chess.engine.Limit(time=2), multipv=3) uci_score_dict = { line['pv'][0].uci(): line['score'].relative.score(mate_score=100000) for line in lines } print(uci_score_dict) if any([v is None for v in uci_score_dict.values()]): print('Somehow, some score is None') print(uci_score_dict) print(lines) if eval_cursor['score'] is None: print('will crash now') print(eval_cursor) print(eval_board.move_stack) set_position_moves_scores(eval_cursor, uci_score_dict)