Analyze_game fonksiyonu yavaş çalışıyor

lama2923

Centipat
Katılım
16 Temmuz 2023
Mesajlar
20
Daha fazla  
Cinsiyet
Diğer
Biliyorum kodun okunaklığı ve anlaşılabilirliği zor. Sizden en baştan bir kod vermenizi istemiyorum, bana fikir verseniz bile yeter. 1 haftadır bu şeyle ilgileniyorum…
Sorun şu: Analyze_game fonksiyonu gerçekten çok yavaş çalışıyor. Bunu çözmek için önerileriniz nedir?
Multiprocessing denedim ama başaramadım, benim beceriksizliğim sanırım.
(yavaş çalışan kısım stocfish.get_top_moves kısmı)




Kod:
import chess
import chess.pgn
from stockfish import Stockfish
from lama2923 import clear_screen, llinput
import datetime
import time
import os

# TerminalCHESS - Stockfish * lama2923

class ChessGame:
    def init(self, stockfish_path, stockfish_depth=12):
        """
        INIT
        """
        self.stockfish_depth = stockfish_depth
        self.language = self.select_language()
        self.messages = self.define_messages()
        self.GAME_MODE = int(self.select_game_mode()) # 0: AI vs AI, 1: Player vs AI, 2: Player vs Player
        self.stockfish = Stockfish(path=stockfish_path)
        self.stockfish.set_depth(stockfish_depth)
        self.board = None
        self.move_history = []
        if self.GAME_MODE == 0:
            self.white_elo = self.get_elo(f" ({self.get_message('white')})")
            self.black_elo = self.get_elo(f" ({self.get_message('black')})")
        elif self.GAME_MODE == 2:
            self.white_elo = 0
            self.black_elo = 0
        else:
            if int(self.select_color()) == 1:
                self.white_elo = self.get_elo(f" ({self.get_message('white')})")
                self.black_elo = 0
            else:
                self.white_elo = 0
                self.black_elo = self.get_elo(f" ({self.get_message('black')})")
        self.unicode_pieces = self.define_unicode_pieces()
        self.FEN = None
        self.game_end = False
        self.move_INDEX = 0
        self.analyz_history = {}
        self.move_analysis = {}
        self.stockfish_history = {}
    
    def str(self):
        return self.move_history
 
    def repr(self):
        return self.move_history, self.FEN, self.white_elo, self.black_elo, self.GAME_MODE, self.stockfish_depth
 
    def select_language(self):
        """
        Dil seçmeye yarar.
    
        -----------------------
    
        Used to select language.
        """
        all_languages = list(self.define_messages().keys())
        chars = set()
        for lang in all_languages:
            for c in lang:
                chars.add(c)
        lang_choice = llinput("Select Language: ", wend=" (" + "/".join(all_languages) + ")", choices=(all_languages, False), availablechars=r"".join(chars), forceinput=True, max_length=2, min_length=2)
        return lang_choice
 
    def select_game_mode(self):
        """
        Oyun modunu seçmeye yarar.
    
        -----------------------
    
        Used to select game mode.
        """
        game_modes = {"0": "AI vs AI", "1": "Player vs AI", "2": "Player vs Player"}
        wend_text = "   "
        for key, value in game_modes.items():
            wend_text += f"      |      {key}: {value}"
        game_mode_choice = llinput(self.get_message("SGM"), wend=wend_text, choices=(list(game_modes.keys()), False), forceinput=True, max_length=1, min_length=1)
        return game_mode_choice
    def select_color(self):
        """
        Oyuncunun rengini seçmeye yarar
        -----------------------
        Used to select player's color
        """
        colors = {"0": "White", "1": "Black"}
        wend_text = "   "
        for key, value in colors.items():
            wend_text += f"      |      {key}: {value}"
        color_choice = llinput(self.get_message("SYC"), wend=wend_text, choices=(list(colors.keys()), False), forceinput=True, max_length=1, min_length=1)
        return color_choice
    def define_messages(self):
        return {
            "en": {
                "SYC": "Select Your Color: ",
                "SGM": "Select Game Mode: ",
                "white": "White",
                "black": "Black",
                "yes": "y",
                "no": "n",
                "enter_elo": "Enter ELO rating: ",
                "fen_prompt": "Enter FEN position: ",
                "use_custom_fen": "Would you like to use a custom FEN?: ",
                "game_over": "Game over!",
                "winner": "Checkmate! Winner: ",
                "draw": "The game ended in a draw!",
                "white_turn": "Turn: White",
                "black_turn": "Turn: Black",
                "white_captured": "Captured by White: ",
                "black_captured": "Captured by Black: ",
                "white_elo": "White's ELO",
                "black_elo": "Black's ELO",
                "fen": "FEN: ",
                "pgn_saved": "Game history saved as PGN."
            },
            "tr": {
                "SYC": "Renginizi Seçin: ",
                "SGM": "Oyun Modunu Seçin: ",
                "white": "Beyaz",
                "black": "Siyah",
                "yes": "e",
                "no": "h",
                "enter_elo": "ELO derecesini girin: ",
                "fen_prompt": "FEN pozisyonunu girin: ",
                "use_custom_fen": "Özel bir FEN kullanmak istiyor musunuz?: ",
                "game_over": "Oyun bitti!",
                "winner": "Şah mat! Kazanan: ",
                "draw": "Oyun berabere sona erdi!",
                "white_turn": "Sıra: Beyaz",
                "black_turn": "Sıra: Siyah",
                "white_captured": "Beyaz tarafından alınan taşlar: ",
                "black_captured": "Siyah tarafından alınan taşlar: ",
                "white_elo": "Beyaz'ın ELO puanı: ",
                "black_elo": "Siyah'ın ELO puanı: ",
                "fen": "FEN: ",
                "pgn_saved": "Oyun geçmişi PGN formatında kaydedildi."
            },
            "es": {
                "SYC": "Selecciona tu color: ",
                "SGM": "Selecciona el modo de juego: ",
                "white": "Blanco",
                "black": "Negro",
                "yes": "s",
                "no": "n",
                "enter_elo": "Introduce tu puntuación ELO: ",
                "fen_prompt": "Introduce la posición FEN: ",
                "use_custom_fen": "¿Quieres usar una FEN personalizada?: ",
                "game_over": "¡Juego terminado!",
                "winner": "¡Jaque mate! Ganador: ",
                "draw": "¡El juego terminó en empate!",
                "white_turn": "Turno: Blanco",
                "black_turn": "Turno: Negro",
                "white_captured": "Piezas capturadas por Blanco: ",
                "black_captured": "Piezas capturadas por Negro: ",
                "white_elo": "ELO de Blanco: ",
                "black_elo": "ELO de Negro: ",
                "fen": "FEN: ",
                "pgn_saved": "Historial del juego guardado como PGN."
            },
            "fr": {
                "SYC": "Choisissez votre couleur : ",
                "SGM": "Choisissez le mode de jeu : ",
                "white": "Blanc",
                "black": "Noir",
                "yes": "o",
                "no": "n",
                "enter_elo": "Entrez votre classement ELO : ",
                "fen_prompt": "Entrez la position FEN : ",
                "use_custom_fen": "Voulez-vous utiliser un FEN personnalisé ? : ",
                "game_over": "Partie terminée !",
                "winner": "Échec et mat ! Gagnant : ",
                "draw": "La partie s'est terminée par un match nul !",
                "white_turn": "Tour : Blanc",
                "black_turn": "Tour : Noir",
                "white_captured": "Pièces capturées par Blanc : ",
                "black_captured": "Pièces capturées par Noir : ",
                "white_elo": "ELO de Blanc ",
                "black_elo": "ELO de Noir ",
                "fen": "FEN ",
                "pgn_saved": "Historique de la partie sauvegardé en tant que PGN."
            },
            "de": {
                "SYC": "Wähle deine Farbe: ",
                "SGM": "Wähle den Spielmodus: ",
                "white": "Weiß",
                "black": "Schwarz",
                "yes": "j",
                "no": "n",
                "enter_elo": "Gib deine ELO-Bewertung ein: ",
                "fen_prompt": "Gib die FEN-Position ein: ",
                "use_custom_fen": "Möchten Sie eine benutzerdefinierte FEN verwenden?: ",
                "game_over": "Spiel vorbei!",
                "winner": "Schachmatt! Gewinner: ",
                "draw": "Das Spiel endete unentschieden!",
                "white_turn": "Zug: Weiß",
                "black_turn": "Zug: Schwarz",
                "white_captured": "Gefangene Figuren von Weiß: ",
                "black_captured": "Gefangene Figuren von Schwarz: ",
                "white_elo": "ELO von Weiß: ",
                "black_elo": "ELO von Schwarz: ",
                "fen": "FEN: ",
                "pgn_saved": "Spielverlauf als PGN gespeichert."
            }
        }
    def get_message(self, key):
        """
        Kullanıcın önceden seçtiği dile göre mesaj döndürür.
    
        -----------------------
    
        Returns message according to the language selected by the user.
        """
        return self.messages[self.language].get(key, "")
    def get_elo(self, wend=""):
        """
        ELO puanını alır.
        -----------------------
        Gets the ELO rating.
        """
        def enter_check(input_str):
            if int(input_str) != 0:
                return True
            return False
        return int(llinput(self.get_message("enter_elo"), wend=wend, forceint=True, forceinput=True, min_length=1, max_length=4, custom_enter_check_func=enter_check))
    def set_board(self):
        """
        Tahtayı Oluşturur.
        -----------------------
        Creates the board.
        """
        if llinput(self.get_message("use_custom_fen"), wend=f" ({self.get_message("yes")}/{self.get_message("no")})", forceinput=True, choices=([self.get_message("yes"), self.get_message("no")], False), max_length=1, min_length=1).lower() == self.get_message("yes"):
            self.FEN = self.custom_fen()
            self.board = chess.Board(self.FEN)
        else:
            self.FEN = chess.Board().fen()
            self.board = chess.Board()
    
    def custom_fen(self):
        """
        Custom FEN Almaya yarar.
        -----------------------
        Used to get custom FEN.
        """
        def check_fen(fen):
            try:
                chess.Board(fen)
                return True
            except ValueError:
                return False
        return llinput(self.get_message("fen_prompt"), forceinput=True, min_length=1, max_length=71, custom_enter_check_func=check_fen)
    def define_unicode_pieces(self):
        return {
            "P": "\033[1m♟︎\033[0m", "N": "\033[1m♞\033[0m", "B": "\033[1m♝\033[0m",
            "R": "\033[1m♜\033[0m", "Q": "\033[1m♛\033[0m", "K": "\033[1m♚\033[0m",
            "p": "\033[1m♙\033[0m", "n": "\033[1m♘\033[0m", "b": "\033[1m♗\033[0m",
            "r": "\033[1m♖\033[0m", "q": "\033[1m♕\033[0m", "k": "\033[1m♔\033[0m"
        }
    
    def print_board(self, move_index=None, reverse_board=False):
        """
        Tahtayı konsola çıktılar. Ayrıca oyun oynanırken stockfish değerlendirmesi yapar.
            move_index: Hangi hamlenin gösterileceğini belirler.
            reverse_board: Tahtayı ters çevirir.
        ------------------------------------------
        Prints the board to the console. Also evaluates the board while playing the game.
        
                move_index: Determines which move will be displayed.
                reverse_board: Reverses the board.
        """
        self.stockfish.set_depth(self.stockfish_depth)
        if self.board:
            text = ""
            if move_index is None:
                move_index = len(self.move_history)
        
            if move_index > len(self.move_history):
                move_index = len(self.move_history)
        
            new_board = chess.Board(self.FEN)
            for i in range(move_index):
                new_board.push_uci(self.move_history)
          
            if self.stockfish_history.get(new_board.fen()) is None:
                stockfish = Stockfish()
                stockfish.set_fen_position(new_board.fen())
                stockfish.set_depth(self.stockfish_depth)
                self.stockfish_history[new_board.fen()] = stockfish.get_evaluation()
            stockfish_evaluation = self.stockfish_history[new_board.fen()]
            eval_display = ""
            if stockfish_evaluation["type"] == "mate":
                eval_display = f"# {'+' if stockfish_evaluation['value'] > 0 else '-'}{abs(stockfish_evaluation['value'])}"
            else:
                eval_score = stockfish_evaluation["value"]
                eval_score = max(min(eval_score, 1000), -1000)
                eval_display = f"{eval_score / 100:+.2f}"
            initial_counts = {chess.PAWN: 8, chess.KNIGHT: 2, chess.BISHOP: 2, chess.ROOK: 2, chess.QUEEN: 1}
            piece_values = {chess.PAWN: 1, chess.KNIGHT: 3, chess.BISHOP: 3, chess.ROOK: 5, chess.QUEEN: 9}
            white_captured, black_captured = [], []
            material_difference = 0
            for piece_type, count in initial_counts.items():
                white_on_board = len(new_board.pieces(piece_type, chess.WHITE))
                black_on_board = len(new_board.pieces(piece_type, chess.BLACK))
                white_captured_count = count - white_on_board
                black_captured_count = count - black_on_board
                material_difference += (white_captured_count - black_captured_count) * piece_values[piece_type]
                white_captured.extend([self.unicode_pieces[chess.Piece(piece_type, chess.BLACK).symbol()]] * black_captured_count)
                black_captured.extend([self.unicode_pieces[chess.Piece(piece_type, chess.WHITE).symbol()]] * white_captured_count)
            white_score = abs(material_difference) if material_difference < 0 else 0
            black_score = abs(material_difference) if material_difference > 0 else 0


            board_lines = [
                "\n     a   b   c   d   e   f   g   h",
                "   ┌───┬───┬───┬───┬───┬───┬───┬───┐"
            ]
            if reverse_board:
                for rank in range(8):
                    row = f" {rank + 1} │"
                    for file in range(8):
                        piece = new_board.piece_at(chess.square(file, rank))
                        row += f" {self.unicode_pieces.get(piece.symbol(), ' ')} │" if piece else "   │"
                    board_lines.append(row)
                    if rank < 7:
                        board_lines.append("   ├───┼───┼───┼───┼───┼───┼───┼───┤")
            else:
                for rank in range(7, -1, -1):
                    row = f" {rank + 1} │"
                    for file in range(8):
                        piece = new_board.piece_at(chess.square(file, rank))
                        row += f" {self.unicode_pieces.get(piece.symbol(), ' ')} │" if piece else "   │"
                    board_lines.append(row)
                    if rank > 0:
                        board_lines.append("   ├───┼───┼───┼───┼───┼───┼───┼───┤")
            board_lines.append("   └───┴───┴───┴───┴───┴───┴───┴───┘\n")
            move_columns = []
            num_moves = len(self.move_history)
            for start in range(0, num_moves, 32):
                column = []
                for i in range(32):
                    index = start + i
                    if index >= num_moves:
                        break
                    move_number = index // 2 + 1
                    if index % 2 == 0:
                        if index == move_index - 1:
                            column.append(f"{move_number:>2}. {self.move_history[index]} <--")
                        else:
                            column.append(f"{move_number:>2}. {self.move_history[index]}")
                    else:
                        if index == move_index - 1:
                            column[-1] += f" {self.move_history[index]} <--"
                        else:
                            column[-1] += f" {self.move_history[index]}"
                move_columns.append(column)
            max_rows = max(len(board_lines), max((len(col) for col in move_columns), default=0))
            clear_screen()
            if self.game_end:
                text += f"Eval ({self.stockfish.depth}): {eval_display}\n"
                evalanalyz = self.analyz_history.get(new_board.fen())
                if evalanalyz is not None:
                    evalanalyzdisplay = ""
                    if evalanalyz["type"] == "mate":
                        evalanalyzdisplay = f"# {'+' if evalanalyz['value'] > 0 else '-'}{abs(evalanalyz['value'])}"
                    else:
                        eval_score20 = evalanalyz["value"]
                        eval_score20 = max(min(eval_score20, 1000), -1000)
                        evalanalyzdisplay = f"{eval_score20 / 100:+.2f}"
                else:
                    evalanalyzdisplay = evalanalyz
                if evalanalyzdisplay is not None:
                    text += f"Eval (analyz): {evalanalyzdisplay}\n"
                    for key, value in self.move_analysis[str("black" if new_board.turn == chess.WHITE else "white")].items():
                        if new_board.fen() in value:
                            text += f"Move Category (analyz): {key}\n"
                            break
            else:
                if self.GAME_MODE == 0:
                    text += f"Eval: {eval_display}\n"
            for row in range(max_rows):
                board_part = board_lines[row] if row < len(board_lines) else ""
              
                if row == 0:
                    text += f"{board_part:<35}\n"
                else:
                    move_row = row - 1
                    columns_part = " | ".join(
                        move_columns[col][move_row] if move_row < len(move_columns[col]) else ""
                        for col in range(len(move_columns))
                    ).strip(" |")
                    text += f"{board_part:<35} | {columns_part}\n" if columns_part else f"{board_part}\n"
          
            text += f"{self.get_message('white_captured')} {' '.join(white_captured)} {'+{}'.format(white_score) if white_score > 0 else ''}\n"
            text += f"{self.get_message('black_captured')} {' '.join(black_captured)} {'+{}'.format(black_score) if black_score > 0 else ''}\n\n"

            text += f"{self.get_message('white_turn') if new_board.turn == chess.WHITE else self.get_message('black_turn')}\n"
            text += f"{self.get_message('white_elo')}: {self.white_elo}  {self.get_message('black_elo')}: {self.black_elo}\n"
            text += f"{self.get_message('fen')}: {new_board.fen()}\n"
            print(text, flush=True)
    def save_pgn(self):
        """
        Oyun Rotasyonunu PGN formatında kaydeder.
        -----------------------
        Saves the game rotation in PGN format.
        """
        if self.board and self.move_history:
            game = chess.pgn.Game()
            game.headers["Event"] = f"Stockfish Game (Depth {self.stockfish.depth})"
            game.headers["White"] = f"Stockfish (ELO {self.white_elo})"
            game.headers["Black"] = f"Stockfish (ELO {self.black_elo})"
            game.headers["Result"] = self.board.result()
            game.headers["Date"] = datetime.datetime.now().strftime("%Y.%m.%d")
            game.headers["FEN"] = self.FEN
            board_position = chess.Board(self.FEN)
            node = game
            for move_uci in self.move_history:
                move = chess.Move.from_uci(move_uci)
                if move in board_position.legal_moves:
                    node = node.add_variation(move)
                    board_position.push(move)
                else:
                    print(f"Illegal move: {move_uci}")
            with open("game_history.pgn", "w") as pgn_file:
                print(game, file=pgn_file)
            print(self.get_message("pgn_saved"))
    def is_playable_position(self):
        """
        True: oynanabilir durum
        False: oynanamaz durum
        -----------------------
        True: playable position
        False: unplayable position
        """
        board = self.board.copy()
        stockfish = self.stockfish
        stockfish.set_fen_position(board.fen())
        evaluation = stockfish.get_evaluation()
        if evaluation["type"] == "cp":
            value = evaluation["value"]
            if (board.turn == chess.BLACK and value >= -500) or (board.turn == chess.WHITE and value <= 500):
                return True # oynanabilir durum. Unplayable position.
              
            else:
                return False  # Oynanamaz durum. Unplayable position.
      
        elif evaluation["type"] == "mate":
            if board.turn == chess.WHITE and evaluation["value"] < 0:
                return True # Oynanabilir durum. Unplayable position.
          
            elif board.turn == chess.BLACK and evaluation["value"] > 0:
                return True # Oynanabilir durum. Unplayable position.
          
            else:
                return False # Oynanamaz durum. Unplayable position.
        else:
            return False # Oynanamaz durum. Unplayable position.
      
    def is_draw(self):
        """
        True: Berabere
        False: Berabere değil
        -----------------------
        True: Draw
        False: Not draw
        """
        board = self.board
        return any([board.is_stalemate(), board.is_insufficient_material(), board.is_seventyfive_moves(), board.is_fivefold_repetition(), board.is_variant_draw()])
    def get_skill_level(self, elo_rating):
        """
        ELO puanı ile orantılı bir şekilde skill_level'i belirler.
        -----------------------
        Determines the skill_level proportionally with the ELO rating.
        """
        return max(0, min(20, int(elo_rating) // 50))
  
    def get_depth_level(self, elo_rating):
        """
        ELO puanı ile orantılı bir şekilde depth_level'i belirler.
        -----------------------
        Determines the depth_level proportionally with the ELO rating.
        """
        return max(1, min(int(self.stockfish.depth), int(elo_rating) // 100))
    def get_key(self):
        """
        sadece rewiev_game fonksiyonunda çağırılır. Arrow key'leri algılar ve döndürür.
        -----------------------
        Called only in the rewiev_game function. Detects and returns the arrow keys.
        """


        if os.name == "nt":  # Windows
            import msvcrt
            key = msvcrt.getch()
            if key in [b"\xe0", b"\x00"]:  # Ok tuşları. Arrow keys.
                key = msvcrt.getch()
                if key == b"H":
                    return "UP"
                elif key == b"P":
                    return "DOWN"
                elif key == b"M":
                    return "RIGHT"
                elif key == b"K":
                    return "LEFT"
          
        else:  # Linux/Unix
            import termios
            import tty
            import sys
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(fd)
                key = sys.stdin.read(1)
                if key == "\x1b":
                    key = sys.stdin.read(2)
                    if key == "[A":
                        return "UP"
                    elif key == "[B":
                        return "DOWN"
                    elif key == "[C":
                        return "RIGHT"
                    elif key == "[D":
                        return "LEFT"
              
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return None
  
    def rewiev_game(self):
        """
        Oyun bittiğinde incelemeye yarar.
        -----------------------
        Used to review the game when the game ends.
        """
        while True:
            if self.game_end:
                key = self.get_key()
                if key == "UP":
                    self.move_INDEX = len(self.move_history)
                elif key == "DOWN":
                    self.move_INDEX = 0
                elif key == "LEFT":
                    self.move_INDEX = self.move_INDEX - 1 if self.move_INDEX > 0 else 0
                elif key == "RIGHT":
                    if self.move_INDEX < len(self.move_history):
                        self.move_INDEX += 1
              
                self.print_board(self.move_INDEX)
    def analyze_game(self):
        """
        Oyunu analiz eder.
        -----------------------
        Analyzes the game.
        """
        board = chess.Board(self.FEN)
        stockfish = Stockfish()
        self.stockfish.set_depth(self.stockfish_depth)
        stockfish.set_depth(20)
        analyzed_moves = 0
        move_analysis = {
            "white": {
                "brilliant": [],
                "best": [],
                "excellent": [],
                "good": [],
                "inaccuracy": [],
                "mistake": [],
                "blunder": [],
                "missed_win": [],
                "unknown": [],
            },
            "black": {
                "brilliant": [],
                "best": [],
                "excellent": [],
                "good": [],
                "inaccuracy": [],
                "mistake": [],
                "blunder": [],
                "missed_win": [],
                "unknown": [],
            }
        }
        evaluation_changes = {"white": [], "black": []}
        def update_bar():
            bar = "█" * int(analyzed_moves / len(self.move_history) * 50) + f" %{int(analyzed_moves / len(self.move_history) * 100)}"
            print(f"Analiz: {bar} {analyzed_moves}/{len(self.move_history)}", end="\r")
        if board.fen() not in self.analyz_history:
            stockfish.set_fen_position(board.fen())
            evaluation_before = stockfish.get_evaluation()
            self.analyz_history[board.fen()] = evaluation_before
        else:
            evaluation_before = self.analyz_history[board.fen()]
        for move in self.move_history:
            player = "white" if board.turn == chess.WHITE else "black"
      
            board.push_uci(move)
            main_board_fen = board.fen()
            analyzed_moves += 1
      
            if int(self.stockfish_depth) >= int(stockfish.depth):
                if board.fen() not in self.stockfish_history:
                    stockfish.set_fen_position(board.fen())
                    evaluation_after = stockfish.get_evaluation()
                    self.analyz_history[board.fen()] = evaluation_after
                else:
                    evaluation_after = self.stockfish_history[board.fen()]
                    self.analyz_history[board.fen()] = evaluation_after
            else:
                if board.fen() in self.analyz_history:
                    evaluation_after = self.analyz_history[board.fen()]
                else:
                    stockfish.set_fen_position(board.fen())
                    evaluation_after = stockfish.get_evaluation()
                    self.analyz_history[board.fen()] = evaluation_after
            eval_diff = None
            if evaluation_before["type"] == "cp" and evaluation_after["type"] == "cp":
                eval_diff = evaluation_after["value"] - evaluation_before["value"]
            elif evaluation_before["type"] == "mate" or evaluation_after["type"] == "mate":
                if evaluation_before["type"] == "mate" and evaluation_after["type"] == "mate":
                    eval_diff = evaluation_after["value"] - evaluation_before["value"]
          
            if player == "black":
                if eval_diff is not None:
                    eval_diff = -eval_diff
            board.pop()
            stockfish.set_fen_position(board.fen())
            stockfish.set_depth(20)
            top_moves = stockfish.get_top_moves(6)
            try:
                top_moves = [move["Move"] for move in top_moves]
            except:
                top_moves = []
            if top_moves:
                best_move = top_moves[0]
                if eval_diff is not None:
                    evaluation_changes[player].append({
                        "move": move,
                        "eval_diff": eval_diff
                    })
                    if eval_diff >= 200 and move == best_move:
                        move_analysis[player]["brilliant"].append(main_board_fen)
                    elif move == best_move:
                        move_analysis[player]["best"].append(main_board_fen)
                    elif len(top_moves) >= 1 and move in top_moves:
                        stockfish.set_fen_position(board.fen())
                        main_move = move
                        x_flag = False
                  

                        for move2 in top_moves:
                            if move2 == main_move:
                                continue
                            board.push_uci(move2)
                            stockfish.set_fen_position(board.fen())
                            evaluation_move = stockfish.get_evaluation()
                            if evaluation_move["type"] == "cp":
                                eval_diff2 = evaluation_move["value"] - evaluation_after["value"]
                            else:
                                eval_diff2 = evaluation_move["value"]
                          
                            if player == "black":
                                if eval_diff2 is not None:
                                    eval_diff2 = -eval_diff2
                            board.pop()
                            if eval_diff2 >= 50 and eval_diff < 0:
                              
                                if -100 <= eval_diff:
                                    move_analysis[player]["inaccuracy"].append(main_board_fen)
                                elif -300 <= eval_diff < -100:
                                    move_analysis[player]["mistake"].append(main_board_fen)
                                elif eval_diff < -300:
                                    move_analysis[player]["blunder"].append(main_board_fen)
                                x_flag = True
                                break



                        if not x_flag:
                            move_analysis[player]["excellent"].append(main_board_fen)
                      
                    elif 0 <= eval_diff:
                        move_analysis[player]["good"].append(main_board_fen)
                    elif -100 <= eval_diff:
                        move_analysis[player]["inaccuracy"].append(main_board_fen)
                    elif -300 <= eval_diff < -100:
                        move_analysis[player]["mistake"].append(main_board_fen)
                    elif eval_diff < -300:
                        move_analysis[player]["blunder"].append(main_board_fen)
                else:
                    if evaluation_before["type"] == "mate" and evaluation_after["type"] != "mate":
                        move_analysis[player]["missed_win"].append(main_board_fen)
            else:
                move_analysis[player]["unknown"].append(main_board_fen)
              
            board.push_uci(move)

            evaluation_before = evaluation_after
            update_bar()
        print("\n")

        def calculate_accuracy_with_eval_changes(move_analysis: dict):
            accuracies = {"white": 0, "black": 0}
            for player in ["white", "black"]:
                accuracies[player] = 0
                correct_moves = len(move_analysis[player]["brilliant"]) + len(move_analysis[player]["best"]) + len(move_analysis[player]["excellent"]) + len(move_analysis[player]["good"])
                incorrect_moves = len(move_analysis[player]["inaccuracy"]) + len(move_analysis[player]["mistake"]) + len(move_analysis[player]["blunder"])
                total_moves = correct_moves + incorrect_moves
                if total_moves > 0:
                    accuracies[player] = (correct_moves / total_moves) * 100
                  
                  
            return accuracies
        accuracies = calculate_accuracy_with_eval_changes(move_analysis)
        white_accuracy_percentage = max(0, min(accuracies["white"], 100))
        black_accuracy_percentage = max(0, min(accuracies["black"], 100))

        print("--------------")
        print(f"Beyaz doğruluk: %{white_accuracy_percentage:.2f}")
        print(f"Siyah doğruluk: %{black_accuracy_percentage:.2f}")
        print("\n\n")
        print("            White     |      Black")
        print("           -----------|-----------")
        for key in ["brilliant", "best", "excellent", "good", "inaccuracy", "mistake", "blunder", "missed_win", "unknown"]:
            white_moves_len = len(move_analysis["white"][key])
            black_moves_len = len(move_analysis["black"][key])
            print(f"{key.capitalize() + ' ' * (10 - len(key))}  {white_moves_len}{' ' * (10 - len(str(white_moves_len)))}| {black_moves_len}")
      
      
        self.move_analysis = move_analysis
        self.rewiev_game()
      

    def bot_move(self):
        """
        Bot'un en iyi hamlesini, Botun ELO puanına göre döndürür. Bazı durumlarda oyun berabere bitmemesi adına ve oyunun oynanabilir bir durumda olması durumunda, oyunu kaybettirmeyecek alternatif hamleler de döndürebilir.
        -----------------------
        Returns the best move of the bot according to the Bot's ELO rating. In some cases, it can return alternative moves that will not lose the game in order to prevent the game from ending in a draw and if the game is in a playable position.
        """
        if self.stockfish:
            board = self.board.copy()
            stockfish = self.stockfish
            elo_rating = self.white_elo if self.board.turn == chess.WHITE else self.black_elo
            stockfish.set_elo_rating(elo_rating)
            stockfish.set_depth(self.get_depth_level(elo_rating))
            stockfish.set_skill_level(self.get_skill_level(elo_rating))
            stockfish.set_fen_position(self.board.fen())
          
            best_move = stockfish.get_best_move()
            if best_move is None:
                print("No legal moves.")
                return None
      
            board.push_uci(best_move)
      
            stockfish.set_fen_position(board.fen())
            if board.is_game_over() and self.is_draw():
                board.pop()
              
                stockfish.set_fen_position(board.fen())
                if self.is_playable_position():
                    alternative_moves = [move['Move'] for move in stockfish.get_top_moves(6)]
                    found_move = False
                    for move in alternative_moves:
                        if move == best_move:
                            continue
                        if move:
                            board.push_uci(move)
                            stockfish.set_fen_position(board.fen())
                            if self.is_playable_position():
                                board.pop()
                              
                                return move
                            else:
                                board.pop()
                              
                        else:
                            print(f"İllegal move: {move}")
                    if not found_move:
                        return best_move
                else:
                    return best_move
              
            return best_move
    def convert_to_uci(self, short_move):
        """
        Kısaltılmış bir hamleyi (örneğin e4, Nf5) tam UCI formatına çevirir.
        -----------------------
        Converts a short move (e.g. e4, Nf5) to full UCI format.
        """
        board = self.board.copy()

        for move in board.legal_moves:
            if board.san(move) == short_move:
                return move.uci()
        if short_move == "O-O" or short_move == "0-0":
            for move in board.legal_moves:
                if board.is_kingside_castling(move):
                    return move.uci()
        if short_move == "O-O-O" or short_move == "0-0-0":
            for move in board.legal_moves:
                if board.is_queenside_castling(move):
                    return move.uci()

        return short_move
      
    def start_game(self):
        """
        Ana oyun döngüsü.
        -----------------------
        Main game loop.
        """
        self.set_board()
        self.print_board()
      
        def get_move(Move):
      
            try:
                if chess.Move.from_uci(Move) in self.board.legal_moves:
                    return chess.Move.from_uci(Move)
              
            except:
                try:
                    if Move in self.board.legal_moves:
                        return Move
                except:
                    return None 
                  
                return None

        while not self.board.is_game_over() or self.game_end:
            self.stockfish.set_depth(self.stockfish_depth)
            if self.GAME_MODE == 0:
                move = self.bot_move()
                if move is None:
                    self.game_end = True
                    break
            elif self.GAME_MODE == 1:
                if self.white_elo == 0:
                    player_color = chess.WHITE
                else:
                    player_color = chess.BLACK
                if self.board.turn == player_color:
                    while True:
                        print("Commands: 'resign', 'takeback'\n")
                        move = llinput("Your move: ", forceinput=True)
                        move = self.convert_to_uci(move)
                        if move == "resign":
                            break
                        elif move == "takeback":
                            if len(self.move_history) > 1:
                                self.board.pop()
                                self.board.pop()
                                self.move_history.pop()
                                self.move_history.pop()
                                break
                          
                            elif len(self.move_history) == 1:
                                self.board.pop()
                                self.move_history.pop()
                                break
                            else:
                                print("No moves to take back.")
                                continue
                        if get_move(move):
                            break
              
                else:
                    move = self.bot_move()
                    if move is None:
                        self.game_end = True
                        break
                  
            elif self.GAME_MODE == 2:
                if self.board.turn == chess.WHITE:
                    while True:
                        print("Commands: 'resign', 'takeback'\n")
                        move = llinput("White's move: ", forceinput=True)
                        move = self.convert_to_uci(move)
                      
                        if move == "resign":
                            break
                        elif move == "takeback":
                            if len(self.move_history) > 1:
                                self.board.pop()
                                self.board.pop()
                                self.move_history.pop()
                                self.move_history.pop()
                                break
                          
                            elif len(self.move_history) == 1:
                                self.board.pop()
                                self.move_history.pop()
                                break
                            else:
                                print("No moves to take back.")
                                continue
                        if get_move(move):
                            break
              
                else:
                    while True:
                        print("Commands: 'resign', 'takeback'\n")
                        move = llinput("Black's move: ", forceinput=True)
                        move = self.convert_to_uci(move)
                        if move == "resign":
                            break
                        elif move == "takeback":
                            if len(self.move_history) > 1:
                                self.board.pop()
                                self.board.pop()
                                self.move_history.pop()
                                self.move_history.pop()
                                break
                            elif len(self.move_history) == 1:
                                self.board.pop()
                                self.move_history.pop()
                                break
                            else:
                                print("No moves to take back.")
                                continue
                        if get_move(move):
                            break
          


          

            if move == "resign":
                print(f"{('White' if self.board.turn == chess.WHITE else 'Black')} resigned.")
                self.game_end = True
                break

            if move:
                if move not in ("resign", "takeback"):
                    self.move_history.append(move)
                    self.board.push_uci(move)
          
      
            if self.GAME_MODE == 0:
                if self.white_elo >= self.black_elo:
                    self.print_board()
                else:
                    self.print_board(reverse_board=True)
            elif self.GAME_MODE == 1:
                if self.white_elo == 0:
                    self.print_board()
                else:
                    self.print_board(reverse_board=True)
            elif self.GAME_MODE == 2:
                if self.board.turn == chess.WHITE:
                    self.print_board()
                else:
                    self.print_board(reverse_board=True)
            else:
                self.print_board()

        self.game_end = True
        time.sleep(1)
        if self.board.is_checkmate():
            print(f"{self.get_message('winner')} {('Black' if self.board.turn == chess.WHITE else 'White')}")
        elif self.board.is_stalemate():
            print(self.get_message("draw"))
        elif self.board.is_insufficient_material():
            print(self.get_message("draw"))
        elif self.board.is_seventyfive_moves():
            print(self.get_message("draw"))
        elif self.board.is_fivefold_repetition():
            print(self.get_message("draw"))
        else:
            print(self.get_message("game_over"))
        self.save_pgn()
        print("\n\n")
        self.analyze_game()
if name == "main":
    chess_game = ChessGame(stockfish_path="/workspaces/Tests/stockfishTest/stockfish", stockfish_depth=20) # Stockfish path'i ve depth'i değiştirebilirsiniz. You can change the Stockfish path and depth.
    chess_game.start_game() # Oyunu başlatır. Starts the game.
 
Son düzenleme:
Stockfish depth degerini 20 yapmissiniz, belki o agir gelmis olabilir. 15'e kadar dusurmeyi deneyin isterseniz.

Benim uzmanlik alanim oyun yapmak olmadigi icin cok da spesifik bir sekilde yardimci olamam ama eger yapabilirseniz parallel processing islemini malum fonksiyona eklemeyi dusunebilirsiniz. Oyundaki hareketler chunklara bolup eszamanli olarak analiz edebilirsiniz. Onerebilecegim kutuphanelerden biri concurrent.futures.

Bir deneyin bakin, olmazsa haber verin koda daha detayli bakip size bir cozum bulalim.
 
dediğiniz şeyleri deneyeceğim…
stockfish_depth olayı da aslında şöyle oraya 1 dersek tabikide çok hızlı olur ancak 20 den fazlası sorunken 20 veya 20’nin altı yine gayet hızlı çalışıyor.

aklımdaki şey şuydu
top_moves #5-6 saniye sürüyor
bu yüzden bütün FEN’leri (konumları) aynı anda multitask yaparak hesaplamak iyi bir şekilde optimize edebilir (threading ile çalışmayacaktır)

sorun şu… Teorik olarak hızlandırır. ama ben beceremedim yani çok multiproccesing kütüphanesinde bilgim yok ve çözüm bulamadım.

ama biraz daha uğraşacağım.
 
dediğiniz şeyleri deneyeceğim…
stockfish_depth olayı da aslında şöyle oraya 1 dersek tabikide çok hızlı olur ancak 20 den fazlası sorunken 20 veya 20’nin altı yine gayet hızlı çalışıyor.

aklımdaki şey şuydu
top_moves #5-6 saniye sürüyor
bu yüzden bütün FEN’leri (konumları) aynı anda multitask yaparak hesaplamak iyi bir şekilde optimize edebilir (threading ile çalışmayacaktır)

sorun şu… Teorik olarak hızlandırır. ama ben beceremedim yani çok multiproccesing kütüphanesinde bilgim yok ve çözüm bulamadım.

ama biraz daha uğraşacağım.
Sizin icin Stockfish satranc motorunu biraz arastirdim ve gorunuse gore stockfish.set_depth() bir pozisyonda gelecekteki hamleleri tahmin ederken kac hamle otesine kadar hesaplama yapacagini ifade ediyor ve CPU-Intensive bir fonksiyon. Yani CPU uzerine oldukca yukleniyor. Eger islemciniz multithreading konusunda cok iyi degilse, cekirdek sayisi dusukse oldukca zorlanacaktir.

Degeri 20 degil, daha dusuk yaparsaniz daha da hizli hesaplama yapar ve islemcinize de cok fazla yuk bindirmez. Ama hesaplamalar da oldukca yuzeysel olabilir.

Benim onerim bu ayari default setting olarak tek haneli bir sayida tutmaniz. Daha sonra oyun arayuzune ayarlar kismi yapin ve bir slider ekleyin. Bu slider'i yana dogru kaydirdikca depth degeri artsin. Boylece herkes kendi islemci gucune layik bir depth ayari yapabilir.

Sonuc olarak depth degerini ya 10 yada 15 yapin. Yine fonksiyon agirlik yaparsa daha da dusurun. Analyze_game fonksiyonunuzu yavaslatan sey depth degerinin yuksek olmasi.

Yine olmazsa soyleyin daha detayli bir sekilde arastirip inceleyeyim.
 
Sizin icin Stockfish satranc motorunu biraz arastirdim ve gorunuse gore stockfish.set_depth() bir pozisyonda gelecekteki hamleleri tahmin ederken kac hamle otesine kadar hesaplama yapacagini ifade ediyor ve CPU-Intensive bir fonksiyon. Yani CPU uzerine oldukca yukleniyor. Eger islemciniz multithreading konusunda cok iyi degilse, cekirdek sayisi dusukse oldukca zorlanacaktir.

Degeri 20 degil, daha dusuk yaparsaniz daha da hizli hesaplama yapar ve islemcinize de cok fazla yuk bindirmez. Ama hesaplamalar da oldukca yuzeysel olabilir.

Benim onerim bu ayari default setting olarak tek haneli bir sayida tutmaniz. Daha sonra oyun arayuzune ayarlar kismi yapin ve bir slider ekleyin. Bu slider'i yana dogru kaydirdikca depth degeri artsin. Boylece herkes kendi islemci gucune layik bir depth ayari yapabilir.

Sonuc olarak depth degerini ya 10 yada 15 yapin. Yine fonksiyon agirlik yaparsa daha da dusurun. Analyze_game fonksiyonunuzu yavaslatan sey depth degerinin yuksek olmasi.

Yine olmazsa soyleyin daha detayli bir sekilde arastirip inceleyeyim.
evet hak veriyorum size.
Ama hesaplamalar çok yüzeysel olur bu şekilde yaparsak,
bunu düşünmüştüm temelde sorunu çözer ama bir sorunu çözerken farklı bir sorun çıkartmış oluruz. (hesaplamalar yüzeysel ve kötü olur)

bu çözümlerden biri ama ben daha kökten sorunu çözen bir yöntem arıyorum.
şuan zaten geçici olarak bu çözümü kullanıyorum.

pythonda paralel işlem yapma hakkında çok bilgim olmadığı için soruyorum.
threading kütüphanesi CPU işlemlerinde çok da işe yaramıyor.
ancak bildiğim kadarıyla multiproccesing CPU işlemlerinde daha güçlü ve optimize oluyor,
bu yüzden bunu önermiştim. Ayrıca çekirdek gücüm yeterli olacağını varsayıyorum.

şunu sorabilirsiniz. “o zaman neden kullanmıyorsun? :D” haklısın, ama dediğim gibi paralel işlemlerde pek bilgili değilim denedim ama başaramadım o kısmı kodlamaya.

hatalı olduğum yerler olabilir, yardımcı olduğunuz için teşekkür ederim.
sizce ne yaparak sorunu kökten çözebilirim? veya kod kısmın yardımcı olabilir misiniz?
 
Hic olmazsa fikir verebileyim diye documentation sayfasinda hizlica goz gezdiriyorum su an ve "Node" diye bir seye denk geldim. Ne kadar fazla olursa o kadar hesaplama istiyor. Yani anlayacaginiz buyuk ihtimal fonksiyonu yavaslatan ana sey bu. Sanirim terminale info adi altinda principal variation ve nodes'u yazdirmak mumkun. Principal dedigimiz sey ana varyant oluyor. Yani olasi en iyi hamleleri icinde barindiran asil arkadas bu. Nodes ise dugum oluyor, o ana kadar hesaplanan tum olasi hamleler oluyor. Bunlari terminale yazdirip okuyacak olursaniz o anki analiz isleminin agirligini gorebilirsiniz.

Nodes degeri arttikca ve principal variation uzadikca analiz islemi daha agir olacak demek. Bu durumlarda stockfish.set_num_threads(islemcinizin_cekirdek_sayisi)ile her seyden once islemcinizin ne kadar cekirdegi kullanilacak onu kesinlestirin. Dikkat edin core degil thread sayisi girmeniz lazim. Islemcinizin thread sayisi atiyorum mesela 12 ise oraya maksimum 12 yazabilirsiniz. Benim onerim mesela 12 thread varsa 9-10 tanesini kullanip bir kac tanesini bosta birakmaniz. Bu sayede arkaplandaki kaynaklar icin de musait cekirdek birakmis olursunuz ve bilgisayarin kasmasi kismen azalir.

Daha sonra ise stockfish.set_option("NodesLimit", 500000)yazarak cekirdekler uzerine yuklenecek nodes sayisini sinirlayabilirsiniz. Verdigim ornekte node limiti 500 bin olacak sekilde yazdim ama ihtiyaciniza gore daha da azaltabilirsiniz. Boylece cekirdeklere overload olmasini biraz da olsun kismis olursunuz.

Bu limitleme islemini duruma gore ayarlayabilirsiniz. Mesela onden bir pre-analysis islemi baslatabilirsiniz. Bu pre-analysis isleminin amaci o yapilacak analiz isleminin agirligini olcmek. Depth degerini dusuk tutun ve bir sayac baslatin. Ne kadar zaman alirsa analiz islemi o kadar agir olacak demektir, siz de ona gore cekirdek ayalarinizi hazirlayip agir analiz islemi icin sinirlandirilmis islemci kaynaklari ile bilgisayarin yavaslamasina sebep olan overload (asiri yukleme) isleminin onune gecmeye calisacaksiniz.

Kodu hizli calistirmak icin acele etmeyin. Ilk once kodun sisteminizde hafif calismasini saglamaniz lazim. Sistemin ve kodun agirliginin kontrolu sizde olmali. Daha sonra ipleri elinize alip kontrolu sagladiktan sonra kodu hafifletmeye calisabilirsiniz.

Bence ilk once kontrolu elimize alalim daha sonra bu yapilacak islemleri daha hizli islemenin bir yolunu bulalim. Sahsen ben islemcinin ayni islemi tekrardan hesaplamasini engellemek icin stockfish.set_option("Hash", megabyte_biriminde_ram_boyutu)eklemenizi oneririm. Mesela kodun 4 GB RAM kullanmasini istiyorsaniz 4096 yazmaniz lazim.

Isterseniz bir deneyin bakalim olacak mi olmayacak mi.
 
Hic olmazsa fikir verebileyim diye documentation sayfasinda hizlica goz gezdiriyorum su an ve "Node" diye bir seye denk geldim. Ne kadar fazla olursa o kadar hesaplama istiyor. Yani anlayacaginiz buyuk ihtimal fonksiyonu yavaslatan ana sey bu. Sanirim terminale info adi altinda principal variation ve nodes'u yazdirmak mumkun. Principal dedigimiz sey ana varyant oluyor. Yani olasi en iyi hamleleri icinde barindiran asil arkadas bu. Nodes ise dugum oluyor, o ana kadar hesaplanan tum olasi hamleler oluyor. Bunlari terminale yazdirip okuyacak olursaniz o anki analiz isleminin agirligini gorebilirsiniz.

Nodes degeri arttikca ve principal variation uzadikca analiz islemi daha agir olacak demek. Bu durumlarda stockfish.set_num_threads(islemcinizin_cekirdek_sayisi)ile her seyden once islemcinizin ne kadar cekirdegi kullanilacak onu kesinlestirin. Dikkat edin core degil thread sayisi girmeniz lazim. Islemcinizin thread sayisi atiyorum mesela 12 ise oraya maksimum 12 yazabilirsiniz. Benim onerim mesela 12 thread varsa 9-10 tanesini kullanip bir kac tanesini bosta birakmaniz. Bu sayede arkaplandaki kaynaklar icin de musait cekirdek birakmis olursunuz ve bilgisayarin kasmasi kismen azalir.

Daha sonra ise stockfish.set_option("NodesLimit", 500000)yazarak cekirdekler uzerine yuklenecek nodes sayisini sinirlayabilirsiniz. Verdigim ornekte node limiti 500 bin olacak sekilde yazdim ama ihtiyaciniza gore daha da azaltabilirsiniz. Boylece cekirdeklere overload olmasini biraz da olsun kismis olursunuz.

Bu limitleme islemini duruma gore ayarlayabilirsiniz. Mesela onden bir pre-analysis islemi baslatabilirsiniz. Bu pre-analysis isleminin amaci o yapilacak analiz isleminin agirligini olcmek. Depth degerini dusuk tutun ve bir sayac baslatin. Ne kadar zaman alirsa analiz islemi o kadar agir olacak demektir, siz de ona gore cekirdek ayalarinizi hazirlayip agir analiz islemi icin sinirlandirilmis islemci kaynaklari ile bilgisayarin yavaslamasina sebep olan overload (asiri yukleme) isleminin onune gecmeye calisacaksiniz.

Kodu hizli calistirmak icin acele etmeyin. Ilk once kodun sisteminizde hafif calismasini saglamaniz lazim. Sistemin ve kodun agirliginin kontrolu sizde olmali. Daha sonra ipleri elinize alip kontrolu sagladiktan sonra kodu hafifletmeye calisabilirsiniz.

Bence ilk once kontrolu elimize alalim daha sonra bu yapilacak islemleri daha hizli islemenin bir yolunu bulalim. Sahsen ben islemcinin ayni islemi tekrardan hesaplamasini engellemek icin stockfish.set_option("Hash", megabyte_biriminde_ram_boyutu)eklemenizi oneririm. Mesela kodun 4 GB RAM kullanmasini istiyorsaniz 4096 yazmaniz lazim.

Isterseniz bir deneyin bakalim olacak mi olmayacak mi.
özel olarak araştırıp dönüş yaptığınız için teşekkür ederim deneyip geri dönüş yapıcağım.

sorunu çözdüm.
önerdiğiniz şeyleri denedim ama istediğim optimizasyona ulaşamadım. Sorunu ilk başta önerdiğiniz concurrent.futures kütüphanesi ile çözdüm. Yardımınız için teşekkürler.
 
Son düzenleme:

Technopat Haberler

Yeni konular

Geri
Yukarı