C language implements snake game design

  • 2020-10-23 20:12:01
  • OfStack

C language to realize the snake, for your reference, the specific content is as follows

Experimental platform: DEV C++


 /********************************************************************************
*File name:SnakeGame3.0.c       
*Description: Snake game source code ( C Language), adopted 
* The width-first algorithm calculates the shortest path from snake to food (time complexity) n^3 Space complexity n^2 ), this algorithm encounters  *
* The shortest path will not be calculated until the self siege is over      *
*********************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<time.h>
#include<math.h>
#include<conio.h>

#define SIZE 25 // Define map size 
#define MAX_LENGTH 19 * 19 // Defines the maximum length of a snake 

typedef struct point // The node of a point on a map 
{
 int r;
 int c;
} point;
typedef struct queue // Iterate to search the queues used by the shortest path 
{
 point * body[5 * SIZE]; // Holds an array of snake bodies (maximum stack depth) 5 * SIZE ) 
 int num; // Record the number of nodes in the queue 
 int first_in_pos; // The first 1 The index value of the elements that were queued 
} queue;
HANDLE stdOutput; // The statement windows Standard output handle 

void init(int * length, point * foodAt, int * dir, point body[], char map[][SIZE]); // Initialize the 
int getDir(int dir); // Gets the snake's direction of travel 
int getAIDir(int dir, int length, point body[], point foodAt); // To obtain AI Judged the direction of travel 
int moveable(point moveTo, int length, point body[]);
int move(point foodAt, int dir, int length, point body[]); // The snake's movement 
void draw(int length, point foodAt, point body[], char map[][SIZE]); // drawing 
void food(point * foodAt, point body[], int length, char map[][SIZE]); // Produce food 

// Stack related operations 
point * pop(queue *queue); // Retrieves the first entry point from the queue. Returns the pointer to the fetch point. Fails to retrieve NULL
void push(point *point, queue *queue); // Push into the queue 

int main()
{
 char map[SIZE][SIZE]; // Define the map 
 point body[MAX_LENGTH], foodAt; // The entire body and food of the snake ( body The first of an array of 1 Is the head of the snake) 
 int length; // The actual length of the snake 
 int dir; // Direction of travel 
 int rate = 1; // Travel rate 
 int result; // Preserve the results of the snake movement: [death], [score], [none] 
 init(&length, &foodAt, &dir, body, map); // Initialize the 

 while (1)
 {
 Sleep(100 / rate);
 //dir = getDir(dir); // Gets the snake's direction of travel 
 dir = getAIDir(dir, length, body, foodAt); // To obtain AI Judged the direction of travel 
 result = move(foodAt, dir, length, body); // The snake's movement 
 if (result == 1) // If you eat food 
 {
 length++;
 rate = length / 3;
 if (length == MAX_LENGTH)
 {
 printf(" You have cleared the customs! ");
 break;
 }
 food(&foodAt, body, length, map); // Produce food 
 }
 draw(length, foodAt, body, map); // drawing 
 if (result == -1) // If death 
 {
 break;
 }

 }
 Sleep(500); 
 printf("  Failure. This time the score is %d    ", (length - 3) * 100);
 system("pause");
}
void init(int * length, point * foodAt, int * dir, point body[], char map[][SIZE]) // Initialize the 
{
 memset(map, '*', SIZE * SIZE); // Initialization map 
 body[0].r = 3, body[0].c = 2; // Initialize the snake's body 
 body[1].r = 2, body[1].c = 2;
 body[2].r = 1, body[2].c = 2;
 *length = 3; // The initial length is 3
 *dir = 2; // Initial downward direction 
 food(foodAt, body, *length, map); // Produce food 
 draw(*length, *foodAt, body, map); // drawing 
 printf("  Press any key to start. Use ASDW Control the direction, ESC suspended \n");
 _getch();
 srand((unsigned)time(NULL)); // Generate random number seeds, spare 
 stdOutput = GetStdHandle(STD_OUTPUT_HANDLE); // Gets the standard output handle 
 CONSOLE_CURSOR_INFO cci;
 cci.bVisible = 0;
 cci.dwSize = 1;
 SetConsoleCursorInfo(stdOutput, &cci);
 COORD coord = { 0, SIZE * 2 + 3 };
}
int getDir(int dir) // Gets the snake's direction of travel, specifying the return value 0 It stands for up, 1 That's to the right, 2 Is for down, 3 On behalf of the left 
{
 char key;
 int newDir = dir;
 if (_kbhit())
 {
 key = _getch();
 switch (key)
 {
 case 'A': case 'a': newDir = 3; break;
 case 'S': case 's': newDir = 2; break;
 case 'D': case 'd': newDir = 1; break;
 case 'W': case 'w': newDir = 0; break;
 case 27: _getch(); break;
 }
 }
 if (newDir - dir == 2 || newDir - dir == -2) // Snakes can't reverse 
 {
 newDir = dir;
 }
 return newDir;
}
int getAIDir(int dir, int length, point body[], point foodAt) // To obtain AI Judged the direction of travel 
{
 static int *shortestPathDir, count = 0; // Save the direction of the shortest path (in reverse order, that is, go first in the direction behind) 

 if (count == 0) // If the shortest path has not been generated, then regenerate 
 {
 int map_of_steps[SIZE][SIZE]; // Save to arrive at some on the map 1 The minimum number of steps in a point 
 queue queue = { 0,0 };
 point *last_body = (point *)malloc(length * sizeof(point)); // Save the body position of the snake during the calculation 
 point *next_body; // Save the 1 The body of the secondary snake 
 point next_point;
 int i, step = 0;
 point moveTo;
 memcpy(last_body, body, length * sizeof(point));
 memset(map_of_steps, 0, SIZE * SIZE * sizeof(int));

 // Put an initial into the queue body
 push(last_body, &queue);
 push(NULL, &queue); // insert NULL To identify a width-first search 1 The end of the floor 
 step++; // with step To represent the number of steps, and also the number of layers 

 while (queue.num != 0)
 {
 last_body = pop(&queue);
 if (last_body == NULL) // If a 1 End of the floor 
 {
 if (queue.num != 0) // If there are any more 1 Layer of the element 
 {
 step++;
 push(NULL, &queue); // Insert the 1 The end of the layer 
 continue;
 }
 else
 {
 break;
 }

 }
 for (i = 0; i < 4; i++) // Detection respectively 4 Can I move in one direction or another 
 {
 switch (i)
 {
 case 0: moveTo.r = last_body[0].r - 1, moveTo.c = last_body[0].c; break;
 case 1: moveTo.r = last_body[0].r, moveTo.c = last_body[0].c + 1; break;
 case 2: moveTo.r = last_body[0].r + 1, moveTo.c = last_body[0].c; break;
 case 3: moveTo.r = last_body[0].r, moveTo.c = last_body[0].c - 1; break;
 }
 if (moveable(moveTo, length, last_body) && map_of_steps[moveTo.r][moveTo.c] == 0) // If you're moving to a point that you haven't moved to before 
    // (that is, the current path is the shortest path to the point), and the point is moveable the 
 {
 map_of_steps[moveTo.r][moveTo.c] = step;
 if (moveTo.r == foodAt.r && moveTo.c == foodAt.c) // If the 1 You can reach the point where the food is 
 {
 // First, free1 Some useless dynamic memory 
 free(last_body);
 while (queue.num != 0)
 {
 free(pop(&queue));
 }
 goto outer; // Jump out of the loop 
 }
 // generate next_body And push it into the queue 
 next_body = (point *)malloc(length * sizeof(point));
 for (i = length - 1; i > 0; i--) // Move the snake's position 
 {
 next_body[i] = body[i - 1];
 }
 next_body[0] = moveTo; // in 1 size 

 push(next_body, &queue);// Push the queue 

 }
 }
 //free1 Some useless dynamic memory 
 free(last_body);
 }
 outer:;
 if (map_of_steps[foodAt.r][foodAt.c] == 0) // If you cannot reach the point where the food is, follow the same path until you die 
 {
 return dir;
 }
 // generate shortestPath
 shortestPathDir = (int *)malloc(step * sizeof(int));
 count = step;
 next_point = foodAt;
 for (i = 0; i < step - 1; i++) // using map_of_steps And the lower 1 The point is extrapolated to the top 1 Point to the next 1 The direction of the dot dir
 {
 if (next_point.r + 1 < SIZE && map_of_steps[next_point.r][next_point.c] ==
 map_of_steps[next_point.r + 1][next_point.c] + 1)
 {
 shortestPathDir[i] = 0;
 next_point.r += 1;
 }
 else if (next_point.c - 1 >= 0 && map_of_steps[next_point.r][next_point.c] ==
 map_of_steps[next_point.r][next_point.c - 1] + 1)
 {
 shortestPathDir[i] = 1;
 next_point.c -= 1;
 }
 else if (next_point.r - 1 >= 0 && map_of_steps[next_point.r][next_point.c] ==
 map_of_steps[next_point.r - 1][next_point.c] + 1)
 {
 shortestPathDir[i] = 2;
 next_point.r -= 1;
 }
 else
 {
 shortestPathDir[i] = 3;
 next_point.c += 1;
 }
 }
 // The first 1 Step must be judged separately (because map_of_steps The value of 0 It could be the head of a snake or the body of a snake, which would treat the snake 1 Step direction judgment produces interference) 
 if (body[0].r > next_point.r)
 {
 shortestPathDir[step - 1] = 0;
 }
 else if (body[0].r < next_point.r)
 {
 shortestPathDir[step - 1] = 2;
 }
 else if (body[0].c > next_point.c)
 {
 shortestPathDir[step - 1] = 3;
 }
 else
 {
 shortestPathDir[step - 1] = 1;
 }
 /*printf("\n\n\n");
 int j;
 for (i = 0; i < SIZE; i++)
 {
 for (j = 0; j < SIZE; j++)
 {
 printf("%3d", map_of_steps[i][j]);
 }
 printf("\n");
 }
 printf("\n");*/
 }
 // Go along the shortest path 
 return shortestPathDir[--count];
}
int moveable(point moveTo, int length, point body[]) // Determine if you can move to moveTo Can point, 1 , can't 0
{
 int i;
 for (i = 0; i < length - 1; i++)
 {
 if (moveTo.r == body[i].r && moveTo.c == body[i].c)
 {
 return 0;
 }
 }
 if (moveTo.r < 0 || moveTo.r >= SIZE || moveTo.c < 0 || moveTo.c >= SIZE) // If you go beyond the boundary 
 {
 return 0;
 }
 return 1;
}
int move(point foodAt, int dir, int length, point body[]) // Snake movement, specifying the return value -1 Is for death, 0 That means no food, 1 It's for eating food 
{
 int i, flag = 0;
 point head = body[0];
 switch (dir)
 {
 case 0: head.r -= 1; break;
 case 1: head.c += 1; break;
 case 2: head.r += 1; break;
 case 3: head.c -= 1; break;
 }
 if (head.r < 0 || head.r >= SIZE || head.c < 0 || head.c >= SIZE) // Death is out of bounds 
 {
 return -1;
 }
 for (i = 0; i < length - 1; i++)
 {
 if (head.r == body[i].r && head.c == body[i].c) // He bit himself to death 
 {
 return -1;
 }
 }
 if (head.r == foodAt.r && head.c == foodAt.c) // Eat the food 
 {
 length++;
 flag = 1; // tag 1 , and the return value is 1
 }
 for (i = length - 1; i > 0; i--) // Move the snake's position 
 {
 body[i] = body[i - 1];
 }
 body[0] = head; // in 1 size 
 if (flag == 1)
 {
 return 1;
 }
 return 0;
}
void draw(int length, point foodAt, point body[], char map[][SIZE]) // drawing 
{

 static char bitmap[SIZE + 2][SIZE + 2]; // define 1 An array to draw the map's background, borders, snakes, and food 
 int i, j;
 for (i = 0; i < SIZE; i++) // background 
 {
 for (j = 0; j < SIZE; j++)
 {
 bitmap[i + 1][j + 1] = map[i][j];
 }
 }
 // A border 
 bitmap[0][0] = '0', bitmap[0][SIZE + 1] = '1';
 bitmap[SIZE + 1][0] = '2', bitmap[SIZE + 1][SIZE + 1] = '3';
 for (i = 0; i < SIZE; i++)
 {
 bitmap[0][i + 1] = '4', bitmap[SIZE + 1][i + 1] = '4';
 bitmap[i + 1][0] = '5', bitmap[i + 1][SIZE + 1] = '5';
 }
 bitmap[foodAt.r + 1][foodAt.c + 1] = 'f'; // food 
 bitmap[body[0].r + 1][body[0].c + 1] = 'h'; // The snake 
 for (i = 1; i < length; i++) // Snake-body. 
 {
 bitmap[body[i].r + 1][body[i].c + 1] = 'b';
 }

 COORD coord = { 0, 0 }; // coordinates 0 . 0
 SetConsoleCursorPosition(stdOutput, coord); // Set the cursor to 0 . 0 location 
 for (i = 0; i < SIZE + 2; i++)
 {
 for (j = 0; j < SIZE + 2; j++)
 {
 switch (bitmap[i][j])
 {
 case 'f': printf(" u "); break;
 case 'b': printf(" low "); break;
 case 'h': printf(" a. "); break;
 case '0': printf(" ┏ "); break;
 case '1': printf("  ━  ┓ "); break;
 case '2': printf(" ┗ "); break;
 case '3': printf("  ━  ┛ "); break;
 case '4': printf("  ━ "); break;
 case '5': printf(" ┃  "); break;
 default: printf(" ");
 }
 }
 printf("\n");
 }
}
void food(point * foodAt, point body[], int length, char map[][SIZE]) // Produce food 
{
 int i;
 while (1)
 {
 foodAt->r = rand() % SIZE, foodAt->c = rand() % SIZE; // Randomly generate food locations 
 for (i = 0; i < length; i++)
 {
 if (foodAt->r == body[i].r && foodAt->c == body[i].c) // If the position is on the snake's body 
 {
 goto retry;
 }
 }
 break;
 retry:;
 }
}

// Queue-dependent functions 
point * pop(queue *queue) // Gets out of the queue and returns a pointer to the fetch point 
{
 queue->num--;
 if (queue->first_in_pos == 5 * SIZE - 1) // Returns the first 1 Enter the point of the queue and put the variable fist_in_pos change 
 {
 queue->first_in_pos = 0;
 return queue->body[5 * SIZE - 1];
 }
 else
 {
 return queue->body[queue->first_in_pos++];
 }
}
void push(point * body, queue *queue)
{
 if (queue->num == 0) // If the queue is empty 
 {
 queue->num++;
 queue->first_in_pos = 0; // Will be the first 1 The queue entry location is set to 0
 queue->body[queue->first_in_pos] = body;
 }
 else // Otherwise, insert the queue 
 {
 if (queue->first_in_pos + queue->num > 5 * SIZE - 1)
 {
 queue->body[queue->first_in_pos + queue->num++ - 5 * SIZE] = body;
 }
 else
 {
 queue->body[queue->first_in_pos + queue->num++] = body;
 }
 }
}

More interesting classic games to realize the special topic, to share with you:

C++ classic game summary

python classic game summary

python Tetris game collection

JavaScript classic games don't stop playing

java classic game summary

javascript classic game summary


Related articles: