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.
主函数中完成一些初始化和设置工作,然后进入主循环。在循环中接受用户的键盘输入,然后调用相应的函数。
三、图形绘制函数
void drawBoard(uint8_t board[SIZE][SIZE]) {
uint8_t x,y;
char color[], reset[] = "\033[m";
printf("\033[H"); printf("2048.c %17d pts\n\n",score); for (y=;y<SIZE;y++) {
for (x=;x<SIZE;x++) {
getColor(board[x][y],color,);
printf("%s",color);
printf(" ");
printf("%s",reset);
}
printf("\n");
for (x=;x<SIZE;x++) {
getColor(board[x][y],color,);
printf("%s",color);
if (board[x][y]!=) {
char s[];
snprintf(s,,"%u",(uint32_t)<<board[x][y]);
uint8_t t = -strlen(s);
printf("%*s%s%*s",t-t/,"",s,t/,"");
} else {
printf(" · ");
}
printf("%s",reset);
}
printf("\n");
for (x=;x<SIZE;x++) {
getColor(board[x][y],color,);
printf("%s",color);
printf(" ");
printf("%s",reset);
}
printf("\n");
}
printf("\n");
printf(" ←,↑,→,↓ or q \n");
printf("\033[A"); // one line up
}
在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行,这种对应关系需要特别注意。
"\033m"之类的符号用于控制终端的颜色和其他一些行为。下面给出本程序中出现的用法,更多控制序列的用法可以参考这个网页。通过输出带颜色的空格和字符,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滑动,对应在屏幕上,也就是向上滑动了。
bool moveUp(uint8_t board[SIZE][SIZE]) {
bool success = false;
uint8_t x;
for (x=;x<SIZE;x++) {
success |= slideArray(board[x]);
}
return success;
}
slideArray函数和它的辅助函数findTarget任务已经比较简单明了,就不需要详细说了。需要注意的就是在滑的时候合并的块不能第二次合并了,例如2 2 2 2一次合并的结果是4 4,而不会是8.
其他几个函数实现比较巧妙,作者先把盘面进行旋转,然后再调用这个moveUp函数实现。作者通过rotateBoard函数把这个4x4的矩阵旋转90度。数组的下标可以通过建立坐标系得到。
void rotateBoard(uint8_t board[SIZE][SIZE]) {
uint8_t i,j,n=SIZE;
uint8_t tmp;
for (i=; i<n/; i++) {
for (j=i; j<n-i-; j++) {
tmp = board[i][j];
board[i][j] = board[j][n-i-];
board[j][n-i-] = board[n-i-][n-j-];
board[n-i-][n-j-] = board[n-j-][i];
board[n-j-][i] = tmp;
}
}
}
了解了这些信息,再看其他的函数比如countEmpty、addRandom等就非常简单了,大家直接去看代码就可以了。
五、总结
2048.c这个小游戏虽然只有400多行,但复现了2048游戏的精髓。而且程序以纯C语言实现,没有使用ncurses之类的第三方库,得到了很不错的效果。实现的过程也有一些精巧的地方,例如如何把问题化繁为简的,如何避免多次编写move函数。其实2048.c不仅可以拿来阅读,无聊的时候玩一局也是相当不错的。
C语言小游戏: 2048.c的更多相关文章
- C语言小游戏——2048
2048 2048这款游戏的玩法很简单,每次可以选择上下左右滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢.相撞时会相加. ...
- 【C语言探索之旅】 第一部分第八课:第一个C语言小游戏
内容简介 1.课程大纲 2.第一部分第八课:第一个C语言小游戏 3.第一部分第九课预告: 函数 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写 ...
- 012-C语言小游戏之推箱子
012-C语言小游戏之推箱子 一.创建游戏地图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #define ROWS 11 #define COLS 12 char ...
- c语言----<项目>_小游戏<2048>
2048 小游戏 主要是针对逻辑思维的一个训练. 主要学习方面:1.随机数产生的概率.2.行与列在进行移动的时候几种情况.3.MessageBox的使用 #include <iostream&g ...
- c语言小游戏-扫雷的完成
C语言-扫雷游戏 本文将对此游戏做一个大致的概述,此代码适合初学者,编写软件使用了vs2017. 该代码可以实现如下功能: 1.用户可以选择3个难度,分别布置不同个数的雷. 2.随机数设置雷的位置. ...
- C语言小游戏: 推箱子 支线(一)--1
好家伙,考完试了 回顾一下2021 回顾一下某次的作业 妙啊 所以, 做一个推箱子小游戏 1.先去4399找一下关卡灵感 就它了 2.在百度上搜几篇推箱子, 参考其中的"■ ☆"图 ...
- JavaScript小游戏--2048(PC端)
1.初始化棋局 $(document).ready(function() { prepare_for_mobile(); //适配移动端 new_game(); }); 2.开始新游戏 functio ...
- C语言 小游戏之贪吃蛇
还记得非常久曾经听群里人说做贪吃蛇什么的,那时候大一刚学了C语言,认为非常难,根本没什么思路. 前不久群里有些人又在谈论C语言贪吃蛇的事了,看着他们在做,我也打算做一个出来. 如今大三,经过了这一年半 ...
- C语言小游戏:贪吃蛇
#include <graphics.h> #include <conio.h> #include <stdio.h> #define WIDTH 40 //设置宽 ...
随机推荐
- uniGUI之TUniHiddenPanel(14)
TUniHiddenPanel是将不在界面上显示的 容器 控件. 只有uniDBGrid实际列才有对应的编辑控件,如果是外键列则无法设置 编辑控件. 里面的控件将不会 显示.将控件放入其中即可. ...
- Java最新面试题
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
- 中山Day10——普及
今天又是愚蠢的一天,估分230,实得110.其中T2.4不会,这里就只说题意和简要思路. 收获:scanf>>a,以及printf<<a. T1:模板题 此题相对简单,就是读入 ...
- win7系统实现内外网同时连接图文教程
解决方案:修改路由表 在工作中,经常会遇到切换内外网的网络情况,通常情况下都是断开/连接网络,很麻烦.我们可以使用route命令来解决此类问题,route add.route delete.route ...
- SpringCloud实战——(2)通过Feign调用其他模块
大型项目下往往有很多模块,ZCGL项目结构如下: 需要引用的其他模块已经发布成服务并在Eureka Server注册中心注册,如下: 写程序时引用了其他模块,并且其他模块在项目中,但是IDEA任然无法 ...
- Linux中{ }的用法
一.生成序列 格式:{#..#},按照ASCII表的顺序进行生成,如{a..c}表示a b c,也可以{c..a}倒叙的形式生成c b a # ..} # echo {z..a} z y x w v ...
- Jsp有哪些内置对象?作用分别是什么?
Page,pageContext,request,response,session,application,out,config,exception Page指的是JSP被翻译成Servlet的对象的 ...
- bootstrap如何设置每一个选项卡对应一个页面
bootstrap选项卡如果直接在每一个选项div中直接插入页面,可以使用<object type="text/html" data="test.html" ...
- P1091合唱队形(LIS问题)
题目描述(题目链接:https://www.luogu.org/problem/P1091) NN位同学站成一排,音乐老师要请其中的(N-KN−K)位同学出列,使得剩下的KK位同学排成合唱队形. 合唱 ...
- angularJs-服务调用与后台数据获取
可以用factory做一些后台数据的获取,例如 happyFarm.factory('seedList',['$http',function($http){ return { ge ...