Saltar al contenido principal

Trabajo Práctico Nº 1

HIT&RUN

Fecha de presentaciónFecha de entrega
17/09/202623/10/2026

1. Introducción

El monorriel de Springfield se puso en marcha aunque no haya pasado la inspección. Se descontroló, avanza a toda marcha y la ciudad está en peligro. La única forma de evitar una tragedia es que ayudes a Homero a llegar a la última estación antes que el monorriel para poder frenarlo. Para eso, Homero deberá manejar por la ciudad con tráfico, reglas y peligros que pueden ayudarlo, o no, a llegar a tiempo.


2. Objetivo

El presente trabajo práctico tiene como objetivo evaluar a los alumnos en aspectos fundamentales de la programación.

Entre ellos:

  • Diseñe y desarrolle las funcionalidades de una biblioteca con un contrato preestablecido.
  • Se familiarice con y utilice correctamente los tipos de datos estructurados.
  • Desarrolle una interfaz gráfica amigable y entendible para el usuario.

Por supuesto, se requiere que el trabajo cumpla con los objetivos de los previos trabajos prácticos en especial con las buenas prácticas de programación profesadas por la cátedra. Se considerarán críticos la modularización, reutilización y claridad del código.


3. Enunciado

Se solicita implementar un programa que permita a Homero frenar el monorriel antes de que este llegue a la última estación.

El juego consiste en manejar por las calles de Springfield con Homero, evitando cualquier posible choque con los otros vehiculos. Se deberá recolectar un ancla que luego debe ser llevada a la última estación del monorriel, antes de que el mismo llegue a ésta.

Homero tendrá nafta disponible para circular que se gastará con cada movimiento que realice.

Al comenzar el juego, se deberá posicionar los elementos que lo componen en posiciones aleatorias en el terreno.

IMPORTANTE
  • Ningún elemento puede inicializarse por fuera de los límites del terreno ni pisar otros elementos ya inicializados, a menos que se indique lo contrario.

3.1 Homero

Al comenzar el juego, Homero estará en la posición (19, 19).

Tendrá 100 de nafta y gastará 1 con cada movimiento. En caso de quedarse sin nafta antes de cumplir con el objetivo, se pierde el juego.

Podrá circular únicamente por las calles.

3.2 Calles

Las calles son las posiciones en las que Homero puede moverse en el terreno. Serán dadas por la cátedra.

3.3 Autos

Además de Homero, habrá otros autos transitando por las calles. Al iniciar el juego, debe haber 6 autos circulando por la ciudad, la nafta debe ser un número aleatorio entre 30 y 50. Cada 5 movimientos, aparecerá un auto nuevo. La velocidad a la que circularán (cuantos casilleros por vez se mueven) será entre 1 y 4 y se definirá de forma aleatoria. La nafta que gastarán los autos en cada movimiento será igual a la velocidad a la que se mueven.

Cada auto tendrá un recorrido fijo en las calles. Este será dado por la cátedra para garantizar el correcto funcionamiento. Los autos sólo se mueven en la dirección del camino. Una vez que su recorrido termine deben ser eliminados.

Si un auto se queda sin nafta, se quedará detenido en la posición donde está.

Si Homero choca con un auto, se pierde el juego. Si dos o más autos coinciden en una misma posición, no tiene ningún efecto sobre ellos.

3.4 Grúa

Homero podrá llamar a la grúa un máximo de 5 veces, en cualquier momento del juego.

Cuando se llama a la grúa, se eliminarán todos los autos que estén en la misma calle que se encuentra Homero (horizontal o vertical) que no tengan nafta.

Estar en la misma calle quiere decir estar en la misma fila o columna que Homero, pero además que la calle entre Homero y el/los auto/s a eliminar debe ser contínua, o sea que no debe haber baches en el medio sin calle.

Los autos eliminados deben de dejar de estar presentes dentro del vector de vehículos.

3.5 Policia

Homero podrá llamar a la policía de tránsito un máximo de 4 veces, en cualquier momento del juego.

Cuando se llama a la policía, se frenarán todos los autos (excepto el de Homero) por 5 movimientos.

3.6 Turbo

Si se necesita más velocidad, Homero podrá activar el turbo en el auto para ir más rápido. Se puede usar hasta 3 veces. Homero pasará a moverse de a 2 casilleros por vez.

El efecto turbo durará hasta 5 movimientos y gastará 3 de nafta por movimiento.

