14 - Ejemplo práctico 3: FastPass en una atracción

Este ejercicio recrea el acceso a una atracción popular con dos colas: general y FastPass. El sistema debe garantizar que por cada dos invitados con FastPass se atienda al menos uno de la cola general para mantener la percepción de justicia.

14.1 Planteo del problema

La operación diaria requiere:

  • Registrar visitantes, indicando nombre y tipo de pase.
  • Despachar visitantes según la regla 2:1 (dos FastPass, uno general) sin dejar colas bloqueadas.
  • Visualizar el estado de cada cola y cuántos visitantes ingresaron desde cada una.
Diagrama de cola general y FastPass con proporción 2 a 1

El tablero muestra en tiempo real la cantidad de visitantes por cola y los ingresados en cada turno.

14.2 Modelo de datos

Utilizamos una cola circular simple para cada tipo de pase.

#define CAP_VISITANTES 40
#define MAX_NOMBRE 40

typedef struct {
  char nombre[MAX_NOMBRE];
} Visitante;

typedef struct {
  Visitante datos[CAP_VISITANTES];
  int frente;
  int final;
  int cantidad;
} ColaVisitantes;

void cola_init(ColaVisitantes *cola) {
  cola->frente = 0;
  cola->final = 0;
  cola->cantidad = 0;
}

int cola_push(ColaVisitantes *cola, Visitante v) {
  if (cola->cantidad == CAP_VISITANTES) {
    return 0;
  }
  cola->datos[cola->final] = v;
  cola->final = (cola->final + 1) % CAP_VISITANTES;
  cola->cantidad++;
  return 1;
}

int cola_pop(ColaVisitantes *cola, Visitante *v) {
  if (cola->cantidad == 0) {
    return 0;
  }
  *v = cola->datos[cola->frente];
  cola->frente = (cola->frente + 1) % CAP_VISITANTES;
  cola->cantidad--;
  return 1;
}

14.3 Reglas de despacho

La estructura principal controla ambas colas y lleva contadores para el reporte.

typedef struct {
  ColaVisitantes fastpass;
  ColaVisitantes general;
  int turnoFast;
  int atendidosFast;
  int atendidosGeneral;
} ControlAtraccion;

void control_init(ControlAtraccion *c) {
  cola_init(&c->fastpass);
  cola_init(&c->general);
  c->turnoFast = 0;
  c->atendidosFast = 0;
  c->atendidosGeneral = 0;
}

int registrarVisitante(ControlAtraccion *c, const char *nombre, int esFastPass) {
  Visitante v;
  strncpy(v.nombre, nombre, MAX_NOMBRE - 1);
  v.nombre[MAX_NOMBRE - 1] = '\0';
  return esFastPass ? cola_push(&c->fastpass, v) : cola_push(&c->general, v);
}

int despachar(ControlAtraccion *c, Visitante *v) {
  if (c->fastpass.cantidad > 0 && (c->turnoFast < 2 || c->general.cantidad == 0)) {
    if (cola_pop(&c->fastpass, v)) {
      c->turnoFast++;
      c->atendidosFast++;
      return 1;
    }
  }
  if (cola_pop(&c->general, v)) {
    c->turnoFast = 0;
    c->atendidosGeneral++;
    return 1;
  }
  if (cola_pop(&c->fastpass, v)) {
    c->turnoFast = 1;
    c->atendidosFast++;
    return 1;
  }
  return 0;
}

void mostrarColas(const ControlAtraccion *c) {
  printf("FastPass (%d):\n", c->fastpass.cantidad);
  for (int i = 0, idx = c->fastpass.frente; i < c->fastpass.cantidad; ++i, idx = (idx + 1) % CAP_VISITANTES) {
    printf("  FP - %s\n", c->fastpass.datos[idx].nombre);
  }
  printf("General (%d):\n", c->general.cantidad);
  for (int i = 0, idx = c->general.frente; i < c->general.cantidad; ++i, idx = (idx + 1) % CAP_VISITANTES) {
    printf("  GEN - %s\n", c->general.datos[idx].nombre);
  }
}

void reporte(const ControlAtraccion *c) {
  puts("--- Resumen ---");
  printf("Atendidos FastPass: %d\n", c->atendidosFast);
  printf("Atendidos General: %d\n", c->atendidosGeneral);
}

14.4 Interfaz de consola

El menú permite registrar invitados, despachar y consultar el estado.

void limpiarEntrada(void) {
  int c;
  while ((c = getchar()) != '\n' && c != EOF) {}
}

int main(void) {
  ControlAtraccion control;
  control_init(&control);

  int opcion;
  char nombre[MAX_NOMBRE];
  Visitante v;

  do {
    puts("\n1) Registrar visitante general");
    puts("2) Registrar visitante FastPass");
    puts("3) Despachar siguiente");
    puts("4) Mostrar colas");
    puts("0) Cerrar atraccion");
    printf("Opcion: ");
    if (scanf("%d", &opcion) != 1) {
      puts("Entrada invalida");
      limpiarEntrada();
      continue;
    }
    limpiarEntrada();
    switch (opcion) {
      case 1:
      case 2:
        printf("Nombre: ");
        if (!fgets(nombre, sizeof nombre, stdin)) break;
        nombre[strcspn(nombre, "\n")] = '\0';
        if (registrarVisitante(&control, nombre, opcion == 2)) {
          puts("Registrado.");
        } else {
          puts("Cola completa.");
        }
        break;
      case 3:
        if (despachar(&control, &v)) {
          printf("Ingresando: %s\n", v.nombre);
        } else {
          puts("No hay visitantes en espera.");
        }
        break;
      case 4:
        mostrarColas(&control);
        reporte(&control);
        break;
      case 0:
        puts("Atraccion cerrada.");
        reporte(&control);
        break;
      default:
        puts("Opcion no valida.");
    }
  } while (opcion != 0);

  return 0;
}

14.5 Código completo para CLion

El siguiente listado reúne todo el simulador en un solo archivo:

#include <stdio.h>
#include <string.h>

#define CAP_VISITANTES 40
#define MAX_NOMBRE 40

typedef struct {
  char nombre[MAX_NOMBRE];
} Visitante;

typedef struct {
  Visitante datos[CAP_VISITANTES];
  int frente;
  int final;
  int cantidad;
} ColaVisitantes;

typedef struct {
  ColaVisitantes fastpass;
  ColaVisitantes general;
  int turnoFast;
  int atendidosFast;
  int atendidosGeneral;
} ControlAtraccion;

void cola_init(ColaVisitantes *cola);
int cola_push(ColaVisitantes *cola, Visitante v);
int cola_pop(ColaVisitantes *cola, Visitante *v);
void control_init(ControlAtraccion *c);
int registrarVisitante(ControlAtraccion *c, const char *nombre, int esFastPass);
int despachar(ControlAtraccion *c, Visitante *v);
void mostrarColas(const ControlAtraccion *c);
void reporte(const ControlAtraccion *c);
void limpiarEntrada(void);

void cola_init(ColaVisitantes *cola) { cola->frente = cola->final = cola->cantidad = 0; }

int cola_push(ColaVisitantes *cola, Visitante v) {
  if (cola->cantidad == CAP_VISITANTES) return 0;
  cola->datos[cola->final] = v;
  cola->final = (cola->final + 1) % CAP_VISITANTES;
  cola->cantidad++;
  return 1;
}

int cola_pop(ColaVisitantes *cola, Visitante *v) {
  if (cola->cantidad == 0) return 0;
  *v = cola->datos[cola->frente];
  cola->frente = (cola->frente + 1) % CAP_VISITANTES;
  cola->cantidad--;
  return 1;
}

void control_init(ControlAtraccion *c) {
  cola_init(&c->fastpass);
  cola_init(&c->general);
  c->turnoFast = 0;
  c->atendidosFast = 0;
  c->atendidosGeneral = 0;
}

