控制台小游戏2048-C++实现

前言:作者刚学了顺序容器和迭代器,想着做个项目练习和巩固,于是就有了这篇文章。本文会有刻意使用顺序容器和迭代器的代码,希望大家多多包涵。

1.头文件包含

#include<iostream>
#include<conio.h>//_getch()函数
#include<array>//定义了顺序容器array的头文件
#include<ctime>//time()函数
#include<iomanip>//setw()函数
using namespace std;

2.定义OC2048类封装变量和成员函数

class OC2048 {
public:	
	void GameStart() ;//主界面函数,作为OC2048类与外界唯一的接口
private:
	array<array<unsigned short, 4>, 4>mine;//游戏地图
	array<unsigned short, 4>support;//移动辅助数组,用于暂时存放地图的一行或一列
	unsigned short menu() ;//游戏主菜单界面
	void updateSupport(array<unsigned short, 4>&x) ;//冒泡算法更新地图
	int moveMine(unsigned short x) ;//移动合并地图上的数字方块
	void ShowMine() ;//显示地图的函数
	void init() ;//初始化地图,该函数仅能被构造函数调用
	bool available() ;//检测是否无法移动,用来判断游戏是否结束
	void create() ;//刷新方块的函数
	unsigned point() ;//计分器
};

3.具体功能的完善

为了方便调试,我们先注释掉OC2048类中的private标签,同时优先定义显示地图的函数void ShowMine(),用来获取地图信息。

	//显示地图
	void ShowMine() {
		unsigned short count;
		cout << "\n+";
		for (count = 0; count < 20; count++)
			cout.put('-');
		cout << '+' << endl;
		for (auto i = mine.begin(); i != mine.end(); i++) {
			cout.put('|');
			for (count = 0; count < 20; count++)cout.put(32);
			cout << "|\n|";
			for (auto j = i->begin(); j != i->end(); j++) {
				if (*j)cout << setw(4) << *j << ' ';
				else cout << "     ";
			}
			cout << '|' << endl;
		}
		cout.put('|');
		for (count = 0; count < 20; count++)cout.put(32);
		cout << "|\n+";
		for (count = 0; count < 20; count++)cout.put('-');
		cout << '+' << endl << "得分:" << point();
	}

然后我们编写一个初始化地图的函数init()。

	//初始化地图
	void init() {
		for (auto i = mine.begin(); i != mine.end(); i++)
			for (auto j = i->begin(); j != i->end(); j++)
				*j = 0;
		unsigned char i, j;
		while (true) {
			i = rand() % 4; j = rand() % 4;
			if (!mine[i][j])break;
		}
		mine[i][j] = 2;
		ShowMine();
	}

2048游戏中,向一个方向移动合并地图上的数字方块,相当于移动合并四次一行或一列的元素,我们通过冒泡算法来实现更新一行或一列元素的操作。

	//冒泡更新地图
	void updateSupport(array<unsigned short, 4>&x) {
		unsigned short flag = 0;
		for (auto it = x.begin(); it != x.end(); it++)
			if (*it) { flag = 1; break; }
		if (0 == flag)return;
		array<unsigned short, 4>::iterator last = x.end() - 1;
		for (auto i = x.begin(); i != last; i++)
			if (0 == *i) {
				auto j = i + 1;
				for (; j != x.end(); j++)if (*j)break;
				if (x.end() == j)break;
				*i = *j; *j = 0;
			}
		do {
			flag = 0;
			for (auto i = x.begin(); i != last; i++)
				if (*i == i[1] && *i) {
					*i *= 2;
					auto j = i + 1;
					for (; j != last && j[1]; j++)
						*j = j[1];
					*j = 0;
					flag = 1;
				}
		} while (flag);
	}

特别需要注意的是,array和传统数组不同,array是一个类模板,如果参数不声明为引用,则值传递不会改变传入的参数值,作者编写程序的过程中就犯了这个错误。

