Cap. 6 - Contar las hojas sin tasarlas

cap. 6 Prueba · Generador

Contar las hojas sin tasarlas

Perft en Mango AC
José Andrés Morales Linares
Madrid, España

Alfa-beta elige. Perft cuenta. Dada una posición y una profundidad, ¿cuántas partidas legales hay si cada bando juega todas las jugadas legales hasta ese ply? No se evalúa. No se poda. No se ordena. Solo hacer, filtrar jaque, recursión, deshacer. Si el número no coincide con las tablas conocidas del mundo, el generador está roto.

Lo escribí en 2012 en perft.c. No he reescrito el algoritmo. Este artículo empieza por para qué sirve un contador, no por el récord de nodos por segundo.

Por qué contar sin jugar

Analogía: contar cuántas puertas hay en el edificio, no decidir cuál es la mejor vivienda. Un error de un peón al paso, un enroque a través de jaque o una promoción mal empaquetada se come (o infla) millones de hojas a profundidad 5. La evaluación no lo delata: el motor juega, pero el árbol está cojo. Perft sí lo delata, porque el número es un entero seco que se compara con Wikipedia, con Crafty, con cualquier motor que ya pasó la prueba.

En consola: perft n. Arranca el cronómetro, llama a perft(0, n) y al terminar imprime nodos, milisegundos y un desglose.

El recuento

Profundidad 0 es una hoja: se devuelve 1. No se genera nada. Eso es el convenio de Perft: las hojas son posiciones, no jugadas.

uint64 perft(int capa, int profundidad)
{
    if (profundidad == 0) return 1;
    generarTodosMov(...);
    for (cada candidata) {
        hacerMovimiento(mov);
        if (!esAtacadoPor(rey_propio, rival))
            numNodos += perft(capa + 1, profundidad - 1);
        desHacerMovimiento(mov);
    }
    return numNodos;
}

El generador es pseudo-legal. El filtro es el mismo que en la búsqueda: si el rey propio queda atacado, esa rama no existe. Por eso Perft valida a la vez el bitboard, el empaquetado de 28 bits, hacer/deshacer y esAtacadoPor. Un fallo en cualquiera de los cuatro miente el total.

El desglose DATA_PERFT

En el último ply (profundidad == 1) se incrementan contadores: capturas, al paso, promociones, enroque corto, enroque largo, y si esa hoja deja al rival en jaque. No hace falta para el número de nodos. Sirve para cazar dónde se rompió el generador: si el total cuadra pero las capturas no, el bug está en comer, no en el silencio.

Campo Qué cuenta (en el último ply)
InvCaptura Capturas
InvPeonPaso Al paso
InvPromocion Promociones
InvEnroqueOO / OOO Enroques
InvJaqueContrario Hojas que dejan al rival en jaque

Analogía: no solo cuántas puertas. Cuántas son de captura, cuántas de enroque. Si el edificio tiene 20 puertas y usted cuenta 19, mira el sótano del al paso: ahí suele estar la que falta.

Perft no usa hash ni orden. Si los usara, un bug de transposición podría esconder un bug del generador. Aquí el árbol es honesto y caro: es una prueba, no una partida.

Qué archivos leer

Archivo Qué hay
perft.c perft(): hoja = 1; hacer / jaque / recursión / deshacer
comandos.c Comando perft n; impresión de nodos y desglose
tipoDatos.h DATA_PERFT

El repositorio del motor: GitHub.

Cierre

Perft no juega. Cuenta. Profundidad 0 es una posición. Cada ply legal suma las de abajo. El número seco caza bugs que la evaluación no ve. Encaja con hacer/deshacer (el carrete) y con el filtro de jaque. Ese filtro es esAtacadoPor. La siguiente entrada.

Correo de contacto: comprasmangocomputer@gmail.com

Comentarios

Entradas populares de este blog

Mango AC Ajedrez

Cap. 18 - Hacer lo que hay que hacer

Cap. 2 - Seis campos y el tablero entero