Шахматы. Часть 2

Во второй части цикла статей, посвященного написанию шахмат, будет рассмотрена организация ходов по доске. Для начала введем два класса: QPiece – фигура и QGame – игра.

QPiece

Вообще далее фигура будет абстрактным классом, а работать мы будем с ее наследниками, но сейчас, для организации ходов по доске можно будет обойтись без них.

Фигура будет наследником от QSvgWidget, виджета, для отображения svg картинок.

Свойства фигуры

  • State - состояние. Фигуры будет иметь два основных состояния: Simple (обычная фигура) и Deleted (уже срубленная). Доступно для записи и для чтения.
  • Color – цвет фигуры (белая или черная). Свойство доступно только для чтения. Тип bool.
  • Position – позиция фигуры на доске. Доступно для записи и чтения.
  • Board – доска. Тип QBoard, доступна только для чтения.

Объявление:

  1. class QPiece : public QSvgWidget
  2. {
  3.     Q_OBJECT
  4. public:
  5.     enum State { Simple, Deleted };
  6.  
  7.     QPoint position();
  8.     void setPosition( QPoint value );
  9.  
  10.     bool color();
  11.     static QGame *game();
  12.  
  13.     void setState( State value );
  14.     State state();
  15.  
  16.     QBoard *board();
  17.     QPiece( QPoint position, QWidget *board );
  18.  
  19. protected:
  20.     QBoard *_board;
  21.     QPoint _position;
  22.     State _state;
  23.     bool _color;
  24. };

Реализация:

  1. QPiece::QPiece( QPoint position, QWidget *board ) :
  2.     QSvgWidget( board )
  3. {
  4.     _board = static_cast<QBoard *>( board );
  5.     // Цвет будет определять согласно позиции по вертикали
  6.     // Если фигура вверху, то черная, если внизу - белая
  7.     _color = position.y() > 4;
  8.  
  9.     setPosition( position );
  10. }
  11.  
  12. QPoint QPiece::position()
  13. {
  14.     return _position;
  15. }
  16.  
  17. bool QPiece::color()
  18. {
  19.     return _color;
  20. }
  21.  
  22. void QPiece::setState( State value )
  23. {
  24.     _state = value;
  25.     update();
  26. }
  27.  
  28. QPiece::State QPiece::state()
  29. {
  30.     return _state;
  31. }
  32.  
  33. QBoard *QPiece::board()
  34. {
  35.     return _board;
  36. }

Рассмотрим подробнее запись в свойство Position. Вообще, так как на доске у нас есть 64 клетки, то перемещение по доске можно сделать, просто присваивая клетку фигуре как родителя. Это довольно легко реализовать и это позволит без проблем узнать есть ли на заданной клетке фигура. Правда есть небольшая проблема, так как одним из требований являлось то, что бы размер доски подстраивался под размер окна, то нужно что бы и фигура меняла размер. Что бы не переопределять обработчик события resizeEvent, проще использовать компоновщик.

Все это можно сделать в методе setPosition() – записи в свойство position.

  1. void QPiece::setPosition( QPoint value )
  2. {
  3.     _position = value;
  4.  
  5.     // Получаем клетку на которую нужно перейти
  6.     QWidget* parentWidget =  _board->Cells[ value.x() ][ value.y() ];
  7.  
  8.     // Если у клетки уже есть компановшик
  9.     if (parentWidget->layout()) {
  10.         // Добавляем в него фигуру
  11.         parentWidget->layout()->addWidget( this );
  12.     }
  13.     else { // Если нет
  14.         // Создаем компановшик
  15.         QHBoxLayout *layout = new QHBoxLayout( parentWidget );
  16.         layout->setMargin(0); // Отступы
  17.         layout->addWidget(this); // Добавляем фигуру
  18.         // Задаем компановшик
  19.         parentWidget->setLayout(layout);
  20.     }
  21.  
  22.     // Задаем клетку родителем фигуры
  23.     this->setParent( _board->Cells[ value.x() ][ value.y() ] );
  24. }

Методы

У фигуры будет довольно много методов, за исключением чтении и записи в свойства, но сейчас мы рассмотрим только один movePiece – перемещение фигуры.

  1. bool QPiece::movePiece( QPoint newPosition )
  2. {
  3.     setPosition( newPosition );
  4.     return true;
  5. }

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

Теперь можно перейти к классу QGame.

QGame

Смысл этого класса в том, что он как бы отвечает за весь процесс игры. Сделаем его одиночкой (Singleton), поскольку нужен глобальный доступ и единовременно должна существовать только одна игра :).

