R8 [Solución]- El Escalafón Mágico de Hogwarts

El Ministerio de Magia ha implantado un nuevo sistema de clasificación de magos para evaluar su progreso, capacidades y responsabilidades dentro del mundo mágico.

1) Casa.java (enum)

La enum sirve para tener un conjunto cerrado de valores (solo esas casas), evitando Strings sueltos.





package modelo;

public enum Casa {
    GRYFFINDOR,
    SLYTHERIN,
    RAVENCLAW,
    HUFFLEPUFF
}

2) Mago.java (clase base)

Qué aporta esta clase

  • Define lo común a todos: nombre, nivel, vida, mana, casa.
  • Aplica un bonus inicial según la casa.
  • Tiene métodos base (entrenar, descansar, ficha, lanzarHechizo).
  • lanzarHechizo NO es abstracto: por defecto hace algo simple.




package modelo;

public class Mago {

    protected String nombre;
    protected int nivel; // 1..100
    protected int vida;  // 0..100
    protected int mana;  // 0..100
    protected Casa casa;

    public Mago(String nombre, Casa casa) {
        this.nombre = nombre;
        this.casa = casa;

        // Valores base
        this.nivel = 1;
        this.vida = 100;
        this.mana = 100;

        aplicarBonusCasa();
    }

    private void aplicarBonusCasa() {
        // Bonus simples y fáciles de ver en consola
        switch (casa) {
            case GRYFFINDOR:
                vida += 10; // más resistencia
                break;
            case SLYTHERIN:
                mana += 10; // más “energía mágica”
                break;
            case RAVENCLAW:
                nivel += 1; // aprende más rápido (nivel inicial superior)
                break;
            case HUFFLEPUFF:
                vida += 5;
                mana += 5; // equilibrio
                break;
        }
        // Normalizamos límites
        vida = clamp(vida, 0, 100);
        mana = clamp(mana, 0, 100);
        nivel = clamp(nivel, 1, 100);
    }

    public void entrenar() {
        // Entrenar sube nivel, pero cansa
        nivel += 1;
        mana -= 5;
        vida -= 2;

        nivel = clamp(nivel, 1, 100);
        mana = clamp(mana, 0, 100);
        vida = clamp(vida, 0, 100);
    }

    public void descansar() {
        // Descansar recupera recursos
        mana += 10;
        vida += 10;

        mana = clamp(mana, 0, 100);
        vida = clamp(vida, 0, 100);
    }

    public String ficha() {
        return nombre + " | Casa: " + casa +
               " | Nivel: " + nivel +
               " | Vida: " + vida +
               " | Mana: " + mana;
    }

    /**
     * Lanza un hechizo genérico. Las clases hijas lo mejoran.
     * Devuelve el "daño" (o 0 si no se puede).
     */
    public int lanzarHechizo(String hechizo) {
        int coste = 5;
        if (!gastarMana(coste)) return 0;
        return 3; // daño base genérico
    }

    protected boolean gastarMana(int cantidad) {
        if (mana < cantidad) {
            return false;
        }
        mana -= cantidad;
        mana = clamp(mana, 0, 100);
        return true;
    }

    public void recibirDaño(int daño) {
        vida -= daño;
        vida = clamp(vida, 0, 100);
    }

    protected int clamp(int valor, int min, int max) {
        if (valor < min) return min;
        if (valor > max) return max;
        return valor;
    }
}

3) Aprendiz.java

Idea clave

  • Un Aprendiz tiene torpeza, así que puede fallar.
  • Aprende con practicarBasico().
  • Sobrescribe lanzarHechizo para permitir solo hechizos básicos.




package modelo;

import java.util.Random;

public class Aprendiz extends Mago {

    protected int torpeza; // 0..100 (más torpeza = más fallos)
    protected Random rnd = new Random();

    public Aprendiz(String nombre, Casa casa) {
        super(nombre, casa);

        torpeza = 40;

        // Ajuste según casa (sencillo y visible)
        if (casa == Casa.RAVENCLAW) torpeza -= 10;   // más “cabeza”
        if (casa == Casa.HUFFLEPUFF) torpeza -= 5;   // constancia
        if (casa == Casa.SLYTHERIN) torpeza += 5;    // “se la juega más”
        torpeza = clamp(torpeza, 0, 100);
    }

    public void practicarBasico() {
        // Practicar reduce torpeza (mejora)
        if (!gastarMana(3)) return;

        torpeza -= 2;
        torpeza = clamp(torpeza, 0, 100);

        // Practicar también puede subir un poquito el nivel
        if (nivel < 100) nivel += 1;
    }

