ml / app.py
doctorlinux's picture
Upload 2 files
5f40865 verified
raw
history blame
7.25 kB
import gradio as gr
import chess
import chess.pgn
import matplotlib.pyplot as plt
import numpy as np
import io
import re
from io import BytesIO
plt.switch_backend('Agg')
class ReparadorPGN:
@staticmethod
def reparar_pgn(pgn_text):
"""Repara PGNs extremadamente corruptos"""
lineas = pgn_text.split('\n')
lineas_reparadas = []
for linea in lineas:
linea = linea.strip()
# Reparar headers corruptos
if linea.startswith('['):
# Corregir comillas y paréntesis
linea = re.sub(r"\[(.*?)[')](.*?)[')]", r"[\1 '\2']", linea)
# Corregir nombres de headers
linea = re.sub(r'\[Ulnite', '[White', linea)
linea = re.sub(r'\[Result "I-0"\]', '[Result "1-0"]', linea)
linea = re.sub(r'\[Result "O-I"\]', '[Result "0-1"]', linea)
linea = re.sub(r'\[Result "I/2-I/2"\]', '[Result "1/2-1/2"]', linea)
# Reparar notación de movimientos
else:
# Corregir piezas mal escritas
correcciones = {
r'\bnf3\b': 'Nf3', r'\bnc3\b': 'Nc3', r'\bng5\b': 'Ng5',
r'\bhc6\b': 'Nc6', r'\bhf6\b': 'Nf6', r'\bhba\b': 'Nb8',
r'\bhbe7\b': 'Nbd7', r'\bnn1\b': 'Nf1', r'\bhe2\b': 'Ne2',
r'\bnh7\b': 'Nh7', r'\bhc5\b': 'Nc5', r'\bqu2\b': 'Qd2',
r'\bre1\b': 'Re1', r'\brn\b': 'Rf1', r'\bbe4:\b': 'Be4',
r'\bo-o-o\b': 'O-O-O', r'\bo-o\b': 'O-O'
}
for error, correccion in correcciones.items():
linea = re.sub(error, correccion, linea, flags=re.IGNORECASE)
lineas_reparadas.append(linea)
return '\n'.join(lineas_reparadas)
class AnalizadorAjedrez:
def __init__(self):
self.datos_jugadas = []
def analizar_partida(self, pgn_string):
"""Analiza partida con múltiples intentos y reparación"""
intentos = [
pgn_string, # Intento 1: Original
ReparadorPGN.reparar_pgn(pgn_string), # Intento 2: Reparado
self._crear_pgn_minimo(pgn_string) # Intento 3: PGN mínimo
]
for i, pgn_intento in enumerate(intentos):
try:
pgn = io.StringIO(pgn_intento)
game = chess.pgn.read_game(pgn)
if game and len(list(game.mainline_moves())) > 0:
blancas = game.headers.get("White", "Anónimo")
negras = game.headers.get("Black", "Anónimo")
resultado = game.headers.get("Result", "*")
# Analizar jugadas
tablero = game.board()
self.datos_jugadas = []
for jugada_num, move in enumerate(game.mainline_moves(), 1):
tablero.push(move)
analisis = self._evaluar_posicion(tablero, jugada_num)
self.datos_jugadas.append(analisis)
print(f"✅ Análisis exitoso (Intento {i+1})")
return blancas, negras, resultado, ""
except Exception as e:
continue
return "Anónimo", "Anónimo", "*", "No se pudo analizar el PGN (formato muy corrupto)"
def _crear_pgn_minimo(self, pgn_text):
"""Crea un PGN mínimo desde los movimientos"""
# Extraer solo movimientos numéricos
movimientos = re.findall(r'\d+\.\s*(\S+)\s+(\S+)', pgn_text)
if not movimientos:
movimientos = re.findall(r'\b([NBRQK]?[a-h]?[1-8]?x?[a-h][1-8]\+?\+?#?)\b', pgn_text, re.IGNORECASE)
pgn_minimo = """[Event "Partida Reparada"]
[White "Blancas"]
[Black "Negras"]
[Result "*"]
"""
for i, (blanca, negra) in enumerate(movimientos[:50], 1): # Máximo 50 jugadas
pgn_minimo += f"{i}. {blanca} {negra} "
return pgn_minimo.strip()
def _evaluar_posicion(self, tablero, jugada_num):
"""Evaluación simple pero robusta"""
valores = {'p': 1, 'n': 3, 'b': 3, 'r': 5, 'q': 9, 'k': 0}
material_blancas = sum(valores.get(p.symbol().lower(), 0)
for p in tablero.piece_map().values()
if p.color == chess.WHITE)
material_negras = sum(valores.get(p.symbol().lower(), 0)
for p in tablero.piece_map().values()
if p.color == chess.BLACK)
ventaja_material = material_blancas - material_negras
movilidad = len(list(tablero.legal_moves))
return {
'jugada': jugada_num,
'evaluacion': ventaja_material + (movilidad * 0.1),
'material_blancas': material_blancas,
'material_negras': material_negras,
'ventaja_material': ventaja_material,
'movilidad': movilidad
}
def generar_grafico(self, blancas, negras):
"""Genera gráfico incluso con datos mínimos"""
if not self.datos_jugadas:
# Gráfico de error informativo
fig, ax = plt.subplots(figsize=(10, 6))
ax.text(0.5, 0.5, '⚠️ PGN no analizable\n\nEl archivo PGN está corrupto\no tiene formato inválido',
ha='center', va='center', fontsize=14, transform=ax.transAxes,
bbox=dict(boxstyle="round,pad=0.3", facecolor="lightcoral", alpha=0.7))
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_xticks([])
ax.set_yticks([])
return fig
# Gráfico normal con datos
jugadas = [d['jugada'] for d in self.datos_jugadas]
evaluaciones = [d['evaluacion'] for d in self.datos_jugadas]
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
ax1.plot(jugadas, evaluaciones, 'b-', linewidth=2)
ax1.fill_between(jugadas, evaluaciones, alpha=0.3)
ax1.axhline(y=0, color='black', linestyle='--')
ax1.set_title(f'📊 Análisis: {blancas} vs {negras}', fontweight='bold')
ax1.set_ylabel('Ventaja')
ax1.grid(True, alpha=0.3)
material = [d['ventaja_material'] for d in self.datos_jugadas]
ax2.bar(jugadas, material, alpha=0.7, color=['green' if x >= 0 else 'red' for x in material])
ax2.set_xlabel('Jugada')
ax2.set_ylabel('Material')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
return fig
def generar_reporte(self, blancas, negras, resultado):
if not self.datos_jugadas:
reporte = """## ❌ PGN NO ANALIZABLE
**El archivo PGN está corrupto o tiene formato inválido.**
### 🔧 Posibles soluciones:
1. **Verifica que el PGN sea de una partida real**
2. **Comprueba que la notación de movimientos sea correcta**
3. **Intenta con otro archivo PGN**
4. **Los headers deben usar comillas: [Event "Nombre"]**
### 📝 Formato PGN correcto: