jueves, 23 de marzo de 2023

Curso C# 2 (Proyecto 1 piedra papel y tijera)

 


Proyecto 1 piedra papel y tijera

Vamos a crear una primera aplicación de consola con los siguientes requisitos:

·         El usuario podrá elegir una única opción (piedra, papel o tijera).

·         La opción del oponente debe ser aleatoria.

·         El usuario debe recibir un mensaje indicando el resultado.

·         El usuario debe ser preguntado si quiere jugar de nuevo o no.

Vamos a empezar por crear un nuevo proyecto en la carpeta Projects que tenemos en el escritorio


Lo abrimos en VS code

Y voy a cambiar el mensaje a consola por piedra papel y tijera

Console.WriteLine("Piedra, Papel, Tijera");

 

Lo que queremos es que el juego dure mientras el usuario no indique lo contrario. Por ello vamos a usar un bucle while, con su condición a true de forma que el juego sea un bucle infinito mientras no se indique lo contrario.

Como se trata de una sencilla app de consola sin interfaz gráfica vamos a ir mandando mensajes por consola para que el usuario entienda por dónde va el juego:

 

Console.WriteLine("Piedra, Papel, Tijera");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
}

Lo primero que tenemos que hacer es dar a elegir al usuario qué opción va a querer seleccionar, piedra, papel o tijera. Para ello como estamos en consola usaremos teclas para elegir las opciones:

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Opciones
    Console.WriteLine("Choose R, P or S: [R]ock, [P]aper or [S]cissors: ");
}

 Si ejecutamos ahora al ser un bucle infinito estaría imprimiendo constantemente los mensajes y eso no es lo que queremos, vamos a pensar cómo parar el bucle y capturarla opción elegida por el usuario. Para ello tenemos la opción Console.ReadLine(), al escribir este método la ejecución se va a detener hasta que el usuario haga su elección o realice la acción que le indiquemos.

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Opciones
    Console.WriteLine("Choose R, P or S: [R]ock, [P]aper or [S]cissors: ");
    Console.ReadLine();
}

Vamos a ver si es cierto que el bucle se detiene a la espera de que el usuario escriba, abrimos nuestra terminal y lanzamos el comando dotnet run


Podemos ver como se muestran nuestros mensajes y el bucle se detiene al llegar al readLine

 

Para saber lo que el usuario ha escrito, lo siguiente será guardar el ReadLine en una variable que podemos llamar selecedChoice y validar que el usuario hay escogido alguna delas 3 opciones permitidas es decir R, P o S.

NOTA: El método ReadLine devuelve un string. Así que para la comparación debo comparar contra un string “comillas dobles”, si pongo un char ‘comillas simples me devolverá error’-

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Opciones
    Console.WriteLine("Choose R, P or S: [R]ock, [P]aper or [S]cissors: ");
   
    //Leemos la respuesta del usuario y lo guardamos en una variable
    var selectedChoice = Console.ReadLine();
 
    /*Validamos que la respuesta sea una de las 3 letras permitidas
      con un operador ternario, además hacemos todas las respuestas
      Minúsculas
    */
   
    if(selectedChoice?.ToLower() != "r" &&
       selectedChoice?.ToLower() != "p" &&
       selectedChoice?.ToLower() != "s")
       {
          Console.WriteLine("Please, select only one letter; R, P or S");
       }
}

 Hasta aquí tenemos hecha la validación, ahora debemos volver a darle al usuario la opción de que vuelva a elegir algo pero esta vez vamos a mover la validación aun método, ya que la estructura de controlo anterior no nos sirve no podemos estar haciendo if else constantemente hasta que el usuario introduzca un respuesta válida.

MÉTODO

Estamos en C# así que a nuestro método debemos darle un tipado, en nuestro caso va a devolver un string por tanto  crearemos nuestro método y dentro de él pondremos nuestra estructura de control:

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
   
}
 
