15 - Ejemplo práctico 4: reservas de restaurante

Construimos un sistema básico que administra las reservas de salón y terraza, da prioridad al ambiente VIP y conserva la información de cuántas personas fueron acomodadas por cada cola.

15.1 Planteo del problema

  • Registrar reservas con nombre, cantidad de comensales y ambiente preferido.
  • Asignar mesas respetando prioridad terraza > salón mientras exista cupo.
  • Visualizar el estado de cada cola y generar un resumen final.
Panel que muestra saldo de las colas de salón y terraza

15.2 Modelo de datos

#define CAP_RESERVAS 30
#define MAX_NOMBRE 48

typedef struct {
  char nombre[MAX_NOMBRE];
  int comensales;
} Reserva;

typedef struct {
  Reserva datos[CAP_RESERVAS];
  int frente;
  int final;
  int cantidad;
} ColaReservas;

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

int cola_push(ColaReservas *cola, Reserva r) {
  if (cola->cantidad == CAP_RESERVAS) {
    return 0;
  }
  cola->datos[cola->final] = r;
  cola->final = (cola->final + 1) % CAP_RESERVAS;
  cola->cantidad++;
  return 1;
}

int cola_pop(ColaReservas *cola, Reserva *r) {
  if (cola->cantidad == 0) {
    return 0;
  }
  *r = cola->datos[cola->frente];
  cola->frente = (cola->frente + 1) % CAP_RESERVAS;
  cola->cantidad--;
  return 1;
}

15.3 Operaciones

typedef struct {
  ColaReservas salon;
  ColaReservas terraza;
  int atendidosSalon;
  int atendidosTerraza;
  int cupoTerraza;
} Restaurante;

void restaurante_init(Restaurante *r, int cupoTerraza) {
  cola_init(&r->salon);
  cola_init(&r->terraza);
  r->atendidosSalon = 0;
  r->atendidosTerraza = 0;
  r->cupoTerraza = cupoTerraza;
}

int registrarReserva(Restaurante *r, const char *nombre, int comensales, int terraza) {
  Reserva res;
  strncpy(res.nombre, nombre, MAX_NOMBRE - 1);
  res.nombre[MAX_NOMBRE - 1] = '\0';
  res.comensales = comensales;
  return terraza ? cola_push(&r->terraza, res) : cola_push(&r->salon, res);
}

int asignarMesa(Restaurante *r, Reserva *res, int *ambiente) {
  if (r->terraza.cantidad > 0 &&
      r->atendidosTerraza + r->terraza.datos[r->terraza.frente].comensales <= r->cupoTerraza) {
    cola_pop(&r->terraza, res);
    r->atendidosTerraza += res->comensales;
    *ambiente = 1;
    return 1;
  }
  if (cola_pop(&r->salon, res)) {
    r->atendidosSalon += res->comensales;
    *ambiente = 0;
    return 1;
  }
  if (cola_pop(&r->terraza, res)) {
    r->atendidosTerraza += res->comensales;
    *ambiente = 1;
    return 1;
  }
  return 0;
}

void mostrarEstado(const Restaurante *r) {
  printf("Terraza: %d reservas en espera, %d comensales acomodados\n", r->terraza.cantidad, r->atendidosTerraza);
  printf("Salon: %d reservas en espera, %d comensales acomodados\n", r->salon.cantidad, r->atendidosSalon);
}

15.4 Interfaz de consola

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

int main(void) {
  Restaurante restaurante;
  restaurante_init(&restaurante, 80);

  int opcion;
  char nombre[MAX_NOMBRE];
  int comensales;
  int ambiente;
  Reserva res;

  do {
    puts("\n1) Registrar reserva salón");
    puts("2) Registrar reserva terraza");
    puts("3) Asignar mesa");
    puts("4) Mostrar estado");
    puts("0) Cerrar turno");
    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';
        printf("Comensales: ");
        if (scanf("%d", &comensales) != 1) {
          puts("Valor invalido");
          limpiarEntrada();
          break;
        }
        limpiarEntrada();
        if (registrarReserva(&restaurante, nombre, comensales, opcion == 2)) {
          puts("Reserva registrada.");
        } else {
          puts("Cola llena.");
        }
        break;
      case 3:
        if (asignarMesa(&restaurante, &res, &ambiente)) {
          printf("Asignado a %s (%d personas) -> %s\n", res.nombre, res.comensales, ambiente ? "Terraza" : "Salon");
        } else {
          puts("No hay reservas pendientes.");
        }
        break;
      case 4:
        mostrarEstado(&restaurante);
        break;
      case 0:
        puts("Turno cerrado.");
        mostrarEstado(&restaurante);
        break;
      default:
        puts("Opcion no valida.");
    }
  } while (opcion != 0);

  return 0;
}

