2048

一、设计思路

1、游戏规则

想要制作游戏,首先需要了解游戏的规则,下面就来介绍2048的游戏规则

2048游戏共有16个格子,初始时初始数字由2或者4构成。

  • 手指向一个方向滑动,所有格子会向那个方向运动。

  • 相同数字的两个格子,相撞时数字会相加。

  • 每次滑动时,空白处会随机刷新出一个数字的格子。

  • 当界面不可运动时(当界面全部被数字填满时),游戏结束;当界面中最大数字是2048时,游戏胜利。

2、思路

了解了游戏规则接下来就好办多了,规则中最重要的是:向某个方向滑动格子就会向哪儿滑,并且相撞的数字相加

根据最重要的规则,我偷了一个懒,想到了以下策略

  • 首先完成向左滑动功能
  • 向右滑动的功能可以借助向左滑动的功能
    • 将游戏界面逆时针旋转180°
    • 向左滑动
    • 还原界面,将界面逆时针旋转180°,归回原位
  • 向上滑动的功能可以借助向左滑动的功能
    • 将游戏界面逆时针旋转270°
    • 向左滑动
    • 还原界面,将界面逆时针旋转90°
  • 向下滑动的功能可以借助向左滑动的功能
    • 将游戏界面逆时针旋转90°
    • 向左滑动
    • 还原界面,将界面逆时针旋转270°

通过以上分析,可以得知向左滑动的功能是最重要,其他方向都可以借助它来完成,因此接下来将分析怎么完成向左滑动的功能

  • 游戏中说共有4行4列16个格子,有4行我们就可以采用循环,一次合并一行,总共4个循环,所以向左滑动又可以分解为向左合并一行
  • 向左合并,首先不管有没有相同的数字,都要将所有的数字紧凑到最左边,两个数字中间不能有空
  • 最后采用合并算法,将一行中相同的数字合并

至此这个游戏的基本就可以设计好了,下面开始实现

二、代码实现

1、存储结构

根据游戏规则很容易想到使用二维数组来存储游戏数据,如下

int data[4][4];

2、初始化游戏数据

规则说初始时初始数字由2或者4构成,这里偷个懒,设置所有的初始数字都是2,接下来要解决的就是初始数字的位置坐标,所以获取随机左边的函数就行。

获取随机数可以使用以下函数

//获取随机数
void randArray(int a[], int n) {
for (int i = 0; i < n; i++) {//产生随机坐标
srand((unsigned)time(NULL) + rand() + i);
a[i] = rand() % 4;
}
}

要产生不一样的随机数,首先需要置不一样的随机数种子,置随机数种子可以采用srand()函数,然后我们把当前时间作为参数也就是srand((unsigned)time(NULL)),但是在后期时16个坐标产生相同的随机坐标的几率非常大,而计算机的速度又非常快,相同的随机种子产生的随机数序列是一样的,这样随机性不高,很容易产生相同的坐标,所以参数还要加上rand()+i,也就是srand((unsigned)time(NULL) + rand() + i),不信的同学可以试试按之前的写法游戏玩到后期每产生一块新的坐标都需要不少时间。

接着按以下写法就可以产生两个不一样的随机坐标

//初始化数据,initNum初始数字
void init(int data[4][4], int initNum) {
int random[4];
randArray(random, 4);
if (random[0] == random[2] && random[1] == random[3]) {
init(data, 2);//若坐标相同,则重新生成坐标
} else {
data[random[0]][random[1]] = initNum;
data[random[2]][random[3]] = initNum;
}
}

3、向左合并

按照之前的思路,首先要将所有的方块移到最右侧且中间不能有空,可以通过如下代码实现

//紧凑数组(向左就凑)
void compact(int data[4]) {
int i, j = 0;
for (i = 0; i < 4; i++) {
if (data[i] != 0) {
data[j] = data[i];
j++;
}
}
for (; j < 4; j++)data[j] = 0;
}

接着完成向左合并函数

//向左合并
void mergerLeft(int data[4][4]) {
for (int i = 0; i < 4; i++) {
compact(data[i]);
for (int j = 0; j < 3; j++) {
if (data[i][j] == data[i][j + 1]) {
data[i][j] = 2 * data[i][j];
data[i][j + 1] = 0;
compact(data[i]);
}
}
}
}

4、其他方向合并

虽然有了向左合并的函数,但是怎么模拟出旋转界面呢,这里只要编写一个可以旋转矩阵的函数就行,如下

//拷贝数组
void copyArray(int data[4][4], int src[4][4]) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
data[i][j] = src[i][j];
}
}
} //旋转矩阵,count旋转次数
void rotateMatrix(int data[4][4], int count) {
int temp[4][4];
copyArray(temp, data);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
data[i][j] = temp[4 - j - 1][i];
}
}
if (count > 1)rotateMatrix(data, count-1);
}

有了旋转矩阵的函数,那么其他方向就很好解决了

