Trabajo Práctico 1
Fecha de entrega: Jueves 25 de junio
Introducción
Objetivo
El objetivo del presente trabajo práctico es implementar un clon (mejorado) del juego Battle Zone de Atari utilizando la biblioteca gráfica SDL2 y el motor de 3D que ya desarrollamos.
El trabajo consiste en la resolución de un problema mediante el diseño y uso de TDAs y en la reutilización de algunos de los tipos, funciones y conceptos desarrollados a lo largo del curso en trabajos anteriores.
Alcances
Mediante el presente TP se busca que el estudiante adquiera y aplique conocimientos sobre los siguientes temas:
Encapsulamiento en TDAs,
Tablas de búsqueda,
Modularización,
Técnicas de abstracción,
además de los temas ya evaluados en trabajos anteriores.
Nota
Este trabajo es un trabajo integrador de final de cursada. En el mismo se van a aplicar todos los temas aprendidos y además se va a evaluar el diseño de la aplicación.
Es imprescindible entender los temas de TDA y modularización antes de empezar con el trabajo.
Es imprescindible diseñar el trabajo como etapa previa a la implementación.
Cualquier abordaje que pretenda empezar a codificar sin haber ordenado el trabajo primero está condenado a fracasar contra la complejidad del problema.
El juego
Nuestro juego transcurre en un espacio 2D descripto por un eje \(x\) y un eje \(y\). Cada objeto tiene una posición según sus coordenadas \((x, y)\). Además los objetos pueden estar rotados sobre su eje, según un ángulo \(\varphi\). El ángulo \(\varphi\) estará en el rango \((-\pi, \pi]\), siendo \(0\) el ángulo para objetos "mirando" hacia el eje \(x\) y \(\frac12\pi\) para objetos mirando hacia el eje \(y\).
En nuestro campo de juego hay 50 obstáculos. Estos obstáculos están ubicados en coordenadas al azar entre \([-150, 150]\) tanto para \(x\) como para \(y\). Los objetos además están rotados aleatoriamente. No importa si en la generación al azar los obstáculos se superponen entre sí.
Finalmente en el campo hay dos tanques de guerra. Uno controlado por el jugador y el otro un tanque enemigo. Ambos tanques representados por sus coordenadas y giro (como todo en este juego).
Ambos tanques pueden disparar misiles. Cada vez que se dispara un misil hay un tiempo de enfriamiento de 2 segundos en los cuales ese tanque no puede disparar de nuevo (sin importar qué haya pasado con el misil). Los tanques pueden disparar misiles sólo hacia donde apunta su cañón, en el caso del tanque del jugador esto es en la dirección de su ángulo \(\varphi\), pero en el caso del tanque enemigo la torreta del tanque puede girar (no más de 1 radián en cada dirección, es decir 2 radianes en total) por lo que hay que sumar la dirección de la torreta a la dirección del tanque para tener la dirección del disparo.
Cuando un misil se dispara el mismo avanzará sobre su trayectoria a una velocidad de 24 m/s durante 2 segundos, es decir, como mucho recorrerá 48 metros hasta desaparecer. En su trayecto puede pegarle a un obstáculo, en cuyo caso se destruirá o puede impactar contra un tanque, en cuyo caso lo destruirá. La verificación de impacto se realizará contra un radio, si el disparo está a menos de 3 metros del centro del objeto habrá colisión, tanto para tanques como para obstáculos.
Los tanques se pueden mover, adelante, atrás y girar sobre su eje a izquierda y derecha. Un tanque siempre puede girar. Un tanque puede avanzar o retroceder siempre y cuando no colisione con otro objeto (obstáculo o tanque). En este caso la colisión se detectará en 5 metros a la redonda.
Cada vez que el tanque del jugador reciba un impacto se perderá una vida. Cada vez que el tanque enemigo reciba un impacto el jugador sumará 1000 puntos y se creará un nuevo tanque enemigo.
Notar que en juego siempre hay dos tanques y como mucho dos misiles, y que el tanque del jugador y el enemigo son idénticos (con la salvedad de que el jugador no puede mover su torreta).
La dinámica del juego es la siguiente: El jugador empieza con la vida que está jugando más tres vidas adicionales. El tanque del jugador se crea en el origen. El tanque enemigo en una posición aleatoria a 50 metros del jugador (verificar que no haya un obstáculo ahí). Mientras el juego está evolucionando ambos tanques se mueven por el campo disparando. Cuando un misil impacta en el tanque enemigo se mostrará una animación, se sumarán 1000 puntos y se creará un nuevo tanque a 50 metros del tanque del jugador en cualquier dirección. Cuando un misil impacta en el tanque del jugador se mostrará una animación, y se restará una vida. Cuando el jugador se quede sin vidas se terminará el juego.
El movimiento
En un juego que se simula en tiempo real la simulación se hace de a un cuadro por vez para cumplir con los cuadros por segundo (FPS) que dicta el juego. Cada cuadro se actualiza la simulación según el tiempo que pasó y todo el tiempo se está recalculando la lógica del juego.
Cuando los objetos se mueven, entonces no saltan de forma instánea del punto A al punto B, si no que lo hacen de a incrementos suaves.
Como se dijo, el jugador puede avanzar, retroceder y girar su tanque. Cada vez que el jugador comande un movimiento el mismo se realizará durante medio segundo (si vuelve a comandar el mismo movimiento se reiniciará este medio segundo cada vez). El avance será a una velocidad de 7 m/s. El giro será a una velocidad de 0.36 radianes por segundo.
Las colisiones se detectan en cada cuadro de simulación al intentar mover, si hay que avanzar y no se puede avanzar no se avanzará en la simulación.
El tanque enemigo tiene el firmware de una aspiradora robótica.
Cuando no esté ejecutando ningún movimiento previo con probabilidad 1/FPS podrá iniciar uno nuevo o seguir estando quieto.
Si decide iniciar uno nuevo hay 1/2 de chances de que sea una rotación y 1/2 de chances de que sea un avance.
Si decide iniciar una rotación la misma durará aleatoriamente entre 0 y 3 segundos y será en el sentido que achique el ángulo entre su giro y el jugador (por algo importa definir todo positivo para un lado y negativo para el otro, y notar que dijimos giro del cuerpo del tanque, no de su torreta).
Si decide iniciar un avance calcular un aleatorio entre -1 y 3. Si es negativo retroceder durante ese tiempo, si es positivo avanzar durante ese tiempo.
Con eso tenemos la inteligencia artificial del enemigo.
Antes dijimos que la torreta del tanque enemigo podía girar. El rango de visión en el juego es de 1 radián para cada lado. Si el tanque del jugador se encontrara a menos de 1 radián con respecto a la posición del tanque enemigo (con respecto al tanque, no con respecto a la torreta) la torreta girará en la dirección que corresponda para apuntar al tanque del jugador a una velocidad de 0,12 radianes por segundo. Si perdiera la visibilidad del tanque del jugador entonces volvería a un giro de 0º a una velocidad de 0,24 radianes por segundo.
Entonces, cuando el tanque del jugador esté en el rango de visión del tanque enemigo, el tanque enemigo intentará apuntar hacia él su torreta. Cuando no lo tenga a la vista irá a la posición de reposo de la torreta alineada con el tanque.
Como ya se dijo previamente la dirección de disparo del tanque enemigo es la suma del ángulo del tanque con el ángulo de la torreta. Si el tanque del jugador estuviera a menos de 0.1 radianes (para cada lado) con respecto a ese ángulo entonces el tanque enemigo efectuará un disparo.
SDL2 en 5 minutos
Para la representación gráfica se utilizará la biblioteca gráfica SDL2.
Una aplicación gráfica tiene un pseudocódigo más o menos de este estilo:
main() {
inicializar;
while(1) {
leer la entrada del usuario;
procesar lo que haya que procesar;
dibujar la pantalla;
esperar un ratito para cumplir con los FPS;
}
destruir;
}
Se provee un esqueleto completo con esta lógica, donde se indica dónde el alumno tiene que completar con su código. De SDL2 sólo habrá que aprender a leer el teclado, a dibujar líneas y a cambiar el color. El resto ya está hecho.
Se proveen ejemplos tanto de teclado, como del dibujado de cosas.
Las transformaciones 3D
Si bien nuestro juego se simula en un espacio 2D la visualización de nuestro campo de juegos será en un entorno 3D.
Contamos con una biblioteca de objetos STL que se provee donde están todas las cosas que van a ser dibujadas en la pantalla: Las piezas del tanque, los obstáculos, los misiles, los elementos del fondo. Incluso cosas que van a ser dibujadas pero que son 2D como los textos o la mira del tanque. Todos estos objetos están generados con centro en el origen.
A lo largo del cuatrimestre estuvimos construyendo un motor capaz de generar transformaciones en el campo 3D. En un motor 3D las transformaciones se representan en una matriz de 4x4 y son acumulativas, es decir, si multiplicamos la transformación de rotar por la de transladar tenemos una operación que rota y translada en ese orden.
Es por esto que los objetos se suelen definir en el origen. Si, por ejemplo, tuviera que dibujar un tanque que se compone de múltiples partes y sé la posición relativa entre ellas, por dar un ejemplo, si la torreta estuviera 3 metros más arriba que el cuerpo del tanque y luego el radar estuviera un metro y medio para atrás y medio metro arriba con respecto a esta... no me importa la posición en la que voy a dibujar el tanque porque las operaciones son acumulativas. Más aún, si la torreta estuviera girada con respecto al cuerpo entonces el radar tendría que estar desplazado metro y medio para atrás con respecto a ese giro... no, no, nada de eso. Las operaciones son acumulativas.
Si tengo una matriz M que ya contiene las transformaciones con respecto a dónde
quiero dibujar mi tanque (por ejemplo, una translación en x,y seguida de una
rotación en x) puedo dibujar el cuerpo de mi tanque sencillamente como
aplicar(M, tanque), eso dibujará el cuerpo en las coordenadas x,y que
corresponda y girado lo que haga falta. Dijimos que la torreta estaba 3 metros
más arriba y rotada algún ángulo, entonces calcularemos una nueva matriz
\(M' = M \times M_t \times M_z\) (siendo matrices de translación y rotación
respectivamente) y dibujaremos la torreta como aplicar(M', torreta). Eso
dibujará la torreta con esa translación y rotación relativa al cuerpo del
tanque. Como ya dijimos, las operaciones son acumulativas. De igual modo, si
modificamos \(M'\) para desplazarla en \((-1.5, 0, 0.5)\) y rotar lo
que corresponda el radar (que está rotando todo el tiempo) podremos dibujar sin
más y relativo a la torreta que era a su vez relativa al cuerpo del tanque.
Si quisiéramos volver al estado de dibujo previo tendríamos que deshacer las operaciones... momento. Las transformaciones en un motor gráfico representan una pila. Cada vez que aplicamos una transformación nueva estamos apilando en la misma la multiplicación del viejo tope de la pila por la nueva operación y cada vez que desapilamos recuperamos el estado anterior.
Con esto si tenemos una matriz M podemos dibujar cualquier cosa donde queramos.
Ahora bien, falta una parte para construir nuestro juego. Queremos construir un first person shooter (no confundir con frames per second, misma sigla) por lo que queremos que nuestra cámara mire desde la perspectiva de nuestro tanque. La matriz \(M^4\) que arrastramos desde el EJ1 es una matriz de perspectiva, aunque hay que decir que "mira" un poco raro, porque mira hacia las z negativas, y como nosotros definimos nuestro juego, nuestro tanque mira hacia sus x positivas. Entonces vamos a tener que hacer un par de rotaciones para enderezar la cámara y después vamos a tener que substraer la posición del tanque del jugador para conseguir como efecto que todo "el mundo" se mueva para el lado contrario.
Dicho todo esto, las primeras 5 matrices a apilar en nuestra transformación son:
matriz_crear_mper(4)
matriz_crear_mz(M_PI / 2 + angz)
matriz_crear_my(M_PI / 2 - angx)
matriz_crear_mz(-phi)
matriz_crear_mt({-x, -y, -3})
donde x, y y phi son las coordenadas y giro del tanque del usuario. El
-3 es porque la cámara está puesta a 3 metros de altura, la altura del cañón.
Los parámetros angx y angz en principio valen cero pero el primero controla
mirar hacia arriba y hacia abajo y el segundo controla "ladear la cabeza".
Mientras el tanque esté avanzando o retrocediendo pondremos en angx un valor
random entre \((0, 0.01)\) y mientras estemos girando lo mismo con angz.
Con eso vamos a mejorar la animación.
Reiteramos que en el caso de la cámara las transformaciones del tanque son negativas porque estamos compensando todo el mundo con respecto a nuestro punto de vista, las demás transformaciones se aplican positivas.
Corrección a aplicar()
(Título alternativo: Debería haber implementado el TP1 antes de escribir el enunciado del EJ2.)
En el EJ3 se implementó la función aplicar() para que devolviera un vector de
dos posiciones. Ahora necesitamos que devuelva un vector de tres posiciones.
Ahhhh, ¿como en el EJ1? No, tampoco, no dije eso, cortale a la ansiedad.
Devolver como tercera coordenada el valor de la cuarta coordenada (que es la coordenada z negativa). Ese valor es importante porque nuestra perspectiva proyecta en z=-1.
¿Para qué queremos este tercer valor?, toda coordenada que tenga ese valor en menos de 1 debe ser descartada del dibujo porque está detrás de la cámara.
¿Pero qué tengo que dibujar?
Justo estaba por responder eso.
La ventana de SDL tiene un ancho y un alto definido en la configuración inicial. Ese ancho y alto está expresado en píxeles. Y, como es usual en monitores, las coordenadas equis van de 0 a ancho de izquierda a derecha, nada raro hasta acá, pero las yes van de 0 a alto de arriba a abajo. Es decir, el origen de coordenadas está arriba a la izquierda y el eje y apunta hacia abajo.
Si recordás del EJ1 las coordenadas nuestras de salida estaban todas muy cerca del origen. Bueno, nosotros estamos mirando justo al origen y queremos dibujar sólo lo que pasa entre -1 y 1.
Lo que pasa en nuestro juego lo vamos a llamar mundo, en nuestro mundo las cosas están en unidades de mundo y el foco de nuestra atención está en el origen. Lo que pasa en la pantalla lo vamos a llmar pantalla (qué originales) y sus coordenadas coordenadas de pantalla, en píxeles.
Dijimos que queríamos que el centro de la pantalla coincidiera con el origen, listo, eso es fácil. Ahora también dijimos que queremos meter en la pantalla lo que esté entre -1 y 1... o sea, un cuadrado, pero la pantalla es rectangular. Si forzáramos la transformación así entonces todo se ensancharía. Generalmente lo que queremos hacer es mantener la relación de aspecto, es decir escalar la misma cantidad las coordenadas en x que las coordenadas en y.
Dado que la pantalla es más ancha que alta y queremos mirar dentro del cuadrado -1,1 lo más razonable sería hacer que la izquierda y derecha de la pantalla coincidan con el cuadrado y perder la franja de arriba y abajo... pero en este curso no somos razonables. Vamos a forzar la parte de arriba y abajo y vamos a incluir en el campo cosas que están a izquierda y derecha que no se deberían ver... con lo que nuestra perspectiva va a terminar siendo un gran angular y haciendo efectos distorsivos.
Entonces tenemos una transformación lineal, el 0,0 va al centro de la pantalla, hay que estirar todo para que una recta de alto 2 cubra exactamente la altura, dar vuelta las yes... poniendo todo eso en la cocktelera nos queda que a las \((x, y)\) que salen de la proyección en coordenadas del mundo tendremos que traducirlas en coordenadas de pantalla según:
Siendo \(W\) el ancho de la pantalla y \(H\) el alto.
Al respecto de la primitiva de dibujar líneas de SDL2, la misma sabe tratar sin problemas con coordenadas que se escapan de la pantala, o sea, si después de hacer las conversiones hay que dibujar una línea que parte de una posición negativa o que supera al ancho o alto de la pantalla, la biblioteca sabe cómo dibujar la fracción que sí aparece en pantalla.
Contenido del STL
(Qué tiene lo pueden ver con el main del EJ3...)
Los modelos de sólo una letra están pensados para generar textos, algunos son letras literalmente, los que están entre A y Z, 0 y 9 y el espacio. Otros son cosas a imprimir en la pantalla, por fuera del motor 3D, y pueden utilizarse las mismas funciones que se utilizan para imprimir texto.
Estos son: * es el dibujito de un tanque para mostrar la cantidad de vidas
restantes, - y + son las dos miras, la segunda debe ser utilizada cuando
el enemigo esté a menos de 0.15 radianes para cada lado, la otra el resto del
tiempo, y finalmente # ese es el vidrio roto de recibir un disparo.
Los demás modelos son modelos propiamente 3D para dibujar en el motor gráfico:
TANQUE, TORRETA, RADAR, los tres permiten dibujar el tanque, como ya se
mencionó previamente. MISIL es el misil que disparan los tanques. CUBO1,
CUBO2, CUBO3, PIRAMIDE1, PIRAMIDE2, PIRAMIDE3, son los dibujos de los
obstáculos, cuando se crean los 50 obstáculos del juego aleatoriamente tienen
que ser creados con estos tipos. HORIZONTE es la línea de horizonte, se
dibuja sin transformar, es un círculo en el plano x,y centrado en el 0, las
MONTANAS y la LUNA están sobre el horizonte y se dibujan igual. LOGO
yo lo uso, me llevó un rato largo tipearlo, si querés usalo, si no no pasa
nada. RESTO1, RESTO2 son restos de tanque para usar en la animación de
destrucción.
Información en pantalla
Sobre la pantalla, escritos con texto o con dibujos deberá estar la siguiente información:
Cantidad de vidas restantes (sin incluir la que está siendo jugada), utilizar el carácter
*.Puntaje total del juego.
La mira del tanque. Como se dijo, la mira cambia según el ángulo entre el tanque del jugador y del enemigo. Si el enemigo está a manos de 0.15 radianes para cada lado, se debe utilizar el carácter
+, si no el carácter-.En la pantalla aparecerá un texto que indique, cuando el enemigo esté fuera del rango de visión, hacia dónde está. Se asume que el rango de visión es de 1 radián para cada lado. Si el ángulo fuera entre 1 y 2.44 radianes se mostrará el mensaje de que el tanque enemigo está a la izquierda, entre -1 y -2.44 que está a la derecha, y mayor en módulo a 2.44 que el enemigo está detrás.
Animaciones
Como ya se dijo (muy al principio, ¿ya te olvidaste?) hay dos animaciones: Cuando se destruye cualquiera de los tanques.
La animación de destrucción del tanque del jugador es la más sencilla. El
carácter # tiene 27 líneas, dibujarlas de a una por vez, eso creará el efecto
de cristal rompiéndose.
La animación de destrucción del tanque enemigo es un poco más difícil: Vas a tener que calcular un tiro oblicuo.
Empecemos con lo que dijimos antes, como las transformaciones 3D son acumulativas podemos pensar que estamos resolviendo algo en los ejes x,z con origen en el cero y eso va a funcionar perfecto si previamente apilamos las transformaciones de posición del tanque.
Entonces, el tiro oblicuo se dispara de la posición (0, 3), con velocidad en x = 5 m/s, y velocidad en z = 10 m/s (con g 9,81 en z negativo). La animación termina cuando se llega al piso.
Se deben dibujar 6 piezas: Una TORRETA, un RADAR dos RESTO1 y dos
RESTO2 cada uno a un sexto de circunferencia, es decir, rotando 60º en z cada
vez.
La operatoria es esa, partiendo de la transformación adonde se dibujaría el
tanque para cada pieza se debe rotar en z la cantidad de grados correspondiente
moverse en x,z lo que diga el cálculo de tiro oblicuo ** y dibujar la pieza
correspondiente. Luego desapilar estas últimas transformaciones y repetir con
las demás piezas.
¿Dejé un ** perdido en el párrafo anterior?, sí, era para no complejizar más,
agregá acá un par de rotaciones pequeñas en más de un eje para que las piezas
en vez de moverse en arco además giren sobre su propio eje.
Trabajo
Ya se dijo todo lo que había por decir. Se desarrollará un clon del Battle Zone, con las reglas dadas en la introducción.
El juego permitirá jugar, en principio, una única partida, desde el comienzo hasta el final.
El problema que se plantea en este trabajo deberá ser resuelto con las herramientas ya adquiridas, extendiendo de forma consistente los TDAs ya planteados de ser necesario y diseñando los TDAs nuevos que hagan falta. Se evaluará además el correcto uso de los TDAs respetando la abstracción y el "modelo" de Alan-Bárbara, así como la separación de la lógica del juego de la lógica de dibujado en la pantalla, que es un paso posterior sólo de presentación.
A esta altura del cuatrimestre es una obviedad decirlo pero: Todo lo que pueda ser iterativo o implementarse de forma indexada, debe ser hecho de esa manera. Todo lo que amerite tablas de búsqueda debe utilizarlas.
Un par de restricciones:
Las transformaciones se gestionan con una pila. Se debe utilizar la pila dada por la cátedra sin modificaciones de ningún tipo. Si se usa de forma directa o se utiliza dentro de un nuevo TDA en torno a ella queda a criterio del alumno.
En el EJ3 se implementaron funciones para leer un formato de datos pero para desacoplarlo de los ejercicios anteriores no se hizo nada con esos datos. Es obligatorio cargar los modelos del archivo STL en una lista, siendo la lista la provista por la cátedra. Por lo tanto será necesario tener un TDA que represente a cada uno de los modelos. Es obligatorio respetar la existencia de coordenadas y líneas como entidades separadas del formato original (y tiene todo el sentido, las coordenadas las vamos a proyectar, pero eso no afecta a las líneas que siempre son las mismas).
Por si te dio paja que el ítem anterior sea largo...
Es obligatorio tener un TDA para los modelos.
Es obligatorio tener una lista de TDAs e implementar la búsqueda de modelos.
Respetar la abstracción de los TDAs. Si no entendés el concepto de abstracción o desacople releé este enunciado y fijate que pude describir completa la lógica y reglas del juego sin ni una sola vez mencionar que iba a ser un juego con una interfaz gráfica 3D. O si no fijate que cuando implementaste las matrices no tenías ni idea de para qué se iban a utilizar.
Por fuera de eso, se deja libertad a la hora de implementar.
Se vuelve a repetir algo que se dijo bastante al comienzo pero que ayuda al diseño, los dos tanques hacen virtualmente las mismas cosas e interactúan con el entorno de forma similar, tiene sentido no duplicar su código.
Aplicación
Se pide diseñar una aplicación que pueda ser ejecutada como:
$ ./battlezone
que permita jugar al juego que describimos con toda la lógica que se mencionó.
Alcance
El alcance es lo especificado en este enunciado. En cualquier aplicación amplia como esta siempre pueden implementarse detalles adicionales, los mismos no forman parte del enunciado y no hay que hacer nada adicional para alcanzar la máxima nota. La prioridad es resolver completa la funcionalidad descrita para terminar con eso la materia.
Material
Fuentes
Se provee el archivo binario de modelos.
Se provee además el ejemplo de SDL2, el cual tiene las constantes que se
mencionaron de forma genérica en este enunciado, disponible para descargar en:
archivos_20261_tp1.tar.gz
Valgrind
En el caso de una aplicación construída con SDL2 se hace complejo el uso de Valgrind porque esta biblioteca tiene desprolijidades en su implementación que hacen que Valgrind tire múltiples errores y estos errores a la tasa que marquen los FPS del juego. Por eso para poder testear con Valgrind sobre el código desarrollado tenemos que suprimir los errores que son relativos a SDL2.
Se provee el siguiente archivo de supresiones para Valgrind:
suppressions_20261_tp1.supp
Con este archivo descargado Valgrind puede ser invocado como:
$ valgrind --suppressions=suppressions_20261_tp1.supp --leak-check=full ./battlezone
con lo que el programa se ejecutará suprimiendo los millones de errores de SDL2.
Al haber errores y haberlos suprimido el reporte no será del todo satisfactorio, y va a haber memoria en el heap y elementos marcados como "still reachable". El siguiente es un reporte de ejemplo:
==27213== Memcheck, a memory error detector
==27213== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27213== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==27213== Command: ./rezta
==27213==
==27213==
==27213== HEAP SUMMARY:
==27213== in use at exit: 196,326 bytes in 1,058 blocks
==27213== total heap usage: 32,817 allocs, 31,759 frees, 118,233,488 bytes allocated
==27213==
==27213== LEAK SUMMARY:
==27213== definitely lost: 0 bytes in 0 blocks
==27213== indirectly lost: 0 bytes in 0 blocks
==27213== possibly lost: 0 bytes in 0 blocks
==27213== still reachable: 2,037 bytes in 10 blocks
==27213== suppressed: 194,289 bytes in 1,048 blocks
==27213== Reachable blocks (those to which a pointer was found) are not shown.
==27213== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==27213==
==27213== For counts of detected and suppressed errors, rerun with: -v
==27213== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1018824 from 25)
Notar que pese a que se señala más de un millón de errores, el reporte de
pérdidas dice que no hay bloques ni definitiva, ni indirecta ni posiblemente
perdidos. Además el reporte, pese a haber sido corrido con --leak-check=full
no muestra ningún error en nuestro propio código.
Se busca que el reporte tenga estas características.
Ahora bien, prestar atención porque ignorar los errores de still reachable
puede enmascarar errores como por ejemplo no haber llamado a fclose() entre
otros.
Nota
Ejecutar con Valgrind implica a poner a un programa a analizar cada cosa que hace nuestro programa. Con un programa pesado en cantidad de operaciones como el nuestro, el programa va a andar muy lento.
La idea es correr con Valgrind como un chequeo a realizar, pero no todo el tiempo durante del desarrollo.
Para liberar la carga, sólo durante las corridas de Valgrind, se puede
reducir el valor de JUEGO_FPS a un valor que lo haga más manejable. Notar
que como el juego itera JUEGO_FPS las fallas de memoria más importante
van a ser evidentes con correr apenas un segundo de juego.
Funcionamiento
Este es un ejemplo de implementación con las reglas presentadas en este enunciado:
Entrega
Requisitos
Los únicos requisitos son que el programa resuelva correctamente la funcionalidad pedida y que lo haga utilizando TDAs con un diseño planificado a conciencia. Si hacés eso nada puede salir mal.
Entregables
Deberá entregarse:
El código fuente del trabajo debidamente documentado.
El archivo
Makefilepara compilar el proyecto.
Nota
El programa debe:
estar programado en C,
estar completo,
compilar...
sin errores...
sin warnings,
correr...
sin romperse...
sin fugar memoria...
resolviendo el problema pedido...
Trabajos que no cumplan con lo anterior no serán corregidos.
La entrega se realiza a través del sistema de entregas.
El ejercicio es grupal permitiéndose grupos de hasta 2 integrantes.
Si elegís hacer el trabajo en grupo te pedimos que nos avises o por mail o por Discord quién es tu compañero de grupo así lo cargamos en el sistema de entregas. (Si el grupo sufriera alteraciones avisanos también.)