C语言实现2048游戏(控制台版)

Setiuo...原创休闲编程C语言小游戏大约 8 分钟

C语言实现2048游戏(控制台版)

问题描述

一个4X4的矩阵,开始随机产生两个数(2或4),接下来的每次操作都会再次产生一个数(2或4) 操作要求:按W,S,A,D进行上,下,左,右操作 操作结果:每次操作都会合并操作方向上相同的数,即将两个相同的数合并在一起产生一个新的数,该数为两个数的和,并将该行(列)上的数移动至 操作方向的顶端(保证数字原有顺序不变)

解决思路

定义一个二维数组用来存储4X4矩阵上的数字 开始游戏时先产生两个随机数,然后开始正常游戏操作 操作: 根据操作方向遍历每行(列),检查是否存在相同的数字 如果该行(列)存在挨着的(即两个数字中间没有其它数字隔开)相同的数字,将操作方向顶端的数字变为原来的2倍,并将被合并掉的数变为0 合并结束后将数字移动到操作方向顶端,如果顶端已经存在数,则按操作顺序排放至后方 操作结束后产生新的随机数

程序关键功能

产生随机数字 读取输入的操作方向 执行方向上数字的合并/移动操作 记录分数和合并出的最大数字 判断是否可以结束游戏 输出游戏界面

代码(详细注释)

见下方

测试

对程序进行方向上的操作,检查数字是否合并正确,移动正确,并对合并的数字作记录检查得分是否正确,合并出的最大数是否正确 当4X4的矩阵格子满后,检查是否存在可以合并的数字 如果存在,则进行非合并方向上的操作,检查游戏是否结束(正常应为不结束游戏);然后进行合并方向上的操作,检查游戏是否结束(正常应为不结束游戏) 如果不存在,则随意方向上进行一次操作,检查游戏是否结束(正常应为结束游戏)

游戏截图

代码

//Setiuo Email:751255159@qq.com
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>
#define Max(a, b) ((a > b) ? a : b)

typedef byte bool;
#define true 1
#define false 0

//Score为记录得分的变量,MaxNum为记录最大的方块数字的变量
int Score = 0, MaxNum = 2;

byte GetType(int Num) //根据数字大小返回一个颜色类型
{
	switch (Num)
	{
	case 2:
		return 13;
	case 4:
		return 1;
	case 8:
		return 2;
	case 16:
		return 3;
	case 32:
		return 4;
	case 64:
		return 5;
	case 128:
		return 6;
	case 256:
		return 7;
	case 512:
		return 8;
	case 1024:
		return 9;
	case 2048:
		return 10;
	case 4096:
		return 11;
	case 8192:
		return 12;
	default:
		return 14;
	}
}

//这个函数用于判断是否可以结束游戏,结束游戏的条件为:所有格子都有数字且不可以合并
//所以直接判断每两个挨着的数字是否可以合并即可
bool HaveIdentical(int Data[][5]) //判断是否存在可以合并的数字
{
	//判断横排是否存在
	for (byte i = 1; i <= 4; i++) //遍历每行
	{
		for (byte j = 2; j <= 4; j++)
		{
			if (Data[i][j] == Data[i][j - 1]) //存在挨着的且相同的数字
				return true;				  //可以合并,返回true
		}
	}

	//判断竖排是否存在
	for (byte i = 1; i <= 4; i++) //遍历每列
	{
		for (byte j = 2; j <= 4; j++)
		{
			if (Data[j][i] == Data[j - 1][i]) //存在挨着的且相同的数字
				return true;				  //可以合并,返回true
		}
	}

	return false; //不存在挨着的且相同的数字,返回假
}

//判断是否存在空的格子
bool HaveEmpty(int Data[][5])
{
	byte iNum = 0; //记录空格子数

	for (byte i = 1; i <= 4; i++) //遍历每行
	{
		for (byte j = 1; j <= 4; j++) //判断每行中的每一个格子
		{
			if (Data[i][j] == 0) //如果当前格子的数为0,即为存在空的格子
				iNum++;
		}
	}

	if (iNum == 0)	//如果不存在空的格子
		return false; //返回假

	return true; //否则返回真
}

//游戏结束,输出结果
void PrintResult()
{
	system("cls"); //清空控制台文字

	printf("========游戏结束========\n\n");
	printf("   您最终的得分是: %d\n", Score);
	printf("   最大方块的数是: %d\n\n", MaxNum);
	printf("========================\n\n");

	system("pause"); //防止控制台退出
}