string SelectChoice()
{
   //Opciones
    Console.WriteLine("Choose R, P or S: [R]ock, [P]aper or [S]cissors: ");
   
    //Leemos la respuesta del usuario y lo guardamos en una variable
    var selectedChoice = Console.ReadLine();
   
    if(selectedChoice?.ToLower() != "r" &&
       selectedChoice?.ToLower() != "p" &&
       selectedChoice?.ToLower() != "s")
       {
          Console.WriteLine("Please, select only one letter; R, P or S");
       }
       return selectedChoice;
}

 RECURSIVIDAD

Por tanto, si llamamos a este método se hará la validación y ahora en caso de que el usuario introduzca una respuesta errónea usaremos una característica muy útil de los métodos y funciones que es la recursividad, es decir que el método se llame a sí mismo:

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡"); 
}
 
string SelectChoice()
{
   //Opciones
    Console.WriteLine("Choose R, P or S: [R]ock, [P]aper or [S]cissors: ");
   
    //Leemos la respuesta del usuario y lo guardamos en una variable
    var selectedChoice = Console.ReadLine();
 
    /*Validamos que la respuesta sea una de las 3 letras permitidas
      con un operador ternario, además hacemos todas las respuestas
      Minúsculas
    */
   
    if(selectedChoice?.ToLower() != "r" &&
       selectedChoice?.ToLower() != "p" &&
       selectedChoice?.ToLower() != "s")
       {
         //Si la respuesta es erróna mandamos mensaje y volvemos a
         //LLamar a este mismo método desde sí mismo RECURSIVIDAD
          Console.WriteLine("Please, select only one letter; R, P or S");
          selectedChoice = SelectChoice();
       }
       return selectedChoice;
}

 

Ahora todas las veces que el usuario responda mal el método se llamará a sí mismo, y solo saldrá de ese bucle si el usuario responde con alguna de las opciones correctas, saliendo por el return, es deci devolviendo la opción escogida.

Pero falta un detalle importante, debemos llamara a nuestro método para que se ejecute, y debemos llamarlo dentro de nuestro bucle while ()

 

Método Parse y Concatenación

Sabemos que nuestro método SelectedChoice nos devuelve un String, pero como es una sola letra, una cosa que podemos hacer es convertir esa respuesta de string a char y así presentamos el método parse. Así que una vez que tenemos la respuesta la convertimos a char y por último mostramos la respuesta en la salida del programa, concatenando texto con la variable, para concatenar anteponemos el símbolo $ y de este modo entre llaves nos deja añadir la variable (template strings) (rojo):

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Llamamos a nuestro método SelectChoice
    var selectedChoice = SelectChoice();
 
    //Convertimos la respuesta a char con el método Parse
    var yourChoice = char.Parse(selectedChoice);
 
    //Mostramos por salida la respuesta elegida
    Console.WriteLine($"You selected {yourChoice}");
 
   
}
 
