• C程序设计 贪吃蛇分析


    这是第一次做小游戏,

    下面对别人写的一个非常简单的基本功能的贪吃蛇进行分析

    VC下可以运行。。先看下源代码,

    基本上注释我都加在里面了,同时保留了作者的版权。

      1 //*******************************************************
      2 //**************版权所有***2011.9.20***咸鱼**************
      3 //*******************************************************
      4 //*友情提示:如想速度快点,请改小_sleep(500)函数中参数*****
      5 //*******************************************************
      6 //*****************如写的不好,请见谅*********************
      7 //*******************************************************
      8 
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <conio.h>
     12 #include <string.h>
     13 #include <time.h>
     14 /*
     15 con就是console,控制台
     16 io就是输入输出
     17 连起来就是用来声明控制台输入输出所需函数的头文件
     18 如果你要用到像
     19 getch()
     20 cprintf()
     21 cputs()
     22 kbhit()
     23 之类的函数,那就需要这个头文件了
     24 */
     25 
     26 
     27 const int H = 8;   //地图的高
     28 const int L = 16;  //地图的长
     29 
     30 char GameMap[H][L];   //游戏地图
     31 int  key;  //用来保存按键
     32 int  sum = 1, over = 0;  //蛇的长度, 游戏结束(自吃或碰墙)
     33 int  dx[4] = {0, 0, -1, 1};  //左、右、上、下的方向
     34 int  dy[4] = {-1, 1, 0, 0};  //左、右、上、下的方向
     35 
     36 struct Snake   //蛇的每个节点的数据类型
     37 {
     38     int x, y;  //左边位置
     39     int now;   //保存当前节点的方向, 0,1,2,3分别为左右上下
     40 }Snake[H*L];        //理论上蛇的长度,因为地图的高和长已确定,所以直接用H*L
     41 
     42 char Shead = 'Y';  //蛇头
     43 char Sbody = '#';  //蛇身
     44 char Sfood = '*';  //食物
     45 char Snode = '.';  //'.'在地图上标示为空
     46 
     47 void Initial();  //地图的初始化
     48 void Create_Food(); //在地图上随机产生食物
     49 void Show();   //刷新显示地图
     50 void Button();  //取出按键,并判断方向
     51 void Move();   //蛇的移动
     52 void Check_Border();  //检查蛇头是否越界
     53 void Check_Head(int x, int y);   //检查蛇头移动后的位置情况
     54 
     55 //主函数
     56 int main() 
     57 {
     58     Initial();
     59     Show();
     60     
     61     return 0;
     62 }
     63 
     64 
     65 
     66 void Initial()  //地图的初始化
     67 {
     68     int i, j;
     69     int hx, hy;
     70     
     71     system("title 贪吃蛇");  //控制台的标题
     72     memset(GameMap, '.', sizeof(GameMap));  //初始化地图全部为空'.'(即将GameMap数组中所有值改为".")
     73 
     74     system("cls"); //清屏
     75     
     76     srand(time(0));   //随机种子,需要头文件time.h
     77     hx = rand()%H;    //产生蛇头
     78     hy = rand()%L;
     79     GameMap[hx][hy] = Shead;  //将蛇头的值(也就是用来表示蛇头的字符,赋给GameMap,以便后面打印)
     80     Snake[0].x = hx;  Snake[0].y = hy; //蛇头的坐标确定
     81     Snake[0].now = -1;
     82     
     83     Create_Food();   //随机产生食物
     84     
     85     for(i = 0; i < H; i++)   //地图显示
     86     { 
     87         for(j = 0; j < L; j++)
     88             printf("%c", GameMap[i][j]);    //将蛇头 和 其他 字符 打印出来
     89         printf("
    ");
     90     }
     91     
     92     printf("
    小小C语言贪吃蛇
    ");
     93     printf("按任意方向键开始游戏
    ");
     94     
     95     getch();   //先接受一个按键,使蛇开始往该方向走
     96     Button();  //取出按键,并判断方向
     97 }
     98 
     99 void Create_Food()  //在地图上随机产生食物
    100 {
    101     int fx, fy;
    102     
    103     while(1)     
    104     {
    105         fx = rand()%H;
    106         fy = rand()%L;
    107         
    108         if(GameMap[fx][fy] == '.')  //食物不能出现在蛇所占有的位置
    109         { 
    110             GameMap[fx][fy] = Sfood;
    111             break;                  //如果成功产生了食物,就while跳出循环,否则继续执行,直到成功.
    112         }
    113     }
    114 }
    115 
    116 void Show()  //刷新显示地图
    117 {
    118     int i, j;
    119     
    120     while(1)
    121     {  
    122         _sleep(200); //延迟半秒(1000为1s),即每半秒刷新一次地图
    123         
    124         Button();   //先判断按键在移动
    125         Move();
    126         
    127         if(over)  //自吃或碰墙即游戏结束
    128         { 
    129             printf("
    **游戏结束**
    ");
    130             printf("     >_<
    ");
    131             getchar();
    132             break;
    133         }
    134         
    135         system("cls");   //清空地图再显示刷新后的地图
    136         for(i = 0; i < H; i++) 
    137         { 
    138             for(j = 0; j < L; j++)
    139                 printf("%c", GameMap[i][j]);
    140             printf("
    ");
    141         }
    142         
    143         printf("
    小小C语言贪吃蛇
    ");
    144         printf("按任意方向键开始游戏
    ");
    145     }
    146     
    147 }
    148 
    149 void Button()  //取出按键,并判断方向
    150 {
    151     if(kbhit() != 0) //需要头文件,检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
    152     { 
    153         while(kbhit() != 0)  //可能存在多个按键,要全部取完,以最后一个为主
    154             key = getch(); //将按键从控制台中取出并保存到key中
    155         
    156         switch(key)
    157         {   //
    158         case 75:  Snake[0].now = 0;
    159             break;
    160             //
    161         case 77:  Snake[0].now = 1;     
    162             break;
    163             //
    164         case 72:  Snake[0].now = 2;
    165             break;
    166             //
    167         case 80:  Snake[0].now = 3;
    168             break;
    169         }
    170     }
    171 }
    172 
    173 void Move()   //蛇的移动
    174 {
    175     int i, x, y;
    176     int t = sum;  //保存当前蛇的长度
    177     
    178     //记录当前蛇头的位置,并设置为空,蛇头先移动
    179     x = Snake[0].x;  y = Snake[0].y;  GameMap[x][y] = '.';
    180     Snake[0].x = Snake[0].x + dx[ Snake[0].now ];
    181     Snake[0].y = Snake[0].y + dy[ Snake[0].now ];
    182     
    183     Check_Border();   //蛇头是否越界
    184     Check_Head(x, y);  //蛇头移动后的位置情况,参数为: 蛇头的开始位置
    185 
    186     if(sum == t)  //未吃到食物即蛇身移动哦
    187         for(i = 1; i < sum; i++)  //要从蛇尾节点向前移动哦,前一个节点作为参照
    188         {
    189             if(i == 1)   //尾节点设置为空再移动
    190                 GameMap[ Snake[i].x ][ Snake[i].y ] = '.';
    191             
    192             if(i == sum-1)  //为蛇头后面的蛇身节点,特殊处理
    193             {
    194                 Snake[i].x = x;
    195                 Snake[i].y = y;
    196                 Snake[i].now = Snake[0].now;
    197             }
    198             else   //其他蛇身即走到前一个蛇身位置
    199             {
    200                 Snake[i].x = Snake[i+1].x;
    201                 Snake[i].y = Snake[i+1].y;
    202                 Snake[i].now = Snake[i+1].now;
    203             }
    204             
    205             GameMap[ Snake[i].x ][ Snake[i].y ] = '#'; //移动后要置为'#'蛇身 
    206         }
    207         
    208 }
    209 
    210 void Check_Border()  //检查蛇头是否越界
    211 {
    212     if(Snake[0].x < 0 || Snake[0].x >= H
    213         || Snake[0].y < 0 || Snake[0].y >= L)
    214         over = 1;
    215 }
    216 
    217 void Check_Head(int x, int y)  //检查蛇头移动后的位置情况
    218 {
    219     
    220     if(GameMap[ Snake[0].x ][ Snake[0].y ] == '.')  //为空
    221         GameMap[ Snake[0].x ][ Snake[0].y ] = 'Y';
    222     else
    223         if(GameMap[ Snake[0].x ][ Snake[0].y ] == '*')  //为食物
    224         {
    225             GameMap[ Snake[0].x ][ Snake[0].y ] = 'Y';  
    226             
    227             Snake[sum].x = x;   //新增加的蛇身为蛇头后面的那个
    228             Snake[sum].y = y;
    229             Snake[sum].now = Snake[0].now;
    230             
    231             GameMap[ Snake[sum].x ][ Snake[sum].y ] = '#'; 
    232             sum++;
    233             
    234             Create_Food();  //食物吃完了马上再产生一个食物
    235         }
    236         else
    237             over = 1;
    238 }

    接下来主要分析下 贪吃蛇的算法部分,

    首先,主函数

    //主函数
    int main()
    {
      Initial();
      Show();

      return 0;
    }

    仅仅只有两个函数,其中Initial()用来初始化界面,并在函数最后读入一个键盘操作,就结束了;

    完全靠Show函数来实现贪吃蛇的功能,

    show函数中,用一个while(1)来实现无限循环,

    其中,最核心的就是Move();

    而函数Move()中有一个关键函数

    Check_Head(x, y); //蛇头移动后的位置情况,参数为: 蛇头的开始位置

    基本上搞清楚这两个函数,就能够掌握这个简单的贪吃蛇了。

    (必须将两个函数一起看,才能看懂)

    下面先分析void Check_Head(int x, int y)

     2 {
     3     
     4     if(GameMap[ Snake[0].x ][ Snake[0].y ] == '.')  //           这里要注意的是 第4行和第6行的一个大的if else.如果if语句为ture,含义为没有吃到食物。那么执行的只有第5行,
                                            下面的else将直接跳过了。

    //第四,五行表示如果蛇头将要移动到的下一个坐标处是空的(没有食物的),就将地图的该坐标处变为蛇头。后面的后续步骤,就要结合move()函数来看了。
    5 GameMap[ Snake[0].x ][ Snake[0].y ] = 'Y';               6 else 7 if(GameMap[ Snake[0].x ][ Snake[0].y ] == '*') //为食物            //这个大的else中,要注意的是sum,也就是蛇头后面那个部分,
                                                         这里先记住一定是紧跟着蛇头的那个部分才是sum,而1表示的是蛇尾。
                                                             也就是实际上Snake[sum-1].x 和Snake[sum-1].y 表示的为蛇头后面第一个蛇节的坐标
                                                             而Snake[1].x 和 Snake[1].y 表示的是蛇尾的坐标。
                                                 
    
    
     8         {
     9             GameMap[ Snake[0].x ][ Snake[0].y ] = 'Y';  
    10             
    11             Snake[sum].x = x;   //新增加的蛇身为蛇头后面的那个,其实也就是原来蛇头所在的坐标,记住,是原来蛇头所在的坐标。
    12             Snake[sum].y = y;
    13             Snake[sum].now = Snake[0].now;
    14             
    15             GameMap[ Snake[sum].x ][ Snake[sum].y ] = '#'; 
    16             sum++;
    17             
    18             Create_Food();  //食物吃完了马上再产生一个食物
    19         }
    20         else
    21             over = 1;
    22 }



    接下来就开始结合最重要的函数Move()函数来看。

     1 void Move()   //蛇的移动
     2 {
     3     int i, x, y;      
     4     int t = sum;  //保存当前蛇的长度   后面需要用到
     5     
     6     //记录当前蛇头的位置,并设置为空,蛇头先移动
     7     x = Snake[0].x;  y = Snake[0].y;  GameMap[x][y] = '.';    //x,y用来存放原来的蛇头的位置,也就是即将成为蛇身第一个节点的位置。
                                          //看这个程序的时候,请务必要搞清楚x,y 和 Snake[i].x,Snake[i].y 是两码事。不能搞混
                                          //
    8 Snake[0].x = Snake[0].x + dx[ Snake[0].now ];        //这里就和上面那个函数呼应了,因为这里把蛇头所用的结构体Snake[0]中的x,y,now,都进行了修改,
                                          //所以在调用Check_head函数的时候,蛇头的位置。或者是坐标,已经改变了,所以在上一个函数的分析中才会有
                                    第四,五行表示如果蛇头将要移动到的下一个坐标处是空的(没有食物的),就将地图的该坐标处变为蛇头。就要结合move()函数来看了
     9     Snake[0].y = Snake[0].y + dy[ Snake[0].now ];
    10     
    11     Check_Border();   //蛇头是否越界                //这个函数比较简单,可以跳过不看,知道是用来判断是否越界的就可以了。
    12     Check_Head(x, y);  //蛇头移动后的位置情况,参数为: 蛇头的开始位置
    13                                           //到这里的话,其实上面的函数都已经执行完毕。
                                                  执行完毕后,会发生一下几个结果,
                                                            1.Snake[0].x 和 Snake[0].y ,也就是蛇头表示的位置,实际上已经发生变化,
                                                                  变为蛇头下一步要去的那个位置。
                                                            2.x,y,保存的是原来蛇头的位置。
                                                            3.如果吃到了食物,就执行了Check_Head()函数中的 大if语句,前面已经提到,就是第四行那个
                                                             如果没吃到食物, 就执行了  大esle中的语句,那么,sum的值就会+1;
                                                            至此,再往下,就是程序的关键了。
                                                            4. 如果吃到了食物,执行力Check_Head()函数中的 大if语句的话,sum的值会等于t,
                                                              那么14行到31行的语句将不会被执行。(即吃到食物不执行14到31行)
                                                              如果没吃到食物,则会执行14到31行。
                                                            5.前面已经讲过,了,Snake[sum-1]代表的是蛇头后面紧跟着的那个蛇节,
                                                                      Snake[1] 代表的是蛇尾的位置,下面的第28到31行,
                                                                      我们就可以清楚的知道为什么是i和i+1的关系了。
    14 if(sum == t) //未吃到食物即蛇身移动哦 15 for(i = 1; i < sum; i++) //要从蛇尾节点向前移动哦,前一个节点作为参照 16 { 17 if(i == 1) //蛇神最后一个节点的情况单独考虑    //将蛇尾变为空,因为蛇头多一节,所以蛇尾必定少一节。 18 GameMap[ Snake[i].x ][ Snake[i].y ] = '.'; 19 20 if(i == sum-1) //为蛇头后面的蛇身节点,特殊处理(蛇身第一个节点的情况也单独考虑) 21 { 22 Snake[i].x = x; 23 Snake[i].y = y; 24 Snake[i].now = Snake[0].now; 25 } 26 else //其他蛇身即走到前一个蛇身位置 (中间部分往前移动的考虑) 27 { 28 Snake[i].x = Snake[i+1].x; 29 Snake[i].y = Snake[i+1].y; 30 Snake[i].now = Snake[i+1].now; 31 } 32 33 GameMap[ Snake[i].x ][ Snake[i].y ] = '#'; //移动后要置为'#'蛇身 , 34 } 35 36 }
  • 相关阅读:
    xplan.sql(本脚本获取执行计划显示执行顺序)
    闪回查询(SELECT AS OF)
    闪回事务查询
    闪回版本查询
    闪回数据库
    shell循环语句
    前后端交互json字符串
    element vuex 语音播报
    highcharts中数据列点击事件
    highcharts为X轴标签添加链接
  • 原文地址:https://www.cnblogs.com/Lee-geeker/p/3140214.html
Copyright © 2020-2023  润新知