//输出数字
void PrintNum(int Data[][5])
{
	HANDLE hOut;
	hOut = GetStdHandle(STD_OUTPUT_HANDLE); //取输入输出设备句柄

	SetConsoleTextAttribute(hOut, 0x0007); //设置颜色

	system("cls"); //清空控制台文字

	printf("========2048========\n");
	printf("食用方法:\n");
	printf("  w、s、a、d分别为上下左右\n");
	printf("  输入字符后按回车\n");
	printf("  当前得分:%d\n", Score);
	printf("====================\n\n");

	//遍历数组,输出界面格子和每个位置的数字
	for (byte i = 1; i <= 4; i++)
	{
		for (byte u = 1; u <= 4; u++)
			printf("—————");
		printf("\n|");

		for (byte u = 1; u <= 4; u++)
			printf("         |");
		printf("\n");

		for (byte j = 1; j <= 4; j++)
		{
			printf("|");
			byte iType = GetType(Data[i][j]);
			SetConsoleTextAttribute(hOut, iType);

			if (Data[i][j] == 0) //如果当前位置数字为0,则不输出数字
				printf("         ");
			else
				printf("%5d    ", Data[i][j]);

			SetConsoleTextAttribute(hOut, 0x0007);
		}
		printf("|\n|");

		for (byte u = 1; u <= 4; u++)
			printf("         |");
		printf("\n");
	}
	for (byte u = 1; u <= 4; u++)
		printf("—————");

	SetConsoleTextAttribute(hOut, 0x0007);
	printf("\n===================\n");
}

//空位置产生随机数
void RandNum(int Data[][5])
{
	//防止产生死循环,所以要判断是否存在空位置
	if (!HaveEmpty(Data))
		return;

	byte X, Y; //记录产生数字的位置

	do
	{
		X = rand() % 4 + 1;
		Y = rand() % 4 + 1;
	} while (Data[X][Y] != 0); //如果当前位置已经有数字了,则继续随机换位置

	int iType = rand() % 3;			   //产生随机数,0, 1, 2,用于判断是否产生数字4
	Data[X][Y] = (iType == 2) ? 4 : 2; //产生2的概率更大

	MaxNum = Max(MaxNum, Data[X][Y]); //刷新最大块数字
}

//按下了向上键,其它方向键同理
bool ChangeNum_Up(int Data[][5])
{
	bool CanDown = false; //记录是否可以移动方块,即如果方块全部在边缘位置,且没有相同的数字,则不能移动

	//合并数字操作
	for (byte i = 1; i <= 4; i++) //遍历每列
	{
		for (byte j = 1; j <= 3; j++) //从上向下寻找是否存在相同的数字
		{
			byte k = j + 1; //记录当前格的向下一个位置

			//如果当前格和下一个格的数字不一样且下一个位置的数字是0且当前位置的数字不是0,则循环寻找下一个位置
			while (Data[j][i] != Data[k][i] && k <= 3 && Data[j][i] != 0 && Data[k][i] == 0)
			{
				k++;
			}
			//如果当前位置的数字和下一个位置的数字一样而且不是0
			if (Data[j][i] == Data[k][i] && Data[j][i] != 0)
			{
				Score += Data[j][i]; //加分
				CanDown = true;		 //已经移动
				Data[j][i] *= 2;	 //当前格数字*2,即把两个数字合并
				Data[k][i] = 0;		 //数字已经合并,被合并的数字清0

				MaxNum = Max(MaxNum, Data[j][i]); //刷新最大值
			}
		}
	}

	for (byte i = 1; i <= 4; i++) //遍历每列
	{
		for (byte j = 1; j <= 3; j++) //从上向下寻找是否存在空位置
		{
			byte k = j + 1;

			while (Data[j][i] == 0 && k <= 4) //如果当前位置的数字是0且将下一位置的数赋值给当前位置后仍然是0,则继续寻找下一个位置
			{
				if (Data[k][i] != 0) //找到不为0的位置
				{
					CanDown = true;			 //已经移动
					Data[j][i] = Data[k][i]; //将下一位置的数赋值给当前位置
					Data[k][i] = 0;			 //下一位置的数清0
				}

				k++; //寻找下一个位置
			}
		}
	}

	return CanDown; //返回操作结果,即刚刚的操作是否合并或移动了格子
}

