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