/*--------------------------------------------------------------------
 * FICHERO:  Segund.c
 * OBJETIVO: Implementar el juego "Segundero"
 * AUTOR:    Pedro Reina
 * FECHA:    S.7.3.1998
 *------------------------------------------------------------------*/

/*--------------------------------------------------------------------
 * Ficheros de cabecera
 *------------------------------------------------------------------*/

#include <Olimpo.h>   /*  Versin 2.0  */
#include <string.h>   /*  strtok()     */

/*--------------------------------------------------------------------
 * Declaracin de funciones
 *------------------------------------------------------------------*/

void LeeConfig();

void Explica();
void ExplicaObjetivo();
void ExplicaManejo();
void ExplicaAutor();
void ExplicaTeclas();

void   Juego();
logico Partida();
void   InventaNumero();
void   InformePartida();

void   Opciones();
octeto PideDificultad();

/*--------------------------------------------------------------------
 * Definicin de macros constantes
 *------------------------------------------------------------------*/

#define DIF_BAJA  (octeto)0
#define DIF_MEDIA (octeto)1
#define DIF_ALTA  (octeto)2

#define MAX_LONG    (octeto)12
#define MAX_JUGADA  (octeto)12

/*--------------------------------------------------------------------
 * Variables globales
 *------------------------------------------------------------------*/

octeto Dificultad = DIF_BAJA;

/*--------------------------------------------------------------------
 * Definicin de macros funciones
 *------------------------------------------------------------------*/

#define LimpiaZonaTrabajo()  Pan_Borra(NEGRO,4,0,21,79)
#define LimpiaZonaJuego()    Pan_Borra(NEGRO,2,1,20,78)

/*--------------------------------------------------------------------
 * Programa principal
 *------------------------------------------------------------------*/
void main (Narg, Arg)
int   Narg;
char *Arg[];
  {
  enum { SALIDA_ESC, SALIDA, EXPLICA, JUEGO, OPCIONES };

  static cadena Principal[] = {">Salida", "E>xplica",
                               ">Juego", ">Opciones", NIL };
  cadena Nombre;
  entero Opcion=JUEGO;
  logico Sigue=SI;

  Pan_Define (PAN_TEXTO);
  Azr_Inicia();
  Prg_Presenta ( "Segundero", "2.1", "Pedro Reina", "Marzo 1998" );

  if ( Narg == 1 )
    {
    Nombre = Fch_Nombre ( "Segund","cnf");
    if ( Fch_Existe (Nombre) )
      { LeeConfig (Nombre); }
    }
  else
    {
    Nombre = Cad_Duplica ( Arg[1] );
    LeeConfig (Nombre);
    }
  Cad_Destruye (Nombre);

  Cdr_Caja (CDR_SIMPLE,1,0,3,79,NEGRO,VERDE);

  while ( Sigue )
    {
    Opcion = Men_Horizontal (2,2,77,Principal,Opcion);
    switch ( Opcion )
      {
      case SALIDA_ESC:
      case SALIDA:
           if ( Usr_Consulta ("Quieres terminar el programa?") )
             { Sigue = NO; }
           Opcion = SALIDA;
           break;
      case EXPLICA:       Explica();       break;
      case JUEGO:         Juego();
                          Cdr_Caja (CDR_SIMPLE,1,0,3,79,NEGRO,VERDE);
                          break;
      case OPCIONES:      Opciones();      break;
      }
    LimpiaZonaTrabajo();
    }

  Pan_Cierra();
  }

/*--------------------------------------------------------------------
 * Definicin de funciones
 *------------------------------------------------------------------*/

/*--------------------------------------------------------------------
 * FUNCION:  LeeConfig()
 * OBJETIVO: Leer el fichero de configuracion
 *------------------------------------------------------------------*/
void LeeConfig (Nombre)
cadena Nombre;
  {
  config    Config;
  cadena    Linea, Informe, CadenaError;
  contador  Tipo;
  logico    Error;
  char     *Etiqueta, *Aux, NumeroLinea[80];

  Informe = "Leyendo configuracin";
  Usr_Informa (Informe);

  if ( Config = Cnf_Abre (Nombre) )
    {
    while ( Linea = Cnf_Lee (Config) )
      {
      Error = NO;
      if ( Etiqueta = strtok (Linea, " ") )
        {

        if ( Cad_Igual (Etiqueta,"Sonido") )
          {
          if ( Aux = strtok (NULL, " ") )
            {
            if ( toupper(Aux[0]) == 'S' ) { Son_Enciende(); }
            else                          { Son_Apaga(); }
            }
          }

        else if ( Cad_Igual (Etiqueta,"Dificultad") )
          {
          if ( Aux = strtok (NULL, " ") )
            {
            if ( Cad_Igual (Aux,"Baja") )
              { Dificultad = DIF_BAJA; }
            else if ( Cad_Igual (Aux,"Media") )
              { Dificultad = DIF_MEDIA; }
            else if ( Cad_Igual (Aux,"Alta") )
              { Dificultad = DIF_ALTA; }
            else
              { Error = SI; }
            }
          }

        else
          { Error = SI; }

        if ( Error )
          {
          sprintf (NumeroLinea,
            "Opcin no reconocida en la lnea %d del fichero ",
            Cnf_Linea (Config));
          CadenaError = Cad_Une (NumeroLinea, Nombre, CAD_FIN);
          Usr_Avisa (CadenaError);
          Cad_Destruye (CadenaError);
          Usr_Informa (Informe);
          }

        } /* Fin if Etiqueta */
      Cad_Destruye (Linea);
      } /* Fin while Linea */
    } /* Fin if Cnf_Abre() */
  }

/*--------------------------------------------------------------------
 * FUNCION:  Explica()
 * OBJETIVO: Dirigir las explicaciones
 *------------------------------------------------------------------*/
void Explica()
  {
  enum { SALIDA_ESC, OBJETIVO, MANEJO, AUTOR, TECLAS };
  static octeto MaxOpcion = 4;

  static cadena MenuExplica[] = { ">Objetivo del juego",
                                  ">Manejo de las opciones",
                                  ">Contactar con el autor",
                                  ">Teclas PC y teclas QL",
                                  NIL };

  static cadena Presentacion[] = {
    "Puedes pedir explicacin sobre diferentes aspectos",
    "de este programa. Elige el que desees y pulsa ESC",
    "cuando hayas terminado.",
    NIL };

  logico Sigue = SI;
  entero Opcion = 1, i;

  while ( Sigue )
    {
    LimpiaZonaTrabajo();
    Pan_Color (NEGRO,BLANCO);
    for ( i=0 ; Presentacion[i] ; i++ )
      { Pan_PonTexto (6+i,15,Presentacion[i]); }

    Opcion = Men_Vertical (11,29,14,52,MenuExplica,Opcion);
    LimpiaZonaTrabajo();
    switch ( Opcion )
      {
      case SALIDA_ESC:  Sigue = NO;         break;
      case OBJETIVO:    ExplicaObjetivo();  break;
      case MANEJO:      ExplicaManejo();    break;
      case AUTOR:       ExplicaAutor();     break;
      case TECLAS:      ExplicaTeclas();    break;
      }
    if ( Opcion )  { Usr_PulsaUnaTecla(""); }
    Opcion++;
    if ( Opcion > MaxOpcion )  { Opcion = 1; }
    }
  }

/*--------------------------------------------------------------------
 * FUNCION:  ExplicaObjetivo()
 * OBJETIVO: Explicar el objetivo del juego
 *------------------------------------------------------------------*/
void ExplicaObjetivo()
  {
  static cadena Mensaje[] = {
      "Este juego pretende estimular la memoria visual inmediata.",
      "Para ello, se van proponiendo nmeros de distintas longitudes,",
      "que el jugador debe recordar despus de verlos un solo segundo.",
      "",
      "Si el jugador acierta el nmero propuesto, obtiene puntos (ms",
      "cuanto ms largo es el nmero) y el siguiente nmero ser un",
      "poco ms largo. Pero si falla, se le restan los puntos (menos",
      "cuanto ms largo sea) y el siguiente nmero ser ms corto.",
      "",
      "Se puede jugar con tres niveles de dificultad. Se aconseja",
      "comenzar por dificultad baja e ir entrenando. Al principio",
      "no se obtendrn muchos puntos, pero segn se vaya jugando,",
      "aumentar tu memoria y mejorars.",
      NIL };

  octeto i;

  Pan_Color (NEGRO,BLANCO);
  for ( i=0 ; Mensaje[i] ; i++ )
    { Pan_PonTexto (6+i,6,Mensaje[i]); }
  }

/*--------------------------------------------------------------------
 * FUNCION:  ExplicaManejo()
 * OBJETIVO: Explicar cmo se maneja el programa
 *------------------------------------------------------------------*/
void ExplicaManejo()
  {
  static cadena Mensaje[] = {
    "La opcion Salida sirve para acabar el programa.",
    "",
    "La opcin Explica es la que ests viendo.",
    "",
    "La opcin Juego permite jugar cuantas partidas quieras, con una",
    "misma dificultad. Se puede elegir la longitud del primer nmero,",
    "y al final de la partida aparece un resumen completo.",
    "",
    "En Opciones se pueden definir dos cosas: si el sonido est encendido",
    "o apagado, y el grado de dificultad del juego.",
      NIL };

  octeto i;

  Pan_Color (NEGRO,BLANCO);
  for ( i=0 ; Mensaje[i] ; i++ )
    { Pan_PonTexto (5+i,4,Mensaje[i]); }
  }

/*--------------------------------------------------------------------
 * FUNCION:  ExplicaAutor()
 * OBJETIVO: Explicar quin es el autor y cmo contactar con l
 *------------------------------------------------------------------*/
void ExplicaAutor()
  {
  static cadena Mensaje[] = {
    "Este programa es de Dominio Pblico. Lo puedes usar, distribuir",
    "y modificar como desees.",
    "",
    "Ha sido escrito en C usando el sistema de programacin Olimpo;",
    "si no dispones de la versin 2.0 de Olimpo, puedes cargarla",
    "directamente desde mi sede web o pedrmela si no dispones de",
    "acceso a Internet.",
    "",
    "Si tienes cualquier duda o consulta, cuntamela e intentar ayudarte.",
    "",
    "Mis datos:",
    "",
    "Pedro Reina",
    "c/ Marquesa de Argeso, 4      Telfono: 91 565 17 59",
    "28019 Madrid                   Correo electrnico: pedro@anit.es",
    "Espaa                         Web: www.anit.es/pedro",
    NIL };

  octeto i;

  Pan_Color (NEGRO,BLANCO);
  for ( i=0 ; Mensaje[i] ; i++ )
    { Pan_PonTexto (5+i,5,Mensaje[i]); }
  }

/*--------------------------------------------------------------------
 * FUNCION:  ExplicaTeclas()
 * OBJETIVO: Explicar las teclas que son distintas en QL y en PC
 *------------------------------------------------------------------*/
void ExplicaTeclas()
  {
  static cadena Mensaje[] = {
      "El QL y el PC tienen distintos teclados y algunas teclas",
      "no tienen correspondiente directo.",
      "",
      "Este programa asigna a ciertas combinaciones de teclas del QL",
      "la funcin de algunas teclas del PC. sta es la relacin:",
      "",
      "         Tecla PC        Equivalente QL",
      "         --------        --------------",
      "",
      "         Inicio          Alt-Izquierda",
      "         Fin             Alt-Derecha",
      "         RePag           Alt-Arriba",
      "         AvPag           Alt-Abajo",
      "         Insert          Maysculas-Fijamaysculas",
      "         Supr            Ctrl-Derecha",
      "         Retroceso       Ctrl-Izquierda",
       NIL };

  octeto i;

  Pan_Color (NEGRO,BLANCO);
  for ( i=0 ; Mensaje[i] ; i++ )
    { Pan_PonTexto (5+i,9,Mensaje[i]); }
  }


