Autor: ciberkaos

  • R6 – Clase PsicoHistoriador (Fundación)

    R6 – Clase PsicoHistoriador (Fundación)


    RETO 1: Clase PsicoHistoriador (Fundación)

    Contexto

    Formas parte del Proyecto Seldon, dentro del universo de Fundación. Vas a modelar a un psico-historiador, un especialista capaz de analizar el futuro del Imperio mediante matemáticas y sociología.

    Objetivo

    Crear una única clase en Java que represente a un psico-historiador. Este reto sirve para practicar atributos privados, constructores, getters, setters y métodos simples. No se permite utilizar listas, arrays, ni otras clases.

    Instrucciones

    1. Nombre de la clase
    La clase debe llamarse PsicoHistoriador.

    2. Atributos privados obligatorios

    Debes declarar:

    • String nombre;
    • int nivelMatematico; (0 a 100)
    • boolean lealtadFundacion;
    • int prediccionesRealizadas;

    3. Constructor
    El constructor debe recibir String nombre e int nivelMatematico.

    El resto se inicializa así:
    lealtadFundacion = true;
    prediccionesRealizadas = 0;

    4. Getters y setters
    Implementa métodos getter y setter para los atributos necesarios, al menos los siguientes:

    • getNombre()
    • getNivelMatematico() y setNivelMatematico(int n)
    • isLealtadFundacion() y setLealtadFundacion(boolean v)
    • getPrediccionesRealizadas()

    5. Método realizarPrediccion()
    Cada vez que se llame:

    • Aumenta en 1 prediccionesRealizadas.
    • Calcula una probabilidad usando nivelMatematico.
    • Devuelve un texto como:
      “El psico-historiador NOMBRE calcula una probabilidad de colapso del Imperio del X%.”

    6. Método evaluarRiesgoImperial()
    Debe devolver un mensaje según el valor de nivelMatematico:

    • Mayor de 80 → “Riesgo alto: el Imperio se derrumbará pronto.”
    • Entre 50 y 80 → “Riesgo moderado: el Plan Seldon sigue estable.”
    • Menor de 50 → “Riesgo bajo: estabilidad aparente, pero no te fíes.”

    7. Método main
    La misma clase debe contener un main que:

    • Cree un objeto PsicoHistoriador.
    • Llame a realizarPrediccion() varias veces e imprima el resultado.
    • Llame a evaluarRiesgoImperial() y muestre el mensaje final.

    RETO 2: Clase NaveEstelarFundacion (Constructores y Aleatoriedad)

    La Fundación mantiene una flota de naves estelares para explorar la galaxia. En este ejercicio practicarás atributos privados, varios constructores y el uso de un método auxiliar que genera valores aleatorios.

    Objetivo

    Crear la clase NaveEstelarFundacion implementando tres constructores, getters, setters, un método aleatorio proporcionado y un pequeño main de prueba. No se permite el uso de listas, arrays ni otras clases.

    Instrucciones

    1. Nombre de la clase
    La clase debe llamarse NaveEstelarFundacion.

    2. Atributos privados

    • String nombre;
    • String modelo;
    • int escudo;
    • int velocidadMaxima;
    • int misionesCompletadas;

    3. Método auxiliar ya proporcionado (NO modificar)

    private int generarAleatorio(int min, int max) { return (int)(Math.random() * (max - min + 1)) + min; } 

    4. Constructores obligatorios

    a) Constructor sin parámetros
    Debe asignar valores por defecto, por ejemplo:

    • nombre = "Anonima"
    • modelo = "Basico"
    • escudo = 50
    • velocidadMaxima = 2000
    • misionesCompletadas = 0

    b) Constructor con parámetros (nombre y modelo)
    Debe asignar los valores recibidos y generar:

    • escudo aleatorio entre 40 y 90
    • velocidadMaxima aleatoria entre 1500 y 4000
    • misionesCompletadas = 0

    c) Constructor con todos los atributos
    Debe recibir y asignar todos los campos: nombre, modelo, escudo, velocidadMaxima y misionesCompletadas.

    5. Getters y setters
    Debes implementar getters y setters para todos los atributos.

    6. Método desplegar()
    Devuelve un texto con los datos de la nave:

    "La nave NOMBRE, modelo MODELO, se despliega con escudo ESCUDO% y velocidad máxima VELOCIDAD km/s." 

    7. Método registrarMision()
    Aumenta en 1 el contador misionesCompletadas.

    8. Main de prueba
    Debe:

    Aumentar misiones con registrarMision() y mostrar el total.

    Crear una nave con el constructor por defecto.

    Crear otra con el constructor de dos parámetros.

    Crear una con el constructor completo.

    Llamar a desplegar() en cada una.

  • R7 – Guerra por el Sistema Solar: Tierra, Marte y el Cinturón” (The Expanse)

    R7 – Guerra por el Sistema Solar: Tierra, Marte y el Cinturón” (The Expanse)

    En el universo de The Expanse, la tensión entre la Tierra, Marte y el Cinturón ha acabado en guerra abierta. Tú vas a programar el sistema de gestión de una flota de naves que participan en esa guerra.

    El objetivo del ejercicio es trabajar:

    • Clases y objetos.
    • Uso de ArrayList con objetos.
    • Encapsulamiento y métodos.
    • Uso de Scanner para leer datos.
    • Un controlador con menú en consola.

    Deberás crear al menos tres clases:

    • Nave
    • Flota
    • Una clase con main (por ejemplo ControlFlota)

    1. Clase Nave

    Crea una clase Nave que represente una nave espacial.

    Atributos mínimos:

    • String nombre
    • String faccion
      (valores recomendados: "Tierra", "Marte", "Cinturon")
    • int misiles → número de misiles que tiene esa nave.
    • boolean necesitaReparacion → indica si la nave necesita pasar por dique seco.

    Requisitos:

    1. Al menos un constructor con todos los parámetros:
      • Nave(String nombre, String faccion, int misiles, boolean necesitaReparacion)
    2. Puedes añadir más constructores si quieres (por ejemplo, uno con menos parámetros).
    3. Métodos get y set para los atributos que consideres necesarios.
    4. Método toString() para mostrar la información de la nave de forma legible.

    2. Clase Flota

    La clase Flota representa un conjunto de naves en guerra.

    Atributo:

    • Un atributo privado:
      • ArrayList<Nave> naves;

    Requisitos de la clase Flota:

    1. Constructor sin parámetros
      Inicializa la lista:
      • Crea un ArrayList<Nave> vacío.
    2. Método para añadir una nave ya creada
      • void agregarNave(Nave nave)
        Añade la nave recibida como parámetro al ArrayList.
    3. Método para crear una nave usando Scanner y añadirla a la flota
      • void crearNaveDesdeTeclado(Scanner sc)
        Este método debe:
      • Pedir por teclado:
        • Nombre de la nave.
        • Facción (Tierra, Marte o Cinturon).
        • Número de misiles.
        • Si necesita reparación (por ejemplo, S/N).
      • Crear un objeto Nave con esos datos.
      • Añadir la nave creada a la lista de la flota.
    4. Método que devuelve el número de naves
      • int getNumeroNaves()
        Devuelve cuántas naves hay en la flota (tamaño del ArrayList).
    5. Método para listar todas las naves
      • void listarTodas()
        Muestra por pantalla todas las naves de la flota.
    6. Método para listar solo las naves que necesitan reparación
      • void listarNavesParaReparar()
        Recorre la lista y muestra solo las naves cuyo atributo necesitaReparacion sea true.
    7. Método para calcular el número total de misiles de la flota
      • int getTotalMisiles()
        Suma el número de misiles de todas las naves y devuelve el total.
    8. Método para listar naves por facción
      • void listarPorFaccion(String faccion)
        Muestra solo las naves cuya facción coincida con la indicada (por ejemplo "Tierra").
    9. Método para contar naves por facción
      • int contarPorFaccion(String faccion)
        Devuelve cuántas naves pertenecen a la facción indicada.

    Puedes añadir métodos auxiliares si lo necesitas (por ejemplo para validaciones).


    3. Clase ControlFlota (o similar) con main

    Crea una clase que contenga el método main y actúe como controlador.

    Requisitos:

    1. Crear un objeto Scanner y un objeto Flota.
    2. Opcionalmente, crear algunas naves iniciales por código (por ejemplo naves típicas de The Expanse) y añadirlas a la flota con agregarNave.
    3. Implementar un menú por consola que se repita hasta que el usuario elija salir (puedes usar while o do-while).

    El menú deberá tener, como mínimo, estas opciones:

    1. Añadir nave pidiendo datos por teclado
      • Llamar a crearNaveDesdeTeclado(sc).
    2. Añadir nave creada en el main
      • Crear un objeto Nave directamente en el main (con valores que tú pongas) y añadirlo a la flota con agregarNave.
    3. Listar todas las naves
      • Llamar a listarTodas().
    4. Listar naves que necesitan reparación
      • Llamar a listarNavesParaReparar().
    5. Mostrar número total de naves
      • Llamar a getNumeroNaves() y mostrar el resultado.
    6. Mostrar número total de misiles de la flota
      • Llamar a getTotalMisiles() y mostrar el resultado.
    7. Listar naves por facción
      • Pedir al usuario una facción (Tierra, Marte o Cinturon) y llamar a listarPorFaccion(faccion).
    8. Mostrar cuántas naves hay de una facción
      • Pedir al usuario una facción y llamar a contarPorFaccion(faccion).
      • Mostrar el resultado.
    9. Salir del programa.

    Cada opción debe ejecutarse según el número introducido por el usuario, y después de ejecutar la acción, el menú se volverá a mostrar hasta que el usuario elija la opción 0.


    4. Información extra The Expanse

    • La facción Tierra representa a la ONU y su flota clásica.
    • La facción Marte representa a la MCRN, con naves militares muy avanzadas.
    • La facción Cinturon representa a la OPA y a los belters, normalmente con naves reutilizadas o modificadas.

    Puedes usar nombres de naves inspirados en la serie (por ejemplo, “Rocinante”, “Donnager”, “Agatha King”, “Nauvoo”, etc.) o inventarte los tuyos, siempre dentro del contexto de la guerra entre Tierra, Marte y el Cinturón.


    Ejemplo Controlador MAIN

    Código del ControlFlota (main)

    import java.util.Scanner;
    
    public class ControlFlota {
    
        public static void main(String[] args) {
    
            Scanner sc = new Scanner(System.in);
            Flota flota = new Flota();
    
            // Naves iniciales opcionales (The Expanse vibes)
            flota.agregarNave(new Nave("Rocinante", "Cinturon", 24, false));
            flota.agregarNave(new Nave("Donnager", "Marte", 60, true));
            flota.agregarNave(new Nave("Agatha King", "Tierra", 40, true));
    
            int opcion;
    
            do {
                mostrarMenu();
                opcion = leerEnteroSeguro(sc);
    
                switch (opcion) {
    
                    case 1:
                        // Crear nave con Scanner
                        flota.crearNaveDesdeTeclado(sc);
                        break;
    
                    case 2:
                        // Crear nave desde el propio main
                        Nave n = new Nave("Nauvoo", "Cinturon", 80, false);
                        flota.agregarNave(n);
                        System.out.println("Nave creada desde main y añadida: " + n);
                        break;
    
                    case 3:
                        flota.listarTodas();
                        break;
    
                    case 4:
                        flota.listarNavesParaReparar();
                        break;
    
                    case 5:
                        System.out.println("Número total de naves: " + flota.getNumeroNaves());
                        break;
    
                    case 6:
                        System.out.println("Misiles totales en la flota: " + flota.getTotalMisiles());
                        break;
    
                    case 7:
                        System.out.print("Introduce la facción (Tierra/Marte/Cinturon): ");
                        String fac = sc.nextLine();
                        flota.listarPorFaccion(fac);
                        break;
    
                    case 8:
                        System.out.print("Introduce la facción (Tierra/Marte/Cinturon): ");
                        String fac2 = sc.nextLine();
                        int num = flota.contarPorFaccion(fac2);
                        System.out.println("Número de naves de la facción " + fac2 + ": " + num);
                        break;
    
                    case 0:
                        System.out.println("Saliendo del sistema de control de flota...");
                        break;
    
                    default:
                        System.out.println("Opción no válida.");
                }
    
                System.out.println();
    
            } while (opcion != 0);
    
            sc.close();
        }
    
        // -------------------------
        // Métodos auxiliares
        // -------------------------
    
        private static void mostrarMenu() {
            System.out.println("=== CONTROL DE FLOTA - GUERRA DEL SISTEMA SOLAR ===");
            System.out.println("1. Añadir nave (datos por teclado)");
            System.out.println("2. Añadir nave (creada en el main)");
            System.out.println("3. Listar todas las naves");
            System.out.println("4. Listar naves que necesitan reparación");
            System.out.println("5. Mostrar número total de naves");
            System.out.println("6. Mostrar número total de misiles");
            System.out.println("7. Listar naves por facción");
            System.out.println("8. Contar naves por facción");
            System.out.println("0. Salir");
            System.out.print("Elige una opción: ");
        }
    
        private static int leerEnteroSeguro(Scanner sc) {
            while (true) {
                try {
                    int valor = Integer.parseInt(sc.nextLine());
                    return valor;
                } catch (NumberFormatException e) {
                    System.out.print("Introduce un número válido: ");
                }
            }
        }
    }
    

  • R8 – El Escalafón Mágico de Hogwarts

    R8 – 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.

    Tu misión será desarrollar, en Java, un pequeño sistema que represente este escalafón utilizando herencia entre clases, donde cada rango mágico añade nuevas habilidades y comportamientos.

    ⚠️ Importante

    Usar una enumeración (enum) para representar las Casas de Hogwarts.

    En este ejercicio NO se deben usar:

    • Métodos abstractos
    • Polimorfismo como concepto principal
    • Interfaces

    Diagrama de clases

    1 – Enumeración de Casas

    Crea una enumeración llamada Casa con las siguientes opciones:

    • GRYFFINDOR
    • SLYTHERIN
    • RAVENCLAW
    • HUFFLEPUFF

    Cada mago pertenecerá a una casa, y esta afectará a sus atributos iniciales.


    2 – Clase base Mago

    Crea la clase Mago, que será la base de todos los personajes mágicos.

    Atributos

    • nombre
    • nivel (1–100)
    • vida (0–100)
    • mana (0–100)
    • casa (tipo Casa)

    Comportamiento

    • entrenar()
      Aumenta el nivel y consume algo de vida y mana.
    • descansar()
      Recupera vida y mana.
    • ficha()
      Devuelve una cadena con los datos del mago.
    • lanzarHechizo(String hechizo)
      Método general que devuelve el daño causado (por defecto, bajo o 0).

    Al crear un mago, se aplicará un bonus inicial según su casa.


    3 – Rangos mágicos (herencia)

    Aprendiz (hereda de Mago)

    Representa a los alumnos más jóvenes de Hogwarts.

    Atributo extra

    • torpeza (probabilidad de fallo)

    Métodos

    • practicarBasico() → reduce torpeza

    Comportamiento

    • Puede lanzar hechizos básicos (Lumos, Alohomora)
    • Tiene posibilidad de fallar según su torpeza

    Hechicero (hereda de Aprendiz)

    Magos con formación avanzada.

    Atributo extra

    • controlVarita

    Métodos

    • estudiarEnBiblioteca() → mejora control y nivel

    Comportamiento

    • Acceso a hechizos más potentes (Expelliarmus, Stupefy)
    • Menos fallos y más daño

    Auror (hereda de Hechicero)

    Magos especializados en combate y seguridad mágica.

    Atributos extra

    • experienciaCombate
    • arrestos

    Métodos

    • patrullar() → mejora experiencia
    • arrestar(Aprendiz objetivo) → solo si el objetivo está débil

    Comportamiento

    • Hechizos más efectivos en combate
    • Puede usar hechizos defensivos

    Profesor (hereda de Hechicero)

    Magos con rol docente en Hogwarts.

    Atributos extra

    • asignatura
    • prestigio

    Métodos

    • enseñar(Aprendiz alumno) → mejora al alumno
    • evaluar() → aumenta prestigio

    Comportamiento

    • Hechizos más controlados
    • Menor consumo de mana

    4 – Programa principal

    Crea una clase Main que:

    • Cree al menos:
      • 2 Aprendices
      • 2 Hechiceros
      • 1 Auror
      • 1 Profesor
    • Muestre la ficha de cada personaje.
    • Ejecute varias acciones:
      • Lanzar hechizos
      • Entrenar / descansar
      • Enseñar, patrullar o arrestar según el rango

    Cada variable debe declararse con su tipo concreto
    (no usar listas genéricas de Mago).




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

  • R3.1 [Solución] – Programación-bucles

    R3.1 [Solución] – Programación-bucles

    Ejercicio 1

    Contando del 1 al 10

    Crea un programa en Java que muestre por pantalla los números del 1 al 10 utilizando un bucle for.

    Requisitos:

    1. El programa debe tener una clase llamada Contador.
    2. Dentro del método main, utiliza un bucle for para recorrer los números del 1 al 10.
    3. En cada iteración, imprime el número actual en una nueva línea.

    Salida esperada:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    

    Idea clave

    Usamos un bucle for cuando conocemos de antemano cuántas veces queremos repetir algo.
    Aquí queremos mostrar los números del 1 al 10, ni más ni menos.

    Lógica

    • Inicializamos el contador en 1
    • Mientras sea menor o igual que 10
    • En cada vuelta lo incrementamos en 1

    Código solución

    
    
    
    
    
    public class Contador {
    
        public static void main(String[] args) {
    
            for (int i = 1; i <= 10; i++) {
                System.out.println(i);
            }
    
        }
    }
    

    🔍 Explicación línea a línea

    • int i = 1 → valor inicial
    • i <= 10 → condición para seguir iterando
    • i++ → incremento en cada vuelta
    • System.out.println(i) → muestra el valor actual


    Ejercicio 2

    Cuenta atrás de 100 a 0 en pasos de 10

    Crea un programa en Java que haga una cuenta atrás desde 100 hasta 0, restando 10 en cada paso.

    Cuando llegue a 0, debe mostrar el mensaje «Máquina hackeada».

    Requisitos:

    1. El programa debe tener una clase llamada CuentaAtras10.
    2. Dentro del método main, usa un bucle for que empiece en 100 y vaya disminuyendo de 10 en 10 hasta llegar a 0.
    3. En cada iteración imprime el número actual.
    4. Al finalizar, muestra el texto Máquina hackeada.

    Salida esperada:

    100
    90
    80
    70
    60
    50
    40
    30
    20
    10
    0
    Máquina hackeada
    
    

    Idea clave

    Un for también sirve para contar hacia atrás si modificamos correctamente el incremento (en este caso, decremento).

    Lógica

    • Empezamos en 100
    • Vamos restando 10 en cada iteración
    • Cuando termina el bucle, mostramos el mensaje final

    Código solución

    
    
    
    
    
    public class CuentaAtras10 {
    
        public static void main(String[] args) {
    
            for (int i = 100; i >= 0; i -= 10) {
                System.out.println(i);
            }
    
            System.out.println("Máquina hackeada");
        }
    }
    

    Detalles importantes

    • i -= 10 → resta 10 en cada vuelta
    • El mensaje no va dentro del bucle, para que se muestre solo una vez


    Ejercicio 3

    Realiza un algoritmo que simule una caja registradora de un supermercado, donde va pidiendo cantidades y las va sumando, cada vez que pongamos una cantidad por la terminal saldra la suma acumulada del total.

    El programa dejara de pedir las cantidades de compra cuando insertemos un 0.

    En ese momento mostrara el total de la compra y nos pedira que le indiquemos con cuanto dinero nos pagan e indicara el cambio que debemos devolver.

    Ejemplo de ejecución:

    Introduce la cantidad de la compra (0 para finalizar): 25.50
    Suma acumulada: 25.50
    Introduce la cantidad de la compra (0 para finalizar): 40.00
    Suma acumulada: 65.50
    Introduce la cantidad de la compra (0 para finalizar): 15.75
    Suma acumulada: 81.25
    Introduce la cantidad de la compra (0 para finalizar): 0
    Total a pagar: 81.25
    Con cuánto dinero te pagan? 100.00
    Cambio a devolver: 18.75
    

    Idea clave

    Aquí necesitamos:

    • Repetir hasta que el usuario introduzca 0
    • Ir sumando cantidades
    • Pedir un pago final y calcular el cambio

    Este es un caso perfecto para un bucle while.

    Lógica

    1. Pedimos cantidades hasta que llegue un 0
    2. Mostramos la suma acumulada
    3. Mostramos el total
    4. Pedimos el dinero entregado
    5. Calculamos el cambio

    Código solución

    
    
    
    
    
    import java.util.Scanner;
    
    public class CajaRegistradora {
    
        public static void main(String[] args) {
    
            Scanner sc = new Scanner(System.in);
            double total = 0;
            double cantidad;
    
            do {
                System.out.print("Introduce la cantidad de la compra (0 para finalizar): ");
                cantidad = sc.nextDouble();
    
                if (cantidad != 0) {
                    total += cantidad;
                    System.out.println("Suma acumulada: " + total);
                }
    
            } while (cantidad != 0); // Direfente es ( ! = )
    
            System.out.println("Total a pagar: " + total);
    
            System.out.print("Con cuánto dinero te pagan? ");
            double pago = sc.nextDouble();
    
            double cambio = pago - total;
            System.out.println("Cambio a devolver: " + cambio);
    
            sc.close();
        }
    }
    

    Conceptos trabajados

    • do-while → asegura al menos una ejecución
    • Acumulador (total)
    • Uso de double para cantidades con decimales
    • Entrada de datos con Scanner

    Ejercicio 4

    Desarrollar un programa en Java que genere un dibujo usando únicamente el carácter asterisco (*). Este ejercicio te ayudará a entender mejor el control de bucles y el diseño de patrones en la consola.

    Descripción:

    Tu tarea es crear un programa que dibuje una pirámide de asteriscos. La altura de la pirámide será determinada por el usuario al inicio del programa.

    Requisitos:

    1. El programa debe pedir al usuario que introduzca un número entero positivo, el cual representará la altura de la pirámide.
    2. Utiliza bucles for o while para generar el patrón.
    3. La pirámide debe estar alineada a la izquierda, sin utilizar espacios para centrar las líneas.

    Ejemplo de salida:

    Si el usuario ingresa 5, el programa deberá mostrar el siguiente patrón:

    *
    **
    ***
    ****
    *****
    

    Idea clave

    Cada línea imprime un número creciente de asteriscos.
    El número de líneas lo decide el usuario.

    Lógica

    • Un bucle controla las filas
    • Otro bucle imprime los * en cada fila

    Código solución

    
    
    
    
    
    import java.util.Scanner;
    
    public class Piramide {
    
        public static void main(String[] args) {
    
            Scanner sc = new Scanner(System.in);
    
            System.out.print("Introduce la altura de la pirámide: ");
            int altura = sc.nextInt();
    
            for (int i = 1; i <= altura; i++) {
                for (int j = 1; j <= i; j++) {
                    System.out.print("*");
                }
                System.out.println();
            }
    
            sc.close();
        }
    }
    

    Si i = 3, el bucle interno imprime:

    
    
    
    
    
    ***
    

    Cada fila tiene tantos asteriscos como el número de la fila.


    Ejercicio 5

    Crea un programa en Java que muestre el siguiente patrón usando dos bucles for anidados:

    Salida esperada:

    54321
    4321
    321
    21
    1
    
    

    Requisitos:

    1. Crea una clase llamada PatronDel5al1.
    2. Dentro del método main, utiliza un primer bucle for para controlar las filas, que empiece en 5 y termine en 1.
    3. Dentro de ese bucle, usa otro for que empiece en el valor de la fila y baje hasta 1.
    4. Después del bucle interno, imprime un salto de línea (System.out.println()).

    Idea clave

    Este ejercicio refuerza el concepto de bucles anidados descendentes.

    Lógica

    • El bucle externo controla las filas (de 5 a 1)
    • El bucle interno imprime los números desde la fila hasta 1

    Código solución

    
    
    
    
    
    public class PatronDel5al1 {
    
        public static void main(String[] args) {
    
            for (int i = 5; i >= 1; i--) {
                for (int j = i; j >= 1; j--) {
                    System.out.print(j);
                }
                System.out.println();
            }
    
        }
    }
    

    Qué ocurre aquí

    Y así hasta llegar a 1

    Primera fila: i = 5 → imprime 54321

    Segunda fila: i = 4 → imprime 4321

  • R3.2 [Solución] Programación-Bucles

    R3.2 [Solución] Programación-Bucles

    Walter White y Jesse Pinkman han cocinado 1000 pastillas de «mentaanfetamienta» en su caravana.

    Cada pastilla tiene una calidad aleatoria entre 0 y 100.

    El “señor del pollo” (Gus Fring) solo acepta aquellas con calidad mayor o igual que 90.

    Base común: generar calidad 0–100

    Usaremos:

    
    
    
    
    
    Random r = new Random(); int calidad = r.nextInt(101); // 0..100 (incluye 100)

    RETO 1: “El recuento del lote”

    Calcular cuántas pastillas tienen una calidad superior a 90.

    Pistas:

    • Usa Random para generar números del 0 al 100.
    • Usa un for para simular las 1000 pastillas.
    • Usa un contador para las “buenas”.

    Ejemplo de salida:

    Pastillas totales: 1000
    De calidad superior: 128

    Objetivo: de 1000 pastillas, contar cuántas tienen calidad > 90 (ojo: tu historia dice “≥90”, pero el reto 1 pide “superior a 90” → >90).

    Idea

    • Bucle for de 1 a 1000
    • Generar calidad
    • Si calidad > 90, incrementamos contador

    Código

    
    
    
    
    
    import java.util.Random;
    
    public class Reto1RecuentoLote {
        public static void main(String[] args) {
            Random r = new Random();
    
            int total = 1000;
            int buenas = 0;
    
            for (int i = 1; i <= total; i++) {
                int calidad = r.nextInt(101); // 0..100
    
                if (calidad > 90) {           // superior a 90
                    buenas++;
                }
            }
    
            System.out.println("Pastillas totales: " + total);
            System.out.println("De calidad superior: " + buenas);
        }
    }
    

    RETO 2: “Hasta lograr la receta perfecta”

    Walter repite la cocción hasta que consiga una pastilla con calidad 100.

    Pistas:

    • Genera números aleatorios entre 0 y 100.
    • Detén el bucle cuando salga 100.
    • Muestra cuántos intentos necesitó.

    Ejemplo:

    ¡Perfecta! Calidad 100 obtenida tras 57 intentos.

    Objetivo: repetir intentos hasta que salga calidad 100, mostrar cuántos intentos.

    Idea

    • Bucle while o do-while
    • Contador de intentos
    • Se para cuando calidad == 100

    Código

    
    
    
    
    
    import java.util.Random;
    
    public class Reto2RecetaPerfecta {
        public static void main(String[] args) {
            Random r = new Random();
    
            int intentos = 0;
            int calidad;
    
            do {
                calidad = r.nextInt(101);
                intentos++;
            } while (calidad != 100);
    
            System.out.println("¡Perfecta! Calidad 100 obtenida tras " + intentos + " intentos.");
        }
    }
    

    Por qué do-while: garantiza al menos un intento (tiene sentido aquí).


    RETO 3: “La producción nocturna”

    Walter y Jesse producen pastillas hasta llegar a 1000 unidades buenas (≥90).

    Muestra cuántas totales han hecho (incluyendo las malas).

    Ejemplo:

    Para conseguir 1000 pastillas buenas, produjeron un total de 8732.
    
    

    Objetivo: producir hasta conseguir 1000 buenas (≥90), y mostrar cuántas totales se produjeron.

    Idea

    • buenas llega a 1000
    • totales cuenta todas, buenas y malas
    • Condición buena: calidad >= 90

    Código

    
    
    
    
    
    import java.util.Random;
    
    public class Reto3ProduccionNocturna {
        public static void main(String[] args) {
            Random r = new Random();
    
            int buenas = 0;
            int totales = 0;
    
            while (buenas < 1000) {
                int calidad = r.nextInt(101);
                totales++;
    
                if (calidad >= 90) {
                    buenas++;
                }
            }
    
            System.out.println("Para conseguir 1000 pastillas buenas, produjeron un total de " + totales + ".");
        }
    }
    

    RETO 4: “El análisis por lotes”

    Dividir la producción en 10 lotes de 100 pastillas y mostrar cuántas buenas hay en cada lote.

    Salida esperada:

    Lote 1 → 13 pastillas buenas
    Lote 2 → 8 pastillas buenas
    ...
    Lote 10 → 11 pastillas buenas
    
    

    Objetivo: 10 lotes de 100, contar buenas (≥90) por lote.

    Idea

    • Bucle externo: lotes 1..10
    • Bucle interno: 100 pastillas
    • Contador buenasLote reiniciado en cada lote

    Código

    
    
    
    
    
    import java.util.Random;
    
    public class Reto4AnalisisPorLotes {
        public static void main(String[] args) {
            Random r = new Random();
    
            int lotes = 10;
            int tamLote = 100;
    
            for (int lote = 1; lote <= lotes; lote++) {
                int buenasLote = 0;
    
                for (int i = 1; i <= tamLote; i++) {
                    int calidad = r.nextInt(101);
                    if (calidad >= 90) {
                        buenasLote++;
                    }
                }
    
                System.out.println("Lote " + lote + " \u2192 " + buenasLote + " pastillas buenas");
            }
        }
    }
    

    RETO 5: “Promedio de calidad”

    Calcular el promedio total de calidad de las 1000 pastillas.

    Salida:

    Promedio de calidad general: 72.38
    
    

    Objetivo: promedio de calidad de 1000 pastillas.

    Idea

    • Acumulador sumaCalidades
    • Tras el bucle: promedio = suma / total
    • Para mostrar con 2 decimales: System.out.printf

    Código

    
    
    
    
    
    import java.util.Random;
    
    public class Reto5PromedioCalidad {
        public static void main(String[] args) {
            Random r = new Random();
    
            int total = 1000;
            int sumaCalidades = 0;
    
            for (int i = 1; i <= total; i++) {
                int calidad = r.nextInt(101);
                sumaCalidades += calidad;
            }
    
            double promedio = (double) sumaCalidades / total;
    
            System.out.printf("Promedio de calidad general: %.2f%n", promedio);
        }
    }
    

    Detalle clave: el casteo a double evita división entera.

    RETO 6 (extra): “La inspección de Gus Fring”

    Genera pastillas hasta tener 500 buenas, pero cada vez que salgan 50 malas seguidas, muestra:

    🔥 Gus Fring sospecha del laboratorio...
    
    

    Objetivo: generar pastillas hasta tener 500 buenas (≥90), pero cada vez que haya 50 malas seguidas, imprimir:

    Gus Fring sospecha del laboratorio...

    Idea

    • Contador buenas hasta 500
    • Contador malasSeguidas que:
      • aumenta si sale mala (<90)
      • se reinicia a 0 si sale buena
    • Cuando malasSeguidas == 50, avisamos y reiniciamos (o seguimos contando, tú eliges; aquí reiniciamos para no spamear).

    Código

    import java.util.Random;
    
    public class Reto6InspeccionGusFring {
        public static void main(String[] args) {
            Random r = new Random();
    
            int buenas = 0;
            int totales = 0;
            int malasSeguidas = 0;
    
            while (buenas < 500) {
                int calidad = r.nextInt(101);
                totales++;
    
                if (calidad >= 90) {
                    buenas++;
                    malasSeguidas = 0; // racha de malas se rompe
                } else {
                    malasSeguidas++;
                }
    
                if (malasSeguidas == 50) {
                    System.out.println("🔥 Gus Fring sospecha del laboratorio...");
                    malasSeguidas = 0; // reiniciamos para volver a detectar otra racha de 50
                }
            }
    
            System.out.println("Buenas conseguidas: " + buenas);
            System.out.println("Pastillas producidas en total: " + totales);
        }
    }
    
    
  • R3.3 [Solución]- Arrays – Bienvenido al mundo de Mr. Robot

    R3.3 [Solución]- Arrays – Bienvenido al mundo de Mr. Robot

    Evil Corp —el gigante que controla todo lo que consumes, lo que piensas y hasta lo que olvidas— cree tenerlo todo bajo control.

    Pero no contaba contigo.

    En un sótano lleno de cables, pantallas parpadeantes y tazas de café frías, un grupo de hackers conocidos como Atriumsociety prepara su próximo golpe digital. Su misión: devolver el poder a la gente, línea de código a línea de código.

    Tu papel comienza aquí.

    Eres parte del equipo. Hoy no vienes a “hacer ejercicios”, vienes a infiltrarte en el sistema, Cada array que declares es una base de datos secreta, cada bucle un intento de intrusión, cada condicional una decisión que puede cambiarlo todo.

    Prepárate: el prompt está parpadeando.

    Atriumsociety te necesita.

    1. “Elliot vs Evil Corp”

    Enunciado:

    Elliot ha interceptado una lista con los balances de Evil Corp.

    Con el siguiente array:

    int[] balances = {12000, -50, 5000, 15000, 0, -300, 800, 22000, 9999, 100};
    
    

    Muestra:

    • Cuántas cuentas tienen saldo negativo.
    • Cuántas tienen saldo superior a 10 000 $.
    • El saldo medio.

    Qué se hace

    • Recorremos balances
    • Contamos negativos (<0)
    • Contamos los que superan 10.000 (>10000)
    • Sumamos todo para el promedio

    Código

    public class ElliotVsEvilCorp {
        public static void main(String[] args) {
    
            int[] balances = {12000, -50, 5000, 15000, 0, -300, 800, 22000, 9999, 100};
    
            int negativos = 0;
            int masDe10000 = 0;
            int suma = 0;
    
            for (int i = 0; i < balances.length; i++) {
                int b = balances[i];
    
                if (b < 0) negativos++;
                if (b > 10000) masDe10000++;
    
                suma += b;
            }
    
            double media = (double) suma / balances.length;
    
            System.out.println("Cuentas con saldo negativo: " + negativos);
            System.out.println("Cuentas con saldo superior a 10000$: " + masDe10000);
            System.out.printf("Saldo medio: %.2f%n", media);
        }
    }
    

    2. “Backdoor Scanner”

    Enunciado:

    Darlene analiza si los servidores de Evil Corp están vulnerables.

    Usa el siguiente array:

    boolean[] vulnerables = {false, true, false, false, true, false, true, false};
    
    

    Muestra:

    • Cuántos servidores son vulnerables (true).
    • En qué posiciones están.

    Qué se hace

    • Recorremos el array de boolean
    • Si es true, contamos y mostramos la posición

    Código

    
    
    
    
    
    public class BackdoorScanner {
        public static void main(String[] args) {
    
            boolean[] vulnerables = {false, true, false, false, true, false, true, false};
    
            int contador = 0;
    
            System.out.print("Servidores vulnerables en posiciones: ");
            for (int i = 0; i < vulnerables.length; i++) {
                if (vulnerables[i]) {
                    contador++;
                    System.out.print(i + " ");
                }
            }
    
            System.out.println();
            System.out.println("Total vulnerables: " + contador);
        }
    }
    


    3. “Password Audit”

    Enunciado:

    Elliot encuentra las siguientes contraseñas antiguas de Evil Corp:

    String[] passwords = {"admin123", "e@mpl3", "supersecure99", "pass", "root@2020"};
    
    

    Indica:

    • Cuántas contienen el carácter @.
    • Cuántas tienen más de 8 caracteres.
    • Cuántas terminan en un número.
    • Cuál es la más larga.

    Qué se hace

    Para cada contraseña:

    • contains("@") → cuenta las que tienen @
    • length() > 8 → largas
    • “termina en número” → miramos el último carácter con charAt y comprobamos si es dígito
    • “la más larga” → guardamos la que tenga mayor longitud

    Código

    
    
    
    
    
    public class PasswordAudit {
        public static void main(String[] args) {
    
            String[] passwords = {"admin123", "e@mpl3", "supersecure99", "pass", "root@2020"};
    
            int conArroba = 0;
            int masDe8 = 0;
            int terminanEnNumero = 0;
    
            String masLarga = passwords[0];
    
            for (int i = 0; i < passwords.length; i++) {
                String p = passwords[i];
    
                if (p.contains("@")) conArroba++;
                if (p.length() > 8) masDe8++;
    
                char ultimo = p.charAt(p.length() - 1);
                if (Character.isDigit(ultimo)) terminanEnNumero++;
    
                if (p.length() > masLarga.length()) {
                    masLarga = p;
                }
            }
    
            System.out.println("Contraseñas con @: " + conArroba);
            System.out.println("Contraseñas con más de 8 caracteres: " + masDe8);
            System.out.println("Contraseñas que terminan en número: " + terminanEnNumero);
            System.out.println("La más larga es: " + masLarga + " (" + masLarga.length() + " caracteres)");
        }
    }
    


    4. “Hack the Gibson”

    Enunciado:

    Simula los niveles de éxito de los ataques de Elliot con el siguiente array:

    int[] intentos = {82, 13, 45, 79, 90, 5, 33, 72, 18, 65, 40, 100, 29, 10, 67, 55, 88, 60, 14, 3};
    
    

    Muestra:

    • El valor máximo y el mínimo.
    • Cuántos intentos fueron “exitosos” (más de 70).
    • Si todos son menores de 30, muestra “Mr Robot sospecha que estás desconectado”.

    Qué se hace

    • Máximo y mínimo: inicializamos con el primer elemento y comparamos
    • Exitosos: >70
    • “si todos son menores de 30”: bandera todosMenor30 (true al inicio, se rompe si encontramos uno >= 30)

    Código

    
    
    
    
    
    public class HackTheGibson {
        public static void main(String[] args) {
    
            int[] intentos = {82, 13, 45, 79, 90, 5, 33, 72, 18, 65, 40, 100, 29, 10, 67, 55, 88, 60, 14, 3};
    
            int max = intentos[0];
            int min = intentos[0];
            int exitosos = 0;
    
            boolean todosMenor30 = true;
    
            for (int i = 0; i < intentos.length; i++) {
                int v = intentos[i];
    
                if (v > max) max = v;
                if (v < min) min = v;
    
                if (v > 70) exitosos++;
    
                if (v >= 30) {
                    todosMenor30 = false;
                }
            }
    
            System.out.println("Máximo: " + max);
            System.out.println("Mínimo: " + min);
            System.out.println("Intentos exitosos (>70): " + exitosos);
    
            if (todosMenor30) {
                System.out.println("Mr Robot sospecha que estás desconectado");
            }
        }
    }
    


    5. “Logs del Servidor”

    Enunciado:

    Angela revisa los registros de acceso de un servidor:

    String[] logs = {"OK", "ERROR", "OK", "OK", "ERROR", "OK", "ERROR", "ERROR", "OK", "OK"};
    
    

    Cuenta:

    • Cuántos accesos fueron "OK".
    • Cuántos "ERROR".
    • Si hay más errores que accesos correctos, muestra “Servidor en peligro”.

    Qué se hace

    • Contar "OK" y "ERROR" usando equals
    • Comparar: si errores > ok → aviso

    Código

    
    
    
    
    
    public class LogsDelServidor {
        public static void main(String[] args) {
    
            String[] logs = {"OK", "ERROR", "OK", "OK", "ERROR", "OK", "ERROR", "ERROR", "OK", "OK"};
    
            int ok = 0;
            int error = 0;
    
            for (int i = 0; i < logs.length; i++) {
                if (logs[i].equals("OK")) ok++;
                else if (logs[i].equals("ERROR")) error++;
            }
    
            System.out.println("OK: " + ok);
            System.out.println("ERROR: " + error);
    
            if (error > ok) {
                System.out.println("Servidor en peligro");
            }
        }
    }
    


    6. “Temperaturas del Servidor”

    Enunciado:

    Durante un ataque, Elliot monitoriza las temperaturas del servidor:

    int[] temperaturas = {45, 49, 52, 57, 60, 59, 55, 48, 46, 50};
    
    

    Calcula:

    • La temperatura media.
    • Cuántas mediciones superan los 55 °C.
    • Si todas las temperaturas están por debajo de 60 °C, muestra “Sistema estable”; si alguna es 60 o más, muestra “¡Sobrecalentamiento detectado!”.

    Qué se hace

    • Media: suma / total
    • Contar >55
    • Comprobar si alguna es >=60 (bandera sobrecalentamiento)

    Código

    
    
    
    
    
    public class TemperaturasServidor {
        public static void main(String[] args) {
    
            int[] temperaturas = {45, 49, 52, 57, 60, 59, 55, 48, 46, 50};
    
            int suma = 0;
            int superan55 = 0;
    
            boolean sobrecalentamiento = false;
    
            for (int i = 0; i < temperaturas.length; i++) {
                int t = temperaturas[i];
    
                suma += t;
    
                if (t > 55) superan55++;
                if (t >= 60) sobrecalentamiento = true;
            }
    
            double media = (double) suma / temperaturas.length;
    
            System.out.printf("Temperatura media: %.2f%n", media);
            System.out.println("Mediciones que superan 55°C: " + superan55);
    
            if (!sobrecalentamiento) {
                System.out.println("Sistema estable");
            } else {
                System.out.println("¡Sobrecalentamiento detectado!");
            }
        }
    }
    


    7. “DDoS Simulator”

    Enunciado:

    Durante un ataque DDoS, se registran los siguientes tiempos de respuesta (en ms):

    int[] tiempos = {200, 540, 720, 150, 950, 330, 480, 510, 870, 610};
    
    

    Muestra:

    • La media, el máximo y el mínimo.
    • Si más del 60 % supera los 500 ms, muestra “Servidor saturado”.
    • Si menos del 10 % supera los 200 ms, muestra “Ataque fallido”.

    Qué se hace

    • Media, máximo, mínimo (igual que antes)
    • Contar cuántos superan 500 → para porcentaje
    • Si (superan500 / total) > 0.60 → “Servidor saturado”
    • Si (superan200 / total) < 0.10 → “Ataque fallido”
      • Nota: “supera 200” significa >200, no >=200.

    Código

    
    
    
    
    
    public class DDoSSimulator {
        public static void main(String[] args) {
    
            int[] tiempos = {200, 540, 720, 150, 950, 330, 480, 510, 870, 610};
    
            int suma = 0;
            int max = tiempos[0];
            int min = tiempos[0];
    
            int superan500 = 0;
            int superan200 = 0;
    
            for (int i = 0; i < tiempos.length; i++) {
                int t = tiempos[i];
    
                suma += t;
                if (t > max) max = t;
                if (t < min) min = t;
    
                if (t > 500) superan500++;
                if (t > 200) superan200++;
            }
    
            double media = (double) suma / tiempos.length;
    
            double porcentaje500 = (double) superan500 / tiempos.length;
            double porcentaje200 = (double) superan200 / tiempos.length;
    
            System.out.printf("Media: %.2f ms%n", media);
            System.out.println("Máximo: " + max + " ms");
            System.out.println("Mínimo: " + min + " ms");
    
            if (porcentaje500 > 0.60) {
                System.out.println("Servidor saturado");
            }
    
            if (porcentaje200 < 0.10) {
                System.out.println("Ataque fallido");
            }
        }
    }
    
  • R8 [Solución]- El Escalafón Mágico de Hogwarts

    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.

  • Obsidian para documentar proyectos en Markdown

    Obsidian para documentar proyectos en Markdown

    1. Introducción

    En este tema aprenderás a utilizar Obsidian para crear y organizar documentación en formato Markdown. Los documentos se guardarán en una carpeta local y, posteriormente, podrán incorporarse a un repositorio de GitHub.

    El objetivo no es utilizar Obsidian como una nube ni como sustituto de GitHub. Obsidian será el editor de documentación y GitHub será el lugar donde se almacenen y entreguen los proyectos.

    2. ¿Qué es Obsidian?

    Obsidian es una aplicación para escribir y organizar notas mediante archivos Markdown. Cada nota es un archivo de texto con extensión .md y puede abrirse también con Visual Studio Code, cualquier editor de texto o directamente desde GitHub.

    Una de sus características principales es que los archivos se guardan en una carpeta normal del ordenador. Obsidian denomina bóveda o vault a esa carpeta.

    Con Obsidian podemos:

    • Crear archivos Markdown sin memorizar inicialmente toda la sintaxis.
    • Organizar documentos mediante carpetas.
    • Insertar imágenes, enlaces, listas, tablas y bloques de código.
    • Relacionar documentos entre sí.
    • Ver el resultado mientras escribimos.
    • Buscar rápidamente en toda la documentación.
    • Mantener una colección de apuntes o proyectos en archivos locales.

    Importante: una bóveda no es un formato especial ni un archivo comprimido. Es una carpeta normal que contiene documentos y recursos.

    3. Obsidian, Git y GitHub no son lo mismo

    HerramientaFunción
    ObsidianCrear, editar y organizar los documentos Markdown.
    GitRegistrar el historial de cambios del proyecto.
    GitHubAlojar el repositorio y permitir su entrega o revisión.

    El flujo de trabajo que utilizaremos será:

    Escribir en Obsidian → Guardar en la carpeta local → Confirmar con Git → Subir a GitHub

    4. Descargar Obsidian

    La descarga debe realizarse desde la página oficial:

    Descargar Obsidian

    Obsidian está disponible para Windows, macOS, Linux, Android, iPhone y iPad. En este curso utilizaremos principalmente la versión de escritorio.

    5. Instalación en Windows

    1. Accede a la página oficial de descarga.
    2. Localiza el apartado Windows.
    3. Pulsa Universal.
    4. Espera a que termine la descarga.
    5. Abre el archivo de instalación descargado.
    6. Sigue los pasos mostrados por el instalador.
    7. Abre Obsidian desde el menú Inicio.

    Si Windows muestra una advertencia, comprueba que el instalador procede del dominio oficial obsidian.md.

    6. Instalación en macOS

    1. Accede a la página oficial de descarga.
    2. Localiza el apartado Mac.
    3. Pulsa Universal.
    4. Abre el archivo descargado.
    5. Arrastra el icono de Obsidian hasta la carpeta Aplicaciones.
    6. Abre la carpeta Aplicaciones y ejecuta Obsidian.

    La primera vez, macOS puede pedir confirmación para abrir una aplicación descargada de Internet.

    7. Instalación en Linux

    La web oficial ofrece diferentes formatos: AppImage, Snap y paquete DEB. También aparece una versión Flatpak mantenida por la comunidad.

    Opción A: paquete DEB

    Esta opción resulta adecuada para Ubuntu, Debian y distribuciones derivadas.

    1. Descarga el paquete Deb desde la web oficial.
    2. Abre una terminal en la carpeta de descargas.
    3. Ejecuta:
    sudo apt install ./obsidian_*.deb
    1. Abre Obsidian desde el menú de aplicaciones.

    Opción B: AppImage

    1. Descarga el archivo AppImage.
    2. Abre una terminal en la carpeta donde se encuentre.
    3. Concede permiso de ejecución:
    chmod u+x Obsidian-*.AppImage
    1. Ejecuta la aplicación:
    ./Obsidian-*.AppImage

    Opción C: Flatpak

    Si el sistema ya tiene Flatpak y Flathub configurados:

    flatpak install flathub md.obsidian.Obsidian

    Para abrirlo desde la terminal:

    flatpak run md.obsidian.Obsidian

    8. Crear una bóveda local

    Al abrir Obsidian por primera vez aparecerá la pantalla de gestión de bóvedas.

    1. Pulsa Create junto a Create new vault.
    2. En Vault name, escribe:
    Documentacion-ASIR
    1. Pulsa Browse.
    2. Selecciona una ubicación fácil de encontrar, por ejemplo Documentos.
    3. Pulsa Create.

    Obsidian creará una carpeta llamada Documentacion-ASIR. Todos los documentos de la bóveda se guardarán físicamente dentro de ella.

    Una ubicación posible en Windows sería:

    C:\Users\alumno\Documents\Documentacion-ASIR

    En macOS o Linux podría ser:

    ~/Documentos/Documentacion-ASIR

    No crees la bóveda en una carpeta temporal, en Descargas ni dentro de otra bóveda.

    9. Crear la estructura inicial

    Dentro de la bóveda crea estas carpetas desde el explorador lateral de Obsidian:

    Documentacion-ASIR/
    ├── 00-Plantillas/
    ├── 01-Actividades/
    ├── 02-Proyectos/
    ├── 03-Apuntes/
    └── img/

    Su finalidad es:

    • 00-Plantillas: modelos reutilizables de documentos.
    • 01-Actividades: actividades breves realizadas durante el curso.
    • 02-Proyectos: proyectos de mayor tamaño.
    • 03-Apuntes: notas personales del alumno.
    • img: imágenes y capturas utilizadas en la documentación.

    10. Crear el primer README

    1. Selecciona la carpeta 01-Actividades.
    2. Crea una carpeta denominada actividad-01-markdown.
    3. Dentro de ella, crea una nota nueva.
    4. Cambia su nombre a README.

    Obsidian añadirá automáticamente la extensión y guardará el archivo como:

    README.md

    Escribe este contenido:

    # Actividad 1: introducción a Markdown
    
    ## Descripción
    
    Esta es mi primera documentación escrita con **Obsidian**.
    
    ## Objetivos
    
    - Aprender a crear un README.
    - Organizar una actividad.
    - Insertar imágenes.
    - Preparar el documento para GitHub.
    
    ## Conclusión
    
    En esta actividad he aprendido a crear y visualizar un documento Markdown.

    11. Modos de edición y lectura

    Obsidian permite trabajar principalmente con dos vistas:

    • Vista de edición: permite modificar el documento.
    • Vista de lectura: muestra el documento renderizado.

    También dispone de Live Preview, que presenta gran parte del formato mientras se escribe.

    Utiliza la vista de lectura antes de entregar una actividad para comprobar:

    • Que los títulos tienen la jerarquía correcta.
    • Que las listas aparecen correctamente.
    • Que las imágenes se muestran.
    • Que no existen enlaces rotos.
    • Que los bloques de código son legibles.

    12. Configuración recomendada para trabajar con GitHub

    Obsidian incorpora funciones propias que no siempre funcionan igual fuera de la aplicación. Para mantener la compatibilidad con GitHub, realiza esta configuración.

    12.1 Utilizar enlaces Markdown

    1. Abre Settings.
    2. Entra en Files and links.
    3. Desactiva Use [[Wikilinks]].

    Obsidian utilizará enlaces estándar:

    [Nombre del documento](otro-documento.md)

    En lugar de:

    [[otro-documento]]

    Los dos formatos funcionan en Obsidian, pero el primero ofrece mejor compatibilidad con otros editores y repositorios.

    12.2 Actualizar enlaces automáticamente

    En Settings → Files and links, mantén activada la actualización automática de enlaces internos. De esta manera, si renombras un documento desde Obsidian, sus referencias se actualizarán.

    12.3 Elegir dónde se guardan las imágenes

    1. Abre Settings.
    2. Entra en Files and links.
    3. Busca Default location for new attachments.
    4. Selecciona In subfolder under current folder.
    5. Escribe:
    img

    Así, cada actividad podrá conservar sus capturas junto al README:

    actividad-01-markdown/
    ├── README.md
    └── img/
        ├── paso-01.png
        └── resultado-final.png

    13. Insertar imágenes

    Puedes arrastrar una imagen desde el explorador de archivos hasta el documento. Obsidian la copiará a la ubicación configurada y la insertará en la nota.

    Para GitHub es recomendable que el resultado utilice esta sintaxis:

    ![Descripción de la captura](img/paso-01.png)

    Buenas prácticas:

    • Usa nombres descriptivos.
    • Evita espacios, tildes y caracteres especiales en los nombres.
    • Recorta las capturas para mostrar solo lo necesario.
    • Añade una explicación antes o después de cada captura.
    • No muestres contraseñas, tokens, datos personales ni direcciones privadas sensibles.
    • Comprueba la imagen tanto en Obsidian como en GitHub.

    14. Enlaces internos y externos

    Enlace a otro documento

    [Consultar instalación](instalacion.md)

    Enlace a una sección del mismo documento

    [Ir a las conclusiones](#conclusiones)

    Enlace externo

    [Documentación oficial de Obsidian](https://help.obsidian.md)

    Evita depender de enlaces especiales exclusivos de Obsidian si el documento debe visualizarse en GitHub.

    15. Bloques de código y comandos

    Para escribir un comando dentro de una frase utiliza una comilla invertida:

    Ejecuta el comando `git status`.

    Para escribir varios comandos utiliza un bloque e indica el lenguaje:

    ```bash
    git add .
    git commit -m "Completa la documentación"
    git push
    ```

    Ejemplo con Java:

    ```java
    public class Principal {
        public static void main(String[] args) {
            System.out.println("Hola");
        }
    }
    ```

    16. Diagramas Mermaid

    Obsidian puede representar diagramas Mermaid escritos dentro del propio documento.

    ```mermaid
    flowchart TD
        A[Crear documentación] --> B[Revisar en Obsidian]
        B --> C[Guardar cambios con Git]
        C --> D[Subir a GitHub]
    ```

    Antes de entregar, comprueba que el diagrama también se representa correctamente en GitHub.

    17. Plantilla básica para las actividades

    Guarda en 00-Plantillas un archivo llamado plantilla-actividad.md:

    # Título de la actividad
    
    ## Información
    
    - **Alumno:**
    - **Asignatura:**
    - **Fecha:**
    
    ## Descripción
    
    Explicación breve de la actividad.
    
    ## Objetivos
    
    - Objetivo 1.
    - Objetivo 2.
    
    ## Desarrollo
    
    ### Paso 1
    
    Explicación del paso.
    
    ![Descripción de la captura](img/paso-01.png)
    
    ## Problemas encontrados
    
    Describe los problemas y las soluciones aplicadas.
    
    ## Resultado
    
    Explica cómo has comprobado el funcionamiento.
    
    ## Conclusiones
    
    Resume lo aprendido con tus propias palabras.
    
    ## Referencias
    
    - [Documentación consultada](https://ejemplo.com)

    18. Llevar una actividad a GitHub

    La carpeta que se abra como repositorio debe contener directamente el proyecto y su documentación:

    mi-proyecto/
    ├── README.md
    ├── img/
    ├── src/
    └── otros-archivos-del-proyecto

    Puedes abrir esa misma carpeta como bóveda de Obsidian mediante Open folder as vault. Así editas el README directamente dentro del repositorio y evitas mantener dos copias diferentes.

    Cuando termines de documentar:

    git status
    git add README.md img/
    git commit -m "Añade la documentación de la actividad"
    git push

    Después, abre el repositorio en GitHub y comprueba visualmente el README.

    19. ¿Subir la configuración de Obsidian al repositorio?

    Obsidian crea una carpeta oculta denominada .obsidian con la configuración de la bóveda.

    Para actividades individuales normalmente no es necesario subirla. Puedes añadirla a .gitignore:

    .obsidian/

    Esto mantiene el repositorio centrado en la documentación y en los archivos del proyecto. Si un equipo necesita compartir una configuración concreta, deberá decidirlo expresamente.

    20. Plugins y temas

    Obsidian permite instalar temas y plugins, pero no son necesarios para crear buenos README.

    Durante las primeras actividades se recomienda:

    • Utilizar el tema predeterminado.
    • No instalar plugins comunitarios.
    • Aprender primero Markdown estándar.
    • Introducir nuevas funciones únicamente cuando resuelvan una necesidad real.

    Los plugins añaden complejidad y algunos generan contenido que no funciona correctamente fuera de Obsidian.

    21. Copias de seguridad y sincronización

    Que los archivos estén en el ordenador no significa que tengan una copia de seguridad.

    Recomendaciones:

    • Utiliza Git y GitHub para los proyectos del curso.
    • Realiza commits frecuentes y con mensajes descriptivos.
    • No confundas sincronización con copia de seguridad.
    • No guardes contraseñas ni secretos en la bóveda.
    • Comprueba qué archivos vas a subir mediante git status.

    Obsidian ofrece servicios opcionales de sincronización y publicación, pero no son necesarios para seguir este tema ni para entregar las actividades en GitHub.

    22. Consejos de organización

    • Utiliza un solo # para el título principal.
    • Organiza los apartados con ## y ###.
    • Mantén nombres coherentes: actividad-01, actividad-02, etc.
    • Evita nombres como final, final-bueno o final-definitivo-2.
    • Escribe primero la explicación y añade después la evidencia.
    • No conviertas el README en una colección de capturas.
    • Incluye siempre una sección de comprobación o resultado.
    • Explica los errores relevantes y cómo los resolviste.
    • Cita las fuentes consultadas.
    • Revisa el documento en GitHub antes de entregar.

    23. Errores frecuentes

    La imagen se ve en Obsidian, pero no en GitHub

    Comprueba que la imagen se encuentre dentro del repositorio y que la ruta respete exactamente mayúsculas y minúsculas:

    ![Resultado](img/resultado.png)

    El enlace funciona en Obsidian, pero no en GitHub

    Es posible que se haya utilizado un Wikilink. Sustitúyelo por un enlace Markdown estándar.

    El README aparece como texto sin formato

    Comprueba que se llama exactamente README.md y no README.md.txt.

    Hay demasiadas imágenes en la raíz

    Configura la ubicación de adjuntos y almacénalos en una carpeta img.

    Obsidian no muestra una bóveda anterior

    Selecciona Open folder as vault y elige la carpeta que contiene los archivos Markdown.

    24. Lista de comprobación antes de entregar

    • El archivo principal se llama README.md.
    • El README está dentro del repositorio correcto.
    • Las imágenes están dentro del repositorio.
    • Las rutas de las imágenes son relativas.
    • Los enlaces funcionan en GitHub.
    • Los títulos tienen una jerarquía lógica.
    • Los comandos están dentro de bloques de código.
    • No aparecen contraseñas, tokens ni datos sensibles.
    • La carpeta .obsidian está excluida si no es necesaria.
    • Se ha realizado un commit antes de la entrega.
    • Se ha comprobado la presentación final en GitHub.

    25. Actividad propuesta

    1. Instala Obsidian.
    2. Crea una bóveda local denominada Documentacion-ASIR.
    3. Crea la estructura de carpetas indicada en este manual.
    4. Configura los enlaces Markdown y la carpeta de imágenes.
    5. Crea un archivo README.md utilizando la plantilla básica.
    6. Inserta una captura en la carpeta img.
    7. Añade una lista, un enlace y un bloque de código.
    8. Revisa el documento en vista de lectura.
    9. Abre la carpeta con Visual Studio Code y comprueba que el archivo continúa siendo editable.
    10. Sube el resultado al repositorio indicado por el profesor.

    26. Referencias oficiales

  • 2.1 – Usuarios, permisos y el impostor legal sudo

    2.1 – Usuarios, permisos y el impostor legal sudo

    Un sistema operativo multiusuario es como un edificio donde vive mucha gente. Cada persona tiene su habitación y sus cosas. Sin permisos, cualquiera podría:

    • Abrirse la nevera de otro.
    • Pagar las facturas del vecino (aunque le convenga).
    • Borrar carpetas del sistema “sin querer”.

    Linux nació para compartir la casa de forma civilizada.

    [La regla de oro]
    El usuario normal puede romper su habitación; el superusuario puede romper el edificio entero.

    ¿Qué es el superusuario (root)?

    Root es ese administrador que tiene todas las llaves, incluidas las que abren las puertas del sistema. Puede instalar programas, cambiar configuraciones, borrar sistemas de archivos y mirar cualquier carpeta.


    ¿Qué es sudo realmente?

    “Substitute User DO” (o “superuser do”).
    Traducción humana: Haz esto como si yo fuera el administrador.

    Sudo no da poderes mágicos. Solo te presta el disfraz de root por unos segundos, si el sistema confía en ti, según lo que aparezca en el archivo /etc/sudoers.

    Además:

    • Sudo pide contraseña porque necesita asegurarse de que eres tú.
    • Guarda un registro de lo que haces (auditoría)
    • Puede limitar qué comandos exactos puede usar cada usuario.

    Ese toque de control es lo que hace que sudo sea más seguro que loguearse directamente como root.


    Explicación práctica del archivo /etc/sudoers

    En el documento sudoers podemos dar permisos para utilizar todos los comando o algunos en concreto.

    usuario maquina=(usuario_objetivo) comandos

    Ejemplo clásico:

    antonio ALL=(ALL:ALL) ALL

    Traducción informal: Antonio puede usar sudo para hacer cualquier cosa, desde cualquier sitio, como cualquier usuario.

    Significado de ALL=(ALL:ALL) ALL

    Piensa en cada ALL como una variable diferente en una cadena de permisos.
    El archivo /etc/sudoers sigue esta estructura general:

    usuario hosts = (usuarios_objetivo : grupos_objetivo) comandos

    Primer ALL → Dónde puede usar sudo

    ALL =

    Esto significa:

    “Desde cualquier máquina o terminal donde este usuario inicie sesión”.

    En aulas, servidores, SSH, local… no importa.
    Significa sin restricciones por host.


    (ALL:ALL) → Quién puede llegar a ser

    Este es el trozo más interesante.

    (ALL:ALL) Se divide en dos partes:

    ==Primer ALL (antes de los dos puntos)==

    Es la lista de usuarios objetivo.

    Traducción:

    “Puede usar sudo para convertirse en cualquier usuario del sistema.”

    No solo root: también www-data, postgres, backup, etc.

    Por ejemplo:

    sudo -u postgres psql

    ==Segundo ALL (tras los dos puntos)==

    Es la lista de grupos objetivo.

    Significa:

    “Puede ejecutar comandos como miembro de cualquier grupo.”

    No siempre se usa, pero cuando está, implica libertad total en cómo sudo crea el entorno del comando.

    En resumen:

    Tiene permiso para ejecutar comandos como cualquier usuario y cualquier grupo.


    Último ALL → Qué comandos puede ejecutar

    ... ALL

    Es la lista de comandos permitidos con sudo.

    Este último ALL significa:

    “Puede ejecutar cualquier comando con sudo.”

    Incluyendo:

    • Administración del sistema
    • Manipulación de archivos críticos
    • Instalación de paquetes
    • Gestión de usuarios
    • Servicios
    • Montaje/desmontaje
    • Cambios en red
    • Etc.

    [!NOTA]
    Es la carta blanca total.—

    En distros como Ubuntu, pertenecer al grupo sudo ya te da superpoderes. Puedes inspeccionar el grupo así:

    
    
    
    
    
    getent group sudo

    O, de forma artesanal:

    
    
    
    
    
    cat /etc/group | grep sudo

    También puedes pedirle al sistema: “Oye, ¿quién puede ejecutar sudo?” con:

    
    
    
    
    
    sudo -l -U nombre_de_usuario

    Esto te dice exactamente qué puede hacer ese usuario con sudo.

    En resumen privilegiado:
    — Usuarios declarados en /etc/sudoers
    — Usuarios declarados en /etc/sudoers.d/*
    — Usuarios que pertenecen al grupo sudo (o wheel, según la distro)

    Crear usuarios sin poderes especiales

    Comandos básicos:

    Crear usuario

    sudo adduser juan

    El sistema pedirá contraseña y datos opcionales.
    Por defecto, juan no tiene poderes administrativos.

    Ver su grupo

    id juan

    Verán que no pertenece al grupo sudo.

    Cambiar a ese usuario

    su juan

    Ahora puedes intentar un comando de administrador:

    sudo apt update

    Te dirá que juan no está en el archivo sudoers.

    Esa frase es el equivalente a “no tienes la llave del edificio”.


    Crear un usuario con permisos de sudo

    Hay dos formas:

    A) Añadirlo al grupo sudo

    sudo usermod -aG sudo juan

    Ahora juan puede usar sudo después de cerrar sesión y volver a entrar.

    B) Darle permisos específicos (ideal para clase avanzada)

    Editar con el editor seguro:

    sudo visudo

    Y añadir algo como:

    juan ALL=(ALL) /usr/bin/apt, /usr/bin/systemctl restart apache2

    Ahora juan solo puede actualizar paquetes y reiniciar Apache.
    Esto crea un usuario “Hawkeye”: no tiene superpoderes totales, pero sí un par de flechas especiales.

    Comandos clave

    • Crear usuario: sudo adduser nombre
    • Cambiar su grupo: sudo usermod -aG sudo nombre
    • Cambiar de usuario: su - nombre
    • Editar permisos: sudo visudo
    • Ver grupos: id nombre

    Archivos importantes

    • /etc/passwd → lista de usuarios.
    • /etc/group → lista de grupos.
    • /etc/sudoers → permisos privilegiados.
    • /var/log/auth.log → historial de uso de sudo.

    Comandos para la creación de usuarios y grupos

    1. Crear usuario con adduser

    adduser es amigable, pregunta cosas, rellena la casa del usuario y pone alfombra.

    Ejemplos:

    Crear usuario normal

    sudo adduser mario

    Crear usuario y saltarse las preguntas (solo contraseña):

    sudo adduser --disabled-password mario

    Después puedes activar contraseña con:

    sudo passwd mario

    Crear usuario sin carpeta home. Esto es útil para cuentas de servicio.

    sudo adduser --no-create-home servidorweb

    Crear usuario en un grupo específico

    sudo adduser juana profesores


    2. Crear usuario con useradd (el técnico)

    useradd es seco, directo… y totalmente predecible, ideal para scripts y prácticas avanzadas.

    Crear usuario con home y shell

    sudo useradd -m -s /bin/bash ana

    Crear usuario sin home

    sudo useradd -M bot

    Crear usuario con grupo primario específico

    sudo useradd -g profesores laura

    Crear usuario con grupos adicionales

    sudo useradd -G sudo,devops carlos

    Crear usuario con fecha de caducidad

    Esto es genial para usuarios temporales.

    sudo useradd -m -e 2025-12-31 invitado

    Puedes ver la caducidad con:

    sudo chage -l invitado

    Crear usuario con comentario

    sudo useradd -m -c "Laura Martínez" laura


    GESTIÓN DE CONTRASEÑAS

    La herramienta clave aquí es passwd.

    Cambiar o poner contraseña a un usuario

    sudo passwd juan

    Desactivar la contraseña (no puede iniciar sesión)

    sudo passwd -l juan

    Activar contraseña bloqueada

    sudo passwd -u juan

    Forzar cambio de contraseña al siguiente inicio

    Esto es ideal para alumnos nuevos:

    sudo passwd -e juan

    Establecer políticas de contraseñas

    Con chage puedes ajustar tiempos.

    Ejemplos:

    Caducidad de contraseña a 90 días

    sudo chage -M 90 juan

    Periodo mínimo entre cambios: 1 día

    sudo chage -m 1 juan

    Avisar 7 días antes de caducar

    sudo chage -W 7 juan

    Bloquear cuenta si no usa su cuenta en X días

    sudo chage -I 30 juan


    GESTIÓN DE GRUPOS

    Crear grupo

    sudo groupadd profesores

    Añadir usuario a grupo

    sudo usermod -aG profesores juan

    Sacar usuario de un grupo

    Para ver grupos primero:

    groups juan

    Eliminarlo del grupo:

    sudo gpasswd -d juan profesores


    MODIFICAR USUARIOS YA EXISTENTES

    Cambiar su shell

    sudo usermod -s /bin/bash mario

    Cambiar su home (se mueve manualmente)

    sudo usermod -d /home/nuevo_mario mario

    Cambiar nombre del usuario

    sudo usermod -l nuevo_nombre viejo_nombre

    Cambiar el UID o GID

    (para nivel pro)

    sudo usermod -u 1500 mario sudo usermod -g staff mario


    Ver en qué grupos está un usuario

    El método rápido, casi intuitivo:

    
    
    
    
    
    groups nombreusuario

    Te devuelve todos los grupos a los que pertenece, con una lista directa, estilo:

    
    
    
    
    
    nombreusuario : grupo1 grupo2 grupo3

    Si quieres la versión más formal:

    
    
    
    
    
    id nombreusuario

    Te muestra UID, GID y todos los grupos extra.

    Y si estás logueado como ese usuario, basta con:

    
    
    
    
    
    groups

    Ver qué usuarios pertenecen a un grupo

    Esta es la operación inversa: mirar dentro del grupo para ver la lista de miembros.

    Forma clara:

    
    
    
    
    
    getent group nombredelgrupo
    

    Salida típica:

    
    
    
    
    
    profesores:x:1003:antonio,juan,marta
    

    También puedes mirar el archivo clásico:

    
    
    
    
    
    cat /etc/group | grep nombredelgrupo

    Y si el grupo tiene muchos miembros, puedes hacerlo más legible:

    
    
    
    
    
    getent group nombredelgrupo | tr ':' '\n'

    El detalle curioso: grupos primarios vs secundarios

    Cada usuario tiene:

    • un grupo primario (su GID principal)
    • varios grupos secundarios

    id te los marca con claridad:

    
    
    
    
    
    id alumna1
    

    Salida:

    
    
    
    
    
    uid=1001(alumna1) gid=1001(alumna1) groups=1001(alumna1),1002(sudo),1003(profesores)

    Cómo ver si un usuario tiene permisos indirectos en un grupo

    Linux no tiene una orden directa tipo “¿quién tiene permisos en esta carpeta vía grupo?”, pero con la trilogía:

    1. ver el grupo dueño de la carpeta
    2. ver los permisos
    3. listar los usuarios del grupo

    Ya puedes reconstruir la escena del crimen digital.

    Ejemplo:

    
    
    
    
    
    ls -ld carpeta

    Resultado:

    
    
    
    
    
    drwxrwx--- 2 root profesores 4096 ...

    Luego:

    
    
    
    
    
    getent group profesores

    Obtienes la lista real de personas con acceso.

  • 2.2 – Por qué root no es tu amigo (pero lo necesitas a veces)

    2.2 – Por qué root no es tu amigo (pero lo necesitas a veces)

    ¿Qué es el superusuario (root)?

    Root es ese administrador que tiene todas las llaves, incluidas las que abren las puertas del sistema. Puede instalar programas, cambiar configuraciones, borrar sistemas de archivos y mirar cualquier carpeta.

    Hay varias formas de obtener una sesión como superusuario. Cada una tiene su sabor.

    Entrar como root con su -

    Este es el método clásico.

    su -

    Te pedirá la contraseña del usuario root.
    Si root está deshabilitado (como en Ubuntu), no funcionará.

    El - significa “cargar el entorno completo de root” (ruta, variables, etc.), no solo cambiar el usuario.


    Entrar como root usando sudo

    Esto solo funciona si tu usuario pertenece al grupo sudo.

    sudo su -

    Aquí la contraseña que pide es la tuya, no la del root.
    Es una forma de entrar en modo administrador sin conocer la clave maestra.


    Abrir directamente una shell como root

    sudo -i

    Es prácticamente equivalente a su -, pero usando el poder de sudo.

    ¿Por qué es mala idea usar root todo el tiempo?

    Root no tiene frenos

    Un usuario normal puede romper su sesión.
    Root puede romper el sistema entero, el arranque, o borrar discos completos sin pestañear.

    [!NOTE]
    ==Ejemplo peligroso: ==
    rm -rf /
    ESTO BOORARA TODO

    Un usuario normal no puede ejecutarlo sobre el sistema.
    Root sí. No pregunta. No duda. No se arrepiente.


    Root no te protege de tus despistes

    Un error tipográfico en root es como enviar un misil por escribir mal una coma.

    Esto pasa más de lo que parece:

    ==**Ejemplo peligroso: **==
    ==mv /home/alumno /home/alumno_backup mv /home/alumno_backup /==
    ESTO DAÑARA EL SISTEMA

    y adiós estructura de carpetas.


    Root no queda registrado igual que sudo

    Cuando usas root directamente:

    • No queda claro qué usuario real hizo qué.
    • No hay auditoría detallada
    • En una clase o empresa, dificulta saber quién tocó qué.

    Cuando usas sudo:

    /var/log/auth.log

    registra cada comando ejecutado.


    Root abre puertas a ataques

    Si un script, un archivo o un comando malicioso se ejecuta como root, ya está dentro de la fortaleza con una llave maestra.
    Malo para servidores. Malo para usuarios.


    5. Root ignora restricciones útiles

    No hay:

    • cuotas
    • límites de procesos
    • límites de memoria
    • restricciones de permisos

    Root puede saturar el sistema sin querer.


    Root fomenta malos hábitos

    Cosas que acaban pasando cuando se usa root “porque es más rápido”:

    • Instalar paquetes sin pensar.
    • Editar archivos del sistema sin copia de seguridad.
    • Romper autenticación, red o fstab
    • Crear permisos demasiado amplios.

    [!NOTA]
    En una frase, se traduce en:
    “Profe, mi VM no arranca y no sé por qué…”