3.7 Estaciones de servicio

Habrá 20 estaciones de servicio en el terreno.

Deberán ser inicializadas en posiciones aleatorias que sean adyacentes a las calles y cargarán la nafta de aquellos autos que se encuentren a distancia manhattan igual a uno. La estación de servicio la puede usar Homero y cualquier otro auto.

Al pasar por una estación de servicio, al vehículo se le sumará 30 de nafta.

Homero nunca puede tener más de 150 de nafta.

3.8 Monorriel

El recorrido del monorriel será un cuadrado dentro del terreno, de forma tal que se moverá por la columna 1, la fila 1, la anteúltima columna y la anteúltima fila.

En cada esquina de ese recorrido habrá una estación que lo hará parar por 5 movimientos. Es un monorriel que ocupa 4 posiciones (4 vagones) y se considera que llega a la estación cuando el primer vagón llega a la estación.

El monorriel comienza su recorrido en la estación que está en la esquina superior izquierda del recorrido (1;1), se mueve contra las agujas del reloj y su recorrido se da por terminado cuando llega a la misma estación de la que salió.

Si Homero choca con cualquiera de los vagones del monorriel, pierde el juego.

3.9 Semáforo

Habrá 15 semáforos que estarán ubicados en lugares aleatorios del terreno que sean adyacentes a una calle. Cuando un auto esté a distancia manhattan 1 de un semáforo, éste lo frena por 2 movimientos.

Los semáforos no afectan a Homero.

3.10 Lomas de burro

Habrá 10 lomas de burro ubicadas de forma aleatoria en las calles.

Al pasar un auto sobre una loma de burro, ésta reducirá en 1 su velocidad por el resto de su recorrido. Si la velocidad del auto ya es 1, no tendrá ningún efecto.

Las lomas de burro no afectan a Homero.

3.11 Ancla

El ancla se inicializará de forma aleatoria en alguna posición que se encuentre sobre una de las calles.

Homero recolectara el ancla al posicionarse sobre ella.

3.12 Terreno

El terreno está compuesto por los elementos mencionados anteriormente, cada uno de ellos con una coordenada (x, y) con x siendo un valor entre 0 y 24 inclusives e y siendo un valor entre 0 y 24 inclusives.


4. Modo de juego

Al moverse, Homero no puede pasarse de los límites del terreno, ni salirse de las calles.

Por ejemplo, si Homero está en la fila 0 y el usuario lo mueve para arriba, Homero debería quedarse ahí, no se debería mover porque estaría saliéndose del terreno.

Un intento de movimiento inválido no cuenta como movimiento.

IMPORTANTE
  • Antes de realizar el movimiento, se debe validar lo que ingresa el usuario, volviéndole a preguntar hasta que ingrese un movimiento correcto.

Homero se podrá mover en 4 direcciones:

  • Arriba: W
  • Abajo: S
  • Derecha: D
  • Izquierda: A

Además, podrá realizar estas acciones:

  • Grúa: G
  • Turbo: T
  • Policía: P

Éstas tres acciones no consumen nafta, ni hace avanzar a los demás vehículos.

Luego de realizar una acción en caso de chocar o estar a la distancia de reacción con un elemento se activará la reacción relacionada al mismo que afectará a Homero, a los autos y el estado del juego, según corresponda. En caso de estar con el turbo activado, se deberá chequear reacción en cada posición por la que se pase, no solo en la final. En cambio, para los autos que tengan velocidad mayor a 1, solo deben chequear la reacción con la posición donde llegan, no con las intermedias.

El orden del juego es: luego de que el usuario ingresa una letra válida, se mueven todos los autos, realizando las acciones que correspondan dependiendo de los elementos que tengan cerca, luego se mueve Homero y se realizan las acciones que correspondan dependiendo de los elementos que tenga cerca.

El juego se dará por perdido cuando se cumpla alguna de las siguientes condiciones:

  • Homero se queda sin nafta.
  • Homero choca con otro vehiculo (auto o monorriel).
  • El monorriel llega a la última estación antes que Homero.

El juego se dará por ganado cuando:

  • Homero recolecta el ancla y llega a la última estación antes que el monorriel.

5. Especificaciones

5.1 Convenciones

  • Homero: H
  • Autos: A
  • Monorriel: M
  • Semáforo: S
  • Loma de burro: L
  • Estación de servicio: E
  • Ancla: C