/*--------------------------------------------------------------------
 * FUNCION:  Juego()
 * OBJETIVO: Dirigir el juego
 * ENTRADAS: Ninguna
 * SALIDAS:  Ninguna
 *------------------------------------------------------------------*/
void Juego()
  {
  static cadena Mensaje[] = {
      "Vas a jugar una partida de Segundero. Aparecern distintos",
      "nmeros durante un segundo, que debers recordar a continuacin.",
      "",
      "Si aciertas, el siguiente nmero ser ms largo, y si fallas,",
      "ms corto.",
      "",
      "Mantn la concentracin hasta el final de la partida.",
      "",
      "Debes elegir la longitud del primer nmero:",
       NIL };
  octeto Longitud, LongitudMaxima, i;
  logico Sigue=SI;

  switch ( Dificultad )
    {
    case DIF_BAJA:   LongitudMaxima =  7;  break;
    case DIF_MEDIA:  LongitudMaxima = 10;  break;
    case DIF_ALTA:   LongitudMaxima = 12;  break;
    }

  Longitud = LongitudMaxima / 2;
  Cdr_Caja (CDR_SIMPLE, 1,0,21,79, NEGRO, VERDE);

  while ( Sigue )
    {
    LimpiaZonaJuego();
    Pan_Color (NEGRO,BLANCO);
    for ( i=0 ; Mensaje[i] ; i++ )
      { Pan_PonTexto (4+i,9,Mensaje[i]); }

    Longitud = Usr_Entero ((contador)Longitud, 2,
                           1, (contador)LongitudMaxima, 15, 40,
                           ROJO, BLANCO);
    if ( Tec_Ultima() == TEC_ESC )
      { Sigue = NO; }
    else
      { Sigue = Partida (Longitud, LongitudMaxima); }
    }
  }

/*--------------------------------------------------------------------
 * FUNCION:  Partida()
 * OBJETIVO: Jugar una partida
 * ENTRADAS: La longitud del primer nmero y la mxima
 * SALIDAS:  Lgica, que indica si el usuario quiere seguir jugando
 *------------------------------------------------------------------*/
logico Partida (Long, LongMax)
octeto Long, LongMax;
  {
  logico   Sigue=SI, Respuesta, Decidido;
  octeto   LongActual, Pos, JugadaMaxima, i;
  region   Panel;
  cadena   Aux;
  caracter Numero[MAX_LONG+1];
  cadena   Propuesto[MAX_JUGADA], Contestado[MAX_JUGADA];

  for ( i=0 ; i<MAX_JUGADA ; i++ )
    {
    Propuesto[i] = Cad_Crea (MAX_LONG);
    Contestado[i] = Cad_Crea (MAX_LONG);
    }

  switch ( Dificultad )
    {
    case DIF_BAJA:   JugadaMaxima =  7;  break;
    case DIF_MEDIA:  JugadaMaxima = 10;  break;
    case DIF_ALTA:   JugadaMaxima = 12;  break;
    }

  LimpiaZonaJuego();
  Panel = Reg_Crea (8,28,14,52);
  Pos = (Reg_Ancho(Panel) - LongMax) / 2;
  LongActual = Long;

  for ( i=0 ; i<JugadaMaxima && Sigue ; i++ )
    {
    InventaNumero (Numero, LongActual);
    Cad_Copia (Propuesto[i], Numero);

    Reg_Borde (Panel, CDR_DOBLE, NEGRO, ROJO);
    Pan_CursorVisible (NO);
    Reg_TextoCentrado (Panel, Numero, 3);
    Tim_Espera (1.0);

    Reg_Borde (Panel, CDR_SIMPLE, NEGRO, ROJO);
    Reg_TextoCentrado (Panel, "Escribe el nmero", 2);

    Decidido = NO;
    while ( !Decidido )
      {
      Aux = Usr_Texto ("", LongMax, Reg_FilSup(Panel)+4, Reg_ColIzq(Panel)+Pos,
                       ROJO, BLANCO);
      Cad_Copia (Contestado[i], Aux);
      Cad_Destruye (Aux);

      if ( Tec_Ultima() == TEC_ESC )
        {
        if ( Usr_Consulta ("Quieres abandonar esta partida?") )
          {
          Decidido = SI;
          Sigue = NO;
          }
        }
      else
        { Decidido = SI; }
      }

    if ( Cad_Igual (Propuesto[i], Contestado[i]) )
      { LongActual = Min (LongActual+1, LongMax); }
    else
      { LongActual = Max (LongActual-1, 1); }
    }

  if ( Sigue )
    {
    InformePartida (Propuesto, Contestado, JugadaMaxima, Long, LongMax);
    Respuesta = Usr_Consulta ("Quieres jugar otra partida?");
    }
  else
    { Respuesta = NO; }

  for ( i=0 ; i<MAX_JUGADA ; i++ )
    {
    Cad_Destruye (Propuesto[i]);
    Cad_Destruye (Contestado[i]);
    }
  Reg_Destruye (Panel);

  return ( Respuesta );
  }

