Шахматы. Часть 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. Пропишем следующее:

  1. background-color: #87a1c0;

Как видно фон задается точно так же, как и в css.

Так же необходим файл ресурсов, в который загрузим картинки фигур. Назовем его image.qrc. Рекомендую в нем создать два "каталога" black и white, и в них уже добавлять изображения в зависимости от цвета. Для удобства каждой картинке можно приписать псевдоним по имени фигуры. К примеру, для ладьи, псевдоним можно сделать rook. И для обращения к картинке белой ладьи будет достаточно написать :/white/rook.

Теперь перейдем к классу QBoardPlace – виджету, на котором будет лежать доска. Добавим в проект новый класс, и укажем, что он наследник QFrame.

QBoardPlace

Этот объект будет являться контейнером для доски. Поскольку одним из  требований являлось, то, что бы доска подстраивалась под размеры окна, то и  QBoardPlace тоже это должен уметь. Кроме того, необходимо, что бы при любом размере окна, доска, а соответственно и BoardPlace, были квадратными. Для этого достаточно переопределить событие resizeEvent.

Объявление:

  1. #ifndef QBOARDPLACE_H
  2. #define QBOARDPLACE_H
  3.  
  4. #include <QWidget>
  5. #include <QFrame>
  6.  
  7. class QBoardPlace : public QFrame
  8. {
  9. Q_OBJECT
  10. public:
  11.     QBoardPlace( QWidget *parent = 0 );
  12.     virtual void resizeEvent( QResizeEvent *event );
  13.  
  14. };
  15. #endif // QBOARDPLACE_H

Реализация:

  1. #include "qboardplace.h"
  2.  
  3. QBoardPlace::QBoardPlace(QWidget *parent) :
  4.     QFrame(parent)
  5. {
  6.     this->setStyleSheet("background-color: #ce5c00");
  7.     this->setMinimumSize(400, 400);
  8.  
  9. }
  10.  
  11. void QBoardPlace::resizeEvent( QResizeEvent *event )
  12. {
  13.     int lesser = std::min( this->width(), this->height() );
  14.     resize(lesser, lesser);
  15.     int newX = ( static_cast<QWidget *>(parent())->width() - width() ) / 2;
  16.     move( newX, y() );
  17. }

QCell

Клетка игрового поля. Класс наследуется от TLabel, что бы не было проблем с компоновкой при пустом виджете.  Координаты клетки записывается в скрытое поле _position.

Объявление:

  1. #ifndef QCELL_H
  2. #define QCELL_H
  3.  
  4. #include <QLabel>
  5.  
  6. class QCell : public QLabel
  7. {
  8. Q_OBJECT
  9. public:
  10.     QCell( QPoint position, QWidget *parent = 0 );
  11.  
  12. private:
  13.     QPoint _position;
  14. };
  15.  
  16. #endif // QCELL_H

Реализация:

  1. #include "qcell.h"
  2.  
  3. QCell::QCell( QPoint position, QWidget *parent ) :
  4.     QLabel( parent )
  5. {
  6.     _position = position;
  7. }

QBoard

Сама игровая доска. Наследуется от QWidget, является родителем для клеток. Содержит двумерный массив клеток QCell в качестве поля.

  1. #ifndef QBOARD_H
  2. #define QBOARD_H
  3.  
  4. #include <QWidget>
  5. #include <QLabel>
  6. #include <QHBoxLayout>
  7. #include <QGridLayout>
  8.  
  9. class QCell;
  10.  
  11. class QBoard : public QWidget
  12. {
  13. Q_OBJECT
  14. public:
  15.     QCell *Cells[8][8];
  16.     QBoard(QWidget *parent = 0);
  17.     void drawCells();
  18.  
  19. private:
  20.     void positionCells();
  21. };
  22.  
  23. #include "qcell.h"
  24.  
  25. #endif // QBOARD_H

Методы класса.

Конструктор

Сначала создается горизонтальный компоновщик, которая задается не для самой доски, а для ее родителя (как известно, это будет объект типа QBoardPlace), что бы размеры доски соответствовали размерам родителя. Потом вызываются методы позиционирования и раскрашивания клеток.

  1. QBoard::QBoard(QWidget *parent) :
  2.     QWidget(parent)
  3. {
  4.     // Создаем компоновщик
  5.     QHBoxLayout *layout = new QHBoxLayout;
  6.     layout->setMargin(25); // Отступы
  7.     layout->addWidget(this); // Добавляем доску в компоновщик
  8.     parent->setLayout(layout); // Устанавливаем коспановщик для boardPlace
  9.  
  10.     positionCells();
  11.     drawCells();
  12. }

Метод positionCells

Метод, который создает 64 клетки и расставляет их по доске. Для расстановки применяется компоновщик по сетке.

  1. void QBoard::positionCells()
  2. {
  3.     QGridLayout *grid_layout = new QGridLayout(this);
  4.  
  5.     grid_layout->setSpacing(0);
  6.     grid_layout->setMargin(0);
  7.     for (int i = 0; i < 8; i++) {
  8.         for (int j = 0; j < 8; j++) {
  9.             Cells[i][j] = new QCell( QPoint(i, j), this );
  10.             grid_layout->addWidget(Cells[i][j], j, i);
  11.         }
  12.     }
  13.  
  14.     setLayout(grid_layout);
  15. }

Метод drawCells

Поочередно раскрашивает клетки либо в белый, либо в серый цвет. Клетка, у которой ровно одна координата четная, становится серой, белой, в противном случае. Для определения четности числа используется тот факт, что у нечетного числа первый бит равен 1.

  1. void QBoard::drawCells()
  2. {
  3.     for (int i = 0; i < 8; i++) {
  4.         for (int j = 0; j < 8; j++) {
  5.             if (  ( (i & 1) && !(j & 1) ) || ( !(i & 1) && (j & 1) )  )
  6.                 Cells[i][j]->setStyleSheet("background-color: #cccccc;");
  7.             else
  8.                 Cells[i][j]->setStyleSheet("background-color: #ffffff;");
  9.         }
  10.     }
  11. }

QGamePlace

Главная форма приложения. Содержит в качестве полей доску и объект QBoardPlace. В конструкторе происходит их создание и задание горизонтального компоновщика для boardPlace.

Объявление:

  1. class QGamePlace : public QMainWindow {
  2.     Q_OBJECT
  3. public:
  4.     QGamePlace( QWidget *parent = 0 );
  5.     ~QGamePlace();
  6.  
  7. protected:
  8.     void changeEvent(QEvent *e);
  9.  
  10. private:
  11.     Ui::QGamePlace *ui;
  12.     QBoardPlace *_boardPlace;
  13.     QBoard *_board;
  14. };

Реализация:

  1. QGamePlace::QGamePlace(QWidget *parent) :
  2.     QMainWindow(parent),
  3.     ui(new Ui::QGamePlace)
  4. {
  5.     ui->setupUi(this);
  6.     _boardPlace = new QBoardPlace(this);
  7.  
  8.     QHBoxLayout *layout = new QHBoxLayout;
  9.     layout->addWidget(_boardPlace);
  10.     ui->centralWidget->setLayout(layout);
  11.  
  12.     _board = new QBoard(_boardPlace);
  13. }

qt, cpp

Комментарии (1)

Женя | 4 мая 2011 г. 1:44 Ответить

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

Добавить комментарий

  • Допустимые html-теги:
    <b> </b> - жирный шрифт
    <em> </em> - наклонный
    <s> </s> - зачеркнутый
    <pre> </pre> - сохранение отступов (печать кода)
    [?]
Введите текст с картинки