当前位置:   article > 正文

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

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()函数执行一次,就实现了动态移动。

 

 

 

实现效果:

 

完整代码:

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include<stdio.h>
  3. #include<time.h>
  4. #include<windows.h>
  5. #include<stdlib.h>
  6. #include<conio.h> //接收键盘输入输出
  7. #define U 1
  8. #define D 2
  9. #define L 3
  10. #define R 4 //蛇的状态,U:上 ;D:下;L:左 R:右
  11. /*******定 义 全 局 变 量 *******/
  12. typedef struct snake //蛇身的一个节点
  13. {
  14. int x;
  15. int y;
  16. struct snake *next;
  17. }snake;
  18. int score=0,add=10; //总得分与每次吃食物得分
  19. int status,sleeptime=200; //蛇前进状态,每次运行的时间间隔
  20. snake *head, *food; //蛇头指针,食物指针
  21. snake *q; //遍历蛇的时候用到的指针
  22. int endgamestatus=0; //游戏结束的情况,1:撞到墙;2:咬到自己;3:主动退出游戏。
  23. HANDLE hOut; //控制台句柄
  24. /*******函 数 声 明 *******/
  25. void gotoxy(int x,int y); //设置光标位置
  26. int color(int c); //更改文字颜色
  27. void welcometogame(); //开始界面
  28. void createMap(); //绘制地图
  29. void scoreandtips(); //游戏界面右侧的得分和小提示
  30. void initsnake(); //初始化蛇身,画蛇身
  31. void createfood(); //创建并随机出现食物
  32. int biteself(); //判断是否咬到了自己
  33. void cantcrosswall(); //设置蛇撞墙的情况
  34. void speedup(); //加速
  35. void speeddown(); //减速
  36. void snakemove(); //控制蛇前进方向
  37. void keyboardControl(); //控制键盘按键
  38. void Lostdraw(); //游戏结束界面
  39. void endgame(); //游戏结束
  40. void choose(); //游戏失败之后的选择
  41. void explation(); //游戏说明
  42. /**
  43. * 设置光标位置
  44. */
  45. void gotoxy(int x,int y)
  46. {
  47. COORD c;
  48. c.X=x;
  49. c.Y=y;
  50. SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
  51. }
  52. /**
  53. * 文字颜色函数
  54. */
  55. int color(int c)
  56. {
  57. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //更改文字颜色
  58. return 0;
  59. }
  60. //开始界面
  61. void welcometogame()
  62. {
  63. int n;
  64. gotoxy(43,10);
  65. color(11);
  66. printf("贪 吃 蛇 大 作 战");
  67. color(12);
  68. gotoxy(25, 22);
  69. printf("1.开始游戏");
  70. gotoxy(45, 22);
  71. printf("2.游戏说明");
  72. gotoxy(65, 22);
  73. printf("3.退出游戏");
  74. gotoxy(40,27);
  75. color(3);
  76. printf("请选择 1 2 3:");
  77. color(14);
  78. scanf("%d", &n); //输入选项
  79. switch (n)
  80. {
  81. case 1:
  82. system("cls");
  83. createMap(); //创建地图
  84. initsnake(); //初始化蛇身
  85. createfood(); //创建食物
  86. keyboardControl(); //按键控制
  87. break;
  88. case 2:
  89. explation(); //游戏说明函数
  90. break;
  91. case 3:
  92. exit(0); //退出游戏
  93. break;
  94. default:
  95. color(12);
  96. gotoxy(40,28);
  97. printf("请输入1—3之间的数!");
  98. _getch(); //输入任意键
  99. system("cls"); //清屏
  100. welcometogame();
  101. }
  102. }
  103. //创建地图
  104. void createMap()
  105. {
  106. int i,j;
  107. for(i=0;i<58;i+=2) //打印上下边框
  108. {
  109. gotoxy(i,0);
  110. color(5);
  111. printf("□");
  112. gotoxy(i,26);
  113. printf("□");
  114. }
  115. for(i=1;i<26;i++) //打印左右边框
  116. {
  117. gotoxy(0,i);
  118. printf("□");
  119. gotoxy(56,i);
  120. printf("□");
  121. }
  122. for(i = 2;i<56;i+=2) //打印中间网格
  123. {
  124. for(j = 1;j<26;j++)
  125. {
  126. gotoxy(i,j);
  127. color(3);
  128. printf("■");
  129. }
  130. }
  131. }
  132. // 游戏界面右侧的得分和小提示
  133. void scoreandtips()
  134. {
  135. gotoxy(64,8);
  136. color(14);
  137. printf("得分:%d ",score);
  138. gotoxy(64,14);
  139. printf("每个食物得分:%d分",add);
  140. gotoxy(64,16);
  141. printf("不能穿墙,不能咬到自己");
  142. gotoxy(64,18);
  143. printf("用↑ ↓ ← →分别控制蛇的移动");
  144. gotoxy(64,20);
  145. printf("F1 为加速,F2 为减速");
  146. gotoxy(64,22);
  147. printf("space:暂停游戏");
  148. gotoxy(64,24);
  149. printf("ESC :退出游戏");
  150. }
  151. //初始化蛇身,画蛇身
  152. void initsnake()
  153. {
  154. snake *tail;
  155. int i;
  156. tail=(snake*)malloc(sizeof(snake));//从蛇尾开始,头插法,以x,y设定开始的位置
  157. tail->x=24; //蛇的初始位置(24,5)
  158. tail->y=5;
  159. tail->next=NULL;
  160. for(i=1;i<=4;i++) //设置蛇身,长度为5
  161. {
  162. head=(snake*)malloc(sizeof(snake)); //初始化蛇头
  163. head->next=tail; //蛇头的下一位为蛇尾
  164. head->x=24+2*i; //设置蛇头位置
  165. head->y=5;
  166. tail=head; //蛇头变成蛇尾,然后重复循环
  167. }
  168. while(tail!=NULL) //从头到尾,输出蛇身
  169. {
  170. gotoxy(tail->x,tail->y);
  171. color(14);
  172. printf("★"); //输出蛇身,蛇身使用★组成
  173. tail=tail->next; //蛇头输出完毕,输出蛇头的下一位,一直输出到蛇尾
  174. }
  175. }
  176. /**
  177. * 随机出现食物
  178. */
  179. void createfood()
  180. {
  181. snake *food_1;
  182. srand((unsigned)time(NULL)); //初始化随机数
  183. food_1=(snake*)malloc(sizeof(snake)); //初始化food_1
  184. while((food_1->x%2)!=0) //保证其为偶数,使得食物能与蛇头对其,然后食物会出现在网格线上
  185. {
  186. food_1->x=rand()%52+2; //食物随机出现
  187. }
  188. food_1->y=rand()%24+1;
  189. q=head;
  190. while(q->next==NULL)
  191. {
  192. if(q->x==food_1->x && q->y==food_1->y) //判断蛇身是否与食物重合
  193. {
  194. free(food_1); //如果蛇身和食物重合,那么释放食物指针
  195. createfood(); //重新创建食物
  196. }
  197. q=q->next;
  198. }
  199. gotoxy(food_1->x,food_1->y);
  200. food=food_1;
  201. color(12);
  202. printf("●"); //输出食物
  203. }
  204. /**
  205. * 判断是否咬到了自己
  206. */
  207. int biteself()
  208. {
  209. snake *self; //定义self为蛇身上的一个节点
  210. self=head->next; //self是蛇头之外的蛇身上的节点
  211. while(self!=NULL)
  212. {
  213. if(self->x==head->x && self->y==head->y) //如果self和蛇身上的节点重合
  214. {
  215. return 1; //返回1
  216. }
  217. self=self->next;
  218. }
  219. return 0;
  220. }
  221. /**
  222. * 设置蛇撞墙的情况
  223. */
  224. void cantcrosswall()
  225. {
  226. if(head->x==0 || head->x==56 ||head->y==0 || head->y==26) //如果蛇头碰到了墙壁
  227. {
  228. endgamestatus=1; //返回第一种情况
  229. endgame(); //出现游戏结束界面
  230. }
  231. }
  232. /**
  233. * 加速,蛇吃到食物会自动提速,并且按F1会加速
  234. */
  235. void speedup()
  236. {
  237. if(sleeptime>=50)
  238. {
  239. sleeptime=sleeptime-10;
  240. add=add+2;
  241. }
  242. }
  243. /**
  244. * 加速,按F2会减速
  245. */
  246. void speeddown()
  247. {
  248. if(sleeptime<350) //如果时间间隔小于350
  249. {
  250. sleeptime=sleeptime+30; //时间间隔加上30
  251. add=add-2; //每吃一次食物的得分减2
  252. }
  253. }
  254. /**
  255. * 控制方向 问题:为什么要设置status,而不使用前两章中接收键盘按键的方法
  256. */
  257. void snakemove() //蛇前进,上U,下D,左L,右R
  258. {
  259. snake * nexthead;
  260. cantcrosswall();
  261. nexthead=(snake*)malloc(sizeof(snake)); //为下一步开辟空间
  262. if(status==U)
  263. {
  264. nexthead->x=head->x; //向上前进时,x坐标不动,y坐标-1
  265. nexthead->y=head->y-1;
  266. nexthead->next=head;
  267. head=nexthead;
  268. q=head; //指针q指向蛇头
  269. if(nexthead->x==food->x && nexthead->y==food->y) //如果下一个有食物 下一个位置的坐标和食物的坐标相同
  270. {
  271. while(q!=NULL)
  272. {
  273. gotoxy(q->x,q->y);
  274. color(14);
  275. printf("★"); //原来食物的位置,从●换成★
  276. q=q->next; //指针q指向的蛇身的下一位也执行循环里的操作
  277. }
  278. score=score+add; //吃了一个食物,在总分上加上食物的分
  279. speedup();
  280. createfood(); //创建食物
  281. }
  282. else
  283. {
  284. while(q->next->next!=NULL) //如果没遇到食物
  285. {
  286. gotoxy(q->x,q->y);
  287. color(14);
  288. printf("★"); //蛇正常往前走,输出当前位置的蛇身
  289. q=q->next; //继续输出整个蛇身
  290. }
  291. gotoxy(q->next->x,q->next->y); //经过上面的循环,q指向蛇尾,蛇尾的下一位,就是蛇走过去的位置
  292. color(3);
  293. printf("■");
  294. free(q->next); //进行输出■之后,释放指向下一位的指针
  295. q->next=NULL; //指针下一位指向空
  296. }
  297. }
  298. if(status==D)
  299. {
  300. nexthead->x=head->x; //向下前进时,x坐标不动,y坐标+1
  301. nexthead->y=head->y+1;
  302. nexthead->next=head;
  303. head=nexthead;
  304. q=head;
  305. if(nexthead->x==food->x && nexthead->y==food->y) //有食物
  306. {
  307. while(q!=NULL)
  308. {
  309. gotoxy(q->x,q->y);
  310. color(14);
  311. printf("★");
  312. q=q->next;
  313. }
  314. score=score+add;
  315. speedup();
  316. createfood();
  317. }
  318. else //没有食物
  319. {
  320. while(q->next->next!=NULL)
  321. {
  322. gotoxy(q->x,q->y);
  323. color(14);
  324. printf("★");
  325. q=q->next;
  326. }
  327. gotoxy(q->next->x,q->next->y);
  328. color(3);
  329. printf("■");
  330. free(q->next);
  331. q->next=NULL;
  332. }
  333. }
  334. if(status==L)
  335. {
  336. nexthead->x=head->x-2; //向左前进时,x坐标向左移动-2,y坐标不动
  337. nexthead->y=head->y;
  338. nexthead->next=head;
  339. head=nexthead;
  340. q=head;
  341. if(nexthead->x==food->x && nexthead->y==food->y)//有食物
  342. {
  343. while(q!=NULL)
  344. {
  345. gotoxy(q->x,q->y);
  346. color(14);
  347. printf("★");
  348. q=q->next;
  349. }
  350. score=score+add;
  351. speedup();
  352. createfood();
  353. }
  354. else //没有食物
  355. {
  356. while(q->next->next!=NULL)
  357. {
  358. gotoxy(q->x,q->y);
  359. color(14);
  360. printf("★");
  361. q=q->next;
  362. }
  363. gotoxy(q->next->x,q->next->y);
  364. color(3);
  365. printf("■");
  366. free(q->next);
  367. q->next=NULL;
  368. }
  369. }
  370. if(status==R)
  371. {
  372. nexthead->x=head->x+2; //向右前进时,x坐标向右移动+2,y坐标不动
  373. nexthead->y=head->y;
  374. nexthead->next=head;
  375. head=nexthead;
  376. q=head;
  377. if(nexthead->x==food->x && nexthead->y==food->y)//有食物
  378. {
  379. while(q!=NULL)
  380. {
  381. gotoxy(q->x,q->y);
  382. color(14);
  383. printf("★");
  384. q=q->next;
  385. }
  386. score=score+add;
  387. speedup();
  388. createfood();
  389. }
  390. else //没有食物
  391. {
  392. while(q->next->next!=NULL)
  393. {
  394. gotoxy(q->x,q->y);
  395. color(14);
  396. printf("★");
  397. q=q->next;
  398. }
  399. gotoxy(q->next->x,q->next->y);
  400. color(3);
  401. printf("■");
  402. free(q->next);
  403. q->next=NULL;
  404. }
  405. }
  406. if(biteself()==1) //判断是否会咬到自己
  407. {
  408. endgamestatus=2;
  409. endgame();
  410. }
  411. }
  412. /**
  413. * 控制键盘按键
  414. */
  415. void keyboardControl()
  416. {
  417. status=R; //初始蛇向右移动
  418. while(1)
  419. {
  420. scoreandtips();
  421. if(GetAsyncKeyState(VK_UP) && status!=D) //GetAsyncKeyState函数用来判断函数调用时指定虚拟键的状态
  422. {
  423. status=U; //如果蛇不是向下前进的时候,按上键,执行向上前进操作
  424. }
  425. else if(GetAsyncKeyState(VK_DOWN) && status!=U) //如果蛇不是向上前进的时候,按下键,执行向下前进操作
  426. {
  427. status=D;
  428. }
  429. else if(GetAsyncKeyState(VK_LEFT)&& status!=R) //如果蛇不是向右前进的时候,按左键,执行向左前进
  430. {
  431. status=L;
  432. }
  433. else if(GetAsyncKeyState(VK_RIGHT)&& status!=L) //如果蛇不是向左前进的时候,按右键,执行向右前进
  434. {
  435. status=R;
  436. }
  437. if(GetAsyncKeyState(VK_SPACE)) //按暂停键,执行pause暂停函数
  438. {
  439. while(1)
  440. {
  441. Sleep(300); //sleep()函数,头文件#include <unistd.h> 另进程暂停,知道达到里面设定的参数的时间。
  442. if(GetAsyncKeyState(VK_SPACE)) //按空格键暂停
  443. {
  444. break;
  445. }
  446. }
  447. }
  448. else if(GetAsyncKeyState(VK_ESCAPE))
  449. {
  450. endgamestatus=3; //按esc键,直接到结束界面
  451. break;
  452. }
  453. else if(GetAsyncKeyState(VK_F1)) //按F1键,加速
  454. {
  455. speedup();
  456. }
  457. else if(GetAsyncKeyState(VK_F2)) //按F2键,减速
  458. {
  459. speeddown();
  460. }
  461. Sleep(sleeptime);
  462. snakemove();
  463. }
  464. }
  465. /*
  466. * 游戏说明
  467. */
  468. void explation()
  469. {
  470. //int i,j = 1;
  471. system("cls");
  472. // color(13);
  473. // gotoxy(44,3);
  474. // printf("游戏说明");
  475. // color(2);
  476. // for (i = 6; i <= 22; i++) //输出上下边框===
  477. //{
  478. // for (j = 20; j <= 75; j++) //输出左右边框||
  479. // {
  480. // gotoxy(j, i);
  481. // if (i == 6 || i == 22) printf("=");
  482. // else if (j == 20 || j == 75) printf("||");
  483. // }
  484. //}
  485. color(3);
  486. gotoxy(30,8);
  487. printf("1. 不能穿墙,不能咬到自己");
  488. color(10);
  489. gotoxy(30,11);
  490. printf("2. 用↑.↓.←.→分别控制蛇的移动");
  491. color(14);
  492. gotoxy(30,14);
  493. printf("3. F1 为加速,F2 为减速");
  494. color(11);
  495. gotoxy(30,17);
  496. printf("4. 按空格键暂停游戏,再按空格键继续");
  497. color(4);
  498. gotoxy(30,20);
  499. printf("5. ESC :退出游戏.space:暂停游戏");
  500. _getch(); //按任意键返回主界面
  501. system("cls");
  502. welcometogame();
  503. }
  504. /**
  505. * 结束游戏
  506. */
  507. void endgame()
  508. {
  509. system("cls");
  510. if(endgamestatus==1)
  511. {
  512. //Lostdraw();
  513. gotoxy(43,9);
  514. color(12);
  515. printf("GAME OVER !");
  516. }
  517. else if(endgamestatus==2)
  518. {
  519. //Lostdraw();
  520. gotoxy(43,9);
  521. color(12);
  522. printf("GAME OVER !");
  523. }
  524. else if(endgamestatus==3)
  525. {
  526. //Lostdraw();
  527. gotoxy(40,9);
  528. color(12);
  529. printf("已结束游戏。");
  530. }
  531. gotoxy(43,12);
  532. color(13);
  533. printf("你的得分是 %d",score);
  534. choose();
  535. }
  536. /**
  537. * 边框下面的分支选项
  538. */
  539. void choose()
  540. {
  541. int n;
  542. gotoxy(25,23);
  543. color(12);
  544. printf("Continue ------ 1");
  545. gotoxy(52,23);
  546. printf("Exit ------ 2");
  547. gotoxy(45,25);
  548. color(11);
  549. printf("选择: ");
  550. scanf("%d", &n);
  551. switch (n)
  552. {
  553. case 1:
  554. system("cls"); //清屏
  555. score=0; //分数归零
  556. sleeptime=200; //设定初始速度
  557. add = 10; //使add设定为初值,吃一个食物得分10,然后累加
  558. welcometogame();
  559. break;
  560. case 2:
  561. exit(0); //退出游戏
  562. break;
  563. default:
  564. gotoxy(35,27);
  565. color(12);
  566. printf(" 输入有误 重新输入 !");
  567. system("pause >nul");
  568. endgame();
  569. choose();
  570. break;
  571. }
  572. }
  573. /**
  574. * 失败界面
  575. */
  576. void Lostdraw()
  577. {
  578. system("cls");
  579. int i;
  580. gotoxy(45,2);
  581. color(6);
  582. printf("\\\\\\|///");
  583. gotoxy(43,3);
  584. printf("\\\\");
  585. gotoxy(47,3);
  586. color(15);
  587. printf(".-.-");
  588. gotoxy(54,3);
  589. color(6);
  590. printf("//");
  591. gotoxy(44,4);
  592. color(14);
  593. printf("(");
  594. gotoxy(47,4);
  595. color(15);
  596. printf(".@.@");
  597. gotoxy(54,4);
  598. color(14);
  599. printf(")");
  600. gotoxy(17,5);
  601. color(11);
  602. printf("+------------------------");
  603. gotoxy(35,5);
  604. color(14);
  605. printf("oOOo");
  606. gotoxy(39,5);
  607. color(11);
  608. printf("----------");
  609. gotoxy(48,5);
  610. color(14);
  611. printf("(_)");
  612. gotoxy(51,5);
  613. color(11);
  614. printf("----------");
  615. gotoxy(61,5);
  616. color(14);
  617. printf("oOOo");
  618. gotoxy(65,5);
  619. color(11);
  620. printf("-----------------+");
  621. for(i = 6;i<=19;i++) //竖边框
  622. {
  623. gotoxy(17,i);
  624. printf("|");
  625. gotoxy(82,i);
  626. printf("|");
  627. }
  628. gotoxy(17,20);
  629. printf("+---------------------------------");
  630. gotoxy(52,20);
  631. color(14);
  632. printf("☆☆☆〃");
  633. gotoxy(60,20);
  634. color(11);
  635. printf("----------------------+");
  636. }
  637. /**
  638. * 主函数
  639. */
  640. int main()
  641. {
  642. system("mode con cols=100 lines=30"); //设置控制台的宽高
  643. //printsnake();
  644. welcometogame();
  645. keyboardControl();
  646. endgame();
  647. return 0;
  648. }

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/585024
推荐阅读
相关标签
  

闽ICP备14008679号