jueves, 6 de abril de 2023

Curso C# 3 Adivina el número

 


Vamos a seguir con nuestro mini curso de C# realizando un nuvo ejercicio, se tratatde una aplicación de consola, que llamaremos Adivina el número. Las características a implementar sonlas siguientes:
  • El usuario deberá adivinar un número generado aleatoriamente comprendido entre el 1 y el 10.
  • Cuando el usuario no acierte, el juego debe indicar si el número a adivinar es más alto o mas bajo.
  • El juego debe guardar registro de los intentos que el usuario ha hacho hasta adivinar el número.
  • El juego debe mostrar la puntuación más alta (menor número de intentos) antes de iniciar una nueva partida.

Creo el proyecto de consola con mi terminal, una vez creado el proyecto me situo dentro de la carpeta y con el comando code . lo abro en VScode:


Empezaremos creando algunos métodos, empezaremos con un método StartGame que será invocado nada más ejecutar y donde tendremos agrupadas las funcionalidades del juego

 
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
 
}
 

Lo primero que voy a hacer es imprimir por consola un mensaje de bienvenida y le pediré su nombre algunos mensajes y le pediremos al usuario si quiere empezar el juego

 
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    var player = Console.ReadLine();
    Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
}

 

Vamos a ejecutar nuestro juego para ver que tenemos 



En el juego anterior hicimos una validación de forma que para que el juego comience el usuario tiene que escribir Y, en este caso le damos la opción desde el inicio de decir Y o N, por tanto, debemos validar que pasa cuando el usuario no escribe una de las dos opciones, en el caso anterior lo hicimos con una estructura de control if else, en este caso tenemos una variable wantToPlay que guarda la respuesta, una estructura que nos podría servir también es el Switch:

switch (wantToPLay?.ToLower())
    {
 
    }

 

Observa que a la variable que vamos a analizar mediante el switch, le he añadido un signo de interrogación, esto es por que el consoleWriteline donde se lee la respuesta Odría ser null (es decir que el usuario no hubiera escrito nada) con el signo de interrogación pregunto si hay un string en la respuesta y si hay un string lo paso a minúsculas

 switch (wantToPLay?.ToLower())
    {
    case "y":
       //Do something
       break;
    case "n":
       //Do somthing
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
       break;
    }
 

Contemplaremos las dos opciones y / n y en el default crearemos el mensaje de error, con esta estructura nos aseguramos de que el usuario conteste y o n cualquier otra respuesta lanzará el mensaje de error.

Ahora crearé dos métodos vacíos por el momento para cada una de las opciones estos métodos se llamarán Play() y DontPlay().

 
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    var player = Console.ReadLine();
    Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower())
    {
    case "y":
       //Do something
       break;
    case "n":
       //Do somthing
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    break;    
    }
 
}
 
void Play()
{
 
}
 
void DontPlay()
{
   
}


Teniendo estos dos métodos, desde el Yes llamo al Play y desde el No llamo al DontPlay():

 
switch (wantToPLay?.ToLower())
    {
    case "y":
       Play();
       break;
    case "n":
       DontPlay();
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    break;    
    }
 

Si ejecutamos ahora el juego e introducimos alguna respuesta errónea, es decir algo que no sea y o n vemos que pasa:



Todo funciona bien, si damos una respuesta errónea se nos indica que demos una respuesta válida, pero en ese momento se sale del juego y eso no es lo que queremos, queremos darle ala opción al usuario de introducir una respuesta válida sin necesidad de volver e ejecutar el juego.

Par solucionar esto lo que podemos hacer es meter la variable wantToPlay y el switch, en un nuevo método que podemos llamar WantToPlay:

De manera que nuestro código ahora queda:

 
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    var player = Console.ReadLine();
 
}
 
void WantToPlay(string player)
{
     Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower())
    {
    case "y":
       Play();
       break;
    case "n":
       DontPlay();
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    break;    
    }
   
}
 
void Play()
{
 
}
 
void DontPlay()
{
 
}
 


Observa que al método WantToPlay hemos tenido que pasarle el parámetro player para que pueda saludar al jugador, también podríamos haber hecho de player una variable global para que estuviera disponible para todos los métodos, pero de momento lo dejaremos así.

