Шахматы. Часть 1. Введение
В этом цикле статей будет разработано приложение для игры в шахматы. Вообще, написание шахмат, без искусственного интеллекта, довольно распространенная задача по практике в объектно-ориентированном программировании. Тут есть и наследование, полиморфизм, инкапсуляция, можно даже придумать, куда приделать паттерны :).
Сначала мы напишем шахматы типа человек против человека, а искусственному интеллекту будет посвящен уже другой цикл статей. В качестве IDE будем использовать отличный Qt Creator (скачать можно здесь, если в системе нет Qt, то лучше качать полностью Qt SDK).
Требования к программе
- Проверка хода согласно правилам для каждой из фигур.
- Проверки на шах, мат и пат.
- Возможность рокировки.
- Подсветка возможных ходов.
- Размер доски должен подстраиваться под размер окна.
Внешний вид
Определимся с внешним видом приложения. За основу можно взять оформление glChess, шахмат, которые идут в комплекте с Gnome. Картинки фигурок должны быть в векторном формате, что бы шахматы смотрелись одинаково хорошо при любом размере окна. Такие картинки были найдены на просторах интернета, скачать их можно здесь (формат svg).
В конечном итоге приложение может выглядеть так (KDE4):
Зеленым подсвечивается выбранная в данный момент фигура, синим - возможные ходы и красным фигуры, которые можно срубить.
Основные классы
Что бы разобраться с какими классами мы будем работать, нужно еще раз посмотреть, на приложение, которое мы хотим получить.
- QGamePlace – основной класс формы. Наследник QMainWindow.
- QBoardPlace– виджет, на котором располагается доска.
- QCell – клетка на доске.
- QBoard– собственно, доска. На картинке не видна из-за клеток на ней.
- QPiece– шахматная фигура. Сам класс является абстрактным, работа идет с его наследниками по каждому типу фигуры (QPawn– пешка, QBishop – слон и т.д.)
Кроме того необходим некоторый класс QGame, который будет описывать сам процесс игры.
К каждому из этих классов мы еще не раз вернемся в процессе разработки программы.
Создание проекта
В Qt Creator'e создадим новый GUI проект. Главную форму приложения назовем QGamePlace.
После создания проекта, в первый, и, пожалуй, последний раз откроем форму в дизайнере. Нам нужно задать фон для центрального виджета (centralWidget). Проще всего это сделать при помощи Qt StyleSheets, своеобразного аналога css для qt. Выберем centralWidget в инспекторе объектов и в окне свойств выберем styleSheet. Пропишем следующее:
- background-color: #87a1c0;
Как видно фон задается точно так же, как и в css.
Так же необходим файл ресурсов, в который загрузим картинки фигур. Назовем его image.qrc. Рекомендую в нем создать два "каталога" black и white, и в них уже добавлять изображения в зависимости от цвета. Для удобства каждой картинке можно приписать псевдоним по имени фигуры. К примеру, для ладьи, псевдоним можно сделать rook. И для обращения к картинке белой ладьи будет достаточно написать :/white/rook.
Теперь перейдем к классу QBoardPlace – виджету, на котором будет лежать доска. Добавим в проект новый класс, и укажем, что он наследник QFrame.
QBoardPlace
Этот объект будет являться контейнером для доски. Поскольку одним из требований являлось, то, что бы доска подстраивалась под размеры окна, то и QBoardPlace тоже это должен уметь. Кроме того, необходимо, что бы при любом размере окна, доска, а соответственно и BoardPlace, были квадратными. Для этого достаточно переопределить событие resizeEvent.
Объявление:
- #ifndef QBOARDPLACE_H
- #define QBOARDPLACE_H
- #include <QWidget>
- #include <QFrame>
- class QBoardPlace : public QFrame
- {
- Q_OBJECT
- public:
- QBoardPlace( QWidget *parent = 0 );
- virtual void resizeEvent( QResizeEvent *event );
- };
- #endif // QBOARDPLACE_H
Реализация:
- #include "qboardplace.h"
- QBoardPlace::QBoardPlace(QWidget *parent) :
- QFrame(parent)
- {
- this->setStyleSheet("background-color: #ce5c00");
- this->setMinimumSize(400, 400);
- }
- void QBoardPlace::resizeEvent( QResizeEvent *event )
- {
- int lesser = std::min( this->width(), this->height() );
- resize(lesser, lesser);
- int newX = ( static_cast<QWidget *>(parent())->width() - width() ) / 2;
- move( newX, y() );
- }
QCell
Клетка игрового поля. Класс наследуется от TLabel, что бы не было проблем с компоновкой при пустом виджете. Координаты клетки записывается в скрытое поле _position.
Объявление:
- #ifndef QCELL_H
- #define QCELL_H
- #include <QLabel>
- class QCell : public QLabel
- {
- Q_OBJECT
- public:
- QCell( QPoint position, QWidget *parent = 0 );
- private:
- QPoint _position;
- };
- #endif // QCELL_H
Реализация:
- #include "qcell.h"
- QCell::QCell( QPoint position, QWidget *parent ) :
- QLabel( parent )
- {
- _position = position;
- }
QBoard
Сама игровая доска. Наследуется от QWidget, является родителем для клеток. Содержит двумерный массив клеток QCell в качестве поля.
- #ifndef QBOARD_H
- #define QBOARD_H
- #include <QWidget>
- #include <QLabel>
- #include <QHBoxLayout>
- #include <QGridLayout>
- class QCell;
- class QBoard : public QWidget
- {
- Q_OBJECT
- public:
- QCell *Cells[8][8];
- QBoard(QWidget *parent = 0);
- void drawCells();
- private:
- void positionCells();
- };
- #include "qcell.h"
- #endif // QBOARD_H
Методы класса.
Конструктор
Сначала создается горизонтальный компоновщик, которая задается не для самой доски, а для ее родителя (как известно, это будет объект типа QBoardPlace), что бы размеры доски соответствовали размерам родителя. Потом вызываются методы позиционирования и раскрашивания клеток.
- QBoard::QBoard(QWidget *parent) :
- QWidget(parent)
- {
- // Создаем компоновщик
- QHBoxLayout *layout = new QHBoxLayout;
- layout->setMargin(25); // Отступы
- layout->addWidget(this); // Добавляем доску в компоновщик
- parent->setLayout(layout); // Устанавливаем коспановщик для boardPlace
- positionCells();
- drawCells();
- }
Метод positionCells
Метод, который создает 64 клетки и расставляет их по доске. Для расстановки применяется компоновщик по сетке.
- void QBoard::positionCells()
- {
- QGridLayout *grid_layout = new QGridLayout(this);
- grid_layout->setSpacing(0);
- grid_layout->setMargin(0);
- for (int i = 0; i < 8; i++) {
- for (int j = 0; j < 8; j++) {
- Cells[i][j] = new QCell( QPoint(i, j), this );
- grid_layout->addWidget(Cells[i][j], j, i);
- }
- }
- setLayout(grid_layout);
- }
Метод drawCells
Поочередно раскрашивает клетки либо в белый, либо в серый цвет. Клетка, у которой ровно одна координата четная, становится серой, белой, в противном случае. Для определения четности числа используется тот факт, что у нечетного числа первый бит равен 1.
- void QBoard::drawCells()
- {
- for (int i = 0; i < 8; i++) {
- for (int j = 0; j < 8; j++) {
- if ( ( (i & 1) && !(j & 1) ) || ( !(i & 1) && (j & 1) ) )
- Cells[i][j]->setStyleSheet("background-color: #cccccc;");
- else
- Cells[i][j]->setStyleSheet("background-color: #ffffff;");
- }
- }
- }
QGamePlace
Главная форма приложения. Содержит в качестве полей доску и объект QBoardPlace. В конструкторе происходит их создание и задание горизонтального компоновщика для boardPlace.
Объявление:
- class QGamePlace : public QMainWindow {
- Q_OBJECT
- public:
- QGamePlace( QWidget *parent = 0 );
- ~QGamePlace();
- protected:
- void changeEvent(QEvent *e);
- private:
- Ui::QGamePlace *ui;
- QBoardPlace *_boardPlace;
- QBoard *_board;
- };
Реализация:
- QGamePlace::QGamePlace(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::QGamePlace)
- {
- ui->setupUi(this);
- _boardPlace = new QBoardPlace(this);
- QHBoxLayout *layout = new QHBoxLayout;
- layout->addWidget(_boardPlace);
- ui->centralWidget->setLayout(layout);
- _board = new QBoard(_boardPlace);
- }



Комментарии (1)
Очень интересно написано, а можешь выложить исходные файлы, просто я еще новичок, и я не все понял как надо делать.
Добавить комментарий