程序代码:
/* 头文件的引入,除了标准输入输出(stdio.h)外,需要使用windows.h与控制台conio.h里面的函数 */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <conio.h> #include <windows.h> #include <time.h> /* 为了便于区分蛇的方向,食物的类型,用单词代替了数字,不易出错 */ #define Up 1 #define Down 2 #define Left 3 #define Right 4 /* 结构体的定义与声明,共三个,分别是 点、蛇的节点、蛇、食物,需要用到链表的知识 */ struct Point { int X; int Y; }; //点结构体 struct SnakeNode { Point pos; SnakeNode *next; int direction; }; //蛇的身子节点 struct Snake { SnakeNode *SnakeHead; int statement; }; //蛇结构体 struct Food{ Point pos; int type; }; /* 全局变量的声明,包括分数,游戏速度,蛇的长度,场景信息,与未始化的蛇,食物 */ Snake mysnake; int i=0; int score = 0; int speed = 200; Point startPoint, endPoint,mapStart, lastPoint = {23,10}; SnakeNode *lastNode; Food food; /* 自定义函数的声明 */ void HideCursor(); void gotoxy(int x, int y); void init(); void loading(); void game(); void start(); void help(); void setLevel(); void exit(); void InitMap(); void InitSnake(); void CreateFood(); void updateSnake(char); void updateNode(); void addNode(); void judge(); void drawMap(); void drawSnake(); /* main()函数 */ int main() { mysnake.SnakeHead = (struct SnakeNode*)malloc(sizeof(struct SnakeNode)); lastNode= (struct SnakeNode*)malloc(sizeof(struct SnakeNode)); lastNode->next = (SnakeNode*)malloc(sizeof(SnakeNode)); init(); loading(); game(); return 0; } /* 自定义函数的实现 */ void HideCursor() //光标隐藏函数 { CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; //1表示是文本高度,0表示false,光标隐藏 SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); } void gotoxy(int x,int y) //光标的定位函数 { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备(窗口)句柄 COORD pos; pos.X = x; pos.Y = y; SetConsoleCursorPosition(hOut, pos); // 设置光标的位置 } void init() { HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备句柄 CONSOLE_SCREEN_BUFFER_INFO bInfo; // 窗口缓冲区信息 GetConsoleScreenBufferInfo(hOut, &bInfo); // 获取窗口缓冲区信息 SetConsoleTitle(L"贪吃蛇V1.0 By ^(-@-)^"); // 获取窗口标题 COORD size = { 80, 25 }; SetConsoleScreenBufferSize(hOut, size); // 重新设置缓冲区大小 SMALL_RECT rc = { 0, 0, 80 - 1, 25 - 1 }; // 重置窗口位置和大小 SetConsoleWindowInfo(hOut, true, &rc); system("color F0"); MessageBox(NULL, L"游戏作者:^(-@-)^\n版权归大家所有.....", L"温馨提示", MB_OK | MB_ICONWARNING); HideCursor(); } void loading() { gotoxy(30, 8); printf("欢迎来到贪吃蛇的世界!\n"); gotoxy(30, 12); system("pause"); //让这个界面固定,如果没有它将会是一闪而过,不会停留的 } void game() { fflush(stdin); //将输入缓冲区清空 Point pos; char ans = NULL; system("cls"); //清除上一个回车键时屏幕的内容 gotoxy(36, 6); printf("贪吃蛇"); gotoxy(35, 9); printf("开始游戏"); gotoxy(35, 10); printf("难度设置"); gotoxy(35, 11); printf("帮助信息"); gotoxy(35, 12); printf("退出游戏"); gotoxy(32, 9); printf("→"); gotoxy(43, 9); pos.X = 43, pos.Y = 9; while (1) //这个函数什么意思? { ans = _getch(); switch (ans) { case 'w': if (pos.Y == 9){ gotoxy(32, pos.Y); printf(" "); pos.Y = 12; gotoxy(32, pos.Y); printf("→"); gotoxy(43, pos.Y); break; } else{ gotoxy(32, pos.Y); printf(" "); gotoxy(32, --pos.Y); printf("→"); gotoxy(43, pos.Y); } break; case 's': if (pos.Y == 12){ gotoxy(32, pos.Y); printf(" "); pos.Y = 9; gotoxy(32, pos.Y); printf("→"); gotoxy(43, pos.Y); break; } else{ gotoxy(32, pos.Y); printf(" "); gotoxy(32, ++pos.Y); printf("→"); gotoxy(43, pos.Y); } break; case 13: switch (pos.Y) { case 9:start(); break; case 10:setLevel(); break; case 11: MessageBox(NULL, L"1.移动说明:键盘按键W、A、S、D。\n2.游戏目的:吃掉尽可能多的食物。", L"帮助文档", MB_OK | MB_ICONINFORMATION); break; case 12: int i = MessageBox(NULL, L"确定要退出贪吃蛇游戏吗?", L"再次询问", MB_OKCANCEL | MB_ICONQUESTION); if (i == IDOK) exit(0); break; } default: break; } } } void start() { system("cls"); fflush(stdin); int last = Right; //这个是宏定义last=4; char key=NULL; InitMap(); InitSnake(); drawMap(); CreateFood(); do //游戏最重要的部分,核心 { drawSnake(); Sleep(speed); while (_kbhit() != 0) key = _getch(); //_kbhit() != 0检查键盘是否按键按下,若有则返回非0,否则返回0值 updateSnake(key); if (mysnake.statement == 0) { MessageBox(NULL, L"下次注意点!", L"游戏结束", MB_OK | MB_ICONINFORMATION); Sleep(2000); system("cls"); } } while (mysnake.statement == 1); game(); } void help() { fflush(stdin); system("cls"); char ans = NULL; gotoxy(36, 6); printf("帮助文档"); gotoxy(26, 8); printf("1.移动说明:键盘按键W、A、S、D。\n"); gotoxy(26, 9); printf("2.游戏目的:吃掉尽可能多的食物。\n"); gotoxy(26, 11); printf("请按回车键返回......\n"); while (ans != 13) { ans = _getch(); } game(); return; } void setLevel() { fflush(stdin); system("cls"); char ans = NULL; int i = 10; gotoxy(36, 6); printf("设置难度"); gotoxy(24, 8); printf("难度说明:默认为200ms,越小难度越大。"); gotoxy(24, 9); printf("当前的游戏时间间隔:%dms。",speed); while(1){ gotoxy(24, i++); printf("请输入您要设置的间隔时间(ms):"); scanf_s("%d", &speed); if (speed <= 0) MessageBox(NULL, L"间隔小于零?别逗我了好吗!!!",L"对你无语!", MB_OK | MB_ICONINFORMATION); else if (speed>1000){ MessageBox(NULL,L"不觉得有点慢了吗!",L"温馨提示", MB_OK | MB_ICONINFORMATION); } else { MessageBox(NULL, L"赶快进入游戏吧!", L"设置成功", MB_OK | MB_ICONINFORMATION); break; } } game(); return; } void exit() { system("cls"); int i=MessageBox(NULL, L"确定要退出贪吃蛇游戏吗?", L"再次询问", MB_OKCANCEL | MB_ICONQUESTION); if (i == IDOK) exit(0); } void InitMap() { startPoint.X = 10; startPoint.Y = 6; mapStart.X = startPoint.X + 1; mapStart.Y = startPoint.Y; endPoint.X = 70; endPoint.Y = 22; return; } void InitSnake() { int i = 3; //i为什么取3?? mysnake.statement = 1; mysnake.SnakeHead->pos.X =24; mysnake.SnakeHead->pos.Y =10; mysnake.SnakeHead->next =NULL; mysnake.SnakeHead->direction =Right; //初始化为长度为1的蛇(不含头结点) SnakeNode *current = mysnake.SnakeHead; lastNode = current; while (i--) addNode(); } void CreateFood() { int flag = 0; //这里是否需要一个seed种子srand函数?这样让rand函数可以任意产生随机数,而不用重复 SnakeNode *current = mysnake.SnakeHead; while (1) { flag = 0; food.pos.X = mapStart.X + 2 + rand() % 56; //随机产生0-56以内的随机数+11+2=13-69区间 food.pos.Y = mapStart.Y + 2 + rand() % 12; //随机产生0-12以内的随机数+6+2=8-20区间 while (current != NULL) { if (food.pos.X == current->pos.X&&food.pos.Y == current->pos.Y) { flag = 1; //食物不能与本身的节点重合 break; } current = current->next; //current->next是不是空指针???? } if (flag == 0) //这个判断条件是什么意思?》退出while循环 break; } gotoxy(food.pos.X, food.pos.Y); printf("$"); } void updateSnake(char key) { SnakeNode *current = mysnake.SnakeHead; switch (key) { case 'w':{ if (current->direction == Down) //表明按下与原方向相反的方向是无效果的 { mysnake.SnakeHead->pos.Y++; //mysnake.SnakeHead->pos.Y++和current->pos.Y++是否是等价的 current->direction = Down; break; } mysnake.SnakeHead->pos.Y--; current->direction = Up; break; } case 's':{ if (current->direction == Up) { mysnake.SnakeHead->pos.Y--; current->direction = Up; break; } mysnake.SnakeHead->pos.Y++; current->direction = Down; break; } case 'a':{ if (current->direction == Right) { mysnake.SnakeHead->pos.X++; current->direction = Right; break; } mysnake.SnakeHead->pos.X--; current->direction = Left; break; } case 'd':{ if (current->direction == Left) { mysnake.SnakeHead->pos.X--; current->direction = Left; break; } mysnake.SnakeHead->pos.X++; current->direction = Right; break; } default: { switch (mysnake.SnakeHead->direction) { case Up:current->pos.X, current->pos.Y--; break; case Down:current->pos.X, current->pos.Y++; break; case Left:current->pos.X--, current->pos.Y; break; case Right:current->pos.X++, current->pos.Y; break; } break; } } judge(); updateNode(); } void updateNode() //这个函数在updateSnake最后用到 { int i=0,dir[100]; Point node[100]; SnakeNode *current = mysnake.SnakeHead; while (current!= NULL) { dir[i] = current->direction; node[i++] = current->pos; current = current->next; //依次记录节点数和每个吃节点的方向 } if (i > 99){ MessageBox(NULL,L"你赢了!", L"恭喜通关", MB_OK|MB_ICONINFORMATION); Sleep(1000); game(); } current = mysnake.SnakeHead->next; i = 0; while (current != NULL) //为什么又要重新将数据放回??? { current->direction = dir[i]; current->pos = node[i++]; current = current->next; } } void addNode() { switch (lastNode->direction) { case Up:lastNode->next->pos.Y = lastNode->pos.Y + 1; break; case Down:lastNode->next->pos.Y = lastNode->pos.Y - 1; break; case Left:lastNode->next->pos.X = lastNode->pos.X+1; break; case Right:lastNode->next->pos.X = lastNode->pos.X-1; break; } lastNode = lastNode->next; lastNode->next = NULL; } void judge() { SnakeNode *current = mysnake.SnakeHead; srand((unsigned)time(NULL)); //这是一个随机发生器函数,将time函数强制转换成unsigned类型作为参数 //判断是否吃到了食物 if (mysnake.SnakeHead->pos.X == food.pos.X&&mysnake.SnakeHead->pos.Y == food.pos.Y) { score += 10; gotoxy(10, 5); printf("分数:%4d分", score); addNode(); CreateFood(); } //判断是否撞墙,如果是,从墙的另一侧穿过来 if (mysnake.SnakeHead->pos.X == mapStart.X) { mysnake.SnakeHead->pos.X = endPoint.X-1; } else if (mysnake.SnakeHead->pos.Y == mapStart.Y) { mysnake.SnakeHead->pos.Y = endPoint.Y-1; } else if (mysnake.SnakeHead->pos.X == endPoint.X) { mysnake.SnakeHead->pos.X = mapStart.X + 1; } else if (mysnake.SnakeHead->pos.Y == endPoint.Y) { mysnake.SnakeHead->pos.Y = mapStart.Y + 1; } //判断是否撞到了自己的身体 current = mysnake.SnakeHead->next; while (current != NULL) { if (mysnake.SnakeHead->pos.X == current->pos.X&&mysnake.SnakeHead->pos.Y == current->pos.Y) mysnake.statement = 0; current = current->next; } } void drawMap() { /* 此块的绘制工作,主要依靠移动光标(gotoxy()函数),输出(printf()函数)实现的。 */ int i, j; //绘制标题,分数,速度 gotoxy(37, 3); printf("贪吃蛇"); gotoxy(10, 5); printf("分数:%4d分", score); gotoxy(60, 5); printf("速度:%4dms", speed); //for循环以扫描的方式绘制地图 for (i = startPoint.Y; i <= endPoint.Y; i++) //这个内嵌for循环是为了让四周都绘制好墙壁 { for (j = startPoint.X; j <= endPoint.X;) { if (i == startPoint.Y || i == endPoint.Y || j == startPoint.X || j == endPoint.X) { gotoxy(j, i); printf("■"); } j += 2; } } gotoxy(20, 23); //版权信息 printf("Write By ^(-@-)^. All rights reserved."); return; } void drawSnake() { SnakeNode *current = mysnake.SnakeHead; gotoxy(lastPoint.X, lastPoint.Y); printf(" "); while (current!=NULL) { if (current->next == NULL) { lastPoint.X= current->pos.X; lastPoint.Y = current->pos.Y ;} gotoxy(current->pos.X, current->pos.Y); printf("*"); current = current->next; } }