string SelectChoice()
{
   //Opciones
    Console.WriteLine("Choose R, P or S: [R]ock, [P]aper or [S]cissors: ");
   
    //Leemos la respuesta del usuario y lo guardamos en una variable
    var selectedChoice = Console.ReadLine();
 
    /*Validamos que la respuesta sea una de las 3 letras permitidas
      con un operador ternario, además hacemos todas las respuestas
      Minúsculas
    */
   
    if(selectedChoice?.ToLower() != "r" &&
       selectedChoice?.ToLower() != "p" &&
       selectedChoice?.ToLower() != "s")
       {
         //Si la respuesta es erróna mandamos mensaje y volvemos a
         //LLamar a este mismo método desde sí mismo RECURSIVIDAD
          Console.WriteLine("Please, select only one letter; R, P or S");
          selectedChoice = SelectChoice();
       }
       return selectedChoice;

Podemos ejecutar desde la terminal de VS o desde el símbolo de sistema


AGREGANDO LA OPCIÓN DEL OPONENTE

Vamos ahora a añadir la opción del oponente, para ello vamos a crear un nuevo método. Esta vez en vez de trabajar con el tipo string vamos a trabajar directamente con el tipo char.

char GetOpponentChoice()
{
   
}

 

Lo que queremos hacer es elegir un número aleatorio, para ello crearemos un Array de tipo char con las opciones posibles, mira como declaramos un array con su tipo y como lo inicializamos con sus valores:

char GetOpponentChoice()
{
  char[] options  = new char[] { 'R', 'P', 'S' };
}

 

Lo que queremos es elegir de forma aleatoria una de estas 3 opciones, para ello usaremos el método random.

char GetOpponentChoice()
{
  char[] options  = new char[] { 'R', 'P', 'S' };
 
  Random random = new Random();
}
 

Ahora vamos a trabajar con el índice del Array usando el método random que nos elegirá aleatoriamente una posición del Array para guardarlo como respuesta aleatoria y lo guardamos en una variable que podemos llamar como queramos en este caso la he llamado randomIndex:

char GetOpponentChoice()
{
  //Creamos e inicailizamos una arry char con las opciones
  char[] options  = new char[] { 'R', 'P', 'S' };
  //Creamos un objeto Random
  Random random = new Random();
  //Escogemos de modo alatorio una opción del array
  int randomIndex = random.Next(0, options.Length);
}

 

Observa que a la variaboe randomIndex le hemos dado el tipo integer ya que lo que lo que va a guardar es un numero entero que se corresponderá con alguna posición del array (0, 1, 2).

Sólo nos queda devolver la respuesta aleatoria que como puedes ver es lo que contiene el Array en la posición indicada de forma aleatoria:

char GetOpponentChoice()
{
  //Creamos e inicailizamos una arry char con las opciones
  char[] options  = new char[] { 'R', 'P', 'S' };
  //Creamos un objeto Random
  Random random = new Random();
  //Escogemos de modo alatorio una opción del array
  int randomIndex = random.Next(0, options.Length);
  //Retornamos la repuesta que será lo que contenga el array options
  //en la posición que le indicamos en la variable randomIndex
  return options[randomIndex];
}

 

Ahora hay que llamar a nuestro nuevo método en el bucle While que es donde tenemos la secuencia principal de nuestro juego:

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Llamamos a nuestro método SelectChoice
    var selectedChoice = SelectChoice();
    var yourChoice = char.Parse(selectedChoice);
   
   //Mostramos por salida la respuesta elegida
    Console.WriteLine($"You selected {yourChoice}");
 
 
 
 
   /*Llamamos al método char GetOpponentChoice() que es el que
   nos devuelve la respuesta aleatoria de nuestro oponente y
   guardamos esa respuesta en una variable
   */  
   var opponentChoice = GetOpponentChoice();
}

 

Vamos a ejecutarlo para ver que tenemos hasta ahora esta vez desde la consola de VSCode.

Como puedes ver yo elijo r y el PC elije P, no tenemos nada mas codificado por lo que el bucle vuelve a empezar, por tanto de momento vamos bien.



RESOVIENDO EL GANADOR DE LA PARTIDA

Primero resolveremos el caso de que sea un empate, que es cuando los dos escogemos la misma opción, por supuesto todo esto lo programamos dentro de nuestro while que es el flujo principal de nuestro juego:

Console.WriteLine("Rock Paper Scissors");
   if(opponentChoice == yourChoice)
   {
      Console.Write("Tie¡");
   }
 
}

 

Ahora debemos pensar cómo vamos a solucionar quien gana en caso de que no sea un empate, podríamos hacerlo con estructuras de contro if else en el while, pero para hacerlo más limpio lo vamos a hacer en un nuevo método en este caso un método de tipo void (que no devuelve nada).

Obsrva que al método le tenemos que pasar como parámetros las variables de las elecciones de cada contrincante, opponentChoice y yourChoice, separadas con una coma e indicando su tipo en cada una (rojo)

void  DecideWinner(char opponentChoice, char yourChoice)
{
  //CASO EMPATE
   if(opponentChoice == yourChoice) Console.Write("Tie¡");
   
}

 

