Programa de Educacion Asistida por Computadora en C.

Iniciado por oblivionxor, 11 Febrero 2013, 03:50 AM

0 Miembros y 1 Visitante están viendo este tema.

oblivionxor

#10
Muchas gracias a todos por sus consejos! dato000 pondré en practica lo de poner signos al principio de los comentarios de funcionamiento y de estructura! leosansan respecto a lo que dices crees que de esta manera sea correcto validar la entrada? :
   
    do
    {
            scanf( "%d", &operando );
     
            if ( operando != 1 && operando != 2 && operando != 3 && operando != 4 && operando != 5 )
                  printf( "Opcion no valida, intenta de nuevo..." );
     
    } while ( operando != 1 && operando != 2 && operando != 3 && operando != 4 && operando != 5 );


O hay otra forma? Todavia no llego al tema de arreglos ni punteros, soy super principiante pero solo me gustaria saber si hay otra forma de hacer la validacion sin usar los temas que no he visto?

oblivionxor


oblivionxor

Y vuelvo a actualizar jajaja, ya me puse a investigar y encontré un truco que limpia el buffer y pues lo use para que elimine cualquier carácter ingresado y deje intactas las variables con el valor que les preestableci en la declaración. leonsansan crees que lo haya hecho bien? El código quedo asi:

/* Este programa sirve para practicar sumas, restas, multiplicaciones y divisiones */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* Prototipos de funciones */
float generarNumero( int nivelDificultad );
float operacion( float numero1, float numero2, int operacionElegida );
void respuestaCorrecta( void );
void respuestaIncorrecta( void );
void impresionTipoDeOperacion( int tipoDeOperacion );

/* Inicia la ejecucion del programa */
int main()
{
int i, correcta, nivel, operando, operando2;
float num1, num2, resultado, intento;
correcta = 0;
/* Estas declaraciones nos serviran para que si el usuario ingresa un caracter en vez de una letra, se falle la prueba de validacion.
La variable intento esta declarada a ese valor por que hay veces que los resultados de las funciones dan 0 y puede haber un error de logica en
el programa */
operando = 0;
nivel = 0;
intento = -2000000;

srand( time( NULL ) ); /* Modificacion de la semilla de los numeros pseudoaleatorios */

printf( "Bienvenido al sistema de aprendizaje asistido por computadora, se te preguntaran\n" );
printf( "10 operaciones y luego se te mencionara si necesitas ayuda tutorial.\n" );

printf( "Por favor ingresa el nivel que quieres practicar ( 1 al 3 )..." );

do
{
scanf( "%d", &nivel );
while ( getchar() != '\n' ); /* Esta instruccion sirve para eliminar todos los caracteres que se ingresen. Esto dejara la variable
nivel intacta con su valor original "0" y fallara la prueba de validacion */

if ( nivel < 1 || nivel > 3 )
printf( "Nivel no valido, intenta otravez..." );

} while ( nivel < 1 || nivel > 3 );

printf( "Que tipo de operaciones quiere resolver:\n" );
printf( "1. Sumas\n2. Restas\n3. Multiplicaciones\n4. Divisiones\n" );
printf( "5. Operaciones Aleatorias\n" );
printf( "Recuerda que no es obligatorio hacer las operaciones mentalmente, puedes\n" );
printf( "apoyarte de lapiz y papel para realizarlas. ATENCION! No se permite el\n" );
printf( "uso de calculadora.\n..." );

do
{
scanf( "%d", &operando );
while ( getchar() != '\n' ); /* Esta instruccion sirve para eliminar todos los caracteres que se ingresen. Esto dejara la variable
operando intacta con su valor original "0" y fallara la prueba de validacion */

if ( operando < 1 || operando > 5 )
printf( "Opcion no valida, intenta de nuevo..." );

} while ( operando < 1 || operando > 5 );

operando2 = operando; /* Esta variable guarda el valor de operando, esto es para tener intacta nuestra variable y modificar la copia */

for ( i = 1; i <= 10; i++ )
{
/* Llamamos a las funciones generadoras de numeros aleatorios */
num1 = generarNumero( nivel );
num2 = generarNumero( nivel );

if ( operando == 5 ) /* Y por este motivo es por el que debemos mantener intacta nuestra variable operando */
operando2 = 1 + rand() % 4; /* Genera una operacion aleatoria */

resultado = operacion( num1, num2, operando2 ); /* Llamamos a la funcion que resuelve la operacion elegida ( o generada ) */

printf( "Cuanto es %.0f ", num1 );
impresionTipoDeOperacion( operando2 );
printf( " %.0f?...", num2 );

do
{
scanf( "%f", &intento );
while ( getchar() != '\n' ); /* Funcion de esta instruccion ya explicada arriba */

if ( intento != resultado )
{
respuestaIncorrecta(); /* Llamada a la funcion que genera los mensajes aleatorios cuando se responde incorrectamente */
correcta--; /* Se decrementa en uno cada vez que se ingresa una respuesta correcta */
}
} while ( intento != resultado );

/* Cuando se ingresa la respuesta correcta, se llama a a funcion que genera el mensaje aleatorio y se incrementa correcta */
respuestaCorrecta();
correcta++;

} /* Fin de for */

if ( correcta < 7 ) /* Si hubo mas de 3 respuestas incorrectas, se imprime este mensaje */
printf( "Cometiste varios errores, por favor pide ayuda adicional a tu profesor. Hasta la proxima!\n" );

return 0;
} /* Fin de main */

