花了一个深夜,才用C语言写了一个2048游戏雏形

12年我毕业的第二个月工资,我就买了一个IPAD,然后在IPAD上下了一个2048游戏,玩起来非常爽。

然后这几天看到好几个公众号都发了自己写这个游戏的代码,然后我自己也想试试,所以就有了这篇文章,写代码还是很有意思的。

针对这个游戏我写几个比较关键的点

— — 在Linux 下输入不用按回车键也就能获取键值

我们平时输入的时候,需要按 Enter ,getchar 才能获取键值,所以这个需要设置下

/*设置之后不用按下回车就可以接收到字符*/
void setBufferedInput(bool enable) {static bool enabled = true;static struct termios old;struct termios new;if (enable && !enabled) {// restore the former settingstcsetattr(STDIN_FILENO,TCSANOW,&old);// set the new stateenabled = true;} else if (!enable && enabled) {// get the terminal settings for standard inputtcgetattr(STDIN_FILENO,&new);// we want to keep the old setting to restore them at the endold = new;// disable canonical mode (buffered i/o) and local echonew.c_lflag &=(~ICANON & ~ECHO);// set the new settings immediatelytcsetattr(STDIN_FILENO,TCSANOW,&new);// set the new stateenabled = false;}
}

如果设置为 false,就可以不输入回车就能获取键值,还需要注意我们有时候按下 Ctrl+c 退出程序,需要在这里的回调函数重新设置,要不然在正常的界面输入会出现问题。

— — 随机值的设置

我们需要在画盘上插入随机值,所以就需要随机值生成的方法,C 库里面就有这个随机函数,但是需要设置下。

随机值的设置需要遵循规则如下

//要取得[a,b)的随机整数,使用(rand() % (b-a))+ a (结果值含a不含b)。

//要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a (结果值含a和b)。

//要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1 (结果值不含a含b)。

/*初始化几个数字在键盘上*/
int add_random(void)
{static bool initialized = false;int pos,data,i;/*随机数需要设置种子,用时间来做随机数的种子*/if (!initialized) {srand(time(NULL));initialized = true;}/*检测如果原来的位置有数据了,就不能在原来的数据上加数据*/for(i=0;i<16;i++){pos = rand()%15;if(array_core[pos] != 0)continue;elsebreak;}/*获取 0~4 的随机值*/data =  rand()%(4-0+1)+ 0;/*再通过数组把这个数值转换成我们需要的随机数*/place_xy(pos,array_rand[data]);return (0);
}

— — 清除屏幕

我们需要循环显示画盘,所以需要每次显示之前都需要清除屏幕,关键代码如下

/*初始化一个键盘*/
int show_board(void)
{printf("\033[H\033[J");/*清空屏幕 但是并不是每一个terminal都会生效*/printf("%s",board);printf("[ ←,↑,→,↓ or q  ]\n");return(0);
}

— — 几个关键的数组

我们需要几个关键的数组来协助我们计算,这部分根据自己的代码要求来设置。

第一个数组是键盘,我们的数据都是在键盘上显示的,因为 2048 是4个数字,所以中间要预留4个空格出来。

d_xy 数组是用来保存这16个数字的位置的坐标数组,有了这些坐标,就可以知道如何摆放这些数值了。

另外两个数组的作用已经在注释里面写清楚了。

/*数组*/
char board[] =" ---------------------\n \
|    |    |    |    |\n \
---------------------\n \
|    |    |    |    |\n \
---------------------\n \
|    |    |    |    |\n \
---------------------\n \
|    |    |    |    |\n \
---------------------\n";const int d_xy[16] = {25,30,35,40, 71,76,81,86, 117,122,127,132, 163,168,173,178};
const int array_rand[5]  = {2,4,4,2,8};/*随机数值*/
int array_core[16] = {0};/*用来保存键盘上的16个数字*/

— — 最关键的移动算法

先说下我们键盘的位置

我没有用二维数组,后面想了下,如果用二维数组的话会方便很多,而且算法可能也会方便很多,特别是用到矩阵变换

— 下移

从下往上的每一列,把相邻相同的数字加起来,放到下面那个位置,然后把上面的位置清0。

把0的数值往上移动,把数字往下移动。

其他方向的移动规则跟这个一样的思路

代码实现

/*下移*/
int move_down(void)
{int i=0,j=0,x=0,y=0;int temp[4] = {0};/*1、合并相邻相同的两个元素*//*2、把0元素移动到最上面*/for(x=0;x<4;x++){for(y=x,i=0;y<=12+x;i++,y+=4){temp[i] = array_core[y];}/*合并相邻的两个非0值*/for(i=3;i>=0;i--){for(j=3;j>0;j--){if(temp[j] == temp[j-1] && temp[j-1] != 0){temp[j] = 2*temp[j-1];temp[j-1] = 0;}}}/*把0往上移动*/for(i=3;i>=0;i--){for(j=3;j>0;j--){if(temp[j] == 0 && temp[j-1] != 0){temp[j] = temp[j-1];temp[j-1] = 0;}}}for(y=x,i=0;y<=12+x;i++,y+=4){array_core[y] = temp[i];}}/*在键盘上显示*/for(i=0;i<16;i++)place_xy(i,array_core[i]);add_random();return (0);
}

— — 完整代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include <signal.h>/*函数声明*/
int show_board(void);
void teset(void);
int move_down(void);
/*宏定义*/
#define X_LENGTH (13)/*数组*/
char board[] =" ---------------------\n \
|    |    |    |    |\n \
---------------------\n \
|    |    |    |    |\n \
---------------------\n \
|    |    |    |    |\n \
---------------------\n \
|    |    |    |    |\n \
---------------------\n";const int d_xy[16] = {25,30,35,40, 71,76,81,86, 117,122,127,132, 163,168,173,178};
const int array_rand[5]  = {2,4,4,2,8};/*随机数值*/
int array_core[16] = {0};/*用来保存键盘上的16个数字*/int move_left(void)
{int i=0,j=0,x=0,y=0;int temp[4] = {0};/*1、合并相邻相同的两个元素*//*2、把0元素移动到最下面*/for(y=0;y<=12;y+=4){for(x=y,i=0;x<=3+y;i++,x++){temp[i] = array_core[x];}/*上移*/for(i=0;i<4;i++){for(j=0;j<3;j++){if(temp[j] == temp[j+1] && temp[j+1] != 0){temp[j] = 2*temp[j+1];temp[j+1] = 0;}}}/*上移*/for(i=0;i<4;i++){for(j=0;j<3;j++){if(temp[j] == 0 && temp[j+1] != 0){temp[j] = temp[j+1];temp[j+1] = 0;}}}for(x=y,i=0;x<=3+y;i++,x++){array_core[x] = temp[i];}}/*在键盘上显示*/for(i=0;i<16;i++){place_xy(i,array_core[i]);}add_random();return (0);
}int move_right(void)
{int i=0,j=0,x=0,y=0;int temp[4] = {0};/*1、合并相邻相同的两个元素*//*2、把0元素移动到最上面*/for(y=0;y<=12;y+=4){for(x=y,i=0;x<=3+y;i++,x++){temp[i] = array_core[x];}/*合并相邻的两个非0值*/for(i=3;i>=0;i--){for(j=3;j>0;j--){if(temp[j] == temp[j-1] && temp[j-1] != 0){temp[j] = 2*temp[j-1];temp[j-1] = 0;}}}/*把0往上移动*/for(i=3;i>=0;i--){for(j=3;j>0;j--){if(temp[j] == 0 && temp[j-1] != 0){temp[j] = temp[j-1];temp[j-1] = 0;}}}for(x=y,i=0;x<=3+y;i++,x++){array_core[x] = temp[i];}}/*在键盘上显示*/for(i=0;i<16;i++)place_xy(i,array_core[i]);add_random();return (0);
}/*上移动*/
int move_up(void)
{int i=0,j=0,x=0,y=0;int temp[4] = {0};/*1、合并相邻相同的两个元素*//*2、把0元素移动到最下面*/for(x=0;x<4;x++){for(y=x,i=0;y<=12+x;i++,y+=4){temp[i] = array_core[y];}/*上移*/for(i=0;i<4;i++){for(j=0;j<3;j++){if(temp[j] == temp[j+1] && temp[j+1] != 0){temp[j] = 2*temp[j+1];temp[j+1] = 0;}}}/*上移*/for(i=0;i<4;i++){for(j=0;j<3;j++){if(temp[j] == 0 && temp[j+1] != 0){temp[j] = temp[j+1];temp[j+1] = 0;}}}for(y=x,i=0;y<=12+x;i++,y+=4){array_core[y] = temp[i];}}/*在键盘上显示*/for(i=0;i<16;i++){place_xy(i,array_core[i]);}add_random();return (0);    
}
/*下移*/
int move_down(void)
{int i=0,j=0,x=0,y=0;int temp[4] = {0};/*1、合并相邻相同的两个元素*//*2、把0元素移动到最上面*/for(x=0;x<4;x++){for(y=x,i=0;y<=12+x;i++,y+=4){temp[i] = array_core[y];}/*合并相邻的两个非0值*/for(i=3;i>=0;i--){for(j=3;j>0;j--){if(temp[j] == temp[j-1] && temp[j-1] != 0){temp[j] = 2*temp[j-1];temp[j-1] = 0;}}}/*把0往上移动*/for(i=3;i>=0;i--){for(j=3;j>0;j--){if(temp[j] == 0 && temp[j-1] != 0){temp[j] = temp[j-1];temp[j-1] = 0;}}}for(y=x,i=0;y<=12+x;i++,y+=4){array_core[y] = temp[i];}}/*在键盘上显示*/for(i=0;i<16;i++)place_xy(i,array_core[i]);add_random();return (0);
}/*测试函数*/
void teset(void)
{
#if 0int i = 0;for(i = 0;i<16;i++){place_xy(i,i+1);}
#else/*计算偏移值*/int i = 0;for(i = 0;i<strlen(board);i++){if(board[i] == '\n')printf(" %d ",i+3); }   printf("\n"); 
#endif
}int place_xy(int pos,int data)
{if(data == 0){board[d_xy[pos]] = 0x20; /*十位*/board[d_xy[pos] +1 ] = 0x20; /*个位*/board[d_xy[pos] +2 ] = 0x20; /*个位*/board[d_xy[pos] +3 ] = 0x20; /*个位*/}else if(data < 10){board[d_xy[pos]] = data + 48;/*如果数值是0,就不显示*/}else if(data < 100){board[d_xy[pos]]     = data/10 + 48;   board[d_xy[pos] +1]  = data%10 + 48;         }else if(data < 1000){board[d_xy[pos]]     = data/100 + 48;   board[d_xy[pos] +1]  = data%100/10 + 48;  board[d_xy[pos] +2]  = data%100%10 + 48;  }else{board[d_xy[pos]]     = data/1000 + 48;        /*千位*/board[d_xy[pos] +1]  = data%1000/100 + 48;    /*百位*/board[d_xy[pos] +2 ] = data%1000%100/10 + 48; /*十位*/board[d_xy[pos] +3 ] = data%1000%100%10 + 48; /*个位*/}/*把数字保存到键盘里面去*/array_core[pos] = data;/*显示键盘*/show_board();return(0);
}/*初始化几个数字在键盘上*/
int add_random(void)
{static bool initialized = false;int pos,data,i;/*随机数需要设置种子,用时间来做随机数的种子*/if (!initialized) {srand(time(NULL));initialized = true;}/*检测如果原来的位置有数据了,就不能在原来的数据上加数据*/for(i=0;i<16;i++){pos = rand()%15;if(array_core[pos] != 0)continue;elsebreak;}data =  rand()%(4-0+1)+ 0;place_xy(pos,array_rand[data]);return (0);
}/*初始化一个键盘*/
int show_board(void)
{printf("\033[H\033[J");/*清空屏幕 但是并不是每一个terminal都会生效*/printf("%s",board);printf("[ ←,↑,→,↓ or q  ]\n");return(0);
}/*设置之后不用按下回车就可以接收到字符*/
void setBufferedInput(bool enable) {static bool enabled = true;static struct termios old;struct termios new;if (enable && !enabled) {// restore the former settingstcsetattr(STDIN_FILENO,TCSANOW,&old);// set the new stateenabled = true;} else if (!enable && enabled) {// get the terminal settings for standard inputtcgetattr(STDIN_FILENO,&new);// we want to keep the old setting to restore them at the endold = new;// disable canonical mode (buffered i/o) and local echonew.c_lflag &=(~ICANON & ~ECHO);// set the new settings immediatelytcsetattr(STDIN_FILENO,TCSANOW,&new);// set the new stateenabled = false;}
}/*需要检测ctrl+c 按键,要不然输入变成一直输入了不转回来,键盘就输入不了了*/
void signal_callback_handler(int signum) {printf("         TERMINATED         \n");setBufferedInput(true);printf("\033[?25h\033[m");exit(signum);
}int main()
{char c;bool success;memset(array_core,0,sizeof(array_core)/sizeof(array_core[0]));/*在键盘上添加三个随机数*/add_random();add_random();add_random();/*显示键盘*/show_board();setBufferedInput(false);teset();/*注册信号量*/signal(SIGINT, signal_callback_handler);while(true){c=getchar();//printf("%d",c);#if 1if (c == -1){puts("\nError! Cannot read keyboard input!");break;}switch(c) {case 97:  // 'a' keycase 104:  // 'h' keycase 68:  // left arrowsuccess = move_left();  break;case 100:  // 'd' keycase 108:  // 'l' keycase 67:  // right arrowsuccess = move_right(); break;case 119:  // 'w' keycase 107:  // 'k' keycase 65:  // up arrowsuccess = move_up();    break;case 115:  // 's' keycase 106:  // 'j' keycase 66:  // down arrow//printf("move_down\n");success = move_down();  break;default: success = false;}#endif/*判断是否退出*/if (c=='q') {printf("        退出? (y/n)         \n");c=getchar();if (c=='y') {break;}}usleep(150000);}setBufferedInput(true);//teset();return (0);
}

— — 运行起来

— — 程序缺陷

1、没有分数判断

2、没有最终结果判断,比如所有格子都不能移动了,需要判断胜负。

3、随机值加入也比较随意

4、没有设置格子颜色,比较单调

5、有一些你不知道的bug。

推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈


嵌入式Linux

微信扫描二维码,关注我的公众号 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466883.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

向银行贷款20万, 分期三年买50万的车,个人借款40万, 贷款10年买200万的房子,再贷款120万分创业...

向银行贷款20万按1年期贷款利率为&#xff1a;6%&#xff0c;若按年还贷款&#xff0c;银行贷款利息为&#xff1a;200&#xff0c;000*6%12&#xff0c;000。连本带息&#xff1a;20*106%21.2万分期三年买50万的车 贷款总额30万 年利率按10%算&#xff0c;分三年还清&#xff…

如何避免测试盲区

测试盲区&#xff0c;也就是软件中测试人员未测试到的地方&#xff0c;造成这方面的原因主要有测试人员对测试需求理解不足、经验不足和思维僵化等原因造成的。接下来&#xff0c;就如何避免测试盲区给出几点建议。 一、 充分理解软件需求需求方面的如果理解有误或者分析遗漏&a…

集成算法——Adaboost代码

集成算法是我们将不同的分类器组合起来&#xff0c;而这种组合结果就被称为集成方法或者是元算法。使用集成方法时会有多种形式&#xff1a;可以是不同算法的集成&#xff0c;也可以是同意算法在不同设置下的集成&#xff0c;还可以是数据集不同部分分配给不同分类器之后的集成…

年终抽奖来了

时间很快&#xff0c;2020年已经到了12月份&#xff0c;我从2018年开始写公众号&#xff0c;经过了快两年是时间&#xff0c;我收获了4万的读者&#xff0c;非常开心。我自己是一个挺逗逼的人&#xff0c;而且我写公众号并不觉得我比别人厉害&#xff0c;技术上我真的就是一个很…

DOS状态下可以安装Win XP

在DOS状态下可以安装Win XP&#xff0c;插入Win XP光盘后&#xff0c;首先从开始中进入MS-DOS&#xff0c;然后在MS-DOS下输入“G:\Setup.exe”(G为光驱盘符)即可。 如果想从纯DOS下进行安装&#xff0c;可用启动盘启动到DOS状态&#xff0c;安装时首先加载Smartdrv.exe(将Win …

嵌入式 Linux下永久生效环境变量bashrc

作者&#xff1a;skdkjxy原文&#xff1a;http://blog.sina.com.cn/s/blog_8795b0970101f1f9.html.bashrc文件 在linux系统普通用户目录&#xff08;cd /home/xxx&#xff09;或root用户目录&#xff08;cd /root&#xff09;下&#xff0c;用指令ls -al可以看到4个隐藏文件&am…

回归分析——线性回归

机器学习中&#xff0c;对于离散的数据可以做分类问题&#xff0c;那对于连续的数据就是做回归问题&#xff0c;这里对一元线性回归和多元线性回归做一个简介&#xff0c;帮组理解。回归分析&#xff1a;从一组样本数据出发&#xff0c;确定变量之间的数学关系式&#xff0c;对…

编译原理(五)自底向上分析之算符优先分析法

自底向上分析之算符优先分析法 说明&#xff1a;以老师PPT为标准&#xff0c;借鉴部分教材内容&#xff0c;AlvinZH学习笔记。 基本过程 1. 一般方法&#xff1a;采用自左向右地扫描和分析输入串&#xff0c;从输入符号串开始&#xff0c;通过反复查找当前句型的句柄&#xff0…

做Android开发,要清楚init.rc里面的东西

init.rc 复习看这个之前&#xff0c;先看看大神总结的文章这篇文章总结的非常到位&#xff0c;但是因为代码不是最新的Android版本&#xff0c;对我们最新的Android版本不适用。http://gityuan.com/2016/02/05/android-init/#init rc文件拷贝拷贝其实也就是把文件放到机器的某个…

abap常用系统变量

常用的系统变量如下: 1.sy-pagno当前页号 2.SY-DATUM当前时间 3.SY-LINSZ当前报表宽度 4.SY-LINCT当前报表长度 5.SPACE空字符 6.SY-SUBRC执行状态为0,表示成功. 7.SY-UNAME 用户名 8.SY-UZEIT当前时间 9.SY-TCODE当前的事务代码 10.SY-LSIND列表索引页 11.SY-LISTI上一个列表的…

随机森林

随机森林的思想&#xff1a;随机取样&#xff0c;创建M棵决策树&#xff0c;并对决策树的结果进行投票&#xff0c;选出最多的类别作为最后的分类结果。这里在创建决策树的时候引入了基尼指数的概念&#xff0c;基尼指数和信息增益的作用一样&#xff0c;都是选出数据集的最佳分…

Linux静态库和动态库的设计

静态库和动态库的设计 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 Linux应用程序设计中需要的外部函数主要由函数库和系统调用来提供 Linux应用程序使用的主要函数库均存放于/lib, /usr/lib目录下&#xff0c;其中采用*.so.*方式命名的是动态函数库…

操作excel

static void create(String path) throws Exception {//读取文件FileInputStream in new FileInputStream(path);XSSFWorkbook wb new XSSFWorkbook(in);// 获得该工作区的第一个sheetXSSFSheet sheet wb.getSheetAt(0);int rowNum sheet.getLastRowNum();//行int columnNu…

无法安装软件之解决其一 (windows installer服务篇)

早上接到兄弟求助&#xff0c;无法安装软件&#xff0c;于是把最直接方法告诉他&#xff0c;还原啊。但ghost 进程到一半就当了&#xff0c;真是对不起他。。挂了电话后查了一下&#xff0c;现将方法总结如下&#xff1a;两种环境1 windows 2000系统 中&#xff1a;第一步&…

决策树——CART和模型树

CART树 理解&#xff1a; 如果CART树处理离散型数据&#xff0c;叫做分类决策树&#xff0c;那么&#xff0c;引入基尼指数作为寻找最好的数据划分的依据&#xff0c;基尼指数越小&#xff0c;说明数据的“纯度越高”&#xff0c;随机森林的代码里边就运用到了基尼指数。如…

宏比较值,坑的一B

昨晚上&#xff0c;我准备睡觉&#xff0c;连总给我发了一段代码#include "stdio.h"#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b)) int MAX_FUNC(int a, int b) {return ((a) > (b) ? (a) : (b)); }int main() {unsigned int a 1;int b -1;printf(&quo…

Linux下Samba服务器搭建

linux文件共享之samba服务器 ——ubuntu 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 关闭LINUX防火墙命令&#xff1a; #ufwdisable 然后就在windows下ping一下linux的IP&#xff0c;如果能ping通&#xff0c;就可以继续下面的内容&#xff0c;如果p…

日志分析linux命令

1、查看文件内容&#xff1a;cat 。-n显示行号。 2. more&#xff1a;全屏幕分页显示文件的内容。下一页&#xff1a;space键&#xff1b;上一页&#xff1a;B&#xff1b;停止&#xff1a;Q&#xff1b;显示下一行&#xff1a;Enter&#xff1b; 3. less&#xff1a;在文件最…

C#比较两时间大小

1。比较时间大小的实验 string st1"12:13"; string st2"14:14"; DateTime dt1Convert.ToDateTime(st1); DateTime dt2Convert.ToDateTime(st2); DateTime dt3DateTime.Now; if(DateTime.Compare(dt1,dt2)>0) msg.Textst1">…

搞懂C++为什么难学,看这篇就够了!

学C能干什么&#xff1f; 往细了说&#xff0c;后端、客户端、游戏引擎开发以及人工智能领域都需要它。往大了说&#xff0c;构成一个工程师核心能力的东西&#xff0c;都在C里。跟面向对象型的语言相比&#xff0c;C是一门非常考验技术想象力的编程语言&#xff0c;因此学习起…