/*--------------------------------------------------------------------
 * FUNCION:  InventaNumero()
 * OBJETIVO: Generar aleatoriamente un nmero de la longitud indicada
 * ENTRADAS: Una cadena donde dejar el nmero y la longitud
 * SALIDAS:  Ninguna
 *------------------------------------------------------------------*/
void InventaNumero (Numero, Long)
cadena Numero;
octeto Long;
  {
  octeto i;

  Numero[0] = Azr_Entero ('1', '9');
  for ( i=1 ; i<Long ; i++ )
    { Numero[i] = Azr_Entero ('0', '9'); }
  Numero[i] = NULO;
  }

/*--------------------------------------------------------------------
 * FUNCION:  InformePartida()
 * OBJETIVO: Informar al usuario cmo ha sido la partida
 * ENTRADAS: Los nmeros propuestos, los contestados, el nmero
 *           de jugadas, la longitud del primer nmero y la mxima longitud
 * SALIDAS:  Ninguna
 *------------------------------------------------------------------*/
void InformePartida (Propuesto, Contestado, MaxJugada, Long, LongMax)
cadena Propuesto[], Contestado[];
octeto MaxJugada, Long, LongMax;
  {
  static octeto Puntuacion[] = { 0, 1, 2, 3, 5, 7, 11, 15, 22, 30, 41, 57 };
  octeto   i;
  contador Punto, Total=0;

  LimpiaZonaJuego();
  Pan_Color (NEGRO, BLANCO);
  Pan_Resalta (SI);
  Pan_PonTexto (3, 27, "Resultado de la partida");
  Pan_Resalta (NO);

  Pan_Tinta (VERDE);
  Pan_PonTexto (5, 5, "Jugada");
  Pan_PonTexto (5, 15, "Propuesto");
  Pan_PonTexto (5, 30, "Contestado");
  Pan_PonTexto (5, 45, "Acierto");
  Pan_PonTexto (5, 56, "Puntos");
  Pan_PonTexto (5, 65, "Acumulado");

  Pan_Tinta (BLANCO);
  for ( i=0 ; i<MaxJugada ; i++ )
    {
    Pan_PonEntero (7+i, 7, i+1, 2);

    Pan_PonTexto (7+i, 15, Propuesto[i]);
    Pan_PonTexto (7+i, 30, Contestado[i]);

    Pan_Cursor (7+i, 47);
    if ( Cad_Igual (Propuesto[i], Contestado[i]) )
      {
      Pan_Texto ("S");
      Punto = Puntuacion[Long-1];
      Long = Min (Long+1, LongMax);
      }
    else
      {
      Pan_Texto ("No");
      Punto = -Puntuacion[LongMax-Long];
      Long = Max (Long-1, 1);
      }

    Pan_PonEntero (7+i, 57, Punto, 3);

    Total += Punto;
    Pan_PonEntero (7+i, 67, Total, 4);
    }

  Pan_Color (NEGRO, VERDE);
  Pan_PonTexto (MaxJugada+8, 27, "Puntos obtenidos: ");
  Pan_Tinta (BLANCO);
  Pan_Resalta (SI);
  Pan_Entero (Total,1);
  Pan_Resalta (NO);
  }

