Salida de carro o moto en programa simulador de estacionamiento en ANSI C

Estacionamiento en ANSI C – Ejercicio de programación resuelto

Hoy vamos a ver un pequeño sistema de estacionamiento en el lenguaje de programación C, también conocido como ANSI C. Es un ejercicio resuelto. El mismo dice así:

Un estacionamiento posee 10 puestos para carros y 6 para moto. La empresa desea un software que le permita mantener el registro de los autos que entran y salen del estacionamiento para saber cuáles puestos quedan libres.

Consideraciones del sistema.

  1. Los puestos del estacionamiento están enumerados y el usuario podrá elegir en donde desea estacionarse, adicionalmente se deberá marcar la hora de entrada y la hora de salida.
  2. En cada puesto solo puede haber un único auto/moto.
  3. A medida que los puestos se estén llenando se deberá mostrar por pantalla cuales están disponibles.
  4. El valor por hora es de 3 mil pesos los carros y 1 mil pesos las motos.
  5. No se debe registrar dos veces el mismo vehículo.
  6. Al final cada usuario se le imprimirá la factura de la cantidad de horas, minutos y segundos que usó el estacionamiento. Y si se pasó por 2 minutos las horas que indicó en el registro, este deberá pagar un excedente de 200 pesos por cada minuto.
  7. Adicional, se deberá imprimir en la factura la placa del vehículo. Horas/minutos extras, Total a pagar, Hora de entrada del vehículo, hora de salida del vehículo y Horas consumidas.
  8. Una vez que alguien paga y sale del estacionamiento, el puesto debe quedar libre para otro usuario pueda usarlo.

Veamos cómo hacer este ejercicio de estacionamiento en C.

Explicación del algoritmo

Vamos a usar un arreglo de structs. Cada struct o estructura de C va a representar un lugar en el estacionamiento, y este tendrá varias propiedades como su estado (ocupado, desocupado), placas, hora de entrada, etcétera.

Las fechas (horas de entrada) las vamos a manejar como marcas de tiempo o timestamps que son un número entero, y después vamos a crear otra función que las va a formatear como fecha local.

Como vamos a tener todo como entero, sacar la diferencia de tiempo será muy fácil ya que tendremos la diferencia en segundos.

Iremos trabajando todo en un arreglo, y dividiremos el trabajo en funciones para poder hacerlo más sencillo.

Lugar de estacionamiento

Comencemos viendo la estructura de un lugar en el estacionamiento. Es un struct de C y se ve así:

struct LugarEstacionamiento
{
    char placa[MAXIMA_LONGITUD_CADENA];
    time_t entrada;
    int estado;
    char tipo;
    int horas;
};

Las horas no son las horas que lleva estacionado el vehículo, sino las horas que el vehículo planea estar dentro del estacionamiento (para, más tarde, calcular el costo extra). Luego declaramos un arreglo y lo inicializamos:

void limpiarLugares(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int i;
    for (i = 0; i < CANTIDAD_CARROS; i++)
    {
        strcpy(lugares[i].placa, "");
        lugares[i].entrada = 0;
        lugares[i].estado = DESOCUPADO;
        lugares[i].tipo = TIPO_CARRO;
        lugares[i].horas = 0;
    }
    for (i = CANTIDAD_CARROS; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
    {
        strcpy(lugares[i].placa, "");
        lugares[i].entrada = 0;
        lugares[i].estado = DESOCUPADO;
        lugares[i].tipo = TIPO_MOTO;
        lugares[i].horas = 0;
    }
}



int main()
{
    struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS];
    limpiarLugares(lugares);
}

Como puedes ver, primero coloqué los carros y después las motos; todos pertenecen al mismo arreglo pero los diferencia el tipo.

La declaración está en la línea 26 y la limpieza está en la función que aparece al inicio.

Marcar la entrada del vehículo

Registrar entrada de vehículo - Ejercicio en C
Registrar entrada de vehículo – Ejercicio en C

Veamos cómo se registra un vehículo. Primero tenemos que verificar que el lugar sea correcto, que no esté ocupado y que no exista un vehículo con esas mismas placas.

Tenemos el menú donde solicitamos las placas, horas y lugar:

void menuAsignarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    char lecturaUsuario[MAXIMA_LONGITUD_CADENA] = "";
    int lugar = 0;
    int horas;
    mostrarEstadoEstacionamiento(lugares);

    while (1)
    {
        printf("Ingrese el lugar: ");
        scanf("%d", &lugar);
        if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
        {
            if (lugares[lugar - 1].estado == OCUPADO)
            {
                printf("Ocupado\n");
            }
            else
            {
                lugar = lugar - 1;
                break;
            }
        }
        else
        {
            printf("Numero no valido\n");
        }
    }

    printf("Ingrese las horas que planea estar en el estacionamiento: ");
    scanf("%d", &horas);
    consumirNuevaLinea();
    while (1)
    {
        printf("Ingrese las placas de");
        if (lugares[lugar].tipo == TIPO_MOTO)
        {
            printf(" la moto: ");
        }
        else
        {
            printf(" el carro: ");
        }
        fgets(lecturaUsuario, MAXIMA_LONGITUD_CADENA, stdin);
        lecturaUsuario[strcspn(lecturaUsuario, "\r\n")] = 0;
        if (existeVehiculoConPlacaYTipo(lugares, lecturaUsuario, lugares[lugar].tipo))
        {
            printf("Ya existe un vehiculo del mismo tipo y con las mismas placas en el sistema\n");
        }
        else
        {
            break;
        }
    }
    marcarEntradaVehiculo(lugares, lugar, lecturaUsuario, horas);
}

Y después invocamos a la función que modifica el arreglo, pasándole el índice del elemento que tiene que modificar (debido a que los arreglos se pasan por referencia, va a modificar al arreglo original):

void marcarEntradaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice, char placa[MAXIMA_LONGITUD_CADENA], int horas)
{
    lugares[indice].estado = OCUPADO;
    strncpy(lugares[indice].placa, placa, MAXIMA_LONGITUD_CADENA);
    lugares[indice].entrada = time(NULL);
    lugares[indice].horas = horas;
}

Simplemente le ponemos el estado ocupado y marcamos la hora de entrada. Para obtenerla invocamos a time pasándole NULL, de modo que nos devuelve el timestamp de la hora actual.

Listado de vehículos en estacionamiento

Estado de estacionamiento - Mostrar carros y motos en parqueadero programado con C
Estado de estacionamiento – Mostrar carros y motos en parqueadero programado con C

Para solicitar el lugar al que ingresa el vehículo, debemos mostrar el estado de los lugares, quién los ocupa y desde cuándo.

El listado queda así:

void mostrarEstadoEstacionamiento(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int i;
    for (i = 0; i < CANTIDAD_CARROS; i++)
    {
        printf("#%d [CARRO] ", i + 1);
        if (lugares[i].estado == OCUPADO)
        {
            printf("Ocupado por %s desde ", lugares[i].placa);
            imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
        }
        else
        {
            printf("Desocupado");
        }
        printf("\n");
    }
    for (; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
    {
        printf("#%d [MOTO] ", i + 1);
        if (lugares[i].estado == OCUPADO)
        {
            printf("Ocupado por %s desde ", lugares[i].placa);
            imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
        }
        else
        {
            printf("Desocupado");
        }
        printf("\n");
    }
}

Lo que hace es listar todos los lugares y su estado junto con la fecha de entrada (aquí estamos convirtiendo el timestamp a fecha). Así de simple.

Marcar salida de vehículo

Salida de carro o moto en programa simulador de estacionamiento en ANSI C
Salida de carro o moto en programa simulador de estacionamiento en ANSI C

Para terminar con nuestro programa en C para gestionar un estacionamiento, veamos cómo marcar la salida de un vehículo. Aquí es en donde se calcula el tiempo transcurrido, costos, etcétera.

Primero un menú para solicitar el lugar y verificar si ese lugar está ocupado:

void menuSacarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int lugar = 0;
    while (1)
    {
        printf("Ingrese el lugar: ");
        scanf("%d", &lugar);
        if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
        {
            if (lugares[lugar - 1].estado == DESOCUPADO)
            {
                printf("No hay vehiculo estacionado en este lugar\n");
            }
            else
            {
                lugar = lugar - 1;
                break;
            }
        }
        else
        {
            printf("Numero no valido\n");
        }
    }
    marcarSalidaVehiculo(lugares, lugar);
}