Debemos ahora llamar a nuestro nuevo método desde el método de inicio StartGame:

void StartGame()
{
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    var player = Console.ReadLine();
    WantToPlay(player);
 
}

 

Y Ahora usuando la recursividad, lo que queremos es que si el usuario introduce algo que no sea y o n que se vuelva a lanzar el juego para darle la oportunidad de dar una respuesta correcta para elo en WantToPlay en el default del Switch donde lanzamos el mensaje de error vovlemos a llamar al método, es decir el método se llama a sí mismo:

void WantToPlay(string? player)
{
     Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower())
    {
    case "y":
       Play();
       break;
    case "n":
       DontPlay();
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    WantToPlay(player);
    break;    
    }
   
}

 

Observa que a la variable string player paada como parámetro al método le hemos tenido que poner el signo de interrogaciín ya que el parámetro puede ser un string pero vacío

void WantToPlay(string? player)

 

Vamos a ejecutar a ver que pasa ahora:


Nos pide le nombre se lo damos pero respondemos a si queremos jugar con una f, nos indica que le demos una respuesta y o n le decimos n y se sale del juego. (si le decimos y también alimos del juego en este momemnto)

Método Trim

Algo bastante común es que el usuario de forma involuntaria introduzca un espacio vacío al ingresar su respuesta esto no daría error , veamos


Contesto y y añado un espacio, podemos ver que nos indica que l respuesta no es válida. Para evitar esta situación añadiremos el a la variable wantToPlay en el switch el método Trim, que elimina los espacio sen blanco por delante y por detrás de una cadena de texto, también tenemos trim end (que lo elimina solo por detrás) y trim start que lo hace sólo por delante:

    switch (wantToPLay?.ToLower().Trim())

 

con esto solucionamos esa situación si ahora ponemos y o n y accidentalmente añadimos un espacio en blanco este no se tiene en cuenta. Como ves podemos trabajar con diferentes métodos para strings e ir concatenándolos.

Métodos DontPlay y Play

Vamos a ocuparnos ahora de los métodos, el caso más sencillo es cuando el usuario decide no jugar, en ese caso simplemente lanzamos un mensaje de despedida y listo:

void DontPlay()
{
  Console.WriteLine("No worries! Have a good one!");
}

 

Vamos ahora al caso de que si quiera jugar. Lo primero que vamos a hacer es generar un número aleatorio del 1 al 10  con el método Random() de la librería math, y lo guardaremos en una variable que podemos llamar randomNumber:

void Play()
{
   //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
}

Try Catch (Manejo de excepciones)

Vamos a introducir el manejo de errores y la primera estructura de control que veremos será el bloque try catch:
Que consta de un try donde haremos lo que queremos hacer y un bloque catch donde trataremos los posbles errores, capturando el objeto Exception y dando un nombre a ese objeto por ejemplo e, de este modo en este objeto tendremos toda la información del error y podremos mostrar esa información al usuario:
void Play()
{
   //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
   try{
 
   }
   catch(Exception e)
   {
      Console.WriteLine($"There has been an error: {e.Message}");
   }
 
}

 

Ahora en el try vamos a desarrollar la lógica de nuestro juego, como se puede ver la dinámica es:

1 pedimos al usuario que introduzca un número entre el 1 y el 10

2 Guardamos ese número en una variable de nombre userNumber

3 validamos que userNumber no venga vacío y si viene vacío lanzamos una excepción con un mensaje de error personalizado

4 validamos que le número introducido se encuentre entre el 1 y el 10 si no lanzamos excepción. Aquí hay que tener en cuenta que todo lo que viene de un readline es un string,  por lo que para comparar el valor de userNumbre con un número (1 o 10) debemos parsear ese string a int ( en rojo)

void Play()
{
   //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
   try
   {
      //1 pedimos al usuario que elija un número del 1 al 10
      Console.WriteLine("Enter a number between 1 and 10");
      //2 Guardamos el número introducido
      var userNumber = Console.ReadLine();
 
      //Validaciones lanzando excepciones
 
      //3 No se ha introducido nada
      if(userNumber == null)
      {
         //Lanzo una excepción que me llevará al catch directamente
         //En las excepciones puedo añadir strings
         throw new Exception("You need to enter a valid number");
 
      }
 
      /* 4 Que el número esté en el rando, recuerda que todo los que viene de
      un readline es un string así que para poder compararlo con un número
      debo parsearlo a int:
      */
      if(int.Parse(userNumber) < 1 || int.Parse(userNumber) > 10)
      {
         throw new Exception("You need to enter a number between 1 and 10");
         Play();
 
      }
 
   }
   catch(Exception e)
   {
      Console.WriteLine($"There has been an error: {e.Message}");
   }
 
}

