Universidad de Costa Rica Facultad de Ingenierı́a Escuela de Ingenierı́a Eléctrica IE-0117 Programación bajo plataformas abiertas Laboratorio 4: Trivia Luis Alberto Soto Camacho, B57082 Esteban Varela Serrano, B57475 Jhonnathan Bolaños Paniagua, B51093 I-2019 Tabla de contenidos 1. Enunciado 2. Procedimiento 2.1. Sección 1 . 2.2. Sección 2 . 2.3. Sección 3 . 2.4. Sección 4 . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 3 4 6 3. Resultados 7 4. Conclusiones 9 1. Enunciado Implemente un juego de trivia utilizando el lenguaje de programación c. El programa funcionará de la siguiente manera: 1. Imprimir una bienvenida. 2. Solicitar a los jugadores (máximo 4) sus nombres. 3. Presentar una pregunta a cada jugador rotando tanto las preguntas como los jugadores hasta que no haya más preguntas que hacer. Las preguntas deben ser leı́das de un archivo de texto cuyo formato esta especificado en la figura 1. 1 4. Sumar un punto por cada pregunta contestada correctamente al jugador apropiado. 5. Imprimir el nombre del ganador al finalizar el juego 2. Procedimiento La elaboración del juego de trivia se dividió en 4 secciones principales, las cuales se explican a continuación: 2.1. Sección 1 Primeramente se declaran las variables a utilizar en el código. Se declaran las variables para almacenar la cantidad de jugadores y las preguntas, por esto se utilizan datos de tipo entero. int pn; int cantprgnts; //Cantidad de jugadores //Cantidad de preguntas Luego, para almacenar los nombres de los jugadores se utilizan variables de tipo char (caracter), estos se almacenan en un puntero (arreglo) de tamaño 4 para poder acceder más fácilmente a ellos. char n1[128]; //Nombres de los jugadores char n2[128]; char n3[128]; char n4[128]; char* n[4]= {n1,n2,n3,n4}; Ahora se inicializan las variables que van a almacenar los puntales de los jugadores, estas poseen un valor inicial de cero que es el puntaje con el que todos los jugadores inician la trivia. Estas variables se almacenan en un arreglo de tipo entero. int int int int int p1 = p2 = p3 = p4 = p[4] 0; //Puntos de los jugadores 0; 0; 0; = {p1,p2,p3,p4}; Se definen las variables para almacenar tanto las preguntas como las respuestas del archivo .txt. De igual manera que en el caso de los nombres de los jugadores, se almacenan las opciones en un puntero de tamaño 4. char pregunta[128]; //Array de la pregunta escaneada del .txt char op1[128]; //Array de las respuestas escaneadas del .txt char op2[128]; char op3[128]; char op4[128]; char* op[4] = {op1,op2,op3,op4}; 2 Finalmente se definen las variables los tipos de respuesta y se inicializa la variable del jugador actual en 0, ya que esta variable funcionará como un contador. También se utiliza una variable de tipo char que guarde el nombre del jugador ganador que se mostrará al final del juego. int respcorrecta; int respdada; int actualplyr = 0; char ganador[128]; 2.2. //Número de la respuesta correcta //Respuesta dada por el jugador //Jugador actual //Nombre del ganador Sección 2 Esta sección muestra las instrucciones presentadas al inicio del juego, donde se solicita la cantidad de jugadores y de acuerdo con el número seleccionado se le pide a cada jugador ingresar su nombre. Primeramente se imprimen en pantalla las instrucciones del juego: printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("**********************************************************\n"); printf(" Juego de Trivia \n"); printf("************************************************************"); printf("\n\n Instrucciones:\n -Lea cada pregunta e ingrese el valor de la respuesta que considere correcta.\n -Cada respuesta correcta suma un punto. \n -El jugador con más puntos gana.\n"); printf("-En caso de querer salir del juego antes presione cualquier letra cuando soliciten el número de la respuesta. \n \n"); Luego, se solicita al usuario que ingrese la cantidad de jugadores, para esto se utiliza la función scanf que va a almacenar el valor entero ingresado por el usuario en la variable pn. También se implementa un ciclo mediante un while para que cuando el valor ingresado sea menor a dos o mayor que cuatro se imprima en pantalla que ocurrió un error y que el usuario debe volver a ingresar el número de jugadores. Al ser variables de tipo entero se utilizan los especificadores de formato %d, %i que corresponden a los datos de tipo entero. printf("Ingrese cantidad de jugadores (2-4): "); scanf("%d", &pn); printf("\n"); while(pn < 2 || pn > 4){ printf("Error, intente de nuevo.\n "); scanf("%i",&pn); printf("\n"); } Para almacenar los nombres de los jugadores se utiliza un for, que recorre el arreglo declarado para almacenar los nombres de los jugadores y mediante la función scanf en cada iteración se guarda cada uno de estos nombres en la posición correspondiente, iniciando en n[0]. El ciclo se 3 detiene cuando el valor de la variable auxiliar i es menor que el número de jugadores ingresado previamente. for (int i = 0; i < pn; i++) { printf("Ingrese el nombre del jugador número %d: ", i+1); scanf("%s", n[i]); } printf("\n"); 2.3. Sección 3 Esta sección se compone de las preguntas junto con sus respectivas opciones, las cuales son escaneadas de un archivo de texto y se imprimen una por una de acuerdo con el turno de cada jugador, además se devuelve el resultado de cada respuesta dada por el jugador y se van sumando los puntos por cada respuesta acertada. Primeramente se define la variable f in, donde se va a almacenar el contenido que se lee del archivo de preguntas. Luego con fopen se abre el archivo y se asocia con f in, se utiliza r ya que solamente se desea leer del archivo. Para leer y guardar las preguntas del archivo de texto se utiliza la función fscanf, que va a guardar el contenido leı́do de la primer lı́nea del archivo y lo almacena en cantprgnts. FILE* f_in; f_in = fopen(argv[1], "r"); fscanf(f_in, "%i\n", &cantprgnts); Luego mediante un for se escanea la pregunta y el número de la respuesta correcta que se encuentra al final de cada pregunta del archivo. Esto se va a repetir hasta finalizar con la cantidad de preguntas presentes en el archivo. for (int i = 0; i < cantprgnts; i++) { fscanf(f_in, "%[^\n] %[^\n] %[^\n] %[^\n] %[^\n]", pregunta, op[0], op[1], op[2], op[3]); fscanf(f_in, "%i\n\n", &respcorrecta); \\Se escanea y almacena la respuesta correcta. 4 Se muestra en consola la pregunta a realizar y entre paréntesis el nombre del jugador actual ( %s indica que el tipo de dato es un caracter). Después se imprimen cada una de las opciones de la pregunta y se solicita la respuesta del jugador, este valor se almacena en la variable respdada mediante la función scanf. printf("%s(Jugador actual: %s)\n", pregunta, n[actualplyr]); printf("%s\n %s\n %s\n %s\n \n", op[0], op[1], op[2], op[3]); printf("Ingrese el número de la respuesta correcta: "); scanf("%i", &respdada); printf("\n"); Mediante la condición if se establece que en caso de que la respuesta ingresada sea la correcta se le asigna un punto al jugador, esto se realiza en la última lı́nea, cuando se accede al vector de puntajes (p) en la posición correspondiente al jugador actual (actualplyr). Además se imprime en pantalla que el jugador respondió correctamente. if (respdada == respcorrecta) { printf("------------------------------------ \n"); printf("¡Correcto!, la respuesta correcta es la %i.\n", respcorrecta); printf("%s obtiene 1 punto. \n", n[actualplyr]); printf("------------------------------------ \n \n"); p[actualplyr] = p[actualplyr] + 1; } En el caso contrario se imprime en pantalla que el jugador falló y se indica el número de la respuesta correcta. El puntaje del jugador se mantiene sin ningún cambio. else { printf("---------------------------------------- \n"); printf("INCORRECTO, la respuesta correcta era la número %i.\n", respcorrecta); printf("---------------------------------------- \n \n" ); } Luego, mediante otro if se controla el contador actualplyr que indica cuando es el turno del siguiente jugador. Cuando se tiene que el jugador actual +1 es igual que el número de jugadores ingresado por el usuario (pn), entonces se vuelve a iniciar con el primer jugador para realizar la siguiente pregunta. Para que el mensaje de ”siguiente jugador”no se repita en todas las ocasiones se utiliza un if, y ası́ solo se muestra el mensaje en consola hasta la última pregunta. 5 if (actualplyr+1 == pn) { actualplyr = 0; \\Se regresa al primer jugador. } else { actualplyr = actualplyr + 1; \\Siguiente jugador. } if(i<cantprgnts-1){ printf("El siguiente jugador es %s. \n\n", n[actualplyr]); } } //Fin del ciclo for. 2.4. Sección 4 En esta parte el procedimiento es sencillo, se cuentan los puntos de todos los jugadores presentes y a partir de esto se imprime al ganador o el empate. //Seccion 4: se muestra el puntaje y se elije un ganador //Se muestra el p printf("\n \n \n"); printf("------------------------------------------- \n"); printf("El puntaje final de cada jugador es: \n \n"); ganador[0] = n[0]; //Ganador default (para que no genere problemas). Se utiliza un for para imprimir en pantalla el nombre de cada jugador y sus puntos obtenidos. for (int i = 0; i < pn; i++) { printf("%s: %i.\n", n[i], p[i]); } Luego mediante cuatro condiciones if se detecta cual jugador ganó o si el juego terminó con un empate. Los else if sólo se ejecutan si la condición que poseen es verdadera, mientras que el else final se utiliza cuando ocurre el empate, ya que ninguna de las condiciones anteriores se cumplió. if (p[0]>p[1] && p[0]>p[2] && p[0]>p[3]){ //Caso en que el jugador 1 gana. printf("El ganador es ¡%s!\nGracias por jugar. :)\n", n[0]); printf("------------------------------------------- \n"); 6 }else if (p[1]>p[0] && p[1]>p[2] && p[1]>p[3]){ //Caso en que el jugador 2 gana. printf("El ganador es ¡%s!\nGracias por jugar. :)\n", n[1]); printf("------------------------------------------- \n"); }else if (p[2]>p[0] && p[2]>p[1] && p[2]>p[3]){ //Caso en que el jugador 3 gana. printf("El ganador es ¡%s!\nGracias por jugar. :)\n", n[2]); printf("------------------------------------------- \n"); }else if (p[3]>p[0] && p[3]>p[1] && p[3]>p[2]){ //Caso en que el jugador 4 gana. printf("El ganador es ¡%s!\nGracias por jugar. :)\n", n[3]); printf("------------------------------------------- \n"); }else{ //Caso en el que ocurre un empate. printf("Hay un empate\n\n"); printf("------------------------------------------- \n"); } fclose(f_in); //Se cierra el archivo de preguntas. return 0; } //Fin del código. 3. Resultados Se muestran los resultados obtenidos del juego con algunos ejemplos de prueba. Estos ejemplos se realizaron con un archivo de texto que contenı́a preguntas sobre capitales de paı́ses, el inicio del juego mostrado en la terminal se observa en la figura 1. Figura 1: Inicio del juego donde se muestran las instrucciones. 7 Luego en la siguiente figura, al ingresar la cantidad de los jugadores se observa el caso cuando la cantidad digitada es incorrecta, y después cuando el número ingresado es el correcto. Si el número se encuentra dentro de los permitidos se solicitan los nombres de los jugadores. Figura 2: Cantidad y nombres de los jugadores. Cuando se realiza la pregunta y el jugador logró responder correctamente se muestra: Figura 3: Cuando la respuesta es correcta. En el caso que la respuesta sea incorrecta: Figura 4: Cuando la respuesta es incorrecta. 8 Al finalizar las preguntas si se obtuvo un ganador se muestra: Figura 5: Cuando uno de los jugadores ganó. Si el puntaje final es igual para al menos dos jugadores se genera un empate y ningún jugador resulta ganador: Figura 6: Empate. 4. Conclusiones Es importante tomar en cuenta que el sı́mbolo ∗ en C se puede utilizar para definir variables de tipo puntero pero también para acceder al contenido que almacena una variable de este tipo al desreferenciarla. Cuando se están utilizando arreglos no se debe utilizar el operador & en ellos, ya que los arreglos son punteros (direcciones de memoria). También, aunque los arreglos son punteros, al definir un arreglo se debe hacer adecuadamente y no utilizando la notación de puntero. Hay que tomar en cuenta las distintas condiciones que se escriben en los ciclos, como en un while, ya que en ocasiones por no corroborar las condiciones establecidas el programa puede entrar en un ciclo infinito. 9