Luego tenemos a la función que invoca a otras 2: la que realmente hace el cálculo y la que limpia el lugar.

void marcarSalidaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice)
{
    calcularEImprimirInformacionSalida(lugares[indice]);
    limpiarLugar(&lugares[indice]);
}

void calcularEImprimirInformacionSalida(struct LugarEstacionamiento lugarEstacionamiento)
{
    int horasSegunVehiculo = lugarEstacionamiento.horas;
    double total = 0;
    double granTotal = 0;
    double totalExcedente = 0;
    if (lugarEstacionamiento.tipo == TIPO_CARRO)
    {
        total = horasSegunVehiculo * VALOR_HORA_CARRO;
    }
    else
    {
        total = horasSegunVehiculo * VALOR_HORA_MOTO;
    }
    time_t ahora = time(NULL);
    time_t entrada = lugarEstacionamiento.entrada;
    // Nota: si se quieren hacer pruebas con otras fechas, hay que
    // cambiar el timestamp a la fecha deseada. Por ejemplo, la siguiente
    // línea apunta al 10 de marzo de 2022 a las 11:00:00
    // time_t entrada = 1646931600;
    time_t diferencia = ahora - entrada;
    printf("===========================\n");
    printf("Entrada: ");
    imprimirFechaAPartirDeTimestamp(entrada);
    printf("\n");
    printf("Salida: ");
    imprimirFechaAPartirDeTimestamp(ahora);
    printf("\n");
    printf("Horas solicitadas: %d\n", horasSegunVehiculo);
    unsigned long long int horas = diferencia / 60 / 60;
    diferencia -= 60 * 60 * horas;
    unsigned long long int minutos = diferencia / 60;
    diferencia -= 60 * minutos;

    int conTiempoExtra = 0;
    if (horas >= horasSegunVehiculo && (((horas - horasSegunVehiculo) * 60) + minutos) >= MINUTOS_EXTRA_PARA_RECARGO)
    {
        conTiempoExtra = 1;
        totalExcedente = (((horas - horasSegunVehiculo) * 60) + minutos) * VALOR_POR_MINUTO;
    }
    granTotal = total + totalExcedente;
    printf("Tiempo dentro de estacionamiento: ");
    printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas, minutos, diferencia);
    if (conTiempoExtra)
    {
        printf("Tiempo extra: ");
        printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas - horasSegunVehiculo, minutos, diferencia);
        printf("Pago por tiempo extra: %lf\n", totalExcedente);
    }
    printf("Placa: %s\n", lugarEstacionamiento.placa);
    printf("Pago por horas: %0.2lf\n", total);
    printf("Total: %0.2lf\n", granTotal);
    printf("===========================\n");
}
void limpiarLugar(struct LugarEstacionamiento *lugarEstacionamiento)
{
    lugarEstacionamiento->entrada = 0;
    lugarEstacionamiento->estado = DESOCUPADO;
    lugarEstacionamiento->horas = 0;
    strcpy(lugarEstacionamiento->placa, "");
}

Por favor nota que en el caso de la limpieza del lugar estamos recibiendo un apuntador al elemento del arreglo de los lugares. Esto es porque no estamos recibiendo el arreglo completo.

Toda la magia y operaciones suceden en la función calcularEImprimirInformacionSalida. Ahí calculamos la diferencia y cobramos las horas que el vehículo solicitó al entrar, sin importar si alcanzó ese tiempo o no.

Luego nos fijamos en el tiempo extra y en caso de que exista multiplicamos los minutos extra (tomando en cuenta también las horas, por ello la multiplicación por 60) por el costo de cada minuto extra.

Poniendo todo junto

Arriba te mostré las funciones más importantes, pero no es todo el código. El código completo queda así:

/*
    https://parzibyte.me/blog
*/
#include <stdio.h>
#include <time.h>
#include <string.h>
#define CANTIDAD_MOTOS 6
#define CANTIDAD_CARROS 10
#define MAXIMA_LONGITUD_CADENA 1000
#define OCUPADO 1
#define DESOCUPADO 0
#define TIPO_MOTO 'M'
#define TIPO_CARRO 'C'
#define VALOR_HORA_CARRO 3000
#define VALOR_HORA_MOTO 1000
#define MINUTOS_EXTRA_PARA_RECARGO 2
#define VALOR_POR_MINUTO 200

