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


Предисловие


В общем, собрался я как-то для восстановления программисткой формы начать писать мини-проектики разных игр и задач, чтобы восстановить и улучшить эту самую программистскую форму. Первый выбор пал на шахматные задачи и на математическую игру Дж. Конве «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();
};

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


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



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



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

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




К сожалению, не доступен сервер mySQL