/* Definicion de funciones */
float generarNumero( int nivelDificultad )
{
switch ( nivelDificultad ) /* Para generar numeros de 1, 2 y 3 digitos en los niveles 1, 2 y 3 respectivamente */
{
case 1:
return 1 + rand() % 9;
break;

case 2:
return 10 + rand() % 90;
break;

case 3:
return 100 + rand() % 900;
break;
} /* Fin de switch */
} /* Fin de funcion generaNumero */

float operacion( float numero1, float numero2, int operacionElegida )
{
switch ( operacionElegida ) /* Para la resolucion de nuestra operacion, esto es para compararlo posteriormente con la respuesta del user*/
{
case 1:
return numero1 + numero2;
break;

case 2:
return numero1 - numero2;
break;

case 3:
return numero1 * numero2;
break;

case 4:
return numero1 / numero2;
break;
} /* Fin de switch */
} /* Fin de funcion operacion */

void respuestaCorrecta( void )
{
switch ( 1 + rand() % 4 )
{
case 1:
printf( "Muy bien!!\n" );
break;

case 2:
printf( "Excelente!!\n" );
break;

case 3:
printf( "Manten ese buen rendimiento!!\n" );
break;

case 4:
printf( "Buen trabajo!!\n" );
break;
} /* Fin de switch */
} /* Fin de funcion respuestaCorrecta */

void respuestaIncorrecta( void )
{
switch ( 1 + rand() % 4 )
{
case 1:
printf( "No. Por favor intenta de nuevo...\n" );
break;

case 2:
printf( "Incorrecto. Trata una vez mas\n" );
break;

case 3:
printf( "No te rindas!!\n" );
break;

case 4:
printf( "No. Sigue intentando\n" );
break;
} /* Fin de switch */
} /* Fin de funcion respuestaIncorrecta */

void impresionTipoDeOperacion( int tipoDeOperacion )
{
switch ( tipoDeOperacion )
{
case 1:
printf( "mas" );
break;

case 2:
printf( "menos" );
break;

case 3:
printf( "por" );
break;

case 4:
printf( "entre" );
break;
} /* Fin de switch */
} /* Fin de funcion impresionTipoDeOperacion */


Si todavia tienen mas consejos, por favor no duden en decirmelos jaja. Gracias.

rir3760

Cita de: oblivionxor en 11 Febrero 2013, 03:50 AMHice un programa anteriormente (distinto a este) y me hicieron la recomendación de no comentar tanto el código por que se veia muy adornado y pues puse en practica ese consejo, esta comentado pero solo lo necesario.
Un buen consejo. En mi opinión deberías eliminar todos los comentarios del tipo "fin de esto" ya que son las llaves las que indican el termino de un bloque. Si tienes problemas para detectar el inicio/fin de un bloque o función es mejor optar por un editor de texto para programadores con la característica de resaltado (por ejemplo los basados en Scintilla).

Cita de: amchacon en 11 Febrero 2013, 08:39 AM
Bueno yo le cambiaría el diseño de las llaves [...] Siguiendo las normativas de estilo las pondría abajo:
Código (cpp) [Seleccionar]

do
{
         scanf( "%d", &operando );

         if ( operando < 1 || operando > 5 )
         {
              printf( "Opcion no valida, intenta de nuevo..." );
         }
} while ( operando < 1 || operando > 5 );

La ventaja que tiene es que puede ver mejor a que corresponde cada llave cerrada, ya que se encuentran al mismo nivel.
Hay varios estilos y algunos de estos se pueden revisar en la pagina de la aplicación astyle), lo importante no es buscar cual es el mejor (no hay respuesta, es subjetivo) sino utilizar uno de forma consistente.

----

En cuanto al programa la única forma 100% a prueba de fallos para la lectura de un numero es mediante funciones como strtol, strtoul y strtod. Se puede utilizar la familia scanf pero con sus limitantes. Con esta ultima se debe verificar primero su valor de retorno (numero de conversiones realizadas con éxito) y, si aplica, que el numero este en el rango valido. Un programa de ejemplo:
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
   int opcion;
   int ok;
   int ch;
   
   while (1){
      printf("Indica la opcion: ");
      fflush(stdout);
      ok = scanf("%d", &opcion) == 1 && opcion > 0 && opcion < 6;
     
      while ((ch = getchar()) != EOF && ch != '\n')
         ;
     
      if (ok)
         break;
      else
         puts("Opcion no valida, intenta de nuevo...");
   }
   printf("La opcion es la %d\n", opcion);
   
   return EXIT_SUCCESS;
}


La función "generarNumero" se puede reducir utilizando la función pow (prototipo en <math.h>):
#include <math.h>

/* ... */

float generarNumero(int nivel)
{
   int a = pow(10.0, nivel - 1.0);
   
   return a + rand() % (9 * a);
}