void imprimirFechaAPartirDeTimestamp(time_t tiempo)
{
    struct tm *tm = localtime(&tiempo);
    printf("%02d/%02d/%02d %02d:%02d:%02d",
           tm->tm_mday, tm->tm_mon + 1,
           tm->tm_year + 1900,
           tm->tm_hour,
           tm->tm_min,
           tm->tm_sec);
}
void consumirNuevaLinea(void)
{
    int c;
    do
    {
        c = getchar();
    } while (c != EOF && c != '\n');
}

struct LugarEstacionamiento
{
    char placa[MAXIMA_LONGITUD_CADENA];
    time_t entrada;
    int estado;
    char tipo;
    int horas;
};

void limpiarLugares(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int i;
    for (i = 0; i < CANTIDAD_CARROS; i++)
    {
        strcpy(lugares[i].placa, "");
        lugares[i].entrada = 0;
        lugares[i].estado = DESOCUPADO;
        lugares[i].tipo = TIPO_CARRO;
        lugares[i].horas = 0;
    }
    for (i = CANTIDAD_CARROS; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
    {
        strcpy(lugares[i].placa, "");
        lugares[i].entrada = 0;
        lugares[i].estado = DESOCUPADO;
        lugares[i].tipo = TIPO_MOTO;
        lugares[i].horas = 0;
    }
}

void mostrarEstadoEstacionamiento(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int i;
    for (i = 0; i < CANTIDAD_CARROS; i++)
    {
        printf("#%d [CARRO] ", i + 1);
        if (lugares[i].estado == OCUPADO)
        {
            printf("Ocupado por %s desde ", lugares[i].placa);
            imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
        }
        else
        {
            printf("Desocupado");
        }
        printf("\n");
    }
    for (; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
    {
        printf("#%d [MOTO] ", i + 1);
        if (lugares[i].estado == OCUPADO)
        {
            printf("Ocupado por %s desde ", lugares[i].placa);
            imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
        }
        else
        {
            printf("Desocupado");
        }
        printf("\n");
    }
}

void marcarEntradaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice, char placa[MAXIMA_LONGITUD_CADENA], int horas)
{
    lugares[indice].estado = OCUPADO;
    strncpy(lugares[indice].placa, placa, MAXIMA_LONGITUD_CADENA);
    lugares[indice].entrada = time(NULL);
    lugares[indice].horas = horas;
}
void limpiarLugar(struct LugarEstacionamiento *lugarEstacionamiento)
{
    lugarEstacionamiento->entrada = 0;
    lugarEstacionamiento->estado = DESOCUPADO;
    lugarEstacionamiento->horas = 0;
    strcpy(lugarEstacionamiento->placa, "");
}

void calcularEImprimirInformacionSalida(struct LugarEstacionamiento lugarEstacionamiento)
{
    int horasSegunVehiculo = lugarEstacionamiento.horas;
    double total = 0;
    double granTotal = 0;
    double totalExcedente = 0;
    if (lugarEstacionamiento.tipo == TIPO_CARRO)
    {
        total = horasSegunVehiculo * VALOR_HORA_CARRO;
    }
    else
    {
        total = horasSegunVehiculo * VALOR_HORA_MOTO;
    }
    time_t ahora = time(NULL);
    time_t entrada = lugarEstacionamiento.entrada;
    // Nota: si se quieren hacer pruebas con otras fechas, hay que
    // cambiar el timestamp a la fecha deseada. Por ejemplo, la siguiente
    // línea apunta al 10 de marzo de 2022 a las 11:00:00
    // time_t entrada = 1646931600;
    time_t diferencia = ahora - entrada;
    printf("===========================\n");
    printf("Entrada: ");
    imprimirFechaAPartirDeTimestamp(entrada);
    printf("\n");
    printf("Salida: ");
    imprimirFechaAPartirDeTimestamp(ahora);
    printf("\n");
    printf("Horas solicitadas: %d\n", horasSegunVehiculo);
    unsigned long long int horas = diferencia / 60 / 60;
    diferencia -= 60 * 60 * horas;
    unsigned long long int minutos = diferencia / 60;
    diferencia -= 60 * minutos;

    int conTiempoExtra = 0;
    if (horas >= horasSegunVehiculo && (((horas - horasSegunVehiculo) * 60) + minutos) >= MINUTOS_EXTRA_PARA_RECARGO)
    {
        conTiempoExtra = 1;
        totalExcedente = (((horas - horasSegunVehiculo) * 60) + minutos) * VALOR_POR_MINUTO;
    }
    granTotal = total + totalExcedente;
    printf("Tiempo dentro de estacionamiento: ");
    printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas, minutos, diferencia);
    if (conTiempoExtra)
    {
        printf("Tiempo extra: ");
        printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas - horasSegunVehiculo, minutos, diferencia);
        printf("Pago por tiempo extra: %lf\n", totalExcedente);
    }
    printf("Placa: %s\n", lugarEstacionamiento.placa);
    printf("Pago por horas: %0.2lf\n", total);
    printf("Total: %0.2lf\n", granTotal);
    printf("===========================\n");
}
void marcarSalidaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice)
{
    calcularEImprimirInformacionSalida(lugares[indice]);
    limpiarLugar(&lugares[indice]);
}

