Загрузка данных
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// gcc -Wall -Werror -Wextra src/game_of_life.c -o game_of_life -lncurses
/* Размеры игрового поля согласно заданию */
#define WIDTH 80
#define HEIGHT 25
/* Настройки скорости (задержка в микросекундах) */
#define DEF_DELAY 100000
#define DELAY_STEP 20000
#define MIN_DELAY 20000
/* Прототипы всех используемых функций */
void init_grid(int grid[HEIGHT][WIDTH]);
void draw_grid(const int grid[HEIGHT][WIDTH], int delay);
int count_neighbors(int grid[HEIGHT][WIDTH], int r, int c);
void update_grid(int grid[HEIGHT][WIDTH], int next[HEIGHT][WIDTH]);
void copy_grid(int src[HEIGHT][WIDTH], int dest[HEIGHT][WIDTH]);
int is_alive(const int grid[HEIGHT][WIDTH]);
int handle_input(int ch, int *delay);
int main() {
int grid[HEIGHT][WIDTH];
int next[HEIGHT][WIDTH];
int delay = DEF_DELAY;
int running = 1;
/* Считываем начальную карту из файла через stdin */
init_grid(grid);
/* Перенаправляем stdin обратно на терминал для чтения клавиатуры */
if (freopen("/dev/tty", "r", stdin) == NULL) {
return 1;
}
/* Инициализация графического режима ncurses */
initscr();
/* Передавать символы сразу, не дожидаясь нажатия Enter */
cbreak();
/* Отключить автоматическое отображение вводимых символов */
noecho();
/* Скрыть курсор в терминале */
curs_set(0);
/* Сделать чтение клавиш неблокирующим (не останавливать игру) */
nodelay(stdscr, TRUE);
/* Главный игровой цикл: пока идет игра и есть живые клетки */
while (running && is_alive(grid)) {
draw_grid(grid, delay);
update_grid(grid, next);
copy_grid(next, grid);
/* Приостановка программы для контроля скорости */
usleep(delay);
/* Считываем нажатую клавишу и обрабатываем её */
int ch = getch();
if (handle_input(ch, &delay) == 0) {
running = 0;
}
}
/* Корректное закрытие ncurses и возврат терминала в обычный режим */
endwin();
return 0;
}
/* Функция заполнения поля данными из файла */
void init_grid(int grid[HEIGHT][WIDTH]) {
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
/* Если скан завершился ошибкой или EOF, пишем 0 */
if (scanf("%d", &grid[i][j]) != 1) {
grid[i][j] = 0;
}
}
}
return;
}
/* Функция отрисовки матрицы и интерфейса на экране */
void draw_grid(const int grid[HEIGHT][WIDTH], int delay) {
/* Очистить виртуальный экран перед новым кадром */
clear();
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
if (grid[i][j]) {
/* Вывести символ '#' в координатах y=i, x=j */
mvaddch(i, j, '#');
} else {
/* Вывести точку на месте пустой клетки */
mvaddch(i, j, '.');
}
}
}
/* Печать панели управления на 26-й строке (индекс HEIGHT) */
mvprintw(HEIGHT, 0, "Speed Delay: %d micro seconds | A: Speed Up | Z: Slow Down | Space bar: exit",
delay / 1000);
/* Обновить физический экран терминала */
refresh();
return;
}
/* Подсчет соседей с учетом зацикленности (тора) */
int count_neighbors(int grid[HEIGHT][WIDTH], int r, int c) {
int count = 0;
/* Проверяем сетку 3х3 вокруг текущей клетки */
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
/* Саму центральную клетку пропускаем */
if (!(i == 0 && j == 0)) {
/* Формула зацикливания краев поля (остаток от деления) */
int nr = (r + i + HEIGHT) % HEIGHT;
int nc = (c + j + WIDTH) % WIDTH;
count += grid[nr][nc];
}
}
}
return count;
}
/* Генерация следующего поколения по правилам Конвея */
void update_grid(int grid[HEIGHT][WIDTH], int next[HEIGHT][WIDTH]) {
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
int neighbors = count_neighbors(grid, i, j);
if (grid[i][j] == 1) {
/* Клетка выживает только если у нее 2 или 3 соседа */
next[i][j] = (neighbors == 2 || neighbors == 3) ? 1 : 0;
} else {
/* В пустой клетке зарождается жизнь, если соседей ровно 3 */
next[i][j] = (neighbors == 3) ? 1 : 0;
}
}
}
return;
}
/* Копирование временной матрицы в основную */
void copy_grid(int src[HEIGHT][WIDTH], int dest[HEIGHT][WIDTH]) {
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
dest[i][j] = src[i][j];
}
}
return;
}
/* Проверка, осталась ли на поле хотя бы одна живая клетка */
int is_alive(const int grid[HEIGHT][WIDTH]) {
int alive = 0;
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
if (grid[i][j] == 1) {
alive = 1;
}
}
}
return alive;
}
/* Обработка интерактивных нажатий игрока */
int handle_input(int ch, int *delay) {
int status = 1;
/* Ускорение (уменьшаем время задержки кадров) */
if (ch == 'a' || ch == 'A') {
if (*delay - DELAY_STEP >= MIN_DELAY) {
*delay -= DELAY_STEP;
}
/* Замедление (увеличиваем время задержки кадров) */
} else if (ch == 'z' || ch == 'Z') {
*delay += DELAY_STEP;
/* Пробел меняет статус на 0 для выхода из игры */
} else if (ch == ' ') {
status = 0;
}
return status;
}