INSCRIPCIONES

Pasted Oct 4, 2026, 1:18 AM1 file405 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgt4epegjcfq4b6
inscripciones.py
class Nodo:
    """
    Representa cada elemento individual (nodo) dentro de las estructuras 
    de datos enlazadas (ya sea para Colas o Pilas).
    """
    def __init__(self, nombre, id_estudiante, prioridad=None, siguiente=None):
        self.nombre = nombre          # Nombre del estudiante
        self.id_estudiante = id_estudiante  # Identificador único del estudiante
        self.prioridad = prioridad    # Nivel de prioridad opcional (1 a 5) usado en ColaPrioridad
        self.siguiente = siguiente    # Puntero de enlace hacia el siguiente nodo en la memoria


class PilaHistorial:
    """
    Estructura de Pila (LIFO - Last In, First Out) heredada del Reto 1.
    Permite registrar todas las operaciones clave y ofrece la función 'deshacer'.
    """
    def __init__(self):
        self.tope = None       # Puntero al elemento ubicado en la cima de la pila
        self._tamanio = 0      # Contador interno de elementos almacenados

    def push(self, accion, descripcion):
        """Inserta una nueva acción registrada en la cima de la pila."""
        # Se crea un nodo guardando el tipo de acción y su descripción detallada
        nuevo_nodo = Nodo(nombre=accion, id_estudiante=descripcion, siguiente=self.tope)
        self.tope = nuevo_nodo
        self._tamanio += 1

    def pop(self):
        """Remueve y retorna el elemento en la cima (permite deshacer la última acción)."""
        if self.is_empty():
            return None
        nodo_removido = self.tope
        self.tope = self.tope.siguiente  # El tope pasa a ser el nodo anterior
        self._tamanio -= 1
        return {"accion": nodo_removido.nombre, "descripcion": nodo_removido.id_estudiante}

    def is_empty(self):
        """Verifica si la pila se encuentra vacía."""
        return self.tope is None

    def size(self):
        """Retorna el número total de acciones registradas."""
        return self._tamanio


class ColaSimple:
    """
    Estructura de Cola Lineal (FIFO - First In, First Out).
    Gestiona la llegada general de turnos de los estudiantes por orden de llegada.
    """
    def __init__(self):
        self.frente = None     # Puntero al primer elemento que va a salir
        self.fin = None        # Puntero al último elemento que acaba de ingresar
        self._tamanio = 0      # Contador de elementos en la cola

    def enqueue(self, nombre, id_estudiante):
        """Agrega un nuevo estudiante al final de la cola simple."""
        nuevo = Nodo(nombre, id_estudiante)
        if self.is_empty():
            self.frente = self.fin = nuevo
        else:
            self.fin.siguiente = nuevo
            self.fin = nuevo
        self._tamanio += 1
        print(f"↳ ENQUEUE [{nombre}, id={id_estudiante}] en ColaSimple")

    def dequeue(self):
        """Extrae y retorna el estudiante que está al frente de la cola."""
        if self.is_empty():
            return None
        removido = self.frente
        self.frente = self.frente.siguiente
        if self.frente is None:
            self.fin = None  # Si la cola queda vacía, el fin también se limpia
        self._tamanio -= 1
        return removido

    def front(self):
        """Devuelve el elemento del frente sin eliminarlo."""
        return self.frente if not self.is_empty() else None

    def is_empty(self):
        """Comprueba si la cola simple está vacía."""
        return self.frente is None

    def size(self):
        """Retorna el tamaño actual de la cola."""
        return self._tamanio

    def mostrar(self):
        """Imprime por consola todos los elementos actuales de la cola."""
        if self.is_empty():
            print("  (ColaSimple vacía)")
            return
        actual = self.frente
        while actual:
            print(f"  -> [{actual.nombre} | ID: {actual.id_estudiante}]")
            actual = actual.siguiente


