Created
June 26, 2022 09:49
-
-
Save gordinmitya/faa949c948c1462c14834aa16886eeef to your computer and use it in GitHub Desktop.
Ping-pong console game
This file contains hidden or 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
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <time.h> | |
#include <sys/select.h> | |
#include <string.h> | |
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |
#define AI_MODE 1 | |
const int TABLE_WIDTH = 21; | |
const int TABLE_HEIGHT = 12; | |
const int RACKET_OFFSET = 1; | |
const int RACKET_SIZE = 3; | |
void clrscr() | |
{ | |
system("clear"); | |
} | |
void printHorizontalLine() | |
{ | |
printf("+"); | |
for (int i = 0; i < TABLE_WIDTH; i++) | |
{ | |
printf("—"); | |
} | |
printf("+"); | |
printf("\n\r"); | |
} | |
struct | |
{ | |
int x, y; | |
int vx, vy; | |
} ball; | |
struct | |
{ | |
int A, B; | |
} racket; | |
struct | |
{ | |
int A, B; | |
} score; | |
void initScore() | |
{ | |
score.A = 0; | |
score.B = 0; | |
} | |
void resetGame() | |
{ | |
ball.x = TABLE_WIDTH / 2; | |
ball.y = TABLE_HEIGHT / 2; | |
ball.vx = rand() % 2 ? 1 : -1; | |
ball.vy = rand() % 2 ? 1 : -1; | |
racket.A = TABLE_HEIGHT / 2 - 2; | |
racket.B = TABLE_HEIGHT / 2 - 2; | |
} | |
char scoreBuffer[16]; | |
void printScoreLine() { | |
sprintf(scoreBuffer, "%3d : %-d", score.A, score.B); | |
int scoreLength = strlen(scoreBuffer); | |
printf("|"); | |
int half = TABLE_WIDTH / 2 - scoreLength / 2; | |
for (int i = 1; i < half; i++) | |
printf(" "); | |
printf("%3d : %-3d", score.A, score.B); | |
for (int i = half + scoreLength; i < TABLE_WIDTH - 1; i++) | |
printf(" "); | |
printf("|"); | |
printf("\n\r"); | |
} | |
void printTable() | |
{ | |
clrscr(); | |
printHorizontalLine(); | |
for (int j = 0; j < TABLE_HEIGHT; j++) | |
{ | |
printf("|"); | |
for (int i = 0; i < TABLE_WIDTH; i++) | |
{ | |
if (i == ball.x && j == ball.y) | |
{ | |
printf("*"); | |
continue; | |
} | |
if (i == RACKET_OFFSET && (j >= racket.A) && (j < racket.A + RACKET_SIZE)) | |
{ | |
printf("|"); | |
continue; | |
} | |
if (i == (TABLE_WIDTH - 1 - RACKET_OFFSET) && (j >= racket.B) && (j < racket.B + RACKET_SIZE)) | |
{ | |
printf("|"); | |
continue; | |
} | |
printf(" "); | |
} | |
printf("|"); | |
printf("\n\r"); | |
} | |
printHorizontalLine(); | |
printScoreLine(); | |
printHorizontalLine(); | |
} | |
void moveBall() | |
{ | |
ball.x += ball.vx; | |
ball.y += ball.vy; | |
} | |
void checkState() | |
{ | |
// check horizontal borders | |
if (ball.x == 0 || ball.x == TABLE_WIDTH - 1) | |
{ | |
if (ball.x == 0) | |
{ | |
score.B++; | |
} | |
else | |
{ | |
score.A++; | |
} | |
resetGame(); | |
return; | |
} | |
// chek vertical borders | |
if (ball.y < 0 || ball.y > TABLE_HEIGHT - 1) | |
{ | |
ball.vy *= -1; | |
ball.y += ball.vy; | |
} | |
// check racket is inside the table | |
racket.A = MAX(0, MIN(racket.A, TABLE_HEIGHT - RACKET_SIZE)); | |
racket.B = MAX(0, MIN(racket.B, TABLE_HEIGHT - RACKET_SIZE)); | |
// check racket collision | |
bool hitRocketA = ball.x == RACKET_OFFSET && (ball.y >= racket.A && ball.y < racket.A + RACKET_SIZE); | |
bool hitRocketB = ball.x == (TABLE_WIDTH - 1 - RACKET_OFFSET) && (ball.y >= racket.B && ball.y < racket.B + RACKET_SIZE); | |
if (hitRocketA || hitRocketB) | |
{ | |
ball.vx *= -1; | |
ball.x += 2 * ball.vx; | |
int mode = rand() % 5; | |
if (mode == 0) | |
{ | |
ball.vy = 0; | |
} | |
else | |
{ | |
if (ball.vy == 0) | |
{ | |
ball.vy = rand() % 2 ? 1 : -1; | |
} | |
else if (mode == 1) | |
{ | |
ball.vy *= -1; | |
} | |
} | |
} | |
} | |
fd_set read_fds; | |
int fd; | |
struct timeval tv = {0, 1000}; | |
#define BUFFER_SIZE 32 | |
char buffer[BUFFER_SIZE]; | |
bool readInput() | |
{ | |
FD_ZERO(&read_fds); | |
FD_SET(fd, &read_fds); | |
int toRead = 0; | |
toRead = select(fd + 1, &read_fds, NULL, NULL, &tv); | |
if (toRead < 1) { | |
return true; | |
} | |
int readed = read(0, buffer, BUFFER_SIZE); | |
int va = 0, vb = 0; | |
for (int i = 0; i < readed; i++) | |
{ | |
switch (buffer[i]) | |
{ | |
case 'a': | |
va = -1; | |
break; | |
case 'z': | |
va = 1; | |
break; | |
case 'k': | |
vb = -1; | |
break; | |
case 'm': | |
vb = +1; | |
break; | |
case 'q': | |
return false; | |
case 10: | |
break; | |
} | |
} | |
racket.A += va; | |
racket.B += vb; | |
return true; | |
} | |
void aiStep() | |
{ | |
if (ball.vx < 0) | |
{ | |
if (racket.A + 1 < ball.y) | |
{ | |
racket.A++; | |
} | |
else if (racket.A + 1 > ball.y) | |
{ | |
racket.A--; | |
} | |
} | |
else | |
{ | |
if (racket.B + 1 < ball.y) | |
{ | |
racket.B++; | |
} | |
else if (racket.B + 1 > ball.y) | |
{ | |
racket.B--; | |
} | |
} | |
} | |
bool assertState() { | |
if (ball.x < 0 || ball.x > TABLE_WIDTH - 1) | |
{ | |
printf("Ball out of table\n\r"); | |
return false; | |
} | |
if (ball.y < 0 || ball.y > TABLE_HEIGHT - 1) | |
{ | |
printf("Ball out of table\n\r"); | |
return false; | |
} | |
if (racket.A < 0 || racket.A > TABLE_HEIGHT - RACKET_SIZE) | |
{ | |
printf("Racket A out of table\n\r"); | |
return false; | |
} | |
if (racket.B < 0 || racket.B > TABLE_HEIGHT - RACKET_SIZE) | |
{ | |
printf("Racket B out of table\n\r"); | |
return false; | |
} | |
return true; | |
} | |
int main() | |
{ | |
if (!AI_MODE) | |
{ | |
system("/bin/stty -icanon min 1"); | |
fd = fileno(stdin); | |
FD_ZERO(&read_fds); | |
FD_SET(fd, &read_fds); | |
} | |
srand(time(NULL)); | |
initScore(); | |
resetGame(); | |
printTable(); | |
unsigned long long counter = 0; | |
while (true) | |
{ | |
moveBall(); | |
if (AI_MODE) | |
{ | |
aiStep(); | |
} | |
else | |
{ | |
if (!readInput()) | |
break; | |
} | |
checkState(); | |
if (!assertState()) { | |
break; | |
} | |
printf("\n\r"); | |
printTable(); | |
printf("%llu\n\r", counter); | |
usleep(100 * 1000); | |
counter += 1; | |
} | |
if (!AI_MODE) | |
{ | |
system("/bin/stty -raw"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment