text
The greedy Snake game written in C language does not use the graphical interface. It is a simple Snake displayed on the console~
It mainly uses the API functions of windows. A little explanation:
1. The system("cmd") function passes the cmd command to adjust the font color and background color of the console (of course, the function is far more than that)
2. COORD is a structure used in windows.h to represent the character coordinate information in the console. The upper left corner is (0,0), the right is the positive direction of x axis, and the down is the positive direction of y axis. (note that it refers to the coordinates of characters, not pixel coordinates)
3. The gotoxy function and HideCursor function are copied from the Internet
4. About the snake display: at first, I refreshed the screen every time I displayed it. Using system("cls") is very fast and convenient. But computers may not think so. It is too laggy to refresh the entire console window if the configuration is bad. So later, it was changed to erase only the position where the snake appeared last time. (because I think my little snake doesn't fit in with the fancy graphical interface, I also specially installed a dos virtual machine and made a dos version, The specific content is at the end of the text)
5. About the position of the new section of the snake: at the beginning, I directly added the new section at the tail with the recursive formula, not necessarily on the path the tail walked (if it happened to turn a corner). Although it is OK, because it can't be seen, due to obsessive-compulsive disorder, I finally added a new global variable to represent the tail
The attached code is as follows (dev-c + + and vs can be compiled):
/*Include file*/ #include <stdlib.h> #include <stdio.h> #include <windows.h> #include <time.h> #include <conio.h> /*Snake with linked list*/ struct snake { COORD coord; //Location of this section struct snake* pNext; //Point to the next section }; /*Macro definition*/ #define WIDTH 40 / / window width (even number required) #define HEIGHT 20 / / window height #define LEN sizeof(struct snake) / / the length of a snake's structure #define N 4 / / total number of difficulty levels #Define initial_length 3 / / initial length #Define length_of_wall 4 / / length of each wall #define NUMBER_OF_WALL 4 / / number of walls /*Function declaration*/ int ChooseLevel(); //Selection difficulty void ChooseGameColor(); //Select color void gotoxy(int x, int y); //Move the cursor to the specified position void HideCursor(BOOL flag); //Hide / show cursor void SetFood(); //Produce food void PrintFood(); //Show food struct snake* SnakeCreate(int lenth); //Generate snake (initial length can be specified) void SnakePrint(struct snake* pHead); //Show Snake void SnakeErase(struct snake* pHead); //Erase snake void SnakeMove(struct snake* pHead); //move void GetDirection(); //Steering control int JudgeDie(struct snake* pHead); //Judge whether you are out of bounds and bite yourself int JudgeGrow(struct snake* pHead); //Determine whether you eat food void SnakeGrow(struct snake* pHead); //Lengthen int CountScore(struct snake* pHead); //Statistical score void ReadArchive(int HighestScore[]); //Load Game void WriteArchive(int HighestScore[]); //Write Archive void WallMaker(struct snake* pHead); //Generate wall void WallGetNext(int i, int j); //Random walk (used when generating walls) void WallPrint(); //Show wall void WallErase(); //Erase wall int SnakeJudgeWall(struct snake* pHead); //Judge whether to hit the wall /*global variable*/ int Direction; //The direction of the snake: it may or may not change in each while loop COORD Food; //Food location: it may or may not change in each while cycle COORD End; //Record the position of the tail so that the newly generated section is on the original path /*Realization of multi wall with two-dimensional structure array*/ COORD wall[NUMBER_OF_WALL][LENTH_OF_WALL]; int main() { int level, HighestScore[N],score,clock; struct snake* pSnakeHead; char repeat; HWND console; time_t t; system("title Greedy snake"); system("mode con cols=40 lines=20"); //Window size, please change with macro definition constant ChooseGameColor(); while (1) { srand ((unsigned)time(&t)) ; ReadArchive(HighestScore); level = ChooseLevel(); //Selection difficulty; if (level < 4) { /*Difficulty description*/ printf("\n $The highest difficulty is divided into:%d\n", HighestScore[level - 1]); printf("\n press any key to continue..."); _getch(); } else if(level == 4) { system("cls"); printf("\n #Greedy snake·Challenge mode#\n"); printf("\n <Mode description>\n"); printf("\n $In this mode\n"); printf("\n Appears in the map%d group%d A continuous wall.\n",NUMBER_OF_WALL, LENTH_OF_WALL); printf("\n The wall randomly refreshes its position every 10 seconds.\n"); printf("\n $be careful:\n\n *The wall may appear directly in front!\n"); printf("\n *Food may be blocked by the wall!\n"); printf("\n $This mode can be divided into:%d\n", HighestScore[level - 1]); printf("\n press any key to continue..."); _getch(); } system("cls"); printf("\n <Game Description>\n\n $Snake:()()()()()()[]\n\n $Initial length:%d\n\n $Press space to pause\n\n Press any key to start the game...",INITIAIL_LENTH); _getch(); system("cls"); HideCursor(TRUE); //hide cursor pSnakeHead = SnakeCreate(INITIAIL_LENTH); //Generative snake SetFood(); //Set food PrintFood(); //Show food SnakePrint(pSnakeHead); //Show Snake if (level == 4) { WallMaker(pSnakeHead); WallPrint(); } _getch(); clock=0; while (1) { GetDirection(); //Get steering information SnakeErase(pSnakeHead); SnakeMove(pSnakeHead); //move PrintFood(); //Show food SnakePrint(pSnakeHead); //Show Snake if (level == 4) WallPrint(); if (JudgeDie(pSnakeHead)) break; //Judge whether Die if (level == 4 && SnakeJudgeWall(pSnakeHead)) break; if (JudgeGrow(pSnakeHead)) //Determine whether you eat food { SetFood(); //Reset food SnakeGrow(pSnakeHead); //Snake grows } if (level < 4) Sleep(400 - 100 * level); //Using Sleep function to control speed else if (level == 4) { Sleep(100); //Challenge mode speed clock++; if (clock == 100) { WallErase(); WallMaker(pSnakeHead); WallPrint(); clock = 0; } } } system("cls"); score = CountScore(pSnakeHead)- INITIAIL_LENTH; printf("\n <game over>\n\n $Final score:%d\n", score); //Output final score if (score > HighestScore[level-1]) { HighestScore[level-1] = score; WriteArchive(HighestScore); printf("\n $New record!\n"); } printf("\n $Another round? Input'y'or'n': "); HideCursor(FALSE); //Redisplay cursor getchar();scanf("%c",&repeat); if (repeat != 'y') break; } return 0; } int ChooseLevel() //Selection difficulty { int level; do { system("cls"); printf("\n #Greedy snake·Endless mode#\n\n < select difficulty > \ n\n 1. Simple 2. General 3. Difficult \ n\n 4. Switch to challenge mode \ n\n please select difficulty: "; scanf("%d", &level); } while (level < 1 || level > N); //1~N must be entered return level; } void ChooseGameColor() { int color; do{ system("cls"); system("color f0"); printf("\n #Greedy snake·Endless mode#\n\n < select color > \ n\n 1. Black 2. Blue 3. Green 4. Red 5. Purple \ n\n please select a color you like: "; scanf("%d",&color); switch(color) { case 1:break; case 2:system("color f9");break; case 3:system("color f2");break; case 4:system("color fc");break; case 5:system("color fd");break; } }while(color<1 || color>5); } void gotoxy(int x, int y) //Move the cursor to the specified position { COORD coord; coord.X = x; coord.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); } void HideCursor(BOOL flag) //hide cursor { CONSOLE_CURSOR_INFO cursor; if (flag) cursor.bVisible = FALSE; else cursor.bVisible = TRUE; cursor.dwSize = sizeof(cursor); HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorInfo(handle, &cursor); } void SetFood() //Produce food { int x; x = rand() % WIDTH; //Two columns in the x direction are reduced to one grid Food.X = (x % 2 == 0) ? x : x - 1; Food.Y = rand() % HEIGHT; } void PrintFood() //Show food { gotoxy(Food.X, Food.Y); printf("@@"); } struct snake* SnakeCreate(int lenth) //Generative snake { struct snake* pHead, * pTemp, * pNew=NULL; int i, x; pHead = (struct snake*)malloc(LEN); if (pHead) { x = rand() % (WIDTH - 2 * 10) + 10; //Set the distance from the left and right borders to avoid Die at birth pHead->coord.X = (x % 2 == 0) ? x : x - 1; //Two columns in the x direction are reduced to one grid pHead->coord.Y = rand() % (HEIGHT - 2 * 5) + 5; //Set the distance from the upper and lower borders to avoid Die at birth pTemp = pHead; Direction = rand() % 4; for (i = 1; i < lenth; i++) { pNew = (struct snake*)malloc(LEN); if (pNew) { switch (Direction) { case 0: {pNew->coord.X = (pTemp->coord.X) + 2; pNew->coord.Y = pTemp->coord.Y; }break; //Head to left case 1: {pNew->coord.X = pTemp->coord.X; pNew->coord.Y = (pTemp->coord.Y) - 1; }break; //Head down case 2: {pNew->coord.X = (pTemp->coord.X) - 2; pNew->coord.Y = pTemp->coord.Y; }break; //Head to right case 3: {pNew->coord.X = pTemp->coord.X; pNew->coord.Y = (pTemp->coord.Y) + 1; }break; //Head up default:printf("Direction error in SnakeCreate\n"); } pTemp->pNext = pNew; pTemp = pNew; } else { printf("malloc error in SnakeCreate\n"); exit(1); } } if(pNew) pNew->pNext = NULL; } else { printf("malloc error in SnakeCreate\n"); free(pHead); exit(1); } return pHead; } void SnakePrint(struct snake* pHead) //Show Snake { struct snake* p = pHead->pNext; gotoxy(pHead->coord.X, pHead->coord.Y); printf("[]"); while (p != NULL) { gotoxy(p->coord.X, p->coord.Y); printf("()"); p = p->pNext; } } void SnakeErase(struct snake* pHead) //Erase snake { struct snake* p = pHead; while(p!=NULL) { gotoxy(p->coord.X, p->coord.Y); printf(" "); p = p->pNext; } } void SnakeMove(struct snake* pHead) //move { int tempX_1, tempY_1, tempX_2, tempY_2; //Two pairs of temporary variables: because you need to remember that the position of the previous section is assigned to this section, and keep the position of this section assigned to the next section struct snake* p; p = pHead; tempX_1 = p->coord.X; tempY_1 = p->coord.Y; switch (Direction) { case 0:p->coord.X -= 2; break; //Go left case 1:p->coord.Y += 1; break; //Go down case 2:p->coord.X += 2; break; //Go right case 3:p->coord.Y -= 1; break; //Go up default: {printf("Direction error in SnakeMove\n"); exit(1); } } p = p->pNext; while (p->pNext != NULL) { tempX_2 = p->coord.X; tempY_2 = p->coord.Y; p->coord.X = tempX_1; p->coord.Y = tempY_1; tempX_1 = tempX_2; tempY_1 = tempY_2; p = p->pNext; } End.X=p->coord.X; End.Y=p->coord.Y; p->coord.X = tempX_1; p->coord.Y = tempY_1; } void GetDirection() //Steering control { char ch; if(_kbhit()) { if ((ch=_getch())==-32) ch=_getch(); if (ch==32) _getch(); else if (ch==75 && Direction != 2) Direction = 0; //Turn left (increase the judgment on the original value of Direction to prevent direct turning) else if (ch==80 && Direction != 3) Direction = 1; //Turn down else if (ch==77 && Direction != 0) Direction = 2; //turn right else if (ch==72 && Direction != 1) Direction = 3; //Turn up } } int JudgeDie(struct snake* pHead) //Judge whether you are out of bounds and bite yourself { int flag = 0; struct snake* p; p = (pHead->pNext); if (pHead->coord.X<0 || pHead->coord.X>=WIDTH || pHead->coord.Y<0 || pHead->coord.Y>=HEIGHT) flag = 1; //out while (p != NULL) { if (pHead->coord.X == p->coord.X && pHead->coord.Y == p->coord.Y) flag = 1; //Bite yourself p = p->pNext; } return flag; } int JudgeGrow(struct snake* pHead) //Determine whether you eat food { int flag = 0; if (pHead->coord.X == Food.X && pHead->coord.Y == Food.Y) flag = 1; return flag; } void SnakeGrow(struct snake* pHead) //Lengthen { struct snake* p, * pNew; p = pHead; pNew = (struct snake*)malloc(LEN); if (pNew) { while (p->pNext != NULL) p = p->pNext; pNew->coord.X =End.X; pNew->coord.Y =End.Y; pNew->pNext = NULL; p->pNext = pNew; } else { printf("malloc error in SnakeMove\n"); free(pNew); exit(1); } } int CountScore(struct snake* pHead) //Statistical score { struct snake* p; int i; p = pHead; i = 0; while (p != NULL) { p = p->pNext; i++; } return i; } void ReadArchive(int HighestScore[]) //Load Game { int i; FILE* fp; fp = fopen("HighestScore.bin", "rb"); if (fp==NULL) { fp = fopen("HighestScore.bin", "wb"); for (i = 0; i < N; i++) HighestScore[i] = 0; fwrite(HighestScore, sizeof(int), N, fp); fclose(fp); fp=fopen("HighestScore.bin", "rb"); } fread(HighestScore, sizeof(int), N, fp); fclose(fp); } void WriteArchive(int HighestScore[]) //Write Archive { FILE* fp; fp = fopen("HighestScore.bin", "wb"); fwrite(HighestScore, sizeof(int), N, fp); fclose(fp); } void WallMaker(struct snake* pHead) { int k, i, j, x, flag = 0; //flag is used to provide conditions for the loop struct snake* p = pHead; for (k = 0; k < NUMBER_OF_WALL; k++) { do { x = rand() % WIDTH; wall[k][0].X = (x % 2 == 0) ? x : x - 1; wall[k][0].Y = rand() % HEIGHT; flag = 0; while (p != NULL) //Prevent overlap with snakes { if (wall[k][0].X == p->coord.X && wall[k][0].Y == p->coord.Y) { flag = 1; p = pHead; break; } p = p->pNext; } } while (flag); i = 1; while (i < LENTH_OF_WALL) { WallGetNext(k, i); flag = 0; for (j = 0; j < i; j++) //Prevent duplication with existing walls { if (wall[k][j].X == wall[k][i].X && wall[k][j].Y == wall[k][i].Y) { flag = 1; break; } } if (flag == 1) continue; if (wall[k][i].X < 0 || wall[k][i].X >= WIDTH || wall[k][i].Y < 0 || wall[k][i].Y >= HEIGHT) //Prevent out of bounds continue; while (p != NULL) //Prevent overlap with snakes { if (wall[k][i].X == p->coord.X && wall[k][i].Y == p->coord.Y) { flag = 1; p = pHead; break; } p = p->pNext; } if (flag == 1) continue; i++; } } } void WallGetNext(int i, int j) { int dir[2] = { 1,-1 }; switch (rand() % 2) { case 0: { wall[i][j].X = 2 * dir[rand() % 2] + wall[i][j - 1].X; wall[i][j].Y = wall[i][j - 1].Y; }break; case 1: { wall[i][j].X = wall[i][j - 1].X; wall[i][j].Y = dir[rand() % 2] + wall[i][j - 1].Y; }break; } } void WallPrint() { int i, j; for (i = 0; i < NUMBER_OF_WALL; i++) { for (j = 0; j < LENTH_OF_WALL; j++) { gotoxy(wall[i][j].X, wall[i][j].Y); printf("[]"); } } } void WallErase() { int i, j; for (i = 0; i < NUMBER_OF_WALL; i++) { for (j = 0; j < LENTH_OF_WALL; j++) { gotoxy(wall[i][j].X, wall[i][j].Y); printf(" "); } } } int SnakeJudgeWall(struct snake* pHead) //Judge whether to hit the wall { int flag = 0, i, j; for (i = 0; i < NUMBER_OF_WALL; i++) for(j=0;j<LENTH_OF_WALL;j++) if (pHead->coord.X == wall[i][j].X && pHead->coord.Y == wall[i][j].Y) flag = 1; //Hit the wall return flag; }
DOS version
Because I think the console program is incompatible with and dwarfed by the graphical interface, I have a DOS version. Don't mention, it's fun. It was compiled with winTC on win98. There are some points to pay attention to:
1. Because windows.h header file cannot be used on DOS, COORD structure cannot be used for coordinate information, so it has to be customized, or int x and int y can be used respectively
2. There is no Sleep function on dos, only sleep(), which is in seconds and can pass in integers. So I customized a Sleep function. Use CLK_TCK constant and clock() in time.h Function. The CLK_TCK constant in dos is 18.2, and the corresponding CLOCKS_PER_SEC value in windows is 1000. It refers to how much the cpu clock counter increases in one second, that is, how many times the cpu ticks in one second, which is related to the cpu operation speed. The custom Sleep function code is as follows (it should be correct):
#include <time.h> void Sleep(unsigned millisecond) { clock_t t_0, t_1; t_1 = t_0 = clock(); while(t_1 - t_0 <= millisecond * CLK_TCK / 1000) { t_1 = clock(); } }
3. Another function to hide the cursor on dos is required, as shown in the following, which can be found on the Internet:
#include <dos.h> void closecur() { union REGS r; r.h.ah=1; r.h.ch=0x20; int86(0x10,&r,&r); } void opencur() { union REGS r; r.h.ah=1; r.h.ch=12; r.h.cl=13; int86(0x10,&r,&r); }
It's OK to use system("cls") to clear the screen on my win10, but it starts to get stuck on dos. So I have to erase the original Snake instead. In short, it's been adjusted for a long time.
But! But! Before deleting the virtual machine, I forgot to save the files in it. Now the dos version has no source code, only two exe files (one with a wall and the other without a wall). The link is as follows:
Link: https://pan.baidu.com/s/1TTP8CdzWEEq7XaJ1ppMdvA
Extraction code: gdsk
Play and cherish it. It has to be a pure dos system. Maybe I can't think of it one day. I may change the source code again, alas~