Загрузка данных


#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;
}