300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > C语言贪吃蛇(详解)——链表实现

C语言贪吃蛇(详解)——链表实现

时间:2023-05-16 11:18:52

相关推荐

C语言贪吃蛇(详解)——链表实现

贪吃蛇设计思路:

屏幕坐标:

拓展功能:

1.F1,F2控制加速减速 空格暂停游戏 Esc退出

2.加速每个食物得分更高

先打印出游戏界面,还有初始化蛇,蛇的节点用字符串★表示,游戏背景用■表示,因为这些字符串占两个字节的宽度,所以每次x,y坐标的对应关系是x=y*2。在相应位置打印出蛇,初始化蛇为五个节点

初始化蛇头的移动方向为右,根据按键来确定蛇的移动状态,要是吃到食物,就让食物为蛇头,然后随机生成食物,

重点模块:

蛇移动的实现:

定义一个结构体,里面放节点坐标,和next指针。

蛇每次移动是通过用户按键方向来确定下一个蛇头节点的x,y坐标,新建一个节点赋给下一个坐标,在这个坐标打出蛇的图标,找到尾节点,将尾节点打印成背景图标,再将节点释放,这样蛇就动了一下,以此重复,蛇就可以了动态移动。

食物的实现:

定义一个食物节点,也是一个类似于蛇的结构体指针,通过随机数生成坐标,注意不能在墙上和蛇身上。

用户按键的检测:可以使用wasd的字符来确定方向,但这里我们还需要使用空格,ESC键,上下左右键,所以直接一点,通过GetAsyncKeyState( )函数检测用户输入的按键,需要引用头文件conio.h,_getch()函数用来检测当前是否有按键输入,有的话返回非0的数。

