C语言实现2048小游戏
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小游戏的更多相关文章
- 2048小游戏代码解析 C语言版
2048小游戏,也算是风靡一时的益智游戏.其背后实现的逻辑比较简单,代码量不算多,而且趣味性强,适合作为有语言基础的童鞋来加强编程训练.本篇分析2048小游戏的C语言实现代码. 前言 游戏截图: 游 ...
- c语言----<项目>_小游戏<2048>
2048 小游戏 主要是针对逻辑思维的一个训练. 主要学习方面:1.随机数产生的概率.2.行与列在进行移动的时候几种情况.3.MessageBox的使用 #include <iostream&g ...
- 2048小游戏4X4C语言
*/ #include<stdio.h> #include<stdlib.h> #include<conio.h> #include<time.h> v ...
- Swift实战之2048小游戏
上周在图书馆借了一本Swift语言实战入门,入个门玩一玩^_^正好这本书的后面有一个2048小游戏的实例,笔者跟着实战了一把. 差不多一周的时间,到今天,游戏的基本功能已基本实现,细节我已不打算继续完 ...
- 如何在CentOS上安装一个2048小游戏
如何在centos上安装一个2048小游戏 最近在学习CentOS系统,就琢磨着玩点什么,然后我看到有人在玩2048小游戏,所有我就在想,为啥不装一个2048小游戏搞一下嘞,于是乎,我就开始工作啦 由 ...
- jQuery实践-网页版2048小游戏
▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...
- C# 开发2048小游戏
这应该是几个月前,闲的手痒,敲了一上午代码搞出来的,随之就把它丢弃了,当时让别人玩过,提过几条更改建议,但是时至今日,我也没有进行过优化和更改(本人只会作案,不会收场,嘎嘎),下面的建议要给代码爱好的 ...
- js、jQuery实现2048小游戏
2048小游戏 一.游戏简介: 2048是一款休闲益智类的数字叠加小游戏 二. 游戏玩法: 在4*4的16宫格中,您可以选择上.下.左.右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合 ...
- 用js实现2048小游戏
用js实现2048小游戏 笔记仓库:https://github.com/nnngu/LearningNotes 1.游戏简介 2048是一款休闲益智类的数字叠加小游戏.(文末给出源代码和演示地址) ...
随机推荐
- post请求下载文件,获取Content-Disposition文件名
1.post下载文件代码,doload方法 import request from "../../src/utils/request"; import { API } from & ...
- Liunx运维(十二)-Liunx系统常用内置命令
文档目录: 一.Liunx内置命令概述 二.LIunx常用内置命令实例 1.help查看内置命令帮助2.查看内置命令使用方法3.":" 占位符4. "." 与s ...
- #2020征文-开发板# 用鸿蒙开发AI应用(二)系统篇
目录: 前言 安装虚拟机 安装 Ubuntu 设置共享文件夹 前言上回说到,我们在一块 HarmonyOS HiSpark AI Camera 开发板,并将其硬件做了一下解读和组装.要在其上编译鸿蒙系 ...
- Laya 踩坑日记-人物模型穿模,模型显示不正常
最近做游戏,人物要跑到很远的位置,z轴距离大概有20000个单位,然后就发现一个bug,到远处人物模型穿了,而且没办法改,这就尴尬了 Z轴对应值 0 100000 100000 当距离零点 ...
- python学习笔记 | strftime()格式化输出时间
time模块 import time t = time.strftime("%Y-%m-%d %H:%M:%S") print(t) datetime模块 import datet ...
- 【Oracle】什么是DRM,怎么关闭
DRM 分析及案例讲解 什么是DRM DRM(Dynamic Resource management)是oracle10.10.2里面推出来的一个新特性,一直到现在最新的12cR1,都存在,且bug非 ...
- HTTP协议相关知识整理:
http协议简介 超文本传输协议:是一种用于分布式.协作式和超媒体信息系统的应用层协议. 一次请求一次响应之后断开连接(无状态,短连接) 分析http请求信息格式 http工作原理 以下是 HTTP ...
- PCB导线长宽与电源压降
为了计算PCB中电源线走线后的压降,需要知道PCB中使用的铜的电阻率, PCB板中的铜是直接贴上去的铜箔,因此可以当成纯铜(我问了PCB打样的厂家他们的铜的电阻率,但是他们给我说不知道,所以干脆就当成 ...
- 控制tomcat日志文件的输出到catalina.out
在catalina.sh中直接把下面的内容注释掉即可:
- vue2.0、vue3.0不同之处
一.响应式赋值操作不同 Vue2.0 1.通过data返回对象做相应: 2.对复杂的对象或数组下的属性等深层次的改变需要通过$set的方式. Vue3.0 1.ref实现简单的实现响应,通过value ...