Y por supuesto hay que llamar al método desde el while

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Llamamos a nuestro método SelectChoice
    var selectedChoice = SelectChoice();
    var yourChoice = char.Parse(selectedChoice);
   
   //Mostramos por salida la respuesta elegida
    Console.WriteLine($"You selected {yourChoice}");
 
   /*Llamamos al método char GetOpponentChoice() que es el que
   nos devuelve la respuesta aleatoria de nuestro oponente y
   guardamos esa respuesta en una variable para mostrar la
   respuesta de nuestro oponente virtul
   */  
   var opponentChoice = GetOpponentChoice();
   Console.WriteLine($"I chose {opponentChoice}");
 
   //Llamada al método para ver el ganador o el empate
   DecideWinner(opponentChoice, yourChoice);
 
}
 

Ya tenemos el método que analiza si hay empate, ahora debemos pensar como resolver quien es el ganador y lo vamos a hacer con un Switch, analizando la opción del usuario contra la opción del sistema

switch (yourChoice)
   {  
      //El usuario elige roca
      case 'R':
      case 'r': //Esto no hace falta ya que la hemos pasado a MAUSCULAS ES SOL PARA VER QUE ESTO SE PUEDE HACER
        if(opponentChoice == 'P') //Si el pc elige Papel gana el pc
         Console.WriteLine("Paper beats rock, I win¡");
        else if(opponentChoice == 'S') //Si el pc elige Tijeras gan el usuario
        Console.WriteLine("Rock beats Scissors, you win¡");
        break;
      //El usuario elige roca
      case 'S':
      case 's':
        if(opponentChoice == 'P') //Si el pc elige Papel gana el pc
         Console.WriteLine("Scissord beats paper, You win¡");
        else if(opponentChoice == 'R') //Si el pc elige Tijeras gan el usuario
        Console.WriteLine("Rock beats Scissors, I win¡");
        break;
      //El usuario elige roca
      case 'P':
      case 'p':
        if(opponentChoice == 'S') //Si el pc elige Papel gana el pc
         Console.WriteLine("Scissord beats paper, I win¡");
        else if(opponentChoice == 'R') //Si el pc elige Tijeras gan el usuario
        Console.WriteLine("Paper beats Rock, You win¡");
        break;  
   }

 

Vamos a ejecutar nuestro juego esta vez usando la consola PowerShell,

1.       Elijo R y el sistema elije R hay empate

2.       Elijo P y el sistema también, hay empate

3.       Elijo s y el sistema elije P, gano yo ya que las tijeras ganan al papel.



Al final el juego se reanuda ya que estamos dentro de un while hecho true, así que si no hacemos nada el juego se reanudará eternamente vamos a dar la opción al usuario de seguir o salir del juego.

En nuestro While añadimos (en rojo)

Console.WriteLine("Rock Paper Scissors");
while (true)
{
    Console.WriteLine("Are you ready?");
    Console.WriteLine("Let´s begin¡¡¡");
 
    //Llamamos a nuestro método SelectChoice
    var selectedChoice = SelectChoice();
    var yourChoice = char.Parse(selectedChoice);
   
   //Mostramos por salida la respuesta elegida
    Console.WriteLine($"You selected {yourChoice}");
 
   /*Llamamos al método char GetOpponentChoice() que es el que
   nos devuelve la respuesta aleatoria de nuestro oponente y
   guardamos esa respuesta en una variable para mostrar la
   respuesta de nuestro oponente virtul
   */  
   var opponentChoice = GetOpponentChoice();
   Console.WriteLine($"I chose {opponentChoice}");
 
   //Llamada al método para ver el ganador o el empate
   DecideWinner(opponentChoice, yourChoice);
 
   //Le preguntamos al usuario si quiere juegar de nuevo
   //Guardamos su respuesta e una variable
   Console.WriteLine("Do you want to play again?");
   Console.WriteLine("Enter[Y] yo play again or any other key to sto...");
 
   //Capturo la respuesta del usuario
   var playAgain = Console.ReadLine();
   //Valido i la respuesta es y continuo en el while
   if(playAgain?.ToLower() == "y")
      continue;
   else
      break;
 
}
 

Si ahora ejecuto: