签到

06月20日
尚未签到

共有回帖数 0

    霜晨守候

    等级:
    如果对代码想看的清晰一些,请下载附件PDF。
    2048是大家手机上喜闻乐见的小游戏,玩法简单,过程却是很虐心,一不小心就gameover暴露智商底线了,这篇帖子就来和大家介绍一下如何在控制台上实现一个2048。和上一篇一样,先简单说一说2048的玩法(百科来的),界面见图
    1:




    4 系统给予的数字方块不是2就是4,玩家要想办法在这小小的16格范围中凑出“2048”这个数字方块。
    以上是游戏玩法,那么接下来自然就是如何在控制台上实现这么一个小程序了。
    1 这也看成是一个地图,4*4的大小,其中存储着2,4,8,16…这些数字
    2 当做一移动的时候如何确定每一个格子新的元素是什么呢?这里要割裂的来看每一个格子,以向上移动为例:
    1 假如当前格子有元素,那么就去下面的格子找一找第一个不为0的格子和我这个格子的数字是否一样,如果一样就发生合并,不一样,就什么都不坐,有的同学可能想,不一样就应该移动上来呀,别急还有第二步。
    2 假如当前格子没有元素,那么就去下面找一个不为零的元素,然后把它移动上来。
    以上两步最为重要,具体实现看代码啦。
    后记:
    写C程序要有一种模块化的思维,将功能最好细分一下,然后分别用函数实现,比如画方块,画数字,等等等等。
    细心可以发现,上下左右函数的逻辑是一样的,但是却占用了这么多行,能不能缩减呢,如何去掉这些冗余的代码,大家可以仔细的思考思考哦。
    (我把后记写到前面了,写在代码后面,大家看不到怎么办。。。^_^)

    代码:
    #include stdlib.h
    #include conio.h
    #include windows.h
    #include time.h
    int map[4][4] = {0}; //2048地图
    int g_Score = 0; //这局游戏的分数
    typedef struct _MYRECT
    {
    short x;
    short y;
    short width;
    short height;
    }MYRECT,*PMYRECT; //一个表示方块的结构体,成员是左上角坐标和方块的长度与宽度

    //输出句柄,用于控制台的界面编程使用,
    //可以看成是与控制台输出的一个抽象物体,就像你要控制游戏,得有一个手柄吧,
    //控制台的一些行为,就可以控制它来实现,比如设置光标位置,设置输出颜色等等。
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);


    /************************************
    函数名 : PrintInstruction
    函数作用: 打印游戏说明
    返回值 : void
    说明 :
    ************************************/
    void PrintInstruction()
    {
    COORD pos = {36,3};
    DWORD dwSize= 0;
    //函数作用:设置光标位置
    SetConsoleCursorPosition(hOut,pos);
    printf("积分规则:积分增长为新生成的数字");
    FillConsoleOutputAttribute(hOut,FOREGROUND_RED,10,pos,&dwSize);
    pos.Y++;
    SetConsoleCursorPosition(hOut,pos);
    printf("游戏按键:w:上 s:下 a:左 d:右");
    //函数作用:设置一下控制台上以某个坐标为起始的文本的颜色
    FillConsoleOutputAttribute(hOut,FOREGROUND_RED,10,pos,&dwSize);
    }

    /************************************
    函数名 : PrintScore
    函数作用: 打印一下目前的分数
    返回值 : void
    说明 :
    ************************************/
    void PrintScore()
    {
    COORD pos = {12,2};
    DWORD dwSize= 0;
    //函数作用:设置光标位置
    SetConsoleCursorPosition(hOut,pos);
    printf("分数:%d",g_Score);
    //函数作用:设置一下控制台上以某个坐标为起始的文本的颜色
    FillConsoleOutputAttribute(hOut,FOREGROUND_RED,10,pos,&dwSize);
    }



    /************************************
    函数名 : DrawRecet
    函数作用: 画一个方块,并且能设置方块的颜色,并且能设置方块内显示的颜色
    返回值 : void
    参数 : char * szBuf 要在方块内显示的内容
    参数 : PMYRECT rect 方块的左上角坐标与长度和宽度
    参数 : short foreColour 方块的前景色,也就是文字颜色
    参数 : short bacColour 方块的背景色,也就是。。。背景颜色
    说明 :
    ************************************/
    void DrawRecet(char* szBuf,PMYRECT rect,short foreColour,short bacColour)
    {
    COORD pos = {0,0};
    DWORD dwCount = strlen(szBuf); //字符个数
    DWORD dwSize = 0;
    //获取到字符在方块的中心位置,并打印字符
    pos.X = rect-x+rect-width/2-dwCount/2;
    pos.Y = rect-y+rect-height/2;
    SetConsoleCursorPosition(hOut,pos);
    printf(szBuf);
    //开始为方块填充颜色,一行一行的添加
    pos.X = rect-x;
    pos.Y = rect-y;
    for (int i = rect-y;irect-y+rect-height;i++)
    {
    pos.Y = i;
    FillConsoleOutputAttribute(hOut,foreColour|bacColour,rect-width,pos,&dwSize);
    }
    }

    /************************************
    函数名 : CreateNumber
    函数作用: 在地图中随机生成一个2或者4
    返回值 : void
    说明 :
    ************************************/
    void CreateNumber()
    {
    //初始化一个随机数种子
    srand(time(NULL));
    //随机出来一个坐标
    int x = rand()%4;
    int y = rand()%4;
    //随机出来一个2或者4
    int nNumber = rand()%2?2:4;
    do
    {
    //看看地图的这个位置是不是没有数字,如果没有数字,就生成成功,
    //否则还需要再生成
    if (map[x][y]==0)
    {
    map[x][y] = nNumber;
    break;
    }
    x = rand()%4;
    y = rand()%4;
    } while (true);
    }

    /************************************
    函数名 : DrawNumber
    函数作用: 在指定的方格内画出游戏中的数字
    返回值 : void
    参数 : int nNumber 2,4,8,16...等等的数字
    参数 : PMYRECT rect 方块的坐标和宽度
    说明 : 1 这里调用了DrawRecet函数
    2 由于控制台的限制,高度始终是3个字符,宽度始终是6个字符,才能使界面好看一些。
    ************************************/
    void DrawNumber(int nNumber,PMYRECT rect)
    {
    switch (nNumber)
    {
    case 0:
    DrawRecet(" ",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    case 2:
    DrawRecet(" 02 ",rect,FOREGROUND_BLUE,BACKGROUND_RED);
    break;
    case 4:
    DrawRecet(" 04 ",rect,FOREGROUND_BLUE,BACKGROUND_GREEN);
    break;
    case 8:
    DrawRecet(" 08 ",rect,FOREGROUND_RED,BACKGROUND_BLUE);
    break;
    case 16:
    DrawRecet(" 16 ",rect,FOREGROUND_RED,BACKGROUND_BLUE|BACKGROUND_GREEN);
    break;
    case 32:
    DrawRecet(" 32 ",rect,FOREGROUND_GREEN,BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    case 64:
    DrawRecet(" 64 ",rect,FOREGROUND_BLUE|FOREGROUND_GREEN,BACKGROUND_BLUE|BACKGROUND_RED);
    break;
    case 128:
    DrawRecet("0128",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_INTENSITY);
    break;
    case 256:
    DrawRecet("0256",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_INTENSITY|BACKGROUND_RED);
    break;
    case 512:
    DrawRecet("0512",rect,FOREGROUND_BLUE,BACKGROUND_INTENSITY|BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    case 1024:
    DrawRecet("1024",rect,FOREGROUND_BLUE,BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    case 2048:
    DrawRecet("2048",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_RED);
    break;
    case 4096:
    DrawRecet("4096",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    case 8192:
    DrawRecet("8912",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    default:
    DrawRecet("NANA",rect,FOREGROUND_BLUE,BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED);
    break;
    }
    }

    /************************************
    函数名 : InitGame
    函数作用: 初始化游戏,创建出一个数字后,将地图画出来
    返回值 : void
    说明 :
    ************************************/
    void InitGame()
    {
    MYRECT rect = {0,0,6,3};
    PrintInstruction();
    CreateNumber();
    //rect宽度始终是6,高度始终是3,画下一个格子的时候,横向就一次越过6个字符
    //纵向就一次越过3个字符
    for (int i =0;i4;i++)
    for (int j =0;j4;j++)
    {
    rect.x = (i+2)*6;
    rect.y = (j+1)*3;
    DrawNumber(map[j],&rect);
    }
    }

    /************************************
    函数名 : DrawMap
    函数作用: 将地图中的所有数字画一遍
    返回值 : void
    说明 :
    ************************************/
    void DrawMap()
    {
    MYRECT rect = {0,0,6,3};
    for (int i =0;i4;i++)
    for (int j =0;j4;j++)
    {
    rect.x = (i+2)*6;
    rect.y = (j+1)*3;
    DrawNumber(map[j],&rect);
    }
    }


    /************************************
    函数名 : Up
    函数作用: 按了向上键后,做的操作,包括移动地图内的元素,合并相同的数字
    返回值 : bool 地图是否发生了一栋
    说明 : 当按下按键的时候,地图也可能并未发生变化,这里需要注意
    ************************************/
    bool Up()
    {
    bool bIsMove = false;
    //1 在地图中一列一列的从上到下搜寻,来确定当前元素是什么
    for (int i = 0;i4;i++)
    {
    for (int j =0;j4;j++)
    {
    //2 找到不为0的元素了,那么就往下找看能不能找到一样的元素
    if (map[j]!=0)
    {
    //注意m的起始值是j+1,也就是继续往下找
    for (int m = j+1;m4;m++)
    {
    //找了到一样的元素,那么就该合并。
    if (map[j]==map[m])
    {
    //自己自增一倍
    map[j] *=2;
    //找到的那个元素被合并了,于是赋值为0
    map[m] =0;
    //加分
    g_Score+=map[j];
    //游戏发生了一栋
    bIsMove = true;
    break;
    }
    //假如往下找到了不一样的元素,那么对于自己来说,
    //自己是不发生任何变化的
    else if (map[m]!=0)
    {
    break;
    }
    }
    }
    //3 找到了为0的元素,那么就往下搜寻,看看是否有元素能够移动上来
    else
    {
    for (int m = j;m4;m++)
    {
    //找到了一个不为0的元素,于是把它移动上来。
    if (map[m]!=0)
    {
    //移动
    map[j] = map[m];
    //将找到的位置赋值为0
    map[m] = 0;
    //j还要自减一下,为了能够检测是否下面还有一样的元素。
    j--;
    //地图上发生了移动
    bIsMove = true;
    break;
    }
    }
    }
    }
    }
    return bIsMove;
    }
    /************************************
    函数名 : Down
    函数作用: 按了向下键后,做的操作,包括移动地图内的元素,合并相同的数字
    返回值 : bool
    说明 : 逻辑同上
    ************************************/
    bool Down()
    {
    bool bIsMove = false;
    for (int i = 0;i4;i++)
    {
    for (int j =3;j=0;j--)
    {
    if (map[j]!=0)
    {
    for (int m = j-1;m=0;m--)
    {
    if (map[j]==map[m])
    {
    map[j] *=2;
    map[m] =0;
    g_Score+=map[j];
    bIsMove = true;
    break;
    }
    else if (map[m]!=0)
    {
    break;
    }
    }
    }
    else
    {
    for (int m = j-1;m=0;m--)
    {
    if (map[m]!=0)
    {
    map[j] = map[m];
    map[m] = 0;
    j++;
    bIsMove = true;
    break;
    }
    }
    }
    }
    }
    return bIsMove;
    }

    /************************************
    函数名 : Down
    函数作用: 按了向下键后,做的操作,包括移动地图内的元素,合并相同的数字
    返回值 : bool
    说明 : 逻辑同上
    ************************************/
    bool Right()
    {
    bool bIsMove = false;
    for (int i = 0;i4;i++)
    {
    for (int j =3;j=0;j--)
    {
    if (map[j]!=0)
    {
    for (int m = j-1;m=0;m--)
    {
    if (map[j]==map[m])
    {
    map[j] *=2;
    map[m] =0;
    g_Score+=map[j];
    bIsMove = true;
    break;
    }
    else if (map[m]!=0)
    {
    break;
    }
    }
    }
    else
    {
    for (int m = j-1;m=0;m--)
    {
    if (map[m]!=0)
    {
    map[j] = map[m];
    map[m] = 0;
    j++;
    bIsMove = true;
    break;
    }
    }
    }
    }
    }
    return bIsMove;
    }

    /************************************
    函数名 : Down
    函数作用: 按了向下键后,做的操作,包括移动地图内的元素,合并相同的数字
    返回值 : bool
    说明 : 逻辑同上
    ************************************/
    bool Left()
    {
    bool bIsMove = false;
    for (int i = 0;i4;i++)
    {
    for (int j =0;j4;j++)
    {
    if (map[j]!=0)
    {
    for (int m = j+1;m4;m++)
    {
    if (map[j]==map[m])
    {
    map[j] *=2;
    map[m] =0;
    g_Score+=map[j];
    bIsMove = true;
    break;
    }
    else if (map[m]!=0)
    {
    break;
    }
    }
    }
    else
    {
    for (int m = j+1;m4;m++)
    {
    if (map[m]!=0)
    {
    map[j] = map[m];
    map[m] = 0;
    bIsMove = true;
    j--;
    break;
    }
    }
    }
    }
    }
    return bIsMove;
    }

    /************************************
    函数名 : StartGame
    函数作用: 开始游戏,获取玩家的按键,a,s,d,w
    返回值 : void
    说明 :
    ************************************/
    void StartGame()
    {
    InitGame();
    PrintScore();
    char cOper =0 ;
    bool bIsMove = false;
    while (true)
    {
    cOper = _getch();
    switch (cOper)
    {
    case 'w':
    bIsMove = Up();
    break;
    case 's':
    bIsMove = Down();
    break;
    case 'a':
    bIsMove = Left();
    break;
    case 'd':
    bIsMove = Right();
    default:
    break;
    }
    //如果地图发生了移动,这个时候就应该:
    //创建一个新的数字出来,重绘地图,更新分数
    if (bIsMove)
    {
    CreateNumber();
    DrawMap();
    PrintScore();
    }

    }
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    StartGame();
    return 0;
    }

    楼主 2015-07-27 21:46 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知