Skip to content
ChessLabs
Go back

#001 - Nuevos Comienzos

¡Bienvenido a mi primer devlog! En esta serie de artículos explicaré mi proceso de creación de un motor de ajedrez desde 0.

Tengo diferentes metas para este proyecto, y como es muy largo, lo voy a dividir en diferentes fases:

  1. Núcleo, almacenamiento e impresión del tablero
  2. Sistema de jugadas, entrada por notación algebraica
  3. Sistema de validación de jugadas, normas, enroques, etc.

Será a partir de este momento en el que tendré una manera de jugar al ajedrez íntegramente desde el programa, y a partir de ahí podré empezar siquiera a pensar en cómo hacer que el motor piense.

Idea inicial

Antes de empezar con esta página ya había empezado con el proyecto, e hice una especie de “boceto”, por así decirlo, en el que hice una versión mínima funcional, que fue esta (Lo podéis encontrar en el repositorio):

import re

# Piezas blancas
K = "♚"
Q = "♛"
P = "♟"
B = "♝"
N = "♞"
R = "♜"

# Piezas negras
k = "♔"
q = "♕"
p = "♙"
b = "♗"
n = "♘"
r = "♖"

# Casilla vacía
v = "·"

COLUMNAS = ["a", "b", "c", "d", "e", "f", "g", "h"]
FILAS = [str(e) for e in list(range(1,9))]

class Tablero:
    def __init__(self):
        self.tablero = [
            [R, P, v, v, v, v, p, r],
            [N, P, v, v, v, v, p, n],
            [B, P, v, v, v, v, p, b],
            [Q, P, v, v, v, v, p, q],
            [K, P, v, v, v, v, p, k],
            [B, P, v, v, v, v, p, b],
            [N, P, v, v, v, v, p, n],
            [R, P, v, v, v, v, p, r],
        ]
    def __str__(self):
        self.tablero_str =[[self.tablero[i][j] for i in range(8)] for j in range(8)]
        self.tablero_str = [f"{" ".join(self.tablero_str[i])} | {FILAS[i]}\n" for i in range(8)]
        self.tablero_str.reverse()
        self.tablero_str = f"{"".join(self.tablero_str)}{"-"*16}\n{" ".join(COLUMNAS)}"
        return self.tablero_str
    
    def validar(self, casilla):
        casilla = casilla.lower().strip()
        if not re.match(f"^[a-h][1-8]$", casilla):
            raise ValueError("Casilla no válida")
        return casilla

    def columna(self,casilla):
        casilla = self.validar(casilla)
        return COLUMNAS.index(list(casilla)[0])

    def fila(self,casilla):
        casilla = self.validar(casilla)
        return FILAS.index(list(casilla)[1])

    def casilla(self, casilla):
        casilla = self.validar(casilla)
        return self.tablero[self.columna(casilla)][self.fila(casilla)]
    
    def mover(self, casilla1, casilla2):
        if self.casilla(casilla1) == v:
            raise ValueError("La casilla especificada está vacía")
        self.tablero[self.columna(casilla2)][self.fila(casilla2)] = self.tablero[self.columna(casilla1)][self.fila(casilla1)]
        self.tablero[self.columna(casilla1)][self.fila(casilla1)] = v

def main():
    tablero = Tablero()
    while True:
        print(tablero)
        tablero.mover(input("Coordenadas de la pieza que quieres mover: "), 
                      input("Coordenadas a la que quieres mover la pieza: "))

if __name__ == "__main__":
    main()code/chess-v1.py

Por qué no funcionó

Con el tiempo y a medida que quise añadir más funciones, me di cuenta de que este enfoque no iba a funcionar, por diferentes motivos:

Almacenamiento de piezas

Si te fijas, la mejor manera que se me ocurrió para almacenar las piezas fue poner el caracter de unicode al que corresponde la pieza en una variable independiente. Esto, por supuesto, no se sostiene a largo plazo, a la hora de la búsqueda de piezas, o detección del color de pieza, por no hablar de importar posiciones que no fuera la iniical, así que tuve que idear un método diferente.

Movimiento

Existen un montón de funciones auxiliares para el movimiento de las piezas, y la única manera de mover las piezas es indicar la casilla de inicio y la de final, lo cual está bien, pero no está implementado bien del todo.

Sin embargo, hay cosas que sí que considero que hice bien, como la impresión del tablero, así que no tengo que tirar todo el código a la basura, pero hay que refactorizarlo bastante.

Como se puede ver, el código sintácticamente está bien, el problema es que no se adaptaba bien a lo que quiero construir después, así que me pareció mejor empezar de cero con unas mejores bases que construir sobre eso, que terminaría siendo incluso más difícil.

En mi siguiente devlog explicaré cómo refactoricé el programa para que fuera más eficiente, más extensible y mejor diseñado en general.