Para representar las calles, se deberá dejar el espacio vacío y poner X en las posiciones en las que no hay calle ni ningún otro elemento. Esto es para poder representar de mejor manera los lugares por los que se puede mover Homero.

5.2 Biblioteca Utiles

Como se comentó anteriormente, los caminos de los autos serán brindados por una biblioteca utiles.h implementada por la cátedra, como así también las calles por las que podrá circular homero.

#ifndef __UTILES_H__
#define __UTILES_H__

#define MAX_CALLES 500
#define MAX_RECORRIDO 100

/*
* Recibe un vector de coordenadas y su tope. En el vector se almacenan las calles de la ciudad.
* Inicializa el vector con las coordenadas de las calles.
*/
void obtener_calles(coordenada_t calles[MAX_CALLES], int *tope_calles);

/*
* Recibe un vector de coordenadas y su tope
* Inicializa el vector con las coordenadas de algún recorrido.
*/
void obtener_recorrido(coordenada_t recorrido[MAX_RECORRIDO], int *tope_recorrido);

#endif

5.3 Funciones y procedimientos

A continuación está la biblioteca que se deberá realizar y que va a contener todo lo necesario para el desarrollo del juego. Este archivo .h NO se puede modificar de ninguna forma.

#ifndef __HITRUN_H__
#define __HITRUN_H__

#include <stdbool.h>

#define MAX_AUTOS 200
#define MAX_OBJETOS 150
#define MAX_FILAS 25
#define MAX_COLUMNAS 25
#define MAX_RECORRIDO 100
#define MAX_POSICIONES 4
#define MAX_CALLES 500

typedef struct coordenada {
int fil;
int col;
} coordenada_t;

typedef struct personaje {
coordenada_t posicion;
bool recolecto_ancla;
int gruas_disponibles;
int policias_disponibles;
int turbos_disponibles;
int movs_con_turbo;
int nafta;
} personaje_t;

typedef struct vehiculo {
int posicion;
coordenada_t recorrido[MAX_RECORRIDO];
int tope_recorrido;
int nafta;
int movimientos_frenado;
int velocidad;
} vehiculo_t;

typedef struct monorriel {
int posiciones[MAX_POSICIONES];
int tope_posiciones;
coordenada_t recorrido[MAX_RECORRIDO];
int tope_recorrido;
int movimientos_frenado;
} monorriel_t;

typedef struct objeto {
char tipo;
coordenada_t posicion;
} objeto_t;

typedef struct juego {
personaje_t homero;
vehiculo_t vehiculos[MAX_AUTOS];
int tope_vehiculos;
objeto_t objetos[MAX_OBJETOS];
int tope_objetos;
coordenada_t calles[MAX_CALLES];
int tope_calles;
coordenada_t ancla;
monorriel_t monorriel;
int movs_realizados;
} juego_t;

/*
* Pre condiciones: -
* Post condiciones: Inicializará el juego, cargando toda la información inicial de Homero, Moe, los bloques, las herramientas y los obstáculos.
*/
void inicializar_juego(juego_t* juego);


/*
* Pre condiciones: El juego debe estar inicializado previamente con `inicializar_juego` y la acción
* debe ser válida.
* Post condiciones: Realizará la acción recibida por parámetro actualizando el juego.
*/
void realizar_jugada(juego_t* juego, char movimiento);

/*
* Pre condiciones: El juego debe estar inicializado previamente con `inicializar_juego `.
* Post condiciones: Imprime el juego por pantalla.
*/
void mostrar_juego(juego_t juego);

/*
* Pre condiciones: El juego deberá estar inicializado previamente con `inicializar_juego `
* Post condiciones: Devuelve:
* --> 1 si es ganado
* --> -1 si es perdido
* --> 0 si se sigue jugando
* El juego se dará por ganado cuando Homero recolecta el ancla y llega a la última estación antes del monorriel
* Se dará por perdido si Homero se queda sin nafta o si choca con otro vehículo.
*/
int estado_juego(juego_t juego);


#endif // _HITRUN__H__

6. Resultado Esperado

El trabajo práctico es un juego. Se espera que el trabajo cumpla la funcionalidad explicada anteriormente. Se deberá:

  • Implementar todas las funciones especificadas en la biblioteca.
  • Inicializar todos los campos del registro juego_t.
  • Pedirle al usuario que ingrese una acción válida a realizar cada turno.
  • Mostrar todos los elementos en forma de terreno de forma clara con información que pueda serle útil al usuario (cuántas energía restante, dialogos, etc).
  • Respetar las buenas prácticas de programación que profesamos en la cátedra.

