Last active
March 22, 2023 12:17
-
-
Save arget13/07d55f6986a6ec776982076592fdb35b to your computer and use it in GitHub Desktop.
A simple implementation of asteroids game for modern terminals.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Name : Asteroids | |
Author : Arget | |
Version : 1.4 | |
Date : 16/11/2017 | |
Description : A simple implementation of asteroids game for modern terminals. | |
Notes : Compile with `gcc -o asteroids asteroids.c' | |
*/ | |
#include <stdio.h> /* printf(), putchar() */ | |
#include <termios.h> /* tcgetattr(), tcsetattr(), struct termios, | |
defs. of ECHO, ICANON, VTIME, VMIN, TCSANOW */ | |
#include <string.h> /* memcpy() */ | |
#include <stdlib.h> /* srandom(), random(), malloc(), realloc(), free() */ | |
#include <unistd.h> /* usleep(), read(), def. of STDIN_FILENO */ | |
#include <signal.h> /* sigaction(), struct sigaction, defs. of SIGTERM, | |
SIGINT */ | |
#include <time.h> /* time(), struct timeval, time_t */ | |
#include <sys/time.h> /* gettimeofday() */ | |
#include <sys/ioctl.h> | |
/* Valores números asociados a | |
las teclas de los cursores */ | |
#define UP 65 | |
#define DOWN 66 | |
#define RIGHT 67 | |
#define LEFT 68 | |
/* Facilitan la comprensión de qué | |
dato guarda cada elemento en los | |
arrays empleados para los asteroides */ | |
#define X 0 | |
#define Y 1 | |
/*** VARIABLES GLOBALES ***/ | |
/* Maneja la ejecución del bucle while en el que | |
se basa todo el juego. En el momento en que keeprunning | |
vale 0 el juego termina. | |
Esta variable la manipula la funcion inthandler() */ | |
int keeprunning; | |
/* Manejan la posición de la nave */ | |
unsigned int x, y; | |
unsigned int oldx, oldy; | |
/* Guardan la posición de la bala */ | |
unsigned int bulletx = 0; | |
unsigned int bullety = 0; | |
/* Almacenará la puntuación */ | |
unsigned int score; | |
/* Contiene el nivel en el que se encuentra el usuario */ | |
int level; | |
/* Número de vidas inicial */ | |
#ifndef INITLIVES | |
# define INITLIVES 5 | |
#endif | |
/* Almacena el número de vidas a cada momento */ | |
int lives; | |
/* Almacena el número de asteroides | |
simultáneos en el campo de juego */ | |
unsigned int numast; | |
/* Una tabla que relaciona cada | |
asteroide con su posición */ | |
unsigned int **asteroids; | |
/* Empleadas para mostrar el tiempo de | |
juego en la función gametime() */ | |
time_t init_time, last_time; | |
/* Manejan las coordenadas de la nueva vida */ | |
int livex, livey; | |
/* En esta estructura obtendremos el tamaño de la terminal */ | |
struct winsize ws; | |
/*** FUNCIONES ***/ | |
/* Dibuja la nave en la nueva posición tras borrarla | |
de la antigua */ | |
void draw_aircraft(int x, int y, int oldx, int oldy) | |
{ | |
if(oldx && oldy) | |
printf("\033[%d;%dH ", oldy, oldx - 1); | |
printf("\033[%d;%dH╔█╗", y, x - 1); | |
} | |
/* | |
void draw_aircraft(int x, int y, int oldx, int oldy) | |
{ | |
if(oldx && oldy) | |
printf("\033[%d;%dH \033[%d;%dH \033[%d;%dH ", | |
oldy - 1, oldx - 1, oldy, oldx - 1, oldy + 1, oldx - 2); | |
printf("\033[%d;%dH═╩═\033[%d;%dH███\033[%d;%dH/]x[\\", | |
y - 1, x - 1, y, x - 1, y + 1, x - 2); | |
}*/ | |
/* Dibuja en pantalla un campo de juego */ | |
void draw_field() | |
{ | |
int i; | |
printf("\033[1;1H╔\033[%1$u;1H╚\033[1;%2$uH╗\033[%1$u;%2$uH╝", ws.ws_row, ws.ws_col); | |
for(i = 2; i < ws.ws_row; i++) | |
printf("\033[%1$u;1H║", i); | |
for(i = 2; i < ws.ws_row; i++) | |
printf("\033[%1$u;%2$uH║", i, ws.ws_col); | |
for(i = 2; i < ws.ws_col; i++) | |
printf("\033[1;%1$uH═", i); | |
for(i = 2; i < ws.ws_col; i++) | |
printf("\033[%2$u;%1$uH═", i, ws.ws_row); | |
printf("\033[1;%uH Tiempo: ", ws.ws_col / 2 - 7); | |
} | |
/* Muestra la "mira" para apuntar */ | |
void show_sight(int x, int oldx) | |
{ | |
if(y == 2) return; | |
if(oldx) | |
printf("\033[%u;%uH \x1b[1;41m\033[%1$u;%3$uH \x1b[0;0m", 2, oldx, x); | |
else | |
printf("\x1b[1;41m\033[%u;%uH \x1b[0;0m", 2, x); | |
} | |
/* Sitúa en el campo de juego la bala en su posición */ | |
void draw_bullet() | |
{ | |
/* Cambiamos la posición de la bala */ | |
bullety--; | |
/* Borramos la bala y la redibujamos en la nueva posición */ | |
if(bullety > 1) printf("\033[%d;%3$dH \033[%2$d;%3$dH_", bullety + 1, bullety, bulletx); | |
else if(bullety == 1) printf("\033[%d;%dH ", bullety + 1, bulletx); | |
else | |
show_sight(x, 0); | |
} | |
/* Muestra en pantalla la puntuación */ | |
void draw_score() | |
{ | |
printf("\033[0;%1$uH \033[0;%1$uH Puntos: %2$07u ", ws.ws_col - 20, score); | |
} | |
void draw_lives() | |
{ | |
int i; | |
printf("\033[1;4H "); | |
printf("\033[1;11H"); | |
for(i = 0; i < INITLIVES; i++) | |
putchar(' '); | |
printf("\033[1;3H Vidas: "); | |
for(i = 0; i < lives; i++) | |
printf("♥"); | |
for(; i < INITLIVES; i++) | |
printf("×"); | |
putchar(' '); | |
} | |
/* Sitúa en el campo cada asteroide en su lugar | |
correspondiente */ | |
void draw_asteroids() | |
{ | |
int i; | |
/* Borramos de su antigua posición todos los asteroides */ | |
for(i = 0; i < numast; i++) | |
printf("\033[%u;%uH ", asteroids[i][Y], asteroids[i][X]); | |
/* Comprobamos si alguno tiene su y abajo del todo, | |
en ese caso los que hayan llegado abajo del todo | |
colocamos su y arriba del todo, si no están abajo | |
del todo simplemente incrementamos su y en 1 */ | |
for(i = 0; i < numast; i++) | |
asteroids[i][Y] = (asteroids[i][Y] > ws.ws_row - 2) ? 2 : asteroids[i][Y] + 1; | |
/* Los que se encuentren arriba del todo es que los | |
acabamos de colocar ahí en las instrucciones anteriores, | |
luego es necesario cambiar su x por un valor aleatorio dentro del | |
rango del campo, si no se encuentran arriba del todo mantenemos | |
su x sin variar */ | |
for(i = 0; i < numast; i++) | |
asteroids[i][X] = (asteroids[i][Y] == 2) ? (random() % (ws.ws_col - 2)) + 2 : asteroids[i][X]; | |
/* Imprimimos cada asteroide en su posición actual */ | |
for(i = 0; i < numast; i++) | |
printf("\033[%u;%uH*", asteroids[i][Y], asteroids[i][X]); | |
} | |
/* Muestra el tiempo de juego en segundos */ | |
void gametime(time_t t) | |
{ | |
last_time = t; | |
printf("\033[1;%2$uH%06u ", last_time - init_time, ws.ws_col / 2 + 2); | |
} | |
/* Imprime el nivel al que se ha llegado */ | |
void gamelevel(int level) | |
{ | |
printf("\033[%2$u;%3$uH Nivel %1$02u \033[%4$u;%5$uHNIVEL %1$02u", level, ws.ws_row, | |
ws.ws_col - 20, ws.ws_row / 2, ws.ws_col / 2 - 5); | |
sleep(1); | |
printf("\033[%u;%uH ", ws.ws_row / 2, ws.ws_col / 2 - 5); | |
} | |
/* Muestra el mensaje de "GAMEOVER" */ | |
void gameover() | |
{ | |
/* Limpiamos la pantalla de la terminal */ | |
printf("\033[H\033[J"); | |
/* Pintamos en pantalla el campo de juego */ | |
draw_field(); | |
/* Pintamos la puntuación */ | |
draw_score(); | |
printf("\033[%u;%uHGAMEOVER", ws.ws_row / 2, ws.ws_col / 2 - 4); | |
gametime(time(NULL)); | |
} | |
/* Establece la terminación del juego */ | |
void inthandler() | |
{ | |
gameover(); | |
printf("\033[%u;%uH Nivel %02u ", ws.ws_row, ws.ws_col - 20, level); | |
char c; | |
while(read(fileno(stdin), &c, 1) == 0 || c != ' '); | |
keeprunning = 0; | |
keeprunning = 0; | |
} | |
/* Maneja el movimiento de "vidas" nuevas que caen */ | |
void draw_new_live(int a) | |
{ | |
livey++; | |
if(livey == ws.ws_row) | |
{ | |
printf("\033[%u;%uH ", livey - 1, livex); | |
livey = 1; | |
livex = random() % (ws.ws_col - 2) + 2; | |
if(!a) return; | |
printf("\033[%u;%uH♥", livey, livex); | |
} | |
else | |
printf("\033[%1$u;%2$uH \033[%3$u;%2$uH♥", livey - 1, livex, livey); | |
} | |
/* Maneja la pausa en el juego */ | |
void gamepause() | |
{ | |
char c; | |
/* Indicamos la pausa */ | |
printf("\033[%u;%uHPAUSE", ws.ws_row / 2, ws.ws_col / 2 - 3); | |
/* y esperamos a que se presione p */ | |
while(keeprunning && (read(fileno(stdin), &c, 1) == 0 || c != 'p')); | |
printf("\033[%u;%uH ", ws.ws_row / 2, ws.ws_col / 2 - 3); | |
} | |
/* Se ejecuta si se cambia de tamaño la terminal */ | |
void winch() | |
{ | |
// ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); | |
keeprunning = 0; | |
} | |
int main(void) | |
{ | |
/* Un simple contador */ | |
unsigned int i; | |
/* Funcionarán para la temporización correcta | |
de cada evento del juego: movimiento de | |
asteroides, de bala, de vida nueva e incremento | |
de dificultad */ | |
struct timeval clk_ast, clk_ast_; | |
struct timeval clk_bul, clk_bul_; | |
time_t level_time, level_time_; | |
unsigned int level_time_diff; | |
time_t add_asteroid_time, add_asteroid_time_; | |
unsigned int add_asteroid_time_diff; | |
time_t live_time, live_time_; | |
unsigned int live_time_diff; | |
unsigned int live_last_time; | |
/* Participarán en el manejo de señales */ | |
struct sigaction act; | |
struct sigaction act2; | |
/* Servirán para el manejo de características de la terminal */ | |
struct termios term_attr; | |
struct termios old_term_attr; | |
/* Almacenará el valor de las teclas presionadas */ | |
char k; | |
/* Almacena el tiempo en microsecundos necesario para que | |
los asteroides avancen una posición, en cada nivel | |
este valor se decrementa en una 0,01s, comienza valiendo | |
0,1s */ | |
int ast_speed; | |
/* Si recibimos alguna señal de terminación | |
ejecutamos inthandler(), quien pondrá a 0 | |
la variable keeprunning, lo que hará que | |
el bucle while termine y se ejecuten funciones | |
de limpieza y terminemos */ | |
memset(&act, 0, sizeof(act)); | |
act.sa_handler = &inthandler; | |
sigaction(SIGINT, &act, NULL); | |
sigaction(SIGTERM, &act, NULL); | |
srandom(time(NULL)); | |
/* Obtenemos el tamaño de la terminal */ | |
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); | |
/* Si cambia de tamaño la terminal ejecutamos winch() */ | |
memset(&act2, 0, sizeof(act2)); | |
act2.sa_handler = &winch; | |
sigaction(SIGWINCH, &act2, NULL); | |
/* Establecemos los valores iniciales de ciertas variables del juego */ | |
ast_speed = 100000; | |
/* Obtenemos tiempo inicial para distintos parámetros */ | |
gettimeofday(&clk_ast, NULL); | |
memcpy(&clk_bul, &clk_ast, sizeof(struct timeval)); | |
level_time = time(NULL); | |
level_time_diff = 20; | |
add_asteroid_time = level_time; | |
add_asteroid_time_diff = 5; | |
live_time = level_time; | |
live_time_diff = random() % 60; | |
live_last_time = level_time; | |
live_time_diff = 5; | |
/* Deshabilitamos el búfer para stdout */ | |
setvbuf(stdout, NULL, _IONBF, 0); | |
/* Ocultamos el cursor */ | |
printf("\x1b[?25l"); | |
/* Limpiamos la pantalla de la terminal */ | |
printf("\033[H\033[J"); | |
/* Ponemos el stdin en modo raw, deshabilitamos el | |
echo de la terminal y leemos directamente las | |
pulsaciones del teclado */ | |
tcgetattr(STDIN_FILENO, &old_term_attr); | |
memcpy(&term_attr, &old_term_attr, sizeof(struct termios)); | |
term_attr.c_lflag &= ~(ECHO | ICANON); | |
term_attr.c_cc[VTIME] = 0; | |
term_attr.c_cc[VMIN] = 0; | |
tcsetattr(STDIN_FILENO, TCSANOW, &term_attr); | |
lives = INITLIVES; | |
score = 0; | |
x = ws.ws_col / 2; | |
y = ws.ws_row - 1; | |
keeprunning = 1; | |
numast = 1; | |
last_time = 0; | |
init_time = time(NULL); | |
livex = random() % ws.ws_col + 2; | |
livey = 1; | |
/* Mostramos el puntero de la mira, debe de ser antes de pintar | |
el campo, si no, al ser 0 el segundo argumento, | |
superior izquierda quedaría con un bloque blanco */ | |
show_sight(x, 0); | |
/* Pintamos en pantalla el campo de juego */ | |
draw_field(); | |
/* Mostramos las vidas */ | |
draw_lives(); | |
/* Mostramos la puntuación */ | |
draw_score(); | |
/* Colocamos la nave en su posición inicial */ | |
draw_aircraft(x, y, 0, 0); | |
/* Asignamos a asteroids espacio para apuntar a la instancia de cada | |
uno de los `numast' asteroides (numast empieza definiendo la cantidad | |
de asteroides iniciales, más adelante este valor se incrementará para | |
añadir más asteroides) */ | |
asteroids = malloc(numast * sizeof(void*)); | |
/* A cada instancia le asignamos espacio para almacenar | |
las coordenadas del asteroide correspondiente */ | |
for(i = 0; i < numast; i++) | |
asteroids[i] = malloc(2 * sizeof(int)); | |
for(i = 0; i < numast; i++) | |
{ | |
/* Cada asteroide lo inicializamos a una posición */ | |
asteroids[i][X] = (random() % (ws.ws_col) - 2) + 2; | |
asteroids[i][Y] = 2; | |
} | |
/* Comenzamos por el nivel 0 */ | |
level = 0; | |
gamelevel(level); | |
/* Este bucle constituye todo el juego | |
tras haberlo inicializado, se ejecuta hasta | |
recibir una señal SIGINT (ctrl-c) o SIGTERM, | |
o hasta que se terminen las vidas del jugador */ | |
while(keeprunning) | |
{ | |
/* Además de asignar a level_time_ el valor | |
de time(), que se comprobará más adelante | |
para aumentar el nivel de dificultad del juego, | |
este código comprueba si ha transcurrido | |
al menos 1 segundo desde la última llamada a gametime(), | |
quien imprime la cantidad de segundos transcurridos desde | |
el comienzo del juego */ | |
if(time(&level_time_) > last_time) | |
gametime(level_time_); | |
/* También asignamos el tiempo actual a add_asteroid_time_ y | |
a live_time_ , que se comprobarán más adelante para añadir | |
un asteroide o para introducir en el campo de juego una vida | |
que podrá recoger el jugador */ | |
add_asteroid_time_ = level_time_; | |
live_time_ = level_time_; | |
if(live_time_ > live_last_time && livey > 1) | |
{ | |
live_last_time = live_time_; | |
draw_new_live(0); | |
} | |
/* Obtenemos la hora actual para comparar con una anterior | |
y saber si ya toca ejecutar algún evento del juego: mover | |
asteroides o balas*/ | |
gettimeofday(&clk_ast_, NULL); | |
memcpy(&clk_bul_, &clk_ast_, sizeof(struct timeval)); | |
/* Se comprueba si hay | |
presionada alguna tecla */ | |
if(read(fileno(stdin), &k, 1) == 1) | |
{ | |
oldy = y; | |
oldx = x; | |
/* Efectuamos las acciones respectivas | |
a cada tecla */ | |
switch(k) | |
{ | |
case 's': | |
if(y < ws.ws_row - 1) /* Terminal de ws.ws_col x ws.ws_row */ | |
y++; | |
break; | |
case DOWN: | |
if(y < ws.ws_row - 1) | |
y++; | |
break; | |
case 'w': | |
if(y > 2) | |
y--; | |
break; | |
case UP: | |
if(y > 2) | |
y--; | |
break; | |
case 'a': | |
if(x > 3) | |
x--; | |
break; | |
case LEFT: | |
if(x > 3) | |
x--; | |
break; | |
case 'd': | |
if(x < ws.ws_col - 2) | |
x++; | |
break; | |
case RIGHT: | |
if(x < ws.ws_col - 2) | |
x++; | |
break; | |
case ' ': | |
if(!bullety) /* Se dispara una bala solo si | |
no hay otra todavía en el campo */ | |
{ | |
bullety = y - 1; | |
bulletx = x; /* La bala se mueve en vertical */ | |
} | |
break; | |
case 'p': | |
gamepause(); | |
break; | |
} | |
/* Redibujamos la nave en la nueva posición */ | |
draw_aircraft(x, y, oldx, oldy); | |
if(oldx != x) show_sight(x, oldx); | |
} | |
/* Dibujamos la bala en la nueva posición, si no ha | |
sido disparada no se hace nada | |
Avanza a una velocidad de una posición cada 0,05 segundos */ | |
if(bullety && ((((long long)(clk_bul_.tv_sec * 1000000) + clk_bul_.tv_usec) | |
- ((long long)(clk_bul .tv_sec * 1000000) + clk_bul .tv_usec)) >= 50000)) | |
{ | |
draw_bullet(); | |
memcpy(&clk_bul, &clk_bul_, sizeof(struct timeval)); | |
} | |
/* Comprobamos para cada asteroide si sus coordenadas coinciden con las de | |
la bala, en ese caso borramos ambos y reiniciamos sus posiciones. Esta comprobación | |
se realiza antes de mover los asteroides para evitar que se crucen | |
bala y asteroide sin detectarse. */ | |
for(i = 0; i < numast; i++) | |
{ | |
if(bulletx == asteroids[i][X] && bullety == asteroids[i][Y]) | |
{ | |
asteroids[i][X] = (random() % (ws.ws_col - 2)) + 2; | |
asteroids[i][Y] = 2; | |
printf("\033[%d;%dH ", bullety, bulletx); | |
printf("\033[%d;%dH+10", bullety, bulletx - 1); | |
usleep(500000); | |
printf("\033[%d;%dH ", bullety, bulletx - 1); | |
bullety = 0; | |
show_sight(x, oldx); | |
/* Actualizamos la puntuación */ | |
score += 10; | |
draw_score(); | |
} | |
} | |
/* Dibujamos cada asteroide en su nueva posición respectiva, | |
avanzan todos a la vez a una velocidad de una posición cada | |
0,1 segundos */ | |
if((((long long)(clk_ast_.tv_sec * 1000000 + clk_ast_.tv_usec)) | |
- ((long long)(clk_ast .tv_sec * 1000000 + clk_ast .tv_usec))) >= ast_speed) | |
{ | |
memcpy(&clk_ast, &clk_ast_, sizeof(struct timeval)); | |
draw_asteroids(); | |
} | |
/* Comprobamos si un asteroide ha tocado alguna parte de la nave */ | |
for(i = 0; i < numast; i++) | |
{ | |
if(asteroids[i][X] >= x - 1 && asteroids[i][X] <= x + 1) | |
{ | |
if(asteroids[i][Y] == y) | |
{ | |
lives--; | |
draw_lives(); | |
putchar('\a'); | |
if(lives == 0) | |
{ | |
gameover(); | |
printf("\033[%u;%uH Nivel %02u ", ws.ws_row, ws.ws_col - 20, level); | |
char c; | |
while(read(fileno(stdin), &c, 1) == 0 || c != ' '); | |
keeprunning = 0; | |
} | |
else | |
{ | |
asteroids[i][X] = (random() % (ws.ws_col - 2)) + 2; | |
asteroids[i][Y] = 2; | |
draw_aircraft(x, y, 0, 0); | |
} | |
} | |
} | |
} | |
/* Comprobamos si la vida ha tocado la nave */ | |
if(livex >= x - 1 && livex <= x + 1) | |
{ | |
if(livey == y) | |
{ | |
/* Normalmente se añade al jugador una vida, */ | |
if(lives < INITLIVES) | |
{ | |
lives++; | |
draw_lives(); | |
} | |
else /* pero si ya se tiene el max de vidas, se dan 50 puntos */ | |
{ | |
printf("\033[%u;%uH+50", y, x - 1); | |
score += 50; | |
draw_score(); | |
usleep(500000); | |
printf("\033[%u;%uH ", y, x - 1); | |
} | |
livey = 1; | |
livex = random() % (ws.ws_col - 2) + 2; | |
draw_aircraft(x, y, 0, 0); | |
} | |
} | |
/* Si un asteroide toca la vida, esta desaparece y el asteroide sigue | |
(balas y asteroides van mucho más rápido que las vidas) */ | |
for(i = 0; i < numast; i++) | |
{ | |
if(livex == asteroids[i][X] && livey == asteroids[i][Y]) | |
{ | |
livey = 1; | |
livex = random() % (ws.ws_col - 2) + 2; | |
} | |
} | |
/* Si la bala toca la vida, esta desaparece y la bala sigue */ | |
if(livex == bulletx && livey == bullety) | |
{ | |
livey = 1; | |
livex = random() % (ws.ws_col - 2) + 2; | |
} | |
/* Si se ha cumplido el tiempo necesario para añadir un asteroide, lo añadimos */ | |
if((add_asteroid_time_ - add_asteroid_time) >= add_asteroid_time_diff) | |
{ | |
/* Incrementamos el tiempo necesario para añadir un asteroide */ | |
add_asteroid_time = add_asteroid_time_; | |
add_asteroid_time_diff += 10; | |
/* Incrementamos el contador de asteroides */ | |
numast++; | |
/* Primero al puntero asteroids le asignamos suficiente espacio | |
para apuntar a una nueva "instancia de asteroide" */ | |
asteroids = realloc(asteroids, numast * sizeof(int*)); | |
/* Luego asignamos a esta última entrada espacio | |
suficiente para almacenar el x e y del nuevo asteroide */ | |
asteroids[numast - 1] = malloc(2 * sizeof(int)); | |
/* Inicializamos la posición del nuevo asteroide */ | |
asteroids[numast - 1][X] = (random() % (ws.ws_col - 2)) + 2; | |
asteroids[numast - 1][Y] = 2; | |
} | |
/* Cada level_time_diff segundos, se aumenta la dificultad, | |
se aumenta 10 veces la velocidad de los asteroides */ | |
if((level_time_ - level_time) >= level_time_diff) | |
{ | |
level++; | |
gamelevel(level); | |
draw_aircraft(x, y, 0, 0); | |
/* Subimos el nivel aumentando la velocidad de los asteroides */ | |
level_time = level_time_; | |
level_time_diff += 10; | |
/* Ahora tarda una centésima menos en avanzar una posición */ | |
ast_speed -= 10000; | |
} | |
/* Cuando se cumpla el tiempo especificado por live_time_diff | |
se añadirá (si no está ya) una vida al campo de juego */ | |
if((live_time_ - live_time) >= live_time_diff) | |
{ | |
live_time = live_time_; | |
live_time_diff = random() % 60; | |
if(livey == 1) | |
{ | |
livey = 2; | |
draw_new_live(1); | |
} | |
} | |
} | |
/* Liberamos toda la memoria reservada dinámicamente */ | |
for(i = 0; i < numast; i++) | |
free(asteroids[i]); | |
free(asteroids); | |
/* Restablecemos la configuración de la terminal a la original */ | |
tcsetattr(STDIN_FILENO, TCSANOW, &old_term_attr); | |
/* Limpiamos la pantalla de la terminal */ | |
printf("\033[H\033[J"); | |
/* Mostramos el cursor de la terminal */ | |
printf("\x1b[?25h"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment