Como Implementar un Juego de Ajedrez en Visual C++
Enviado por Ivan Cachicatari
Introducción
A mi entender, la mejor forma de aprender y comprender algoritmos de programación es programando juegos. Los juegos suelen presentar situaciones que ayudan a despertar la creatividad y por la complejidad incentivan el uso de algoritmos complejos casi como jugando.
Hace algunos años implementé un juego de ajedrez que publique sin documentación, miles de usuarios descargaron el código fuente del proyecto pero muchos me escribieron recordándome que no había documentación para entender el proyecto. Es por eso que decidí re-hacer el proyecto pero esta vez documentado y explicándolo paso a paso.
Este tutorial le ayudará a usted a implementar un juego de ajedrez, para ello he utilizado Microsoft Visual C++ 2005, sin embargo usted puede seguir los pasos con otras versiones de Visual C++ incluso otros entornos de programación.
Hace algunos años implementé un juego de ajedrez que publique sin documentación, miles de usuarios descargaron el código fuente del proyecto pero muchos me escribieron recordándome que no había documentación para entender el proyecto. Es por eso que decidí re-hacer el proyecto pero esta vez documentado y explicándolo paso a paso.
Este tutorial le ayudará a usted a implementar un juego de ajedrez, para ello he utilizado Microsoft Visual C++ 2005, sin embargo usted puede seguir los pasos con otras versiones de Visual C++ incluso otros entornos de programación.
Objetivo
El objetivo, obviamente, es implementar un juego de ajedrez escrito en Visual C++ con los siguientes requisitos:
- Uso de la interfaz gráfica con manejo del Mouse.
- Controlar validar movidas de los jugadores.
- Permitir dos jugadores.
- Detectar jaque, jaque mate, enroques, y otras movidas.
Así se verá el juego cuando se termine de codificar:

Imagen 1: Juego de Ajedrez en Visual C++ Terminado
Contenido del tutorial
- Creación del proyecto
- Dibujar el tablero
- Dibujar las piezas de ajedrez
- Mover las piezas de ajedrez
- Funciones generales para mover las piezas de ajedrez
- Movida de cada pieza de ajedrez
Juego de Ajedrez en Visual C++
Creación del proyecto
Para no alargar más este asunto, lo primero que tenemos que hacer es crear el proyecto, el tipo de aplicación que necesitamos es “Documento único” conocido también como SDI (Simple Document Interface), asegurarse de que el check de la opción “Usar bibliotecas unicode” este deshabilitado por que no siempre están disponibles dichas bibliotecas en tiempo de ejecución (Ver Imagen 2).

Imagen 2: Elección del tipo de aplicación del proyecto.
En el siguiente paso del asistente podemos prescindir de algunas características que no utilizaremos en el proyecto, como: Controles ActiveX, Impresión y vista preliminar, Ayuda Contextual. Sólo consideraremos la opción “Manifiesto de controles comunes”, esta opción nos ayudará a sintonizar los controles comunes con el tema de windows actual, de tal forma que no se vean extraños.

Imagen 3: Elección de las características avanzadas del proyecto.
El resto de opciones no necesita mayor explicación, y al final encontrarán una ventana como de la imagen 4, en este punto deben asegurarse de tener a
CView como clase base de la clase CAjedrezView; esto significa la clase donde dibujaremos y realizaremos todas las operaciones que tienen que ver con ajedrez estarán en la clase CAjedrezView.
Imagen 4: Vista final del asistente de creación de proyectos del Visual C++
Luego de hacer clic en finalizar se habrá creado un proyecto con las 4 clases que se muestran en la figura 4, podemos compilar y ejecutar el proyecto y obtendremos un programa como el que se muestra en la imagen 5.

Programa inicial ejecutándose
Juego de Ajedrez en Visual C++
Dibujar el tablero
Para dibujar el tablero de ajedrez dentro de la vista creada tenemos que tener en cuenta los parámetros siguientes:
- Etiquetas de filas y columnas.
- Margen entre el borde de la ventana y la cuadrícula del tablero.
- Tamaño de la celda del tablero.

