Я нашел жизненную игру Конвея. Я, конечно, слышал об этом, но не думал, что это будет так легко и весело реализовать. В любом случае, вот мой код, который, как я знаю, требует большой доработки:
#include <iostream>
#include <vector>
#include <windows.h>
#define SIZE 10
using std::vector;
struct Pos {
int x;
int y;
Pos(int ix, int iy) : x(ix), y(iy) {}
};
void printGrid();
int getNeighborCount(Pos cellPos);
void next(int (&universe)[SIZE][SIZE]);
void killOrBirthCells(int (&universe)[SIZE][SIZE], vector<Pos> &toKill, vector<Pos> &toBirth);
void cellFate(int &cell, Pos cellPos, int neighborCount, vector<Pos> &toKill, vector<Pos> &toBirth);
void printUniverse(int (&universe)[SIZE][SIZE]);
int main() {
int universe[SIZE][SIZE] = {0};
// glider
universe[0][2] = 1;
universe[1][2] = 1;
universe[2][2] = 1;
universe[2][1] = 1;
universe[1][0] = 1;
while (1) {
system("cls");
next(universe);
printUniverse(universe);
Sleep(500);
}
return 0;
}
int getNeighborCount(int (&universe)[SIZE][SIZE], Pos cellPos) {
int count = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
int x = cellPos.x - 1 + j;
int y = cellPos.y - 1 + i;
// handle cells on the edge
if (x >= 0 && y >= 0 && x < SIZE && y < SIZE
&& universe[cellPos.y - 1 + i][cellPos.x - 1 + j] == 1) {
count++;
}
}
}
// exclude itself
if (universe[cellPos.y][cellPos.x]) {
return count - 1;
}
return count;
}
void next(int (&universe)[SIZE][SIZE]) {
std::vector<Pos> toKill;
std::vector<Pos> toBirth;
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
Pos cellPos{j, i};
int neighborCount = getNeighborCount(universe, cellPos);
cellFate(universe[i][j], cellPos, neighborCount, toKill, toBirth);
}
}
killOrBirthCells(universe, toKill, toBirth);
}
// populate toKill and toBirth vectors
void cellFate(int &cell, Pos cellPos, int neighborCount, vector<Pos> &toKill, vector<Pos> &toBirth) {
if (cell == 1) {
// lonely
if (neighborCount == 0 || neighborCount == 1) {
toKill.push_back(cellPos);
// overcrowded
} else if (neighborCount >= 4 && neighborCount <= 8) {
toKill.push_back(cellPos);
}
} else {
if (neighborCount == 3) {
toBirth.push_back(cellPos);
}
}
}
void killOrBirthCells(int (&universe)[SIZE][SIZE], vector<Pos> &toKill, vector<Pos> &toBirth) {
for (Pos cellPos : toKill) {
universe[cellPos.y][cellPos.x] = 0;
}
for (Pos cellPos : toBirth) {
universe[cellPos.y][cellPos.x] = 1;
}
toKill.clear();
toBirth.clear();
}
void printUniverse(int (&universe)[SIZE][SIZE]) {
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (universe[i][j] == 1) {
std::cout << 'x';
} else {
std::cout << '.';
}
}
std::cout << 'n';
}
}
1 ответ
Если вы собираетесь использовать C ++, используйте его в полной мере. Создайте класс, который будет содержать вашу вселенную, а затем все ваши не-main
функции могут быть членами. Вам также не нужно будет передавать ссылку на universe
массив, как если бы он был членом класса.
Одна альтернатива toKill
и toBirth
векторы должны иметь два массива «вселенная». Вы бы использовали один в качестве источника и скопировали и обновили значения во вторую, а затем выберете, какой массив вы изменяете при следующем проходе. cellFate
вернет новое (обновленное) значение для этой ячейки и killOrBirthCells
будет устранено.
getNeighborCount
использует только i
и j
как смещения к значениям x и y. Вы можете повторить циклы, чтобы напрямую обновить значения x и y, а также обработать обрезку краев, правильно отслеживая начальные и конечные значения для циклов. Также было бы легко исключить саму ячейку.
Соединив все это, мы получим новое getNeighborCount
(это будет член класса):
int Universe::getNeighborCount(Pos cellPos) const {
int count = 0;
int sx = std::max(cellPos.x - 1, 0);
int ex = std::min(cellPos.x + 1, SIZE - 1);
int sy = std::max(cellPos.y - 1, 0);
int ey - std::min(cellPos.y + 1, SIZE - 1);
for (int y = sy; y <= ey; ++y) {
for (int x = sx; x <= ex; ++x) {
if (y != cellPos.y || x != cellPos.x) {
if (universe[y][x] == 1)
count++;
}
}
}
return count;
}
Вы можете уменьшить мерцание, переместив system("cls");
позвонить непосредственно перед вызовом printUniverse
. Это сократит время, в течение которого у вас будет пустой экран.
В
int y
мне показалось очевидной опечаткой. Но потом я увидел, что нетsy
вообще. Я бы добавил эту дополнительную переменную, чтобы не путать читателей. Компилятор достаточно умен, чтобы оптимизировать его. Ваш текущий код асимметричен между координатами x и y в месте, где нет реальной асимметрии.— Роланд Иллиг
@RolandIllig Хорошие моменты. Я немного изменил код, чтобы улучшить читаемость.
— 1201 ProgramAlarm