15.5 Código completo para CLion

El siguiente archivo reúne todas las piezas listas para compilar:

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

#define CAP_RESERVAS 30
#define MAX_NOMBRE 48

typedef struct {
  char nombre[MAX_NOMBRE];
  int comensales;
} Reserva;

typedef struct {
  Reserva datos[CAP_RESERVAS];
  int frente;
  int final;
  int cantidad;
} ColaReservas;

typedef struct {
  ColaReservas salon;
  ColaReservas terraza;
  int atendidosSalon;
  int atendidosTerraza;
  int cupoTerraza;
} Restaurante;

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

int cola_push(ColaReservas *cola, Reserva r) {
  if (cola->cantidad == CAP_RESERVAS) return 0;
  cola->datos[cola->final] = r;
  cola->final = (cola->final + 1) % CAP_RESERVAS;
  cola->cantidad++;
  return 1;
}

int cola_pop(ColaReservas *cola, Reserva *r) {
  if (cola->cantidad == 0) return 0;
  *r = cola->datos[cola->frente];
  cola->frente = (cola->frente + 1) % CAP_RESERVAS;
  cola->cantidad--;
  return 1;
}

void restaurante_init(Restaurante *r, int cupoTerraza) {
  cola_init(&r->salon);
  cola_init(&r->terraza);
  r->atendidosSalon = 0;
  r->atendidosTerraza = 0;
  r->cupoTerraza = cupoTerraza;
}

int registrarReserva(Restaurante *r, const char *nombre, int comensales, int terraza) {
  Reserva res;
  strncpy(res.nombre, nombre, MAX_NOMBRE - 1);
  res.nombre[MAX_NOMBRE - 1] = '\0';
  res.comensales = comensales;
  return terraza ? cola_push(&r->terraza, res) : cola_push(&r->salon, res);
}

int asignarMesa(Restaurante *r, Reserva *res, int *ambiente) {
  if (r->terraza.cantidad > 0 &&
      r->atendidosTerraza + r->terraza.datos[r->terraza.frente].comensales <= r->cupoTerraza) {
    cola_pop(&r->terraza, res);
    r->atendidosTerraza += res->comensales;
    *ambiente = 1;
    return 1;
  }
  if (cola_pop(&r->salon, res)) {
    r->atendidosSalon += res->comensales;
    *ambiente = 0;
    return 1;
  }
  if (cola_pop(&r->terraza, res)) {
    r->atendidosTerraza += res->comensales;
    *ambiente = 1;
    return 1;
  }
  return 0;
}

void mostrarEstado(const Restaurante *r) {
  printf("Terraza: %d reservas en espera, %d comensales acomodados\n", r->terraza.cantidad, r->atendidosTerraza);
  printf("Salon: %d reservas en espera, %d comensales acomodados\n", r->salon.cantidad, r->atendidosSalon);
}

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

int main(void) {
  Restaurante restaurante;
  restaurante_init(&restaurante, 80);

  int opcion;
  char nombre[MAX_NOMBRE];
  int comensales;
  int ambiente;
  Reserva res;

  do {
    puts("\n1) Registrar reserva salón");
    puts("2) Registrar reserva terraza");
    puts("3) Asignar mesa");
    puts("4) Mostrar estado");
    puts("0) Cerrar turno");
    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';
        printf("Comensales: ");
        if (scanf("%d", &comensales) != 1) {
          puts("Valor invalido");
          limpiarEntrada();
          break;
        }
        limpiarEntrada();
        if (registrarReserva(&restaurante, nombre, comensales, opcion == 2)) {
          puts("Reserva registrada.");
        } else {
          puts("Cola llena.");
        }
        break;
      case 3:
        if (asignarMesa(&restaurante, &res, &ambiente)) {
          printf("Asignado a %s (%d personas) -> %s\n", res.nombre, res.comensales, ambiente ? "Terraza" : "Salon");
        } else {
          puts("No hay reservas pendientes.");
        }
        break;
      case 4:
        mostrarEstado(&restaurante);
        break;
      case 0:
        puts("Turno cerrado.");
        mostrarEstado(&restaurante);
        break;
      default:
        puts("Opcion no valida.");
    }
  } while (opcion != 0);

  return 0;
}

15.6 Pruebas sugeridas

  • Reducir la capacidad de la terraza (p.ej. 20 comensales) y observar cómo se reasignan reservas al salón.
  • Registrar y asignar alternadamente en ambos ambientes para verificar los contadores de estado.
  • Modificar el código para permitir cancelaciones y reinsertar una reserva al frente de la cola correspondiente.

El siguiente ejemplo práctico incorporará almacenamiento persistente y reportes diarios.