鱼C论坛

 找回密码
 立即注册
查看: 2755|回复: 30

[已解决]一个贪吃蛇的小问题,百思不得骑姐

[复制链接]
发表于 2018-8-2 03:09:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 sswy 于 2018-8-2 03:14 编辑
  1. //头文件需要用的函数
  2. #include <stdio.h>//标准输入输出的库函数
  3. #include <time.h>//要用随机数所以用time函数
  4. #include <Windows.h>//需要,控制DOS界面,要在DOS界面中设置文字颜色,获取坐标位置
  5. #include <stdlib.h>//宏定义和通用工具函数
  6. #include <conio.h>//接收键盘输入输出

  7. //宏定义用来定义上下左右方向,把方向定义成整型的数字,方便在后面代码中进行逻辑运算
  8. #define U 1
  9. #define D 2
  10. #define L 3
  11. #define R 4

  12. //全局变量
  13. typedef struct Snake   //蛇身的一个节点
  14. {
  15.         int x;                           //用来存放节点X坐标
  16.         int y;                           //用来存放节点Y坐标
  17.         struct Snake *next;//蛇身的下一个节点
  18. }SNAKE;



  19. int score = 0, add = 10;//总得分,每次吃食物得分

  20. int HighScore = 0;//游戏最高分

  21. int sleeptime = 200;//sleeptime每次运行的时间间隔

  22. int status; //方向

  23. SNAKE *head, *food;//蛇头指针和食物指针

  24. SNAKE *q;//便利蛇身

  25. int endgamestatus = 0;//表示游戏状态结束的情况,因为有3种情况,咬到自己,撞到墙,主动退出

  26. HANDLE hOut;//为了使用windows函数,建立句柄,它本身是void *


  27.                         /*************函数声明**************/
  28. void gotoxy(int x, int y);//光标位置函数
  29. int color(int c);        //设置文字颜色
  30. void printsnake();        //开始界面蛇绘制
  31. void welcometogame();     //开始界面
  32. void creatMap();          //绘制游戏地图
  33. void scoreandtips();      //游戏界面右侧的得分和小提示
  34. void initsnake();         //初始化蛇身,绘制蛇身
  35. void createfood();        //创建并随机出现食物
  36. int biteself();                  //判断是否咬到了自己
  37. void cantcrosswall();     //设置蛇撞墙情况
  38. void speedup();                          //加速
  39. void speeddown();                  //减速
  40. void snakemove();                  //控制蛇前进方向
  41. void keyboardControl();   //控制键盘按键
  42. void lostdraw();                  //游戏结束界面
  43. void endgame();                          //游戏结束
  44. void choose();                          //游戏失败后的选择
  45. void File_out();                  //存储最高分进文件
  46. void File_in();                          //在文件种读取最高分
  47. void explation();                  //游戏说明


  48. //设置颜色函数
  49. int color(int c)
  50. {

  51.         SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);//设置颜色


  52.         return 0;
  53. }

  54. //控制台移动函数
  55. void gotoxy(int x, int y)
  56. {
  57.         COORD c;//通过修改pos.X和pos.Y的值就可以实现光标的位置控制。
  58.         c.X = x;
  59.         c.Y = y;
  60.         SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);//控制光标位置
  61. }

  62. //在界面中绘制蛇
  63. void printsnake()
  64. {
  65.         //利用建立好的移动函数和颜色函数绘制图形

  66.         color(6);//用颜色函数给字体上色
  67.         gotoxy(35, 1);//移动光标到合适位置,打印出图案
  68.         printf("/^\\/^\");//注意哦C语言中要答应出\需要多加一个\

  69.         gotoxy(34, 2);//移动光标到第二行,打印出图案
  70.         printf("|_| o|");
  71.         gotoxy(33, 2);
  72.         color(2);
  73.         printf("_");

  74.         gotoxy(25, 3);
  75.         color(12);
  76.         printf("\\/");                      //蛇信

  77.         gotoxy(31, 3);
  78.         color(2);
  79.         printf("/");

  80.         gotoxy(37, 3);
  81.         color(6);
  82.         printf(" \\_/");                     //蛇眼睛

  83.         gotoxy(41, 3);
  84.         color(10);
  85.         printf(" \");

  86.         gotoxy(26, 4);
  87.         color(12);
  88.         printf("\\____");                   //舌头

  89.         gotoxy(32, 4);
  90.         printf("_________/");

  91.         gotoxy(31, 4);
  92.         color(2);
  93.         printf("|");

  94.         gotoxy(43, 4);
  95.         color(10);
  96.         printf("\");

  97.         gotoxy(32, 5);
  98.         color(2);
  99.         printf("\\_______");     //蛇嘴

  100.         gotoxy(44, 5);
  101.         color(10);
  102.         printf("\");

  103.         gotoxy(39, 6);
  104.         printf("|     |                  \");  //下面都是画蛇身

  105.         gotoxy(38, 7);
  106.         printf("/      /                   \");

  107.         gotoxy(37, 8);
  108.         printf("/      /                    \\ \");

  109.         gotoxy(35, 9);
  110.         printf("/      /                       \\ \");

  111.         gotoxy(34, 10);
  112.         printf("/     /                          \\  \");

  113.         gotoxy(33, 11);
  114.         printf("/     /             _----_         \\   \");

  115.         gotoxy(32, 12);
  116.         printf("/     /           _-~      ~-_         |  |");

  117.         gotoxy(31, 13);
  118.         printf("(      (        _-~    _--_    ~-_     _/  |");

  119.         gotoxy(32, 14);
  120.         printf("\\     ~-____-~    _-~    ~-_    ~-_-~    /");

  121.         gotoxy(33, 15);
  122.         printf("~-_           _-~          ~-_       _-~");

  123.         gotoxy(35, 16);
  124.         printf("~--______-~                ~-___-~");







  125. }

  126. //欢迎界面
  127. void welcometogame()
  128. {
  129.         int n;
  130.         int i, j = 1;
  131.         color(11);//设置"贪吃蛇大作战"字体颜色
  132.                           //gotoxy(43, 18);//调整光标位置
  133.                           //printf("贪 吃 蛇 大 作 战");//打印


  134.         color(14);//设置边框颜色


  135.         for (i = 20; i <= 26; i++)//移动纵横坐标光标,打印边框
  136.         {

  137.                 for (j = 27; j < 74; j++)
  138.                 {
  139.                         gotoxy(j, i);//移动光标 x不停移动j就是x

  140.                         if (i == 20 || i == 26)//横向定位打印范围      打印横边框
  141.                         {
  142.                                 printf("-");
  143.                         }
  144.                         else if (j == 27 || j == 73)//横向定位2条边的位置才打印    打印竖边框
  145.                         {
  146.                                 printf("|");
  147.                         }
  148.                 }
  149.         }

  150.         //打印选择文字
  151.         color(12);
  152.         gotoxy(35, 22);
  153.         printf("1.开始游戏");
  154.         gotoxy(55, 22);
  155.         printf("2.游戏说明");
  156.         gotoxy(35, 24);
  157.         printf("3.退出游戏");


  158.         //打印下方小文字并根据输入进入函数
  159.         color(3);
  160.         gotoxy(29, 27);
  161.         printf("请选择[1 2 3]:[ ]\b\b");//  \b就是光标退格


  162.         color(14);//输入选择的文字
  163.         scanf("%d", &n);//输入选择

  164.         switch (n)
  165.         {
  166.         case 1:
  167.                 system("cls");//发出DOS命令,清屏
  168.                 creatMap();


  169.                 initsnake();
  170.                 createfood();

  171.                 break;
  172.         case 2:
  173.                 break;

  174.         case 3: exit(0);
  175.                 break;

  176.         default:
  177.                 printf("乱输入我就退出\n");
  178.                 break;
  179.         }

  180. }

  181. //创建地图
  182. void creatMap()
  183. {
  184.         int i, j;

  185.         //打印上下边框(外围)
  186.         for (i = 0; i < 58; i += 2)//因为小方括占用2个字符,所以是i+=2
  187.         {
  188.                 color(5);//边框颜色

  189.                 gotoxy(i, 0);//上坐标
  190.                 printf("●");

  191.                 gotoxy(i, 26);//下坐标
  192.                 printf("●");
  193.         }

  194.         //打印左右竖边框(外围)
  195.         for (i = 1; i < 26; i++)
  196.         {
  197.                 gotoxy(0, i);//打印左边竖边框
  198.                 printf("●");

  199.                 gotoxy(56, i);//打印右边竖边框
  200.                 printf("●");
  201.         }


  202.         //打印棋盘内部
  203.         for (i = 2; i < 56; i += 2)
  204.         {
  205.                 for (j = 1; j < 26; j++)
  206.                 {
  207.                         gotoxy(i, j);
  208.                         color(3);
  209.                         printf("●\n\n");
  210.                 }

  211.         }






  212. }

  213. //创建地图右侧信息
  214. void scoreandtips()//此函数应该在键盘控制的函数中调用,因为每次按键,如果蛇吃到食物,得分都会变化
  215. {
  216.         //File_out();//读取游戏最高分函数

  217.         //打印最高记录和当前得分信息
  218.         color(11);
  219.         gotoxy(64, 4);
  220.         printf("最高记录:%d", HighScore);
  221.         color(11);
  222.         gotoxy(64, 8);
  223.         printf("得分:%d", score);


  224.         //打印小提示
  225.         color(13);//设置颜色
  226.         gotoxy(73, 11);//移动光标
  227.         printf("小提示");//打印

  228.         color(6);//设置颜色
  229.         gotoxy(60, 13);//移动光标
  230.         printf("------------------------------------");//打印
  231.         gotoxy(60, 25);//移动光标
  232.         printf("------------------------------------");//打印

  233.         color(3);
  234.         gotoxy(64, 14);
  235.         printf("每个食物得分:%d分", add);
  236.         gotoxy(64, 16);
  237.         printf("不能撞墙,不能咬到自己");
  238.         gotoxy(64, 18);
  239.         printf("用↑ ↓ ← →分别控制蛇的移动");
  240.         gotoxy(64, 20);
  241.         printf("F1 为加速,F2 为减速");
  242.         gotoxy(64, 22);
  243.         printf("SPACE:暂停游戏");
  244.         gotoxy(64, 24);
  245.         printf("ESC :退出游戏");



  246. }

  247. //读取文本中最高分
  248. void File_in()
  249. {
  250.         FILE *fp;
  251.         fp = fopen("save.txt", "a+");//a+以附加的方式,打开文件,文件不存在创建文件
  252.         fscanf(fp, "%d", &HighScore);//把读取文件中数据给全局变量
  253.         fclose(fp);
  254. }

  255. //绘制蛇身,其实就是定位蛇的初始位置,调整光标位置,打印结构体
  256. void initsnake()
  257. {
  258.         SNAKE *tail;//蛇身
  259.         int i;
  260.         tail = (SNAKE *)malloc(sizeof(SNAKE));//创建蛇尾
  261.         tail->x = 24;//蛇尾坐标定义
  262.         tail->y = 5;
  263.         tail->next = NULL;


  264.         for (i = 1; i <= 4; i++)//分配蛇的光标位置
  265.         {
  266.                 head = (SNAKE *)malloc(sizeof(SNAKE));//创建蛇头
  267.                 head->next = tail;//指向蛇身
  268.                 head->x = 24 + 2 * i;//2代表一个格子
  269.                 head->y = 5;//纵向坐标y,y不动和尾巴在同一排
  270.                 tail = head;//类似头插法
  271.         }

  272.        

  273.         //打印蛇身
  274.         while (tail != NULL)//因为tail = head,所以tail在for结束后是头指针
  275.         {
  276.                 gotoxy(tail->x, tail->y);//根据每一个结构体坐标调整光标位置,并打印出来
  277.                 color(14);
  278.                 printf("★");
  279.                 tail = tail->next;
  280.         }
  281. }

  282. //创建并随机出现食物
  283. void createfood()
  284. {
  285.         SNAKE *food_1=NULL;//创建食物指针


  286.         srand((unsigned)time(NULL));//随机种子

  287.         food_1 = (SNAKE*)malloc(sizeof(SNAKE));//给食物内存
  288.         food_1->next = NULL;

  289.         while ((food_1->x % 2) != 0)//限制食物只会出现在棋盘中的坐标,准备把坐标给到移动光标,绘制食物           %2这里余2是要求食物在棋盘格中间
  290.         {
  291.                 food_1->x = rand() % 25 + 2;//棋盘格子宽度是52  让食物出现在X轴最小限制2-52之间   2因为是2个字符为一个格子


  292.         }
  293.         food_1->y = 24 + 1;//棋盘格子长度是24 让食物出现在Y轴最小限制1-24之间     1因为是纵向1个字符为1列


  294.         q = head;//取得蛇头头指针
  295.        

  296.         //int num = 20;//递归返回的时候可以记录进入递归之前的值,但是如果是指针,无法记录之前的
  297.         char ch = 'n';
  298.         while ( q != NULL)//蛇任何一个部分都不能和食物重叠
  299.         {
  300.                 //num -= 1;
  301.                 if (q->x == food_1->x && q->y == food_1->y)//如果随机出现的食物坐标和蛇初始化坐标重叠,删除这个食物内容,重新创建
  302.                 {
  303.                         free(food_1);//释放当前食物内容

  304.                         /*
  305.                         food_1 = (SNAKE*)malloc(sizeof(SNAKE));
  306.                         while ((food_1->x % 2) != 0)
  307.                         {
  308.                                 food_1->x = rand() % 52 + 2;
  309.                                 food_1->y = 24 + 1;
  310.                                 q = head;
  311.                         }
  312.                         */
  313.                         ch = 'y';
  314.                         createfood();//递归重新创建食物坐标,但是这里注意递归进去后,循环q到NULL运行完毕,函数返回来位置在if(ch=='y'),q坐标已经被移动到NULL了,指针无法记录之前的
  315.                        


  316.                 }
  317.                 if (ch == 'y')
  318.                 {
  319.                         food_1 = food;
  320.                         return;
  321.                 }
  322.                 q = q->next;//蛇身上任何坐标不能和食物重合

  323.                 if (q == NULL)
  324.                 {
  325.                         break;
  326.                 }
  327.         }

  328.         gotoxy(food_1->x, food_1->y);//把随机好的食物坐标给到移动光标里,并绘制出来
  329.         free(food);
  330.         food = food_1;//把食物指针给出去全局指针

  331.        

  332.         color(12);//食物颜色

  333.         printf("●");//绘制食物

  334. }

  335. //判断蛇咬到自己
  336. int biteself()
  337. {
  338.         SNAKE *self;
  339.         q = head;
  340.         self = head;



  341.         while (q ->next!= NULL)//任何一个点碰到身体算失败
  342.         {
  343.                 q = q->next;
  344.                 if (self->x == q->x && self->y == q->y)
  345.                 {
  346.                         return 1;//返回1表示蛇咬到了自己
  347.                 }


  348.         }
  349.         return 0;
  350. }

  351. //判断蛇是否撞墙
  352. void cantcrosswall()
  353. {
  354.         if (head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26)
  355.         {
  356.                 endgamestatus = 1;//撞墙情况的失败界面,给全局
  357.                 printf("撞墙失败");
  358.                 exit(1);
  359.                 return;
  360.         }

  361. }

  362. //蛇加速前进
  363. void speedup()
  364. {
  365.         if (sleeptime >= 50)//限制50的刷新率,
  366.         {
  367.                 sleeptime = sleeptime - 10; //时间间隔 - 10  刷新更快了
  368.                 add = add + 2;//同时每吃一个食物的得分+2,可叠加再吃到14再吃到16   add全局基础得分
  369.         }

  370. }

  371. //蛇减速减速
  372. void speeddown()
  373. {
  374.         if (sleeptime < 350)//限制时间间隔小于350不能太慢了哈
  375.         {
  376.                 sleeptime = sleeptime + 30;//加大刷新间隔,速度就慢了


  377.                 add = add - 2;//减速了游戏就很容易,所以降低基础分

  378.                 if (sleeptime == 350)//当游戏最慢的时候,基础分为1
  379.                 {
  380.                         add = 1;
  381.                 }
  382.         }

  383. }

  384. //不按键时蛇前进,蛇的动力都在这里,这个程序就类似,1帧一个执行,1帧一个执行,可以这样想象,用程序执行间隔Sleep控制蛇的运行速度
  385. void snakemove()
  386. {
  387.         //这个动力其实就是把新的结构体节点,根据当前蛇头位置移动一个位置,然后不断捕捉键盘方向,来循环移动

  388.         SNAKE *nexthead;//表示蛇头下一个位置,新的头指针,代表新的位置,类似移动了一步
  389.         cantcrosswall();//判断蛇是否撞墙
  390.         nexthead = (SNAKE*)malloc(sizeof(SNAKE));//为下一步开辟一个空间

  391.        

  392.         if (status == U)//向上移动一个格子,其实就是根据头指针位置放一个新结构体,再删除最后的尾巴
  393.         {
  394.                 //不断向上前进
  395.                 nexthead->x = head->x;   //新节点保持当前横向坐标不变 ,因为我们要上移一格,所以直接改Y就好了
  396.                 nexthead->y = head->y - 1; //新节点,上移动一个光标位置,因为y是间隔1个字符,所以-1
  397.                 nexthead->next = head;   //链接老蛇头,并把当前地址设成最新蛇头
  398.                 head = nexthead;

  399.                 q = head;//给一个全局傀儡方便控制

  400.                 if (nexthead->x == food->x && nexthead->y == food->y)
  401.                 {
  402.                        

  403.                         while (q != NULL)//当上一移动位置和食物位置重叠,就把当前位置画出来,并不删除尾巴上的
  404.                         {
  405.                                 gotoxy(q->x, q->y);
  406.                                 color(14);
  407.                                 printf("★");
  408.                                 q = q->next;
  409.                         }
  410.                         score = score + add;//食物吃到后加10分

  411.                         speedup();//加速 速度初始200  并且提高下次吃到食物的基础分数

  412.                         createfood();//重新生成食物
  413.                 }
  414.                 else//如果没有吃到食物就把痕迹删除
  415.                 {
  416.                        

  417.                         while (q->next->next != NULL)//当上一移动位置和食物位置没有重叠,就打印当前蛇,但是要把最后一个痕迹删除
  418.                         {
  419.                                 gotoxy(q->x, q->y);
  420.                                 color(14);
  421.                                 printf("★");
  422.                                 q = q->next;
  423.                         }

  424.                         gotoxy(q->next->x, q->next->y);  //清除蛇尾巴,因为移动了一个位置,就要把后面的去除并还原棋盘格
  425.                         color(3);
  426.                         printf("■");
  427.                         free(q->next);        //释放尾巴结构体
  428.                         q->next = NULL;       //当前结构体,指向空
  429.                 }
  430.         }


  431.         if (status == D)//向上移动一个格子,其实就是根据头指针位置放一个新结构体,再删除最后的尾巴
  432.         {
  433.                 //不断向上前进
  434.                 nexthead->x = head->x;   //新节点保持当前横向坐标不变 ,因为我们要上移一格,所以直接改Y就好了
  435.                 nexthead->y = head->y+1; //新节点,上移动一个光标位置,因为y是间隔1个字符,所以-1
  436.                 nexthead->next = head;   //链接老蛇头,并把当前地址设成最新蛇头
  437.                 head = nexthead;

  438.                 q = head;//给一个全局傀儡方便控制

  439.                 if (nexthead->x == food->x && nexthead->y == food->y)
  440.                 {
  441.                

  442.                         while (q != NULL)//当上一移动位置和食物位置重叠,就把当前位置画出来,并不删除尾巴上的
  443.                         {
  444.                                 gotoxy(q->x, q->y);
  445.                                 color(14);
  446.                                 printf("★");
  447.                                 q = q->next;
  448.                         }
  449.                         score = score + add;//食物吃到后加10分

  450.                         speedup();//加速 速度初始200  并且提高下次吃到食物的基础分数

  451.                         createfood();//重新生成食物
  452.                 }
  453.                 else//如果没有吃到食物就把痕迹删除
  454.                 {
  455.                

  456.                         while (q->next->next != NULL)//当上一移动位置和食物位置没有重叠,就打印当前蛇,但是要把最后一个痕迹删除
  457.                         {
  458.                                 gotoxy(q->x, q->y);
  459.                                 color(14);
  460.                                 printf("★");
  461.                                 q = q->next;
  462.                         }

  463.                         gotoxy(q->next->x, q->next->y);  //清除蛇尾巴,因为移动了一个位置,就要把后面的去除并还原棋盘格
  464.                         color(3);
  465.                         printf("■");
  466.                         free(q->next);        //释放尾巴结构体
  467.                         q->next = NULL;       //当前结构体,指向空
  468.                 }
  469.         }


  470.         if (status == L)//向上移动一个格子,其实就是根据头指针位置放一个新结构体,再删除最后的尾巴
  471.         {
  472.                 //不断向上前进
  473.                 nexthead->x = head->x-2;   //新节点保持当前横向坐标不变 ,因为我们要上移一格,所以直接改Y就好了
  474.                 nexthead->y = head->y; //新节点,上移动一个光标位置,因为y是间隔1个字符,所以-1
  475.                 nexthead->next = head;   //链接老蛇头,并把当前地址设成最新蛇头
  476.                 head = nexthead;

  477.                 q = head;//给一个全局傀儡方便控制

  478.                 if (nexthead->x == food->x && nexthead->y == food->y)
  479.                 {
  480.                

  481.                         while (q != NULL)//当上一移动位置和食物位置重叠,就把当前位置画出来,并不删除尾巴上的
  482.                         {
  483.                                 gotoxy(q->x, q->y);
  484.                                 color(14);
  485.                                 printf("★");
  486.                                 q = q->next;
  487.                         }
  488.                         score = score + add;//食物吃到后加10分

  489.                         speedup();//加速 速度初始200  并且提高下次吃到食物的基础分数

  490.                         createfood();//重新生成食物
  491.                 }
  492.                 else//如果没有吃到食物就把痕迹删除
  493.                 {
  494.                

  495.                         while (q->next->next != NULL)//当上一移动位置和食物位置没有重叠,就打印当前蛇,但是要把最后一个痕迹删除
  496.                         {
  497.                                 gotoxy(q->x, q->y);
  498.                                 color(14);
  499.                                 printf("★");
  500.                                 q = q->next;
  501.                         }

  502.                         gotoxy(q->next->x, q->next->y);  //清除蛇尾巴,因为移动了一个位置,就要把后面的去除并还原棋盘格
  503.                         color(3);
  504.                         printf("■");
  505.                         free(q->next);        //释放尾巴结构体
  506.                         q->next = NULL;       //当前结构体,指向空
  507.                 }
  508.         }


  509.         if (status == R)//向上移动一个格子,其实就是根据头指针位置放一个新结构体,再删除最后的尾巴
  510.         {
  511.                 //不断向上前进
  512.                 nexthead->x = head->x + 2;   //新节点保持当前横向坐标不变 ,因为我们要上移一格,所以直接改Y就好了
  513.                 nexthead->y = head->y; //新节点,上移动一个光标位置,因为y是间隔1个字符,所以-1
  514.                 nexthead->next = head;   //链接老蛇头,并把当前地址设成最新蛇头
  515.                 head = nexthead;

  516.                 q = head;//给一个全局傀儡方便控制

  517.                 if (nexthead->x == food->x && nexthead->y == food->y)
  518.                 {
  519.                

  520.                         while (q != NULL)//当上一移动位置和食物位置重叠,就把当前位置画出来,并不删除尾巴上的
  521.                         {
  522.                                 gotoxy(q->x, q->y);
  523.                                 color(14);
  524.                                 printf("★");
  525.                                 q = q->next;
  526.                         }
  527.                         score = score + add;//食物吃到后加10分

  528.                         speedup();//加速 速度初始200  并且提高下次吃到食物的基础分数

  529.                         createfood();//重新生成食物
  530.                 }
  531.                 else//如果没有吃到食物就把痕迹删除
  532.                 {
  533.        


  534.                         while (q->next->next != NULL)//当上一移动位置和食物位置没有重叠,就打印当前蛇,但是要把最后一个痕迹删除
  535.                         {
  536.                                 gotoxy(q->x, q->y);
  537.                                 color(14);
  538.                                 printf("★");
  539.                                 q = q->next;
  540.                         }

  541.                         gotoxy(q->next->x, q->next->y);  //清除蛇尾巴,因为移动了一个位置,就要把后面的去除并还原棋盘格
  542.                         color(3);
  543.                         printf("■");
  544.                         free(q->next);        //释放尾巴结构体
  545.                         q->next = NULL;       //当前结构体,指向空
  546.                 }
  547.         }








  548.         if (biteself() == 1)//判断是否吃到自己    为什么先判断撞墙呢,因为要判断当前蛇头位置是否和最开始的边沿重合   

  549.                                             //而吃到自己是要在自己移动后判断是否蛇头和身体重合
  550.         {
  551.                 endgamestatus = 2;
  552.         }


  553. }

  554. //控制键盘按键
  555. void keyboardControl()
  556. {
  557.         status = R;//设置初始移动方向为右

  558.         while (1)//可以一直按键所以设置循环
  559.         {
  560.                 scoreandtips();//游戏右侧得分小提示

  561.                 if (GetAsyncKeyState(VK_UP) && status != D)//运行方向不是向下,就可以记录上
  562.                 {
  563.                         status = U;
  564.                 }
  565.                 else if (GetAsyncKeyState(VK_DOWN) && status != U)
  566.                 {
  567.                         status = D;
  568.                 }
  569.                 else if (GetAsyncKeyState(VK_LEFT) && status != R)
  570.                 {
  571.                         status = L;
  572.                 }
  573.                 else if (GetAsyncKeyState(VK_RIGHT)  && status != L)
  574.                 {
  575.                         status = R;
  576.                 }


  577.                 if (GetAsyncKeyState(VK_SPACE))//如果按下空格键,暂停游戏300毫秒
  578.                 {
  579.                         Sleep(300);
  580.                         if (GetAsyncKeyState(VK_SPACE))
  581.                         {
  582.                                 break;

  583.                         }
  584.                 }
  585.                 else if (GetAsyncKeyState(VK_ESCAPE))//如果按了ESC键就退出游戏
  586.                 {
  587.                         endgamestatus = 3;
  588.                         break;
  589.                 }
  590.                 else if (GetAsyncKeyState(VK_F1))//如果按了F1键就加速
  591.                 {

  592.                         speedup();
  593.                 }
  594.                 else if (GetAsyncKeyState(VK_F2))//如果按了F1键就加速
  595.                 {
  596.                         speeddown();
  597.                 }

  598.                 Sleep(sleeptime);//控制刷新率

  599.                 snakemove();
  600.         }
  601. }





  602. int main(void)//每一个函数解决一个单一问题,进行组合,在以后编程中也要这样
  603. {
  604.         system("mode con cols=100 lines=30");//让C语言发一个DOS命令,设置窗口宽度高度
  605.         printsnake();//输出绘制图案


  606.         welcometogame();//输出边框和文
  607.         //File_in();//把文件中最高分读取进来

  608.        
  609.         keyboardControl();


  610.         return 0;
  611. }
复制代码






代码有点长,但是只需要看 <创建并随机出现食物函数>   那里就好了。   
问题是这样的:在游戏初始化的时候,随机出现食物,代码如下,并且食物如果随机出现的坐标和蛇身坐标重合,就重新定位食物坐标。
实现方式我用了递归和常规2种方式,常规的没有问题,但是递归的在游戏运行的时候就会卡死,关键是我找了很久很久问题所在都觉得是正确的,但是程序就是要卡死。希望老师们能看看这个隐藏问题,当研究研究玩耍,但是请b21老师绕道,哈哈哈




while ( q != NULL)//蛇任何一个部分都不能和食物重叠
        {
                //num -= 1;
                if (q->x == food_1->x && q->y == food_1->y)//如果随机出现的食物坐标和蛇初始化坐标重叠,删除这个食物内容,重新创建
                {
                        free(food_1);//释放当前食物内容

                        /*
                        food_1 = (SNAKE*)malloc(sizeof(SNAKE));
                        while ((food_1->x % 2) != 0)
                        {
                                food_1->x = rand() % 52 + 2;
                                food_1->y = 24 + 1;
                                q = head;
                        }
                        */
                        ch = 'y';
                        createfood();//递归重新创建食物坐标,但是这里注意递归进去后,循环q到NULL运行完毕,函数返回来位置在if(ch=='y'),q坐标已经被移动到NULL了,指针无法记录之前的
                       


                }
                if (ch == 'y')
                {
                        food_1 = food;
                        return;
                }
                q = q->next;//蛇身上任何坐标不能和食物重合

                if (q == NULL)
                {
                        break;
                }
        }





最佳答案
2018-8-2 13:28:39
y坐标一直是24 + 1
一直是25
x坐标虽然是随机的,但是很难超过20
如果蛇占用了这些位置,那就一直递归

2.png
1.png
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-8-2 08:35:25 | 显示全部楼层
407行那个赋值坏了事,把ch又还原了。
另外我的vs2013编译通过不了你用的是vc6还是cb?

点评

你应该判断food_1是不是NULL  发表于 2018-8-2 08:39
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 13:28:39 | 显示全部楼层    本楼为最佳答案   
y坐标一直是24 + 1
一直是25
x坐标虽然是随机的,但是很难超过20
如果蛇占用了这些位置,那就一直递归

2.png
1.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 13:32:24 | 显示全部楼层
再说一个问题
1.png
2.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 14:49:59 | 显示全部楼层
无符号整形 发表于 2018-8-2 08:35
407行那个赋值坏了事,把ch又还原了。
另外我的vs2013编译通过不了你用的是vc6还是cb?

vs2017 嘻嘻

点评

……哎~  发表于 2018-8-2 14:50
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 14:52:36 | 显示全部楼层

谢谢老师,太牛了,您应该当版主
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 14:59:46 | 显示全部楼层

老师,您好,因为那个Y坐标,我也是故意不随机的,希望食物经常和身体重合,但是用DBUG调试,照理说一直递归,就别跳出来嘛,结果用DBUG调试就不会一直递归,而且感觉没问题,但是一运行程序才显现问题
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 15:03:14 | 显示全部楼层
人造人 发表于 2018-8-2 13:28
y坐标一直是24 + 1
一直是25
x坐标虽然是随机的,但是很难超过20

我觉得问题可能不是在这里,因为照理说,食物和身体重合,递归进去,X坐标还是会随机,一直随机到不在身体上为止,不会因为说食物和身体重合,就一直递归。。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 15:20:40 | 显示全部楼层
本帖最后由 人造人 于 2018-8-2 15:21 编辑
sswy 发表于 2018-8-2 15:03
我觉得问题可能不是在这里,因为照理说,食物和身体重合,递归进去,X坐标还是会随机,一直随机到不在身 ...


1.png
看这个程序
我想要10个随机数
调用10次GetRand
应该得到10个不同的值
但是,这10个值是相同的

原因是 srand
srand要在程序开始时调用一次,之后不需要再次调用了,如果这个程序要运行很长时间,例如服务器的守护进程,这个可以每隔一天调用一次srand,详情可以百度srand的用法

2.png

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 15:27:59 | 显示全部楼层
1.png

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>

  4. int GetRand(void)
  5. {
  6.         //srand((unsigned int)time(NULL));
  7.         return rand();
  8. }

  9. int main(void)
  10. {
  11.         srand(0);
  12.         for(int i = 0; i < 10; ++i)
  13.                 printf("%d\n", GetRand());

  14.         srand(1);
  15.         for(int i = 0; i < 10; ++i)
  16.                 printf("%d\n", GetRand());

  17.         srand(2);
  18.         for(int i = 0; i < 10; ++i)
  19.                 printf("%d\n", GetRand());
  20.         printf("***********************************\n");

  21.         srand(0);
  22.         for(int i = 0; i < 10; ++i)
  23.                 printf("%d\n", GetRand());

  24.         srand(1);
  25.         for(int i = 0; i < 10; ++i)
  26.                 printf("%d\n", GetRand());

  27.         srand(2);
  28.         for(int i = 0; i < 10; ++i)
  29.                 printf("%d\n", GetRand());
  30.         printf("***********************************\n");
  31.        
  32.         return 0;
  33. }
复制代码

  1. 38
  2. 7719
  3. 21238
  4. 2437
  5. 8855
  6. 11797
  7. 8365
  8. 32285
  9. 10450
  10. 30612
  11. 41
  12. 18467
  13. 6334
  14. 26500
  15. 19169
  16. 15724
  17. 11478
  18. 29358
  19. 26962
  20. 24464
  21. 45
  22. 29216
  23. 24198
  24. 17795
  25. 29484
  26. 19650
  27. 14590
  28. 26431
  29. 10705
  30. 18316
  31. ***********************************
  32. 38
  33. 7719
  34. 21238
  35. 2437
  36. 8855
  37. 11797
  38. 8365
  39. 32285
  40. 10450
  41. 30612
  42. 41
  43. 18467
  44. 6334
  45. 26500
  46. 19169
  47. 15724
  48. 11478
  49. 29358
  50. 26962
  51. 24464
  52. 45
  53. 29216
  54. 24198
  55. 17795
  56. 29484
  57. 19650
  58. 14590
  59. 26431
  60. 10705
  61. 18316
  62. ***********************************
  63. 请按任意键继续. . .
复制代码


种子值一样,得到的随机值就一样
这就是伪随机
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 15:36:17 | 显示全部楼层
1.png

Stack overflow
不断的递归,导致堆栈溢出了
因为蛇占用了位置,导致的不断递归
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 15:52:19 | 显示全部楼层
1.png
你能确定这里是25吗?
还是说你把52写成了25?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 16:15:25 | 显示全部楼层
人造人 发表于 2018-8-2 15:27
种子值一样,得到的随机值就一样
这就是伪随机

醍醐灌顶,请问老师如此隐蔽的问题,老师您是怎么发现的?之前遇上过这样的问题?
太感谢人造人老师了,谢谢谢谢谢谢,太谢谢了

我其实找这个问题至少找了8小时,真的百思不得其解,没想到是这个随机数问题,我开始以为是内存堆栈溢

出,或者是内存没释放干净,或者是内存污染,太没想到了,按正常思维,根本想不到是伪随机数,再次感谢人造人老师!

~~而且这个问题DBUG根本不会报错,递归还有去有回,程序还每一步执行的好好的,太烧人了,还好有老

师解答,要不这个问题不知道什么时候我才会明白
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 16:16:35 | 显示全部楼层
人造人 发表于 2018-8-2 15:27
种子值一样,得到的随机值就一样
这就是伪随机

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 16:34:08 | 显示全部楼层
sswy 发表于 2018-8-2 16:15
醍醐灌顶,请问老师如此隐蔽的问题,老师您是怎么发现的?之前遇上过这样的问题?
太感谢人造人老师了, ...

没错,之前遇到过这样的问题
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 16:38:55 | 显示全部楼层
1.png
2.png
3.png
4.png

这是在调试,需要一个合适的速度,太快了没办法调试,太慢了也不行

然后就是调试运行
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 16:40:53 | 显示全部楼层
本帖最后由 人造人 于 2018-8-2 16:43 编辑

每一次吃到食物就会停下来
然后多次观察生成的这个食物的坐标,就会看到问题
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 16:43:48 | 显示全部楼层
5.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-8-2 16:49:33 | 显示全部楼层
人造人 发表于 2018-8-2 15:52
你能确定这里是25吗?
还是说你把52写成了25?

这里不是25老师,是52或者53我记不清楚了,但是这里是因为我故意把x位置调小,让程序多出错,我好方便看为什么
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-2 16:51:22 | 显示全部楼层
sswy 发表于 2018-8-2 16:49
这里不是25老师,是52或者53我记不清楚了,但是这里是因为我故意把x位置调小,让程序多出错,我好方便看 ...


小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-11 05:41

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表