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