前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >200行实现2048(c语言)

200行实现2048(c语言)

原创
作者头像
ys.h
修改2024-01-23 07:56:43
2691
修改2024-01-23 07:56:43
举报
文章被收录于专栏:小游戏小游戏

前言:

刚刚开始学习c语言和游戏开发,是个小白,如有大佬有意见和建议可以在评论区留言,请大佬们不吝赐教。

介绍:

此次实现的程序是一个经典的小游戏2048 , 想到实现的时候第一时间想的就是每次的上下左右的操作怎么实现的,这也是本程序的主要算法,还有随机算法比较次要,于是开始实现。

详解:

将程序分成两个大部分,一部分是UI界面(虽然很垃圾) , 一部分是游戏事件。

初始化Init()

代码语言:c++
复制
static bool gameing = false;
static int N = 4;// 默认4
static int map[55][55];
static int direction[4][2]{ -1 , 0 , 1 , 0 , 0 , -1 , 0,  1 };//上 下 左 右
static int directionMap[300]; //字符转化数字对应方向
static char buf[20][100] = { {"\033[0m"},{"\033[36m"}, {"\033[35m"}, {"\033[34m"}, {"\033[33m"}, {"\033[32m"}, {"\033[31m"}, {"\033[96m"}, {"\033[95m"}, {"\033[94m"}, {"\033[93m"}, {"\033[92m"}, {"\033[91m"}, {"\033[0m"}};
static int historySroce = 0;
static int nowSroce = 0;
static int nowShu = 0; //当前数字个数,如果达到 16 进行判断

static void s_Init() //地图初始化, 4 果子 , 3 墙 , 2 身 , 1 头 , 0 空
{
	nowShu = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			map[i][j] = 0;
		}
	}
	for (int i = 0; i < 2; i++)
		s_Rand();
}

UI界面(void UI())

UI界面实现了继续游戏的功能,在游戏事件以后,若在游戏结束之前按q键退出,gameing(bool)将会是true的,则可以拓展出继续游戏的功能。否则,则没有继续游戏,游戏结束时gameing则会变成false。若输入了不是菜单的选项,则输出"选项没有!!!" , 程序睡眠一秒。

代码语言:c++
复制
static void ClearPing()
{
	printf(" \033[2J");//清屏
	printf("\033[?25l");//隐藏光标
	printf("\033[0;0H");
}


static void UI()
{
	while(true)
	{
		clearPing();
		int xu = 1;
		printf("                                      历史最高:%d\n", historySroce);
		printf("---------------主菜单---------------\n");
		if (gameing)
			printf("---------------%d.继续游戏---------------\n", xu++);
		printf("---------------%d.新游戏-------------\n", xu++);
		printf("---------------%d.退出---------------\n", xu++);

		int op;
		scanf("%d", &op);
		if (xu == 4)
		{
			switch (op)
			{
			case 1:
			{
                //继续游戏直接进行游戏事件
				s_GameEvent();
			}
			break;
			case 2:
			{
				s_Init();//新游戏,要进行初始化
				s_GameEvent();//再启动游戏
			}
			break;
			case 3:
			{
				return;
			}
			break;
			default:
			{
				printf("选项没有!!!");
				Sleep(1000);
			}
			break;
			}
		}
		else if (xu == 3) {
			switch (op)
			{

			case 1:
			{
				s_Init();
				s_GameEvent();
			}
			break;
			case 2:
			{
				return;
			}
			break;
			default:
			{
				printf("选项没有!!!");
				Sleep(1000);
			}
			break;
			}
		}
		else {
			printf("bug!!\n");
		}
	}
}

游戏事件(GameEvent)

游戏事件中包含了4个函数

游戏输入只允许输入wsad , q的大小写字母,其他一律不反应,将wsad用一个hash反应出对应的0123来对Move进行操作。

然后展示map

再判断是否结束。

  1. 游戏移动(void Move(int f))随机生成数( void Rand()) 3. 游戏展示(viod DisplayGame()) 4. 判断游戏结束(CheckGameEnd())
