El siguiente paso lógico tras la fase 3 es controlar todos los movimientos de piezas para asegurarnos que cumplen con las reglas del ajedrez. Además, añadiremos el control del tiempo. Esto empieza a ser serio porque, una vez terminada esta fase, tendremos una primera versión completamente operativa del juego, en la que será posible que dos jugadores humanos jueguen al ajedrez.
Para lograrlo, dividiremos esta fase en 4 pasos:
- Controlar la casilla de origen
- Controlar la casilla de destino
- Controlar si se toma una pieza enemiga
- Controlar el tiempo
1º) Controlar la casilla de origen
Este control es muy fácil de realizar. Consiste, simplemente, en comprobar que, en la casilla de origen, existe una pieza propia, es decir, del color del jugador a quien le corresponde mover.
Si el jugador selecciona una casilla en la que hay una pieza enemiga, o bien no hay ninguna pieza, se mostrará un mensaje de error y se obligará al jugador a elegir otra casilla de origen.
2º) Controlar la casilla de destino
Este control es más complicado: consiste en verificar que la pieza que se ha elegido como origen puede efectivamente moverse a la casilla seleccionada como destino, sin infringir ninguna regla del juego. Como cada pieza tiene sus propios movimientos, el algoritmo de control dependerá de la pieza que se intenta mover. Dicho de otra forma: tendrá que escribir una función de control diferente para cada tipo de pieza.
Llevar a cabo este control va a ser de las cosas más difíciles del programa, así que debe pensarlo con mucha calma y, otra vez, dividir la tarea en subtareas:
- Primero pensaremos en los movimientos normales de cada tipo de pieza. Más abajo encontrará esbozado un algoritmo para controlar los movimientos de la torre.
- Después pensaremos en cómo controlar los movimientos especiales de aquéllas piezas que los tienen (el enroque, la promoción del peón, etc)
- Por último, pensaremos en cómo controlar las situaciones de jaque y de tablas, ya que en estos casos los movimientos correctos quedan muy reducidos.
Además, recuerde que las piezas no pueden pasar, al moverse, por encima de otras, excepto el caballo.
Como el asunto es complicado, es mejor que lo dividamos en los tres subproblemas antes mencionados. En primer lugar, por tanto, nos ocuparemos de los movimientos habituales de cada pieza y luego añadiremos los controles necesarios para los movimientos y situaciones excepcionales. Cada uno de los movimientos habituales de cada pieza lo trataremos como un problema individual. Como ve, se trata de usar otra vez la táctica de “divide y vencerás”
A modo de ejemplo, pensemos en cómo podríamos controlar los movimientos habituales de la torre.
Supongamos que el jugador ya ha elegido la casilla de origen del movimiento, que ésta ha sido comprobada y que en su interior hay una torre de su propiedad. Llamaremos a esta casilla (ox, oy), siendo ox la columna de origen y oy la fila de origen.
Supongamos también que el jugador ha elegido la casilla de destino, que identificaremos con las coordenadas (dx, dy). Nuestro objetivo ahora es comprobar si esta casilla de destino es o no correcta. He aquí un posible algoritmo para hacerlo.
1) La primera comprobación consistirá en ver si (ox, oy) y (dx, dy) son dos casillas diferentes:
si (ox == dx) y (oy == dy) entonces MOVIMIENTO INCORRECTO
2) Después habrá que comprobar que la torre se está moviendo de acuerdo a sus posibilidades, es decir, a lo largo de su fila o a lo largo de su columna. Es decir, ox debe ser igual a dx o, si no, oy debe ser igual a dy. Si no se cumple ninguna de las igualdades, el movimiento es incorrecto:
si (ox != dx) y (oy != dy) entonces MOVIMIENTO INCORRECTO
3) Ya sabemos que la casilla de destino está en la misma fila o en la misma columna que la de origen. En principio, es un movimiento correcto, pero antes de darlo por bueno hay que comprobar que en la trayectoria del movimiento no haya ninguna pieza que intercepte a la torre. Para eso, haremos un bucle que recorra la columna (si la torre se mueve a lo largo de su columna) o la fila (si el movimiento es a lo largo de la fila):
si (ox != dx) entonces // La torre se desplaza a lo largo de la columna para i desde ox hasta dx hacer si hay una pieza en (i, oy) --> MOVIMIENTO INCORRECTO
si (oy != dy) entonces // La torre se desplaza a lo largo de la fila para i desde oy hasta dy hacer si hay una pieza en (ox, i) --> MOVIMIENTO INCORRECTO
En realidad, esto es un poco más complicado, porque hay que hacer la comprobación desde una casilla después del origen (ya que en el origen está situada la misma torre, y este algoritmo interpretaría que se intercepta a sí misma), y dejarla una casilla antes del destino (porque en la casilla destino puede haber otra pieza que va a ser “comida” por la torre)
4) Por último, hay que comprobar si en la casilla destino hay una pieza. Si es una pieza enemiga, va a ser tomada (o “comida”). Si es una pieza propia, el movimiento es incorrecto.
Con esto quedaría comprobada la corrección o incorrección del movimiento habitual de una torre. Si el movimiento resultara ilegal, se debe mostrar un mensaje de error y pedir al jugador que vuelva a elegir un origen y un destino. Algo similar hay que pensar para cada tipo de pieza, ya que cada una tiene su propio movimiento.
Una vez programados estos controles para los movimientos habituales de cada pieza, habría que ocuparse de los movimientos especiales (como el enroque o la salida del peón).
Y más aún: cuando haya programado eso, hay que controlar la situación de jaque y la de tablas: si el rey está amenazado con un jaque, hay que hacer obligatoriamente un movimiento que deshaga el mismo, y cualquier otro movimiento, aunque en circunstancias normales fuera legal, debe ser prohibido. Más complicado puede ser controlar las tablas: cuando no sea posible realizar ningún movimiento, el juego debe terminar. Esto debemos dejarlo para las últimas fases de desarrollo del juego, cuando introduzcamos la inteligencia artificial.
3º) Comprobar si se toma alguna pieza enemiga
Tomar o “comer” una pieza enemiga consiste en ubicar una pieza propia en el lugar del tablero que ocupaba la otra, y eliminar a la enemiga del tablero.
Es posible que tenga que programar algún código adicional para sustituir una pieza por otra en el tablero. También puede emitir algún mensaje informativo al respecto.
Por último, recuerde dos cosas sobre el peón: se mueve de forma diferente cuando se “come” a una pieza enemiga que cuando no lo hace, y tiene un movimiento especial llamado “toma al paso”. El peón, con su aparente insignificancia, le puede dar bastantes quebraderos de cabeza…
4º) Controlar el tiempo
Añadir el control del tiempo será bastante sencillo. Necesitaremos mantener dos contadores de tiempo, uno para el jugador blanco y otro para el negro. Utilizando las funciones estándar para obtener la hora del reloj interno (time(), localtime(), gmtime(), etc) podemos saber cuánto tiempo tarda un jugador en seleccionar su casilla de origen y su casilla de destino.
Una posible manera de hacerlo es mirar qué hora marca el reloj interno cuando un jugador recibe el turno. En el bucle de lectura del teclado (cuando el jugador está pulsando las flechas del cursor), volveremos a leer la hora del reloj interno, una vez en cada pasada. Cuando transcurra un segundo, lo reflejaremos en el reloj del jugador.

2 comments
Comments feed for this article
Trackback link
http://profeblog.es/blog/alfredo/2009/06/20/ajedrez-paso-4-movimientos-controlados/trackback/
25 Julio 2009 at 22:40
Efrain
Hola Alfredo. Hay una cosa que no entiendo muy bien de tu explicación de la función strcpy().
Según dices la sintaxis es:
strcpy(cadena_origen, cadena_destino);
Entonces en el ejemplo el resultado de:
strcpy(cad1, cad2);
Sería vaciar cad2, ¿no?
Aprovecho para darte las gracias por este tremendo curso de C. Estoy aprendoendo muchísimo con él.
Saludos,
Efrain
25 Julio 2009 at 22:42
Efrain
Perdón, he hecho el comentario donde no era. Me refiero a sección: “3.2 Funciones para cadenas”