class ColaCircular:
    """
    Estructura de Cola Circular.
    Utilizada para manejar la lista de espera cuando el curso se satura,
    permitiendo rotación cíclica de los estudiantes.
    """
    def __init__(self, capacidad_max=100):
        self.cabeza = None     # Puntero al inicio de la cola circular
        self.cola = None       # Puntero al final del círculo
        self._tamanio = 0
        self.capacidad_max = capacidad_max

    def enqueue(self, nombre, id_estudiante):
        """Inserta un estudiante cerrando el enlace circular hacia la cabeza."""
        nuevo = Nodo(nombre, id_estudiante)
        if self.is_empty():
            self.cabeza = nuevo
            self.cola = nuevo
            self.cola.siguiente = self.cabeza  # Apunta a sí mismo cerrando el ciclo
        else:
            self.cola.siguiente = nuevo
            self.cola = nuevo
            self.cola.siguiente = self.cabeza  # Mantiene la circularidad
        self._tamanio += 1
        print(f"↳ ENQUEUE ESPERA CIRCULAR [{nombre}, id={id_estudiante}]")

    def dequeue(self):
        """Extrae al estudiante que se encuentra al frente del ciclo."""
        if self.is_empty():
            return None
        removido = self.cabeza
        if self.cabeza == self.cola:
            self.cabeza = None
            self.cola = None
        else:
            self.cabeza = self.cabeza.siguiente
            self.cola.siguiente = self.cabeza  # Actualiza la referencia circular del último
        self._tamanio -= 1
        return removido

    def front(self):
        """Retorna el elemento al frente sin extraerlo."""
        return self.cabeza if not self.is_empty() else None

    def is_empty(self):
        """Verifica si la cola circular está vacía."""
        return self.cabeza is None

    def size(self):
        """Retorna el número de elementos en espera circular."""
        return self._tamanio

    def mostrar(self):
        """Recorre la estructura circular de forma controlada para mostrar los elementos."""
        if self.is_empty():
            print("  (ColaCircular de espera vacía)")
            return
        actual = self.cabeza
        for _ in range(self._tamanio):
            print(f"  -> [Espera: {actual.nombre} | ID: {actual.id_estudiante}]")
            actual = actual.siguiente


class ColaPrioridad:
    """
    Estructura de Cola de Prioridad.
    Atiende casos especiales con ponderación (5 = prioridad más alta, 1 = más baja).
    """
    def __init__(self):
        self.frente = None
        self._tamanio = 0

    def enqueue(self, nombre, id_estudiante, prioridad):
        """Inserta de manera ordenada según la prioridad numérica (mayor prioridad va primero)."""
        nuevo = Nodo(nombre, id_estudiante, prioridad=prioridad)
        # Si está vacía o el nuevo elemento tiene mayor prioridad que el frente actual
        if self.is_empty() or prioridad > self.frente.prioridad:
            nuevo.siguiente = self.frente
            self.frente = nuevo
        else:
            actual = self.frente
            # Busca la posición adecuada según el nivel de prioridad
            while actual.siguiente and actual.siguiente.prioridad >= prioridad:
                actual = actual.siguiente
            nuevo.siguiente = actual.siguiente
            actual.siguiente = nuevo
        self._tamanio += 1
        print(f"↳ ENQUEUE PRIORIDAD [{nombre}, id={id_estudiante}, p={prioridad}]")

    def dequeue(self):
        """Extrae el elemento con la máxima prioridad (ubicado en el frente)."""
        if self.is_empty():
            return None
        removido = self.frente
        self.frente = self.frente.siguiente
        self._tamanio -= 1
        print(f"↳ DEQUEUE [{removido.nombre}, id={removido.id_estudiante}] desde ColaPrioridad (p={removido.prioridad})")
        return removido

    def front(self):
        return self.frente if not self.is_empty() else None

    def is_empty(self):
        return self.frente is None

    def size(self):
        return self._tamanio

    def mostrar(self):
        """Muestra los casos especiales ordenados por su nivel de prioridad."""
        if self.is_empty():
            print("  (ColaPrioridad vacía)")
            return
        actual = self.frente
        while actual:
            print(f"  -> [Prioridad {actual.prioridad}] {actual.nombre} (ID: {actual.id_estudiante})")
            actual = actual.siguiente