int registrarVisitante(ControlAtraccion *c, const char *nombre, int esFastPass) {
  Visitante v;
  strncpy(v.nombre, nombre, MAX_NOMBRE - 1);
  v.nombre[MAX_NOMBRE - 1] = '\0';
  return esFastPass ? cola_push(&c->fastpass, v) : cola_push(&c->general, v);
}

int despachar(ControlAtraccion *c, Visitante *v) {
  if (c->fastpass.cantidad > 0 && (c->turnoFast < 2 || c->general.cantidad == 0)) {
    cola_pop(&c->fastpass, v);
    c->turnoFast++;
    c->atendidosFast++;
    return 1;
  }
  if (c->general.cantidad > 0) {
    cola_pop(&c->general, v);
    c->turnoFast = 0;
    c->atendidosGeneral++;
    return 1;
  }
  if (cola_pop(&c->fastpass, v)) {
    c->turnoFast = 1;
    c->atendidosFast++;
    return 1;
  }
  return 0;
}

void mostrarColas(const ControlAtraccion *c) {
  printf("FastPass (%d):\n", c->fastpass.cantidad);
  for (int i = 0, idx = c->fastpass.frente; i < c->fastpass.cantidad; ++i, idx = (idx + 1) % CAP_VISITANTES) {
    printf("  FP - %s\n", c->fastpass.datos[idx].nombre);
  }
  printf("General (%d):\n", c->general.cantidad);
  for (int i = 0, idx = c->general.frente; i < c->general.cantidad; ++i, idx = (idx + 1) % CAP_VISITANTES) {
    printf("  GEN - %s\n", c->general.datos[idx].nombre);
  }
}

void reporte(const ControlAtraccion *c) {
  puts("--- Resumen ---");
  printf("Atendidos FastPass: %d\n", c->atendidosFast);
  printf("Atendidos General: %d\n", c->atendidosGeneral);
}

void limpiarEntrada(void) {
  int c;
  while ((c = getchar()) != '\n' && c != EOF) {}
}

int main(void) {
  ControlAtraccion control;
  control_init(&control);

  int opcion;
  char nombre[MAX_NOMBRE];
  Visitante v;

  do {
    puts("\n1) Registrar visitante general");
    puts("2) Registrar visitante FastPass");
    puts("3) Despachar siguiente");
    puts("4) Mostrar colas");
    puts("0) Cerrar atraccion");
    printf("Opcion: ");
    if (scanf("%d", &opcion) != 1) {
      puts("Entrada invalida");
      limpiarEntrada();
      continue;
    }
    limpiarEntrada();
    switch (opcion) {
      case 1:
      case 2:
        printf("Nombre: ");
        if (!fgets(nombre, sizeof nombre, stdin)) break;
        nombre[strcspn(nombre, "\n")] = '\0';
        if (registrarVisitante(&control, nombre, opcion == 2)) {
          puts("Registrado.");
        } else {
          puts("Cola completa.");
        }
        break;
      case 3:
        if (despachar(&control, &v)) {
          printf("Ingresando: %s\n", v.nombre);
        } else {
          puts("No hay visitantes en espera.");
        }
        break;
      case 4:
        mostrarColas(&control);
        reporte(&control);
        break;
      case 0:
        puts("Atraccion cerrada.");
        reporte(&control);
        break;
      default:
        puts("Opcion no valida.");
    }
  } while (opcion != 0);

  return 0;
}

14.6 Pruebas sugeridas

  • Simular una hora pico registrando muchos FastPass y verificar que la regla 2:1 nunca bloquea la cola general.
  • Ejecutar la atracción con la cola general llena para confirmar que el sistema sigue despachando FastPass cuando no hay espacio en general.
  • Modificar la proporción (por ejemplo 3:1) y observar cómo cambian los contadores.

En el siguiente ejemplo práctico integraremos sensores en tiempo real y almacenamiento persistente.