int existeVehiculoConPlacaYTipo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], char placa[MAXIMA_LONGITUD_CADENA], char tipo)
{
    int i;
    for (i = 0; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
    {
        if (lugares[i].tipo == tipo && strcmp(lugares[i].placa, placa) == 0)
        {
            return 1;
        }
    }
    return 0;
}

void menuAsignarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    char lecturaUsuario[MAXIMA_LONGITUD_CADENA] = "";
    int lugar = 0;
    int horas;
    mostrarEstadoEstacionamiento(lugares);

    while (1)
    {
        printf("Ingrese el lugar: ");
        scanf("%d", &lugar);
        if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
        {
            if (lugares[lugar - 1].estado == OCUPADO)
            {
                printf("Ocupado\n");
            }
            else
            {
                lugar = lugar - 1;
                break;
            }
        }
        else
        {
            printf("Numero no valido\n");
        }
    }

    printf("Ingrese las horas que planea estar en el estacionamiento: ");
    scanf("%d", &horas);
    consumirNuevaLinea();
    while (1)
    {
        printf("Ingrese las placas de");
        if (lugares[lugar].tipo == TIPO_MOTO)
        {
            printf(" la moto: ");
        }
        else
        {
            printf(" el carro: ");
        }
        fgets(lecturaUsuario, MAXIMA_LONGITUD_CADENA, stdin);
        lecturaUsuario[strcspn(lecturaUsuario, "\r\n")] = 0;
        if (existeVehiculoConPlacaYTipo(lugares, lecturaUsuario, lugares[lugar].tipo))
        {
            printf("Ya existe un vehiculo del mismo tipo y con las mismas placas en el sistema\n");
        }
        else
        {
            break;
        }
    }
    marcarEntradaVehiculo(lugares, lugar, lecturaUsuario, horas);
}

void menuSacarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int lugar = 0;
    while (1)
    {
        printf("Ingrese el lugar: ");
        scanf("%d", &lugar);
        if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
        {
            if (lugares[lugar - 1].estado == DESOCUPADO)
            {
                printf("No hay vehiculo estacionado en este lugar\n");
            }
            else
            {
                lugar = lugar - 1;
                break;
            }
        }
        else
        {
            printf("Numero no valido\n");
        }
    }
    marcarSalidaVehiculo(lugares, lugar);
}

void menu(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
    int eleccion = 0;
    while (eleccion != 4)
    {
        printf("1. Registrar entrada\n2. Registrar salida\n3. Ver estado de estacionamiento\n4. Salir\nElige: ");
        scanf("%d", &eleccion);
        if (eleccion == 1)
        {
            menuAsignarVehiculo(lugares);
        }
        else if (eleccion == 2)
        {
            menuSacarVehiculo(lugares);
        }
        else if (eleccion == 3)
        {
            mostrarEstadoEstacionamiento(lugares);
        }
    }
}

int main()
{
    struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS];
    limpiarLugares(lugares);
    menu(lugares);
}

Yo lo he probado con el compilador GCC y Dev-C++; en ambos lugares funciona correctamente aunque no recomiendo usar Dev-C++.

Para terminar te dejo con más tutoriales de C en mi blog.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *