zoukankan      html  css  js  c++  java
  • 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不仅可以拿来阅读,无聊的时候玩一局也是相当不错的。

  • 相关阅读:
    常用知识点集合
    LeetCode 66 Plus One
    LeetCode 88 Merge Sorted Array
    LeetCode 27 Remove Element
    LeetCode 26 Remove Duplicates from Sorted Array
    LeetCode 448 Find All Numbers Disappeared in an Array
    LeetCode 219 Contains Duplicate II
    LeetCode 118 Pascal's Triangle
    LeetCode 119 Pascal's Triangle II
    LeetCode 1 Two Sum
  • 原文地址:https://www.cnblogs.com/cocode/p/10268623.html
Copyright © 2011-2022 走看看