Inhalt

Aktueller Ordner: /

chessteg.py

import copy
from typing import List, Tuple, Optional, Dict, Any
import math
import time

# Konstanten
WHITE = 1
BLACK = -1
EMPTY = 0
DUMMY = 100
PAWN = 1
BISHOP = 3
KNIGHT = 4
ROOK = 5
QUEEN = 9
KING = 99

class ChesstegEngine:
    def __init__(self):
        self.board = self.initialize_board()
        self.pieces = []
        self.white_turn = True
        self.evaluation = 0
        self.checkmate = False
        self.stalemate = False
        self.last_move = None
        self.move_history = []
        
        # KI-Einstellungen
        self.ai_settings = {
            'search_depth': 3,
            'extended_evaluation': True,
            'quiescence_search': True,
            'quiescence_depth': 2,
            'timeout_ms': 8000
        }
        
        # Bewertungstabellen
        self.evaluation_table = {
            'material': {
                PAWN: 100,
                KNIGHT: 320,
                BISHOP: 330,
                ROOK: 500,
                QUEEN: 900,
                KING: 20000
            },
            
            'position': {
                PAWN: [
                    [0,   0,   0,   0,   0,   0,   0,   0],
                    [50,  50,  50,  50,  50,  50,  50,  50],
                    [10,  10,  20,  30,  30,  20,  10,  10],
                    [5,   5,  10,  25,  25,  10,   5,   5],
                    [0,   0,   0,  20,  20,   0,   0,   0],
                    [5,  -5, -10,   0,   0, -10,  -5,   5],
                    [5,  10,  10, -20, -20,  10,  10,   5],
                    [0,   0,   0,   0,   0,   0,   0,   0]
                ],
                
                KNIGHT: [
                    [-50, -40, -30, -30, -30, -30, -40, -50],
                    [-40, -20,   0,   5,   5,   0, -20, -40],
                    [-30,   5,  10,  15,  15,  10,   5, -30],
                    [-30,   0,  15,  20,  20,  15,   0, -30],
                    [-30,   5,  15,  20,  20,  15,   5, -30],
                    [-30,   0,  10,  15,  15,  10,   0, -30],
                    [-40, -20,   0,   0,   0,   0, -20, -40],
                    [-50, -40, -30, -30, -30, -30, -40, -50]
                ],
                
                BISHOP: [
                    [-20, -10, -10, -10, -10, -10, -10, -20],
                    [-10,   0,   0,   0,   0,   0,   0, -10],
                    [-10,   0,   5,  10,  10,   5,   0, -10],
                    [-10,   5,   5,  10,  10,   5,   5, -10],
                    [-10,   0,  10,  10,  10,  10,   0, -10],
                    [-10,  10,  10,  10,  10,  10,  10, -10],
                    [-10,   5,   0,   0,   0,   0,   5, -10],
                    [-20, -10, -10, -10, -10, -10, -10, -20]
                ],
                
                ROOK: [
                    [0,   0,   0,   5,   5,   0,   0,   0],
                    [-5,   0,   0,   0,   0,   0,   0,  -5],
                    [-5,   0,   0,   0,   0,   0,   0,  -5],
                    [-5,   0,   0,   0,   0,   0,   0,  -5],
                    [-5,   0,   0,   0,   0,   0,   0,  -5],
                    [-5,   0,   0,   0,   0,   0,   0,  -5],
                    [5,  10,  10,  10,  10,  10,  10,   5],
                    [0,   0,   0,   0,   0,   0,   0,   0]
                ],
                
                QUEEN: [
                    [-20, -10, -10, -5, -5, -10, -10, -20],
                    [-10,   0,   5,  0,  0,   0,   0, -10],
                    [-10,   5,   5,  5,  5,   5,   0, -10],
                    [0,     0,   5,  5,  5,   5,   0,  -5],
                    [-5,    0,   5,  5,  5,   5,   0,  -5],
                    [-10,   0,   5,  5,  5,   5,   0, -10],
                    [-10,   0,   0,  0,  0,   0,   0, -10],
                    [-20, -10, -10, -5, -5, -10, -10, -20]
                ],
                
                KING: [
                    [20,  30,  10,   0,   0,  10,  30,  20],
                    [20,  20,   0,   0,   0,   0,  20,  20],
                    [-10, -20, -20, -20, -20, -20, -20, -10],
                    [-20, -30, -30, -40, -40, -30, -30, -20],
                    [-30, -40, -40, -50, -50, -40, -40, -30],
                    [-30, -40, -40, -50, -50, -40, -40, -30],
                    [-30, -40, -40, -50, -50, -40, -40, -30],
                    [-30, -40, -40, -50, -50, -40, -40, -30]
                ]
            }
        }

        self.move_counter = 0
        self.node_counter = 0
        self.calculation_start_time = 0
        
        self.initialize_pieces()

    def initialize_board(self):
        board = [DUMMY] * 120
        for row in range(2, 10):
            for col in range(1, 9):
                board[row * 10 + col] = EMPTY
        return board

    def add_piece(self, piece_type, color, position):
        self.pieces.append({'type': piece_type, 'color': color, 'pos': position, 'captured': False})
        self.board[position] = piece_type * color

    def initialize_pieces(self):
        self.pieces = []
        # Könige
        self.add_piece(KING, BLACK, 95)
        self.add_piece(KING, WHITE, 25)
        # Damen
        self.add_piece(QUEEN, BLACK, 94)
        self.add_piece(QUEEN, WHITE, 24)
        # Türme
        self.add_piece(ROOK, BLACK, 91)
        self.add_piece(ROOK, BLACK, 98)
        self.add_piece(ROOK, WHITE, 21)
        self.add_piece(ROOK, WHITE, 28)
        # Springer
        self.add_piece(KNIGHT, BLACK, 92)
        self.add_piece(KNIGHT, BLACK, 97)
        self.add_piece(KNIGHT, WHITE, 22)
        self.add_piece(KNIGHT, WHITE, 27)
        # Läufer
        self.add_piece(BISHOP, BLACK, 93)
        self.add_piece(BISHOP, BLACK, 96)
        self.add_piece(BISHOP, WHITE, 23)
        self.add_piece(BISHOP, WHITE, 26)
        # Bauern
        for i in range(1, 9):
            self.add_piece(PAWN, BLACK, 80 + i)
            self.add_piece(PAWN, WHITE, 30 + i)

    def is_opponent(self, color, field_value):
        if field_value == EMPTY or field_value == DUMMY:
            return False
        return (color > 0 and field_value < 0) or (color < 0 and field_value > 0)

    def generate_moves(self, color):
        all_moves = []
        
        for piece in self.pieces:
            if not piece['captured'] and piece['color'] == color:
                piece_moves = self.generate_piece_moves(piece)
                all_moves.extend(piece_moves)
        
        return [move for move in all_moves if self.is_move_legal(move)]

    def generate_piece_moves(self, piece):
        piece_type = piece['type']
        if piece_type == ROOK:
            return self.generate_rook_moves(piece)
        elif piece_type == BISHOP:
            return self.generate_bishop_moves(piece)
        elif piece_type == QUEEN:
            return self.generate_queen_moves(piece)
        elif piece_type == KNIGHT:
            return self.generate_knight_moves(piece)
        elif piece_type == KING:
            return self.generate_king_moves(piece)
        elif piece_type == PAWN:
            return self.generate_pawn_moves(piece)
        else:
            return []

    def generate_rook_moves(self, piece):
        moves = []
        directions = [1, -1, 10, -10]
        
        for direction in directions:
            field = piece['pos'] + direction
            
            while self.board[field] != DUMMY:
                if self.board[field] == EMPTY:
                    moves.append(self.create_move(piece, field))
                else:
                    if self.is_opponent(piece['color'], self.board[field]):
                        moves.append(self.create_move(piece, field, True))
                    break
                field += direction
        return moves

    def generate_bishop_moves(self, piece):
        moves = []
        directions = [9, 11, -9, -11]
        
        for direction in directions:
            field = piece['pos'] + direction
            
            while self.board[field] != DUMMY:
                if self.board[field] == EMPTY:
                    moves.append(self.create_move(piece, field))
                else:
                    if self.is_opponent(piece['color'], self.board[field]):
                        moves.append(self.create_move(piece, field, True))
                    break
                field += direction
        return moves

    def generate_queen_moves(self, piece):
        return self.generate_rook_moves(piece) + self.generate_bishop_moves(piece)

    def generate_knight_moves(self, piece):
        moves = []
        knight_moves = [8, 12, 19, 21, -8, -12, -19, -21]
        
        for move in knight_moves:
            field = piece['pos'] + move
            if self.board[field] != DUMMY:
                if self.board[field] == EMPTY:
                    moves.append(self.create_move(piece, field))
                elif self.is_opponent(piece['color'], self.board[field]):
                    moves.append(self.create_move(piece, field, True))
        return moves

    def generate_king_moves(self, piece):
        moves = []
        king_moves = [1, -1, 10, -10, 9, 11, -9, -11]
        
        for move in king_moves:
            field = piece['pos'] + move
            if self.board[field] != DUMMY:
                if self.board[field] == EMPTY:
                    moves.append(self.create_move(piece, field))
                elif self.is_opponent(piece['color'], self.board[field]):
                    moves.append(self.create_move(piece, field, True))
        return moves

    def generate_pawn_moves(self, piece):
        moves = []
        forward = 10 if piece['color'] == WHITE else -10
        start_row = 3 if piece['color'] == WHITE else 8
        current_row = piece['pos'] // 10

        # Ein Feld vorwärts
        field = piece['pos'] + forward
        if self.board[field] == EMPTY:
            moves.append(self.create_move(piece, field))
            
            # Zwei Felder vorwärts von Startposition
            if current_row == start_row:
                double_field = field + forward
                if self.board[double_field] == EMPTY:
                    moves.append(self.create_move(piece, double_field))

        # Schlagen
        for side in [forward + 1, forward - 1]:
            field = piece['pos'] + side
            if self.board[field] != DUMMY and self.board[field] != EMPTY:
                if self.is_opponent(piece['color'], self.board[field]):
                    moves.append(self.create_move(piece, field, True))

        return moves

    def create_move(self, piece, target_pos, is_capture=False):
        captured_piece = self.find_captured_piece(target_pos) if is_capture else None
        return {
            'from_pos': piece['pos'],
            'to_pos': target_pos,
            'type': piece['type'],
            'color': piece['color'],
            'captured': captured_piece
        }

    def is_move_legal(self, move):
        if move['from_pos'] == move['to_pos']:
            return False
        
        if self.board[move['from_pos']] == EMPTY or self.board[move['from_pos']] == DUMMY:
            return False
        
        piece_value = self.board[move['from_pos']]
        is_white_turn = self.white_turn
        
        if ((is_white_turn and piece_value < 0) or 
            (not is_white_turn and piece_value > 0)):
            return False
        
        target_value = self.board[move['to_pos']]
        if target_value != EMPTY and target_value != DUMMY:
            if ((is_white_turn and target_value > 0) or 
                (not is_white_turn and target_value < 0)):
                return False

        # Schach-Prüfung
        original_board = self.board.copy()
        original_pieces = copy.deepcopy(self.pieces)

        # Temporären Zug ausführen
        self.board[move['from_pos']] = EMPTY
        self.board[move['to_pos']] = move['type'] * move['color']
        
        moving_piece = next((p for p in self.pieces 
                           if p['pos'] == move['from_pos'] and 
                           not p['captured'] and p['color'] == move['color']), None)

        if moving_piece:
            original_pos = moving_piece['pos']
            moving_piece['pos'] = move['to_pos']
            
            if move['captured']:
                captured_piece_temp = next((p for p in self.pieces 
                                          if p['pos'] == move['to_pos'] and 
                                          not p['captured'] and p['color'] == -move['color']), None)
                if captured_piece_temp:
                    captured_piece_temp['captured'] = True
        
            in_check = self.is_king_in_check(move['color'])
        
            # Zustand wiederherstellen
            self.board = original_board
            self.pieces = original_pieces
            
            return not in_check
        
        # Fallback
        self.board = original_board
        self.pieces = original_pieces
        return False

    def find_captured_piece(self, position):
        return next((p for p in self.pieces 
                   if p['pos'] == position and not p['captured'] and 
                   ((self.white_turn and p['color'] == BLACK) or 
                    (not self.white_turn and p['color'] == WHITE))), None)

    def execute_move(self, move):
        if not self.is_move_legal(move):
            print(f"Move is not legal: {self.move_to_notation(move)}")
            return False

        self.board[move['from_pos']] = EMPTY
        self.board[move['to_pos']] = move['type'] * move['color']

        piece = next((p for p in self.pieces 
                     if p['pos'] == move['from_pos'] and 
                     not p['captured'] and p['color'] == move['color']), None)
        
        if piece:
            piece['pos'] = move['to_pos']
            
            if move['captured']:
                captured = next((p for p in self.pieces 
                               if p['pos'] == move['to_pos'] and 
                               not p['captured'] and p['color'] == -move['color']), None)
                if captured:
                    captured['captured'] = True

        self.white_turn = not self.white_turn
        self.last_move = copy.deepcopy(move)
        self.move_history.append(copy.deepcopy(move))
        
        self.check_game_status()
        
        print(f"Move executed: {self.move_to_notation(move)}")
        return True

    def copy_state(self):
        return {
            'board': self.board.copy(),
            'pieces': copy.deepcopy(self.pieces),
            'white_turn': self.white_turn,
            'evaluation': self.evaluation,
            'checkmate': self.checkmate,
            'stalemate': self.stalemate
        }

    def restore_state(self, state):
        self.board = state['board'].copy()
        self.pieces = copy.deepcopy(state['pieces'])
        self.white_turn = state['white_turn']
        self.evaluation = state['evaluation']
        self.checkmate = state['checkmate']
        self.stalemate = state['stalemate']

    def undo_move(self):
        if not self.last_move:
            return False

        move = self.last_move
        
        self.board[move['from_pos']] = move['type'] * move['color']
        self.board[move['to_pos']] = (move['captured']['type'] * move['captured']['color'] 
                                    if move['captured'] else EMPTY)

        piece = next((p for p in self.pieces 
                     if p['pos'] == move['to_pos'] and 
                     not p['captured'] and p['color'] == move['color']), None)
        if piece:
            piece['pos'] = move['from_pos']

        if move['captured']:
            captured = next((p for p in self.pieces 
                           if p['type'] == move['captured']['type'] and 
                           p['color'] == move['captured']['color'] and p['captured']), None)
            if captured:
                captured['captured'] = False
                captured['pos'] = move['to_pos']

        self.white_turn = not self.white_turn
        self.last_move = None
        self.checkmate = False
        self.stalemate = False
        
        return True

    # KI-Funktionen
    def computer_move(self):
        print("=== KI MOVE CALCULATION STARTED ===")
        start_time = time.time()
        self.calculation_start_time = start_time
        self.node_counter = 0
        self.move_counter = 0
        
        current_color = WHITE if self.white_turn else BLACK
        all_moves = self.generate_moves(current_color)
        
        if not all_moves:
            print("No legal moves available")
            return None

        # Eröffnungszug prüfen
        opening_move = self.check_opening_move(all_moves)
        if opening_move:
            print(f"Opening move used: {self.move_to_notation(opening_move)}")
            return opening_move

        # Züge sortieren
        all_moves = self.sort_moves(all_moves, current_color)
        
        # Alpha-Beta-Suche
        best_move = all_moves[0]  # Fallback
        best_evaluation = -math.inf if current_color == WHITE else math.inf
        
        print(f"Searching with depth {self.ai_settings['search_depth']}")
        print(f"Available moves: {len(all_moves)}")
        
        for move in all_moves:
            # Timeout-Check
            if time.time() - start_time > self.ai_settings['timeout_ms'] / 1000:
                print("Timeout - using best move so far")
                break
            
            self.move_counter += 1
            
            state_before_move = self.copy_state()
            self.execute_temp_move(move)
            
            if current_color == WHITE:
                evaluation = self.alpha_beta(
                    self.ai_settings['search_depth'] - 1, 
                    -math.inf, 
                    math.inf, 
                    False,
                    start_time
                )
            else:
                evaluation = self.alpha_beta(
                    self.ai_settings['search_depth'] - 1, 
                    -math.inf, 
                    math.inf, 
                    True,
                    start_time
                )
            
            self.restore_state(state_before_move)
            
            print(f"Move {self.move_counter}: {self.move_to_notation(move)} -> Evaluation: {evaluation}")
            
            if current_color == WHITE:
                if evaluation > best_evaluation:
                    best_evaluation = evaluation
                    best_move = move
            else:
                if evaluation < best_evaluation:
                    best_evaluation = evaluation
                    best_move = move
        
        end_time = time.time()
        print(f"=== KI MOVE CALCULATION COMPLETED ===")
        print(f"Best move: {self.move_to_notation(best_move)}")
        print(f"Evaluation: {best_evaluation}")
        print(f"Calculated nodes: {self.node_counter}")
        print(f"Time needed: {(end_time - start_time) * 1000:.0f}ms")
        
        return best_move

    def alpha_beta(self, depth, alpha, beta, maximizing, start_time):
        # Timeout-Check
        if time.time() - start_time > self.ai_settings['timeout_ms'] / 1000:
            return -10000 if maximizing else 10000
        
        self.node_counter += 1
        
        # Blattknoten oder Endstellung
        if depth == 0:
            if self.ai_settings['quiescence_search']:
                return self.quiescence_search(alpha, beta, maximizing, start_time)
            return self.evaluate_position()
        
        current_color = WHITE if maximizing else BLACK
        moves = self.generate_moves(current_color)
        
        # Terminale Stellungen
        if not moves:
            if self.is_king_in_check(current_color):
                return (-20000 + depth) if maximizing else (20000 - depth)
            return 0
        
        # Züge sortieren
        moves = self.sort_moves(moves, current_color)
        
        if maximizing:
            max_evaluation = -math.inf
            
            for move in moves:
                state_before_move = self.copy_state()
                self.execute_temp_move(move)
                
                evaluation = self.alpha_beta(depth - 1, alpha, beta, False, start_time)
                
                self.restore_state(state_before_move)
                
                max_evaluation = max(max_evaluation, evaluation)
                alpha = max(alpha, evaluation)
                
                if beta <= alpha:
                    break  # Beta-Cutoff
            
            return max_evaluation
        else:
            min_evaluation = math.inf
            
            for move in moves:
                state_before_move = self.copy_state()
                self.execute_temp_move(move)
                
                evaluation = self.alpha_beta(depth - 1, alpha, beta, True, start_time)
                
                self.restore_state(state_before_move)
                
                min_evaluation = min(min_evaluation, evaluation)
                beta = min(beta, evaluation)
                
                if beta <= alpha:
                    break  # Alpha-Cutoff
            
            return min_evaluation

    def quiescence_search(self, alpha, beta, maximizing, start_time):
        stand_evaluation = self.evaluate_position()
        
        if maximizing:
            if stand_evaluation >= beta:
                return beta
            alpha = max(alpha, stand_evaluation)
        else:
            if stand_evaluation <= alpha:
                return alpha
            beta = min(beta, stand_evaluation)
        
        # Nur Schlagzüge generieren
        capture_moves = self.generate_capture_moves(WHITE if maximizing else BLACK)
        
        if not capture_moves:
            return stand_evaluation
        
        # Timeout-Check
        if time.time() - start_time > self.ai_settings['timeout_ms'] / 1000:
            return stand_evaluation
        
        if maximizing:
            for move in capture_moves:
                state_before_move = self.copy_state()
                self.execute_temp_move(move)
                
                evaluation = self.quiescence_search(alpha, beta, False, start_time)
                
                self.restore_state(state_before_move)
                
                alpha = max(alpha, evaluation)
                if beta <= alpha:
                    break
            return alpha
        else:
            for move in capture_moves:
                state_before_move = self.copy_state()
                self.execute_temp_move(move)
                
                evaluation = self.quiescence_search(alpha, beta, True, start_time)
                
                self.restore_state(state_before_move)
                
                beta = min(beta, evaluation)
                if beta <= alpha:
                    break
            return beta

    def sort_moves(self, moves, color):
        def move_key(move):
            # Schlagzüge zuerst
            capture_value = (self.evaluation_table['material'][move['captured']['type']] 
                           if move['captured'] else 0)
            
            # Zentrumszüge bevorzugen
            center_value = self.calculate_center_value(move['to_pos'])
            
            return (-capture_value, -center_value)  # Höhere Werte zuerst
        
        return sorted(moves, key=move_key)

    def check_opening_move(self, moves):
        move_number = len(self.move_history)
        
        # Nur in den ersten 8 Zügen Eröffnungslogik anwenden
        if move_number >= 8:
            return None
        
        # Zentrumsbauern bevorzugen
        center_moves = [move for move in moves 
                       if move['type'] == PAWN and 
                       move['to_pos'] in [44, 45, 54, 55]]
        
        if center_moves:
            return center_moves[0]
        
        # Springer entwickeln
        knight_moves = [move for move in moves 
                       if move['type'] == KNIGHT and 
                       move['to_pos'] in [33, 36, 63, 66]]
        
        if knight_moves:
            return knight_moves[0]
        
        return None

    def calculate_center_value(self, position):
        row, col = self.position_to_coordinates(position)
        center_row = 5.5  # Zwischen Reihe 5 und 6
        center_col = 4.5  # Zwischen Linie 4 und 5
        
        row_distance = abs(row - center_row)
        col_distance = abs(col - center_col)
        
        # Je näher am Zentrum, desto höher der Wert
        return 10 - (row_distance + col_distance)

    def generate_capture_moves(self, color):
        all_moves = self.generate_moves(color)
        return [move for move in all_moves if move['captured'] is not None]

    def evaluate_position(self):
        if self.checkmate:
            return -20000 if self.white_turn else 20000
        
        if self.stalemate:
            return 0
        
        material = 0
        position = 0
        development_advantage = self.calculate_development_advantage()
        center_control = self.calculate_center_control()
        pawn_structure = self.calculate_pawn_structure()
        
        # Material und Positionsbewertung
        for piece in self.pieces:
            if piece['captured']:
                continue
            
            piece_value = self.evaluation_table['material'][piece['type']]
            pos_value = self.calculate_position_value(piece)
            
            if piece['color'] == WHITE:
                material += piece_value
                position += pos_value
            else:
                material -= piece_value
                position -= pos_value
        
        # Verbesserte Gewichtung
        total_evaluation = (
            material + 
            position * 0.1 + 
            development_advantage * 2 + 
            center_control * 1.5 +
            pawn_structure * 0.5
        )
            
        self.evaluation = round(total_evaluation)
        return self.evaluation

    def calculate_position_value(self, piece):
        if not self.ai_settings['extended_evaluation']:
            return 0
        
        board_row, board_col = self.position_to_coordinates(piece['pos'])
        
        row = board_row - 2 if piece['color'] == WHITE else 7 - (board_row - 2)
        col = board_col - 1
        
        row = max(0, min(7, row))
        col = max(0, min(7, col))
        
        return self.evaluation_table['position'][piece['type']][row][col]

    def position_to_coordinates(self, position):
        row = position // 10
        col = position % 10
        return row, col

    def calculate_development_advantage(self):
        advantage = 0
        
        developed_pieces = [piece for piece in self.pieces 
                          if not piece['captured'] and 
                          (piece['type'] == KNIGHT or piece['type'] == BISHOP) and
                          self.is_piece_developed(piece)]
        
        for piece in developed_pieces:
            advantage += 20 if piece['color'] == WHITE else -20
        
        return advantage

    def is_piece_developed(self, piece):
        start_row = 2 if piece['color'] == WHITE else 9
        return piece['pos'] // 10 != start_row

    def calculate_center_control(self):
        center_fields = [44, 45, 54, 55]
        control = 0
        
        for field in center_fields:
            if self.board[field] != EMPTY:
                piece = next((p for p in self.pieces 
                            if p['pos'] == field and not p['captured']), None)
                if piece:
                    # Höhere Werte für stärkere Figuren
                    piece_value = self.evaluation_table['material'][piece['type']] / 100
                    control += piece_value * 5 if piece['color'] == WHITE else -piece_value * 5
        
        return control

    def calculate_pawn_structure(self):
        evaluation = 0
        
        # Doppelbauern bestrafen
        pawns_per_file = {}

        for piece in self.pieces:
            if not piece['captured'] and piece['type'] == PAWN:
                file = piece['pos'] % 10
                key = f"{file}-{piece['color']}"
                pawns_per_file[key] = pawns_per_file.get(key, 0) + 1

        for key, count in pawns_per_file.items():
            if count > 1:
                color = WHITE if key.endswith(str(WHITE)) else BLACK
                evaluation += -20 if color == WHITE else 20
        
        return evaluation

    def move_to_notation(self, move):
        from_pos = self.position_to_notation(move['from_pos'])
        to_pos = self.position_to_notation(move['to_pos'])
        return f"{from_pos}{to_pos}"

    def execute_temp_move(self, move):
        self.board[move['from_pos']] = EMPTY
        self.board[move['to_pos']] = move['type'] * move['color']
        
        piece = next((p for p in self.pieces 
                     if p['pos'] == move['from_pos'] and 
                     not p['captured'] and p['color'] == move['color']), None)
        
        if piece:
            piece['pos'] = move['to_pos']
            
            if move['captured']:
                captured = next((p for p in self.pieces 
                               if p['pos'] == move['to_pos'] and 
                               not p['captured'] and p['color'] == -move['color']), None)
                if captured:
                    captured['captured'] = True

    # Spielregeln
    def is_checkmate(self, color):
        if self.is_king_in_check(color):
            legal_moves = self.generate_moves(color)
            return len(legal_moves) == 0
        return False

    def is_stalemate(self, color):
        if not self.is_king_in_check(color):
            legal_moves = self.generate_moves(color)
            return len(legal_moves) == 0
        return False

    def is_king_in_check(self, color):
        king = next((p for p in self.pieces 
                   if p['type'] == KING and p['color'] == color and not p['captured']), None)
        
        if not king:
            return False

        opponent_color = -color
        
        for piece in self.pieces:
            if piece['captured'] or piece['color'] != opponent_color:
                continue
            
            attack_moves = self.generate_piece_moves(piece)

            for move in attack_moves:
                if move['to_pos'] == king['pos']:
                    return True
        
        return False

    def check_game_status(self):
        current_color = WHITE if self.white_turn else BLACK
        
        if self.is_checkmate(current_color):
            self.checkmate = True
            self.stalemate = False
        elif self.is_stalemate(current_color):
            self.stalemate = True
            self.checkmate = False
        else:
            self.checkmate = False
            self.stalemate = False

    def position_to_notation(self, position):
        files = ['', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '']
        row = position // 10
        file = position % 10
        return f"{files[file]}{row - 1}"

    def get_piece_name(self, piece_value):
        color = 'White' if piece_value > 0 else 'Black'
        piece_type = abs(piece_value)
        
        names = {
            PAWN: 'Pawn',
            ROOK: 'Rook',
            BISHOP: 'Bishop',
            KNIGHT: 'Knight',
            QUEEN: 'Queen',
            KING: 'King'
        }
        
        return f"{color} {names.get(piece_type, 'Unknown')}"