调试好updataSupport()函数,我们通过另一个函数moveMine()连续调用四次updataSupport()函数,来实现对一个方向上地图数字方块的更新。

	//移动合并方块
	int moveMine(unsigned short x) {
		unsigned short count;
		int moveOrNot = 0;
		switch (x) {
		case 0://↑
			for (count = 0; count < 4; count++) {
				auto i = support.begin();
				auto j = mine.begin();
				for (; i != support.end(); i++, j++)
					*i = (*j)[count];
				updateSupport(support);
				for (i = support.begin(), j = mine.begin(); i != support.end(); i++, j++)
					if ((*j)[count] != *i) {
						moveOrNot++;
						(*j)[count] = *i;
					}
			}break;
		case 1://↓
			for (count = 0; count < 4L; count++) {
				auto i = support.begin();
				auto j = mine.rbegin();
				for (; i != support.end(); i++, j++)
					*i = (*j)[count];
				updateSupport(support);
				for (i = support.begin(), j = mine.rbegin(); i != support.end(); i++, j++)
					if ((*j)[count] != *i) {
						moveOrNot++;
						(*j)[count] = *i;
					}
			}break;
		case 2://←
			for (count = 0; count < 4; count++) {
				auto i = support.begin();
				auto j = mine[count].begin();
				for (; i != support.end(); i++, j++)
					*i = *j;
				updateSupport(support);
				for (i = support.begin(), j = mine[count].begin(); i != support.end(); i++, j++)
					if (*j != *i) {
						moveOrNot++;
						*j = *i;
					}
			}break;
		case 3://→
			for (count = 0; count < 4; count++) {
				auto i = support.begin();
				auto j = mine[count].rbegin();
				for (; i != support.end(); i++, j++)
					*i = *j;
				updateSupport(support);
				for (i = support.begin(), j = mine[count].rbegin(); i != support.end(); i++, j++)
					if (*j != *i) {
						moveOrNot++;
						*j = *i;
					}
			}break;
		}
		return moveOrNot;
	}

这个移动合并方块的函数moveMine()具有返回值,如果执行了至少一次移动操作,则返回一个非零值,否则返回零值。需要注意的是,当地图充满数字方块时,在某个方向上可能无法再移动数字方块,所以需要一个检测是否能继续移动的函数available()。

	//检测是否无法移动
	bool available() {
		for (auto i = mine.begin(); i != mine.end(); i++)
			for (auto j = i->begin(); j != i->end(); j++)
				if (0 == *j)return false;
		array<array<unsigned short, 4>, 4>temp(mine);
		if (moveMine(0)) { mine = temp; return false; }mine = temp;
		if (moveMine(1)) { mine = temp; return false; }mine = temp;
		if (moveMine(2)) { mine = temp; return false; }mine = temp;
		if (moveMine(3)) { mine = temp; return false; }
		return true;
	}

每一次成功移动数字方块后,生成一个新的数字方块2或4,生成的概率比为2:1,我们用一个函数creare()实现。

	//刷新方块
	void create() {
		unsigned char i, j;
		while (true) {
			i = rand() % 4; j = rand() % 4;
			if (!mine[i][j])break;
		}
		mine[i][j] = rand() % 3 ? 2 : 4;
	}

再编写一些无足轻重的函数,增强游戏性。

	//菜单界面
	unsigned short menu() {
		system("mode con cols=33 lines=5");
		cout << "+-------------------------------+" << endl;
		cout << "|------------OC2048-------------|" << endl;
		cout << "|---------Enter开始游戏---------|" << endl;
		cout << "|----------Esc退出游戏----------|" << endl;
		cout << "+-------------------------------+";
		char choice;
		do choice = _getch(); while ('\r' != choice && 27 != choice);
		return '\r' == choice ? 1 : 0;
	}
	//计分器
	unsigned point() {
		unsigned sum = 0;
		for (auto i = mine.begin(); i != mine.end(); i++)
			for (auto j = i->begin(); j != i->end(); j++)
				switch (*j) {
				case 2048:sum += 20480; break;
				case 1024:sum += 9216; break;
				case 512:sum += 4096; break;
				case 256:sum += 1792; break;
				case 128:sum += 768; break;
				case 64:sum += 320; break;
				case 32:sum += 128; break;
				case 16:sum += 48; break;
				case 8:sum += 16; break;
				case 4:sum += 4; break;
				case 2:sum += 0; break;
				}
		return sum;
	}

