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. netcore项目中使用 SpringCloudConfig 和apollo做配置中心

    版权所有,转载请注明出处 https://www.cnblogs.com/netqq/p/14251403.html 一.使用apollo作为配置中心 首先apollo 项目简介和安装请自行百度,本文 ...

  2. TensorFlow 基础概念

    初识TensorFlow,看了几天教程后有些无聊,决定写些东西,来夯实一下基础,提供些前进动力. 一.Session.run()和Tensor.eval()的区别: 最主要的区别就是可以使用sess. ...

  3. 【Dart】语言概述

    // 导入(import) // 导入核心库 //导入外部库 import 'package:test_api/test_api.dart'; // 导入文件 //import 'path/test. ...

  4. 基于 MPI 的快速排序算法的实现

    完整代码: #include <iostream> #include <cstdlib> #include <ctime> #include <algorit ...

  5. 编译安装 nginx -1.14.2

    编译安装 nginx -1.14.2 1 ) 下载nginx-1.14.2 源码包: wget http://nginx.org/download/nginx-1.14.2.tar.gz 2 ) 编译 ...

  6. 没搞清楚网络I/O模型?那怎么入门Netty

    微信搜索[阿丸笔记],关注Java/MySQL/中间件各系列原创实战笔记,干货满满. 本文是Netty系列笔记第二篇 Netty是网络应用框架,所以从最本质的角度来看,是对网络I/O模型的封装使用. ...

  7. sa-token 之权限验证

    权限验证 核心思想 所谓权限验证,验证的核心就是当前账号是否拥有一个权限码 有:就让你通过.没有:那么禁止访问 再往底了说,就是每个账号都会拥有一个权限码集合,我来验证这个集合中是否包括我需要检测的那 ...

  8. mysqldump 的-T参数

    /usr/local/mysql/bin/mysqldump -uroot -T /tmp lina xuehao 把lina数据库中的xuehao表在tmp目录下备份出来两个文件,一个是纯数据.tx ...

  9. vxfs(Veritas File System)扩充目录大小

    1.新增加一个磁盘并初始化 # vxdisk list # vxdisksetup -i 3pardata0_22 2.将新增加的磁盘合并到磁盘组中 # vxdg -g testdg01 adddis ...

  10. springAOP的概述及使用

    Spring AOP SpringAOP是Spring中非常重要的功能模块之一,该模块提供了面向切面编程,在事务处理,日志记录,安全控制等操作中广泛使用. SpringAOP的基本概念 AOP的概念 ...