从零开始学编程

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 79|回复: 3

C语言之迷官代码+注释

[复制链接]
  • ta_mind
    擦汗
    2016-3-2 13:17
  • classn_01: 5 classn_02

    [LV.2]偶尔看看I

    602

    主题

    612

    帖子

    681

    积分

    高级会员

    Rank: 4

    积分
    681
    发表于 2016-10-19 00:37:57 | 显示全部楼层 |阅读模式
    #include <stdio.h>
    #include <conio.h>
    #include <windows.h>
    #include <time.h>
    #define Height 31 //迷宫的高度,必须为奇数
    #define Width 25 //迷宫的宽度,必须为奇数
    #define Wall 1
    #define Road 0
    #define Start 2
    #define End 3
    #define Esc 5
    #define Up 1
    #define Down 2
    #define Left 3
    #define Right 4
    int map[Height+2][Width+2];
    void gotoxy(int x,int y) //移动坐标
    {
    COORD coord;
    coord.X=x;
    coord.Y=y;
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), coord );
    }
    void hidden()//隐藏光标
    {
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cci;
    GetConsoleCursorInfo(hOut,&cci);
    cci.bVisible=0;//赋1为显示,赋0为隐藏
    SetConsoleCursorInfo(hOut,&cci);
    }
    void create(int x,int y) //随机生成迷宫
    {
    int c[4][2]={0,1,1,0,0,-1,-1,0}; //四个方向
    int i,j,t;
    //将方向打乱
    for(i=0;i<4;i++)
    {
    j=rand()%4;
    t=c[0];c[0]=c[j][0];c[j][0]=t;
    t=c[1];c[1]=c[j][1];c[j][1]=t;
    }
    map[x][y]=Road;
    for(i=0;i<4;i++)
    if(map[x+2*c[0]][y+2*c[1]]==Wall)
    {
    map[x+c[0]][y+c[1]]=Road;
    create(x+2*c[0],y+2*c[1]);
    }
    }
    int get_key() //接收按键
    {
    char c;
    while(c=getch())
    {
    if(c==27) return Esc; //Esc
    if(c!=-32)continue;
    c=getch();
    if(c==72) return Up; //上
    if(c==80) return Down; //下
    if(c==75) return Left; //左
    if(c==77) return Right; //右
    }
    return 0;
    }
    void paint(int x,int y) //画迷宫
    {
    gotoxy(2*y-2,x-1);
    switch(map[x][y])
    {
    case Start:
    printf("入");break; //画入口
    case End:
    printf("出");break; //画出口
    case Wall:
    printf("※");break; //画墙
    case Road:
    printf(" ");break; //画路
    }
    }
    void game()
    {
    int x=2,y=1; //玩家当前位置,刚开始在入口处
    int c; //用来接收按键
    while(1)
    {
    gotoxy(2*y-2,x-1);
    printf("☆"); //画出玩家当前位置
    if(map[x][y]==End) //判断是否到达出口
    {
    gotoxy(30,24);
    printf("到达终点,按任意键结束");
    getch();
    break;
    }
    c=get_key();
    if(c==Esc)
    {
    gotoxy(0,24);
    break;
    }
    switch(c)
    {
    case Up: //向上走
    if(map[x-1][y]!=Wall)
    {
    paint(x,y);
    x--;
    }
    break;
    case Down: //向下走
    if(map[x+1][y]!=Wall)
    {
    paint(x,y);
    x++;
    }
    break;
    case Left: //向左走
    if(map[x][y-1]!=Wall)
    {
    paint(x,y);
    y--;
    }
    break;
    case Right: //向右走
    if(map[x][y+1]!=Wall)
    {
    paint(x,y);
    y++;
    }
    break;
    }
    }
    }
    int main()
    {
    int i,j;
    srand((unsigned)time(NULL)); //初始化随即种子
    hidden(); //隐藏光标
    for(i=0;i<=Height+1;i++)
    for(j=0;j<=Width+1;j++)
    if(i==0||i==Height+1||j==0||j==Width+1) //初始化迷宫
    map[j]=Road;
    else map[j]=Wall;

    create(2*(rand()%(Height/2)+1),2*(rand()%(Width/2)+1)); //从随机一个点开始生成迷宫,该点行列都为偶数
    for(i=0;i<=Height+1;i++) //边界处理
    {
    map[0]=Wall;
    map[Width+1]=Wall;
    }

    for(j=0;j<=Width+1;j++) //边界处理
    {
    map[0][j]=Wall;
    map[Height+1][j]=Wall;
    }
    map[2][1]=Start; //给定入口
    map[Height-1][Width]=End; //给定出口
    for(i=1;i<=Height;i++)
    for(j=1;j<=Width;j++) //画出迷宫
    paint(i,j);
    game(); //开始游戏
    getch();
    return 0;
    }
    首先,先挂上代码。然后说部分废话,读代码好处非常之多,提高技术,增加理解力,以及获得不同思路等。读代码甚至对比写代码来说,学习效率有过之而无不及.文章针对初级又在初级之上,没有一定的基础看不懂,有一定的基础就可以跟着这篇帖子,做出你自己的C语言随机迷宫,这里的做出并不是抄代码,而是变为你真正的知识,在没有参考的时候,也可以流畅的写出你的代码.

    然后我们开始分析代码..
    先看头文件。
    #include <stdio.h> //包涵标准输入输出函数
    #include <conio.h> //控制台输入输出库,非标准库哦
    #include <windows.h> //WINDOWS.H是主要的头文件,它包含了其他Windows头文件,这些头文件的某些也包含了其他头文件。 具体包涵了什么。。太多了 自行百度
    #include <time.h> //包涵时间和日期处理函数


    #define Height 21 //迷宫的高度,必须为奇数
    #define Width 21 //迷宫的宽度,必须为奇数

    #define Wall 1 //即字面意思 墙
    #define Road 0 //即字面意思 路
    #define Start 2 //入口
    #define End 3 //终点
    #define Esc 5 //退出
    #define Up 1 //上,下,左,右
    #define Down 2
    #define Left 3
    #define Right 4

    这里为什么要用宏,什么情况下使用宏,可能要问为什么不直接用 12345来代替。简单的来说就是提供一个方便,并增加一定的效率。还有重要的一点就是增加代码的可读性。尽量避免用01234这样无意义的数字而使用宏定义能良好的提高开发效率,在小程序中可能不算什么,但是在大程序可就不一样了,然而宏定义也并非只能定义简单的1234 。

    接下来我们看一共有几个函数
    void gotoxy(int x,int y) //既字面含义 移动坐标
    void create(int x,int y) //字面含义 创建迷宫
    void hidden() //隐藏光标(注并非鼠标)
    int get_key() //得到按键
    void paint(int x,int y) //绘制迷宫
    void game() //游戏相关操作
    先不要管main函数里的代码,我们先逐个分析一下函数

    先来看看 gotoxy() 接受两个参数, 代码如下
    void gotoxy(int x,int y) //移动坐标
    {
    COORD coord;
    coord.X=x;
    coord.Y=y;
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), coord );
    }
    看到这里可能就有很多人蒙了 COORD是个什么东西? COORD实际上是一个结构体 包括结构体成员SHORT X,SHORT Y;
    typedef struct COORD { SHORT X; SHORT Y;} COORD, *PCOORD;暂时先不管他是做什么的,先看看SetConsoleCursorPosition这个API的声明他接受两个参数,作用是定位光标位置,需要配合COORD 使用

    BOOL WINAPI SetConsoleCursorPosition( __in HANDLE hConsoleOutput, //句柄 就不介绍了 __in COORD dwCursorPosition // COORD原来是SetConsoleCursorPosition的形参之一);
    GetStdHandle 声明如下:
    HANDLE WINAPI GetStdHandle( //获得输入、输出/错误的屏幕缓冲区的句柄。 __in DWORD nStdHandle );
    而其参数nStdHandle的值可以为下面几种类型的一种: STD_INPUT_HANDLE 标准输入的句柄   STD_OUTPUT_HANDLE 标准输出的句柄   STD_ERROR_HANDLE 标准错误的句柄
    这么说出来其实也并非很容易理解,我们用一个小程序来举例

    #include <stdio.h>
    #include <windows.h>
    int main(void)
    {
    COORD cod;
    cod.X=5;
    cod.Y=4;
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), cod );
    }

    编译一下程序,就能理解这两个函数是做什么的了
    gotoxy() 就讲到这里
    下面讲第二个函数
    声明:void create(int x,int y)
    void create(int x,int y) //随机生成迷宫
    {
    int c[4][2]={0,1,1,0,0,-1,-1,0}; //四个方向 // 这里的四个方向 乃是0,1 1,0 0,-1 -1,0 代表着上下左右
    int i,j,t;
    //将方向打乱
    for(i=0;i<4;i++)
    {
    j=rand()%4;
    t=c[0];c[0]=c[j][0];c[j][0]=t;
    t=c[1];c[1]=c[j][1];c[j][1]=t;
    }
    map[x][y]=Road;
    for(i=0;i<4;i++)
    if(map[x+2*c[0]][y+2*c[1]]==Wall) //
    {
    map[x+c[0]][y+c[1]]=Road;
    create(x+2*c[0],y+2*c[1]);
    }
    }
    这个函数中的算法根据源代码作者所说是从网上找的 .然后搜了搜.我去 略长,略长,转到论坛压力略大略大 还分别有不会离散数学的和使用深度优先遍历..两种版本,最主要的是带图了,推荐亲自前去观看吧网页链接 ,带上图理解比较容易..所以想要理解算法部分,还是看连接文章吧

    然后我们接着说一下int get_key()
    函数如下:
    int get_key() //接收按键
    {
    char c;
    while(c=getch())
    {
    if(c==27) return Esc; //Esc
    if(c!=-32)continue;
    c=getch();
    if(c==72) return Up; //上
    if(c==80) return Down; //下
    if(c==75) return Left; //左
    if(c==77) return Right; //右
    }
    return 0;
    }

    其实理解这个函数很容易 getch() 为无回显获取一个字符,什么叫无回显获取一个字符? 写一个小程序来测试一下
    #include <conio.h>
    #include <stdio.h>
    int main()
    {
    char a;
    a=getch();
    putch(a);
    }
    可以发现只会进行输出,而输入时不会显示所输入字符。 可能会有人疑惑ESC的ASCII确实是27 可是其他的是什么呀?ASCII中是没有上下左右的值的,这里的值是键盘控制码,不是ASCII!注意了!关于获取某个按键的值可以用以下程序
    #include <conio.h>
    #include <stdio.h>
    int main()
    {
    int key;
    key = getch();
    while( key != 27 ) //键入值不为 ESC
    {
    printf("%d\n", key);
    key = getch();
    }
    }

    继续看看 paint 函数
    函数如下
    void paint(int x,int y) //画迷宫
    {
    gotoxy(2*y-2,x-1);
    switch(map[x][y])
    {
    case Start:
    printf("入");break; //画入口
    case End:
    printf("出");break; //画出口
    case Wall:
    printf("※");break; //画墙
    case Road:
    printf(" ");break; //画路
    }
    }
    这个函数实际上比较容易理解,他首先将光标指向入口,然后在main函数中遍历整个map数组,画出地图 。也可以不用 入 出 ※ 空格 这些来做地图 比如改一下

    switch(map[x][y])
    {
    case Start:
    printf("1");break; //画入口
    case End:
    printf("2");break; //画出口
    case Wall:
    printf("3");break; //画墙
    case Road:
    printf("4");break; //画路
    }
    然后就应该能理解了这部分是怎么做到的

    接下来我们看最后一个函数void game() //游戏相关操作

    void game()
    {
    int x=2,y=1; //玩家当前位置,刚开始在入口处
    int c; //用来接收按键
    while(1)
    {
    gotoxy(2*y-2,x-1); //这里的含义是到达入口处
    printf("☆"); //画出玩家当前位置
    if(map[x][y]==End) //判断是否到达出口
    {
    gotoxy(30,24);
    printf("到达终点,按任意键结束");
    getch();
    break;
    }
    c=get_key();
    if(c==Esc)
    {
    gotoxy(0,24);
    break;
    }
    switch(c)
    {
    case Up: //向上走
    if(map[x-1][y]!=Wall)
    {
    paint(x,y);
    x--;
    }
    break;
    case Down: //向下走
    if(map[x+1][y]!=Wall)
    {
    paint(x,y);
    x++;
    }
    break;
    case Left: //向左走
    if(map[x][y-1]!=Wall)
    {
    paint(x,y);
    y--;
    }
    break;
    case Right: //向右走
    if(map[x][y+1]!=Wall)
    {
    paint(x,y);
    y++;
    }
    break;
    }
    }
    }
    事实上这个函数也不是很难理解 所有函数除了算法部分都不难理解,就看用不用心去读代码了
    就像注释那样, gotoxy(2*y-2,x-1); 到达入口处 用☆当玩家,然后先进行判断是否到达终点,如果达到终点或按ESC则结束游戏,负责就开始接受按键,进行移动,说是移动实际上就是不断变化 map[x][y] x和y的值 并进行判断是否到达终点,按下ESC如果没有则根据 x++ x-- y++ y-- 来进行上下左右后改变所在二维数组中的位置 打个比方

    a[2][2]
    分别有a[0][0]a[0][1] 11
    a[1][0]a[1][1] 21 如果处在2的位置 那么按下上 也就是说数组元素a[1][0]变成了a[0][0] 如果用 这里用a[x][y]来进行表示,那么就是等于x--;其他同理

    最后我们来看一下main函数

    int main()
    {
    int i,j;

    srand((unsigned)time(NULL)); //初始化随即种子
    hidden(); //隐藏光标

    for(i=0;i<=Height+1;i++)
    for(j=0;j<=Width+1;j++)
    if(i==0||i==Height+1||j==0||j==Width+1) //初始化迷宫
    map[j]=Road;
    else map[j]=Wall;

    create(2*(rand()%(Height/2)+1),2*(rand()%(Width/2)+1)); //从随机一个点开始生成迷宫,该点行列都为偶数

    for(i=0;i<=Height+1;i++) //边界处理
    {
    map[0]=Wall;
    map[Width+1]=Wall;
    }

    for(j=0;j<=Width+1;j++) //边界处理
    {
    map[0][j]=Wall;
    map[Height+1][j]=Wall;
    }

    map[2][1]=Start; //给定入口
    map[Height-1][Width]=End; //给定出口

    for(i=1;i<=Height;i++)
    for(j=1;j<=Width;j++) //画出迷宫
    paint(i,j);
    game(); //开始游戏
    getch();
    return 0;
    }

    先是srand来初始化随机种子 为create打下基础
    然后给map赋值 输出后成四面全是墙,中间全是路的 也就是000
    010
    000 //0是墙,1是路 这样
    然后用create生成正式的迷宫,根据rand来随机生成路线
    之后重新处理边界,以及定义入口点和出口点 ,可以试着删掉
    for(i=0;i<=Height+1;i++) //边界处理
    {
    map[0]=Wall;
    map[Width+1]=Wall;
    }

    for(j=0;j<=Width+1;j++) //边界处理
    {
    map[0][j]=Wall;
    map[Height+1][j]=Wall;
    }
    这段 如果在入口处按左,你会发现BUG了!!
    删掉这两段
    map[2][1]=Start; //给定入口
    map[Height-1][Width]=End; //给定出口
    导致你一旦从入字走出,就再也走出不迷宫
    最后使用print函数 根据数组元素的值 画出迷宫.
    用game进行游戏循环并判断游戏是否结束




    上一篇:C语言抢红包的原理
    下一篇:文件进度读取并打印

    classn_11

    0

    主题

    18

    帖子

    20

    积分

    新手上路

    Rank: 1

    积分
    20
    发表于 2016-10-19 22:03:20 | 显示全部楼层
    好东东,谢谢大大分享呀!

    classn_11

    0

    主题

    18

    帖子

    20

    积分

    新手上路

    Rank: 1

    积分
    20
    发表于 2016-10-19 22:03:36 | 显示全部楼层
    好东东,谢谢大大分享呀!

    classn_11

    0

    主题

    18

    帖子

    20

    积分

    新手上路

    Rank: 1

    积分
    20
    发表于 2016-10-19 22:03:53 | 显示全部楼层
    好东东,谢谢大大分享呀!
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Archiver|小黑屋|sitemap|从零开始学编程 ( 豫ICP备15032706号-2 )

    GMT+8, 2017-1-23 22:53 , Processed in 1.156287 second(s), 35 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

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