游戏的基本框架已经成型,最后用一个接口函数GameStart()集成所有的函数,实现游戏功能。

	//主界面
	void GameStart() {
		srand((unsigned)time(NULL));
		if (menu()) {
			system("mode con cols=22 lines=12");
			init();
			char c;
			while (true) {
				if (available())goto GAME_OVER_LABEL;
				c = _getch();
				switch (c) {
				case'w':case'W':if (moveMine(0))create(); ShowMine(); break;
				case's':case'S':if (moveMine(1))create(); ShowMine(); break;
				case'a':case'A':if (moveMine(2))create(); ShowMine(); break;
				case'd':case'D':if (moveMine(3))create(); ShowMine(); break;
				case 27:goto GAME_OVER_LABEL;
				}
			}
		GAME_OVER_LABEL:
			cout << "succeed";
			system("pause");
		}
	}

到这里,取消我们编写程序时对private标签的注释,一个完整的2048控制台小游戏就完成了。
感谢阅读,文章有任何错误欢迎在评论区指出,最后附上完整代码。

#include<iostream>
#include<conio.h>
#include<array>
#include<ctime>
#include<iomanip>
using namespace std;
//OC2048
class OC2048 {
public:
	//构造函数
	OC2048() { init(); }
	//主界面
	void GameStart() {
		srand((unsigned)time(NULL));
		if (menu()) {
			system("mode con cols=22 lines=12");
			init();
			char c;
			while (true) {
				if (available())goto GAME_OVER_LABEL;
				c = _getch();
				switch (c) {
				case'w':case'W':if (moveMine(0))create(); ShowMine(); break;
				case's':case'S':if (moveMine(1))create(); ShowMine(); break;
				case'a':case'A':if (moveMine(2))create(); ShowMine(); break;
				case'd':case'D':if (moveMine(3))create(); ShowMine(); break;
				case 27:goto GAME_OVER_LABEL;
				}
			}
		GAME_OVER_LABEL:
			cout << "succeed";
			system("pause");
		}
	}
private:
	//创建地图
	array<array<unsigned short, 4>, 4>mine;//游戏地图
	array<unsigned short, 4>support;//移动辅助数组
	//菜单界面
	unsigned short menu() {
		system("mode con cols=33 lines=5");
		cout << "+-------------------------------+" << endl;
		cout << "|------------OC2048-------------|" << endl;
		cout << "|---------Enter开始游戏---------|" << endl;
		cout << "|----------Esc退出游戏----------|" << endl;
		cout << "+-------------------------------+";
		char choice;
		do choice = _getch(); while ('\r' != choice && 27 != choice);
		return '\r' == choice ? 1 : 0;
	}
	//冒泡更新地图
	void updateSupport(array<unsigned short, 4>&x) {
		unsigned short flag = 0;
		for (auto it = x.begin(); it != x.end(); it++)
			if (*it) { flag = 1; break; }
		if (0 == flag)return;
		array<unsigned short, 4>::iterator last = x.end() - 1;
		for (auto i = x.begin(); i != last; i++)
			if (0 == *i) {
				auto j = i + 1;
				for (; j != x.end(); j++)if (*j)break;
				if (x.end() == j)break;
				*i = *j; *j = 0;
			}
		do {
			flag = 0;
			for (auto i = x.begin(); i != last; i++)
				if (*i == i[1] && *i) {
					*i *= 2;
					auto j = i + 1;
					for (; j != last && j[1]; j++)
						*j = j[1];
					*j = 0;
					flag = 1;
				}
		} while (flag);
	}
	//移动合并方块
	int moveMine(unsigned short x) {
		unsigned short count;
		int moveOrNot = 0;
		switch (x) {
		case 0://↑
			for (count = 0; count < 4; count++) {
				auto i = support.begin();
				auto j = mine.begin();
				for (; i != support.end(); i++, j++)
					*i = (*j)[count];
				updateSupport(support);
				for (i = support.begin(), j = mine.begin(); i != support.end(); i++, j++)
					if ((*j)[count] != *i) {
						moveOrNot++;
						(*j)[count] = *i;
					}
			}break;
		case 1://↓
			for (count = 0; count < 4L; count++) {
				auto i = support.begin();
				auto j = mine.rbegin();
				for (; i != support.end(); i++, j++)
					*i = (*j)[count];
				updateSupport(support);
				for (i = support.begin(), j = mine.rbegin(); i != support.end(); i++, j++)
					if ((*j)[count] != *i) {
						moveOrNot++;
						(*j)[count] = *i;
					}
			}break;
		case 2://←
			for (count = 0; count < 4; count++) {
				auto i = support.begin();
				auto j = mine[count].begin();
				for (; i != support.end(); i++, j++)
					*i = *j;
				updateSupport(support);
				for (i = support.begin(), j = mine[count].begin(); i != support.end(); i++, j++)
					if (*j != *i) {
						moveOrNot++;
						*j = *i;
					}
			}break;
		case 3://→
			for (count = 0; count < 4; count++) {
				auto i = support.begin();
				auto j = mine[count].rbegin();
				for (; i != support.end(); i++, j++)
					*i = *j;
				updateSupport(support);
				for (i = support.begin(), j = mine[count].rbegin(); i != support.end(); i++, j++)
					if (*j != *i) {
						moveOrNot++;
						*j = *i;
					}
			}break;
		}
		return moveOrNot;
	}
	//显示地图
	void ShowMine() {
		unsigned short count;
		cout << "\n+";
		for (count = 0; count < 20; count++)
			cout.put('-');
		cout << '+' << endl;
		for (auto i = mine.begin(); i != mine.end(); i++) {
			cout.put('|');
			for (count = 0; count < 20; count++)cout.put(32);
			cout << "|\n|";
			for (auto j = i->begin(); j != i->end(); j++) {
				if (*j)cout << setw(4) << *j << ' ';
				else cout << "     ";
			}
			cout << '|' << endl;
		}
		cout.put('|');
		for (count = 0; count < 20; count++)cout.put(32);
		cout << "|\n+";
		for (count = 0; count < 20; count++)cout.put('-');
		cout << '+' << endl << "得分:" << point();
	}
	//初始化地图
	void init() {
		for (auto i = mine.begin(); i != mine.end(); i++)
			for (auto j = i->begin(); j != i->end(); j++)
				*j = 0;
		unsigned char i, j;
		while (true) {
			i = rand() % 4; j = rand() % 4;
			if (!mine[i][j])break;
		}
		mine[i][j] = 2;
		ShowMine();
	}
	//检测是否无法移动
	bool available() {
		for (auto i = mine.begin(); i != mine.end(); i++)
			for (auto j = i->begin(); j != i->end(); j++)
				if (0 == *j)return false;
		array<array<unsigned short, 4>, 4>temp(mine);
		if (moveMine(0)) { mine = temp; return false; }mine = temp;
		if (moveMine(1)) { mine = temp; return false; }mine = temp;
		if (moveMine(2)) { mine = temp; return false; }mine = temp;
		if (moveMine(3)) { mine = temp; return false; }
		return true;
	}
	//刷新方块
	void create() {
		unsigned char i, j;
		while (true) {
			i = rand() % 4; j = rand() % 4;
			if (!mine[i][j])break;
		}
		mine[i][j] = rand() % 3 ? 2 : 4;
	}
	//计分器
	unsigned point() {
		unsigned sum = 0;
		for (auto i = mine.begin(); i != mine.end(); i++)
			for (auto j = i->begin(); j != i->end(); j++)
				switch (*j) {
				case 2048:sum += 20480; break;
				case 1024:sum += 9216; break;
				case 512:sum += 4096; break;
				case 256:sum += 1792; break;
				case 128:sum += 768; break;
				case 64:sum += 320; break;
				case 32:sum += 128; break;
				case 16:sum += 48; break;
				case 8:sum += 16; break;
				case 4:sum += 4; break;
				case 2:sum += 0; break;
				}
		return sum;
	}
};

int main() {
	OC2048 test;
	test.GameStart();
	return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值