Imagen 6: Parámetros para dibujar el tablero de ajedrez
Para dibujar el tablero necesitaremos definir 3 valores fijos que no cambiarán y nos servirán para dibujar el tablero cada vez que lo necesitemos.
#define MARGEN 30 #define TAMCELDA 60 #define CELDAS 8
El siguiente paso es crear la función que dibujará el tablero, para ello debemos abrir la vista de clases, hacer clic derecho sobre la clase y elegir “Agregar->Función” luego aparecerá un dialogo como el de la imagen 7. No se deben olvidar de agregar un parámetro del tipo CDC* para que podamos dibujar los elementos.

Imagen 7: Agregar Función DibujarTablero
La función DibujarTablero
A continuación una descripción del algoritmo utilizado para dibujar el trablero de ajedréz.
- Crea inicialmente los objetos necesarios para darle color al fondo y a las líneas del tablero de ajedrez (CPen, CBrush)
- Luego dibuja celda por celda (for anidado de 8 iteraciones cada uno)
- Al inicio de la iteración se calcula las dimensiones de la celda en base a los valores de i y j y los parámetros definidos inicialmente (margen y tamaño de celda) estos valores calculados son almacenados en la variable rcCelda de tipo RECT.
- Para dibujar una celda oscura o blanca se verifica si es par o no, de esa forma se intercalan los colores, según estos valores se utiliza el Brush indicado junto a la función CDC::Rectangle.
- Se aprovecha el valor de rcCelda para calcular la posición de las etiquetas que están contenidas en la variable m_vEtiquetas de tipo std::vector.
Segun el algoritmo anterior podemos escribir el siguiente código:
void CAjedrezView::DibujarTablero(CDC* pDC)
{
//Definición de colores y fondos
RECT rcCelda;
CBrush brNegro,brSeleccion;
brNegro.CreateSolidBrush(0xf36d4d);
brSeleccion.CreateSolidBrush(RGB(255,240,108));
CPen penAzulMarino,penAmarillo;
penAzulMarino.CreatePen(PS_SOLID,3,0x00640000);
penAmarillo.CreatePen(PS_SOLID,4,0x0000EEEE);
CPen *penAnterior = pDC->SelectObject(&penAzulMarino);
pDC->SetBkMode(TRANSPARENT);
for(int i = 0 ; i < CELDAS ; i++)
{
for(int j = 0 ; j < CELDAS ; j++)
{
rcCelda.left = j*TAMCELDA + MARGEN;
rcCelda.top = i*TAMCELDA + MARGEN;
rcCelda.right = rcCelda.left + TAMCELDA;
rcCelda.bottom = rcCelda.top + TAMCELDA;
if( i%2 == 0 )
{
if(j%2 != 0)
{
CBrush *pFondoAnterior = pDC->SelectObject(&brNegro);
pDC->Rectangle(&rcCelda);
pDC->SelectObject(pFondoAnterior);
}
else
{
pDC->Rectangle(&rcCelda);
}
}
else
{
if(j%2 == 0)
{
CBrush *pFondoAnterior = pDC->SelectObject(&brNegro);
pDC->Rectangle(&rcCelda);
pDC->SelectObject(pFondoAnterior);
}
else
{
pDC->Rectangle(&rcCelda);
}
}
//Dibujando las etiquetas
if(j == 0 || j == 7)
{
rcCelda.left = j==0?MARGEN-15:rcCelda.right;
rcCelda.right = j==0?MARGEN:rcCelda.right + 15;
pDC->DrawText(m_vEtiquetas[i],
&rcCelda,
DT_CENTER|DT_VCENTER|DT_SINGLELINE);
//Restaurando valores para que no
//afecte el pintado de las etiquetas verticales
rcCelda.left = j*TAMCELDA + MARGEN;
rcCelda.right = rcCelda.left + TAMCELDA;
}
if(i == 0 || i == 7)
{
rcCelda.top = i==0?MARGEN-20:rcCelda.bottom;
rcCelda.bottom = i==0?MARGEN:rcCelda.bottom + 20;
pDC->DrawText(m_vEtiquetas[j+8],
&rcCelda,
DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}
}
}
pDC->SelectObject(penAnterior);
}
Este código debe invocarse desde la función OnDraw, la función OnDraw ya esta establecida y recibe como parámetro también un objeto CDC, el mismo debe pasar como parámetro de la función DibujarTablero.
#include "memdc.h"
...
void CAjedrezView::OnDraw(CDC* pDC)
{
CAjedrezDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CMemDC memdc(pDC);
DibujarTablero(&memdc);
}
La clase CMemDC es una herramienta para crear un mapa de bits en segundo plano para ir dibujando en él y al final pintar el mapa de bits sobre la ventana para así evitar el parpadeo que se produce al pintar objeto por objeto, esta técnica se le conoce como Double Buffering.
Pueden descargar el archivo memdc.h de codeproject.com, esta clase fue escrita por Keith Rule (keithr@europa.com)

Para que la técnica de double buffering quede completa debemos agregar la función asociada al evento WM_ERASEBKGND que decide si se va a eliminar el fondo antes de pintar nuevamente el tablero.
El código de la función generada se debe ver como sigue:
BOOL CAjedrezView::OnEraseBkgnd(CDC* pDC)
{
return FALSE;
}
Luego de terminar de escribir la función pueden compilar y verán un resultado como el de la imagen 8.

Imagen 8: Tablero Dibujado por la Función
Juego de Ajedrez en Visual C++
Dibujar las piezas
Buscando en la red pude encontrar una imagen con todas las piezas de ajedrez que vamos a necesitar. Si ustedes encuentran algún juego de imágenes mejorado pueden utilizarlo y seguir los mismos pasos.

Imagen 9: Imagen con las piezas de ajedrez
Les recomiendo que utilicen la opción Imagen -> Configuración de cuadrícula, para que pueda aparecer una cuadricula de 60x60 que va a facilitarles la ubicación de cada imagen. Las imágenes las guardaremos en los recursos con el id IDB_PIEZAS, deben modificar las propiedades de la imagen para que pueda ser guardada con “color verdadero”.
Si han podido notar, aun no creamos la matriz conde vamos a almacenar cada pieza de ajedrez, esta matriz no necesita ser muy compleja, solo necesitamos un arreglo de dos dimensiones de tipo entero.
int m_pCeldas[8][8];
CImageList m_imgPiezas;
Cada pieza de ajedrez será identificada según su posición en la imagen, por ejemplo las posiciones 0 ,1 y 2 corresponden a las piezas: reina negra, reina blanca, y rey negro respectivamente (Ver imagen 9), para facilitarnos las cosas definiremos los siguientes valores:
#define VACIO -1 #define PEONB 11 #define PEONN 10 #define ALFILB 7 #define ALFILN 6 #define CABALLOB 9 #define CABALLON 8 #define REYB 1 #define REYN 0 #define REINAB 3 #define REINAN 2 #define TORREB 5 #define TORREN 4
La terminación de cada definición anterior corresponde a la primera letra del color al que corresponde.
Para inicializar todos los valores en el arreglo m_pCeldas debemos crear una función que llamaremos InicializarTablero.
// Situa todas las piezas en su lugar
void CAjedrezView::InicializarTablero(void)
{
//Inicializacion de celdas
for(int i = 0 ; i < 8 ;i++)
for(int j = 0 ; j < 8 ; j++)
m_pCeldas[i][j] = VACIO;
m_pCeldas[0][0] = TORREN;
m_pCeldas[0][1] = CABALLON;
m_pCeldas[0][2] = ALFILN;
m_pCeldas[0][3] = REINAN;
m_pCeldas[0][4] = REYN;
m_pCeldas[0][5] = ALFILN;
m_pCeldas[0][6] = CABALLON;
m_pCeldas[0][7] = TORREN;
m_pCeldas[7][0] = TORREB;
m_pCeldas[7][1] = CABALLOB;
m_pCeldas[7][2] = ALFILB;
m_pCeldas[7][3] = REINAB;
m_pCeldas[7][4] = REYB;
m_pCeldas[7][5] = ALFILB;
m_pCeldas[7][6] = CABALLOB;
m_pCeldas[7][7] = TORREB;
m_pCeldas[1][0] = PEONN;
m_pCeldas[1][1] = PEONN;
m_pCeldas[1][2] = PEONN;
m_pCeldas[1][3] = PEONN;
m_pCeldas[1][4] = PEONN;
m_pCeldas[1][5] = PEONN;
m_pCeldas[1][6] = PEONN;
m_pCeldas[1][7] = PEONN;
m_pCeldas[6][0] = PEONB;
m_pCeldas[6][1] = PEONB;
m_pCeldas[6][2] = PEONB;
m_pCeldas[6][3] = PEONB;
m_pCeldas[6][4] = PEONB;
m_pCeldas[6][5] = PEONB;
m_pCeldas[6][6] = PEONB;
m_pCeldas[6][7] = PEONB;
}
La variable m_imgPiezas es donde cargaremos las imágenes, utilizaremos las funciones de la clase CImageList para extraer imágenes individuales al momento de pintar. En el constructor de la clase CAjedrezView debemos inicializar las imágenes para tenerlas disponibles en todo momento.
CAjedrezView::CAjedrezView()
{
m_imgPiezas.Create(IDB_PIEZAS,TAMCELDA,0,RGB(255,0,255));
InicializarTablero();
}
Finalmente para que podamos ver cada pieza en el tablero tenemos que agregar las siguientes líneas a la función DibujarTablero:
void CAjedrezView::DibujarTablero(CDC* pDC)
{
...
for(int i = 0 ; i < CELDAS ; i++)
{
for(int j = 0 ; j < CELDAS ; j++)
{
...
if(m_pCeldas[i][j] >= 0)
{
POINT p;
p.x = rcCelda.left;
p.y = rcCelda.top;
SIZE s;
s.cx = s.cy = 60;
m_imgPiezas.DrawEx( pDC, m_pCeldas[i][j],
p,s,RGB(255,0,255),0,ILD_TRANSPARENT);
}
...
}
}
...
}
Luego de seguir todos estos pasos, al compilar se podrá observar las piezas inicializadas en su lugar tal como se muestra en la imagen 10.

Imagen 10: Tablero de ajedrez con sus piezas
Ahora solo falta modificar las propiedades de la ventana para que pueda tener un tamaño fijo también debemos modificar algunas propiedades más para evitar que al usuario pueda cambiar el tamaño de la ventana.
Para ello debemos ir a la clase CMainFrame a las funciones PreCreateWindow y OnCreate, y agregar las líneas siguientes:
Para ello debemos ir a la clase CMainFrame a las funciones PreCreateWindow y OnCreate, y agregar las líneas siguientes:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
//Asignar las dimensiones
MoveWindow(0,0,60*9,60*10,FALSE);
CenterWindow();
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("No se pudo crear la barra de estado\n");
return -1;
}
return 0;
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style = WS_POPUPWINDOW|WS_CAPTION;
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
return TRUE;
}
Si no lo han notado aun, también he borrado las líneas que corresponden a la creación de la barra de herramientas que no vamos a necesitar en adelante.
Al compilar nuestro proyecto en este punto podremos el siguiente resultado:
Al compilar nuestro proyecto en este punto podremos el siguiente resultado:

Imagen 11: Tablero del juego de ajedréz
Juego de Ajedrez en Visual C++
Utilizar el Mouse para mover las piezas
Antes de continuar debemos definir algunas variables que nos ayudarán a manipular los eventos, estas variables deben definirse dentro de la clase CAjedrezView para tenerlas disponibles en todo momento.
class CAjedrezView : public CView
{
...
POINT m_celdaSel;
POINT m_celdaCursor;
bool m_bFichaSeleccionada;
bool m_bTurnoBlancas;
...
}
Algunas aclaraciones sobre las variables:
- La variable m_celdaSel es de tipo POINT y ahí guardaremos la celda seleccionada para ser movida en el siguiente clic.
- En la variable m_celdaCursor guardaremos las coordenadas de la celda sobre la cual esta el cursor del Mouse, para dibujar un borde amarillo en pantalla.
- La variable booleana m_FichaSeleccionada será un indicador que nos permitirá verificar si hay una ficha seleccionada para moverla, esto nos evitará varias líneas de código para verificar si ha habido una movida antes que la actual. Esta variable se inicializa en false por que al principio no hay fichas seleccionadas.
- La variable m_bTurnoBlancas decidirá el turno actual, esta variable se inicializará en verdadero (true) por que las reglas del ajedrez indican que primero empiezan las blancas. Si esta variable es false entonces obviamente le toca a equipo negro.
OnMouseMove
Para activar el uso del Mouse solo tenemos que hacer clic en las propiedades de la clase CAjedrezView y buscar los eventos WM_MOUSEMOVE y WM_LMOUSEDOWN, opcionalmente también utilizaremos el evento WM_KEYDOWN para interceptar la tecla de escape.