/*--------------------------------------------------------------------
 * FUNCION:  Opciones()
 * OBJETIVO: Seleccionar las opciones del programa
 * ENTRADAS: Ninguna
 * SALIDAS:  Las variables globales pueden modificarse
 *------------------------------------------------------------------*/
void Opciones()
  {
  enum { SALIDA_ESC, SONIDO, DIFICULTAD };
  static cadena MenuOpc[] = {">Sonido", ">Dificultad", NIL };
  entero Opcion = SONIDO;
  logico Sigue = SI;
  region Region;

  if ( Region = Reg_Crea (4,54,7,79) )
    {
    Reg_Borde (Region,CDR_SIMPLE,NEGRO,VERDE);
    while ( Sigue )
      {
      Pan_Color (NEGRO, VERDE);
      Reg_Cursor (Region,1,15);
      if ( Son_Estado() )  { Pan_Texto ("Encendido"); }
      else                 { Pan_Texto ("Apagado  "); }

      Reg_Cursor (Region,2,15);
      switch ( Dificultad )
        {
        case DIF_BAJA:  Pan_Texto ("Baja ");  break;
        case DIF_MEDIA: Pan_Texto ("Media");  break;
        case DIF_ALTA:  Pan_Texto ("Alta ");  break;
        }

      Opcion = Men_Vertical (Reg_FilSup(Region)+1, Reg_ColIzq(Region)+1,
                             Reg_FilInf(Region)-1, Reg_ColIzq(Region)+12,
                             MenuOpc, Opcion);
      switch ( Opcion )
        {
        case SALIDA_ESC: Sigue = NO;                      break;
        case SONIDO:     Son_Cambia();                    break;
        case DIFICULTAD: Dificultad = PideDificultad (Dificultad);
                         break;
        }
      }
    Reg_Destruye (Region);
    }
  else  { Usr_Avisa ("Falta memoria"); }
  }

/*--------------------------------------------------------------------
 * FUNCION:  PideDificultad()
 * OBJETIVO: Pedir al usuario la dificultad de juego
 * ENTRADAS: La dificultad original
 * SALIDAS:  La nueva dificultad
 *------------------------------------------------------------------*/
octeto PideDificultad (Dif)
octeto Dif;
  {
  static cadena MenuDificultad[] = {
                ">Baja     7 jugadas    Longitud mxima  7",
                ">Media   10 jugadas    Longitud mxima 10",
                ">Alta    12 jugadas    Longitud mxima 12",
                NIL };
  region Region;
  entero Opcion;

  if ( Region = Reg_Crea (11,17,17,62) )
    {
    Reg_Borde (Region,CDR_SIMPLE,NEGRO,VERDE);
    Pan_Color (NEGRO, VERDE);
    Reg_TextoCentrado (Region,"Elige la dificultad:",1);

    switch ( Dif )
      {
      case DIF_BAJA:   Opcion = 1;  break;
      case DIF_MEDIA:  Opcion = 2;  break;
      case DIF_ALTA:   Opcion = 3;  break;
      }

    Opcion = Men_Vertical (Reg_FilSup(Region)+3, Reg_ColIzq(Region)+2,
                        Reg_FilInf(Region)-1, Reg_ColDer(Region)-1,
                        MenuDificultad, (entero)Opcion);
    switch ( Opcion )
      {
      case 1: Dif = DIF_BAJA;   break;
      case 2: Dif = DIF_MEDIA;  break;
      case 3: Dif = DIF_ALTA;   break;
      }

    Reg_Limpia (Region, NEGRO);
    Reg_Destruye (Region);
    }
  else  { Usr_Avisa ("Falta memoria"); }

  return ( Dif );
  }