一、背景

上一篇文章介绍了readline的基本用法,这一篇通过readline读取按键对2048游戏的c语言实现;

二、思路

2.1 游戏介绍

关于2048游戏,网上找了一下介绍,你可以点击这里在线进行体验:

"《2048》的初始数字则是由2+2组成的基数4。在操作方面的不同则表现为一步一格的移动,变成更为爽快的一次到底。

相同数字的方况在靠拢、相撞时会相加。系统给予的数字方块不是2就是4,玩家要想办法在这小小的16格范围中凑出「2048」这个数字方块。"

    看似简单的4x4方格游戏,梳理规则的时候发现还是比较繁琐的;

2.2 移动、合并规则

以向右移动为例,向右移动时,按照行(row)为单位各自进行处理:
  1. 移动完成后,需要保持有数字的方块都移动到最右侧,.eg: (0204)->(0024) / (2004)->(0024) / (2400)->(0024);
  2. 相邻的同等值方块进行一次合并动作,.eg: (0022)->(0004) / (0222) -> (0024) / (2222) -> (0044) / (2244) -> (0048);
  3. 相邻的意思为非零方块的相邻,即中间夹杂的零方块还需进行移动消除,.eg:(0202) -> (0004) / (2024) -> (0044);
  4. 移动、合并之后,在最左列某个零方块上产生一个2或4的新值,其中2出现的概率是4的两倍;

2.3 胜利、失败的界定

胜利则查找数组,若出现某个方格值大于等于2048则表示胜利;

失败的界定得符合以下两个条件:

  1. 4x4 数组方格都被填满,没有零值方格;
  2. 4x4 数组方格无法再相互合并,即相邻方块值各不相等;

3 实现

参考了文章[1]的思路,通过另外一个切入点,根据第二小节的描述,对移动、合并画出处理流程图如下:

开始代码的实现说明,先是定义了一个4x4的全局数组;

#define SIZE 4static int grid[SIZE][SIZE] = { {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},
};

定义了通用的移动、合并的宏,方便后续操作;

#define __move_a2b(a, b) if ( (a) != 0 ) { \printf("Move  %d to %d\n", (a), (b)); \(b) = (a); \(a) = 0; \
}#define __merge_a2b(a, b) if ( (b) != 0 ) { \printf("Merge %d to %d\n", (a), (b)); \(b) *= 2; \(a) = 0; \
}#define __try_merge_a2b(a, b) if ( (b) == (a) ) { \__merge_a2b(a, b); \
}

然后是对应流程图的右移函数的实现,(左移、上移、下移类似)

#define __for_each(i, n) for ( i = 0, n = random() % SIZE; i < SIZE; i++, n = (n + 1) % SIZE )int grid_move_right()
{   int ix = 0, jx = 0, row = 0, col = 0;printf("right\n");/* Move */for ( row = 0; row < SIZE; row++ ) {for ( col = SIZE - 1; col > 0; col-- ) {/* Fill zero by pulling */for ( ix = 0; ix < col && grid[row][col] == 0; ix++ ) {for ( jx = col; jx > 0; jx-- ) {__move_a2b(grid[row][jx - 1], grid[row][jx]);}}for ( ix = 0; ix < col - 1 && grid[row][col - 1] == 0; ix++ ) {for ( jx = col - 1; jx > 0; jx-- ) {__move_a2b(grid[row][jx - 1], grid[row][jx]);}}__try_merge_a2b(grid[row][col - 1], grid[row][col]);}}/* Generate one random cells in col[0] */if ( is_won() || is_over() ) {return;}__for_each(ix, row) {if ( grid[row][0] == 0 ) {grid[row][0] = __new_cell();break;}}grid_display(row, 0);
}

对于胜利失败的界定函数,根据需求描述进行实现:

int is_won()
{int ix = 0, jx = 0;for ( ix = 0; ix < SIZE; ix++ ) {for ( jx = 0; jx < SIZE; jx++ ) {if ( grid[ix][jx] >= 2048 ) {grid_display(ix, jx);printf("[ You won! ]\n");return 1;}}}return 0;
}int is_over()
{int ix = 0, jx = 0;for ( ix = 0; ix < SIZE; ix++ ) {for ( jx = 0; jx < SIZE; jx++ ) {if ( ix < SIZE - 1 && grid[ix][jx] == grid[ix + 1][jx] ) {return 0;}if ( jx < SIZE - 1 && grid[ix][jx] == grid[ix][jx + 1] ) {return 0;}if ( grid[ix][jx] == 0 ) {return 0;}}}printf("[ Game over! ]\n");return 1;
}

如果游戏还没结束,那么还得产生一个新的随机块,随机函数实现如下:

/* 66.66% is 2; 33.33% is 4 */
#define __new_cell() \(random() & 0x3 ? 2 : 4)

然后对应的显示函数,为了显示方便,对新产生的方块进行黄颜色标记处理

char *grid_generate(char *buffer, size_t size, int row, int col)
{int ix = 0;int jx = 0;int len = 0;#define __do_append(buffer, len, size, args...) do { \len += snprintf(buffer + len, size - len, args); \
} while(0);__do_append(buffer, len, size, "+----+----+----+----+\n");for ( ix = 0; ix < SIZE; ix++ ) {for ( jx = 0; jx < SIZE; jx++ ) {if ( grid[ix][jx] ) {if ( ix == row && jx == col ) {__do_append(buffer, len, size, "|\033[40;33m%4d\033[0m", grid[ix][jx]);}else {__do_append(buffer, len, size, "|%4d", grid[ix][jx]);}}else {__do_append(buffer, len, size, "|    ");}}__do_append(buffer, len, size, "|\n");__do_append(buffer, len, size, "+----+----+----+----+\n");}return buffer;
}void grid_display(int row, int col)
{char line[2048] = {0};fprintf(stderr, "%s", grid_generate(line, sizeof(line), row, col));
}