//向右合并
void mergerRight(int data[4][4]) {
rotateMatrix(data, 2);
mergerLeft(data);
rotateMatrix(data, 2);
} //向上合并
void mergerUp(int data[4][4]) {
rotateMatrix(data, 3);
mergerLeft(data);
rotateMatrix(data, 1);
} //向下合并
void mergerDown(int data[4][4]) {
rotateMatrix(data, 1);
mergerLeft(data);
rotateMatrix(data, 3);
}

5、产生新的方块

通过之前的函数很容易编写出来

//生成新数字
void newNum(int data[4][4], int initNum) {
int random[2];
while (1) {
int flag = 1;
randArray(random, 2);//随即产生坐标
for (int i = 0; i < 4; i++) {//检测坐标是否重复
for (int j = 0; j < 4; j++) {
if (data[i][j] != 0 && i == random[0] && j == random[1]) {
flag = 0;
}
}
}
if (flag)break;
}
data[random[0]][random[1]] = initNum;
}

6、源代码

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<conio.h> //函数声明
void randArray(int a[], int n);
void rotateMatrix(int data[4][4], int count);
void copyArray(int data[4][4], int src[4][4]); //全局变量定义
int data[4][4]; //初始化数据
void init(int data[4][4], int initNum) {
int random[4];
randArray(random, 4);
if (random[0] == random[2] && random[1] == random[3]) {
init(data, 2);//若坐标相同,则重新生成坐标
} else {
data[random[0]][random[1]] = initNum;
data[random[2]][random[3]] = initNum;
}
} //紧凑数组(向左就凑)
void compact(int data[4]) {
int i, j = 0;
for (i = 0; i < 4; i++) {
if (data[i] != 0) {
data[j] = data[i];
j++;
}
}
for (; j < 4; j++)data[j] = 0;
} //向左合并
void mergerLeft(int data[4][4]) {
for (int i = 0; i < 4; i++) {
compact(data[i]);
for (int j = 0; j < 3; j++) {
if (data[i][j] == data[i][j + 1]) {
data[i][j] = 2 * data[i][j];
data[i][j + 1] = 0;
compact(data[i]);
}
}
}
} //向右合并
void mergerRight(int data[4][4]) {
rotateMatrix(data, 2);
mergerLeft(data);
rotateMatrix(data, 2);
} //向上合并
void mergerUp(int data[4][4]) {
rotateMatrix(data, 3);
mergerLeft(data);
rotateMatrix(data, 1);
} //向下合并
void mergerDown(int data[4][4]) {
rotateMatrix(data, 1);
mergerLeft(data);
rotateMatrix(data, 3);
} //生成新数字
void newNum(int data[4][4], int initNum) {
int random[2];
while (1) {
int flag = 1;
randArray(random, 2);//随即产生坐标
for (int i = 0; i < 4; i++) {//检测坐标是否重复
for (int j = 0; j < 4; j++) {
if (data[i][j] != 0 && i == random[0] && j == random[1]) {
flag = 0;
}
}
}
if (flag)break;
}
data[random[0]][random[1]] = initNum;
} //获取随机数
void randArray(int a[], int n) {
for (int i = 0; i < n; i++) {//产生随机坐标
srand((unsigned)time(NULL) + rand() + i);
a[i] = rand() % 4;
}
} //显示界面
void show(int data[4][4]) {
printf("\n\t2048小游戏");
for (int i = 0; i < 4; i++) {
printf("\n|-----|-----|-----|-----|\n|");
for (int j = 0; j < 4; j++) {
if (data[i][j] == 0) {
printf("%5c|", '*');
} else {
printf("%5d|", data[i][j]);
}
}
}
printf("\n|-----|-----|-----|-----|\n");
} //拷贝数组
void copyArray(int data[4][4], int src[4][4]) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
data[i][j] = src[i][j];
}
}
} //旋转矩阵
void rotateMatrix(int data[4][4], int count) {
int temp[4][4];
copyArray(temp, data);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
data[i][j] = temp[4 - j - 1][i];
}
}
if (count > 1)rotateMatrix(data, count-1);
} //保存文件
void saveFile() {
FILE* f = fopen("2048.dat", "w");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
fprintf(f, "%d ", data[i][j]);
}
fprintf(f, "\n");
}
fclose(f);
} //读取文件
void readFile() {
FILE* f = fopen("2048.dat", "r");
if (f == NULL) {
printf("\n无游戏存档,开始新游戏\n");
init(data, 2);
return;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
fscanf(f, "%d ", &data[i][j]);
}
fscanf(f, "\n");
}
fclose(f);
} int main() {
char ch;//方向 printf("1、新游戏\n");
printf("2、继续游戏\n");
ch = getch();
if(ch == '1'){
init(data, 2);
} else {
readFile();
} while (1) {
show(data);
ch = getch();
switch (ch) {
case 72://上
mergerUp(data);
newNum(data, 2);
break;
case 77://右
mergerRight(data);
newNum(data, 2);
break;
case 80://下
mergerDown(data);
newNum(data, 2);
break;
case 75://左
mergerLeft(data);
newNum(data, 2);
break;
case 27://ESC
saveFile();
return 0;
}
saveFile();
system("cls");
}
return 0;
}

7、实例演示

三、问题