Свойства игры

  • ActiveMove – свойство, определяющее выбрана ли в данный момент фигура. То есть, если следующий клик по доске будет ходом, то это свойство true.
  • SelectPiece– выбранная фигура. То есть, та фигура по которой кликнул пользователь, если его ход.
  • Turn – это не свойство, а просто открытое поле. Определяет, чей ход. True – белые, false – черные.

Объявление:

  1. class QGame : public QObject
  2. {
  3.     Q_OBJECT
  4. public:
  5.     bool turn;
  6.  
  7.     void setActiveMove( bool value );
  8.     bool activeMove();
  9.  
  10.     void setSelectPiece( QPiece *value );
  11.     QPiece *selectPiece();
  12.  
  13.     static QGame *instance();
  14.  
  15. protected:
  16.     QGame();
  17.  
  18. private:
  19.     bool _activeMove;
  20.     static QGame *_instance;
  21.     QPiece *_selectPiece;
  22. };

Реализация:

  1. QGame *QGame::_instance = 0;
  2.  
  3. QGame::QGame()
  4. {
  5.     turn = true;
  6.     _activeMove = false;
  7.     _selectPiece = 0;
  8. }
  9.  
  10. void QGame::setSelectPiece( QPiece *value )
  11. {
  12.     _selectPiece = value;
  13.     setActiveMove(true);
  14. }
  15.  
  16. void QGame::setActiveMove( bool value )
  17. {
  18.     _activeMove = value;
  19. }
  20.  
  21. bool QGame::activeMove()
  22. {
  23.     return _activeMove;
  24. }
  25.  
  26.  
  27. QPiece *QGame::selectPiece()
  28. {
  29.     return _selectPiece;
  30. }
  31.  
  32. QGame *QGame::instance()
  33. {
  34.     if ( _instance == 0 ) {
  35.         _instance = new QGame();
  36.     }
  37.     return _instance;
  38. }

Методы чтения и записи не должны вызвать проблем.

Ведем в класс игры так же метод doMove() – сделать ход. Смысл метода в том, что он выполняет ход для выбранной фигуры на заданную позицию, и меняет очередность хода.

  1. void QGame::doMove( QPoint newPosition )
  2. {
  3.     if ( _selectPiece->movePiece( newPosition ) ) {
  4.         turn = !turn;
  5.     }
  6. }

Вернемся к классу фигуры. Добавим объект QGameв качестве поля. Теперь нам нужно переопределить обработчик события при клике мыши. Тут, в общем-то, нужно проверить, если ход активный то значит, эту фигуру пытаются рубить, в противном случае, эту фигуру нужно сделать выбранной.

  1. class QPiece : public QSvgWidget
  2. {
  3.     Q_OBJECT
  4. public:
  5.     ...
  6. protected:
  7.     ...
  8.     QGame *_game;
  9.     void mousePressEvent( QMouseEvent *ev );
  10. };
  11.  
  12. ...
  13.  
  14. QPiece::QPiece( QPoint position, QWidget *board ) :
  15.     QSvgWidget( board )
  16. {
  17.     ...
  18.     _game = QGame::instance();
  19. }
  20.  
  21. void QPiece::mousePressEvent( QMouseEvent *ev )
  22. {
  23.     if ( _game->activeMove() && _game->turn != _color ) {
  24.         _game->doMove( _position );
  25.         _game->setActiveMove(false);
  26.     }
  27.     else if ( _game->turn == _color ) {
  28.         _game->setSelectPiece(this);
  29.     }
  30.  
  31. }

Теперь нужно обработать клик по клетке. Для начала в класс клетки добавим поле _game типа QGame. Переопределим обработчик события mousePressEvent.

  1. class QCell : public QLabel
  2. {
  3. ...
  4.  
  5. private:
  6.     QGame *_game;
  7.  
  8. protected:
  9.     void mousePressEvent( QMouseEvent *ev );
  10. };
  11.  
  12. QCell::QCell( QPoint position, QWidget *parent ) :
  13.     QLabel( parent )
  14. {
  15.     _game = QGame::instance();
  16.     _position = position;
  17. }
  18.  
  19. void QCell::mousePressEvent( QMouseEvent *ev )
  20. {
  21.     if ( _game->activeMove() ) {
  22.         _game->doMove( _position );
  23.         _game->setActiveMove(false);
  24.     }
  25. }

На этом с организацией ходов можно закончить.

паттерны, qt, cpp

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

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

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