class SistemaInscripcionAcademico:
    """
    Gestor principal del sistema universitario.
    Coordina el curso, las diferentes colas, la lista de inscritos y el historial de acciones.
    """
    def __init__(self):
        self.nombre_curso = ""
        self.cupos_totales = 0
        self.inscritos = []  # Arreglo para visualizar los estudiantes matriculados exitosamente
        
        # Instanciación de las estructuras de datos requeridas
        self.cola_simple = ColaSimple()
        self.cola_circular = ColaCircular()
        self.cola_prioridad = ColaPrioridad()
        self.historial = PilaHistorial()

    def configurar_curso(self):
        """Configura el nombre del curso y la cantidad máxima de cupos disponibles."""
        print("\n--- 1. CONFIGURAR CURSO ---")
        self.nombre_curso = input("Ingrese el nombre del curso: ")
        self.cupos_totales = int(input("Ingrese la cantidad máxima de CUPOS: "))
        print(f"✅ Curso '{self.nombre_curso}' configurado con {self.cupos_totales} cupos.")

    def llegada_estudiante(self):
        """Registra la llegada de un estudiante y lo deriva a la cola correspondiente."""
        print("\n--- 2. LLEGADA DE ESTUDIANTE ---")
        id_est = input("Ingrese ID del estudiante: ")
        nombre = input("Ingrese Nombre del estudiante: ")
        es_especial = input("¿Tiene caso especial de prioridad (1-5)? (Deje en blanco si no): ").strip()

        if es_especial:
            prioridad = int(es_especial)
            self.cola_prioridad.enqueue(nombre, id_est, prioridad)
            self.historial.push("LLEGADA_PRIORIDAD", f"Estudiante {nombre} con prioridad {prioridad}")
        else:
            self.cola_simple.enqueue(nombre, id_est)
            self.historial.push("LLEGADA_SIMPLE", f"Estudiante {nombre} en cola general")

    def procesar_turnos(self):
        """Procesa N turnos para inscribir estudiantes evaluando cupos y prioridades."""
        print(f"\n--- 3. PROCESAR TURNOS (Cupos actuales: {len(self.inscritos)}/{self.cupos_totales}) ---")
        n = int(input("¿Cuántos turnos desea procesar?: "))
        
        for _ in range(n):
            # Si el curso se llena, los estudiantes de la cola general pasan a espera circular
            if len(self.inscritos) >= self.cupos_totales:
                print("⚠️ ¡Curso saturado! No hay más cupos directos. Los siguientes van a espera circular.")
                if not self.cola_simple.is_empty():
                    est = self.cola_simple.dequeue()
                    self.cola_circular.enqueue(est.nombre, est.id_estudiante)
                    self.historial.push("ESPERA", f"{est.nombre} enviado a cola circular por saturación")
                break

            # Se atiende primero la cola de prioridad si contiene elementos
            if not self.cola_prioridad.is_empty():
                est = self.cola_prioridad.dequeue()
                self.inscritos.append({"nombre": est.nombre, "id": est.id_estudiante})
                print(f"✅ INSCRITO (Especial): {est.nombre} (id={est.id_estudiante})")
                self.historial.push("INSCRIBIR_PRIORIDAD", f"Inscrito caso especial {est.nombre}")
            elif not self.cola_simple.is_empty():
                est = self.cola_simple.dequeue()
                self.inscritos.append({"nombre": est.nombre, "id": est.id_estudiante})
                print(f"✅ INSCRITO: {est.nombre} (id={est.id_estudiante})")
                self.historial.push("INSCRIBIR", f"Inscrito general {est.nombre}")
            else:
                print("⚠️ No hay más estudiantes en las colas de recepción.")
                break

    def mostrar_estado(self):
        """Muestra de forma visual el estado del curso, inscritos y todas las colas."""
        print(f"\n================ ESTADO DEL CURSO: {self.nombre_curso} ================")
        print(f"Cupos Ocupados: {len(self.inscritos)} / {self.cupos_totales}")
        print("\n--- LISTA DE INSCRITOS ---")
        if not self.inscritos:
            print("  (Ningún inscrito aún)")
        else:
            for idx, ins in enumerate(self.inscritos, 1):
                print(f"  {idx}. {ins['nombre']} (ID: {ins['id']})")

        print("\n--- COLA SIMPLE (General) ---")
        self.cola_simple.mostrar()

        print("\n--- COLA DE PRIORIDAD (Casos Especiales) ---")
        self.cola_prioridad.mostrar()

        print("\n--- COLA CIRCULAR (Lista de Espera) ---")
        self.cola_circular.mostrar()
        print("================================================================")

    def registrar_retiro(self):
        """Libera el cupo de un estudiante retirado y reasigna automáticamente el espacio."""
        print("\n--- 5. REGISTRAR RETIRO DE UN INSCRITO ---")
        if not self.inscritos:
            print("❌ No hay estudiantes inscritos para retirar.")
            return
        
        print("Inscritos actuales:")
        for idx, ins in enumerate(self.inscritos, 1):
            print(f"  {idx}. {ins['nombre']} (ID: {ins['id']})")
        
        op = int(input("Seleccione el número del estudiante que se retira: ")) - 1
        if 0 <= op < len(self.inscritos):
            retirado = self.inscritos.pop(op)
            print(f"⚠️ RETIRADO: {retirado['nombre']} ha liberado un cupo.")
            self.historial.push("RETIRAR", f"Retiro de {retirado['nombre']}")

            # Reasignación de cupo liberado: prioridad primero, luego circular
            if not self.cola_prioridad.is_empty():
                nuevo_est = self.cola_prioridad.dequeue()
                self.inscritos.append({"nombre": nuevo_est.nombre, "id": nuevo_est.id_estudiante})
                print(f"✅ REASIGNADO cupo a {nuevo_est.nombre} desde ColaPrioridad")
                self.historial.push("REASIGNAR", f"Cupo reasignado a {nuevo_est.nombre} desde prioridad")
            elif not self.cola_circular.is_empty():
                nuevo_est = self.cola_circular.dequeue()
                self.inscritos.append({"nombre": nuevo_est.nombre, "id": nuevo_est.id_estudiante})
                print(f"❓ REASIGNADO cupo a {nuevo_est.nombre} desde ColaCircular")
                self.historial.push("REASIGNAR", f"Cupo reasignado a {nuevo_est.nombre} desde circular")
            else:
                print("ℹ️ No hay estudiantes en espera para reasignar el cupo.")
        else:
            print("❌ Opción inválida.")

    def atender_prioridad(self):
        """Atiende directamente un caso especial desde la cola de prioridad."""
        print("\n--- 6. ATENDER CASO ESPECIAL DIRECTO ---")
        if self.cola_prioridad.is_empty():
            print("ℹ️ No hay casos especiales en la cola de prioridad.")
            return
        
        est = self.cola_prioridad.dequeue()
        self.inscritos.append({"nombre": est.nombre, "id": est.id_estudiante})
        print(f"✅ ATENDIDO Y INSCRITO (Prioridad {est.prioridad}): {est.nombre}")
        self.historial.push("ATENDER_PRIORIDAD", f"Atención prioritaria a {est.nombre}")

    def deshacer_accion(self):
        """Invoca la función pop de la pila historial para revertir el último cambio."""
        print("\n--- 7. DESHACER ÚLTIMA ACCIÓN (Pila Historial) ---")
        accion_deshecha = self.historial.pop()
        if not accion_deshecha:
            print("❌ No hay acciones registradas para deshacer.")
            return
        
        print(f"↩️ DESHACER: Se revierte la acción '{accion_deshecha['accion']}' -> {accion_deshecha['descripcion']}")


