12 - Ejemplo práctico 1: simulador de turnos

Este es el primero de una serie de ejemplos. Comenzamos con un escenario sencillo que consolida los conceptos básicos antes de avanzar hacia proyectos más complejos en temas posteriores.

12.1 Planteo del problema

Necesitamos un simulador pequeño para una sucursal que atiende clientes en orden de llegada. La cola debe registrar el número de ticket y un nombre breve. El objetivo es permitir tres operaciones desde consola:

  • Agregar un nuevo cliente con ticket incremental.
  • Llamar al siguiente cliente disponible.
  • Mostrar el estado actual de la cola.

Este ejemplo se centra en una cola circular de tamaño fijo implementada en Python, ideal para comenzar porque garantiza O(1) y evita gestiones manuales de memoria.

Representación de la cola de turnos en memoria circular

La estructura circular reutiliza casilleros liberados y mantiene el orden de llegada de cada ticket.

12.2 Modelo de datos

Usaremos una lista con capacidad fija y un índice circular para apuntar al frente y al final, además de un contador para detectar estados vacío/lleno.

CAP_TURNOS = 16
MAX_NOMBRE = 32


class Cliente:
  def __init__(self, ticket=0, nombre=""):
    self.ticket = ticket
    self.nombre = nombre


class ColaTurnos:
  def __init__(self, capacidad=CAP_TURNOS):
    self.capacidad = capacidad
    self.datos = [None] * capacidad
    self.frente = 0
    self.final = 0
    self.cantidad = 0
    self.siguiente_ticket = 1

12.3 Operaciones principales

Los métodos agregar_cliente, llamar_cliente y mostrar_cola encapsulan el flujo. Cada uno valida estados para informar éxito o error (cola llena/vacía).

class ColaTurnos:
  ...

  def agregar_cliente(self, nombre):
    if self.cantidad == self.capacidad:
      return 0
    slot = Cliente(self.siguiente_ticket, nombre[:MAX_NOMBRE])
    self.datos[self.final] = slot
    self.siguiente_ticket += 1
    self.final = (self.final + 1) % self.capacidad
    self.cantidad += 1
    return slot.ticket

  def llamar_cliente(self):
    if self.cantidad == 0:
      return None
    cliente = self.datos[self.frente]
    self.frente = (self.frente + 1) % self.capacidad
    self.cantidad -= 1
    return cliente

  def mostrar_cola(self):
    print(f"Turnos pendientes ({self.cantidad}):")
    idx = self.frente
    for _ in range(self.cantidad):
      slot = self.datos[idx]
      print(f"  #{slot.ticket} - {slot.nombre}")
      idx = (idx + 1) % self.capacidad

12.4 Interfaz de consola

El programa principal muestra un pequeño menú y ejecuta las operaciones según la entrada del usuario. Las entradas se validan para evitar desbordes.

def main():
  cola = ColaTurnos()

  while True:
    print("\n1) Agregar cliente")
    print("2) Llamar siguiente")
    print("3) Mostrar cola")
    print("0) Salir")
    opcion = input("Opcion: ")
    opcion = opcion.strip()

    if opcion == "1":
      nombre = input("Nombre: ")
      nombre = nombre.strip()
      ticket = cola.agregar_cliente(nombre)
      if ticket == 0:
        print("Cola llena, intenta mas tarde.")
      else:
        print(f"Ticket asignado: #{ticket}")
    elif opcion == "2":
      cliente = cola.llamar_cliente()
      if cliente:
        print(f"Atendiendo #{cliente.ticket} - {cliente.nombre}")
      else:
        print("No hay clientes esperando.")
    elif opcion == "3":
      cola.mostrar_cola()
    elif opcion == "0":
      print("Hasta luego")
      break
    else:
      print("Opcion no valida")


if __name__ == "__main__":
  main()

12.5 Código completo listo para Python

Ejecuta el siguiente archivo con python simulador_turnos.py:

CAP_TURNOS = 16
MAX_NOMBRE = 32


class Cliente:
  def __init__(self, ticket=0, nombre=""):
    self.ticket = ticket
    self.nombre = nombre


class ColaTurnos:
  def __init__(self, capacidad=CAP_TURNOS):
    self.capacidad = capacidad
    self.datos = [None] * capacidad
    self.frente = 0
    self.final = 0
    self.cantidad = 0
    self.siguiente_ticket = 1

  def agregar_cliente(self, nombre):
    if self.cantidad == self.capacidad:
      return 0
    slot = Cliente(self.siguiente_ticket, nombre[:MAX_NOMBRE])
    self.datos[self.final] = slot
    self.siguiente_ticket += 1
    self.final = (self.final + 1) % self.capacidad
    self.cantidad += 1
    return slot.ticket

  def llamar_cliente(self):
    if self.cantidad == 0:
      return None
    cliente = self.datos[self.frente]
    self.frente = (self.frente + 1) % self.capacidad
    self.cantidad -= 1
    return cliente

  def mostrar_cola(self):
    print(f"Turnos pendientes ({self.cantidad}):")
    idx = self.frente
    for _ in range(self.cantidad):
      slot = self.datos[idx]
      print(f"  #{slot.ticket} - {slot.nombre}")
      idx = (idx + 1) % self.capacidad


def main():
  cola = ColaTurnos()

  while True:
    print("1) Agregar cliente")
    print("2) Llamar siguiente")
    print("3) Mostrar cola")
    print("0) Salir")
    opcion = input("Opcion: ").strip()

    if opcion == "1":
      nombre = input("Nombre: ").strip()
      ticket = cola.agregar_cliente(nombre)
      if ticket == 0:
        print("Cola llena, intenta mas tarde.")
      else:
        print(f"Ticket asignado: #{ticket}")
    elif opcion == "2":
      cliente = cola.llamar_cliente()
      if cliente:
        print(f"Atendiendo #{cliente.ticket} - {cliente.nombre}")
      else:
        print("No hay clientes esperando.")
    elif opcion == "3":
      cola.mostrar_cola()
    elif opcion == "0":
      print("Hasta luego")
      break
    else:
      print("Opcion no valida")


if __name__ == "__main__":
  main()
Flujo general del simulador de turnos con cola circular

La cola circular facilita mantener los tickets en orden incluso cuando la cantidad de clientes alcanza el límite.

El programa es autocontenible y puede usarse como plantilla para futuros ejemplos con mayor complejidad.

12.6 Pruebas sugeridas

  • Ingresar más de 16 clientes para verificar el manejo de cola llena.
  • Atender todos los clientes y comprobar que el contador vuelve a cero.
  • Alternar altas y bajas rápidamente para asegurar que el movimiento circular de punteros funciona.

En los siguientes ejemplos prácticos aumentaremos la complejidad incorporando hilos, prioridades y almacenamiento persistente.