Si ejecutamos ahora nuestro juego siempre que se produzca alguno de los errores contemplados se lanzan las excepciones y funciona, pero el juego no continúa, ¿cómo podemos hacer que el juego continúe esperando que el usuario introduzca un número correcto? Seguro que lo has adivinado, con recursividad es decir haciendo que el método se llame así mismo después de una excepción en el catch, en azul

Ahora ya nos funciona la parte de validar y no se sale del juego cuando hay un error:

Como puedes ver introduzaco nombre, introduzco un número superior a 10 y me da el error pero me vuelve  pedir el número si introduzco un número válido ya salimos.


Completando la lógica del juego

En este punto ya podemos terminar la lógica de nuestro juego, tenemos 3 posibilidades

·         Que el usuario haya adivinado el número.

·         Que el número del usuario sea mayor que el número random.

·         Que el número del usuario sea menor que el número random.

Si el número del usuario es mayor o menor que random, el usuario tiene que volver a intentarlo tantas veces como sea necesario hasta adivinar el número. Así que ¿qué deberemos modificar en nuestro método Play para logra esto?

Validando las posibles situaciones

try
   {
      //pedimos al usuario que elija un número del 1 al 10
      Console.WriteLine("Enter a number between 1 and 10");
      //Guardamos el número introducido
      var userNumber = Console.ReadLine();
 
      //Validaciones lanzando excepciones
 
      //No se ha introducido nada
      if(userNumber == null)
      {
         //Lanzo una excepción que me llevará al catch directamente
         //En las excepciones puedo añadir strings
         throw new Exception("You need to enter a valid number");
 
      }
 
      /*Que el número esté en el random, recuerda que todo los que viene de
      un readline es un string así que para poder compararlo con un número
      debo parsearlo a int:
      */
      if(int.Parse(userNumber) < 1 || int.Parse(userNumber) > 10)
      {
         throw new Exception("You need to enter a number between 1 and 10");
      }
 
      //VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         Console.WriteLine("Nice! You gessed the number");
      }
      else if(int.Parse(userNumber) < randomNumber) //Si el numero elegido es menor
      {
         Console.WriteLine("Try again! the number is higer");
      }
      else if(int.Parse(userNumber) > randomNumber) //Si el numero elegido es mayor
      {
         Console.WriteLine("Try again! the number is lower");
      }
 
   }
   catch(Exception e)
   {
      Console.WriteLine($"There has been an error: {e.Message}");
      Play();
   }

  •       Como vemos si ha userNumber es igual a randomNumber, el usuario ha adivinado el número y le lanzamos el mensaje de que lo ha adivinado
  •          Si userNumber es menor que randomNumber le damos la pista de que el numero a adivinar es mayor
  •          Si userNumber es mayor que randomNumber le damos la pista de que el numero a adivinar es menor

Si lo dejamos así el juego se va a detener en los casos de que no haya adivinado por esllo debo usar recursividad para reiniciar el método void Play();

      //VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         Console.WriteLine("Nice! You gessed the number");
      }
      else if(int.Parse(userNumber) < randomNumber) //Si el numero elegido es menor
      {
         Console.WriteLine("Try again! the number is higer");
         Play();
      }
      else if(int.Parse(userNumber) > randomNumber) //Si el numero elegido es mayor
      {
         Console.WriteLine("Try again! the number is lower");
         Play();
      }
 

Que no cambie el numero a adivinar cuando el usuario falle

En este punto tenemos un problema, si el usuario falla, estamos reiniciando el método play() por tanto el numero a adivinar randomNumber, va acambiar y la pista que le hemos dado al usuario puede ser errónea ya que es lo primero que hacemos en este método, generar el random:

