前言
大家都应该玩过这个风靡一时的游戏吧。至于这个游戏的一些技巧就不在给大家讲解了,我也是一个菜鸡的。但是这个游戏后面所包含的算法可以通过C++来进行实现。
原理
移动
这个原理就是向一个方向移动,遇到相同的元素就翻倍。
首先我们看一下怎么实现非零元素的移动(以向上移动为例):
上面这一个我们可以检查:
- 每一列第一个元素是否为0,如果为零就与第二个元素交换;
- 继续检查第一个元素是否为0,如果为零就与第三个元素交换;
- 继续检查第一个元素是否为0,如果为零就与第四个元素交换;
- 继续检查第二个元素是否为0,如果为零就与第三个元素交换;
- 继续检查第二个元素是否为0,如果为零就与第四个元素交换;
- 继续检查第三个元素是否为0,如果为零就与第四个元素交换。
以第一列为例:
- 第一个元素为0,所以和第二个元素交换;
- 第一个元素依然为0,所以和第三个元素交换;
- 第一个元素依然为0,所以和第四个元素交换;
以此类推,第二个位置第三个位置,这就实现了移动的功能。
消除
对于消除来说,和前面的移动的类似的,只不过在判断前面的为零元素的时候换成判断相等的情况。依然以向上移动作为例子。
- 检查第一列第一个元素是否和第二个元素相等,如果相等就将第一个元素翻倍,将第二个元素置为零;
- 检查第一个元素是否和第三个元素相等,如果相等就将第一个元素翻倍,将第三个元素置为零;
- 检查第一个元素是否和第四个元素相等,如果相等就将第一个元素翻倍,将第四个元素置为零;
- 检查第二个元素是否和第三个元素相等,如果相等就将第二个元素翻倍,将第三个元素置为零;
- 检查第二个元素是否和第四个元素相等,如果相等就将第二个元素翻倍,将第四个元素置为零;
- 检查第三个元素是否和第四个元素相等,如果相等就将第三个元素翻倍,将第四个元素置为零;
以第一列为例
- 第一个元素和第二个元素不相等;
- 第一个元素和第三个元素相等,第一个元素变为两倍,第三个元素置为零;
- 后面的情况都是零,翻倍也不需要处理了。
**上面两种处理方式是不冲突的,如果是0那么就不会和另外一个元素相等。**下面我们对一个例子进行一个分析:
- 判断第一个元素是否为0(否),判断是第一个元素是否和第二个元素相等(否);
- 判断第一个元素是否为0(否),判断是第一个元素是否和第三个元素相等(否);
- 判断第一个元素是否为0(否),判断是第一个元素是否和第四个元素相等(否);
- 判断第二个元素是否为0(是),第三个元素和第二个元素交换;
- 判断第二个元素是否为0(否),判断是第二个元素是否和第四个元素相等(是),第二个元素翻倍,第四个元素置为0;
6.判断第一个元素是否为0(是),判断是第一个元素是否和第二个元素相等(是);
最后的结果就是:
细节讲解
总体结构
StartGame
没有实现具体的功能,它的作用就是调用Game
对象的功能,实现对外封装的效果。在main()
里面直接调用StartGame
即可。
Game
Game.h
#ifndef MYGAME_GAME_H
#define MYGAME_GAME_H
#include <iostream>
#include <iomanip>
#include "windows.h"
#include<fstream>
using namespace std;
class Game {
public:
void setSize(const int size_);
void Draw(ostream& os, std::string f);
void Draw(ostream& os);//重载函数打印界面
void move();
bool generate();//生成新的数字,生成成功返回true,生成失败返回false
void init();
int getScore();
void setMaxScore();//生成新的最高成绩
Game();
private:
int array1[10][10];
int score;
int x, y;
int size;
int max_score;
bool isFully();//判断是否已满
void addScore(int score_);//加成绩
void moveUp();
void moveDown();
void moveRight();
void moveLeft();
void getControl(char& c);//获取输入字符,判断输入是否合法
void clear();//清除历史最高成绩
};
#endif // MYGAME_GAME_H
Game.cpp
#include "Game.h"
void Game::moveUp() {//向上移动
for (int k = 0; k < size; ++k) {
for (int i = 0; i < size; ++i) {
for (int j = i + 1; j < size; ++j) {
if (array1[i][k] == 0 && array1[k][j] != 0) {
array1[i][k] = array1[j][k];
array1[j][k] = 0;
}
if (array1[i][k] == array1[j][k] && array1[k][i] != 0)
{
array1[i][k] *= 2;
addScore(array1[i][k]);
array1[j][k] = 0;
}
}
}
}
}
void Game::moveDown() {//向下移动
for (int k = 0; k < size; ++k) {
for (int i = size - 1; i >= 0; --i) {
for (int j = i - 1; j >= 0; --j) {
if (array1[i][k] == 0 && array1[k][j] != 0) {
array1[i][k] = array1[j][k];
array1[j][k] = 0;
}
if (array1[i][k] == array1[j][k] && array1[k][i] != 0)
{
array1[i][k] *= 2;
addScore(array1[i][k]);
array1[j][k] = 0;
}
}
}
}
}
void Game::moveRight() {//向右移动
for (int k = 0; k < size; ++k) {
for (int i = size - 1; i >= 0; --i) {
for (int j = i - 1; j >= 0; --j) {
if (array1[k][i] == 0 && array1[k][j] != 0)
{
array1[k][i] = array1[k][j];
array1[k][j] = 0;
}
if (array1[k][i] == array1[k][j] && array1[k][i] != 0)
{
array1[k][i] *= 2;
addScore(array1[k][i]);
array1[k][j] = 0;
}
}
}
}
}
void Game::moveLeft() {//向左移动
for (int k = 0; k < size; ++k) {
for (int i = 0; i < size; ++i) {
for (int j = i + 1; j < size; ++j) {
if (array1[k][i] == 0 && array1[k][j] != 0) {
array1[k][i] = array1[k][j];
array1[k][j] = 0;
}
if (array1[k][i] == array1[k][j] && array1[k][i] != 0)
{
array1[k][i] *= 2;
addScore(array1[k][i]);
array1[k][j] = 0;
}
}
}
}
}
bool Game::generate() {//生成新的数字
if (isFully())
return false;
x = rand() % size;
y = rand() % size;
while (array1[x][y] != 0) {
x = rand() % size;
y = rand() % size;
}
array1[x][y] = 2;
return true;
}
void Game::move() {
char c;
getControl(c);
switch (c)
{//判断输入的字符
case 'W':
moveUp();
break;
case 'S':
moveDown();
break;
case 'D':
moveRight();
break;
case 'A':
moveLeft();
break;
case '0':
clear();
break;
}
}
void Game::getControl(char& c) {//获取输入字符,判断输入是否合法
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
cout << "Press the keys to start and continue." << endl;
cout << "W => Up" << endl << "S => Down" << endl << "A => Left" << endl << "D => Right" << endl << "0 => clear max score" << endl;
while (cin >> c && !(c == 'A' || c == 'S' || c == 'D' || c == 'W' || c == '0'))
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
cout << "please input right instruction" << endl;
};
}
void Game::Draw(ostream& os) {
cout << endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE | FOREGROUND_RED);
cout << "===================================================================================" << endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 9);
cout << "your score:" << getScore() << endl;
cout << "your max score : " << max_score << endl;
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
if (i == x && j == y)
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_RED);
cout << setw(4) << array1[i][j];
}
cout << endl;
}
}
void Game::Draw(ostream& os, std::string f) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);
os << f;
}
bool Game::isFully() {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
if (array1[i][j] == 0)
return false;
}
}
return true;
}
void Game::init() {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
array1[i][j] = 0;
}
}
score = 0;
}
int Game::getScore() {
return score;
}
void Game::addScore(int score_) {
score += score_;
}
void Game::setSize(const int size_) {
size = size_;
}
Game::Game() {
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
array1[i][j] = 0;
}
ifstream is("max_score.txt");
is >> max_score;//从文件中读入最高成绩
}
void Game::setMaxScore() {
if (score > max_score)
{
ofstream os("max_score.txt");
os << score;//将新的最高成绩写入文件中
}
}
void Game::clear() {
ofstream os("max_score.txt");
os << 0;
}
这里面最核心的内容就是那个四个move()
函数,所以我们将这其中的一个move函数单独拿出来进行解释:
/*
*k:表示一列
*i:表示选定的标定位置
*j:表示另一个移动的位置
*/
void Game::moveUp() {//向上移动
for (int k = 0; k < size; ++k) {
for (int i = 0; i < size; ++i) {
for (int j = i + 1; j < size; ++j) {
if (array1[i][k] == 0 && array1[k][j] != 0) {
array1[i][k] = array1[j][k];
array1[j][k] = 0;
}
/*
*当一列中的i元素为0,且j元素不为0时,二者交换,实现前面原理当中的向前移动
*/
if (array1[i][k] == array1[j][k] && array1[k][i] != 0)
{
array1[i][k] *= 2;
addScore(array1[i][k]);
array1[j][k] = 0;
}
/*
*其中两个元素不为0且相等的时候其中一个翻倍,另一个置为0,实现消除功能。
*/
}
}
}
}
对于不同的移动方向,相应的i,j,k
的移动方向的初始大小不同而已,其余原理均相同。
StartGame
StartGame.h
#ifndef MYGAME_STARTGAME_H
#define MYGAME_STARTGAME_H
#include "Game.h"
void StartGame();
#endif // MYGAME_STARTGAME_H
StartGame.cpp
#include "StartGame.h"
void StartGame() {
Game game;
game.init();
string str = R"(
/\\\\\\\\\ /\\\\\\\ /\\\ /\\\\\\\\\
/\\\///////\\\ /\\\/////\\\ /\\\\\ /\\\///////\\\
\/// \//\\\ /\\\ \//\\\ /\\\/\\\ \/\\\ \/\\\
/\\\/ \/\\\ \/\\\ /\\\/\/\\\ \///\\\\\\\\\/
/\\\// \/\\\ \/\\\ /\\\/ \/\\\ /\\\///////\\\
/\\\// \/\\\ \/\\\ /\\\\\\\\\\\\\\\\ /\\\ \//\\\
/\\\/ \//\\\ /\\\ \///////////\\\// \//\\\ /\\\
/\\\\\\\\\\\\\\\ \///\\\\\\\/ \/\\\ \///\\\\\\\\\/
\/////////////// \/////// \/// \/////////
)";
int size;
game.Draw(cout, str);//打印封面
cout << "input your size(>3&&<11):" << endl;
cin >> size;
if (size < 3 || size > 10) {
cout << "Game Over!" << endl;
cout << "your score:" << game.getScore() << endl;
return;
}
game.setSize(size);
while (game.generate()) {
system("cls");
game.Draw(cout, str);
game.Draw(cout);
game.move();
}//循环游戏
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
cout << "Game Over!" << endl;
cout << "your score:" << game.getScore() << endl;
game.setMaxScore();//设置新的记录
}
实现
main函数
#include <iostream>
#include <iomanip>
#include "windows.h"
#include "StartGame.h"
using namespace std;
int main() {
StartGame();
return 0;
}
开始界面
游戏过程中
游戏结束
总结
经过这一系列的写作练习,发现自己的写作能力在潜移默化中发生改表,希望自己能够继续坚持下去。这个2048还有可以优化的地方,比如我们可以直接读取上下左右键而不是WASD键等等,以后我还会继续优化的。希望这个能够对大家有帮助! 谢谢!