Bienveenidos sean a este post, hoy repasaremos un tema que ya vimos indirectamente.
Cuando hablamos de una estructura de una estructura nos estamos refiriendo a un struct donde internamente tendra los tipos de datos de otro struct y no solamente de los tipos primitivos como puede ser int, double o char, dado que en la vida real tendremos siempre que manejar con nuestros propios tipos y no solamente los primitivos, para explicar un poco mejor esto vamos a tomar el ejemplo que vinimos trabajando en los posts anteriores, si no lo tienen aqui pueden descargar el ultimo codigo:
Extraigan los dos archivos en un mismo directorio y listo, nuestro siguiente paso sera ir al archivo cartas.h y modificaremos este bloque:
enum {
cCartasEnMazo = 48,
cCartasEnMano = 3,
cCartasEnPalo = 12
};
De la siguiente manera:
enum {
cCartasEnMazo = 48,
cCartasEnMano = 3,
cCartasEnPalo = 12,
cNumJugadores = 4
};
En este caso simplemente agregamos una nueva constante que sera para indicar la cantidad de jugadores que tenemos en esta ronda, nuestro siguiente paso sera agregar un nuevo struct en el archivo cartas.h:
typedef struct {
int totalCartas;
Carta carta1;
Carta carta2;
Carta carta3;
} Jugador;
Este struct debera estar definido luego del struct Carta, en este caso tendra cuatro elementos los cuales representaran a las cartas que dispondra cada jugador y una variable que indicara la cantidad de cartas que dispone y nos sera util si necesitamos crear un nuevo «jugador» donde simplemente hacemos lo de siempre:
Jugador j1;
Nota: Esto podria funcionar mejor con un array de estructuras tal como vimos en el post anterior pero por el momento lo haremos asi para visualizar mejor el concepto comentado aqui y mas al final veremos el porque se puede trabajar con este otro concepto, con esto aclarado continuemos
Dsspues para modificar los datos simplemente lo hacemos de esta forma:
j1.carta.palo=basto;
j1.carta.numero=dos;
Y despues para utilizarlo podemos hacerlo de la siguiente manera:
Palo p = j1.carta.palo;;
Numero n = j1.carta.numero;
Esto que vinimos haciendo tambien podemos hacerlo por medio de apuntadores de la siguiente manera:
Jugador j1;
Jugador* aJugador = &j1;
Palo p;
Numero n;
aJugador->carta1.palo = basto;
aJugador->carta1.numero = dos;
p = aJugador->carta1.palo;
n = aJugador->carta1.numero;
Y a su vez esta forma de trabajar la podemos mejorar de la siguiente manera:
Jugador j1;
Jugador* aJugador = &j1.carta1;
Palo p;
Numero n;
aJugador->palo = basto;
aJugador->numero = dos;
p = aJugador->palo;
n = aJugador->numero;
Como pueden ver es mas especifico dado que solo trabajaremos con una sola carta, en este caso la carta1, y trabajar de esta forma nos permitiria trabajar nuevamente con la funcion IniciarCarta, con todo esto comentado volvamos a nuestro archivo cartas.h al cual le agregaremos unas cuatro funciones nuevas, estas seran las encargadas de manejar a los jugadores:
- IniciarJugador
- AgregarCartaAJugador
- MostrarJugador
- ObtenCartaJugador
Pero para poder trabajar con estas funciones debemos hacer unos cuantos cambios antes, el primero sera el struct que usamos para el jugador de la siguiente forma:
typedef struct {
int totalCartas;
Carta* aCarta1;
Carta* aCarta2;
Carta* aCarta3;
} Jugador;
En este caso la unica modificacion es que en lugar de usar elementos directo usaremos apuntadores de ese tipo, nuestro siguiente paso sera definir la funcion encargada de Iniciar las cartas, para ello deben agregar el siguiente bloque al final del archivo:
void IniciarJugador(Jugador* aJugador)
{
aJugador->totalCartas = 0;
aJugador->aCarta1 = NULL;
aJugador->aCarta2 = NULL;
aJugador->aCarta3 = NULL;
}
En este caso primero estableceremos un valor para el total de cartas y tambien haremos que nuestro apuntadores esten asignados con el NULL y por lo tanto no apunten a ningun lado, de esto ya hablamos en este post, a continuacion de esta funcion agregaremos el siguiente bloque:
Carta** ObtenCartaJugador(Jugador* aJugador, int indiceCarta)
{
Carta** aaC;
switch(indiceCarta)
{
case 0: aaC = &(aJugador->aCarta1); break;
case 1: aaC = &(aJugador->aCarta2); break;
case 2: aaC = &(aJugador->aCarta3); break;
}
return aaC;
}
Esta funcion sera la encargada de conseguir cuales son las cartas del jugador, primera particularidad que tendremos sera un apuntador de un apuntador, esto es debido a que nosotros pasamos de utilizar objetos creados de un struct a apuntadores de ese tipo, por lo tanto en esta funcion deberemos acceder al apuntador de un apuntador, como argumentos recibiremos un apuntador de tipo Jugador y un valor tipo int para indicar cual carta es, en el bloque declaramos otro apuntador de apuntador pero para un tipo Carta, nuestro siguiente paso sera un switch donde verificara cual es la carta que pasamos y este lo almacenaremos en el apuntador aaC, si observan todos los case son iguales pero se diferencian que en base al valor informado pasaremos el apuntador de la carta1 a la carta3, por ultimo devolvemos a este apuntador, pasemos a agregar la siguiente funcion:
void AgregarCartaAJugador(Jugador* aJugador, Carta* aCarta)
{
int numEnJugador = aJugador->totalCartas;
if (numEnJugador == cCartasEnMano) return;
Carta** aaC = ObtenCartaJugador(aJugador, numEnJugador);
*aaC = aCarta;
aJugador->totalCartas++;
}
Esta funcion sera la encargada de asignar las cartas a cada jugador, para ello pasaremos dos datos como son un apuntador para el jugador y otro para la carta, lo primero que haremos en el bloque es recuperar el valor de total de cartas del jugador que pasamos como argumento, el condicional verifica si el total de cartas es igual a la cantidad que puede tener en mano, este se encuentra en lass constantes que definimos al comienzo del archivo, y en caso de ser verdadero no hara nada y saldra de la funcion, en caso de no cumplirse la condicion definiremos un nuevo apuntador de apuntador de Carta para obtener la carta del jugador mediante la funcion antes explicada, para ello le pasaremos el jugador y el numero que recuperamos de este, aqui en lugar de isar el doble apuntador le pasamos la carta al apuntador de la variable, por ultimo incrementamos el contador de cartas del jugador, continuemos agregando la siguiente funcion:
void MostrarJugador(Jugador* aJugador, char* aCadenaJug, char* aCadenaLider)
{
printf("%s%s\n", aCadenaLider, aCadenaJug);
for(int i=0; i < aJugador->totalCartas; i++)
{
Carta** aaCarta = ObtenCartaJugador(aJugador, i);
printf("%s", aCadenaLider);
MostrarCarta(*aaCarta);
printf("\n");
}
}
Esta sera la funcion encargada de mostrar a cada jugador, por ello pasaremos tres datos, el primero sera el jugador, el segundo sera un texto para identificar el jugador, y un tercer texto que no usaremos por ahora, primero mostraremos las cadenas que pasamos como argumentos, despues tenemos un bucle for donde pasaremos por todas las cartas del jugador, primero definiremos un nuevo apuntador de tipo Carta pero que es doble, por todo lo comentado anteriormente, en ella almacenaremos el resultado de la funcion para obtener la carta, mostraremos uno de los argumentos recibidos y por ultimo llamaremos a la funcion encargada de mostrar la carta, de la cual hablamos en este post, la ultima linea es un simple Enter para una nueva linea, por ultimo agregaremos la siguiente funcion:
Carta* TomarCartaDelMazo(Carta aMazo[], int indice)
{
Carta* aCarta = &aMazo[indice];
return aCarta;
}
Esta funcion sera la encargada de tomar una carta del mazo para asignarla a un jugador pero ya veremos como elegir cualquiera del mazo mas adelante, por el momento le pasaremos el objeto que representa al mazo y un indice, definiremos un apuntador de tipo Carta y le asignaremos la carta del mazo en base al indice, una vez asignado lo devolveremos, con todo esto comentado veamos como quedo el codigo de nuestro archivo cartas.h:
cartas.h
enum {
cCartasEnMazo = 48,
cCartasEnMano = 3,
cCartasEnPalo = 12,
cNumJugadores = 4
};
typedef enum {
oro = 1,
copa,
basto,
espada
} Palo;
typedef enum {
uno = 1, dos, tres, cuatro, cinco, seis,
siete, ocho, nueve, sota, caballo, rey
} Numero;
typedef struct
{
Palo palo;
int valorPalo;
Numero numero;
int valorNumero;
} Carta;
typedef struct {
int totalCartas;
Carta* aCarta1;
Carta* aCarta2;
Carta* aCarta3;
} Jugador;
void CartaAString(Carta* aCarta,char aCadenaCarta[20])
{
switch(aCarta->numero)
{
case uno: strcpy(aCadenaCarta, "as "); break;
case dos: strcpy(aCadenaCarta, "dos "); break;
case tres: strcpy(aCadenaCarta, "tres "); break;
case cuatro: strcpy(aCadenaCarta, "cuatro "); break;
case cinco: strcpy(aCadenaCarta, "cinco "); break;
case seis: strcpy(aCadenaCarta, "seis "); break;
case siete: strcpy(aCadenaCarta, "siete "); break;
case ocho: strcpy(aCadenaCarta, "ocho "); break;
case nueve: strcpy(aCadenaCarta, "nueve "); break;
case sota: strcpy(aCadenaCarta, "sota "); break;
case caballo: strcpy(aCadenaCarta, "caballo "); break;
case rey: strcpy(aCadenaCarta, "rey "); break;
default: strcpy(aCadenaCarta, "??? "); break;
}
switch(aCarta->palo)
{
case oro: strcat(aCadenaCarta,"de Oro"); break;
case copa: strcat(aCadenaCarta,"de Copas"); break;
case basto: strcat(aCadenaCarta,"de Bastos"); break;
case espada: strcat(aCadenaCarta,"de Espadas"); break;
default: strcat(aCadenaCarta,"de ????"); break;
}
}
void IniciarCarta(Carta* aCarta, Palo p, Numero n)
{
aCarta->palo = p;
aCarta->valorPalo = (int)p;
aCarta->numero = n;
aCarta->valorNumero = (int)n;
}
void MostrarCarta(Carta* aCarta)
{
char cadenaCarta[20] = {0};
CartaAString(aCarta, cadenaCarta);
printf("%18s", cadenaCarta);
}
void IniciarMazo( Carta* aMazo )
{
Numero n[]={ uno, dos, tres, cuatro, cinco, seis,
siete, ocho, nueve, sota, caballo, rey };
Carta* aC;
for(int i=0; i < cCartasEnPalo; i++)
{
aC=&(aMazo[i + (0*cCartasEnPalo)]);
IniciarCarta(aC, oro, n[i]);
aC=&(aMazo[i + (1*cCartasEnPalo)]);
IniciarCarta(aC, copa, n[i]);
aC=&(aMazo[i + (2*cCartasEnPalo)]);
IniciarCarta(aC, basto, n[i]);
aC=&(aMazo[i + (3*cCartasEnPalo)]);
IniciarCarta(aC, espada, n[i]);
}
}
void MostrarMazo( Carta* aMazo )
{
printf("%d cartas en el mazo\n\n", cCartasEnMazo);
int indice = 0;
printf("El mazo ordenado: \n");
for(int i=0; i < cCartasEnPalo; i++)
{
indice=i + (0*cCartasEnPalo);
printf(" (%2d)", indice + 1);
MostrarCarta(&(aMazo[indice]));
indice=i + (1*cCartasEnPalo);
printf(" (%2d)", indice + 1);
MostrarCarta(&(aMazo[indice]));
indice=i + (2*cCartasEnPalo);
printf(" (%2d)", indice + 1);
MostrarCarta(&(aMazo[indice]));
indice=i + (3*cCartasEnPalo);
printf(" (%2d)", indice + 1);
MostrarCarta(&(aMazo[indice]));
printf("\n");
}
printf("\n");
}
void IniciarJugador(Jugador* aJugador)
{
aJugador->totalCartas = 0;
aJugador->aCarta1 = NULL;
aJugador->aCarta2 = NULL;
aJugador->aCarta3 = NULL;
}
Carta** ObtenCartaJugador(Jugador* aJugador, int indiceCarta)
{
Carta** aaC;
switch(indiceCarta)
{
case 0: aaC = &(aJugador->aCarta1); break;
case 1: aaC = &(aJugador->aCarta2); break;
case 2: aaC = &(aJugador->aCarta3); break;
}
return aaC;
}
void AgregarCartaAJugador(Jugador* aJugador, Carta* aCarta)
{
int numEnJugador = aJugador->totalCartas;
if (numEnJugador == cCartasEnMano) return;
Carta** aaC = ObtenCartaJugador(aJugador, numEnJugador);
*aaC = aCarta;
aJugador->totalCartas++;
}
void MostrarJugador(Jugador* aJugador, char* aCadenaJug, char* aCadenaLider)
{
printf("%s%s\n", aCadenaLider, aCadenaJug);
for(int i=0; i < aJugador->totalCartas; i++)
{
Carta** aaCarta = ObtenCartaJugador(aJugador, i);
printf("%s", aCadenaLider);
MostrarCarta(*aaCarta);
printf("\n");
}
}
Carta* TomarCartaDelMazo(Carta aMazo[], int indice)
{
Carta* aCarta = &aMazo[indice];
return aCarta;
}
Nuestro sigueinte paso sera modificar el codigo del archivo cartas.c con el siguiente codigo:
cartas.c
#include <stdio.h>
#include <string.h>
#include "cartas.h"
int main()
{
Carta mazo[ cCartasEnMazo ];
Carta* aMazo = mazo;
IniciarMazo(&mazo[0]);
Jugador j1, j2, j3, j4;
IniciarJugador(&j1);
IniciarJugador(&j2);
IniciarJugador(&j3);
IniciarJugador(&j4);
for(int i=0; i < cCartasEnMazo; i++)
{
AgregarCartaAJugador(&j1,TomarCartaDelMazo(aMazo, i));
AgregarCartaAJugador(&j2,TomarCartaDelMazo(aMazo, i+12));
AgregarCartaAJugador(&j3,TomarCartaDelMazo(aMazo, i+24));
AgregarCartaAJugador(&j4,TomarCartaDelMazo(aMazo, i+36));
}
MostrarJugador(&j1, "Jug 1: ", "");
MostrarJugador(&j2, "Jug 2: ", "");
MostrarJugador(&j3, "Jug 3: ", "");
MostrarJugador(&j4, "Jug 4: ", "");
}
Observen que las tres primeras son para crear el mazo, tal como vimos en el post anterior, lo siguiente sera declarar a nuestros cuatro «jugadores», despues iniciaremos a los cuatro jugadores por medio de IniciarJugador, nuestro siguiente paso sera pasar por todas las cartas del mazo, no seria necesario pero en otro post veremos porque, en cada caso llamamos a la funcion para agregar una carta al jugador, primero pasaremos la direccion de memoria de cada jugador, luego usaremos la funcion para tomar una carta del mazo, observen como para el primero usamos el contador del bucle pero para los siguientes incrementamos en doce para jugador tenga un palo distinto, por ultimo mostraremos a cada uno de los jugadores, con todo esto comentado compilemos y veamos como es su salida:
tinchicus@dbn001vrt:~/lenguajes/C$ ./prog/cartas
Jug 1:
as de Oro
dos de Oro
tres de Oro
Jug 2:
as de Copas
dos de Copas
tres de Copas
Jug 3:
as de Bastos
dos de Bastos
tres de Bastos
Jug 4:
as de Espadas
dos de Espadas
tres de Espadas
tinchicus@dbn001vrt:~/lenguajes/C$
Como pueden ver ahora no solo tenemos un mazo sino a cuatro jugadores los cuales dispondran de tres cartas distintas pero antes de terminar hablemos de porque no usamos lo visto en el post anterior, esto es debido a que todavia no tenemos un verdadero juego de cartas porque no hacemos una mezcla de las mismas y para poder llevar un mejor control lo haremos mejor con este tipo de manejo de estructuras, pero de eso hablaremos en un proximo post.
En resumen, hoy hemos visto como son las estructuras de estructuras, como se componen, como podemos recuperar informacion mediante varias formas asi como despues hemos modificado nuestro ejemplo de cartas para poder implementarlo, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
