I’m writing a basic snake game in C. Upon launch, the program renders a snake that stays in place until it receives input from either the 'w', 's', 'a', 'd' keys or the arrow keys. After receiving the 'd' key, the snake moves to the right and continues moving in that direction until it receives input from either the 'w', 's', 'a' keys or the corresponding arrow keys. Once the snake has started moving, it will continue to move until the player presses the ']' key, which quits the game gracefully. Other keys will not stop the snake.
The program takes either 1 or 3 ASCII characters at a time. The 3-ASCII-character case is only for the arrow keys (up, down, left, and right). The 'w', 's', 'a', and 'd' keys control the movement of the snake well, but each of the arrow keys stops the snake from moving.
here is the code
#define map_size 10
struct termios orig_termios;
void clear_screen()
{
printf("\033[H\033[J\033[?25l");
fflush(stdout);
}
void red_square()
{
printf("\033[101m \033[0m");
fflush(stdout);
}
void green_square()
{
printf("\033[102m \033[0m");
fflush(stdout);
}
void move_cursor(int hx, int hy)
{
printf("\033[%d;%dH", hy, hx);
fflush(stdout);
}
void disableRawMode()
{
tcsetattr(0, TCSAFLUSH, &orig_termios);
printf("\033[?25h\n");
}
void enableRawMode()
{
tcgetattr(0, &orig_termios);
atexit(disableRawMode);
struct termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(0, TCSAFLUSH, &raw);
}
int generate_food_x(int lower, int upper)
{
int num = (rand() % (upper - lower + 1)) + lower;
return num * 2 - 1;
}
int generate_food_y(int lower, int upper)
{
int num = (rand() % (upper - lower + 1)) + lower;
return num;
}
bool eats(int snake_x, int snake_y, int food_x, int food_y, int *total)
{
if (snake_x == food_x && snake_y == food_y)
{
(*total)++;
return 1;
}
return 0;
}
bool kbhit()
{
int byteswaiting;
ioctl(0, FIONREAD, &byteswaiting);
bool result = byteswaiting > 0;
return result;
}
int main()
{
int snake_x = 1; // starting position of the green square
int snake_y = 1;
int food_x = 5;
int food_y = 1;
int scoreboard_x = map_size * 2 + 3;
int scoreboard_y = 1;
bool eaten = 0;
int total = 0;
enableRawMode();
srand(time(0));
char ch = 0;
char next1 = 0;
char next2 = 0;
while (1)
{
clear_screen();
move_cursor(snake_x, snake_y);
green_square();
if (eats(snake_x, snake_y, food_x, food_y, &total))
{
food_x = generate_food_x(1, map_size);
food_y = generate_food_y(1, map_size);
}
move_cursor(food_x, food_y);
red_square();
move_cursor(scoreboard_x, scoreboard_y);
printf("total: %d", total);
fflush(stdout);
move_cursor(scoreboard_x, scoreboard_y+1);
printf("next1: %d next2: %d", next1, next2);
fflush(stdout);
if (kbhit()) // check if any key gets hit
{
ch = getchar();
if (ch == ']')
{
exit(0);
}
}
if (ch != 0)
{
if (ch == 'w')
{
snake_y -= 1; // move up 1 line
}
else if (ch == 's')
{
snake_y += 1; // move down 1 line
}
else if (ch == 'a')
{
snake_x -= 2; // move left 2 spaces
}
else if (ch == 'd')
{
snake_x += 2; // move right 2 spaces
}
else if (ch == '\033')
{
next1 = getchar();
next2 = getchar();
if (next1 == '[' && next2 == 'D')
{
snake_x -= 2; // move left 2 spaces
}
else if (next1 == '[' && next2 == 'C')
{
snake_x += 2; // move right 2 spaces
}
else if (next1 == '[' && next2 == 'A')
{
snake_y -= 1; // move up 1 line
}
else if (next1 == '[' && next2 == 'B')
{
snake_y += 1; // move down 1 line
}
}
}
usleep(100000);
}
return 0;
}
the lines around scoreboard_y+1
are for debugging
When the game started, I pressed the right arrow key and the snake moved one step before stopping when next1
was 91 and next2
was 67. When I pressed the right arrow key again, the snake didn't move at all when next1
was 27 and next2
was 91.
there're 2 bugs and I have no clue where to debug deeper.
bug1: the snake is supposed to keep moving with or without further input after initial start. However, any of the arrow keys stops the snake's movement.
bug2: The program is supposed to take 3 ASCII characters for any of the arrow keys though, it sometimes loses some of them.
could someone give a hint?