bool ChangeNum_Down(int Data[][5])
{
	bool CanDown = false;

	for (byte i = 1; i <= 4; i++)
	{
		for (byte j = 4; j >= 2; j--)
		{
			byte k = j - 1;
			while (Data[j][i] != Data[k][i] && k >= 2 && Data[j][i] != 0 && Data[k][i] == 0)
			{
				k--;
			}
			if (Data[j][i] == Data[k][i] && Data[j][i] != 0)
			{
				CanDown = true;
				Score += Data[j][i];
				Data[j][i] *= 2;
				Data[k][i] = 0;

				MaxNum = Max(MaxNum, Data[j][i]);
			}
		}
	}

	for (byte i = 1; i <= 4; i++)
	{
		for (byte j = 4; j >= 2; j--)
		{
			byte k = j - 1;

			while (Data[j][i] == 0 && k >= 1)
			{
				if (Data[k][i] != 0)
				{
					CanDown = true;
					Data[j][i] = Data[k][i];
					Data[k][i] = 0;
				}

				k--;
			}
		}
	}

	return CanDown;
}

bool ChangeNum_Left(int Data[][5])
{
	bool CanDown = false;

	for (byte i = 1; i <= 4; i++)
	{
		for (byte j = 1; j <= 3; j++)
		{
			byte k = j + 1;
			while (Data[i][j] != Data[i][k] && k <= 3 && Data[i][j] != 0 && Data[i][k] == 0)
			{
				k++;
			}
			if (Data[i][j] == Data[i][k] && Data[i][j] != 0)
			{
				CanDown = true;
				Score += Data[i][j];
				Data[i][j] *= 2;
				Data[i][k] = 0;

				MaxNum = Max(MaxNum, Data[i][j]);
			}
		}
	}

	for (byte i = 1; i <= 4; i++)
	{
		for (byte j = 1; j <= 3; j++)
		{
			byte k = j + 1;

			while (Data[i][j] == 0 && k <= 4)
			{
				if (Data[i][k] != 0)
				{
					CanDown = true;
					Data[i][j] = Data[i][k];
					Data[i][k] = 0;
				}

				k++;
			}
		}
	}

	return CanDown;
}

bool ChangeNum_Right(int Data[][5])
{
	bool CanDown = false;

	for (byte i = 1; i <= 4; i++)
	{
		for (byte j = 4; j >= 2; j--)
		{
			byte k = j - 1;
			while (Data[i][j] != Data[i][k] && k >= 2 && Data[i][j] != 0 && Data[i][k] == 0)
			{
				k--;
			}
			if (Data[i][j] == Data[i][k] && Data[i][j] != 0)
			{
				CanDown = true;
				Score += Data[i][j];
				Data[i][j] *= 2;
				Data[i][k] = 0;

				MaxNum = Max(MaxNum, Data[i][j]);
			}
		}
	}

	for (byte i = 1; i <= 4; i++)
	{
		for (byte j = 4; j >= 2; j--)
		{
			byte k = j - 1;

			while (Data[i][j] == 0 && k >= 1)
			{
				if (Data[i][k] != 0)
				{
					CanDown = true;
					Data[i][j] = Data[i][k];
					Data[i][k] = 0;
				}

				k--;
			}
		}
	}

	return CanDown;
}
//按下移动键
bool DownKey(int Data[][5], char KEY)
{
	bool CanDown = false; //初始化变量,赋值为没有成功进行操作

	//判断按下的是哪个键并进行移动
	switch (KEY)
	{
	case 'w':
	{
		CanDown = ChangeNum_Up(Data);
		break;
	}
	case 's':
	{
		CanDown = ChangeNum_Down(Data);
		break;
	}
	case 'a':
	{
		CanDown = ChangeNum_Left(Data);
		break;
	}
	case 'd':
	{
		CanDown = ChangeNum_Right(Data);
		break;
	}
	}
	return CanDown; //返回操作结果
}

int main()
{
	int Data[5][5] = {};			 //记录4X4矩阵中的数字
	srand((unsigned int)time(NULL)); //利用系统时间改变种子值,用来产生随机数

	//开局先产生两个随机数
	RandNum(Data);
	RandNum(Data);
	//输出数字界面
	PrintNum(Data);

	while (true)
	{
		char KEY = _getch();; //取得按下的键

		if (KEY != 'w' && KEY != 's' && KEY != 'a' && KEY != 'd') //如果输入的字符不合法,则跳出本次操作
			continue;

		if (DownKey(Data, KEY)) //如果操作成功,则产生随机数
			RandNum(Data);
		else //如果没有操作成功
		{
			if (!HaveIdentical(Data) && !HaveEmpty(Data)) //判断是否存在空的格子且存在可以合并的数字,如果没有,则跳出循环结束游戏
			{
				break;
			}
		}

		PrintNum(Data); //输出数字界面
	}

	PrintResult(); //输出得分结果

	return 0;
}

如果代码有问题欢迎指正

你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.7