核心函数讲完了,最后说一下主函数,本例使用的readline库rl_bin_key()函数对按键进行绑定操作,

上下左右分别为(WSAD、KJHL),绑方向键的方法还没有找到;

// gcc -g -o 2048 2048.c -lreadline
int main(int argc, char *argv[])
{char *pline = NULL;char prompt[2048] = {0};grid_init();rl_bind_key('h', grid_move_left);rl_bind_key('j', grid_move_up);rl_bind_key('k', grid_move_down);rl_bind_key('l', grid_move_right);rl_bind_key('d', grid_move_left);rl_bind_key('w', grid_move_up);rl_bind_key('s', grid_move_down);rl_bind_key('a', grid_move_right);while ( 1 ) {pline = readline("");if ( !pline ) {break;}free(pline);}return EXIT_SUCCESS;
}

四、结果分析

运行成功、失败的结果如下:

    

上手发现一次成功的难度较大,需后续增加回滚功能减低难度,要不一步走错基本gg

然后在显示方面可以优化,比如说每次都清屏再重新打印,并增加一些提示功能...

工程源码下载

参考文章:

[1] http://www.cnblogs.com/judgeyoung/p/3760515.html

[2] https://github.com/gabrielecirulli/2048

Linux下使用readline库实现2048游戏相关推荐

  1. Linux下基于SDL库贪吃蛇游戏

    Linux下基于SDL库贪吃蛇游戏   SDL(Simple DirectMediaLayer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成.SDL提供了数种控制图像.声音.输出入的函数,让开 ...

  2. linux怎么编写弹球游戏,Linux下利用curses库实现弹球游戏.docx

    #define TOP #define TOP 0/*当前屏幕的最上边 */ Linux 下利用 curses 库实现弹球游戏 #include #include #include #define R ...

  3. linux curses,Linux下利用curses库实现弹球游戏

    控制球水平运动的变量*/ int vdir;   /*控制球垂直运动的变量*/ int pos_X;  /*球的横坐标*/ int pos_Y;  /*球的纵坐标*/ int delay=100; v ...

  4. Linux下的动态库和静态库

    什么是库? 在 Linux 开发时,我们经常会看到一些形如 xxx.so 的名称出现,其中 so 是 Shared Object 的缩写,即可以共享的目标文件,也就是我们所称为的动态链接库,和在 Wi ...

  5. Linux下的静态库、动态库和动态加载库

    from: http://www.techug.com/linux-static-lib-dynamic-lib 库的存在极大的提高了C/C++程序的复用性,但是库对于初学者来说有些难以驾驭,本文从L ...

  6. linux 下基于jrtplib库的实时传送实现

    linux 下基于jrtplib库的实时传送实现 一.RTP 是进行实时流媒体传输的标准协议和关键技术 实时传输协议(Real-time Transport Protocol,PRT)是在 Inter ...

  7. linux下安装uuid库

    1.linux 下安装UUID库 1.1)ubuntu下安装uuid链接库 sudo apt-get install uuid-dev 1.2)CentOS yum install libuuid-d ...

  8. Linux下基于Libmad库的MP3音乐播放器编写

    linux下基于Libmad库的MP3音乐播放器编写 libmad是一个开源mp3解码库,其对mp3解码算法做了很多优化,性能较好,很多播放器如mplayer.xmms等都是使用这个开源库进行解码的: ...

  9. linux下基于jrtplib库的实时传送实现

    linux 下基于jrtplib库的实时传送实现 一.RTP 是进行实时流媒体传输的标准协议和关键技术  实时传输协议(Real-time Transport Protocol,PRT)是在 Inte ...

最新文章

  1. 求5个学生4门成绩的总分和平均分
  2. 【数学与算法】三角函数公式【仅供查询】
  3. 运用神经网络方法找寻集成学习中的最优权重
  4. Maven(二):常用命令、依赖管理
  5. 【Python】学习笔记总结9(数据库与Python交互)
  6. ckks方案优化最好的_网站优化关键词怎么选?好的关键词长什么样?
  7. String类的构造与析构相关处理
  8. Ndarry/Dataframe使用pytorch转为tensor格式
  9. Spring中控制反转IoC理论推导
  10. 强化学习 RL - DQN - 智能体Agent
  11. Qt: 解决Qt语言家 更新翻译(lupdate) 非常慢卡死没反应现象
  12. GD32F103学习笔记(1)——搭建环境、编译烧写
  13. Makfile 应用进阶实例
  14. php融云开源sdk使用im,开源项目索引
  15. python中输入字符串_python如何输入字符串
  16. 漏洞修复:javascript框架库漏洞
  17. 哥德巴赫猜想“1+1″的证明(李扩继)
  18. 背负青天而莫之夭阏者,而后乃今将图南。
  19. Scroller的使用
  20. MyBatis日期用法技巧

热门文章

  1. java matcher find,java - 无尽循环matcher.find()
  2. 荆州高考 2021成绩查询,湖北2021年高考成绩发布和填报志愿时间安排出炉—湖北—荆州新闻网...
  3. FinalShell教程
  4. 电影记忆之19(搏击俱乐部)
  5. 用这些渠道玩转淘宝内容营销
  6. ParameterizedType使用简单了解
  7. 基于Echarts+HTML5可视化数据大屏展示——智慧物流服务中心
  8. tp3.2中企业向个人微信钱包零钱付款
  9. 爸爸,我不想做你的女儿
  10. 选择计算机配置win8,win8电脑打算恢复出厂设置,最好这么做!