这个程序当初只是为了帮助同学妹妹完成一下c语言作业,所以偷了个懒,有部分功能都没完成,比如判断2048游戏结束的功能等

C语言实现2048小游戏的更多相关文章

  1. 2048小游戏代码解析 C语言版

    2048小游戏,也算是风靡一时的益智游戏.其背后实现的逻辑比较简单,代码量不算多,而且趣味性强,适合作为有语言基础的童鞋来加强编程训练.本篇分析2048小游戏的C语言实现代码. 前言 游戏截图:  游 ...

  2. c语言----<项目>_小游戏<2048>

    2048 小游戏 主要是针对逻辑思维的一个训练. 主要学习方面:1.随机数产生的概率.2.行与列在进行移动的时候几种情况.3.MessageBox的使用 #include <iostream&g ...

  3. 2048小游戏4X4C语言

    */ #include<stdio.h> #include<stdlib.h> #include<conio.h> #include<time.h> v ...

  4. Swift实战之2048小游戏

    上周在图书馆借了一本Swift语言实战入门,入个门玩一玩^_^正好这本书的后面有一个2048小游戏的实例,笔者跟着实战了一把. 差不多一周的时间,到今天,游戏的基本功能已基本实现,细节我已不打算继续完 ...

  5. 如何在CentOS上安装一个2048小游戏

    如何在centos上安装一个2048小游戏 最近在学习CentOS系统,就琢磨着玩点什么,然后我看到有人在玩2048小游戏,所有我就在想,为啥不装一个2048小游戏搞一下嘞,于是乎,我就开始工作啦 由 ...

  6. jQuery实践-网页版2048小游戏

    ▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...

  7. C# 开发2048小游戏

    这应该是几个月前,闲的手痒,敲了一上午代码搞出来的,随之就把它丢弃了,当时让别人玩过,提过几条更改建议,但是时至今日,我也没有进行过优化和更改(本人只会作案,不会收场,嘎嘎),下面的建议要给代码爱好的 ...

  8. js、jQuery实现2048小游戏

    2048小游戏 一.游戏简介:  2048是一款休闲益智类的数字叠加小游戏 二. 游戏玩法: 在4*4的16宫格中,您可以选择上.下.左.右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合 ...

  9. 用js实现2048小游戏

    用js实现2048小游戏 笔记仓库:https://github.com/nnngu/LearningNotes 1.游戏简介 2048是一款休闲益智类的数字叠加小游戏.(文末给出源代码和演示地址) ...

随机推荐

  1. 探索 .NET团队对API的设计流程

    在这篇文章中,我想介绍一些我觉得非常有趣的东西,.NET 团队是如何设计API的? 我们先来看下.NET团队面临的有哪些挑战,您正在设计一套API库,每天有数百万的开发人员在使用这些库,它们在世界各地 ...

  2. 【对线面试官】Java 反射&&动态代理

    // 抽象类,定义泛型<T> public abstract class BaseDao<T> { public BaseDao(){ Class clazz = this.g ...

  3. Flutter 布局类组件:弹性布局(Flex)

    前言 弹性布局允许子组件按照一定比例来分配父容器空间,Flutter中的弹性布局主要通过Flex和Expanded来配合实现. Flex Flex组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方 ...

  4. Linux复制某个目录下结构

    Linux复制某个目录下结构 ​结合tree命令把当前目录下的文件夹路径存储到document.txt文件,然后再使用mkdir命令把document.txt文件下的目录输入创建: tree -fid ...

  5. 【Linux】ssh反映特别慢,但是网络没有问题的时怎么办

    用crt连接服务器的时候,感觉很久才有反映,大约持续2秒以上,这种情况下,是解析的问题 这里有一个方法可以优化ssh cd /etc/ssh/ cp sshd_config sshd_config.b ...

  6. kubernets之pod的标签拓展

    一 标签的拓展使用 1.1 标签的作用范围不仅仅适用于pod对node以及其他类的大部分资源同样适用 k label node node01 gpu=true k是kubectl的别名形式 同样对于n ...

  7. ctfhub技能树—信息泄露—备份文件下载—bak文件

    打开靶机 查看页面信息 继续使用dirsearch进行扫描 python3 dirsearch.py -u http://challenge-d4234042e1d43e96.sandbox.ctfh ...

  8. Kubernetes 开船记-脚踏两只船:用 master 服务器镜像克隆出新集群

    自从2020年2月23日 园子全站登船 之后,我们一边感叹"不上船不知道,一上船吓一跳" -- kubernetes 比 docker swarm 强大太多,一边有一个杞人忧天的担 ...

  9. 【Android】编译报错 Annotation processors must be explicitly declared now 解决方案

    问题 在网上下载一个demo,因为版本久远,里面添加了本地 Butter Knife 的jar包,在编译时报错 Annotation processors must be explicitly dec ...

  10. uni-app开发经验分享十九: uni-app对接微信小程序直播

    uni-app对接微信小程序直播 1.登录微信小程序后台-点击>设置->第三方设置->添加直播插件 2.添加直播组件后->点击<详情>      记录这两个参数直播 ...