代码语言:c++
复制
static void s_GameEvent()
{
	gameing = true;.
	while (true)
	{
		char c = _getche();
		if (c == 'w' || c == 'W' || c == 's' || c == 'S' || c == 'a' || c == 'A' || c == 'd' || c == 'D')
		{
			int nowDirection = directionMap[c];
			if (s_Move(nowDirection))
			{
				s_Rand();
			}
		}
		else if (c == 'q' || c == 'Q')
		{
			break;
		}
		s_DisplayGame();
		if (CheckGameEnd())
		{
			gameing = false;
			historySroce = max(historySroce, nowSroce);
			break;
		}
	}
	return;
}

游戏移动(void Move(int f))

根据f来判断操作。

例如,如果是f == 0 , 则是向上操作,得到相应的indXindY。从上往下遍历每个格子(左右方向不影响)进行Move1操作。

如果是f == 2, 则是向右操作,得到相应的indXindY。从左往右遍历每个格子(上下方向不影响)进行Move1操作。

Move1操作是将格子往得到的方向进行查找,找一个非0的数,找不到直接return,如果自身本来是0 , 则将自身和找到的非0的数交换,确认自己非0以后再找一次,看找到的第一个非0的数跟自己是否相同,相同则自身*2,找到的数赋0。

最后将所有的格子按顺序Move1完。

代码语言:c++
复制
static bool CheckRange(int x, int y)
{
	if (x >= N || x < 0 || y < 0 || y >= N)//在的地图范围之外则归零
		return false;
	return true;
}

static void s_Move1(int i, int j, int indX, int indY)
{
	int x = i, y = j;
	while (CheckRange(x + indX, y + indY))
	{
		x += indX, y += indY;
		if (map[x][y])break;
	}
	if (!CheckRange(x, y) || (x == i && y == j))
		return;

	if (!map[i][j])
	{
		map[i][j] = map[x][y];
		map[x][y] = 0;
	}

	x = i, y = j;
	while (CheckRange(x + indX, y + indY))
	{
		x += indX, y += indY;
		if (map[x][y])break;
	}
	if (!CheckRange(x, y) || (x == i && y == j))
		return;

	if (map[i][j] && map[x][y] == map[i][j])
	{
		map[i][j] += map[x][y];
		map[x][y] = 0;
		nowSroce += map[i][j];
		nowShu--;
	}
	else {
		if (!map[i][j])
		{
			map[i][j] = map[x][y];
			map[x][y] = 0;
		}
	}
}


static bool s_Move(int f)
{
	int indX = direction[f][0] * -1, indY = direction[f][1] * -1; //乘-1是为了得到与动作相反的方向,好进行Move1操作。
	if (f == 0)
	{
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < N; j++)
			{
				s_Move1(i, j, indX, indY);
			}
		}
	}
	else if (f == 1) {
		for (int i = N - 1; i >= 0; i--)
		{
			for (int j = 0; j < N; j++)
			{
				s_Move1(i, j, indX, indY);

			}
		}
	}
	else if (f == 2)
	{
		for (int j = 0; j < N; j++)
		{
			for (int i = 0; i < N; i++)
			{
				s_Move1(i, j, indX, indY);
			}
		}
	}
	else if (f == 3)
	{
		for (int j = N - 1; j >= 0; j--)
		{
			for (int i = 0; i < N; i++)
			{
				s_Move1(i, j, indX, indY);

			}
		}
	}
	return true;
}

随机生成数( void Rand())

R % 10 是为了将2,4生成的概率成分90% , 10%。temp则是生成的值。

用了一个rand()函数,随机生成一个数字,然后得到现在空的格子,用随机数 现在的空格子数 得到了在第几个空格子上放这次随机生成的数。

代码语言:c++
复制
static void s_Rand()
{
	int R = rand() % 10;
	int temp = 0;
	if (R == 0)
	{
		temp = 4;
	}
	else temp = 2;
	int nowEmpty = 16 - nowShu;
	if (nowEmpty == 0)
		return;
	int index = rand() % nowEmpty;
	int now = 0;
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if (map[i][j] == 0 && now++ == index)
			{
				map[i][j] = temp;
				nowShu++;
				return;
			}
		}
	}
}

游戏展示(viod DisplayGame())

这个没什么好说的,就是把地图打印出来,但是有一个清屏操作,可以使每次地图都在最上方。

代码语言:c++
复制
static int s_Calmi(int x)//一个小小的计算2的次方的函数
{
	int ans = 0; while (x) { x >>= 1; ans++; }  return ans;
}

static void s_DisplayGame()
{
	ClearPing();
	printf("                                      目前得分:%d\n", nowSroce);
	printf("                                      目前格子:%d\n", nowShu);
	printf("                                      按Q键退出\n");
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%s%d  \033[0m" , buf[s_Calmi(map[i][j])], map[i][j]);
		}
		printf("\n");
		printf("\n");

	}
}

判断游戏结束(bool CheckGameEnd())

这里有一个小优化,因为计算了现存的格子数,所以在每次判断的时候不必判断每个格子附件是否有相同的数,当格子数满的时候才进行for循环判断。(虽然每次判断也就16次,因为2048游戏大小就是4,qaq)

代码语言:c++
复制
static bool CheckGameEnd()
{
	if (16 != nowShu)
		return false;
	
	bool flag = 1;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			for (int k = 0; k < 4; k++)
			{
				int x = i + direction[k][0], y = j + direction[k][1];
				if (!CheckRange(x, y))continue;
				if (map[i][j] == map[x][y])
					flag = 0;
			}
		}
	}
	return flag;
	//todo
}

源代码

将game_2048改成main即可运行。

代码语言:c++
复制
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <random>
#include <Windows.h>
#include <conio.h>

static bool gameing = false;
static int N = 4;// 默认4
static int map[55][55];
static int direction[4][2]{ -1 , 0 , 1 , 0 , 0 , -1 , 0,  1 };//上 下 左 右
static int directionMap[300]; //字符转化数字对应方向
static char buf[20][100] = { {"\033[0m"},{"\033[36m"}, {"\033[35m"}, {"\033[34m"}, {"\033[33m"}, {"\033[32m"}, {"\033[31m"}, {"\033[96m"}, {"\033[95m"}, {"\033[94m"}, {"\033[93m"}, {"\033[92m"}, {"\033[91m"}, {"\033[0m"}};
static int historySroce = 0;
static int nowSroce = 0;
static int nowShu = 0; //当前数字个数,如果达到 16 进行判断
static void s_Rand()
{
	int R = rand() % 10;
	int temp = 0;
	if (R == 0)
	{
		temp = 4;
	}
	else temp = 2;
	int nowEmpty = 16 - nowShu;
	if (nowEmpty == 0)
		return;
	int index = rand() % nowEmpty;
	int now = 0;
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if (map[i][j] == 0 && now++ == index)
			{
				map[i][j] = temp;
				nowShu++;
				return;
			}
		}
	}
}
static void s_Init() //地图初始化, 4 果子 , 3 墙 , 2 身 , 1 头 , 0 空
{
	nowShu = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			map[i][j] = 0;
		}
	}
	for (int i = 0; i < 2; i++)
		s_Rand();
}



static void ClearPing()
{
	printf(" \033[2J");//清屏
	printf("\033[?25l");//隐藏光标
	printf("\033[0;0H");
}

static bool CheckRange(int x, int y)
{
	if (x >= N || x < 0 || y < 0 || y >= N)
		return false;
	return true;
}

static void s_Move1(int i, int j, int indX, int indY)
{
	int x = i, y = j;
	while (CheckRange(x + indX, y + indY))
	{
		x += indX, y += indY;
		if (map[x][y])break;
	}
	if (!CheckRange(x, y) || (x == i && y == j))
		return;

	if (!map[i][j])
	{
		map[i][j] = map[x][y];
		map[x][y] = 0;
	}

	x = i, y = j;
	while (CheckRange(x + indX, y + indY))
	{
		x += indX, y += indY;
		if (map[x][y])break;
	}
	if (!CheckRange(x, y) || (x == i && y == j))
		return;

	if (map[i][j] && map[x][y] == map[i][j])
	{
		map[i][j] += map[x][y];
		map[x][y] = 0;
		nowSroce += map[i][j];
		nowShu--;
	}
	else {
		if (!map[i][j])
		{
			map[i][j] = map[x][y];
			map[x][y] = 0;
		}
	}
}


