Combate
Instancia de evaluación | Fecha |
---|---|
Final | 20/04/2022 |
Criterios de evaluación
- Se deberá entregar un código limpio y acorde a las reglas de estilo de la cátedra.
- Colocar sus datos personales en el nombre de la carpeta principal y la solución: Apellido.Nombre.Div. Ej: Pérez.Juan.2D. No sé corregirán proyectos que no sea identificable su autor.
- No se corregirán exámenes que no compilen.
- Reutilizar tanto código como crean necesario.
- Aplicar los principios de la programación orientada a objetos.
Consigna
Parte I
Crear una base de datos llamada COMBATE_DB
y ejecutar el siguiente script:
USE [COMBATE_DB]
GO
CREATE TABLE [dbo].[PERSONAJES](
[id] [int] NOT NULL,
[nombre] [varchar](150) NOT NULL,
[nivel] [smallint] NOT NULL,
[clase] [smallint] NOT NULL,
[titulo] [varchar](500) NULL,
CONSTRAINT [PK_PERSONAJES] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT INTO [dbo].[PERSONAJES]
([id]
,[nombre]
,[nivel]
,[clase]
,[titulo])
VALUES
(1
,'Falcorn'
,1
,1
,'Defender of the Alliance');
INSERT INTO [dbo].[PERSONAJES]
([id]
,[nombre]
,[nivel]
,[clase]
,[titulo])
VALUES
(2
,'NWBZPWNR'
,1
,2
,null);
GO
Parte II
Crear un proyecto del tipo Biblioteca de Clases y agregarle los siguientes elementos:
Excepción BusinessException
- Crear una excepción personalizada
BusinessException
con dos constructores, uno que reciba sólo el mensaje y otro que reciba además laInnerException
.
Enumerado LadosMoneda
- Tiene dos posible valores.
Cara
con valor1
yCeca
con valor2
.
Clase ExtensionRandom
- Extenderá el tipo
Random
y le agregará el método de extensiónTirarUnaMoneda
que retornará de forma aleatoria alguno de los valores del enumeradoLadosMoneda
.
Parte III
Desarrollar el siguiente esquema de clases:
Alerta
El diagrama no está completo, se espera que se agreguen a la solución los elementos necesarios para cumplir con las consignas.
Clase Personaje
Implementa la interfaz
IJugador
.No se puede instanciar.
Tendrá una propiedad
Titulo
de sólo escritura que cambia el valor del atributotitulo
.El atributo
random
es estático y debe inicializarse en un constructor estático.Constructores de instancia:
- Todos los personajes arrancan con una base de:
- 100 puntos de defensa por cada nivel que tenga el personaje.
- 100 puntos de poder por cada nivel que tenga el personaje.
- 500 puntos de vida por cada nivel que tenga el personaje.
- Si se usa la sobrecarga de constructores que no recibe un nivel, por defecto será 1.
- El constructor debe recibir un nombre que no sea
null
ni solamente espacio en blanco, de lo contrario lanzar la excepciónArgumentNullException
(ya definida en .NET). - Asegurarse de que el nombre proporcionado no tenga espacios en blanco al inicio o al final. Si los tuviera, eliminarlos del string.
- Inicializar el id con el argumento proporcionado.
- Valida que el
nivel
se encuentre entre el máximo y el mínimo permitidos (incluídos).- El máximo de nivel deberá estar definido en una constante de la clase y será
100
. - El mínimo de nivel deberá estar definido en una constante de la clase y será
1
. - Si el nivel no es válido, lanzará la excepción personalizada
BusinessException
con un mensaje descriptivo.
- El máximo de nivel deberá estar definido en una constante de la clase y será
- Todos los personajes arrancan con una base de:
Dos personajes serán iguales sólo si tienen el mismo
id
. Cambiar el comportamiento por defecto de las operaciones de comparación: operador==
, métodoEquals
y métodoGetHashCode
.
Tip
Si se llama al método GetHashCode
de dos valores numéricos (por ejemplo el id
del personaje) que sean iguales (tengan el mismo valor), retorna el mismo código hash.
Define el método
AplicarBeneficiosDeClase
que debe ser implementado de manera obligatoria por las clases derivadas.Tendrá dos eventos llamados
AtaqueLanzado
yAtaqueRecibido
respectivamente, cuyos manejadores recibirán unPersonaje
y unint
y no retornarán nada.Al atacar:
- Se detendrá el hilo de ejecución por un tiempo aleatorio de entre 1 y 5 segundos.
- Retornará los puntos de ataque que tendrán un valor de entre un 10% y un 100% de los puntos de poder. El porcentaje a aplicar se debe definir de manera aleatoria.
- Por último, lanza el evento
AtaqueLanzado
pasándole como argumentos a la instancia del personaje que está atacando y los puntos de ataque calculados. Sólo lanza el evento si el mismo tiene subscriptores.
Al recibir un ataque:
- El personaje se defenderá restando a los puntos de ataque recibidos entre un 10% y un 100% de los puntos de defenza. El porcentaje a aplicar se debe definir de manera aleatoria.
- Una vez que se ejecutó la defenza, se restarán los puntos de ataque resultantes a los puntos de vida del personaje.
- Los puntos de vida no pueden quedar en negativo, el valor mínimo es cero.
- Por último, lanza el evento
AtaqueRecibido
pasándole como argumentos a la instancia del personaje que está recibiendo el ataque y los puntos de ataque que impactaron efectivamente (luego de aplicar la defenza). Sólo lanza el evento si el mismo tiene subscriptores.
Cambiar el comportamiento por defecto del método
ToString
para que retorne el nombre del personaje. Si además el personaje tiene un título, retonará "nombre
,título
".
Clase Guerrero
Deriva de
Personaje
.Implementa el método
AplicarBeneficiosDeClase
aplicando una bonificación para el personaje de un 10% de puntos de defenza adicionales. Descartar los decimales.
Clase Hechicero
Deriva de
Personaje
.Implementa el método
AplicarBeneficiosDeClase
aplicando una bonificación para el personaje de un 10% de puntos de poder adicionales. Descartar los decimales.
Clase Combate
No debe poder heredarse de ella (no puede ser clase base de otras clases).
Tiene un evento llamado
RondaIniciada
. Sus manejadores reciben dos objetos de tipoIJugador
y no retornan nada.Tiene un evento llamado
CombateFinalizado
. Sus manejadores reciben un objeto de tipoIJugador
y no retornan nada.El atributo
random
es estático y debe inicializarse en un constructor estático.El método de clase
SeleccionarJugadorAleatoriamente
lanza una moneda para elegir de forma aleatoria un jugador. Reutilizar código.- Si sale cara, retorna al jugador 1.
- Si sale ceca, retorna al jugador 2.
El método de clase
SeleccionarPrimerAtacante
elige al jugador que ejecutará el primer ataque a partir del siguiente criterio:- Si el nivel de los jugadores es diferente, empieza a atacar el jugador con menos nivel.
- Si el nivel de los jugadores es igual, selecciona uno de forma aleatoria. Reutilizar código.
El método
IniciarRonda
:- Lanza el evento
RondaIniciada
pasandole como primer argumento al jugador atacante y como segundo argumento al jugador atacado. Sólo lanza el evento si el mismo tiene subscriptores. - Genera un ataque del jugador atacante y lo impacta en el jugador atacado.
- Lanza el evento
El método
EvaluarGanador
retorna al jugador atacante si el jugador atacado tiene cero puntos de vida. De lo contrario, si el atacado todavía tiene vida, intercambia los roles (el jugador atacante pasará a ser el atacado, y el atacado pasará a ser el atacante) y retornanull
.El método
Combatir
:- Llama a
IniciarRonda
y luego aEvaluarGanador
, repite este proceso hasta que se encuentre un ganador, es decir queEvaluarGanador
no retornenull
. - Una vez que haya un ganador lanza el evento
CombateFinalizado
pasándole como argumento al jugador ganador, siempre y cuando el evento tenga suscriptores. - Cuando el combate finaliza genera una instancia de
ResultadoCombate
y la serializa a formatoXML
oJSON
(a elección del alumno). Se debe instanciarResultadoCombate
con los siguientes datos:- Nombre del ganador (
ToString
) - Nombre del perdedor (
ToString
) - Fecha y hora actual.
- Nombre del ganador (
- Llama a
El método
IniciarCombate
ejecutaCombatir
en un hilo secundario y retorna el objetoTask
.
¿Cómo que Task? ¿Qué es eso? ¡Yo vi hilos con la clase Thread!
Si no viste Task
en tu cursada y preferís usar Thread
no hay problema, retornar el objeto de tipo Thread
en su lugar y avisar al profesor para que te indique qué modificar en el método main al final del examen.
- El constructor de instancia usa el método
SeleccionarPrimerAtacante
para definir cuál de los dos jugadores será elatacante
, inicializando dicho atributo con ese jugador. Elatacado
será, por descarte, el jugador que no haya sido elegido como atacante.
Clase Logger
- Tiene un constructor de instancia que recibe como argumento la ruta donde se almacenará el log.
- Tiene un método
GuardarLog
que guarda el texto recibido como argumento en el archivo del log. No sobrescribir el contenido del archivo, anexar.
Clase PersonajeDAO
- Será estática.
- Su método
ObtenerPersonajePorID
consulta la base de datos buscando en la tablaPERSONAJES
por elid
recibido como argumento, y retorna una instancia dePersonaje
con los datos recuperados.- Si el registro tiene el valor
1
en su columnaCLASE
, se deberá instanciar y retornar unGuerrero
. - Si el registro tiene el valor
2
en su columnaCLASE
, se deberá instanciar y retornar unHechicero
. - Si no encuentra nada, retorna null.
- Si el registro tiene el valor
Parte IV
Crear un proyecto de pruebas unitarias y probar las siguientes funcionalidades:
- Que se lance la excepción
BusinessException
cuando se trata de instanciar unPersonaje
con un nivel inválido. - Que cuando un
Personaje
recibe un ataque no quede con puntos de vida negativos, sino en cero. - Que se inicien correctamente los puntos de defenza para cada tipo de personaje.
Parte V
Crear un proyecto de consola y reemplazar el contenido de la clase Program
con el siguiente código:
static void Main(string[] args)
{
Personaje personaje1 = PersonajeDAO.ObtenerPersonajePorId(1);
Personaje personaje2 = PersonajeDAO.ObtenerPersonajePorId(2);
Combate combate = new Combate(personaje1, personaje2);
Console.WriteLine("¡FIGHT!");
combate.IniciarCombate().Wait();
}
static void IniciarRonda(IJugador atacante, IJugador atacado)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
Console.WriteLine("--------------------------------");
Console.WriteLine($"¡{atacante} ataca a {atacado}!");
}
static void FinalizarCombate(IJugador ganador)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine();
Console.WriteLine("--------------------------------");
Console.WriteLine($"Combate finalizado. El ganador es {ganador}.");
}
static void MostrarAtaqueLanzado(Personaje personaje, int puntosDeAtaque)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{personaje} lanzó un ataque de {puntosDeAtaque} puntos.");
}
static void MostrarAtaqueRecibido(Personaje personaje, int puntosDeAtaque)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"{personaje} recibió un ataque por {puntosDeAtaque} puntos. Le quedan {personaje.PuntosDeVida} puntos de vida.");
}
Parte VI
Manejar los eventos
AtaqueLanzado
de los personajes usando el manejadorMostrarAtaqueLanzado
.Manejar los eventos
AtaqueRecibido
de los personajes usando el manejadorMostrarAtaqueRecibido
.Manejar el evento
RondaIniciada
del combate usando el manejadorIniciarRonda
.Manejar el evento
CombateFinalizado
del combate usando el manejadorFinalizarCombate
.
Parte VII
Manejar las posibles excepciones de tipo
BusinessException
mostrando su mensaje por la salida de la consola.Manejar las posibles excepciones de cualquier otro tipo mostrando su mensaje y su
StackTrace
por la salida de la consola. Almacenar dichos datos en un archivo de textolog.txt
usando la funcionalidad deLogger
.
Resultado esperado
Al completar todos los puntos, compilar y ejecutar la solución se deberá ver una salida similar a la siguiente:
Resolución
Video | Código |
---|