MiddleКодРедкоЕщё не отвечали
Реализуйте игру «Жизнь» Конвея в ООП-стиле
Реализуйте игру «Жизнь» Конвея в ООП-стиле, разделив состояние и отображение.
Требования:
- класс
Gridвладеет клетками и продвигает одно поколение вstep(); отдельныйPrinter/Rendererрисует поле (не сливайте рендеринг вGrid) step()должен использовать двойной буфер (пишите во второй grid, затем swap) — обновление на месте портит подсчёт соседей в пределах одного поколения- принимайте размеры поля параметрами конструктора, а не глобалами
- обрабатывайте граничные клетки явно (крайние клетки имеют меньше 8 соседей)
class Grid {
public:
Grid(int rows, int cols, bool wrap = false);
void set(int r, int c, bool alive);
bool get(int r, int c) const;
int rows() const;
int cols() const;
void step(); // advance one generation (double-buffered)
// ваш код здесь
};
Допишите реализацию.
Разделите на Grid (владеет клетками, считает поколение) и Printer/Renderer (рисует). Grid::step() считает соседей и применяет четыре правила через двойной буфер — пишет в новый grid и меняет местами — чтобы избежать багов мутации на месте.
- ✗Обновлять клетки на месте — клетка, обновлённая на текущем шаге, влияет на подсчёт соседей позже на том же шаге
- ✗Хардкодить размеры поля в глобалах вместо параметров конструктора
- ✗Не обрабатывать граничные условия (крайние клетки имеют меньше 8 соседей)
- →Как поддержать бесконечное (торовое) поле?
- →Как запустить
step()параллельно в нескольких потоках?
Оглавление
Задача
Реализуйте игру «Жизнь» Конвея:
- Класс
Gridвладеет полем и реализует методstep() - Используйте двойной буфер, чтобы обновление было атомарным
- Напишите
Printer, который выводит поле
Решение
#include <vector>
#include <iostream>
#include <cassert>
class Grid {
public:
Grid(int rows, int cols, bool wrap = false)
: rows_(rows), cols_(cols), wrap_(wrap),
cells_(rows * cols, false),
next_ (rows * cols, false) {}
void set(int r, int c, bool alive) {
cells_[index(r, c)] = alive;
}
[[nodiscard]] bool get(int r, int c) const {
return cells_[index(r, c)];
}
[[nodiscard]] int rows() const { return rows_; }
[[nodiscard]] int cols() const { return cols_; }
// Apply one generation of the Game of Life rules.
void step() {
for (int r = 0; r < rows_; ++r)
for (int c = 0; c < cols_; ++c) {
int n = countNeighbours(r, c);
bool alive = cells_[index(r, c)];
next_[index(r, c)] = alive ? (n == 2 || n == 3) : (n == 3);
}
std::swap(cells_, next_);
}
private:
int rows_, cols_;
bool wrap_;
std::vector<bool> cells_, next_;
[[nodiscard]] int index(int r, int c) const {
return r * cols_ + c;
}
[[nodiscard]] int countNeighbours(int r, int c) const {
static constexpr int dr[] = {-1,-1,-1, 0, 0, 1, 1, 1};
static constexpr int dc[] = {-1, 0, 1,-1, 1,-1, 0, 1};
int count = 0;
for (int i = 0; i < 8; ++i) {
int nr = r + dr[i];
int nc = c + dc[i];
if (wrap_) {
nr = (nr + rows_) % rows_;
nc = (nc + cols_) % cols_;
} else if (nr < 0 || nr >= rows_ || nc < 0 || nc >= cols_) {
continue;
}
if (cells_[index(nr, nc)]) ++count;
}
return count;
}
};
class Printer {
public:
static void print(const Grid& g, char live = '#', char dead = '.') {
for (int r = 0; r < g.rows(); ++r) {
for (int c = 0; c < g.cols(); ++c)
std::cout << (g.get(r, c) ? live : dead);
std::cout << '\n';
}
std::cout << '\n';
}
};
// --- tests ---
int main() {
// Blinker oscillator (period 2)
Grid g(5, 5);
g.set(2, 1, true);
g.set(2, 2, true);
g.set(2, 3, true);
Printer::print(g); // horizontal bar
g.step();
// After one step the blinker must be vertical
assert(!g.get(2, 1) && !g.get(2, 3));
assert( g.get(1, 2) && g.get(2, 2) && g.get(3, 2));
Printer::print(g); // vertical bar
g.step();
// Back to horizontal
assert( g.get(2, 1) && g.get(2, 2) && g.get(2, 3));
assert(!g.get(1, 2) && !g.get(3, 2));
std::cout << "Blinker test passed\n";
return 0;
}
Ключевые моменты
| Аспект | Решение |
|---|---|
| Двойной буфер | cells_ + next_ → std::swap в конце step() |
| Граничные условия | Проверка диапазона или торовое обёртывание через wrap_ |
| Соседи | Константные массивы смещений dr[]/dc[] |
| Тест | Мигалка (blinker) с периодом 2 — классический осциллятор |
Оглавление