Nuestro siguiente juego es el famoso Quiz.
Se trata de un juego en el que
vamos a tener una serie de preguntas con opciones y el usuario tendrá que
elegir cuál cree que es la correcta.
Reglas:
·
Las preguntas tendrán 4 opciones como posibles
respuestas donde sólo una es correcta.
·
Cada vez que el juego termina tendremos que
mostrar en consola el nombre del usuario y los aciertos.
·
Al terminar el juego debe poder reiniciarse,
otro usuario puede iniciar una partida y el programa debe mantener los scores
de cada usuario y sus nombres.
Este programa debe estar
programado bajo el paradigma de orientación a objetos POO, es decir TODO lo que
puede ser tratado como una entidad tienen que tener una clase y debemos
trabajar con instancias de esas clases.
Para hacer este ejercicio un poco
más sencillo las preguntas no serán dinámicas serán hardcodeadas, más adelante
ya usaremos bases de datos.
Vamos a crear un nuevo proyecto
de consola que llamaremos QuizGame, con la consola como ya sabemos hacer
Esta vez creamos el proyecto ya
con el IDE Visual Studio instalado. Una vez hemos creado un nuevo proyecto
conel nombre de nuestro proyecto hacemos click derecho sobre él para crear una
clase
Y podemos llamara a la clase
Question
Al hacer esto se nos crea nuestra
nueva clase junto a todas la librerías necesarias, estamos trabajando ya en
Visual Studio, que no es lo mismo que Visual Studio Code.
Creamos una entidada para las
preguntas, es decir cada una de la preguntas va a tener una clase, que luego me
va a permitir instanciar cada pregunta individualmente.
El primer cambio que vamos a
ahacer es hacer nuestra clase pública y dentro de esta primera clase vamos a
tener 3 propiedades
public int Id { get; set; } esta
propiedad int Id dispone de un get y un ser para obtener y setear el valor de
int.
namespace QuizGame
public class Question
{
public int Id { get; set; }
}
Además digimos que cada pregunta tendrá 4 posibles opciones de
respuesta, por lo que para esa entidad vamos a crear otra clase a la que vamos
a llamar Option
using System;
namespace QuizGame
internal class Option
{
//Id
de las respuestas
public int Id { get; set; }
public string Text { get; set; }
public bool IsValid { get; set; }
}
Una vez creada la clase options vamos a volver a la clase Question
para agregar una nueva propiedad que va a se una colección de de Opciones con
la palabra reservada List
List<Option>
Representa una lista
fuertemente tipada de objetos a los que se puede acceder por índice.
Proporciona métodos para buscar, ordenar y manipular listas.
namespace QuizGame
public class Question
{
public int Id { get; set; }
}
}
Es decir,
esta es la lista donde voy a tener todas las opciones de esta pregunta.
Vamos a crear una clase más que vamos a necesitar en el futuro
que se llamará Answer
Podemos ver como nuestro proyecto ya tiene el archivo
principal y las clases que hemos ido creando, podemos verlas en el menú lateral
derecho y en las pestañas de navegación:
Pero vayamos a nuestra ultima clase Answer que también será
pública y donde tendremos dos propiedades:
namespace QuizGame
{
public class Answer
{
/*
* Propiedad donde almacenaremos el Id
de la pregunta
* a la cuál es usuario está
respondiendo
*/
public int QuestionId { get; set; }
//La
opción que el usuario eligió
public Option SelectedOption { get; set; }
}
Entonces en mi entidad Answer voy a tener un Id de la pregunta
y voy a tener el objeto de la respuesta.
Bien en este puto tenemos ya
todas las clases que vamos a necesitar, todos nuestros objetos. Lo que vamos a
hacer ahora es crear la preguntas, para ello volveremos a la pestaña
Program.cs, como dijimos las preguntas de nuestro Quiz van a estar
Hardcodeadas, mas adelante ya entraremos en programas más dinámicos.
Vamos a crear una variable global
que llamaremos questions y la vamos a inicializar a una nueva lista.
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Questions>
Lo primero que pasa es que no me reconoce la clase questions,esto
puede pasar con algunas ibrerías esto significa que debemos importar el using,
si vamos a la lupa se nos sugerirá cual es la solución
Vemos que de entre algunas otras
soluciones nos indica que agregemos el using
NOTA: La directiva using crea un alias para un espacio de
nombres “namespace” o importa tipos definidos en otros espacio s de nombres.
le damos a agregar y ahora ya
tenemos acceso a nuestra clase questions.
using QuizGame;
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Question>();
Vamos a crear un método de tiopo void que será un seed de
perguntas, es decir vamos a crear en este método las preguntas que luego va
aparecer opr pantalla.
Para ello invocamos nuestra clase Questions y le vamos dando los
atributos que espera, que son el Id, eltexto de la pregunta y las opciones.
Para ir añadiendo estas propiedades uaremos el método Add de las listas:
using QuizGame;
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Question>();
//Creo un método de tipo void
que será un seed de tipo questions
void SeddQuestionsAndOptions()
{
questions.Add(new Question
{
Id = 1,
QuestionText = "What is the biggest contry on earth?",
//Ahora
debo añadir las opciones pero recuerda que la opciones en la clase Question es
tambien una lista
Options = new List<Option>()
//Otra
opción de añadir items a una lista además de Add, es abrir llaves y separar
//Las
opciones con comas ya que se la lista se está comportando así como un Array
{
new Option { Id = 1, Text = "Australia"},
new Option { Id = 2, Text = "China"},
new Option { Id = 3, Text = "Canada"},
//Y
recuerda que la clase Options tenemos la opción isValid para decidir cual es la
correcta
new Option { Id = 4, Text = "Russia", IsValid = true },
}
});
}
Repasemos que acabamos de hacer:
1.
Primero creé una lista de
nombre Questions
2.
Usando el método Add agregé el
primer item que es un item de tipo Question (clase Question)
3.
Ahora debo dar valores a las
propiedades de Question (definidas en la clase Question, Id, Text Options
<Lista>) Abro llaves y dentro de esas llaves separadas por comas puedo
agregar las opciones id, Text, options donde options es también una lista, por
lo que tengo que inicializarla también como la lista Optons. Y dentro de
options repito lo que he hecho con questions le doy los valores a los atributos
que en la clase options son Id, Text, y
IsValid para la opción que sea correcta (sólo una puede serlo). Solo
necesito poner la opción isValid = true en la opción correcta ya que por
defecto todos los valores booleanos están inicializados a flase.
Parece un poco complejo, pero
nos iremos acostumbrando ya que esta es una delas maneras que tenemos para
inicializar objetos complejos como éste.
Agregando más ítems a questions (más preguntas con sus opciones)
Repetimos el proceso anterior, para agregar tantas preguntas a
nuestro Quiz como queramos
using QuizGame;
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Question>();
//Creo un método de tipo void
que será un seed de tipo questions
void SeddQuestionsAndOptions()
{
questions.Add(new Question
{
Id = 1,
QuestionText = "Which is the biggest contry on earth?",
//Ahora
debo añadir las opciones pero recuerda que la opciones en la clase Question es
tambien una lista
Options = new List<Option>()
//Otra
opción de añadir items a una lista además de Add, es abrir llaves y separar
//Las
opciones con comas ya que se la lista se está comportando así como un Array
{
new Option { Id = 1, Text = "Australia"},
new Option { Id = 2, Text = "China"},
new Option { Id = 3, Text = "Canada"},
//Y
recuerda que la clase Options tenemos la opción isValid para decidir cual es la
correcta
new Option { Id = 4, Text = "Russia", IsValid = true }
}
});
//Nueva
question (repetimos el proceos para añadir las perguntas que queramos)
questions.Add(new Question
{
Id = 2,
QuestionText = "Which is the country
with the greatest population?",
Options = new List<Option>()
{
new Option { Id = 1, Text = "India"},
new Option { Id = 2, Text = "China", IsValid = true },
new Option { Id = 3, Text = "United
States"},
new Option { Id = 4, Text ="Indonesia"}
}
});
}
Y así podemos agregar tantas preguntas como queramos , voy a copiar y a pegar para añadir una última pregunta:
using QuizGame;
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Question>();
//Invocamos el métod de las preguntas
SeddQuestionsAndOptions();
//Creo un método de tipo void
que será un seed de tipo questions
void SeddQuestionsAndOptions()
{
questions.Add(new Question
{
Id = 1,
QuestionText = "Which is the biggest contry on earth?",
//Ahora
debo añadir las opciones pero recuerda que la opciones en la clase Question es
tambien una lista
Options = new List<Option>()
//Otra
opción de añadir items a una lista además de Add, es abrir llaves y separar
//Las
opciones con comas ya que se la lista se está comportando así como un Array
{
new Option { Id = 1, Text = "Australia"},
new Option { Id = 2, Text = "China"},
new Option { Id = 3, Text = "Canada"},
//Y
recuerda que la clase Options tenemos la opción isValid para decidir cual es la
correcta
new Option { Id = 4, Text = "Russia", IsValid = true }
}
});
//Nueva
question (repetimos el proceos para añadir las perguntas que queramos)
questions.Add(new Question
{
Id = 2,
QuestionText = "Which is the country
with the greatest population?",
Options = new List<Option>()
{
new Option { Id = 1, Text = "India"},
new Option { Id = 2, Text = "China", IsValid = true },
new Option { Id = 3, Text = "United
States"},
new Option { Id = 4, Text ="Indonesia"}
}
});
//Nueva
question
questions.Add(new Question
{
Id = 3,
QuestionText = "Which was the less corrupt country in the world in
2021?",
Options = new List<Option>()
{
new Option { Id = 1, Text = "Finland"},
new Option { Id = 2, Text = "new
Zealand"},
new Option { Id = 3, Text = "Denmark" , IsValid = true },
new Option { Id = 4, Text ="Norway"}
}
});
//Nueva
question
questions.Add(new Question
{
Id = 4,
QuestionText = "Which was the best country for living in 2021",
Options = new List<Option>()
{
new Option { Id = 1, Text = "Norway" , IsValid = true },
new Option { Id = 2, Text = "Belgium"},
new Option { Id = 3, Text = "Sweden"},
new Option { Id = 4, Text ="Spain"}
}
});
}
Con esto tenemos tenemos la 4 preguntas de nuestro Quiz listas. Así que lo que tenemos que hacer ahora es directamente invocar el método SeedQuestionsAndOption, ya lo hemos hecho lo puedes ver resaltado en amarillo en el código completo anterior.
Perfecto, una vez hecho lo anterior, minimizamos el método
SeedQuestionsAndOptions, para ver todo un poco más organizado
Y vamos a crear un nuevo método
también aquí en Program.cs que será el
método de inicialización del juego y al que vamos a llamar StartGame()
using QuizGame;
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Question>();
//Invocamos el métod de las
preguntas
SeedQuestionsAndOptions();
//Métod inicializador del
juego
void StartGame()
{
}
//Creo un método de tipo void
que será un seed de tipo questions
void SeedQuestionsAndOptions()
Pensemos en qué cosas vamos a
necesitar hacer en nuestro método principal, son cosas qiue ya hemos hecho en
ejercicios anteriores:
- Presentar el juego al usuario
- Mantener el nombre del usuario
- Mostrar el nombre del usuario por consola antes de empezar el juego
- tenemos que crear una estructura de control para recorrer la lista de preguntas que acabamos de crear y mostrar por pantalla el texto de cada pregunta, el texto de cada una de las opciones de respuestas
- Tenemos que dejar el programa a la espera de la respuesta del usuario para cada pregunta
Vamos a nuestro método de inicio
a hacer todo lo anterior. Empezaremos con un Console.writeLine (leer
comentarios en verde)
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el ombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
}
Para
crear esa estructura de control tenemos varias posibilidades, pero las dos más
directas serían el for y el foreach. En este caso vamos a usar el foreach
porque para recorrer listas que tienen instancias de clases como en este caso,
el foreach nos resultará mucho más cómodo.
foreach (var item in questions)
{
}
Vamos a recorrer la lista qestions tenemos la variable item
donde se irá almacenando cada item de la lista en cada iteración, la iteración
no termina mientras la lista tenga ítems.
Lo primero que haremos será imprimir la pregunta,m que como
sabemos es la propiedad questionText:
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
}
Una vez aparece la pregunta le indico al usario lo que tiene
que hacer:
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
}
}
Y a continuación debo imprimir las opciones de respuesta,
como las opciones también son una lista usaremos otro foreach para la lista
options (anidado). Recuerda options es una propiedad de cada item que
recorremos, pero no podemos llamar ambien item al segundo bucle foreach lo
vamos a cambiar por option como item de options, que al final es la lista que estamos
recorriendo
//Imprimimos las opciones de
respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var item in item.options)
{
}
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option
in
item.options)
{
}
}
Y ahora si imprimimos e número de la opción, para que el
usuario sepa que opción elegir y la opción en sí misma usaremos interpolación
de string
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
}
}
Vamos a ejecutar lo que tenemos hasta ahora para ver si
funciona.
Invoco el método StartGame debajo de SeedQuestionsAndOptions
y le damos a ejecutar, play verde recuerda que estamos en Visual Studio
Me pide el nombre
Se lo doy me muestra los mensajes y me imprime todas las
preguntas luego termina la ejecución:
Luego hasta aquí todo está funcionando perfectamente
Necesitamos ahora capturar la opción que el usuario elija y validar que su respuesta sea válida, es decir un número entre el 1 y el 4 y tenemos que hacer todo esto sin detener la aplicación.
Para resolver lo que debemos hacer vamos a usar de nuevo la
recursividad, primero vamos a crear un nuevo método que podemos llamar
GetSelectedAnswer. Este método me va a devolver un string con el valor 1, 2,3 o
4 y el método se repetir tantas veces como haga falta hasta que el usuario responda.
string GetSelectedAnswer()
//Método
que devuelve un string con la respuesta 1,2,3,4
{
var answer = Console.ReadLine();
//Validamos
que la respuesta no venga vacía y que sea una de las posibles opciones
if(answer != null && (answer == "1") || (answer == "2") || (answer == "3") || (answer == "4"))
{
return answer;
}
else
{
Console.WriteLine("Please select a valid option");
}
}
Pero si el usuario no ofrece una respuesta válida, además de
lanzar el mensaje de error debemos darle de nuevo la oportunidad de contestar y
es aquí donde otra vez hacemos uso de la recursividad, haciendo que el método
se llame a sí mismo, y no olvides retornar la respuesta fuera del bloque else:
Esto se podría haber resuelto con un try catch.
string GetSelectedAnswer()
//Método
que devuelve un string con la respuesta 1,2,3,4
{
var answer = Console.ReadLine();
//Validamos
que la respuesta no venga vacía y que sea una de las posibles opciones
if(answer != null && (answer == "1") || (answer == "2") || (answer == "3") || (answer == "4"))
{
return answer;
}
else
{
Console.WriteLine("Please select a valid option");
answer = GetSelectedAnswer();
}
return answer;
}
Una vez que tenemos creado nuestro método debemos usarlo en
nuestro método principal StartGame, llamándolo justo después de mostrar las
preguntas y las opciones de respuesta y guardándolo en una variable podemos
llamar también answer:
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el nombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.Options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
var answer = GetSelectedAnswer();
}
}
En este punto, se han desplegado
las preguntas del Quiz y se ha guardado la respuesta a cada opción que ha va
dando el usuario, pero ¿qué vamos a hacer con cada respuesta? Necesitamos crear
un histórico de cada repuesta , para ello necesitaremos crear una lista de
ámbito global, del mismo modo que tenemos la lista de preguntas, vamos a tener
una lista de respuestas que iremos rellenando con las respuestas del usuario.
using QuizGame;
Console.WriteLine("Quiz Game...");
// creo una variable global
que guardará las preguntas en una lista
var questions = new List<Question>();
//Lista global para ir
guardando las respuestas
var answers = new List<Answer>();
Lo que nos interesa ahora es crear otro método que se
encargue de ir agregando las respuestas del usuario a la lista que acabamos de
crear, lo podemos crear debajo del método GetSelectedAnswer y éste método va a
recibir como parámetro la respuesta seleccionada que tenemos guardada en la
variable answer del método principal y
también recibirá por parámetro el objeto Question, veamos cómo:
//Método que se guarda las
respuestas del usuario en la lista answers
void AddAnswersToList(string answer, Question question)
{
}
Antes de crearlo vamos a
invocarlo, lo invocaremos justo en el momento en le que el usuario de una
respuesta válida a la pregunta, esta se recibe en el método principal StartGame
(que es donde se invoca le método GetSelectedAnswer) y en ese
momento guardamos la respuesta invocando nuestro nuevo método AddAnswerToList,
así que vamos a StartGame y hacemos:
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el nombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.Options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
var answer = GetSelectedAnswer();
AddAnswersToList(answer, item);
}
}
Recuerda que le método espera dos parámetros, la respuesta y
el objeto Question,
que es item (la
pregunta en iteración) en este método.
Volvamos ahora a completar nuestro método AddAnswerToList,
Lo que debo hacer es leer el string que me llega como parámetro (la respuesta),
recordemos que la lista answers es de la clase Answer:
//Lista global para ir
guardando las respuestas
var answers = new List<Answer>();
y la clase Answer tiene las propiedades:
·
QuestionId () que ya lo tengo por que ya
recibo el objeto Question como parámetro
·
SelectedOption: tengo que ver como hago
para obtener todo el objeto option, sólo basándo me en el string que recibo
también por parámetro (la respuesta)
namespace QuizGame
{
public class Answer
{
public int QuestionId { get; set; }
//La
opción que el usuario eligió
public Option SelectedOption { get; set; }
}
}
Entonces nuestro método AddAnswerToList,
recibimos los parámetros, y vamos añadirlos a la lista answers
·
QuestionId ya lo tenemos ya que tenemos
acceso al objeto question, solo debemos sacar su id (amarillo)
·
SelectedOption , ¿cómo accedo al objeto option ¿
TENDRÉ QUE CREAR UN NUEVO
MÉTODO
void AddAnswersToList(string answer, Question question)
{
//Añadir
el Id de la pregunta y la respuesta a la lista answers del tipo Answer
answers.Add(new Answer
{
QuestionId = question.Id,
SelectedOption = crear un nuevo método
});
}
para guardar la respuesta escogida debemos comparar el
string de la respuesta que nos llega como parámetro, con la respuesta que
coincide con ese número. Por eso creamos este nuevo método que llamamremos GetSelectedOption()
y que además será un método del tipo Option:
lo creo justo debajo
//Método que se guarda las
respuestas del usuario en la lista answers
void AddAnswersToList(string answer, Question question)
{
//Añadir
el Id de la pregunta y la respuesta a la lista answers del tipo Answer
answers.Add(new Answer
{
QuestionId = question.Id,
SelectedOption =
});
}
Option GetSelectedOption(string answer, Question question)
{
}
Por supuesto este método también necesita recibir la
respuesta y la pregunta, lo que tengo que hacer es recorrer todas las opciones
que recibo en el objeto question y comparar el Id de cada opción con el id que
me viene en answer:
Option GetSelectedOption(string answer, Question question)
{
var selectedOption = new Option();
//Recorrer
todas las opciones de question
return selectedOption;
}
La solución es recorrer con un
foreach todas las opciones de las repuestas y cuando el Id de la opción se
corresponda con el la respuesta del usuario (que parseamos ya que viene como
string) esa es la respuesta opción que ha seleccionado el usuario y la
retornamos:
Option GetSelectedOption(string answer, Question question)
{
var selectedOption = new Option();
//Recorrer
todas las opciones de question
foreach (var item in question.Options)
{
if (item.Id == int.Parse(answer))
selectedOption = item;
}
return selectedOption;
}
Con el método terminado, solo nos queda invocarlo en e
método anterior AddAnswerToList:
//Método que se guarda las
respuestas del usuario en la lista answers
void AddAnswersToList(string answer, Question question)
{
//Añadir
el Id de la pregunta y la respuesta a la lista answers del tipo Answer
answers.Add(new Answer
{
QuestionId = question.Id,
SelectedOption = GetSelectedOption (answer,
question)
}); ;
}
En este momento ya tendríamo las respuestas del usuario
almacenadas en la lista answersdel tipo Answers, esto nos va a servir para el
Scoring, que era otro de los requisitos de este juego.
Cuando el usuario ha terminado de contestar las preguntas,
es decir cuando el foreach del método principal ha llegado a su fin, debo
mostrar cuantas respuestas válidas obtuvo el usuario, es decir cuantas veces
selectedOption dentro de cada item answers tiene true es decir es la respuesta
correcta. (recuerda que de
toda las respuestas posibles una y solo una tiene la propiedad isValid a true)
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el nombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.Options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
var answer = GetSelectedAnswer();
AddAnswersToList(answer, item);
}
//Iterar la lista de respuestas (que ahora está llena) y encontrar
cuantas respuestas acertadas tubo el
usuario
}
¿Como vamos a hacer esto? Pues creando otro método que
podemos llamará GetScore() y será de tipo int. Lo vamos a invocar en su lugar
incluso antes de crearlo, lo tenemos que invocar desde el método principal
donde tenemos el comentario resaltado en amarillo:
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el nombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.Options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
var answer = GetSelectedAnswer();
AddAnswersToList(answer, item);
}
int score = GetScore();
}
Vamos ahora a crear este nuevo método, podemos crearlo al final del todo:
int GetScore()
{
}
Vamos a necesitar una variable par aguardar la puntuación que podemos llamar
score y que inicializaremos a cero y vamos a necesitar tambien un ciclo foreach
int score = 0;
int GetScore()
{
int score = 0;
foreach( var item in answers)
{
if(item.SelectedOption.IsValid)
score++;
}
return score;
}
Así de sencillo, como tenemos todas la respuestas en una
lista puedo iterarla y ver cuales son las respuesta que el usario ha elegido y
discriminar si son las correctas o no. Por cada acierto incremento el score y
lo retorno al final.
Ahora que ya tengo el valor del score vuelvo al método
principal StartGame y justo debajo de donde he invocado a GetScore, puedo
mostrar por pantalla la información que ese método me devuelve:
int score = GetScore();
Console.WriteLine($"Nice try {player}!
You answered well {score} questions");
En este momento tenemos el score de este jugador en
particular, pero recordemos que uno de los requisitos del juego era mantener el
histórico de cada uno de los jugadores, es decir de cada jugador debemos
guardar nombre y score, para lo cual vamos a usar otro tipo de colección de
datos, hasta este momento hemos usado listas, ahora vamos a usar un diccionario.
Introduciendo los diccionarios
Como hemos dicho el nombre y el score de cada jugador, lo
vamos a guardar en un tipo de colección de datos que llamamos diccionarios,
vamos a declarar nuestro primer diccionario al que vamos a llamar scores
//Diccionario para guardar
todos los nombres de usuarios y sus scores
var scores = new
Dictionary<string, int>();
un diccionario se declara como se puede ver en el código
anterior, tiene dos valores
·
Key: en este caso de tipo string donde guardaré el nombre del jugador
player
·
Value:
en este caso de tipo int (el valor de score).
Disponiendo de esta estructura lo
único que tengo que hacer es que cada vez que el juego termina, tengo que
actualizar el diccionario, igual que hacemos con el método AddAnsweToList solo
que vamos a añadirlo al diccionario, es un poco más complejo que le manejo de
listas pero no demasiado.
Vamos al final del todo y
crearemos un nuevo método que podemos llamar UpdateScore(). Como
puedes ver todo lo estaos haciendo a través de métodos de forma que nos queda
un código mas legible y sostenible:
Este nuevo método UpdateScore
necesita recibir por parámetro el player y el score:
void UpdateScore(string player, int score)
{
}
Lo primero que vamos a hacer es
no asumir que el diccionario está vacío, asumiremos que el diccionario existe y
comprobaremos si el jugador actual ya ha jugado antes.
Para ello con un foreach vamos a
iterar el diccionario (aunque ahora sabemos que está vacío)
//Método para actualizar y
almacenar el diccionario jugador-score
void UpdateScore(string player, int score)
{
foreach(var item in scores)
{
if(item.)
}
}
Y observa como visual studio ya nos dice que tenemos acceso
a las propiedades del diccionario, que se declararon al inicializarlo:
·
Value: string nombre del jugador
·
Int: score
Entonces si key es igual a player, es que este jugador ya ha
jugado antes y lo que haré será actualizar su valor. A un diccionario se accede
como sifuera un array claveàvalor:
void UpdateScore(string player, int score)
{
foreach(var item in scores) //el jugador ya ha jugado
antes así que actualizao el diccionario
{
if(item.Key == player)
{
scores[item.Key] = score;
}
}
}
Esto busca el item o jugador del diccionario por su key que
és única y le asigna el valor de score que le pertenece a ese jugador. Las key
de los diccionarios són únicas siempre, esta es una característica de los
diccionarios.
Si esto no es así, es decir el jugador es nuevo, es la
primera vez que juega, lo voy a agregar, pera ello necesitaré una variable
local booleana para saber si estoy haciendo un update o un insert, así que me
creo una variable que puedo llamar updated que inicializo a false:
void UpdateScore(string player, int score)
{
foreach(var item in scores) //el jugador ya ha jugado
antes así que actualizao el diccionario
{
bool uodated = false; //Saber si hago update o insert
if(item.Key == player)
{
scores[item.Key] = score;
}
}
}
Con esta variable, si entramos por el caso anterior de que
el jugador no es nuevo, hago esta variable true y no tengo que hacer nada mas:
void UpdateScore(string player, int score)
{
foreach(var item in scores) //el jugador ya ha jugado
antes así que actualizao el diccionario
{
bool updated = false; //Saber si hago update o insert
if(item.Key == player)
{
scores[item.Key] = score;
updated = true;
}
}
Y ahora el caso de que el jugador sea nuevo, debo agregarlo
al diccionario:
void UpdateScore(string player, int score)
{
foreach(var item in scores) //el jugador ya ha jugado
antes así que actualizao el diccionario
{
bool updated = false; //Saber si hago update o insert
if(item.Key == player)
{
scores[item.Key] = score;
updated = true;
}
if(!updated) //El jugador es nuevo por lo tanto tengo que agregarlo al
diccionario
{
scores.Add(player, score);
}
}
}
Una vez que tenemos esto, vamos a volver a nuestro método
principal y solo tenemos que invocar a nuestro nuevo método UpdateScore()
. Podemos hacerlo justo después de mostrale su score al jugador
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el nombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.Options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
var answer = GetSelectedAnswer();
AddAnswersToList(answer, item);
}
int score = GetScore();
Console.WriteLine($"Nice try {player}! You answered well {score} questions");
//Actualizamos
el diccionario invocando al método
UpdateScore(player, score);
}
Una vez que tenemos nuestro diccionario de scores y player actualizada lo único que nos queda es mostrar la lista de todos los scores, una vez que el usuario ha terminado la partida, puede ver cuál es su score y con esto va a poder compararlo con el resto de los scores de los demás jugadores.
Esto lo podemos hacer iterando todo el diccionario y
mostrando por pantalla tdos los jugadores y sus puntuaciones. Lo vamos a
resolver creando un nuevo método abajo del todo que será de tipo void y que
podemos llamar ShowScores()
void ShowScores()
{
Console.WriteLine("Scores:");
foreach(var item in scores) //Iteramos el diccionario
{
Console.WriteLine($"{item.Key}, score: {item.Value}");
}
}
Como puedes ver con un foreach itero el diccionario y saco
por pantalla el nombre del jugador
y su score
Como siempre lo único que nos resta hacer es invocar este
nuevo método en nuestro método principal StartGame:
void StartGame()
{
Console.WriteLine("Are you ready? We are starting now¡");
Console.WriteLine("What is your name?");
//Guardamos
el nombre enuna variable player y esperar que se introduzca el nombre
var player = Console.ReadLine();
//Mostrar
el nombre del usuario or pantalla
Console.Write($"ok, {player} let´s do this");
//Usar
una estructura de control para recorrer la lista de preguntas que acabamos de
crear
//y
mostrar por pantalla el texto de cada pregunta, el texto de cada una de las
opciones
//de
respuestas
foreach (var item in questions)
{
//Impriminos
en pantalla la pregunta
Console.WriteLine(item.QuestionText);
//Indicar
al usuario que elija una respuesta
Console.WriteLine("Please enter 1, 2, 3 or 4");
//Imprimimos
las opciones de respuesta, para ello usaremos un foreach anidado
//en
el foreach principal
foreach (var option in item.Options)
{
Console.WriteLine($"{option.Id}. {option.Text}");
}
var answer = GetSelectedAnswer();
AddAnswersToList(answer, item);
}
int score = GetScore();
Console.WriteLine($"Nice try {player}! You answered well {score} questions");
//Actualizamos
el diccionario invocando al método
UpdateScore(player, score);
//Mostrar
los scores de todos los jugadores
ShowScores();
}
Vamos a testear nuestro juego
Primero nos pregunta el nombre y nos muestra sólo una
pregunta a la vez, esto es porque tenemos un console.writeline esperando
respuesta y esto detiene la ejecución hasta recibir esa respuesta y es lo que
debe ser
En el momento en que doy una respuesta debería pasara la
siguiente pregunta y así es
Voy a introducir una respuesta que sea válida por ejemplo un
5 y efectivamente me muestra el mensaje de eeror y vuelve a esperar una
respuesta válida
Si termino de jugar me muestra mi score y termina la
ejecución
Sin embargo, en scores no me ha mostrado nada, debería
haberme mostrado mi nombre y mi puntuación, debo tener algún error.
El error lo tenemos en el método Update score:
Primero
la variable bool updated debería etar fuera y antes del foreach
Y la
validación del updated debería estar fuera del foreach ya que como el foreach
no tiene nada no itera
void UpdateScore(string player, int score)
{
foreach(var item in scores) //el jugador ya ha jugado
antes así que actualizao el diccionario
{
bool updated = false; //Saber si hago update o insert
if(item.Key == player)
{
scores[item.Key] = score;
updated = true;
}
if(!updated)
//El jugador es nuevo por lo tanto tengo que agregarlo al diccionario
{
scores.Add(player,
score);
}
}
}
Sería asi:
void UpdateScore(string player, int score)
{
bool updated = false; //Saber si hago update o insert
foreach (var item in scores) //el jugador ya ha jugado
antes así que actualizao el diccionario
{
if(item.Key == player)
{
scores[item.Key] = score;
updated = true;
}
}
if (!updated) //El jugador es nuevo por lo tanto tengo que agregarlo al
diccionario
{
scores.Add(player, score);
}
}
Si ahora jugamos una partida, al teminar veremos el score y
el nombre
Para finalizar lo último que nos quedaría sería dar la
opción de volver a jugar de nuevo, para ello lo primero que necesitamos hacer
es limpiar la lista de preguntas para que quede de nuevo vacía
Vamos al método principal StartGame y al final del todo
cuando el juego ha teminado la inicializamos y agregaremos dos impresiones por
consola para dar info al jugador:
//VOLVER
A JUGAR________________________________________
//Limpiar
la lista de preguntas
answers = new List<Answer>();
Console.WriteLine("Do you want to play again?");
Console.WriteLine("Enter yes to play again or any other key to exit...");
var playAgain = Console.ReadLine();
if (playAgain.ToLower().Trim() == "yes")
StartGame();
//________________________________________________________
}Esta
vez si el usuario no escribe yes el juego termina