Further enhance and add in an advanced chess-move memory webbing with an advanced multilayered chain-of-thought processing.

```
import tkinter as tk
from tkinter import messagebox, ttk, scrolledtext
from PIL import Image, ImageTk
import chess
import os
import g4f
from g4f.client import Client as OpenAI
import subprocess
import sys
import threading
import re
import logging
from queue import Queue

# Configure logging
logging.basicConfig(
    level=logging.DEBUG,
    filename='chess_gui.log',
    filemode='w',
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# Constants
SQUARE_SIZE = 60  # Increased size for better visibility
LIGHT_COLOR = "#F0D9B5"
DARK_COLOR = "#B58863"
HIGHLIGHT_COLOR = "#FFFF00"

# Paths to executables and models
STOCKFISH_PATH = "stockfish-windows-x86-64-sse41-popcnt.exe"  # Update this path accordingly
NNUE_MODEL_PATH = "nn-bc638d5ec9-20240730.nnue"  # Path to your NNUE model

# Verify existence of Stockfish and NNUE model
if not os.path.exists(STOCKFISH_PATH):
    messagebox.showerror("Error", f"Stockfish executable not found at {STOCKFISH_PATH}")
    sys.exit(1)

if not os.path.exists(NNUE_MODEL_PATH):
    messagebox.showerror("Error", f"NNUE model file not found at {NNUE_MODEL_PATH}")
    sys.exit(1)


def play_chess_gpt(board, evaluation, api_key, reasoning_queue):
    client = OpenAI(provider=g4f.Provider.Blackbox)
    legal_moves = [move.uci() for move in board.legal_moves]
    prompt = (
        "You are a grandmaster chess player with deep strategic insight. "
        "Based on the current board evaluation provided by a chess engine, "
        "provide a detailed reasoning (chain-of-thought) and respond with a single move in UCI chess notation (e.g., e2e4).\n\n"
        f"Board Evaluation: {evaluation}\n\n"
        f"Legal Moves: {', '.join(legal_moves)}.\n\n"
        "Make your move with thorough reasoning."
    )
    try:
        completion = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": prompt},
                {"role": "user", "content": "Please provide your move along with reasoning."},
            ]
        )
        response = completion.choices[0].message.content
        reasoning_queue.put((response, None))
    except Exception as e:
        logging.error(f"GPT-4 Communication Error: {e}")
        reasoning_queue.put(("", e))


def play_chess_claude(board, evaluation, api_key, reasoning_queue):
    client = OpenAI(provider=g4f.Provider.Blackbox)
    legal_moves = [move.uci() for move in board.legal_moves]
    prompt = (
        "You are a grandmaster chess player with exceptional strategic skills. "
        "Using the current board evaluation provided by a chess engine, "
        "provide detailed reasoning (chain-of-thought) and respond with a single move in UCI chess notation (e.g., e2e4).\n\n"
        f"Board Evaluation: {evaluation}\n\n"
        f"Legal Moves: {', '.join(legal_moves)}.\n\n"
        "Please make your move with comprehensive reasoning."
    )
    try:
        completion = client.chat.completions.create(
            model="claude-3.5-sonnet",
            messages=[
                {"role": "system", "content": prompt},
                {"role": "user", "content": "Please provide your move along with reasoning."},
            ]
        )
        response = completion.choices[0].message.content
        reasoning_queue.put((response, None))
    except Exception as e:
        logging.error(f"Claude Communication Error: {e}")
        reasoning_queue.put(("", e))


class AIWorker(threading.Thread):
    def __init__(self, board, evaluation, ai_type, api_key, stockfish_engine, result_queue):
        super().__init__()
        self.board = board.copy()
        self.evaluation = evaluation
        self.ai_type = ai_type  # "gpt" or "claude"
        self.api_key = api_key
        self.stockfish = stockfish_engine
        self.result_queue = result_queue

    def run(self):
        reasoning_queue = Queue()
        if self.ai_type == "gpt":
            play_chess_gpt(self.board, self.evaluation, self.api_key, reasoning_queue)
        elif self.ai_type == "claude":
            play_chess_claude(self.board, self.evaluation, self.api_key, reasoning_queue)
        else:
            logging.error(f"Unsupported AI type: {self.ai_type}")
            self.result_queue.put(("", "Unsupported AI type"))
            return

        response, error = reasoning_queue.get()
        if error:
            self.result_queue.put(("", f"AI Communication Error: {error}"))
        else:
            reasoning, ai_move = self.parse_ai_response(response)
            self.result_queue.put((reasoning, ai_move))

    @staticmethod
    def parse_ai_response(response):
        """
        Parses the AI response to extract reasoning and move.
        Assumes the AI provides reasoning followed by the move in UCI notation.
        """
        # Attempt to find UCI move in the response
        move_search = re.search(r'([a-h][1-8][a-h][1-8][qQrRnNbBkK]?)$', response.strip())
        if move_search:
            ai_move = move_search.group(1)
            reasoning = response[:move_search.start()].strip()
            return reasoning, ai_move
        else:
            # If no valid move found, assume the last word is the move
            tokens = response.strip().split()
            ai_move = tokens[-1]
            reasoning = ' '.join(tokens[:-1])
            return reasoning, ai_move


class StockfishEngine:
    def __init__(self, path=STOCKFISH_PATH, nnue_path=NNUE_MODEL_PATH):
        try:
            self.engine = subprocess.Popen(
                [path],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,  # Capture stderr for error messages
                universal_newlines=True,
                bufsize=1
            )
            logging.info("Stockfish engine started successfully.")
        except Exception as e:
            logging.error(f"Failed to start Stockfish engine: {e}")
            messagebox.showerror("Error", f"Failed to start Stockfish engine: {e}")
            sys.exit(1)
        
        # Initialize UCI protocol
        self.engine.stdin.write("uci\n")
        self.engine.stdin.flush()
        
        # Wait for UCI to initialize
        try:
            while True:
                output = self.engine.stdout.readline().strip()
                if output == "uciok":
                    logging.debug("Stockfish UCI initialization complete.")
                    break
        except Exception as e:
            logging.error(f"Error during UCI initialization: {e}")
            self.quit()
            messagebox.showerror("Error", f"Error during UCI initialization: {e}")
            sys.exit(1)
        
        # Attempt to set NNUE model
        try:
            self.engine.stdin.write(f"setoption name EvalFile value {nnue_path}\n")
            self.engine.stdin.flush()
            logging.debug("Sent setoption command for NNUE model to Stockfish.")
            # Wait for Stockfish to acknowledge the option
            while True:
                output = self.engine.stdout.readline().strip()
                if output == "readyok":
                    logging.debug("Stockfish is ready after setting options.")
                    break
                elif output.startswith("Unknown option"):
                    logging.warning("Stockfish does not recognize the 'EvalFile' option. Continuing without NNUE model.")
                    break
        except Exception as e:
            logging.error(f"Error setting NNUE model: {e}")
            self.quit()
            messagebox.showerror("Error", f"Error setting NNUE model: {e}")
            sys.exit(1)
        
        # Ensure engine is ready
        try:
            self.engine.stdin.write("isready\n")
            self.engine.stdin.flush()
            while True:
                output = self.engine.stdout.readline().strip()
                if output == "readyok":
                    logging.debug("Stockfish is ready for further commands.")
                    break
        except Exception as e:
            logging.error(f"Error during readiness check: {e}")
            self.quit()
            messagebox.showerror("Error", f"Error during readiness check: {e}")
            sys.exit(1)

    def get_evaluation(self, board_fen):
        # Set position
        try:
            self.engine.stdin.write(f"position fen {board_fen}\n")
            self.engine.stdin.flush()
            logging.debug(f"Sent position to Stockfish: {board_fen}")
        except Exception as e:
            logging.error(f"Failed to send position to Stockfish: {e}")
            return "N/A"

        # Request evaluation at a certain depth
        try:
            self.engine.stdin.write("go depth 15\n")  # Adjust depth as needed
            self.engine.stdin.flush()
            logging.debug("Sent 'go depth 15' to Stockfish.")
        except Exception as e:
            logging.error(f"Failed to start evaluation in Stockfish: {e}")
            return "N/A"

        evaluation = None
        try:
            while True:
                output = self.engine.stdout.readline().strip()
                if output.startswith("info depth"):
                    # Extract score
                    match = re.search(r"score (cp|mate) (-?\d+)", output)
                    if match:
                        score_type, value = match.groups()
                        if score_type == "cp":
                            evaluation = float(value) / 100.0
                        elif score_type == "mate":
                            evaluation = 10000.0 if int(value) > 0 else -10000.0
                elif output.startswith("bestmove"):
                    logging.debug(f"Received bestmove from Stockfish: {output}")
                    break
        except Exception as e:
            logging.error(f"Error during evaluation: {e}")
            return "N/A"

        # Interpret evaluation
        if evaluation is not None:
            if evaluation > 0:
                eval_text = f"{evaluation:.2f} (White advantage)"
            elif evaluation < 0:
                eval_text = f"{evaluation:.2f} (Black advantage)"
            else:
                eval_text = "0.00 (Equal)"
        else:
            eval_text = "N/A"

        logging.debug(f"Stockfish evaluation: {eval_text}")
        return eval_text

    def quit(self):
        try:
            self.engine.stdin.write("quit\n")
            self.engine.stdin.flush()
            self.engine.terminate()
            logging.info("Stockfish engine terminated successfully.")
        except Exception as e:
            logging.error(f"Error terminating Stockfish engine: {e}")


class ChessGUI:
    def __init__(self, master):
        self.master = master
        self.master.title("Advanced Chess Game with AI Support")
        self.master.geometry("1000x900")  # Increased size for additional elements
        self.master.configure(bg='#f0f0f0')  # Light gray background
        self.style = ttk.Style()
        self.style.theme_use('clam')  # Modern look

        self.board = chess.Board()
        self.buttons = [[None for _ in range(8)] for _ in range(8)]
        self.selected_square = None
        self.piece_images = {}
        self.empty_images = {}
        self.ai_opponent = None
        self.api_key = None
        self.opponent_var = tk.StringVar()  # To track selected opponent
        self.stockfish = StockfishEngine()  # Initialize Stockfish engine

        self.load_images()
        self.create_opponent_selection()

    def load_images(self):
        piece_files = {
            'p': 'black_pawn.png', 'r': 'black_rook.png', 'n': 'black_knight.png',
            'b': 'black_bishop.png', 'q': 'black_queen.png', 'k': 'black_king.png',
            'P': 'white_pawn.png', 'R': 'white_rook.png', 'N': 'white_knight.png',
            'B': 'white_bishop.png', 'Q': 'white_queen.png', 'K': 'white_king.png'
        }
        for piece, file_name in piece_files.items():
            img_path = os.path.join("pieces", file_name)
            if not os.path.exists(img_path):
                logging.error(f"Image file not found: {img_path}")
                messagebox.showerror("Error", f"Image file not found: {img_path}")
                self.master.quit()
            try:
                img = Image.open(img_path).convert("RGBA")
                img = img.resize((SQUARE_SIZE, SQUARE_SIZE), Image.LANCZOS)
                self.piece_images[piece] = {
                    'light': ImageTk.PhotoImage(self.overlay_image(img, LIGHT_COLOR)),
                    'dark': ImageTk.PhotoImage(self.overlay_image(img, DARK_COLOR)),
                    'highlight': ImageTk.PhotoImage(self.overlay_image(img, HIGHLIGHT_COLOR))
                }
                logging.debug(f"Loaded image for piece: {piece}")
            except Exception as e:
                logging.error(f"Failed to load image {img_path}: {e}")
                messagebox.showerror("Error", f"Failed to load image {img_path}: {e}")
                self.master.quit()

        for color, color_value in [('light', LIGHT_COLOR), ('dark', DARK_COLOR), ('highlight', HIGHLIGHT_COLOR)]:
            empty_img = Image.new('RGBA', (SQUARE_SIZE, SQUARE_SIZE), color_value)
            self.empty_images[color] = ImageTk.PhotoImage(empty_img)
            logging.debug(f"Created empty image for color: {color}")

    def overlay_image(self, img, color):
        background = Image.new("RGBA", img.size, color)
        return Image.alpha_composite(background, img)

    def create_opponent_selection(self):
        self.opponent_frame = ttk.Frame(self.master, padding="20")
        self.opponent_frame.pack(expand=True)

        ttk.Label(self.opponent_frame, text="Choose your opponent:", font=('Helvetica', 16)).pack(pady=10)

        ttk.Radiobutton(
            self.opponent_frame,
            text="GPT-4 (via g4f)",
            variable=self.opponent_var,
            value="gpt",
            style='TRadiobutton'
        ).pack(pady=5)

        ttk.Radiobutton(
            self.opponent_frame,
            text="Claude Pro (via g4f)",
            variable=self.opponent_var,
            value="claude",
            style='TRadiobutton'
        ).pack(pady=5)

        ttk.Button(
            self.opponent_frame,
            text="Next",
            command=self.show_api_key_input,
            style='TButton'
        ).pack(pady=20)

        # Configure styles
        self.style.configure('TRadiobutton', font=('Helvetica', 12))
        self.style.configure('TButton', font=('Helvetica', 12), padding=10)

    def show_api_key_input(self):
        self.ai_opponent = self.opponent_var.get()
        if not self.ai_opponent:
            messagebox.showerror("Error", "Please select an opponent.")
            return

        self.opponent_frame.destroy()

        if self.ai_opponent in ["gpt", "claude"]:
            self.api_frame = ttk.Frame(self.master, padding="20")
            self.api_frame.pack(expand=True)

            api_label = "g4f configuration (optional)"
            ttk.Label(
                self.api_frame,
                text=f"Enter {api_label}:",
                font=('Helvetica', 16)
            ).pack(pady=10)

            self.api_key_entry = ttk.Entry(self.api_frame, width=50, show="*")
            self.api_key_entry.pack(pady=10)

            ttk.Button(
                self.api_frame,
                text="Start Game",
                command=self.start_game,
                style='TButton'
            ).pack(pady=20)
        else:
            # Future opponents can be handled here
            self.start_game()

    def start_game(self):
        if self.ai_opponent in ["gpt", "claude"]:
            self.api_key = self.api_key_entry.get()
            self.api_frame.destroy()
        self.create_board()

    def create_board(self):
        self.board_frame = ttk.Frame(self.master, padding="20")
        self.board_frame.pack(expand=True)

        for row in range(8):
            for col in range(8):
                color = 'light' if (row + col) % 2 == 0 else 'dark'
                button = tk.Button(
                    self.board_frame,
                    image=self.empty_images[color],
                    width=SQUARE_SIZE,
                    height=SQUARE_SIZE,
                    command=lambda r=row, c=col: self.on_square_click(r, c),
                    bd=0,
                    highlightthickness=0
                )
                button.grid(row=row, column=col)
                self.buttons[row][col] = button
        self.update_board()

        # Add a status label
        self.status_label = ttk.Label(self.master, text="Your turn", font=('Helvetica', 14))
        self.status_label.pack(pady=10)

        # Add a scrolled text widget to display AI reasoning
        self.reasoning_text = scrolledtext.ScrolledText(self.master, height=10, wrap=tk.WORD, font=('Helvetica', 12))
        self.reasoning_text.pack(pady=10)
        self.reasoning_text.configure(state='disabled')

        # Add an evaluation label
        self.evaluation_label = ttk.Label(self.master, text="Board Evaluation: N/A", font=('Helvetica', 14))
        self.evaluation_label.pack(pady=10)

        # Initialize evaluation
        self.update_evaluation()

    def update_board(self):
        for row in range(8):
            for col in range(8):
                square = chess.square(col, 7 - row)
                piece = self.board.piece_at(square)
                color = 'light' if (row + col) % 2 == 0 else 'dark'
                if piece:
                    self.buttons[row][col].config(image=self.piece_images[piece.symbol()][color])
                else:
                    self.buttons[row][col].config(image=self.empty_images[color])

    def update_evaluation(self):
        if self.stockfish:
            threading.Thread(target=self.fetch_evaluation, daemon=True).start()
        else:
            self.evaluation_label.config(text="Board Evaluation: N/A")

    def fetch_evaluation(self):
        board_fen = self.board.fen()
        eval_text = self.stockfish.get_evaluation(board_fen)
        self.evaluation_label.config(
            text=f"Board Evaluation: {eval_text}"
        )

    def on_square_click(self, row, col):
        square = chess.square(col, 7 - row)
        if self.selected_square is None:
            piece = self.board.piece_at(square)
            if piece and piece.color == chess.WHITE:
                self.selected_square = square
                self.highlight_square(row, col)
        else:
            move = chess.Move(self.selected_square, square)
            if move in self.board.legal_moves:
                self.make_move(move)
            self.clear_highlights()
            self.selected_square = None
            self.update_board()
            self.update_evaluation()

    def highlight_square(self, row, col):
        square = chess.square(col, 7 - row)
        piece = self.board.piece_at(square)
        if piece:
            self.buttons[row][col].config(image=self.piece_images[piece.symbol()]['highlight'])
        else:
            self.buttons[row][col].config(image=self.empty_images['highlight'])

    def clear_highlights(self):
        for row in range(8):
            for col in range(8):
                color = 'light' if (row + col) % 2 == 0 else 'dark'
                square = chess.square(col, 7 - row)
                piece = self.board.piece_at(square)
                if piece:
                    self.buttons[row][col].config(image=self.piece_images[piece.symbol()][color])
                else:
                    self.buttons[row][col].config(image=self.empty_images[color])

    def make_move(self, move):
        self.board.push(move)
        self.update_board()
        self.update_evaluation()

        if self.board.is_game_over():
            self.game_over()
            return

        # Update status
        self.status_label.config(text="AI is thinking...")
        self.reasoning_text.configure(state='normal')
        self.reasoning_text.delete('1.0', tk.END)
        self.reasoning_text.configure(state='disabled')

        # Make AI move in a separate thread to keep UI responsive
        threading.Thread(target=self.make_ai_move, daemon=True).start()

    def make_ai_move(self):
        reasoning, ai_move = "", ""
        try:
            # Fetch current evaluation from Stockfish
            evaluation = self.stockfish.get_evaluation(self.board.fen())

            # Initialize result queue
            result_queue = Queue()

            # Create and start AI worker
            ai_worker = AIWorker(
                board=self.board,
                evaluation=evaluation,
                ai_type=self.ai_opponent,
                api_key=self.api_key,
                stockfish_engine=self.stockfish,
                result_queue=result_queue
            )
            ai_worker.start()

            # Wait for AI to finish processing
            reasoning, ai_move = result_queue.get()

            if not ai_move:
                raise ValueError("AI did not provide a valid move.")

            # Validate and execute the AI move
            move = chess.Move.from_uci(ai_move.strip())
            if move in self.board.legal_moves:
                self.board.push(move)
                self.update_board()
                self.update_evaluation()
                # Display reasoning
                if reasoning:
                    self.reasoning_text.configure(state='normal')
                    self.reasoning_text.insert(tk.END, reasoning)
                    self.reasoning_text.configure(state='disabled')
                # Update status
                if not self.board.is_game_over():
                    self.status_label.config(text="Your turn")
                else:
                    self.game_over()
            else:
                raise ValueError(f"Invalid move received from AI: {ai_move}")

        except Exception as e:
            logging.error(f"AI Move Error: {e}")
            messagebox.showerror("Error", f"An error occurred: {str(e)}")
            self.master.quit()

    def game_over(self):
        result = self.board.result()
        if result == "1-0":
            message = "You win!"
        elif result == "0-1":
            message = "AI wins!"
        else:
            message = "It's a draw!"
        messagebox.showinfo("Game Over", message)
        if self.stockfish:
            self.stockfish.quit()
        self.master.quit()

    def __del__(self):
        # Ensure Stockfish is terminated properly
        if self.stockfish:
            self.stockfish.quit()


def main():
    root = tk.Tk()
    ChessGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()