蛇的变速:还是通过GetAsyncKeyState( )函数检测F1,F2,键来确定蛇移动一次Sleep()函数中的毫秒数,这样就实现了蛇的加速减速。而且蛇每移动一次,不必使用system(“cls")来清屏,通过在循环中snakemove()执行一次,Sleep()函数执行一次,就实现了动态移动。

实现效果:

完整代码:

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>#include<time.h>#include<windows.h>#include<stdlib.h>#include<conio.h>//接收键盘输入输出#define U 1#define D 2#define L 3 #define R 4//蛇的状态,U:上 ;D:下;L:左 R:右/*******定 义 全 局 变 量 *******/typedef struct snake //蛇身的一个节点{int x;int y;struct snake *next;}snake;int score=0,add=10;//总得分与每次吃食物得分int status,sleeptime=200;//蛇前进状态,每次运行的时间间隔snake *head, *food;//蛇头指针,食物指针snake *q;//遍历蛇的时候用到的指针int endgamestatus=0;//游戏结束的情况,1:撞到墙;2:咬到自己;3:主动退出游戏。HANDLE hOut;//控制台句柄/*******函 数 声 明 *******/void gotoxy(int x,int y); //设置光标位置int color(int c); //更改文字颜色void welcometogame(); //开始界面void createMap(); //绘制地图void scoreandtips();//游戏界面右侧的得分和小提示void initsnake(); //初始化蛇身,画蛇身void createfood();//创建并随机出现食物int biteself(); //判断是否咬到了自己void cantcrosswall(); //设置蛇撞墙的情况void speedup();//加速void speeddown();//减速void snakemove(); //控制蛇前进方向void keyboardControl();//控制键盘按键void Lostdraw(); //游戏结束界面void endgame(); //游戏结束void choose();//游戏失败之后的选择void explation(); //游戏说明/*** 设置光标位置*/void gotoxy(int x,int y){COORD c;c.X=x;c.Y=y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);}/*** 文字颜色函数*/int color(int c){SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //更改文字颜色return 0;}//开始界面void welcometogame(){int n;gotoxy(43,10);color(11);printf("贪 吃 蛇 大 作 战");color(12);gotoxy(25, 22);printf("1.开始游戏");gotoxy(45, 22);printf("2.游戏说明");gotoxy(65, 22);printf("3.退出游戏");gotoxy(40,27);color(3);printf("请选择 1 2 3:"); color(14);scanf("%d", &n); //输入选项switch (n){case 1:system("cls");createMap(); //创建地图initsnake(); //初始化蛇身createfood(); //创建食物keyboardControl();//按键控制break;case 2:explation();//游戏说明函数break;case 3:exit(0);//退出游戏break;default:color(12);gotoxy(40,28);printf("请输入1—3之间的数!");_getch();//输入任意键system("cls");//清屏welcometogame();}}//创建地图void createMap(){int i,j;for(i=0;i<58;i+=2)//打印上下边框{gotoxy(i,0);color(5);printf("□");gotoxy(i,26);printf("□");}for(i=1;i<26;i++)//打印左右边框{gotoxy(0,i);printf("□"); gotoxy(56,i);printf("□"); }for(i = 2;i<56;i+=2)//打印中间网格{for(j = 1;j<26;j++){gotoxy(i,j);color(3);printf("■");}}}// 游戏界面右侧的得分和小提示void scoreandtips(){gotoxy(64,8);color(14);printf("得分:%d ",score);gotoxy(64,14);printf("每个食物得分:%d分",add);gotoxy(64,16);printf("不能穿墙,不能咬到自己");gotoxy(64,18);printf("用↑ ↓ ← →分别控制蛇的移动");gotoxy(64,20);printf("F1 为加速,F2 为减速");gotoxy(64,22);printf("space:暂停游戏");gotoxy(64,24);printf("ESC :退出游戏");}//初始化蛇身,画蛇身void initsnake(){snake *tail;int i;tail=(snake*)malloc(sizeof(snake));//从蛇尾开始,头插法,以x,y设定开始的位置tail->x=24; //蛇的初始位置(24,5)tail->y=5;tail->next=NULL;for(i=1;i<=4;i++) //设置蛇身,长度为5{head=(snake*)malloc(sizeof(snake)); //初始化蛇头head->next=tail; //蛇头的下一位为蛇尾head->x=24+2*i; //设置蛇头位置head->y=5;tail=head; //蛇头变成蛇尾,然后重复循环}while(tail!=NULL)//从头到尾,输出蛇身{gotoxy(tail->x,tail->y);color(14);printf("★"); //输出蛇身,蛇身使用★组成tail=tail->next; //蛇头输出完毕,输出蛇头的下一位,一直输出到蛇尾}}/*** 随机出现食物*/void createfood(){snake *food_1;srand((unsigned)time(NULL)); //初始化随机数food_1=(snake*)malloc(sizeof(snake)); //初始化food_1while((food_1->x%2)!=0) //保证其为偶数,使得食物能与蛇头对其,然后食物会出现在网格线上{food_1->x=rand()%52+2; //食物随机出现}food_1->y=rand()%24+1;q=head;while(q->next==NULL){if(q->x==food_1->x && q->y==food_1->y) //判断蛇身是否与食物重合{free(food_1);//如果蛇身和食物重合,那么释放食物指针createfood();//重新创建食物}q=q->next;}gotoxy(food_1->x,food_1->y);food=food_1;color(12);printf("●"); //输出食物}/*** 判断是否咬到了自己*/int biteself(){snake *self; //定义self为蛇身上的一个节点self=head->next; //self是蛇头之外的蛇身上的节点while(self!=NULL){if(self->x==head->x && self->y==head->y) //如果self和蛇身上的节点重合{return 1; //返回1}self=self->next;}return 0;}/*** 设置蛇撞墙的情况*/void cantcrosswall(){ if(head->x==0 || head->x==56 ||head->y==0 || head->y==26) //如果蛇头碰到了墙壁{endgamestatus=1; //返回第一种情况endgame(); //出现游戏结束界面}}/*** 加速,蛇吃到食物会自动提速,并且按F1会加速*/void speedup(){if(sleeptime>=50){sleeptime=sleeptime-10;add=add+2;}}/*** 加速,按F2会减速*/void speeddown(){if(sleeptime<350)//如果时间间隔小于350{sleeptime=sleeptime+30;//时间间隔加上30add=add-2; //每吃一次食物的得分减2}}/*** 控制方向 问题:为什么要设置status,而不使用前两章中接收键盘按键的方法*/void snakemove()//蛇前进,上U,下D,左L,右R{snake * nexthead;cantcrosswall();nexthead=(snake*)malloc(sizeof(snake));//为下一步开辟空间if(status==U){nexthead->x=head->x; //向上前进时,x坐标不动,y坐标-1nexthead->y=head->y-1;nexthead->next=head;head=nexthead;q=head; //指针q指向蛇头if(nexthead->x==food->x && nexthead->y==food->y)//如果下一个有食物 下一个位置的坐标和食物的坐标相同{while(q!=NULL){gotoxy(q->x,q->y);color(14);printf("★"); //原来食物的位置,从●换成★q=q->next;//指针q指向的蛇身的下一位也执行循环里的操作}score=score+add; //吃了一个食物,在总分上加上食物的分speedup();createfood(); //创建食物}else {while(q->next->next!=NULL)//如果没遇到食物{gotoxy(q->x,q->y);color(14);printf("★"); //蛇正常往前走,输出当前位置的蛇身q=q->next; //继续输出整个蛇身}gotoxy(q->next->x,q->next->y); //经过上面的循环,q指向蛇尾,蛇尾的下一位,就是蛇走过去的位置color(3);printf("■");free(q->next);//进行输出■之后,释放指向下一位的指针q->next=NULL; //指针下一位指向空}}if(status==D){nexthead->x=head->x; //向下前进时,x坐标不动,y坐标+1nexthead->y=head->y+1;nexthead->next=head;head=nexthead;q=head;if(nexthead->x==food->x && nexthead->y==food->y) //有食物{while(q!=NULL){gotoxy(q->x,q->y);color(14);printf("★");q=q->next;}score=score+add;speedup();createfood();}else //没有食物{while(q->next->next!=NULL){gotoxy(q->x,q->y);color(14);printf("★");q=q->next;}gotoxy(q->next->x,q->next->y);color(3);printf("■");free(q->next);q->next=NULL;}}if(status==L){nexthead->x=head->x-2; //向左前进时,x坐标向左移动-2,y坐标不动nexthead->y=head->y;nexthead->next=head;head=nexthead;q=head;if(nexthead->x==food->x && nexthead->y==food->y)//有食物{while(q!=NULL){gotoxy(q->x,q->y);color(14);printf("★");q=q->next;}score=score+add;speedup();createfood();}else //没有食物{while(q->next->next!=NULL){gotoxy(q->x,q->y);color(14);printf("★");q=q->next; }gotoxy(q->next->x,q->next->y);color(3);printf("■");free(q->next);q->next=NULL;}}if(status==R){nexthead->x=head->x+2; //向右前进时,x坐标向右移动+2,y坐标不动nexthead->y=head->y;nexthead->next=head;head=nexthead;q=head;if(nexthead->x==food->x && nexthead->y==food->y)//有食物{while(q!=NULL){gotoxy(q->x,q->y);color(14);printf("★");q=q->next;}score=score+add;speedup();createfood();}else //没有食物{while(q->next->next!=NULL){gotoxy(q->x,q->y);color(14);printf("★");q=q->next; }gotoxy(q->next->x,q->next->y);color(3);printf("■");free(q->next);q->next=NULL;}}if(biteself()==1) //判断是否会咬到自己{endgamestatus=2;endgame();}}/*** 控制键盘按键*/void keyboardControl(){status=R; //初始蛇向右移动while(1){scoreandtips();if(GetAsyncKeyState(VK_UP) && status!=D) //GetAsyncKeyState函数用来判断函数调用时指定虚拟键的状态{status=U; //如果蛇不是向下前进的时候,按上键,执行向上前进操作}else if(GetAsyncKeyState(VK_DOWN) && status!=U)//如果蛇不是向上前进的时候,按下键,执行向下前进操作{status=D;}else if(GetAsyncKeyState(VK_LEFT)&& status!=R)//如果蛇不是向右前进的时候,按左键,执行向左前进{status=L;}else if(GetAsyncKeyState(VK_RIGHT)&& status!=L)//如果蛇不是向左前进的时候,按右键,执行向右前进{status=R;}if(GetAsyncKeyState(VK_SPACE))//按暂停键,执行pause暂停函数{while(1){Sleep(300); //sleep()函数,头文件#include <unistd.h> 另进程暂停,知道达到里面设定的参数的时间。if(GetAsyncKeyState(VK_SPACE))//按空格键暂停{break;}} }else if(GetAsyncKeyState(VK_ESCAPE)){endgamestatus=3; //按esc键,直接到结束界面break;}else if(GetAsyncKeyState(VK_F1)) //按F1键,加速{speedup();}else if(GetAsyncKeyState(VK_F2)) //按F2键,减速{speeddown();}Sleep(sleeptime);snakemove();}}/** 游戏说明*/void explation(){//int i,j = 1;system("cls");// color(13);// gotoxy(44,3);// printf("游戏说明");// color(2);// for (i = 6; i <= 22; i++) //输出上下边框===//{//for (j = 20; j <= 75; j++) //输出左右边框||//{//gotoxy(j, i);//if (i == 6 || i == 22) printf("=");//else if (j == 20 || j == 75) printf("||");//}//}color(3);gotoxy(30,8);printf("1. 不能穿墙,不能咬到自己");color(10);gotoxy(30,11);printf("2. 用↑.↓.←.→分别控制蛇的移动");color(14);gotoxy(30,14);printf("3. F1 为加速,F2 为减速");color(11);gotoxy(30,17);printf("4. 按空格键暂停游戏,再按空格键继续");color(4);gotoxy(30,20);printf("5. ESC :退出游戏.space:暂停游戏");_getch();//按任意键返回主界面system("cls");welcometogame();}/*** 结束游戏*/void endgame(){system("cls");if(endgamestatus==1){//Lostdraw();gotoxy(43,9);color(12);printf("GAME OVER !");}else if(endgamestatus==2){//Lostdraw();gotoxy(43,9);color(12);printf("GAME OVER !");}else if(endgamestatus==3){//Lostdraw();gotoxy(40,9);color(12);printf("已结束游戏。");}gotoxy(43,12);color(13);printf("你的得分是 %d",score);choose();}/*** 边框下面的分支选项*/void choose(){int n;gotoxy(25,23);color(12);printf("Continue ------ 1");gotoxy(52,23);printf("Exit ------ 2");gotoxy(45,25);color(11);printf("选择: ");scanf("%d", &n);switch (n){case 1:system("cls");//清屏score=0;//分数归零sleeptime=200;//设定初始速度add = 10;//使add设定为初值,吃一个食物得分10,然后累加welcometogame();break;case 2:exit(0);//退出游戏break;default:gotoxy(35,27);color(12);printf(" 输入有误 重新输入 !");system("pause >nul");endgame();choose();break;}}/*** 失败界面*/void Lostdraw(){system("cls");int i;gotoxy(45,2);color(6);printf("\\\\\\|///");gotoxy(43,3);printf("\\\\");gotoxy(47,3);color(15);printf(".-.-");gotoxy(54,3);color(6);printf("//");gotoxy(44,4);color(14);printf("(");gotoxy(47,4);color(15);printf(".@.@");gotoxy(54,4);color(14);printf(")");gotoxy(17,5);color(11);printf("+------------------------");gotoxy(35,5);color(14);printf("oOOo");gotoxy(39,5);color(11);printf("----------");gotoxy(48,5);color(14);printf("(_)");gotoxy(51,5);color(11);printf("----------");gotoxy(61,5);color(14);printf("oOOo");gotoxy(65,5);color(11);printf("-----------------+");for(i = 6;i<=19;i++) //竖边框{gotoxy(17,i);printf("|");gotoxy(82,i);printf("|");}gotoxy(17,20);printf("+---------------------------------");gotoxy(52,20);color(14);printf("☆☆☆〃");gotoxy(60,20);color(11);printf("----------------------+");}/*** 主函数*/int main(){system("mode con cols=100 lines=30"); //设置控制台的宽高//printsnake();welcometogame();keyboardControl();endgame();return 0;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。