    @Override
    public int lanzarHechizo(String hechizo) {
        // Coste base
        if (!gastarMana(5)) return 0;

        // Probabilidad de fallo basada en torpeza
        int tirada = rnd.nextInt(100); // 0..99
        if (tirada < torpeza) {
            // falló
            return 0;
        }

        // Hechizos permitidos
        if (hechizo.equalsIgnoreCase("Lumos")) return 5;
        if (hechizo.equalsIgnoreCase("Alohomora")) return 7;

        // Si escribe otro, no lo conoce
        return 0;
    }

    public int getTorpeza() {
        return torpeza;
    }

    public void setTorpeza(int torpeza) {
        this.torpeza = clamp(torpeza, 0, 100);
    }
}

4) Hechicero.java

Idea clave

  • Hereda de Aprendiz, pero ahora tiene controlVarita.
  • Puede hacer hechizos más potentes.
  • Los fallos deberían ser menos frecuentes: usamos torpeza pero “rebajada”.




package modelo;

import java.util.Random;

public class Hechicero extends Aprendiz {

    protected int controlVarita; // 0..100
    protected Random rnd2 = new Random();

    public Hechicero(String nombre, Casa casa) {
        super(nombre, casa);

        controlVarita = 50;

        // Ravenclaw suele destacar en control
        if (casa == Casa.RAVENCLAW) controlVarita += 10;
        if (casa == Casa.GRYFFINDOR) controlVarita += 5;

        controlVarita = clamp(controlVarita, 0, 100);
    }

    public void estudiarEnBiblioteca() {
        if (!gastarMana(6)) return;

        controlVarita += 3;
        controlVarita = clamp(controlVarita, 0, 100);

        nivel += 1;
        nivel = clamp(nivel, 1, 100);
    }

    @Override
    public int lanzarHechizo(String hechizo) {
        if (!gastarMana(7)) return 0;

        // Los Hechiceros fallan menos: torpeza influye menos
        int torpezaEfectiva = clamp(torpeza - (controlVarita / 5), 0, 100);
        int tirada = rnd2.nextInt(100);
        if (tirada < torpezaEfectiva) return 0;

        // Hechizos
        if (hechizo.equalsIgnoreCase("Lumos")) return 6;
        if (hechizo.equalsIgnoreCase("Alohomora")) return 8;
        if (hechizo.equalsIgnoreCase("Expelliarmus")) return 12;
        if (hechizo.equalsIgnoreCase("Stupefy")) return 18;

        return 0;
    }

    public int getControlVarita() {
        return controlVarita;
    }
}

5) Auror.java

Idea clave

  • Un Auror es “modo combate”: tiene experiencia y puede arrestar.
  • Añadimos métodos propios: patrullar() y arrestar(...).




package modelo;

public class Auror extends Hechicero {

    private int experienciaCombate; // 0..100
    private int arrestos;

    public Auror(String nombre, Casa casa) {
        super(nombre, casa);

        experienciaCombate = 40;
        arrestos = 0;

        if (casa == Casa.GRYFFINDOR) experienciaCombate += 10; // valentía
        if (casa == Casa.SLYTHERIN) experienciaCombate += 5;   // ambición táctica
        experienciaCombate = clamp(experienciaCombate, 0, 100);
    }

    public void patrullar() {
        if (!gastarMana(8)) return;

        experienciaCombate += 4;
        experienciaCombate = clamp(experienciaCombate, 0, 100);

        nivel += 1;
        nivel = clamp(nivel, 1, 100);
    }

    public boolean arrestar(Aprendiz objetivo) {
        // Regla: solo si está débil
        if (objetivo == null) return false;

        // Como vida es protected en Mago, podemos leerla aquí directamente:
        if (objetivo.vida <= 20) {
            arrestos++;
            return true;
        }
        return false;
    }

    @Override
    public int lanzarHechizo(String hechizo) {
        if (!gastarMana(9)) return 0;

        // Auror: daño mejorado por experienciaCombate
        if (hechizo.equalsIgnoreCase("Stupefy")) {
            return 20 + (experienciaCombate / 10);
        }

        // Protego: “defensa” simple -> recupera vida (no daño)
        if (hechizo.equalsIgnoreCase("Protego")) {
            vida += 10;
            vida = clamp(vida, 0, 100);
            return 0;
        }

        // Otros hechizos como en Hechicero (reutilizamos lógica)
        return super.lanzarHechizo(hechizo);
    }

    public int getArrestos() {
        return arrestos;
    }
}

6) Profesor.java

Idea clave

  • Un Profesor enseña: puede reducir torpeza y subir nivel del alumno.
  • Sus hechizos son “eficientes”: consumen menos mana o fallan menos.




package modelo;

public class Profesor extends Hechicero {

    private String asignatura;
    private int prestigio; // 0..100

    public Profesor(String nombre, Casa casa, String asignatura) {
        super(nombre, casa);
        this.asignatura = asignatura;

        prestigio = 60;
        if (casa == Casa.SLYTHERIN) prestigio += 5;
        if (casa == Casa.RAVENCLAW) prestigio += 10;
        prestigio = clamp(prestigio, 0, 100);
    }

