1、2048游戏规则
控制所有方块向同一个方向运动,两个相同数字方块撞在一起之后合并成为他们的和,每次操作之后会随机生成一个2或者4,最终得到一个“2048”的方块即胜利。
2、简介
此游戏是笔者大一初学c语言时某次周三上午的上机实验,程序主体多用if-else,while,for等关键字,适合c语言初学者阅读,若有疑问或bug或建议可评论指出。
3、编写前的一些辅助函数
(1)控制台光标的显示和隐藏
作用:美观。
#include <windows.h>
CONSOLE_CURSOR_INFO cci;
void hidecursor() {
static char bFirst = 1;
if (bFirst) {
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
bFirst = 0;
}
CONSOLE_CURSOR_INFO c = cci;
c.bVisible = 0;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &c);
}
void displaycursor() {
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
}
(2)指哪写哪的画板式删写
作用:绘制游戏棋盘和界面。
void GotoXY(short x, short y)
{
HANDLE app = GetStdHandle(STD_OUTPUT_HANDLE);
COORD t;
t.X = x * 2;
t.Y = y;
SetConsoleCursorPosition(app, t);
}
(3)输入的及时响应
作用:贴近游戏操作,区别于一般的控制台输入。
#include <conio.h>
ch = _getch();
(4)随机数
作用:随机生成2和4,以及在棋盘随机位置生成数字。
#include <time.h>
srand(clock());
n = rand();
4、程序主体
本程序流程为:初始界面->选择开始->进入棋盘->游戏ing->结束游戏
(1)首先进入游戏初始界面
a.展示
该部分功能为画出框图文字,用一个Exh()函数即可:
int main()
{
while (1)
{
Exh();
End();
}
}
void Exh()
{
hidecursor();
char ch;
Draw2(11, 80);
GotoXY(4, 5); printf("\t\t欢迎来到“2048”");
GotoXY(10, 10); printf("\t\t\t\t\t\t按任意键继续........");
ch = _getch();//给出输入
while (1)//进入初始界面的死循环
{
Draw2(11, 40);
GotoXY(4, 5); printf("\t\t1.开始");
GotoXY(5, 8); printf("\t\t0.退出");
ch = _getch();//给出输入
if (ch == '1')//登陆
Start();
else if (ch == '0' || ch == 0x1b)//输入为0,退出
break;
}
}
这个函数就放在main函数即可,可见在输入1或者0或者ESC时程序会跳出这个函数,否则一直卡在这个界面,此操作是为了防止恶性输入。
b、相关函数
首先是绘制出由****组成的框框函数Draw2(int weth, int leth),可自定义长宽。
void Draw2(int weth, int leth)
{
system("cls");//清屏
printf("\n\n\t");
//制图
for (int i = 0; i < weth + 1; i++)//行数
{
for (int j = 0; j < leth; j++)//长度
{
if (i == 0 || j == 0 || i == weth || j == leth - 1)//边界
printf("*");
else printf(" ");//中间空白
}
printf("\n\t");//换行
}
}
和游戏结束End()函数:
void End()
{
char ch;
Draw2(11, 80);
GotoXY(4, 5); printf("\t\t游戏结束");
GotoXY(10, 10); printf("\t\t\t\t\t按“Esc”键重新开始........");
while (1)
{
if (ch = _getch() == 0x1b)
break;
}
}
这里的重新开始并没有将棋盘数据清0,留作一个小bug供读者解决,亦可引申为 重新开始 和 继续游戏。
最后便是游戏主体函数Start()
(2)进入棋盘
void Start()
{
char ch;
bool end = false;
Draw1(n);
Ran(); Ran();
while (1)
{
ch = _getch();//给出输入
bool flag = false;
switch (ch)
{
case 'w':case 'W':flag = Func_U();
break;
case 'a':case 'A':flag = Func_L();
break;
case 's':case 'S':flag = Func_D();
break;
case 'd':case 'D':flag = Func_R();
break;
default:
break;
}
if(flag)
Ran();
if (IfEnd())
{
GotoXY(LeftB + 1, UpB + n * Dis + 1); printf("游戏结束,按“Esc”键重新开始");
while (1)
{
if (ch = _getch() == 0x1b)
{
end = true;
break;
}
}
}
if (ch == 0x1b || end) //Esc == 0x1b
break;
}
}
Ran()负责在随机位置生成随机的2或4。
Fun_c()负责响应输入WASD。
flag变量用于判断该次操作下棋盘是否有响应(数字是否移动,是否合并),若无响应应不生成新数字。
IfEnd()用于判断棋盘已满无法继续游戏。
a.Fun_c()
这里只展示左移,其他移动可简易修改,笔者考虑过用一个函数完成该功能,只需将需处理的棋盘翻转镜面等操作即可。
bool Func_L()
{
bool flag = false;
for (int i = 0; i < n; i++)
{ //Step1:每行的非0左移
int j = 0, k = 0, copy[Nmax] = { 0 };
for (j = 0; j < n; j++)
if(data[i][j] != 0)
copy[k++] = data[i][j];
//Step2:同项合并
for (j = 0; j < n - 1; j++)
if (copy[j] == copy[j + 1])
for (k = j; k < n; k++)
{
if (k == j)
copy[k + 1] = 0;
copy[k] = k == n - 1 ? 0 : k == j ? copy[k] * 2 : copy[k + 1];
}
//Step3:赋值
for (j = 0; j < n; j++)
{
if (data[i][j] != copy[j])
flag = true;
data[i][j] = copy[j];
}
}
return flag;
}
b.IfEnd()
优先级:有0 -----> 左右相邻相等 ------> 上下相邻相等
bool IfEnd()
{
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (data[i][j] == 0)
return false;
for (int i = 0; i < n; i++)
for (int j = 0; j < n - 1; j++)
if (data[i][j] == data[i][j + 1])
return false;
for (int i = 0; i < n; i++)
for (int j = 0; j < n - 1; j++)
if (data[j][i] == data[j + 1][i])
return false;
return true;
}
c.Ran()
随机位置生成2或4
void Ran()
{
int sum = 0, n_add = 0, n_sum = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (data[i][j] == 0)
sum++;
srand(clock());//产生随机数种子
if (sum) //矩阵空间有空则进入随机生成
{
n_sum = rand() % sum + 1;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
{
if (data[i][j] == 0)
n_add++;
if (n_add == n_sum && data[i][j] == 0)
data[i][j] = rand() % 10 < 5 ? 4 : 2;
}
}
Show();
}
d.Show()
void Show()
{
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
{
GotoXY(LeftB + j * Blank + 1, UpB + i * Dis + 1);
if (data[i][j] != 0)
printf("%4d", data[i][j]);
else
printf(" ");
}
}
将存在数组中的数据打印。
(3)程序可编程性
绘图的位置左空上空间隔空白都是宏定义,数据数组大小也可变,所以5*5的2048快乐不是梦
#define Nmax 10 //数组最大操作空间
#define LeftB 20 //左空
#define UpB 5 //上空
#define Blank 4 //间隔空白
#define Dis 3 //格子上下间距
CONSOLE_CURSOR_INFO cci; //隐藏光标
int n = 5; //数组实际空间
int data[Nmax][Nmax] = { 0 }; //游戏数据
5、可提升空间
此游戏为v0.1版本,可在以下几个方面将程序内容更丰富
(1)增加按键修改
(2)增加游戏操作介绍
(3)增加新游戏,继续游戏,重新开始等功能
(4)增加积分功能
(5)增加排行榜功能
(6)与排行榜相关的用户信息的登录注册功能
(7)棋盘数据记录,重现棋局功能。
(8)撤回恢复功能
(9)增加色块,合成声音等
6、代码
代码连接放在评论区。