6.1 Compilación y entrega

La funcionalidad indicada en .h debe ser implementada en un archivo llamado:

hitrun.c

conformando una biblioteca que luego será utilizada por el programa principal en un archivo llamado:

juego.c

e incluyendo el pedido del movimiento al usuario y la validación del mismo.

Se espera que el programa compile sin errores utilizando la siguiente línea:

gcc juego.c hitrun.c utiles.o -o juego -std=c99 -Wall -Wconversion -Werror -lm

Por último debe ser entregado en la plataforma de corrección de trabajos prácticos AlgoTrón (patente pendiente), en la cual deberá tener la etiqueta ¡Exito! significando que ha pasado las pruebas a las que la cátedra someterá al trabajo.

IMPORTANTE

La etiqueta ¡Éxito! es un requisito necesario pero no suficiente para la aprobación del trabajo práctico.
El trabajo deberá cumplir tanto con las pruebas automatizadas como con los criterios de calidad evaluados por la cátedra.

ACLARACIÓN

Para la entrega en AlgoTrón (patente pendiente), recuerde que deberá subir un archivo zip conteniendo únicamente los archivos antes mencionados, sin carpetas internas ni otros archivos. De lo contrario, la entrega no será validada por la plataforma.


7. Anexos

7.1 Obtención de números aleatorios en C

Para obtener números aleatorios debe utilizarse la función rand(), la cual está disponible en la biblioteca stdlib.h.

Esta función devuelve números pseudo-aleatorios, esto quiere decir que, cuando uno ejecuta nuevamente el programa, los números, aunque aleatorios, son los mismos.

Para resolver este problema debe inicializarse una semilla, cuya función es determinar desde dónde empezarán a calcularse los números aleatorios.

Los números arrojados por rand() son enteros sin signo, generalmente queremos que estén acotados a un rango (queremos números aleatorios entre tal y tal). Para esto, podemos obtener el resto de la división de rand() por el valor máximo del rango que necesitamos.

Aquí dejamos un breve ejemplo de como obtener números aleatorios entre 10 y 30.

#include <stdio.h>
#include <stdlib.h> // Para usar rand
#include <time.h> // Para obtener una semilla desde el reloj

int main(){
srand ((unsigned)time(NULL));
int numero = rand() % 20 + 10; // la amplitud del rango es 20 y el valor mínimo es 10.
printf("El valor aleatorio es: %i\n", numero);

return 0;
}

7.2 Limpiar la pantalla durante la ejecución de un programa

Muchas veces nos gustaría que nuestro programa pueda verse siempre en la pantalla sin ver texto anterior.

Para esto, podemos utilizar la llamada al sistema clear, de esta manera, limpiaremos todo lo que hay en nuestra terminal hasta el momento y podremos dibujar la información actualizada.

Y se utiliza de la siguiente manera:

#include <stdio.h>
#include <stdlib.h>

int main(){
printf("Escribimos algo\n");
printf("que debería\n");
printf("desaparecer...\n");

system("clear"); // Limpiamos la pantalla

printf("Solo deberiamos ver esto...\n");
return 0;
}

7.3 Distancia Manhattan

Para obtener la distancia entre 2 puntos mediante este método, se debe conocer a priori las coordenadas de dichos puntos.

Luego, la distancia entre ellos es la suma de los valores absolutos de las diferencias de las coordenadas. Se ve claramente en los siguientes ejemplos:

  • La distancia entre los puntos (0,0) y (1,1) es 2 ya que:
| 0 - 1 | + | 0 - 1 | = 1 + 1 = 2
  • La distancia entre los puntos (10,5) y (2,12) es 15 ya que:
| 10 - 2 | + | 5 - 12 | = 8 + 7 = 15
  • La distancia entre los puntos (7,8) y (9,8) es 2 ya que:
| 7 - 9 | + | 8 - 8 | = 2 + 0 = 2

8. FAQs

A continuación dejamos preguntas frecuentes que surgieron de la consigna y sus respuestas.

  1. ¿Se puede generar el ancla, lomas de burro y demas objetos por donde pasa el monorriel? No, se debe que evitar que se generen en esas posiciones.