Agregar Función OnMouseMove
La función OnMouseMove, esta función nos ayudará a identificar la celda sobre la cual esta el puntero del Mouse. La secuencia del código fuentes es como sigue:
- Inicialmente crearemos las variables de las coordenadas inicializadas en -1 al principio.
- Luego transformaremos las coordenadas actuales de píxeles a posiciones de la matriz de piezas dividiendo las coordenadas entre 60.
- Luego verificamos si las coordenadas transformadas están dentro del margen permitido.
- Si están dentro del margen permitido y si han cambiado respecto a las coordenadas anteriores entonces cambiamos la celda de cursor a la posición actual.
- Caso contrario modificamos la celda de cursor a -1,-1 para evitar que se vea en pantalla.
Sin tantas complicaciones podemos escribir este codigo para el algoritmo anterior:
void CAjedrezView::OnMouseMove(UINT nFlags, CPoint point)
{
static int x = -1;
static int y = -1;
x = (point.x - MARGEN)/60;
y = (point.y - MARGEN)/60;
if(x >= 0 && y >= 0 && x<8 && y<8)
{
if(x != m_celdaCursor.x || y != m_celdaCursor.y )
{
m_celdaCursor.x = x;
m_celdaCursor.y = y;
Invalidate();
}
}
else
{
x=-1;
y=-1;
if(x != m_celdaCursor.x || y != m_celdaCursor.y )
{
m_celdaCursor.x = -1;
m_celdaCursor.y = -1;
Invalidate();
}
}
CView::OnMouseMove(nFlags, point);
}
OnLButtonDown
La función OnLButtonDown (Al presional el boton izquierdo del mouse) , se hace la misma verificación de las coordenadas pero esta vez se invoca a la función MoverFicha, La función MoverFicha la veremos mas adelante.
void CAjedrezView::OnLButtonDown(UINT nFlags, CPoint point)
{
static int x = -1;
static int y = -1;
x = (point.x - MARGEN)/60;
y = (point.y - MARGEN)/60;
if(x >= 0 && y >= 0 && x<8 && y<8)
{
MoverFicha(x,y);
}
CView::OnLButtonDown(nFlags, point);
}
Modificar la funcion DibujarTablero
Debemos agregar líneas de código a la función DibujarTablero para ver los efectos:
void CAjedrezView::DibujarTablero(CDC* pDC)
{
...
for(int i = 0 ; i < CELDAS ; i++)
{
for(int j = 0 ; j < CELDAS ; j++)
{
...
if(m_celdaSel.x == j && m_celdaSel.y == i)
{
CBrush *pFondoAnterior = pDC->SelectObject(&brSeleccion);
pDC->Rectangle(&rcCelda);
pDC->SelectObject(pFondoAnterior);
}
...
Juego de Ajedrez en Visual C++
Funciones generales para mover las piezas de ajedrez
Ahora veremos el código mas difícil de hacer, todo lo que hicimos hasta el momento ha estado relativamente fácil comparado con lo que vamos a ver. Pero creo que no va a ser tan dificil por que todo el codigo fuente esta explicado y será pan comido.
Antes de seguir debemos implementar algunas funciones que nos va a permitir controlar todas las movidas. Todo empieza desde la función OnLButtonDown que invoca a la función MoverFicha.
Función MoverFicha
- Si hay una ficha seleccionada.
- Si la celda es nula o es del otro equipo, entonces intentamos mover la pieza.
- Sino, mostramos el mensaje “Movida no permitida” y volvemos a esperar la movida del usuario.
- Sino
- Si la celda esta vacía, no hacemos nada.
- Si la pieza corresponde al turno es el correcto:
- Cambiamos la variable m_celdaSel con la posición actual.
- Levantamos el flag m_bFichaSeleccionada
- Mostramos un mensaje para hacerle saber al usuario que debe realizar el siguiente paso.
- Refrescar la pantalla.
Código fuente de la función MoverFicha
void CAjedrezView::MoverFicha(int x,int y)
{
if(m_bFichaSeleccionada)
{
if(EsCeldaNula(x,y)
|| (!EsPiezaBlanca(x,y) && m_bTurnoBlancas)
|| (EsPiezaBlanca(x,y) && !m_bTurnoBlancas))
{
IntentarMovidaHacia(x,y);
}
else
{
Mensaje("Movida no permitida");
CancelarMovida();
}
}
else
{
if(EsCeldaNula(x,y))
return;
if((EsPiezaBlanca(x,y) && m_bTurnoBlancas )
|| (!EsPiezaBlanca(x,y) && !m_bTurnoBlancas))
{
m_celdaSel.x = x;
m_celdaSel.y = y;
m_bFichaSeleccionada = true;
Mensaje("Presione ESC para cancelar la selección");
Invalidate();
}
}
}
La función IntentarMovidaHacia
- Obtener el valor de la celda de la celda seleccionada.
- Identificar el tipo de pieza y e invocar a la función que corresponde.
- Si hay resultado exitoso al intentar mover la pieza
- Mostrar mensaje
- Realizar la movida.
- Sino
- Mostar mensaje de error
void CAjedrezView::IntentarMovidaHacia(int x, int y)
{
int nPiezaActual = m_pCeldas[m_celdaSel.y][m_celdaSel.x];
bool bSePuedeMover = false;
switch(nPiezaActual)
{
case PEONB:
case PEONN:
bSePuedeMover = MoverPeonHacia(x,y);
break;
case ALFILB:
case ALFILN:
bSePuedeMover = MoverAlfilHacia(x,y);
break;
case CABALLOB:
case CABALLON:
bSePuedeMover = MoverCaballoHacia(x,y);
break;
case REYB:
case REYN:
bSePuedeMover = MoverReyHacia(x,y);
break;
case REINAB:
case REINAN:
bSePuedeMover = MoverReinaHacia(x,y);
break;
case TORREB:
case TORREN:
bSePuedeMover = MoverTorreHacia(x,y);
break;
}
if(bSePuedeMover)
{
Mensaje("Movida correcta, siguiente jugador");
RealizarMovida(x, y);
}
else
{
Mensaje("Movida incorrecta, intenta nuevamente");
CancelarMovida();
}
}
La función EsViaLibre
- Inicialmente obtenemos la diferenca entre las coordenadas, esto nos ayudará a identificar que tipo de movida se ha realizado.
- Si la diferencia de las coordenadas x es igual a cero entonces se ha realizado un movimiento vertical.
- Recorremos todas las celdas que hay entre las dos coordenadas, si encontramos alguna celda con una pieza de ajedrez entonces retornamos false.
- Si la diferencia de las coordenadas y es igual a cero entonces se ha realizado un movimiento horizontal.
- Recorremos todas las celdas que hay entre las dos coordenadas, si encontramos alguna celda con una pieza de ajedrez entonces retornamos false.
- Si la diferencia de las coordenas x e y son iguales entonces se ha realizado un movimiento diagonal
- Recorremos todas las celdas que hay entre las dos coordenadas, si encontramos alguna celda con una pieza de ajedrez entonces retornamos false.
- Si al final no se ha determinado que tipo de movimiento se ha realizado entonces se devuelve el valor de ok que en ese punto deberá ser false.
Codigo fuente de la función EsViaLibre
bool CAjedrezView::EsViaLibre(int x, int y)
{
int dify = y - m_celdaSel.y;
int difx = x - m_celdaSel.x;
bool ok = true;
if(difx == 0) //Movimiento Vertical
{
for(int i = 1 ; i < abs(dify) ; i++)
{
if(m_pCeldas[m_celdaSel.y + (dify<0?-i:i)][m_celdaSel.x] != VACIO)
{
ok = false;
}
}
}
else if(dify == 0) //Movimiento Horizontal
{
for(int i = 1 ; i < abs(difx) ; i++)
{
if(m_pCeldas[m_celdaSel.y][m_celdaSel.x + (difx<0?-i:i)] != VACIO )
{
ok = false;
}
}
}
else if(abs(difx) == abs(dify)) //Movimiento diagonal
{
for(int i = 1 ; i < abs(dify) ; i++)
{
if(m_pCeldas[m_celdaSel.y + (dify<0?-i:i)][m_celdaSel.x + (difx<0?-i:i)] != VACIO)
{
ok = false;
}
}
}
return ok;
}
En adelante, para todos los casos se utilizará la diferencia de coordenadas, por que es una manera facil y rápida de identificar la dirección y tipo de movida.
La función RealizarMovida
- Cambiar turno de jugador.
- Ubicar la pieza de origen a la posición de destino.
- Cambiar los valores que tienen que ver con la ficha seleccionada.
- Repintar.
Codigo fuente de la función RalizarMovida.
void CAjedrezView::RealizarMovida(int x, int y)
{
//Cambiar Turno
m_bTurnoBlancas = !m_bTurnoBlancas;
//Asignar la pieza de origen al destino
m_pCeldas[y][x] = m_pCeldas[m_celdaSel.y][m_celdaSel.x];
//TODO: Agregar a la lista de eliminados
m_pCeldas[m_celdaSel.y][m_celdaSel.x] = VACIO;
//Soltar la ficha seleccionada
m_bFichaSeleccionada = false;
m_celdaSel.x = -1;
m_celdaSel.y = -1;
Invalidate();
}
- Si la celda es nula o es del otro equipo, entonces intentamos mover la pieza.
- Sino, mostramos el mensaje “Movida no permitida” y volvemos a esperar la movida del usuario.
- Si la celda esta vacía, no hacemos nada.
- Si la pieza corresponde al turno es el correcto:
- Cambiamos la variable m_celdaSel con la posición actual.
- Levantamos el flag m_bFichaSeleccionada
- Mostramos un mensaje para hacerle saber al usuario que debe realizar el siguiente paso.
- Refrescar la pantalla.
- Mostrar mensaje
- Realizar la movida.
- Mostar mensaje de error
- Recorremos todas las celdas que hay entre las dos coordenadas, si encontramos alguna celda con una pieza de ajedrez entonces retornamos false.
- Recorremos todas las celdas que hay entre las dos coordenadas, si encontramos alguna celda con una pieza de ajedrez entonces retornamos false.
- Recorremos todas las celdas que hay entre las dos coordenadas, si encontramos alguna celda con una pieza de ajedrez entonces retornamos false.
Juego de Ajedrez en Visual C++
Moviendo cada pieza de ajedrez
Ahora veremos en detalle como podemos controlar la movida de cada pieza de ajedréz.
Movida del peón
Es la movida que parece simple de implementar, pero presenta situaciones especiales.
- Obtenemos la diferencia entre las coordenadas de origen y destino.
- Si es una movida vertical
- Si se ha movido un casillero
- Si la dirección de la movida corresponde al turno, entonces es posible realizar la movida
- Si se ha movido dos casilleros
- Si la dirección de la movida corresponde al turno y la pieza del peon aun no se ha movido, entonces es posible realizar la movida
- Si hay vía libre para realizar la movida, entonces es posible realizar la movida.
- Si es un movimiento diagonal y si la orientación de la movida es correcta con el turno y si la celda de destino no esta vacía y si la celda de destino corresponde a una pieza del oponente.
- Se puede realizar la movida.
Código fuente de la función MoverPeonHacia
bool CAjedrezView::MoverPeonHacia(int x, int y)
{
int dify = y - m_celdaSel.y;
int difx = x - m_celdaSel.x;
bool bMovidaPosible = false;
if(difx == 0) //Movida Vertical
{
if(abs(dify) == 1)//Si mueve un casillero
{
if(dify == (EsPiezaBlanca(m_celdaSel.x ,m_celdaSel.y)?-1:1))
{
bMovidaPosible = true;
}
}
else if(abs(dify) == 2) //Movida larga de peon
{
if(m_celdaSel.y == (EsPiezaBlanca(m_celdaSel.x ,m_celdaSel.y)?6:1))
{
if(EsViaLibre(x,y))
bMovidaPosible = true;
}
}
}
else if((abs(difx) == 1) // Si esta intentando comer
&& (dify == (EsPiezaBlanca(m_celdaSel.x ,m_celdaSel.y)?-1:1))
&& !EsCeldaNula(x,y)
&& (m_pCeldas[m_celdaSel.y ][m_celdaSel.x] != m_pCeldas[y][x]))
{
bMovidaPosible = true;
}
return bMovidaPosible;
}
Movida del alfil
El alfil es una pieza que se mueve diagonalmente, entonces basta con verificar si hay vía libre entre las posiciones.
- Obtenemos la diferencia entre las coordenadas de origen y destino.
- Si la diferencia de ambas coordenadas son iguales entonces es un movimiento diagonal.
- Si hay vía libre y la celda de destino esta vacía o corresponde al enemigo
- Devolver true, indicando que se puede realizar la movida.
- Sino, devolver falso.
Código fuente de la función
bool CAjedrezView::MoverAlfilHacia(int x, int y)
{
int dify = y - m_celdaSel.y;
int difx = x - m_celdaSel.x;
if(abs(difx) == abs(dify))
{
if(EsViaLibre(x,y) && m_pCeldas[m_celdaSel.y ][m_celdaSel.x] != m_pCeldas[y][x])
{
return true;
}
}
return false;
}
Movida de la torre
La movida de la torre es similar a la del alfil con la diferencia que una de las dos diferencias debe ser cero.
bool CAjedrezView::MoverTorreHacia(int x, int y)
{
int dify = y - m_celdaSel.y;
int difx = x - m_celdaSel.x;
if((difx == 0) || (dify == 0) )
{
if(EsViaLibre(x,y) && m_pCeldas[m_celdaSel.y ][m_celdaSel.x] != m_pCeldas[y][x])
{
return true;
}
}
return false;
}
Movida de la reina
Es mas fácil de lo que parece:
bool CAjedrezView::MoverReinaHacia(int x, int y)
{
return MoverAlfilHacia(x,y) || MoverTorreHacia(x,y);
}
Movida del rey
En esta movida solo hay que verificar que se haya movido un casillero y que la celda de destino sea la del oponente.
bool CAjedrezView::MoverReyHacia(int x, int y)
{
int dify = y - m_celdaSel.y;
int difx = x - m_celdaSel.x;
if((abs(difx) == 1) && (abs(dify) == 1) )
{
if(m_pCeldas[m_celdaSel.y ][m_celdaSel.x] != m_pCeldas[y][x])
{
return true;
}
}
return false;
}
Movida del caballo
Esta movida no se parece a ninguna de las anteriores, es por eso que necesita explicación.
- Obtenemos la diferencia entre las coordenadas de origen y destino.
- Si la diferencia entre las coordenadas debe ser 2 y 1 para cada caso
- Si la celda de destino es del oponente o esta en blanco
- Devolver true indicando que se puede realizar la movida
- Sino, retornar false.
bool CAjedrezView::MoverCaballoHacia(int x, int y)
{
int dify = y - m_celdaSel.y;
int difx = x - m_celdaSel.x;
if(((abs(difx) == 2) && (abs(dify) == 1))||((abs(difx) == 1) && (abs(dify) == 2)))
{
if(m_pCeldas[m_celdaSel.y ][m_celdaSel.x] != m_pCeldas[y][x])
{
return true;
}
}
return false;
}
- Si se ha movido un casillero
- Si la dirección de la movida corresponde al turno, entonces es posible realizar la movida
- Si se ha movido dos casilleros
- Si la dirección de la movida corresponde al turno y la pieza del peon aun no se ha movido, entonces es posible realizar la movida
- Si hay vía libre para realizar la movida, entonces es posible realizar la movida.
- Si la dirección de la movida corresponde al turno y la pieza del peon aun no se ha movido, entonces es posible realizar la movida
- Se puede realizar la movida.
- Si hay vía libre y la celda de destino esta vacía o corresponde al enemigo
- Devolver true, indicando que se puede realizar la movida.
- Si la celda de destino es del oponente o esta en blanco
- Devolver true indicando que se puede realizar la movida
Tareas por hacer
El proyecto no esta completo, por lo que dejo una lista de tareas por hacer para que algun aventurero se anime a completarlas. Si alguien ha completado alguna solo tiene que escribirme a mi correo e inmediatamente subiré su actualización.
- Permitir enroque y peon al paso.
- Detectar Jaque / Jaque Mate.
- Visualizar las movidas realizadas segun la notación oficial.
- Permitir guardar/recuperar una partida.

