Ejercicio obligatorio 4

Fecha de entrega: Domingo 27 de junio

Introducción

El archivo

Un juego codifica sus niveles en un archivo binario. Este archivo contiene una sucesión de niveles, donde cada nivel se compone de una determinada cantidad de obstáculos. Cada uno de los obstáculos tiene un tipo, un movimiento, un color, y parámetros que dependerán de su tipo.

Un archivo de niveles tendrá entonces este aspecto:

+---------+---------+---------+---------+---------+
| Nivel 1 | Nivel 2 | Nivel 3 |   ...   | Nivel M |
+---------+---------+---------+---------+---------+

es decir, una sucesión de un largo desconocido de niveles.

Luego cada nivel tendrá este aspecto:

+--------+--------+--------+-------+--------+
| N obst | Obst 1 | Obst 2 |  ...  | Obst N |
+--------+--------+--------+-------+--------+

es decir, el nivel empieza con un número (de 16 bits) N que indica la cantidad de obstáculos contenidos en dicho nivel y luego sigue una sucesión de estos obstáculos.

Los obstáculos

Como se dijo, los obstáculos pueden ser de diferentes tipos. La estructura de cada uno de ellos será:

+------------+------------+-----------+
| Encabezado | Movimiento | Geometría |
+------------+------------+-----------+

donde el encabezado es un byte que codifica el tipo, movimiento y color y los parámetros son una sucesión de valores y su cantidad y significado dependerá del tipo.

El byte de encabezado tiene el siguiente patrón:

MSB                                   LSB
+----+----+----+----+----+----+----+----+
| C1 | C0 | M1 | M0 | T3 | T2 | T1 | T0 |
+----+----+----+----+----+----+----+----+

donde los bits C representan el color, los bits M el movimiento y los bits T el tipo.

Hay 4 tipos de color:

Valor

Color

0

Azul

1

Naranja

2

Verde

3

Gris

Hay tres tipos de movimiento:

Valor

Movimiento

0

Inmóvil

1

Circular

2

Horizontal

Y, finalmente, se conocen (de momento) 3 tipos de objetos:

Valor

Geometría

0

Círculo

1

Rectángulo

2

Polígono

Luego del encabezado vendrán los valores del movimiento y de la geometría. Todos estos valores son de tipo entero de 16 bits.

Dependiendo del tipo de movimiento se esperan estos parámetros:

Inmóvil:

Ningún parámetro.

Circular:

3 parámetros: x, y, velocidad.

Horizontal:

4 parámetros: x0, x1, xi, velocidad.

Dependiendo del tipo de geometría se esperan estos parámetros:

Círculo:

3 parámetros: x, y, radio.

Rectángulo:

5 parámetros: x, y, ancho, alto, ángulo.

Polígono:

El primer parámetro es la cantidad de puntos, y luego vienen las coordenadas de los puntos como pares x e y. Por ejemplo, si el primer parámetro es 3, entonces se espera luego 6 valores correspondientes a sus coordenadas x0, y0, x1, y1, x2, y2.

Tipos

Se pide declarar tres tipos enumerativos color_t, movimiento_t y geometria_t que codifiquen los colores y tipos de movimiento de las tablas.

Además se tienen las siguientes definiciones:

struct poligono;
typedef struct poligono poligono_t;

que declaran el TDA polígono del EJ3. Si bien hace falta la definición del tipo el mismo no se usará en este ejercicio dado que el ejercicio anterior aún no está cerrado.

Lectura

Encabezado

Desarrollar una función bool leer_encabezado(FILE *f, color_t *color, movimiento_t *movimiento, geometria_t *geometria); que dado un archivo f lea de él un byte y devuelva su color, movimiento y geometria. Debe devolver true si todo es correcto.

Movimiento

Desarrollar una función bool leer_movimiento_inmovil(FILE *f, int16_t parametros[], size_t *n_parametros); que... no haga nada. Pero devuelva true por el nombre y 0 en n_parametros.

Desarrollar una función bool leer_movimiento_circular(FILE *f, int16_t parametros[], size_t *n_parametros); que lea del archivo f los 3 parámetros que representan el movimiento circular y los almacene en parametros, debe devolver por el nombre true en caso de estar todo correcto y por la interfaz n_parametros con la cantidad de parámetros leída.

Desarrollar una función bool leer_movimiento_horizontal(FILE *f, int16_t parametros[], size_t *n_parametros); análoga a las anteriores pero para el movimiento horizontal.

Se pide implementar una función bool leer_movimiento(FILE *f, movimiento_t movimiento, int16_t parametros[], size_t *n_parametros); que dado el archivo f lea de él el movimiento correspondiente al movimiento dado.

Nota

Deben usarse tablas de búsqueda con las funciones previamente desarrolladas para resolver esta función.

Cualquier solución basada en lógica de ifs, switch, etc. será considerada inválida.

Polígonos

Como se dijo previamente, dado que todavía no se encuentran terminados los EJ3 se evitará interactuar con el tipo polígono. La idea de las funciones que generan la forma es poder generar el poligono_t que representa al tipo de obstáculo dado.

Es decir, todas las funciones de lectura de parámetros del obstáculo serán de la forma:

poligono_t *leer_xxxxxx(FILE *f) {
    // Extraer de f la información sobre la geometría del polígono y sobre
    // su centro, rotación, etc.

    poligono_t *p = poligono_crear(...);    // Crear en el origen
    if(p == NULL) return NULL;

    poligono_rotar(p, rotacion);            // Del EJ2
    poligono_trasladar(p, cx, cy);          // Del EJ2

    return p;
}

Como no se tiene funcional al poligono_t entonces nos centraremos en este trabajo en la parte de la extracción de los parámetros y el completado de las funciones quedará para más adelante.

Es decir, la función será del estilo:

poligono_t *leer_xxxx(FILE *f) {
    // Extraer de f la información sobre la geometría del polígono y sobre
    // su centro, rotación, etc.

    printf("XXXX: Centro: %d, %d, Rotacion: %f, ...\n", ...);

    return NULL;
}

y se completará después.

Las funciones deben extraer los parámetros leídos correctamente.

Se pide implementar las funciones:

  • poligono_t *leer_geometria_circulo(FILE *f);,

  • poligono_t *leer_geometria_rectangulo(FILE *f);,

  • poligono_t *leer_geometria_poligono(FILE *f);,

que lean en base a los formatos especificados. (Todas ellas deben imprimir lo leído y devolver NULL.)

Se pide implementar una función poligono_t *leer_geometria(FILE*f, geometria_t geometria); que dado el archivo f lea de él la geometría correspondiente a un obstáculo del tipo dado.

Nota

Deben usarse tablas de búsqueda con las funciones previamente desarrolladas para resolver esta función.

Cualquier solución basada en lógica de ifs, switch, etc. será considerada inválida.

Aplicación

Se provee el siguiente main():

const char *colores[] = {
    // Completar adecuadamente
};

int main(int argc, char *argv[]) {
    if(argc != 2) {
        fprintf(stderr, "Uso: %s <archivo>\n", argv[0]);
        return 1;
    }

    FILE *f = fopen(argv[1], "rb");
    if(f == NULL) {
        fprintf(stderr, "No pudo abrirse \"%s\"\n", argv[1]);
        return 1;
    }

    int nivel = 0;
    while(1) {
        nivel++;

        int16_t obstaculos;
        if(! fread(&obstaculos, sizeof(int16_t), 1, f)) break;

        printf("Nivel %d, Cantidad de obstaculos: %u\n", nivel, obstaculos);

        for(size_t obstaculo = 0; obstaculo < obstaculos; obstaculo++) {
            color_t color;
            movimiento_t movimiento;
            geometria_t geometria;

            assert(leer_encabezado(f, &color, &movimiento, &geometria));

            printf("Obstaculo %zd, color = %s\n", obstaculo, colores[color]);

            int16_t parametros[4];
            size_t n_parametros;

            assert(leer_movimiento(f, movimiento, parametros, &n_parametros));

            printf("Movimiento: Parametros = %zd", n_parametros);
            for(size_t i = 0; i < n_parametros; i++)
                printf(", %d", parametros[i]);
            putchar('\n');

            assert(leer_geometria(f, geometria) == NULL);
        }
    }

    fclose(f);

    return 0;
}

Y además se provee el siguiente juegos de archivos: archivos_20211_ej4.tar.gz de una entrada con su respectiva salida.

Este main(), sin alteraciones, debe ser entregado como parte del ejercicio y el mismo debe correr para el archivo de entrada generando la salida pedida.

Nota

Para este trabajo no es necesario modularizar el problema, pero se recomienda hacerlo para practicar modularización y Makefile.

En el caso de modularizar en archivos entregar tanto los archivos como el Makefile que compila el proyecto.

Entrega

Deberá entregarse el código fuente del programa desarrollado o los fuentes y el Makefile en caso de haber modularizado.

El programa debe:

  1. Compilar correctamente con los flags:

    -Wall -Werror -std=c99 -pedantic
    
  2. validar la salida contra los ejemplos dados.

La entrega se realiza a través del sistema de entregas.

El ejercicio es de entrega individual.