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