
程序代码:
/*
头文件的引入,除了标准输入输出(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;
}
}