Реализация симулятора математической игры Дж.Конвея «Life» («Жизнь»)

от автора


Предисловие

В общем, собрался я как-то для восстановления программисткой формы начать писать мини-проектики разных игр и задач, чтобы восстановить и улучшить эту самую программистскую форму. Первый выбор пал на шахматные задачи и на математическую игру Дж. Конве «Life».

Сегодня я расскажу о том, как реализовывал симулятор игры «Life» на базе кроссплатформенной библиотеки Qt. Делал всё на Qt Widgets без использования QML.

Идея

Первым делом в голову приходит выбор использовать готовый виджет типа QTableView или делать свой. Я решил сделать свой виджет, для упрощения всей работы он будет реализован без скролла и будет отображать столько ячеек поля игры «Жизнь», сколько на нём поместится. Это не совсем удобно, и классически, в профессиональных версиях симулятора «Жизни» сделано наоборот: там размер универсума константен, а меняется только масштаб и скролл у виджета отображения этого самого унивесума. У меня же будет сделано по-простому: размер универсума будет определятся размером самого виджета, сколько поместится клеток на виджете — столько и клеток будет в сетке унивесума. Дополнительно будет сделан интересный сервис: возможность просматривать список заранее составленных конфигураций-популяций с возможностью их размещени в произвольном месте сетки симулятора.

Архитектура

Архитектура будет простая. Один объект будет инкапсулировать сетку симулятора и отвечать за все аспекты симуляции, в том числе просчёт популяций по сигналу таймера QTimer. Другой объект — это классический виджет с переопределенным paintEvent, который отрисовывает текущую популяцию на экране средствами Qt. Третий объект — объект, инкапсулирующий работу с сэмплами (готовыми конфигурациями-популяциями хранящимися в файлах) и позволяющий загружать их из файлов для дальнейшего размещения на экране. Кроме того, нужно будет реализовать объекты окон программы и прописать в них логику работы с юзером.

Реализация

Сам универсум решено было сделать в виде обычной динамической матрицы, то есть массива размерности 2. Динамичность нужна для более гибкой работы с объектом. Итак, основной объект симулятора должен обеспечивать просчёт матрицы универсума и остальные связанные с этим функции. Матрица составлена из структуры вида:

struct Cell{   // Ячейка матрицы для симуляции игры     bool current;     bool next; } ; 

Где current представляет текущую ячейку, которая показывается в интерфейсе и которая модифицируется из интерфейса, а next приставляет временную ячейку, служащую для просчета следующей популяции в главном цикле симулятора.

Объект, хранящий матрицу из этой структуры, называется LifeObject. Приведу определение объекта:

/// Объект содержащий основной код симулятора игры class LifeObject : public QObject {     Q_OBJECT public:     explicit LifeObject(QObject *parent = 0);     ~LifeObject();  signals:     void signal_on_timer(); public slots:     void slot_on_timer(); public:     Cell ** matrix; // матрица симулятора     QTimer* timer; // объект таймера для симуляции     uint col_count,row_count;// размерность матрицы     uint timer_duration; //      void init_matrix(uint row_count, uint col_count);     void reinit_matix(uint row_count,uint col_count );      void deinit_matix();     void start_simulation();     void stop_simulation();     void process_population();     uint get_neighbor_count(uint row, uint col);      void random_population();     void clean_population();      void test_population();     void qdebug_matrix(); }; 

За отображение картинки отвечает виджет, унаследованный от QWidget с переопределенным методом paintEvent, этот виджет сам отрисовывает на себе текущее состояние симуляции в виде сетки с квадратиками:

Определение объекта LifeWidget такое:

/// Объект, представляющий собой виджет для отображения ячеек сетки игры class LifeWidget : public QWidget {     Q_OBJECT public:     explicit LifeWidget(QWidget *parent = 0);     ~LifeWidget();     void paintEvent( QPaintEvent *event ); signals:  public slots:     void slot_on_timer(); public:     uint row_height,col_width;     uint cell_padding;     Sample* sample;     bool draw_numbers;     void load_sample(QString path,QString filename);     bool putting_sample;     uint sample_row,sample_col;     void  put_sample(); protected:     virtual void resizeEvent(QResizeEvent *);     void mousePressEvent(QMouseEvent *event);     void mouseMoveEvent(QMouseEvent *event); public:     LifeObject* life_object;     void autoresize_widget(); }; 

Картинки и ссылки

На последок приведу скриншоты и ссылки на исходный код и дистрибутив с симулятором.

Окно выбора сэмпла:

Исходный код можно получить из репозитория на битбакете.

Скачать скомпилированную версию симулятора можно отсюда.

ссылка на оригинал статьи https://habrahabr.ru/post/282475/


Комментарии

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

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