Y las funciones que seleccionan e imprimen una cadena al azar o indicando su posición se pueden reducir utilizando arrays:
void respuestaCorrecta(void)
{
   char *resp[] = {
      "Muy bien!!",
      "Excelente!!",
      "Manten ese buen rendimiento!!",
      "Buen trabajo!!"
   };
   
   puts(resp[rand() % 4]);
}

void impresionTipoDeOperacion(int num_op)
{
   char *op[] = {
      "mas",
      "menos",
      "por",
      "entre"
   };
   
   printf("%s", op[num_op - 1]);
}


Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

capsulasinformaticas

Cita de: rir3760 en 12 Febrero 2013, 18:05 PM
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
   int opcion;
   int ok;
   int ch;
   
   while (1){
      printf("Indica la opcion: ");
      fflush(stdout);
      ok = scanf("%d", &opcion) == 1 && opcion > 0 && opcion < 6;
     
      while ((ch = getchar()) != EOF && ch != '\n')
         ;
     
      if (ok)
         break;
      else
         puts("Opcion no valida, intenta de nuevo...");
   }
   printf("La opcion es la %d\n", opcion);
   
   return EXIT_SUCCESS;
}


Me da lata extender mas el tema e irnos por las ramas, pero tengo una duda, ¿Siempre que quiera mostrar un printf sin que finalize con un '\n' seria conveniente usar un fflush(stdout)?

Y otra cosa que no entendi es el uso de while ((ch = getchar()) != EOF && ch != '\n')
         ;
en ese codigo, es decir, probe el codigo sin esa parte y como que el bucle hace un loop infinito y no me puedo ingresar la opcion con scanf("%d", &opcion) si ingreso una opcion invalida, pero no se por que pasa.

Saludos.

amchacon

Cita de: capsulasinformaticas en 13 Febrero 2013, 03:46 AM
Me da lata extender mas el tema e irnos por las ramas, pero tengo una duda, ¿Siempre que quiera mostrar un printf sin que finalize con un '\n' seria conveniente usar un fflush(stdout)?
En realidad, el buffer se vacía automáticamente al usar un \n. Por lo que no sería necesario

Cita de: capsulasinformaticas en 13 Febrero 2013, 03:46 AMY otra cosa que no entendi es el uso de while ((ch = getchar()) != EOF && ch != '\n')
         ;
en ese codigo, es decir, probe el codigo sin esa parte y como que el bucle hace un loop infinito y no me puedo ingresar la opcion con scanf("%d", &opcion) si ingreso una opcion invalida, pero no se por que pasa.

Saludos.

Es una forma para limpiar el buffer de teclado (mientras no se llegue al salto de línea o al máximo buffer disponible...).

Por cierto muy elegante el código, me gusta (aunque sigo insistiendo que la llave del while estaría mejor abajo).
Por favor, no me manden MP con dudas. Usen el foro, gracias.

¡Visita mi programa estrella!

Rar File Missing: Esteganografía en un Rar

rir3760

Cita de: capsulasinformaticas en 13 Febrero 2013, 03:46 AM¿Siempre que quiera mostrar un printf sin que finalize con un '\n' seria conveniente usar un fflush(stdout)?
Si, es necesario.

En el caso de los bufers relacionados con streams de salida estos se vacían en tres escenarios, cuando:

A) Se llena, vaciado automático.
B) La secuencia de caracteres es seguida por el caracter '\n'.
C) Se indica de forma explicita mediante la llamada fflush(stream).

En nuestro caso la opción A no vale porque no se tiene control de este, la segunda tampoco porque la cadena no termina con el mentado carácter, la única opción es la restante.

Si ello no se hace el funcionamiento del programa puede resultar confuso ya que puede este esperar una entrada y solo después enviar el mensaje al stream de salida (por ejemplo instrucciones de uso).

Cita de: capsulasinformaticas en 13 Febrero 2013, 03:46 AMY otra cosa que no entendi es el uso de while ((ch = getchar()) != EOF && ch != '\n')
         ;
en ese codigo, es decir, probe el codigo sin esa parte y como que el bucle hace un loop infinito y no me puedo ingresar la opcion con scanf("%d", &opcion) si ingreso una opcion invalida, pero no se por que pasa.
Porque la función scanf solo consume los caracteres que sean validos para la conversión en turno. Si el carácter a procesar no cumple se queda en la entrada estándar.

Dos ejemplos para explicarlo mejor (con la mentada llamada a scanf):

A) La entrada contiene los caracteres:
' ', ' ', ' ', '1', '2', '3', '\n'
Aquí los caracteres de espacio blanco se descartan, '1', '2' y '3' se utilizan para obtener el entero 123. Como el '\n' no es valido para la conversión (valor de tipo signed int) este se queda en el bufer de la entrada estándar.

B) La entrada contiene:
'J', '\n'
Aquí la función revisa el bufer encontrando el carácter 'J', como este no es valido se queda en el bufer y la función termina de inmediato. En la siguiente iteracion sucede lo mismo y ello ocasiona el bucle infinito que mencionas.

Para evitarlo se descarta el resto de la linea mediante el bucle.

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language