static bool s_Move(int f)
{
	int indX = direction[f][0] * -1, indY = direction[f][1] * -1;
	if (f == 0)
	{
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < N; j++)
			{
				s_Move1(i, j, indX, indY);
			}
		}
	}
	else if (f == 1) {
		for (int i = N - 1; i >= 0; i--)
		{
			for (int j = 0; j < N; j++)
			{
				s_Move1(i, j, indX, indY);

			}
		}
	}
	else if (f == 2)
	{
		for (int j = 0; j < N; j++)
		{
			for (int i = 0; i < N; i++)
			{
				s_Move1(i, j, indX, indY);
			}
		}
	}
	else if (f == 3)
	{
		for (int j = N - 1; j >= 0; j--)
		{
			for (int i = 0; i < N; i++)
			{
				s_Move1(i, j, indX, indY);

			}
		}
	}
	return true;
}


static bool CheckGameEnd()
{
	if (16 != nowShu)
		return false;
	
	bool flag = 1;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			for (int k = 0; k < 4; k++)
			{
				int x = i + direction[k][0], y = j + direction[k][1];
				if (!CheckRange(x, y))continue;
				if (map[i][j] == map[x][y])
					flag = 0;
			}
		}
	}
	return flag;
	//todo
}


static int s_Calmi(int x)
{
	int ans = 0; while (x) { x >>= 1; ans++; }  return ans;
}

static void s_DisplayGame()
{
	ClearPing();
	printf("                                      目前得分:%d\n", nowSroce);
	printf("                                      目前格子:%d\n", nowShu);
	printf("                                      按Q键退出\n");
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%s%d  \033[0m" , buf[s_Calmi(map[i][j])], map[i][j]);
		}
		printf("\n");
		printf("\n");

	}
}



static void s_GameEvent()
{
	gameing = true;
	while (true)
	{
		char c = _getche();//不显示输入
		if (c == 'w' || c == 'W' || c == 's' || c == 'S' || c == 'a' || c == 'A' || c == 'd' || c == 'D')
		{
			int nowDirection = directionMap[c];
			if (s_Move(nowDirection))
			{
				s_Rand();
			}
		}
		else if (c == 'q' || c == 'Q')
		{
			break;
		}
		s_DisplayGame();
		if (CheckGameEnd())
		{
			gameing = false;
			historySroce = max(historySroce, nowSroce);
			break;
		}
	}
	return;
}

static void s_UI()
{
	while(true)
	{
		ClearPing();
		int xu = 1;
		printf("                                      历史最高:%d\n", historySroce);
		printf("---------------主菜单---------------\n");
		if (gameing)
			printf("---------------%d.继续游戏---------------\n", xu++);
		printf("---------------%d.新游戏-------------\n", xu++);
		printf("---------------%d.退出---------------\n", xu++);

		int op;
		scanf("%d", &op);
		if (xu == 4)
		{
			switch (op)
			{
			case 1:
			{
				s_GameEvent();
			}
			break;
			case 2:
			{
				s_Init();
				s_GameEvent();
			}
			break;
			case 3:
			{
				return;
			}
			break;
			default:
			{
				printf("选项没有!!!");
				Sleep(1000);
			}
			break;
			}
		}
		else if (xu == 3) {
			switch (op)
			{

			case 1:
			{
				s_Init();
				s_GameEvent();
			}
			break;
			case 2:
			{
				return;
			}
			break;
			default:
			{
				printf("选项没有!!!");
				Sleep(1000);
			}
			break;
			}
		}
		else {
			printf("bug!!\n");
		}
	}
}



int game_2048()
{
	directionMap['w'] = directionMap['W'] = 0;
	directionMap['s'] = directionMap['S'] = 1;
	directionMap['a'] = directionMap['A'] = 2;
	directionMap['d'] = directionMap['D'] = 3;
	srand(time(0));
	s_UI();



	return 0;
}

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 介绍:
  • 详解:
    • 初始化Init()
      • UI界面(void UI())
        • 游戏事件(GameEvent)
          • 游戏移动(void Move(int f))
            • 随机生成数( void Rand())
              • 游戏展示(viod DisplayGame())
                • 判断游戏结束(bool CheckGameEnd())
                • 源代码
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档