моя реализация игры жизни Конвея на C ++

Я нашел жизненную игру Конвея. Я, конечно, слышал об этом, но не думал, что это будет так легко и весело реализовать. В любом случае, вот мой код, который, как я знаю, требует большой доработки:

#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 ответ
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 в месте, где нет реальной асимметрии.

    – Роланд Иллиг

  • 1

    @RolandIllig Хорошие моменты. Я немного изменил код, чтобы улучшить читаемость.

    – 1201 ProgramAlarm

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *