En esta fase desarrollaremos el menú de opciones del juego y la posibilidad de guardar las partidas en disco para continuar jugándolas más tarde.
Menú de opciones
El juego tiene dos menús de opciones:
- El que aparece al inicio del juego, para seleccionar el tipo y el color de los jugadores (este ya lo programamos en la fase 2)
- El que puede invocarse desde el tablero de juego, pulsando en cualquier momento alguna tecla especial (como ESC, F2, etc)
El que vamos a programar en esta fase es el segundo, que es más complejo.
Composición del menú
El menú debe aparecer al pulsar alguna tecla especial (ESC, F2, “m”, o la tecla que decida) durante el juego. Por lo tanto, hay que añadir el control de esa tecla en los procedimientos de selección de casilla, que es donde se lee el teclado.
El menú puede aparecer a toda pantalla (borrando momentáneamente el tablero) o bien en el lateral (en el espacio reservado a los mensajes del usuario)
Las opciones del menú deben ser las siguientes (el texto, el aspecto y el orden, que cada cual lo elija a su gusto):
- Salir del programa. El programa terminará inmediatamente.
- Empezar una partida nueva. Volveremos al principio del juego: elección de jugadores, dibujo del tablero con las piezas en su disposición inicial, etc.
- Continuar la partida. Volveremos al tablero y continuaremos el juego tal y como lo habíamos dejado.
- Guardar la partida. El programa nos preguntará un nombre de archivo. A ese nombre le añadiremos la extensión “.PGN” y lo guardaremos en un archivo de disco, con el formato que en el siguiente epígrafe se detalla. Después volveremos a este mismo menú de opciones, para que el usuario decida qué quiere hacer a continuación (salir, continuar, etc)
- Cargar una partida guardada. El programa nos preguntará un nombre de archivo. Luego buscará un archivo con ese nombre y, si lo encuentra, cargará la partida almacenada en él. Después volverá a este mismo menú de opciones para que el usuario decida qué quiere hacer a continuación.
Selección de opciones
Para seleccionar una opción se puede optar por varios caminos:
- Lo más fácil es mostrar un número delante de cada opción y luego pedir al usuario que teclee el número de la opción que quiere seleccionar (ver figuras).
- Una versión más elaborada consiste en escribir una marca delante de la primera opción. El usuario podrá mover esa marca de una opción a otra con las teclas del cursor (flecha arriba y flecha abajo), seleccionando una opción al pulsar Enter o Espacio.
- Aún quedaría mejor si la opción seleccionada apareciese escrita en vídeo inverso.
Puede empezar programando la versión 1 del menú y, más adelante, cuando todo lo demás funcione, mejorar su aspecto.
Primera versión del menú (la opción se selecciona tecleando el número):
MIAJEDREZ 1.0 MENÚ DE OPCIONES (1) Continuar partida (2) Empezar otra partida (3) Guardar partida (4) Cargar partida (5) Salir del programa Introduzca opción (1-5): _
Versión mejorada del menú (la opción se selecciona moviendo una marca al lado de las opciones, o poniéndolas en video inverso, hasta que se pulse Intro):
MIAJEDREZ 1.0 MENÚ DE OPCIONES >> Continuar partida Empezar otra partida Guardar partida Cargar partida Salir del programa
Guardar y cargar partidas
La otra función que vamos a añadir en esta fase es la posibilidad de guardar y cargar partidas en archivos de disco.
Para guardar una partida en un archivo podemos hacer dos cosas: una, guardar el estado actual del tablero; dos, guardar todos los movimientos que se hayan producido en la partida desde el comienzo.
- Con el primer método, la recuperación de la partida es muy fácil: basta con recuperar el estado del tablero.
- Con el segundo, tendremos que reproducir todos los movimientos efectuados desde el principio y reflejarlos en el tablero.
Aunque el primer método resulta, sin duda, más sencillo, nosotros vamos a optar por el segundo. La razón estriba en que en la fase 6 vamos a añadir una función (la reproducción de partidas guardadas) que necesitará conocer todos los movimientos de la partida.
Tenemos que decidir cómo vamos a guardar los movimientos para poder recuperarlos después y reproducirlos sin posibilidad de duda. En los siguientes apartados describiremos una forma de hacerlo.
Notación algebraica
Para guardar una partida, por lo tanto, necesitamos haber guardado en alguna estructura de datos todos los movimientos realizados desde el principio. Es el momento de elegir una estructura de datos adecuada para ello y añadirla al diseño de las estructuras que hicimos en la fase 1.
La estructura elegida debería ser dinámica porque, en principio, no sabemos cuantos movimientos va a tener la partida; aunque, para simplificar, también se puede utilizar una estructura estática, siempre que tenga espacio suficiente para almacenar un número elevado de movimientos.
Los movimientos, en ajedrez, suelen representarse con la llamada notación algebraica. Esta notación es muy simple y es conveniente que la use para almacenar en su estructura de datos los movimientos de la partida.
La notación algebraica consiste en lo siguiente:
- Cada pieza se identifica con una letra: R = rey, D = dama, T = torre, A = alfil, C = caballo, P = peón.
- Cada casilla se identifica con su letra (en minúscula) y su número. Por ejemplo: f3, d5, h1, etc.
- Cada movimiento se identifica con la letra de la pieza que se mueve seguida de la casilla de origen y la casilla de destino, separadas por un guión (-). Por ejemplo: Af1-c4 quiere decir que el alfil se ha movido de la casilla f1 a la c4.
- Cuando la pieza que se mueve es un peón, no se suele poner la P, sobreentendiéndose que, en ausencia de letra, la pieza movida es un peón. Por ejemplo: d2-d3 significa que se mueve el peón de d2 a d3.
- Cuando una pieza toma a otra (se la “come”), el guión se sustituye por una “x”. Por ejemplo: Cf6xd5 quiere decir que el caballo que había en f6 se mueve a d5 y se come la pieza que allí hubiera.
- El enroque se representa con O-O (enroque corto) o con O-O-O (enroque largo)
- Cuando hay jaque se añade un “+” al movimiento. Por ejemplo: Cf6-d5+
- Cada jugada se antepone del número de la misma. En cada jugada aparecerán dos movimientos: primero el del jugador blanco y luego el del negro. Estos son, por ejemplo, los 6 primeros turnos de una partida:
1. e2-e4 e7-e5
2. Cg1-f3 Cb8-c6
3. Af1-c4 Cg8-f6
4. Cf3-g5 d7-d5
5. e4xd5 Cf6xd5
6. Cg5xf7 …
La notación algebraica reducida es una variación de la notación convencional, consistente en omitir la casilla de origen (excepto cuando el peón come a otra pieza). Por ejemplo, si un movimiento se nota como Cf3, quiere decir que el caballo se ha movido a f3. Pero, ¿qué caballo? Lo normal es que sólo haya un caballo que se pueda mover a f3, pero a veces pueden producirse ambigüedades, que ya veremos como se resuelven. La partida anterior, en esta notación algebraica reducida, se representaría así:
1. e4 e5
2. Cf3 Cc6
3. Ac4 Cf6
4. Cg5 d5
5. e4xd5 Cxd5
6. Cxf7 …
De cualquiera de los modos se puede representar, con pocos símbolos, la partida completa. Elija una de las dos notaciones para almacenar en tu estructura de datos la partida conforme se vaya disputando, aunque es más recomendable la notación algebraica convencional, que carece de ambigüedades.
El formato PGN
PGN (Portable Game Notation) es el nombre de un formato de archivo para guardar partidas de ajedrez muy extendido entre la comunidad informático-ajedrecista. Muchos programas de ajedrez pueden leer y grabar partidas en este formato. En Internet puede encontrar muchos sitios donde descargarte partidas (famosas, históricas o simplemente curiosas) grabadas en archivos PGN.
Los archivos PGN son de texto ASCII, es decir, que pueden abrirse y leerse perfectamente con cualquier editor de texto. Utilizan notación algebraica reducida, así que, con un poco de práctica, pueden interpretarse a mano, es decir, sin necesidad de ordenador.
Debido a lo extendido que está, vamos a intentar que nuestro programa guarde las partidas en formato PGN. Si lo hace bien, no sólo podrá compartir las partidas guardadas por su programa con otros jugadores, sino que podrá descargarse partidas de Internet y cargarlas (y reproducirlas) en su programa.
Descripción del formato PGN
Los archivos PGN ofrecen muchas posibilidades. Aquí sólo nos referiremos a las imprescindibles para guardar y recuperar partidas. Puede encontrar descripciones completas del formato en Internet.
Aquí tiene un ejemplo de archivo PGN. Después comentaremos qué significa cada línea.
[Event "Badalona Open"] [Site "?"] [Date "1991.??.??"] [Round "?"] [White "J.Vila"] [Black "Richard Guerrero"] [Result "0-1"]
1. d4 d5 2. c4 e5 3. dxe5 d4 4. Nf3 Nc6 5. g3 Bg4 6. Nbd2 Bb4 7. Bg2 Qd7 8. a3 Bxd2+ 9. Qxd2 O-O-O 10. b3 f6 11. exf6 Nxf6 12. h3 Bf5 13. g4 Bg6 14. Nh4 Ne4 15. Bxe4 Bxe4 16. f3 Rhf8 17. fxe4 Qe7 18. Ng2 Qxe4 19. Rg1 Ne5 20. Nh4 Rf3 21. Kd1 Rf2 22. Qe1 d3 23. e3 d2 24. Qxf2 dxc1=Q+ 25. Kxc1 Nd3+ 0-1
Los archivos PGN tienen dos secciones: la cabecera, donde aparece información general (nombre del torneo, fecha, nombre de los jugadores, etc) y el cuerpo, donde se almacenan los movimientos de la partida usando notación algebraica reducida.
Cabecera del archivo PGN
En la cabecera encontramos varios campos, cada uno en una línea distinta y encerrados entre dos corchetes, “[" y "]“. Los campos más habituales son:
- Event: nombre del torneo o evento donde se produjo la partida
- Site: lugar de la partida. Observa que se puede escribir una “?” si no se conoce algún dato
- Date: fecha de la partida, en formato AAAA:MM:DD
- Round: ronda (si se trata de un torneo)
- White: nombre del jugador blanco
- Black: nombre del jugador negro
- Result: resultado de la partida. Puede ser “1-0″ (ganaron las blancas), “0-1″ (ganaron las negras), “1/2-1/2″ (tablas) o ” * ” (partida sin terminar)
Es posible que en algunos archivos PGN aparezcan otros campos en la cabecera. Si es así, nuestro programa puede, simplemente, ignorarlos.
Movimientos
Si observa el ejemplo de notación PGN que hemos escrito más arriba, lo primero que llama la atención es que los nombres de las piezas son diferentes. La razón es que se usan los nombres en inglés, y no en castellano, para identificar las piezas. Así pues, la letra que corresponde a cada pieza es:
- K = king (rey)
- Q = queen (reina o dama)
- R = rook (torre)
- B = bishop (alfil)
- N = knight (caballo)
- P = pawn (peón)
La P, como en español, no se usa. Observe que los movimientos se representan con notación algebraica reducida, en donde no aparece la casilla de origen, salvo cuando el peón se come a otra pieza (en este caso, puede aparecer la casilla de origen o sólo su columna).
Si sigue observando, verá que los movimientos se escriben con su número seguido de un punto, y, a continuación, separados por espacios, cada uno de los movimientos de ese turno, primero el del jugador blanco y luego el del negro. Después hay otro espacio y, tras el, el siguiente movimiento.
El jaque mate, que no aparece en este ejemplo, se representa con el símbolo # en lugar de + (éste se reserva para el jaque normal). Después del último movimiento, si la partida está acabada, aparece el resultado (0-1 en el ejemplo)
Ambigüedades
La notación algebraica reducida, al contrario que la algebraica normal, puede interpretarse, en algunas ocasiones, de varias maneras. La razón es que, al no indicarse la casilla de origen del movimiento, puede ocurrir que la casilla de destino pueda ser ocupada por varias piezas. En caso de ambigüedad, ésta se resuelve utilizando estas tres reglas:
- Si la pieza que debe moverse puede distinguirse por la letra de la columna que ocupa, se inserta esa letra en el movimiento, justo después de la letra que identifica la pieza. Por ejemplo: Nbd2 significa que el caballo que se mueve a d2 es el que estaba en la columna b.
- Si lo anterior falla, se inserta el número de la fila en vez de la letra de la columna.
- Si ambas cosas siguen provocando ambigüedad, se añadirán las dos cosas, o sea, la letra de la columna y el número de la fila de origen, como en la notación algebraica convencional, sólo que después de la letra de la pieza, no antes.
Por ejemplo, imagine que los caballos blancos están ocupando las casillas c3 y g1, y que al jugador blanco, que le toca mover, decide mover un caballo a la casilla e2. Si el movimiento se especifica sólo con “Ne2″, es imposible saber cuál de los dos caballos se ha movido. En cambio, usando el primer criterio para eliminar ambigüedades, se usará la notación “Nce2″ o “Nge2″ para indicar si el caballo que se mueve es el que estaba en la columna c o el de la g.
Otros símbolos
En los archivos PGN pueden aparecer otros símbolos, como interrogaciones, admiraciones, etc. Nosotros no los vamos a usar y, por lo tanto, no los generaremos desde nuestro programa. Al leer archivos PGN bajados de internet, ignoraremos cualquier símbolo que no sea los que hasta aquí hemos expuesto.
Guardar y cargar partidas usando el formato PGN
Una vez conocido y comprendido el formato PGN, lo que hay que hacer para guardar partidas está muy claro:
- Almacenar en alguna estructura de datos, y en notación algebraica, todos los movimientos de la partida conforme se vayan produciendo
- Desde la opción “guardar partida” del menú de opciones, invocar a una función que pregunte un nombre de archivo y, a continuación, escriba un archivo con formato PGN a partir de los movimientos almacenados en memoria. Este archivo tiene que tener su cabecera y su cuerpo, tal y como hemos descrito, para cumplir con el estándar PGN.
Para cargar las partidas guardadas, el procedimiento será al contrario:
- Preguntar un nombre de archivo y comprobar si existe.
- Leer los datos del archivo PGN e ir interpretando los movimientos, modificando las estructuras de datos como si los movimientos se estuvieran realizando en realidad.
Evidentemente, lo más difícil de hacer es controlar las posibles ambigüedades. Podemos establecer un plan de acción para dividir este problema en subproblemas:
- Primero, modificar el programa para que vaya almacenando en memoria los movimientos de la partida.
- Segundo, escribir el módulo para guardar partidas sin preocuparnos de las ambigüedades.
- Tercero, escribir el módulo que carga partidas, sin preocuparnos de las ambigüedades. Este paso lo podemos dividir en dos: la lectura del archivo propiamente dicha y la realización de los movimientos que están guardados en el archivo.
- Cuarto, pensar un modo de controlar las ambigüedades tanto al escribir como al leer.

19 comments
Comments feed for this article
Trackback link
http://profeblog.es/blog/alfredo/2009/06/22/ajedrez-paso-5-guardar-y-recuperar-partidas/trackback/
3 Agosto 2009 at 4:17
Ariel
EXCELENTE, EXCELENTE material. Muchas gracias, no he encontrado mucho material que explique las funciones para el manejo de archivos, hasta que entre a este blog. Ya esta en mis favoritos, seguire mirando. Gracias Alfredo.
Ariel !!!
3 Agosto 2009 at 4:20
Ariel
perdon mande el comentario en otra sección, este era para la seccion de “manipulación de archivos y directorios en C”
Ariel !!!
4 Agosto 2009 at 3:09
Jerson Peña
A pesar de que no te lo pedí, muchísimas gracias por esta tan clara explicación. A mi me pareció muy bien y aprendí mucho de él.
28 Agosto 2009 at 20:20
Gonzalo
hola alfredo, soy gonzalo te escribo desde argentina y queria pedirte si es pisble el programa ese de la ajedez, lo vi y me resulto muy interesante.
si es posible me lo enviarias por correo. ( gon_d10@hotmail.com )
saludos.
muchas gracias.
29 Agosto 2009 at 16:26
stonet
¡Muchas gracias por el post!
Me ha sido de gran ayuda.
Saludos.
10 Septiembre 2009 at 2:20
John
La forma de liberar memoria no la has explicado bien.
Si hacemos lo que tu dices, (un free solo) el sistema libera el contenido a donde apunta pero no libera la direccion del puntero y por lo tanto esa memoria no puede ser reutilizada.
Para liberar punteros correctamente, se debe usar free y despues establecer su valor a NULL.
Asi:
free(puntero);
puntero=NULL; /* o a 0 */
21 Septiembre 2009 at 5:42
Leandro
Muy buen Blog, y sobre todo muy bueno el blog… mis felicitaciones!!!. Gracias por exponer tus conocimientos!. Saludos…
22 Septiembre 2009 at 6:51
Carlos M.
Gracias por la función para saber el tamaño de un archivo.
Tuve algunos problemas por usarla debido a que el puntero de tipo FILE quedaba en la posición final.
Por ejemplo, obtenía el tamaño del archivo, y después cuando quería leer carácter obtenía solamente EOF, entonces de un rato noté que se debía hacer rewind(), pero finalmente noté que la función que obtiene el tamaño solo debe obtener el tamaño y no modificar la posición del puntero, es decir debe dejarlo intacto.
Así es que este es el código:
unsigned long int getFileSize(FILE* file)
{
unsigned long int sizeOfFile = 0;
if (file != NULL)
{
fpos_t position;
fgetpos( file, &position );
fseek( file, 0, SEEK_END );
sizeOfFile = ftell( file );
fsetpos( file, &position );
}
return sizeOfFile;
}
25 Septiembre 2009 at 18:13
javier
hola k tal, me llamo javier y le escribo desde argentina, navegando encontre esta pagina.
y se me ocurrio mandarle este mail, pues tengo un problemita de programacion pascal, por ai tal ves usted m pueda dar una idea y ayudarme
tneog un programa en pascal, el cual me lee un archivo de texto y realiza unas operaciones con el, me tiene que extraer todas las palabras que tienen algun carater raro, por ejemplo, los acentos en las vocales, y cuando me detecta algo asi me lo guarda en un archivo, el problema que me di cuanta es que cuando toma un renglon, linea o como se llame y que sea mayor a 255 carateres, no me procesa el resto de las pakabras que tiene ese renglon, y lo que yo queria saber es como puedo solucionar este problemita si es que se puede, tengo pascal por DOS,
no se si se prodra acer lo que kiero, pues estoy navegando y buscando ideras de como solucionarlo pero no encontre nada todavia, si usted ède le agradeceria que me diera alguna idea
o por ai si se puede, tomar ese renglon que tiene 300 carateres, dividirlo en dos renglones de 150 carateres cada uno, y procesarlos en forma separada
gracias
2 Octubre 2009 at 7:29
eyad
hola como estas ?
queria preguntarte si puedes disenar un juego de ajedrez para un sitio web que voy a hacer ? pero tengo varias sugerencias para adecionarla para la sala y la mesa , si puedes hacerlo , mandame a este emial o a :
nadaamor69@yahoo.com
esperando su respuesta
gracias
7 Octubre 2009 at 7:29
isra
excelente excelente excelente
programacion al alcance de los legos
7 Octubre 2009 at 12:35
hjbbh
#include <stdio.
main(void)
{
int opción;
puts(”¿Qué desea hacer? 1 = escribir, 2 = leer”);
puts(”Teclee 1 ó 2: “);
scanf(”%i”, opcion);
if (opcion == 1) escribir_archivo();
if (opcion == 2) leer_archivo();
return 0;
}
13 Octubre 2009 at 2:18
jose
por favor, me puede enviar un correo para una pregunta?
Gracias
14 Octubre 2009 at 16:08
yuleidis
fue de mucha ayuda para sacarme de algunas dudas que tenia de los cotadores y acomuladores
18 Octubre 2009 at 0:10
CARMEN NAVARRO
Los ordenadores son icialmente para realizar tareas sencillas, repetitivas y
programables, pueden realizar la misma tarea muchas veces por segundo durante años y nunca se aburren…
carmen navarro de IM-401 19793281
BUENAS NOCHE
18 Octubre 2009 at 0:41
karla P Núñez
Un contador es una variable cuyo valor se incrementa o decrementa en cada repetición de un bucle. Las variables acumuladoras tienen la misión de almacenar resultados sucesivos, y de acumular los resultados.
Los bucle se controla por medio de las variables y se componen, básicamente, de dos elementos:
un cuerpo del bucle o conjunto de instrucciones que se ejecutan repetidamente, una condición de salida para dejar de repetir las instrucciones y continuar con el resto del algoritmo.
karla patricia Núñez 19912860 IM-401
BUENAS NOCHE
18 Octubre 2009 at 2:17
Luis Enriquez
hOla me gusto mucho esta pagina, palabras sencillas y muy practico de enterder y comprender la programacion, yo soy novato, pero me gustaria aprencer a programar y es muy buena esta pagina para poder hacerlo.
Saludos
18 Octubre 2009 at 17:18
Iván
Buen artículo.
Un saludo.
18 Octubre 2009 at 22:54
Seccion IM-401 Jesus
Los contadores son variables que por la mayoría son enteros y se escribe en pascal como cont. Por otra parte los acumuladores se designa con la palabra acum y acumulan variables. Y por ultimo los conmutadores o interruptores que solo pueden tomar dos variables.
Un ejemplo de esto puede ser una calculadora.