void Play()
{
   //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);

para solucionar esta situación lo que vamos a hacer es calcular ese número aleatorio fuera del método play(), nos vamos a llevar esas dos líneas de código al método de inicio StartGame:

void StartGame()
{
   
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
 
    //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
    var player = Console.ReadLine();
    WantToPlay(player);
}

Pero al hacer esto la variable randomNumber ya no está disponible para el método Play() ya que ahora esa variable solo vive en el método StartGame(). Aí que necesitamos pasar esa variable como parámetro al método play, al método wantToplay y a todas las llamadas recursivas de estos métodos (en rojo)



Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
   
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
    var player = Console.ReadLine();
    WantToPlay(player, randomNumber);
}
 
void WantToPlay(string? player, int randomNumber)
{
     Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower().Trim())
    {
    case "y":
       Play(randomNumber);
       break;
    case "n":
       DontPlay();
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    WantToPlay(player, randomNumber);
    break;    
    }
   
}
 
void Play(int randomNumber)
{
   
   try
   {
      //pedimos al usuario que elija un número del 1 al 10
      Console.WriteLine("Enter a number between 1 and 10");
      //Guardamos el número introducido
      var userNumber = Console.ReadLine();
 
      //Validaciones lanzando excepciones
 
      //No se ha introducido nada
      if(userNumber == null)
      {
         //Lanzo una excepción que me llevará al catch directamente
         //En las excepciones puedo añadir strings
         throw new Exception("You need to enter a valid number");
 
      }
 
      /*Que el número esté en el random, recuerda que todo los que viene de
      un readline es un string así que para poder compararlo con un número
      debo parsearlo a int:
      */
      if(int.Parse(userNumber) < 1 || int.Parse(userNumber) > 10)
      {
         throw new Exception("You need to enter a number between 1 and 10");
      }
 
      //VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         Console.WriteLine("Nice! You gessed the number");
      }
      else if(int.Parse(userNumber) < randomNumber) //Si el numero elegido es menor
      {
         Console.WriteLine("Try again! the number is higer");
         Play(randomNumber);
      }
      else if(int.Parse(userNumber) > randomNumber) //Si el numero elegido es mayor
      {
         Console.WriteLine("Try again! the number is lower");
         Play(randomNumber);
      }
 
   }
   catch(Exception e)
   {
      Console.WriteLine($"There has been an error: {e.Message}");
      Play(randomNumber);
   }
 
}
 
void DontPlay()
{
  Console.WriteLine("No worries! Have a good one!");
}

Esta es una forma aceptable de hacerlo, pasar la variable como argumento allí donde se necesite, tal vez habría sido más óptimo dar un scope global a la variable randomNumber de forma que estuviera disponible en toda la app, pero lo veremos más adelante.

Si ahora ejecutamos, podremos completar el juego:


Y efectivamente así es

Registrando número de intentos y highscore

Lo primero que necesitaremos es preguntarle al usuario si quiere jugar de nuevo. Ya que en este momento cuando el usuario adivina el número el juego termina. Pero la idea de llevar un listado de resultados es que el juego no termine, sino que vuelva a empezar y que vaya guardando los intentos anteriores, todo lo vamos a hacer en memoria de momento aún no estamos viendo BBDD.

Por tanto, para preguntarle a usuario si quiere jugar de nuevo, lo tenemos que hacer en el bloque condicional de cuando ha adivinado el número.

//VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         Console.WriteLine("Nice! You gessed the number");
      }

 

Bien para hacer esto, vamos a crear un nuevo método debajo del último por ejemplo y lo podemos llamar YouGuessed:

void YouGuessed()
{
   
}

 

Y este método lo ejecutare sólo cuando el usuario adivine el número, así que lo llamo desde el bloque de cuando se ha adivinado el número

if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         YouGuessed();
      }

 

y me llevo el mensaje de adivinaste el número al nuevo método:

void YouGuessed()
{
   Console.WriteLine("Nice! You gessed the number");
}
 

Vamos a programar todo lo que necesitamos en este nuevo método:

1 preguntamos al usuario si quiere jugar de nuevo

void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?")
 

En este punto si el usuario indica que si [Y] tendremos que hacer lo mismo que hacemos en el método WantToPlyay en a qué método usábamos la recursividad ya que hacíamos que el método se llamara a sí mismo:

WantToPlay(player, randomNumber);

