/* Implementación en C del programa pong.s */ typedef unsigned long long Hora; // en milisegundos desde 1970-01-01T0:00 typedef struct { int x; int y; } Vector; typedef Vector Punto; /* configuración del juego */ static const int alto_campo = 1000000; static const int ancho_campo = 2000000; static const Vector aceleracion_rebote = { .x = 25, .y = 150 }; static int alto_jugador; static int velocidad_jugador; /* configuración del juego relativa al dibujo del tablero */ static const int ancho_dibujo = 40; static const int alto_dibujo = 16; static const int fps_max = 5; /* estado de la partida actual */ static Punto posicion_pelota; static Vector velocidad_pelota; static Punto posicion_jugador_izquierdo; static Punto posicion_jugador_derecho; static int puntuacion_jugador_izquierdo; static int puntuacion_jugador_derecho; /* Multiplica un vector de enteros por un entero */ static Vector prod_vector_int(Vector v, int c) { Vector ret = { .x = v.x * c, .y = v.y * c }; return ret; } /* Suma dos vectores de enteros */ static Vector suma_vector(Vector a, Vector b) { Vector ret = { .x = a.x + b.x, .y = a.y + b.y }; return ret; } /* Dibuja el campo de juego */ static void dibuja_campo(void) { /* transforma las coordenadas del espacio del juego al espacio de la * pantalla (dibujo) */ const float escala_x = ((float) ancho_dibujo) / ancho_campo; const float escala_y = ((float) alto_dibujo) / alto_campo; const int e_pelota_x = posicion_pelota.x * escala_x; const int e_pelota_y = posicion_pelota.y * escala_y; const int e_jug_izq_y_sup = posicion_jugador_izquierdo.y * escala_y; const int e_jug_izq_y_inf = (posicion_jugador_izquierdo.y + alto_jugador) * escala_y; const int e_jug_izq_x = posicion_jugador_izquierdo.x * escala_x; const int e_jug_der_y_sup = posicion_jugador_derecho.y * escala_y; const int e_jug_der_y_inf = (posicion_jugador_derecho.y + alto_jugador) * escala_y; const int e_jug_der_x = posicion_jugador_derecho.x * escala_x; print_character('+'); for (int x = 0; x < ancho_dibujo; ++x) { print_character('#'); } print_character('+'); print_character('\n'); for (int y = 0; y < alto_dibujo; ++y) { print_character('#'); for (int x = 0; x < ancho_dibujo; ++x) { print_character((x == e_pelota_x && y == e_pelota_y) ? 'O' : x == e_jug_izq_x && y >= e_jug_izq_y_sup && y < e_jug_izq_y_inf ? ']' : x == e_jug_der_x && y >= e_jug_der_y_sup && y < e_jug_der_y_inf ? '[' : ' '); } print_character('#'); print_character('\n'); } print_character('+'); for (int x = 0; x < ancho_dibujo; ++x) { print_character('#'); } print_character('+'); print_character('\n'); } /* Muestra los marcadores alineando uno a la izquierda y otro a la derecha */ static void dibuja_marcadores(void) { print_integer(puntuacion_jugador_izquierdo); int nespacios = ancho_dibujo; for (int i = puntuacion_jugador_izquierdo / 10; i > 0; i = i / 10) { nespacios = nespacios - 1; } for (int i = puntuacion_jugador_derecho / 10; i > 0; i = i / 10) { nespacios = nespacios - 1; } for (int i = nespacios; i > 0; --i) { print_character(' '); } print_integer(puntuacion_jugador_derecho); print_character('\n'); } typedef enum {izquierda, derecha} Direccion; /* Pone los jugadores en sus posiciones iniciales y una bola en movimiento */ static void inicia_bola(Direccion dir_bola) { // Pone el centro de la pelota en el centro de una casilla central del campo // (la pelota y las casillas miden alto_campo/alto_dibujo de alto y // ancho_campo/ancho_dibujo de ancho) posicion_pelota.x = ancho_campo / 2 - (ancho_campo / ancho_dibujo) / 2; posicion_pelota.y = alto_campo / 2 - (alto_campo / alto_dibujo) / 2; // Pone los jugadores a media casilla de distancia desde el borde de la pista // y a la misma altura que la pelota posicion_jugador_izquierdo.x = (ancho_campo / ancho_dibujo) / 2; posicion_jugador_izquierdo.y = posicion_pelota.y - alto_jugador / 2; posicion_jugador_derecho.x = ancho_campo - ((ancho_campo / ancho_dibujo) / 2); posicion_jugador_derecho.y = posicion_pelota.y - alto_jugador / 2; // Pone la pelota en movimiento velocidad_pelota.x = dir_bola == izquierda ? -150 : 150; velocidad_pelota.y = 0; } /* Pone a cero los marcadores y empieza una partida nueva */ static void inicia_partida(void) { puntuacion_jugador_derecho = 0; puntuacion_jugador_izquierdo = 0; inicia_bola(derecha); } /* Mueve la pelota y detecta colisiones. "transcurrido" indica cuántos * milisegundos han pasado desde la última actualización */ static void avanza_pong(int transcurrido) { posicion_pelota = suma_vector(posicion_pelota, prod_vector_int(velocidad_pelota, transcurrido)); // rebote con pala derecha if (posicion_pelota.x > posicion_jugador_derecho.x && posicion_pelota.y >= posicion_jugador_derecho.y && posicion_pelota.y < posicion_jugador_derecho.y + alto_jugador) { posicion_pelota.x = posicion_jugador_derecho.x - (posicion_pelota.x - posicion_jugador_derecho.x); velocidad_pelota.x = -velocidad_pelota.x - aceleracion_rebote.x; int dist_pelota_centro_pala = posicion_pelota.y - (posicion_jugador_derecho.y + (alto_jugador / 2)); float f = ((float) dist_pelota_centro_pala) / (alto_jugador / 2); velocidad_pelota.y = velocidad_pelota.y + f * aceleracion_rebote.y; } // rebote con pala izquierda else if (posicion_pelota.x < posicion_jugador_izquierdo.x && posicion_pelota.y >= posicion_jugador_izquierdo.y && posicion_pelota.y < posicion_jugador_izquierdo.y + alto_jugador) { posicion_pelota.x = posicion_jugador_izquierdo.x + (posicion_jugador_izquierdo.x - posicion_pelota.x); velocidad_pelota.x = -velocidad_pelota.x + aceleracion_rebote.x; int dist_pelota_centro_pala = posicion_pelota.y - (posicion_jugador_izquierdo.y + (alto_jugador / 2)); float f = ((float) dist_pelota_centro_pala) / (alto_jugador / 2); velocidad_pelota.y = velocidad_pelota.y + f * aceleracion_rebote.y; } // pared derecha else if (posicion_pelota.x > ancho_campo) { puntuacion_jugador_izquierdo = puntuacion_jugador_izquierdo + 1; inicia_bola(derecha); } // pared izquierda else if (posicion_pelota.x < 0) { puntuacion_jugador_derecho = puntuacion_jugador_derecho + 1; inicia_bola(izquierda); } // pared inferior else if (posicion_pelota.y > alto_campo) { posicion_pelota.y = alto_campo - (posicion_pelota.y - alto_campo); velocidad_pelota.y = -velocidad_pelota.y; } // pared superior else if (posicion_pelota.y < 0) { posicion_pelota.y = -posicion_pelota.y; velocidad_pelota.y = -velocidad_pelota.y; } } /* Comprueba si se ha pulsado alguna tecla y la procesa. Devuelve true si se debe seguir jugando */ static bool poll_entrada(void) { int ch = tecla_pulsada(); if (ch == 'a') { if (posicion_jugador_izquierdo.y > 0) { posicion_jugador_izquierdo.y -= velocidad_jugador; } } else if (ch == 'z') { if (posicion_jugador_izquierdo.y + alto_jugador < alto_campo) { posicion_jugador_izquierdo.y += velocidad_jugador; } } else if (ch == 'k') { if (posicion_jugador_derecho.y > 0) { posicion_jugador_derecho.y -= velocidad_jugador; } } else if (ch == 'm') { if (posicion_jugador_derecho.y + alto_jugador < alto_campo) { posicion_jugador_derecho.y += velocidad_jugador; } } else if (ch == 'x') { return false; } return true; } int main(int argc, char* argv[]) { // Fijamos que el alto de los jugadores sea 3 caracteres alto_jugador = (alto_campo / alto_dibujo) * 3; // y que se muevan un caracter arriba o abajo cada vez que se pulse la // tecla correspondiente. velocidad_jugador = alto_campo / alto_dibujo; inicia_partida(); int pausa = 1000 / fps_max; // milisegundos entre cada actualización de pantalla Hora antes = get_time(); while (poll_entrada()) { Hora ahora = get_time(); int transcurrido = ahora - antes; if (transcurrido > pausa) { antes = ahora; avanza_pong(transcurrido); clear_screen(); dibuja_marcadores(); dibuja_campo(); // a continuación, información de depuración print_integer(transcurrido); print_character(' '); print_integer(posicion_jugador_izquierdo.y); print_character(' '); print_integer(posicion_jugador_derecho.y); print_character(' '); print_integer(posicion_pelota.x); print_character(' '); print_integer(posicion_pelota.y); print_character(' '); print_integer(velocidad_pelota.x); print_character(' '); print_integer(velocidad_pelota.y); print_character('\n'); } } return 0; }