所以我被要求使用C++做一个快速的2048游戏,但是只使用数组、结构、函数、指针之类的东西,而不使用向量、lambda等等。限制在这里,因为这只是一个学习练习(或家庭作业,我应该说)。
如果需要,我可以注释掉以下代码。这就是我到目前为止想出来的:
#include <iostream>
#include <ctime>
#include <iomanip>
#include <windows.h>
#include <conio.h>
using namespace std;
enum colors
{
BLACK, BLUE, GREEN, CYAN, RED, PURPLE, YELLOW, GREY,
LIGHTGREY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED,
LIGHTPURPLE, LIGHTYELLOW, WHITE
};
void setConsoleColor(int textColor, int bgColor)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (textColor + (bgColor * 16)));
}
int randN(int start, int end)
{
return rand() % (end - start) + start;
}
int randN(int n1, int n2, int percent)
{
if ((rand() % 100) < percent)
return n1;
else
return n2;
}
enum Direction
{
Left = 75,
Right = 77,
Up = 72,
Down = 80
};
struct Cell
{
int x;
int y;
int value;
Cell()
{
x = -1;
y = -1;
value = -1;
}
Cell(int x, int y, int value)
{
this->x = x;
this->y = y;
this->value = value;
}
void setValue(int value)
{
this->value = value;
}
void display()
{
cout << setw(4) << value;
}
bool isEmpty()
{
return value == 0;
}
bool isEqualTo(Cell cell)
{
return value == cell.value;
}
};
struct Game
{
Cell *cells;
Cell *prevCells;
int size;
int score = 0;
int highscore;
int moves = 0;
bool isMoved()
{
for (int i = 0; i < size * size; i++)
{
if (!cells[i].isEqualTo(prevCells[i]))
{
moves += 1;
return true;
}
}
return false;
}
void Doubled(int points)
{
score += points;
}
bool isCellsFull()
{
for (int i = 0; i < size * size; i++)
{
if (cells[i].isEmpty())
return false;
}
return true;
}
Cell &findCell(int x, int y)
{
for (int i = 0; i < size * size; i++)
{
if (cells[i].x == x && cells[i].y == y)
return cells[i];
}
return Cell();
}
Cell &randEmptyCell()
{
if (isCellsFull())
return Cell();
int i;
do
{
i = randN(0, size * size);
} while (!cells[i].isEmpty());
return cells[i];
}
void create(int size)
{
this->size = size;
cells = new Cell[size * size];
prevCells = new Cell[size * size];
for (int x = 0, c = 0; x < size; x++)
{
for (int y = 0; y < size; y++, c++)
{
cells[c] = Cell(x, y, 0);
}
}
}
void display()
{
for (int i = 0, c = 0; i < size + 1; i++)
{
for (int j = 0; j < size; j++)
cout << " ----";
cout << endl;
if (i == size)
break;
for (int j = 0; j < size + 1; j++, c++)
{
cout << "|";
if (j == size)
break;
if (cells[c].isEmpty())
cout << setw(4) << " ";
else
{
setConsoleColor(LIGHTGREEN, BLUE);
cells[c].display();
setConsoleColor(GREY, BLACK);
}
}
cout << endl;
}
cout << "Score: " << score << " Highscore: " << highscore << endl;
cout << "Moves: " << moves << endl;
}
void handleKey()
{
copy(cells, cells + size * size, prevCells);
_getch();
int ch = _getch();
switch (ch)
{
case Left:
moveCells(Left);
break;
case Right:
moveCells(Right);
break;
case Up:
moveCells(Up);
break;
case Down:
moveCells(Down);
break;
}
}
void moveCells(Direction dir)
{
int start = dir == Left || dir == Up ? 0 : size - 1;
int end = start == 0 ? size : -1;
for (int x = 0; x < size; x++)
{
for (int y = start; y != end; (start < end ? y++ : y--))
{
Cell &c1 = dir == Left || dir == Right ? findCell(x, y) : findCell(y, x);
if (c1.isEmpty())
continue;
for (int k = y + (start == 0 ? -1 : 1); k != (end == -1 ? size : -1); (start < end ? k-- : k++))
{
Cell &c2 = dir == Left || dir == Right ? findCell(x, k) : findCell(k, x);
Cell &c3 = dir == Left ? findCell(x, k + 1) :
dir == Right ? findCell(x, k - 1) :
dir == Up ? findCell(k + 1, x) :
findCell(k - 1, x);
if (c1.isEqualTo(c2))
{
c2.setValue(c2.value * 2);
c1.setValue(0);
Doubled(c2.value);
}
else if (!c2.isEmpty() && c3.isEmpty())
{
c3.setValue(c1.value);
c1.setValue(0);
}
else if (k == start && c2.isEmpty())
{
c2.setValue(c1.value);
c1.setValue(0);
}
}
}
}
}
void spawnCells(int amount)
{
for (int i = 0; i < amount; i++)
{
randEmptyCell().setValue(randN(2, 4, 85));
}
}
bool isOver()
{
if (!isCellsFull())
return false;
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
Cell ¢er = findCell(i, j);
Cell &right = findCell(i, j + 1);
Cell &bottom = findCell(i + 1, j);
if (center.isEqualTo(right) || center.isEqualTo(bottom))
return false;
}
return true;
}
}
bool isWin()
{
for (int i = 0; i < size * size; i++)
{
if (cells[i].value == 2048)
return true;
}
return false;
}
};
void main()
{
setlocale(LC_ALL, "rus");
srand(time(0));
bool playAgain = true;
int highscore = 0;
do
{
Game game;
game.create(4);
game.spawnCells(2);
game.highscore = highscore;
game.display();
do
{
game.handleKey();
if (game.isMoved())
game.spawnCells(1);
if (game.score > highscore)
game.highscore = game.score;
system("cls");
game.display();
} while (!game.isWin() && !game.isOver());
if (game.isOver())
{
setConsoleColor(LIGHTRED, BLACK);
cout << "You won!" << endl;
setConsoleColor(GREY, BLACK);
}
else if (game.isWin())
{
setConsoleColor(LIGHTGREEN, BLACK);
cout << "You lost!" << endl;
setConsoleColor(GREY, BLACK);
}
cout << "Play again? y/n\n";
char ch;
do
{
cin >> ch;
if (ch != 'y' && ch != 'n')
cout << "Incorrect!\n";
} while (ch != 'y' && ch != 'n');
switch (ch)
{
case 'y':
highscore = game.score;
system("cls");
break;
case 'n':
playAgain = false;
break;
}
} while (playAgain);
}你认为需要修改/简化什么?有没有增加可读性的方法?此外,我会感谢一些一般的技巧,编写控制台游戏,这样。
发布于 2018-02-16 05:22:55
此代码:
Cell &findCell(int x, int y)
{
for (int i = 0; i < size * size; i++)
{
if (cells[i].x == x && cells[i].y == y)
return cells[i];
}
return Cell();
}...has是一个严重的问题-- return Cell();试图返回对临时对象的引用,当调用代码接收到该引用时,该对象将不再有效。猜测一下,您一定是在使用旧的编译器-- VC++和MinGW的当前版本都错误地拒绝了这一点。
不幸的是,解决这个问题可能并不简单--你几乎需要在某种程度上重新设计代码。可供选择的办法包括:
Optional<T> (但您不能执行可选的引用,所以如果您真的想走这个路线,您可能需要类似于optional<reference_wrapper<Cell>>的东西)。至少在另外一个例子中,randEmptyCell也有同样的问题。
您的Game类有一个create成员函数,在我看来,这个函数很像设计味。要创建一个Game,您应该just...create一个Game。创建一个Game对象,然后调用该对象上的create来真正创建一个实际的游戏,这是两个步骤的初始化,您通常希望避免这种初始化。一般来说,如果您需要做一些事情来创建一个对象,这应该在构造函数中完成。
在我看来,这个开关语句是这样的:
switch (ch)
{
case Left:
moveCells(Left);
break;
case Right:
moveCells(Right);
break;
case Up:
moveCells(Up);
break;
case Down:
moveCells(Down);
break;
}
}...is不必要的冗长,有点毫无意义。在大多数情况下,它等同于just:moveCells(ch);,尽管您可能需要确保ch首先是Up、Down、Left或Right中的一个。一种可能性(仍然使用switch语句)是:
switch (ch) {
case Up:
case Down:
case Left:
case Right:
moveCells(ch);
}这样可以更容易地完成这项工作。
虽然使用它们要做的工作要多一些,但我更喜欢使用random中的随机数生成器,而不是srand/rand。它们的质量通常要高得多,并且它们已经提供了处理在一个范围内创建随机数的代码。我花了一段时间来创建一个包装器,以使使用这些包装更加容易,并提出了以下内容:
#pragma once
#include <array>
#include <random>
#include <algorithm>
class generator {
template <class Rand>
class Seed {
class seeder {
std::array < std::random_device::result_type, Rand::state_size > rand_data;
public:
seeder() {
std::random_device rd;
std::generate(rand_data.begin(), rand_data.end(), std::ref(rd));
}
typename std::array < std::random_device::result_type, Rand::state_size >::iterator begin() { return rand_data.begin(); }
typename std::array < std::random_device::result_type, Rand::state_size >::iterator end() { return rand_data.end(); }
} seed;
std::seed_seq s;
public:
Seed() : s(seed.begin(), seed.end()) { }
template <class I>
auto generate(I a, I b) { return s.generate(std::forward<I>(a), std::forward<I>(b)); }
};
using Rand = std::mt19937_64;
Seed<Rand> seed;
Rand rng;
std::uniform_int_distribution<int> uni;
public:
generator(int high) : rng(seed), uni(0, high) {}
generator(int low, int high) : rng(seed), uni(low, high) { }
int operator()() { return uni(rng); }
};我意识到这是一堆你现在可能不想处理的代码。为了保持它的简单使用,它是作为一个标题编写的,所以基本上只需执行以下操作:
#include "rand.h"
int main() {
generator g(100); // create a generator for numbers from 0 to 100
// generate and print out some numbers:
for (int i = 0; i < 100; i++) {
std::cout << g() << "\t";
if (i % 10 == 0)
std::cout << "\n";
}
}因此,即使它比我想要的代码更多,使用它也很容易(甚至比srand()/rand()更容易),而且可以生成更好的随机数。哦,还有一点是次要的:就目前的情况而言,这取决于C++ 17特性,它从传递给构造函数的值中推断模板参数。对于尚未实现该功能的编译器,请将generator g(100);更改为generator<int> g(100);。
https://codereview.stackexchange.com/questions/187660
复制相似问题