Así que en este método YouGuessed vamos a llamar también al método WantToPlay, eliminaremos la pregunta de si quiere jugar ya que WantToPlay ya hace esa pregunta

void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
 
   WantToPlay(player, randomNumber);
 
}


Esto nos está arrojando un error ya que tanto las variables player como randomNumber no existen el el contexto del método YouGuessed, esto lo solucionaremos luego

 

Antes vamos a agregar al método WantToPlay un nuevo parámetro en sus paréntesis, será una bool que podemos llamar playAgain inicializada a false y nos servirá para saber si es la primera vez que el usuario juega o va a jugar una nueva partida. Esta variable de control no espera recibir nada por lo que no debo mandarla al llamar al método, ahora en el método haremos lo siguiente:

Si playAgain es false, se lanza el mensaje de, ¿estas preparado para jugar? (rojo)

Si playAgain fuera true se lanzaría el mensaje, ¿listo para jugar otra vez? (azul)

NOTA: La variable o parámetro playAgain es lo que llamamos parámetro opcional, como el parámetro tiene un valor que le hemos dado al inicializarl, no hace falta que envíe ese parámetro al invocar este método.

Sin embargo si lo necesito si puedo enviar un nuevo valor para ese parámetro como veremos, la primera vez que se ejecuta no le mando un valor sin embargo en el método YouGuessed si le mandaré el valor true prar indicar que no es la primera vez que estoy jugando

void WantToPlay(string? player, int randomNumber, bool playAgain = false)
{
   if(!playAgain)
     Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
     else
     Console.WriteLine($"Hi {player}, ready to play again? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower().Trim())
    {


Ahora volviendo al método YouGuessed le voy a añadir true al parámetro playAgain de este modo sabré que no es la primera vez que juego:

void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
 
   WantToPlay(player, randomNumber, true);
 
}

 

Solo nos queda arrglar el error de las variables player y randomNumber, que no existen en el scope del método YouGuessed. Para solucionar esto haremos globales estas varables:

HACER GLOBAL UNA VARIABLE

¿Como podemos hacer la variable player global? Simplemente vamos a la primera línea del programa y la declaramos con su tipo (no es necesario inicializrla), una vez declarada en el scope global del programa ya no hay que poner var  y está disponible en todo el programa, por lo que tampoco necesitamos pasarla por parámetros al llamar al método WantToPlay

 
string? player; //Declaración global de la variable player
 
Console.WriteLine("\n");
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
   
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
    var player = Console.ReadLine();
    WantToPlay(player, randomNumber);
}

 

Además ya no necesitamos parla por parámetro así que debemos quitar player de todas las llamadas al método WantTo Play. Así el código refactiorizado nos queda:

 

string? player; //Declaración global de la variable player
 
Console.WriteLine("\n");
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
   
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
    //1 Generamos un número aleatorio
   Random random = new Random();
   var randomNumber = random.Next(1, 10);
 
    player = Console.ReadLine();
    WantToPlay(randomNumber);
}
 
void WantToPlay(int randomNumber, bool playAgain = false)
{
   if(!playAgain)
     Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
     else
     Console.WriteLine($"Hi {player}, ready to play again? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower().Trim())
    {
    case "y":
       Play(randomNumber);
       break;
    case "n":
       DontPlay();
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    WantToPlay(randomNumber);
    break;    
    }  
}
 
void Play(int randomNumber)
{
   
   try
   {
      //pedimos al usuario que elija un número del 1 al 10
      Console.WriteLine("Enter a number between 1 and 10");
      //Guardamos el número introducido
      var userNumber = Console.ReadLine();
 
      //Validaciones lanzando excepciones
 
      //No se ha introducido nada
      if(userNumber == null)
      {
         //Lanzo una excepción que me llevará al catch directamente
         //En las excepciones puedo añadir strings
         throw new Exception("You need to enter a valid number");
 
      }
 
      /*Que el número esté en el random, recuerda que todo los que viene de
      un readline es un string así que para poder compararlo con un número
      debo parsearlo a int:
      */
      if(int.Parse(userNumber) < 1 || int.Parse(userNumber) > 10)
      {
         throw new Exception("You need to enter a number between 1 and 10");
      }
 
      //VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         YouGuessed();
      }
      else if(int.Parse(userNumber) < randomNumber) //Si el numero elegido es menor
      {
         Console.WriteLine("Try again! the number is higer");
         Play(randomNumber);
      }
      else if(int.Parse(userNumber) > randomNumber) //Si el numero elegido es mayor
      {
         Console.WriteLine("Try again! the number is lower");
         Play(randomNumber);
      }
 
   }
   catch(Exception e)
   {
      Console.WriteLine($"There has been an error: {e.Message}");
      Play(randomNumber);
   }
 
}
 
