""" Chessteg Evaluation Module - KORRIGIERTE VERSION MIT DEBUG Vollständige Bewertungsfunktion für Schachstellungen """ import math from typing import Dict, Any, List class PositionEvaluator: """ Bewertet Schachstellungen basierend auf Material, Position und strategischen Faktoren """ def __init__(self, engine): self.engine = engine # Erweiterte Bewertungstabellen self.evaluation_table = { 'material': { 1: 100, # PAWN 4: 320, # KNIGHT 3: 330, # BISHOP 5: 500, # ROOK 9: 900, # QUEEN 99: 20000 # KING }, 'position': { # Bauer - Positionstabelle 1: [ [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] ], # Springer - Positionstabelle 4: [ [-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] ], # Läufer - Positionstabelle 3: [ [-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] ], # Turm - Positionstabelle 5: [ [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] ], # Dame - Positionstabelle 9: [ [-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] ], # König - Positionstabelle 99: [ [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] ] } } # Debug-Zähler self.eval_counter = 0 def evaluate_position(self) -> int: """ Vollständige Stellungsbewertung - KORRIGIERTE VERSION MIT DEBUG """ self.eval_counter += 1 # Terminal-Stellungen zuerst prüfen if self.engine.checkmate: current_color = 1 if self.engine.white_turn else -1 if self.engine.is_king_in_check(current_color): score = -30000 if current_color == 1 else 30000 if self.eval_counter <= 5: print(f" ♟️ MATT Bewertung: {score}") return score if self.engine.stalemate: if self.eval_counter <= 5: print(f" 🤝 PATT Bewertung: 0") return 0 # Einfache Materialbewertung zuerst testen material = self._evaluate_material() # 🚨 DEBUG: Zeige erste Bewertungen if self.eval_counter <= 5: print(f" 📊 Bewertung #{self.eval_counter}: Material = {material}") # Nur Material für erste Tests - später erweitern total_score = material if self.eval_counter <= 5: print(f" 📈 Gesamtbewertung: {total_score}") return total_score def _evaluate_material(self) -> int: """ Einfache Materialbewertung - KORRIGIERTE VERSION """ material = 0 for piece in self.engine.pieces: if piece['captured']: continue piece_value = self.evaluation_table['material'].get(piece['type'], 0) if piece['color'] == 1: # WHITE material += piece_value else: # BLACK material -= piece_value return material def _evaluate_piece_squares(self) -> int: """ Bewertet die Position der Figuren auf dem Brett """ position_score = 0 for piece in self.engine.pieces: if piece['captured']: continue board_row, board_col = self._position_to_coordinates(piece['position']) # Für weiße Figuren: Tabelle von unten nach oben # Für schwarze Figuren: Tabelle spiegeln if piece['color'] == 1: # WHITE row = board_row - 2 # 0-7 von weißer Seite else: # BLACK row = 7 - (board_row - 2) # 0-7 von schwarzer Seite col = board_col - 1 # 0-7 # Sicherstellen, dass Indizes im gültigen Bereich row = max(0, min(7, row)) col = max(0, min(7, col)) # Positionswert aus Tabelle holen pos_value = self.evaluation_table['position'][piece['type']][row][col] if piece['color'] == 1: position_score += pos_value else: position_score -= pos_value return position_score def _evaluate_attacks(self) -> int: """ Bewertet Angriffe auf gegnerische Figuren """ attack_score = 0 for piece in self.engine.pieces: if piece['captured']: continue # Angriffene Felder dieser Figur attacked_squares = self._get_attacked_squares(piece) for square in attacked_squares: target_piece = self._get_piece_at(square) if target_piece and target_piece['color'] != piece['color']: # Bonus für Angriff auf gegnerische Figur target_value = self.evaluation_table['material'][target_piece['type']] attack_bonus = target_value * 0.1 # 10% des Figurenwerts if piece['color'] == 1: attack_score += attack_bonus else: attack_score -= attack_bonus return attack_score def _evaluate_defense(self) -> int: """ Bewertet Verteidigung eigener Figuren """ defense_score = 0 for piece in self.engine.pieces: if piece['captured']: continue # Zähle Verteidiger dieser Figur defenders = self._get_defenders(piece) piece_value = self.evaluation_table['material'][piece['type']] # Bonus für verteidigte Figuren defense_bonus = len(defenders) * piece_value * 0.05 # 5% pro Verteidiger if piece['color'] == 1: defense_score += defense_bonus else: defense_score -= defense_bonus return defense_score def _evaluate_king_safety(self) -> int: """ Bewertet Sicherheit der Könige """ safety_score = 0 for color in [1, -1]: king = self._get_king(color) if not king: continue king_pos = king['position'] king_row, king_col = self._position_to_coordinates(king_pos) # Strafe für exponierten König in der Mitte if 3 <= king_col <= 6: # König in der Mitte safety_penalty = -30 else: # König am Rand (sicherer) safety_penalty = 10 # Zusätzliche Strafe wenn keine Bauern um den König pawn_shield = self._count_pawn_shield(king_pos, color) safety_penalty += (3 - pawn_shield) * -10 # Bis zu -30 Strafe if color == 1: safety_score += safety_penalty else: safety_score -= safety_penalty return safety_score def _evaluate_mobility(self) -> int: """ Bewertet Bewegungsfreiheit der Figuren """ mobility_score = 0 for piece in self.engine.pieces: if piece['captured'] or piece['type'] == 99: # König ausgeschlossen continue # KORREKTUR: Verwende move_generator statt engine direkt possible_moves = self.engine.move_generator.generate_piece_moves(piece) move_count = len(possible_moves) # Mobilitätsbonus basierend auf Figurentyp mobility_bonus = move_count * self._get_mobility_weight(piece['type']) if piece['color'] == 1: mobility_score += mobility_bonus else: mobility_score -= mobility_bonus return mobility_score def _evaluate_center_control(self) -> int: """ Bewertet Kontrolle des Zentrums """ center_score = 0 center_fields = [44, 45, 54, 55] # d4, e4, d5, e5 for field in center_fields: # Figur auf Zentrumsfeld piece = self._get_piece_at(field) if piece: piece_value = self.evaluation_table['material'][piece['type']] / 100 if piece['color'] == 1: center_score += piece_value * 5 else: center_score -= piece_value * 5 # Angriffe auf Zentrumsfelder attackers = self._get_attackers(field) for attacker in attackers: attacker_value = self.evaluation_table['material'][attacker['type']] / 100 if attacker['color'] == 1: center_score += attacker_value * 2 else: center_score -= attacker_value * 2 return center_score def _evaluate_pawn_structure(self) -> int: """ Bewertet Bauernstruktur """ structure_score = 0 # Doppelbauern bestrafen pawns_per_file = {} for piece in self.engine.pieces: if not piece['captured'] and piece['type'] == 1: # PAWN file = piece['position'] % 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 = 1 if key.endswith("1") else -1 double_pawn_penalty = -20 * (count - 1) # -20 pro zusätzlichem Bauer if color == 1: structure_score += double_pawn_penalty else: structure_score -= double_pawn_penalty return structure_score # ========================================================================= # HILFSFUNKTIONEN # ========================================================================= def _position_to_coordinates(self, position: int) -> tuple: """Konvertiert interne Position zu (row, col)""" row = position // 10 col = position % 10 return row, col def _get_piece_at(self, position: int) -> Any: """Gibt Figur an einer Position zurück""" for piece in self.engine.pieces: if piece['position'] == position and not piece['captured']: return piece return None def _get_attacked_squares(self, piece: Dict[str, Any]) -> List[int]: """Gibt alle von einer Figur angegriffenen Felder zurück""" # Verwende die MoveGenerator-Funktion falls verfügbar if hasattr(self.engine.move_generator, 'get_attacked_squares'): return self.engine.move_generator.get_attacked_squares(piece) # Fallback: Einfache Implementierung attacked_squares = [] piece_type = piece['type'] if piece_type == 1: # PAWN color = piece['color'] forward = 10 if color == 1 else -10 for side in [forward + 1, forward - 1]: field = piece['position'] + side if self.engine.board[field] != 100: # Nicht DUMMY attacked_squares.append(field) # Für andere Figurentypen: Vereinfachte Logik # In der Praxis sollte dies vom MoveGenerator kommen return attacked_squares def _get_defenders(self, piece: Dict[str, Any]) -> List[Dict[str, Any]]: """Findet Verteidiger einer Figur""" defenders = [] for potential_defender in self.engine.pieces: if (potential_defender['captured'] or potential_defender['color'] != piece['color']): continue attacked_squares = self._get_attacked_squares(potential_defender) if piece['position'] in attacked_squares: defenders.append(potential_defender) return defenders def _get_king(self, color: int) -> Any: """Findet König einer Farbe""" for piece in self.engine.pieces: if (piece['type'] == 99 and piece['color'] == color and not piece['captured']): return piece return None def _count_pawn_shield(self, king_pos: int, color: int) -> int: """Zählt Bauern in der Nähe des Königs""" king_row, king_col = self._position_to_coordinates(king_pos) pawn_count = 0 # Prüfe Bauern vor dem König for file_offset in [-1, 0, 1]: check_file = king_col + file_offset if 1 <= check_file <= 8: if color == 1: # Weißer König pawn_row = king_row - 1 # Reihe vor dem König else: # Schwarzer König pawn_row = king_row + 1 # Reihe vor dem König pawn_pos = pawn_row * 10 + check_file pawn = self._get_piece_at(pawn_pos) if pawn and pawn['type'] == 1 and pawn['color'] == color: pawn_count += 1 return pawn_count def _get_mobility_weight(self, piece_type: int) -> int: """Gibt Mobilitätsgewicht für Figurentyp zurück""" weights = { 1: 1, # PAWN 4: 3, # KNIGHT 3: 2, # BISHOP 5: 2, # ROOK 9: 1 # QUEEN } return weights.get(piece_type, 1) def _get_attackers(self, position: int) -> List[Dict[str, Any]]: """Findet alle Figuren, die ein Feld angreifen""" attackers = [] for piece in self.engine.pieces: if piece['captured']: continue attacked_squares = self._get_attacked_squares(piece) if position in attacked_squares: attackers.append(piece) return attackers # Test des Evaluators if __name__ == "__main__": print("Testing PositionEvaluator...") from core import ChesstegEngine engine = ChesstegEngine() evaluator = PositionEvaluator(engine) eval = evaluator.evaluate_position() print(f"Initial position evaluation: {eval}") # Test spezifischer Komponenten material = evaluator._evaluate_material() position = evaluator._evaluate_piece_squares() print(f"Material: {material}, Position: {position}")