def main():
    """Función principal que despliega el menú interactivo en consola."""
    sistema = SistemaInscripcionAcademico()
    while True:
        print("\n=== MENÚ SISTEMA DE INSCRIPCIONES PUCE ===")
        print("1. Configurar curso (nombre, CUPOS)")
        print("2. Llegada estudiante (ID, Nombre, ¿prioridad? 1-5)")
        print("3. Procesar siguientes N turnos")
        print("4. Mostrar inscritos / mostrar todas las colas")
        print("5. Registrar retiro de un inscrito (libera cupo y reasigna)")
        print("6. Atender prioridad")
        print("7. Deshacer última acción (usando PilaHistorial)")
        print("8. Salir")
        
        opcion = input("Seleccione una opción: ").strip()
        
        if opcion == "1":
            sistema.configurar_curso()
        elif opcion == "2":
            sistema.llegada_estudiante()
        elif opcion == "3":
            sistema.procesar_turnos()
        elif opcion == "4":
            sistema.mostrar_estado()
        elif opcion == "5":
            sistema.registrar_retiro()
        elif opcion == "6":
            sistema.atender_prioridad()
        elif opcion == "7":
            sistema.deshacer_accion()
        elif opcion == "8":
            print("Saliendo del sistema de inscripciones. ¡Hasta luego!")
            break
        else:
            print("❌ Opción no válida. Intente de nuevo.")


if __name__ == "__main__":
    main()
    

17,173 chars · codepastes.com