I. knowledge points:
Structure linked list, dynamic memory allocation, keyboard input detection, cursor setting.
II. Implementation logic
1. Multiple structures are involved, including snake, direction, node, position coordinate and game 2. The nodes are connected in series to form a chain list, traversing to get the member coordinates, printing symbols to get the snake body. 3. Continuously add the head, remove the tail, traverse the coordinates again, and then print to form the movement of the snake. 4. The position of food production cannot be determined beyond the boundary or coincide with the snake's body. 5. It is a rule to judge the direction of the snake. It is not allowed to reverse. 6. The realization of steering determines the new joint coordinates (the top, bottom, left and right of the current head) according to the direction of travel 7. Death detection: whether the coordinates of the head node coincide with the wall or other joints of the body. 8. Accelerate and decelerate, and set the refresh sleep time. 9. The cursor can be set to print the setting symbol at the setting position.
Source code
//Model.h structure header file #pragma once typedef enum Direction { UP, DOWN, LEFT, RIGHT }Direction; typedef struct Position { int x; int y; }Position; typedef struct Node { Position pos; struct Node *next; }Node; typedef struct Snake { Node *head; Node *tail; Direction direction; }Snake; typedef struct Game { Snake snake; Position food; int width; int height; }Game;
//View.h #pragma once #include "Model.h" void SetPos(int X, int Y); void DisplayWall(int width, int height); void DisplaySnake(const Snake *pSnake); void DisplayFood(const Position *pFood); void CleanSnakeNode(const Position *pPos); void DisplaySnakeNode(const Position *pPos); void GameDescription1(); void GameDescription2();
//View.c #include "View.h" #include "Model.h" #include <Windows.h> #include <stdio.h> void ViewInit() { HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //Console? Cursor? Info this structure contains the information of the console cursor CONSOLE_CURSOR_INFO ConsoleCursorInfo; GetConsoleCursorInfo(hOutput, &ConsoleCursorInfo); ConsoleCursorInfo.bVisible = FALSE; SetConsoleCursorInfo(hOutput, &ConsoleCursorInfo); } //Set cursor position void SetPos(int X, int Y) { HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); COORD coord = { X, Y }; SetConsoleCursorPosition(hOutput, coord); } static void TranslateSetPos(int x, int y) { int X = 2 * (x + 1); int Y = y + 1; SetPos(X, Y); } void DisplayWall(int width, int height) { ViewInit(); // upper SetPos(0, 0); for (int i = 0; i < width + 2; i++) { printf("◆"); } // lower SetPos(0, height + 1); for (int i = 0; i < width + 2; i++) { printf("◆"); } // Left for (int i = 0; i < height + 2; i++) { SetPos(0, i); printf("◆"); } // right for (int i = 0; i < height + 2; i++) { SetPos(2 * (width + 1), i); printf("◆"); } } void DisplaySnake(const Snake *pSnake) { for (Node *node = pSnake->tail; node != NULL; node = node->next) { TranslateSetPos(node->pos.x, node->pos.y); printf("■"); } } void DisplayFood(const Position *pFood) { TranslateSetPos(pFood->x, pFood->y); printf("■"); } void CleanSnakeNode(const Position *pPos) { TranslateSetPos(pPos->x, pPos->y); printf(" "); } void DisplaySnakeNode(const Position *pPos) { TranslateSetPos(pPos->x, pPos->y); printf("■"); } //Game Description Interface void GameDescription1() { SetPos(40, 14); printf("Welcome to the snake game!\n\n"); system("pause"); system("color 5e"); system("cls"); SetPos(10, 14); printf("You will control the movement of the snake through the keyboard: up↑ lower↓ Left ← right→ accelerate F1 Slow down F2 suspend space Sign out Esc"); SetPos(10, 15); printf("The body grows when you eat\n"); SetPos(10, 16); printf("Hitting the body or the wall will end the game\n"); system("pause"); system("cls"); } void GameDescription2() { SetPos(64, 15); printf("You can't go through the wall, you can't bite yourself\n"); SetPos(64, 16); printf("use↑ ↓ ← → Control Snake Movement."); SetPos(64, 17); printf("F1 To accelerate, F2 To slow down\n"); SetPos(64, 18); printf("ESC : Quit game space: Pause game."); }
//Controller.c #include "Model.h" #include "View.h" #include <stdlib.h> #include <stdio.h> #include <windows.h> #include <stdbool.h> #include <time.h> void SnakeInit(Snake *pSnake) { //In the list, the head node is the snake's tail, and the tail node is the snake's head pSnake->tail = NULL; for (int i = 0; i < 3; i++){ Node *node = (Node *)malloc(sizeof(Node)); //Create node from snake head node->pos.x = 7 + i; node->pos.y = 2; if (i == 0){ pSnake->head = node; } node->next = pSnake->tail; pSnake->tail = node; } pSnake->direction = LEFT; } bool IsOverLap(Position pos,Snake *pSnake) { Node *node; for (node = pSnake->tail; node != pSnake->head; node = node->next){ if (node->pos.x == pos.x&&node->pos.y == pos.y){ return true; } } return false; } //Produce food Position GenerateFood(int width, int height, Snake *pSnake) { Position pos; SetPos(1, 1); do { pos.x = rand() % width; pos.y = rand() % height; } while (IsOverLap(pos, pSnake)); DisplayFood(&pos); return pos; } //Initialize Game void GameInit(Game *pGame) { pGame->width = 28; pGame->height = 27; //Initialize snake SnakeInit(&pGame->snake); //Initialize food //pGame->food = GenerateFood(pGame->width, pGame->height, &pGame->snake); } //Get the coordinates where the snake is going Position GetNextPosition(const Snake *pSnake) { Position next = pSnake->head->pos; switch (pSnake->direction){ case UP: next.y -= 1; break; case DOWN: next.y += 1; break; case LEFT: next.x -= 1; break; case RIGHT: next.x += 1; break; } return next; } //Add a space to the snake's head void AddHead(Snake *pSnake, Position nextPos) { Node *node = (Node *)malloc(sizeof(Node)); node->pos = nextPos; node->next = NULL; pSnake->head->next = node; pSnake->head = node; DisplaySnakeNode(&nextPos);//Pay attention to the parameter passing here } //Snake tail minus one grid void RemoveTail(Snake *pSnake) { Node *tail = pSnake->tail; pSnake->tail = pSnake->tail->next; CleanSnakeNode(&tail->pos); free(tail); } //Judge whether the snake can eat bool IsEat(Position food, Position next) { return food.x == next.x&&food.y == next.y; } //Judge if the snake hit the wall bool KilledByWall(const Snake *pSnake,int width,int height) { int x = pSnake->head->pos.x; int y = pSnake->head->pos.y; if (x>=0 && x<width && y>=0 && y<=height){ return false; } return true; } //Determine if the snake hit itself bool KilledBySelf(const Snake *pSnake,Position nextPos) { Node *node = pSnake->tail; for (node = pSnake->tail; node != pSnake->head; node = node->next){ if (node->pos.x == nextPos.x&&node->pos.y == nextPos.y){ return true; } } return false; } //Judge whether it's dead or not bool GameOver(const Snake *pSnake,Position nextPos,int width,int height) { if (KilledByWall(pSnake,width,height)){ printf("Hit the wall\n"); return true; } if (KilledBySelf(pSnake,nextPos)){ printf("Suicide\n"); return true; } else{ return false; } } //Pause game void PauseGame() { while (1) { Sleep(300); if (GetAsyncKeyState(VK_SPACE)) { break; } } } void GameRun() { Game game; int score = 0, //score add = 10, //Bonus points sleeptime = 200; //Normal speed GameInit(&game); GameDescription1(); DisplayWall(game.width,game.height); DisplaySnake(&game.snake); game.food = GenerateFood(game.width, game.height, &game.snake); GameDescription2(); while (1) { SetPos(64, 10); printf("score:%d ", score); SetPos(64, 11); printf("Current food score:%d branch", add); if (game.snake.direction != DOWN&&GetAsyncKeyState(VK_UP)){ game.snake.direction = UP; } else if (game.snake.direction != UP&&GetAsyncKeyState(VK_DOWN)){ game.snake.direction = DOWN; } else if (game.snake.direction != RIGHT&&GetAsyncKeyState(VK_LEFT)){ game.snake.direction = LEFT; } else if (game.snake.direction != LEFT&&GetAsyncKeyState(VK_RIGHT)){ game.snake.direction = RIGHT; } else if (GetAsyncKeyState(VK_F1)) //accelerate { if (sleeptime > 50) { sleeptime -= 30; add = add + 2; if (sleeptime == 320) { add = 2;//To prevent the error of adding back after reducing to 1 } } } else if (GetAsyncKeyState(VK_F2)) //Slow down { if (sleeptime < 350) { sleeptime = sleeptime + 30; add = add - 2; if (sleeptime == 350) { add = 1; //Guarantee a minimum score of 1 } } } else if (GetAsyncKeyState(VK_SPACE)){ //suspend PauseGame(); } else if (GetAsyncKeyState(VK_ESCAPE)) { //Sign out system("cls"); SetPos(18,12 ); printf("Quit game\n"); break; } Position nextPos = GetNextPosition(&game.snake); if (IsEat(game.food,nextPos)){ //Add a space to your head AddHead(&game.snake,nextPos); score += add; //Regenerate a food game.food = GenerateFood(game.width, game.height, &game.snake); } else{ //Add a space to your head AddHead(&game.snake,nextPos); //Tail minus one grid RemoveTail(&game.snake); } if (GameOver(&game.snake,nextPos,game.width,game.height)){ break; } Sleep(sleeptime); } } //Test the position of the control cursor void DemoSetPos() { HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); COORD coord; for (int i = 0; i < 6; i++) { coord.X = 2 * i; coord.Y = i; SetConsoleCursorPosition(hOutput, coord); printf("%02d", i); } } int main() { //DemoSetPos(); srand((unsigned)time(NULL)); system("title ★★Greedy snake★★"); //Change command window name GameRun(); system("pause"); system("cls"); return 0; }