void DontPlay()
{
  Console.WriteLine("No worries! Have a good one!");
}
 
void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
 
   WantToPlay(randomNumber, true);
 
}
 

Y aún tenemos el error en el método YouGuessed, de randomNumber ya que esta variable tampoco está disponible en el scoppe del método y si lo piensas tiene sentido ya que al empezar un nuevo juego necesitaré un nuevo número aleatorio así que necesitamos hacer global también randomNumber

string? player; //Declaraciónes globales de las variables player y randomNumber
Random random = new Random();
 

Solo necesitamos copiar  la generación del numero aleatorio en el método StartGame y llevarlo a nuestro ultimo método YouGuessed. El código final queda:

 

string? player; //Declaraciónes globales de las variables player y randomNumber
Random random = new Random();
 
Console.WriteLine("\n");
Console.WriteLine("**************** Guess The Number ****************");
Console.WriteLine("****************        ***       ****************");
Console.WriteLine("****************         *        ****************");
Console.WriteLine("\n");
 
StartGame();
 
void StartGame()
{
   
    Console.WriteLine("Hello! Wellcome to this game...");
    Console.WriteLine("Please enter your name");
 
   var randomNumber = random.Next(1, 10);
 
    player = Console.ReadLine();
    WantToPlay(randomNumber);
}
 
void WantToPlay(int randomNumber, bool playAgain = false)
{
   if(!playAgain)
     Console.WriteLine($"Hi {player}, ready to play? (Enter Y/N)");
     else
     Console.WriteLine($"Hi {player}, ready to play again? (Enter Y/N)");
 
    //Guardo en una variable si el usuario quiere jugar o no
    var wantToPLay = Console.ReadLine();
 
    switch (wantToPLay?.ToLower().Trim())
    {
    case "y":
       Play(randomNumber);
       break;
    case "n":
       DontPlay();
       break;
    default:
    Console.WriteLine("Please enter a valid option y/n");
    WantToPlay(randomNumber);
    break;    
    }  
}
 
void Play(int randomNumber)
{
   
   try
   {
      //pedimos al usuario que elija un número del 1 al 10
      Console.WriteLine("Enter a number between 1 and 10");
      //Guardamos el número introducido
      var userNumber = Console.ReadLine();
 
      //Validaciones lanzando excepciones
 
      //No se ha introducido nada
      if(userNumber == null)
      {
         //Lanzo una excepción que me llevará al catch directamente
         //En las excepciones puedo añadir strings
         throw new Exception("You need to enter a valid number");
 
      }
 
      /*Que el número esté en el random, recuerda que todo los que viene de
      un readline es un string así que para poder compararlo con un número
      debo parsearlo a int:
      */
      if(int.Parse(userNumber) < 1 || int.Parse(userNumber) > 10)
      {
         throw new Exception("You need to enter a number between 1 and 10");
      }
 
      //VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         YouGuessed();
      }
      else if(int.Parse(userNumber) < randomNumber) //Si el numero elegido es menor
      {
         Console.WriteLine("Try again! the number is higer");
         Play(randomNumber);
      }
      else if(int.Parse(userNumber) > randomNumber) //Si el numero elegido es mayor
      {
         Console.WriteLine("Try again! the number is lower");
         Play(randomNumber);
      }
 
   }
   catch(Exception e)
   {
      Console.WriteLine($"There has been an error: {e.Message}");
      Play(randomNumber);
   }
 
}
 
void DontPlay()
{
  Console.WriteLine("No worries! Have a good one!");
}
 
void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
   
   //Genero un nuevo número aleatorio
   var randomNumber = random.Next(1, 10);
   WantToPlay(randomNumber, true);
 
}

 

Veamos qué tenemos si ejecutamos y si podemos seguir jugando

Y como podemos ver al terminar la primera partida puedo volver a jugar, hasta aquí está todo perfecto




Ahora pasaremos a contar el número de intentos para ello vamos a necesitar una variable global para llevar la cuenta, podemos llamarla attemps y la declararemos inicializada a cero

 
string? player; //Declaraciónes globales de las variables player y randomNumber
Random random = new Random();
int attemps = 0;

 

Ahora la forma de contar el número de intentos es muy sencilla, dentro del método Play cada vez que el usuario no haya adivinado el número contará como un intento así que debemos incrementar la variable en los dos casos en los que no ha adivinado el número (es mayor o es menor) y  para poder mostrar el numero de intentos realizados simplemente vamos a la parte de cuando si lo ha adivinado y mostramos el valor dela variable attemps con una interpolación:

Método Play()

      //VALIDANDO OPCIONES
     
      if(int.Parse(userNumber) == randomNumber) //El usuario adivinó el número
      {
         YouGuessed();
         Console.WriteLine($"It has taken you {attempts} attempts to guess the number");
      }
      else if(int.Parse(userNumber) < randomNumber) //Si el numero elegido es menor
      {
         Console.WriteLine("Try again! the number is higer");
         attempts++;
         Play(randomNumber);
      }
      else if(int.Parse(userNumber) > randomNumber) //Si el numero elegido es mayor
      {
         Console.WriteLine("Try again! the number is lower");
         attempts++;
         Play(randomNumber);
      }
 
   }

 Para finalizar opdríamos agregar una variable global más para el cálculo de la punturación más alta, que llamaremos highestScoreAttempts que inicializaremos a cero.


string? player; //Declaraciónes globales de las variables player y randomNumber
Random random = new Random();
int attempts = 0;
int highestScoreAttempts = 0;

 

y lo que básicamente va a hacer esta variable es guardar el score más alto, priemro tenemos que ir al método YoyGuess e incrementar la variable attempts, es decir si adivina directamente también cuenta esa partida.

void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
   attempts++;
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
 
   //Genero un nuevo número aleatorio
   var randomNumber = random.Next(1, 10);
   WantToPlay(randomNumber, true);
 
}

 

Y nos falta validar si lo ha adivinado a la primera o en menor número de intentos que en la partida anterior;

void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
   attempts++;
 
   //Calculamos el highScore
   if(highestScoreAttempts == 0 || attempts < highestScoreAttempts)
   highestScoreAttempts = attempts;
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
 
   //Genero un nuevo número aleatorio
   var randomNumber = random.Next(1, 10);
   WantToPlay(randomNumber, true);
 
}

 

Ahora querremos mostrar el score por pantalla cada vez que el usuario va a empezar el juego, para ello crearemos un método nuevo ShowScore

void ShowScore()
{
   //Caso de que aún no se ha jugado ninguna partida
   if(highestScoreAttempts == 0)
   Console.WriteLine("There is currently no high score");
   //Caso de si hay partidas y puntuación
   else
   Console.WriteLine($"The current high score is {highestScoreAttempts} attempts");
}

 

Y este método lo llamaremos justo antes de iniciar partida o jugar de nuevo (azul). Y por ultimo antes de empezar a jugar de nuevo deberemos limpiar la variable de cuenta attempts (rojo)

void YouGuessed() //Método para cuando el usuario adivina el número
{
   //mensaje de que el usuario acertó
   Console.WriteLine("Nice! You gessed the number");
   attempts++;
 
   //Calculamos el highScore
   if(highestScoreAttempts == 0 || attempts < highestScoreAttempts)
   highestScoreAttempts = attempts;
 
    Console.WriteLine($"It has taken you {attempts} attempts to guess the number");
    ShowScore();
    attempts = 0;
 
   //preguntamos si quiere jugar de nuevo
   Console.WriteLine("Do you want to play again?");
 
   //Genero un nuevo número aleatorio
   var randomNumber = random.Next(1, 10);
   WantToPlay(randomNumber, true);
}

 

Probemos el juego

Primera partida



Me cuesta 7 intentos adivinar el número por lo que mi highscore es 7

Segunda partida


Me cuesta 5 intentos por lo que mi highscore cambia a 5. Todo está funcionando perfectamente.

No hay comentarios:

Publicar un comentario