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