    public void enseñar(Aprendiz alumno) {
        if (alumno == null) return;
        if (!gastarMana(6)) return;

        // Enseñar reduce torpeza y sube nivel
        alumno.setTorpeza(alumno.getTorpeza() - 8);
        alumno.nivel = clamp(alumno.nivel + 1, 1, 100);
    }

    public void evaluar() {
        if (!gastarMana(4)) return;
        prestigio += 2;
        prestigio = clamp(prestigio, 0, 100);
    }

    @Override
    public int lanzarHechizo(String hechizo) {
        // Profesor: coste menor y resultados más estables
        int coste = 6;
        if (!gastarMana(coste)) return 0;

        // “Daño controlado”
        if (hechizo.equalsIgnoreCase("Expelliarmus")) return 14;
        if (hechizo.equalsIgnoreCase("Stupefy")) return 17;
        if (hechizo.equalsIgnoreCase("Lumos")) return 5;
        if (hechizo.equalsIgnoreCase("Alohomora")) return 7;

        return 0;
    }

    public String getAsignatura() {
        return asignatura;
    }
}

7) Main.java (sin listas de Mago, tipos concretos)

package app;

import modelo.*;

public class Main {

    public static void main(String[] args) {

        Aprendiz harry = new Aprendiz("Harry", Casa.GRYFFINDOR);
        Aprendiz hermione = new Aprendiz("Hermione", Casa.RAVENCLAW);

        Hechicero draco = new Hechicero("Draco", Casa.SLYTHERIN);
        Hechicero luna = new Hechicero("Luna", Casa.HUFFLEPUFF);

        Auror kingsley = new Auror("Kingsley", Casa.HUFFLEPUFF);
        Profesor snape = new Profesor("Snape", Casa.SLYTHERIN, "Pociones");

        System.out.println("=== FICHAS INICIALES ===");
        System.out.println(harry.ficha());
        System.out.println(hermione.ficha());
        System.out.println(draco.ficha());
        System.out.println(luna.ficha());
        System.out.println(kingsley.ficha());
        System.out.println(snape.ficha());

        System.out.println("\n=== RONDA 1: PRACTICAR Y ESTUDIAR ===");
        harry.practicarBasico();
        hermione.practicarBasico();
        draco.estudiarEnBiblioteca();
        luna.estudiarEnBiblioteca();
        kingsley.patrullar();
        snape.evaluar();

        System.out.println(harry.ficha());
        System.out.println(hermione.ficha());
        System.out.println(draco.ficha());
        System.out.println(luna.ficha());
        System.out.println(kingsley.ficha());
        System.out.println(snape.ficha());

        System.out.println("\n=== RONDA 2: DUELO SIMPLE (daño a vida) ===");
        int d1 = draco.lanzarHechizo("Stupefy");
        harry.recibirDaño(d1);
        System.out.println("Draco lanza Stupefy a Harry. Daño: " + d1);
        System.out.println("Harry -> " + harry.ficha());

        int d2 = harry.lanzarHechizo("Lumos");
        draco.recibirDaño(d2);
        System.out.println("Harry lanza Lumos a Draco (sí, es cutre). Daño: " + d2);
        System.out.println("Draco -> " + draco.ficha());

        System.out.println("\n=== RONDA 3: PROFESOR EN ACCIÓN ===");
        snape.enseñar(harry);
        System.out.println("Snape enseña a Harry (baja torpeza y sube nivel).");
        System.out.println("Harry -> " + harry.ficha());

        System.out.println("\n=== RONDA 4: AUROR Y PROTEGO ===");
        int d3 = kingsley.lanzarHechizo("Stupefy");
        luna.recibirDaño(d3);
        System.out.println("Kingsley lanza Stupefy a Luna. Daño: " + d3);
        System.out.println("Luna -> " + luna.ficha());

        kingsley.lanzarHechizo("Protego");
        System.out.println("Kingsley usa Protego (se cura un poco).");
        System.out.println("Kingsley -> " + kingsley.ficha());

        System.out.println("\n=== RONDA 5: INTENTO DE ARRESTO ===");
        // Bajamos a propósito la vida de Hermione para probar arresto
        hermione.recibirDaño(85);
        System.out.println("Hermione queda débil: " + hermione.ficha());

        boolean arresto = kingsley.arrestar(hermione);
        System.out.println("¿Kingsley arresta a Hermione? " + arresto);
        System.out.println("Arrestos de Kingsley: " + kingsley.getArrestos());
    }
}

Aquí el alumnado ve claramente la herencia: cada clase tiene lo suyo.




El conocimiento es poder… pero en Hogwarts, también lo es la herencia bien diseñada.