Skip to content

Instantly share code, notes, and snippets.

@gordinmitya
Created June 26, 2022 09:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gordinmitya/faa949c948c1462c14834aa16886eeef to your computer and use it in GitHub Desktop.
Save gordinmitya/faa949c948c1462c14834aa16886eeef to your computer and use it in GitHub Desktop.
Ping-pong console game
#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