• C语言小游戏: 2048.c


    概要:2048.c是一个C语言编写的2048游戏,本文将详细分析它的源码和实现。C语言是一种经典实用的编程语言,本身也不复杂,但是学会C语言和能够编写实用的程序还是有一道鸿沟的。本文试图通过一个例子展示如何用C语言实现一个简单但有用的程序。

    一、程序简介

    本文分析的代码是mevdschee在GitHub上的项目2048.c,游戏的规则和安装说明都可以到主页查看,本文不再赘述。顺便一提,这个程序虽然是纯C编写的,但是它适用于Linux终端,因此如果你想要看一下运行效果可能需要一个Linux.

    2048.c源代码只有一个文件,也就是2048.c。它支持图形和色彩,右上角显示分数,下面是操作说明。界面整体看起来挺简洁美观,我们一会看一下它是怎么做到的。

    二、代码结构

    我们先看一下程序所包含的函数,大体了解它的结构和功能。

    程序入口和测试:

    • main (argc,argv[])
    • test ()

    绘制界面相关:

    • getColor (value,color,length)
    • drawBoard (board[][])
    • setBufferedInput (enable):设置终端的行为
    • signal_callback_handler (signum)

    游戏逻辑:

    • findTarget (array[],x,stop)
    • slideArray (array[])
    • rotateBoard (board[][])
    • moveUp (board[][])
    • moveLeft (board[][])
    • moveDown (board[][])
    • moveRight (board[][])
    • findPairDown (board[][])
    • countEmpty (board[][])
    • gameEnded (board[][])
    • addRandom (board[][])
    • initBoard (board[][])

    从函数的参数中可以看出,游戏使用的主要的数据结构是一个二维数组,在主函数中定义: uint8_t board[SIZE][SIZE] 。SIZE的值默认是4,这是2048游戏面板的一般大小,下文直接称为4。数组中的元素保存的是指数,例如如果显示的数是1024,那么存储的应该是10。在初始化过程中,该数组被填满0.

    主函数中完成一些初始化和设置工作,然后进入主循环。在循环中接受用户的键盘输入,然后调用相应的函数。

    三、图形绘制函数

     1 void drawBoard(uint8_t board[SIZE][SIZE]) {
     2     uint8_t x,y;
     3     char color[40], reset[] = "33[m";
     4     printf("33[H");
     5 
     6     printf("2048.c %17d pts
    
    ",score);
     7 
     8     for (y=0;y<SIZE;y++) {
     9         for (x=0;x<SIZE;x++) {
    10             getColor(board[x][y],color,40);
    11             printf("%s",color);
    12             printf("       ");
    13             printf("%s",reset);
    14         }
    15         printf("
    ");
    16         for (x=0;x<SIZE;x++) {
    17             getColor(board[x][y],color,40);
    18             printf("%s",color);
    19             if (board[x][y]!=0) {
    20                 char s[8];
    21                 snprintf(s,8,"%u",(uint32_t)1<<board[x][y]);
    22                 uint8_t t = 7-strlen(s);
    23                 printf("%*s%s%*s",t-t/2,"",s,t/2,"");
    24             } else {
    25                 printf("   ·   ");
    26             }
    27             printf("%s",reset);
    28         }
    29         printf("
    ");
    30         for (x=0;x<SIZE;x++) {
    31             getColor(board[x][y],color,40);
    32             printf("%s",color);
    33             printf("       ");
    34             printf("%s",reset);
    35         }
    36         printf("
    ");
    37     }
    38     printf("
    ");
    39     printf("        ←,↑,→,↓ or q        
    ");
    40     printf("33[A"); // one line up
    41 }
    View Code

    在drawBoard函数中我们看到绘制的实现过程。函数的主体是一个for循环,每循环一次画一行,这里指的是Board中的一行。循环体中有3个小for循环,每个循环画出终端中的一行,也就是说Board的一行是终端的3行。每个格子的尺寸是3行7列,最中间的位置是数字,如果没有数字则输出一个点。其他区域则用空格填充。

    细心的朋友可能已经发现,外循环的变量是y,内循环的变量为x,这样一来board[0][0]到board[3][0]表示的是第1行,board[0][1]到board[3][1]表示第2行,这种对应关系需要特别注意。

    "33m"之类的符号用于控制终端的颜色和其他一些行为。下面给出本程序中出现的用法,更多控制序列的用法可以参考这个网页。通过输出带颜色的空格和字符,2048.c在终端中实现了类似图形界面的效果。

    33[0m 关闭所有属性 
    33[30m -- 33[37m 设置前景色 
    33[40m -- 33[47m 设置背景色 
    33[nA 光标上移n行 
    33[nB 光标下移n行 
    33[nC 光标右移n行 
    33[nD 光标左移n行 
    33[y;xH设置光标位置 
    33[2J 清屏 
    33[?25l 隐藏光标 
    33[?25h 显示光标 

     四、游戏逻辑

    我们现在已经知道游戏的主要数据结构,以及如何将它显示在屏幕上,我们接下来要关注游戏罗杰是怎么实现的。2048游戏本身非常简单,其实我们只想关心划的那一下是怎么实现的。我们已经看到2048.c实现了moveUp、moveLeft、moveDown、moveRight四个函数,表示4个划的方向。

    moveUp函数看起来也非常简单,它仅仅调用4次slideArray函数。还记得刚刚说过的二维数组和盘面的对应规则吗,矩阵的每一行代表的是盘面的一列,因此每次滑动一个一维数组,实际上滑动的是一列。slideArray函数负责将数组从高index到低index滑动,对应在屏幕上,也就是向上滑动了。

    1 bool moveUp(uint8_t board[SIZE][SIZE]) {
    2     bool success = false;
    3     uint8_t x;
    4     for (x=0;x<SIZE;x++) {
    5         success |= slideArray(board[x]);
    6     }
    7     return success;
    8 }
    View Code

    slideArray函数和它的辅助函数findTarget任务已经比较简单明了,就不需要详细说了。需要注意的就是在滑的时候合并的块不能第二次合并了,例如2 2 2 2一次合并的结果是4 4,而不会是8.

    其他几个函数实现比较巧妙,作者先把盘面进行旋转,然后再调用这个moveUp函数实现。作者通过rotateBoard函数把这个4x4的矩阵旋转90度。数组的下标可以通过建立坐标系得到。

     1 void rotateBoard(uint8_t board[SIZE][SIZE]) {
     2     uint8_t i,j,n=SIZE;
     3     uint8_t tmp;
     4     for (i=0; i<n/2; i++) {
     5         for (j=i; j<n-i-1; j++) {
     6             tmp = board[i][j];
     7             board[i][j] = board[j][n-i-1];
     8             board[j][n-i-1] = board[n-i-1][n-j-1];
     9             board[n-i-1][n-j-1] = board[n-j-1][i];
    10             board[n-j-1][i] = tmp;
    11         }
    12     }
    13 }
    View Code

    了解了这些信息,再看其他的函数比如countEmpty、addRandom等就非常简单了,大家直接去看代码就可以了。

    五、总结

    2048.c这个小游戏虽然只有400多行,但复现了2048游戏的精髓。而且程序以纯C语言实现,没有使用ncurses之类的第三方库,得到了很不错的效果。实现的过程也有一些精巧的地方,例如如何把问题化繁为简的,如何避免多次编写move函数。其实2048.c不仅可以拿来阅读,无聊的时候玩一局也是相当不错的。

  • 相关阅读:
    smb 共享文件夹
    php nginx 配置
    mysql 存储过程
    ubuntu 源码下载分析
    rust 小米R3G官方rom(openwrt) openssl
    rust 配置
    mac 制作树莓派3b启动盘
    小米R2D
    golang 配置
    golang pprof操作流程
  • 原文地址:https://www.cnblogs.com/cocode/p/10268623.html
Copyright © 2020-2023  润新知