您的位置:首页 > 其它

中国象棋游戏Chess(3) - 实现走棋规则

2015-07-28 18:01 375 查看
棋盘的绘制和走棋参看博文:中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制,中国象棋游戏Chess(2)
- 走棋

现在重新整理之前写的代码,并且对于每个棋子的走棋规则都进行了限制,不像之前那样每个棋子都可以走到任意位置。

也实现了红先黑后,并且每一方走一步,交替走棋的功能。



直接看代码:

// Board.h
// Board类实现了棋盘的绘制以及显示
//

#ifndef BOARD_H
#define BOARD_H

#include <QFrame>
#include "Stone.h"
#include "Step.h"
#include <QVector>

class Board : public QFrame
{
Q_OBJECT
public:
explicit Board(QWidget *parent = 0);
~Board();

/*===> 游戏数据 <===*/
Stone _s[32]; // 定义32个棋子
int _r; // 棋子的半径
QPoint _off;
bool _bSide;

QVector<Step*> _steps; // 悔棋时使用

/*===> 游戏状态 <===*/
int _selectid;
bool _bRedTurn;
void init(bool bRedSide);

/*===> 绘图函数 <===*/
virtual void paintEvent(QPaintEvent *);
void DrawBackground(); // 设置背景颜色
void drawPlate(QPainter& p); // 绘制棋盘
void drawPlace(QPainter& p); // 绘制九宫格
void drawInitPosition(QPainter& p); // 绘制炮兵位置上的十字
void drawInitPosition(QPainter& p, int row, int col);
void drawStone(QPainter &p); // 绘制棋子
void drawStone(QPainter &p, int id);

/*===> 坐标转换相关函数 <===*/
QPoint center(int row, int col); // 返回棋盘行列对应的像素坐标
QPoint center(int id);
QPoint topLeft(int row, int col);
QPoint topLeft(int id);
QRect cell(int row, int col);
QRect cell(int id);

bool getClickRowCol(QPoint pt, int &row, int &col);

/*===> 帮助函数 <===*/
QString name(int id);
bool red(int id);
bool sameColor(int id1, int id2);
int getStoneId(int row, int col); // 获取行row列col上的棋子id
void killStone(int id);
void reliveStone(int id);
void moveStone(int moveid, int row, int col);
bool isDead(int id); // 判断id棋子是否死亡

/*===> 移动相关函数 <===*/
virtual void mouseReleaseEvent(QMouseEvent *ev); // 鼠标点击象棋并释放鼠标时候触发
void click(QPoint pt);
virtual void click(int id, int row, int col);
void trySelectStone(int id);
void tryMoveStone(int killid, int row, int col);
void moveStone(int moveid, int killid, int row, int col);
void saveStep(int moveid, int killid, int row, int col, QVector<Step*>& steps);
void backOne();
void back(Step* step);
virtual void back();

/*===> 移动规则 <===*/
bool canMove(int moveid, int killid, int row, int col);
bool canMoveJiang(int moveid, int killid, int row, int col);
bool canMoveShi(int moveid, int, int row, int col);
bool canMoveXiang(int moveid, int, int row, int col);
bool canMoveChe(int moveid, int, int row, int col);
bool canMoveMa(int moveid, int killid, int row, int col);
bool canMovePao(int moveid, int killid, int row, int col);
bool canMoveBing(int moveid, int killid, int row, int col);

bool canSelect(int id);

/*===> 移动规则相关的几个帮助函数 <===*/
int relation(int row1, int col1, int row, int col); // 得到两点之间的关系值
bool isBottomSide(int id); // 判断id棋子是否在棋盘下方
// 判断两个点是否在同一条直线上,炮和车走棋的时候需要用到
int getStoneCountAtLine(int row1, int col1, int row2, int col2);

signals:

public slots:
void slotBack();
};

#endif // BOARD_H


// Board.cpp

#include "Board.h"
#include <QPainter> // 绘制棋盘需要
#include <QMouseEvent>
#include <QDebug>
#define GetRowCol(__row, __col, __id) \
int __row = _s[__id]._row;\
int __col = _s[__id]._col

Board::Board(QWidget *parent) : QFrame(parent)
{
this->_r = 20;
setMinimumSize(_r * 18 + 1, _r * 20 + 1);
init(true);
}

Board::~Board()
{

}

void Board::init(bool bRedSide)
{
for (int i = 0; i < 32; ++i) {
_s[i].init(i);
}
if (bRedSide) {
for (int i = 0; i < 32; ++i) {
_s[i].rotate();
}
}

_selectid = -1;
_bRedTurn = true;
_bSide = bRedSide;
update();
}

// 绘制棋盘
void Board::paintEvent(QPaintEvent *)
{
DrawBackground(); // 绘制背景颜色

int r = height() / 20;
_r = r;
_off = QPoint(r + 1, r + 1);

QPainter p(this);
p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

p.save();
drawPlate(p);
p.restore();

p.save();
drawPlace(p);
p.restore();

p.save();
drawInitPosition(p);
p.restore();

p.save();
drawStone(p);
p.restore();
}

// 设置背景颜色
void Board::DrawBackground()
{
QPalette p = this->palette();
p.setColor(QPalette::Window, QColor(224, 255, 255));
this->setPalette(p);
}

// 绘制棋盘
void Board::drawPlate(QPainter &p)
{
// 绘制10条横线
for (int i = 0; i < 10; ++i) {
if (i == 0 || i == 9) { // 上下边框画笔设置的粗一些
p.setPen(QPen(Qt::black, 3, Qt::SolidLine));
}
else {
p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
}
p.drawLine(center(i, 0), center(i, 8));
}

// 绘制9条竖线
for (int i = 0; i < 9; ++i) {
if (i == 0 || i == 8) { // 中间有楚河汉界,不能画通
p.setPen(QPen(Qt::black, 3, Qt::SolidLine));
p.drawLine(center(0, i), center(9, i));
}
else {
p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
p.drawLine(center(0, i), center(4, i));
p.drawLine(center(5, i), center(9, i));
}
}
}

// 绘制九宫格
void Board::drawPlace(QPainter &p)
{
p.drawLine(center(0, 3), center(2, 5));
p.drawLine(center(2, 3), center(0, 5));

p.drawLine(center(9, 3), center(7, 5));
p.drawLine(center(7, 3), center(9, 5));
}

// 绘制炮兵位置上的十字
void Board::drawInitPosition(QPainter &p)
{
drawInitPosition(p, 3, 0);
drawInitPosition(p, 3, 2);
drawInitPosition(p, 3, 4);
drawInitPosition(p, 3, 6);
drawInitPosition(p, 3, 8);

drawInitPosition(p, 6, 0);
drawInitPosition(p, 6, 2);
drawInitPosition(p, 6, 4);
drawInitPosition(p, 6, 6);
drawInitPosition(p, 6, 8);

drawInitPosition(p, 2, 1);
drawInitPosition(p, 2, 7);

drawInitPosition(p, 7, 1);
drawInitPosition(p, 7, 7);
}

void Board::drawInitPosition(QPainter& p, int row, int col)
{
QPoint pt = center(row, col);
QPoint off = QPoint(_r / 6, _r / 6);
int len = _r / 3;

QPoint ptStart;
QPoint ptEnd;

if (col != 0) {
// 左上角
ptStart = QPoint(pt.x() - off.x(), pt.y() - off.y());
ptEnd = ptStart + QPoint(-len, 0);
p.drawLine(ptStart, ptEnd);
ptEnd = ptStart + QPoint(0, -len);
p.drawLine(ptStart, ptEnd);

// 左下角
ptStart = QPoint(pt.x() - off.x(), pt.y() + off.y());
ptEnd = ptStart + QPoint(-len, 0);
p.drawLine(ptStart, ptEnd);
ptEnd = ptStart + QPoint(0, +len);
p.drawLine(ptStart, ptEnd);
}

if (col != 8) {
// 右下角
ptStart = QPoint(pt.x() + off.x(), pt.y() + off.y());
ptEnd = ptStart + QPoint(+len, 0);
p.drawLine(ptStart, ptEnd);
ptEnd = ptStart + QPoint(0, +len);
p.drawLine(ptStart, ptEnd);

// 右上角
ptStart = QPoint(pt.x() + off.x(), pt.y() - off.y());
ptEnd = ptStart + QPoint(+len, 0);
p.drawLine(ptStart, ptEnd);
ptEnd = ptStart + QPoint(0, -len);
p.drawLine(ptStart, ptEnd);
}
}

// 绘制棋子
void Board::drawStone(QPainter &p)
{
for (int i = 0; i < 32; ++i) {
drawStone(p, i);
}
}

void Board::drawStone(QPainter &p, int id)
{
if (isDead(id)) {
return;
}

QColor color;
if (red(id)) {
color = Qt::red;
}
else {
color = Qt::black;
}

p.setPen(QPen(QBrush(color), 2));

if (id == _selectid) {
p.setBrush(Qt::gray);
}
else {
p.setBrush(Qt::yellow);
}

p.drawEllipse(cell(id));

p.setFont(QFont("system", _r * 1.2, 700)); // 设置字体大小和类型
p.drawText(cell(id), name(id), QTextOption(Qt::AlignCenter));
}

// 返回棋盘行列对应的像素坐标
QPoint Board::center(int row, int col)
{
QPoint pt(_r * col * 2, _r * row * 2);
return pt + _off;
}

// 重载center函数,方便调用
QPoint Board::center(int id)
{
return center(_s[id]._row, _s[id]._col);
}

QPoint Board::topLeft(int row, int col)
{
return center(row, col) - QPoint(_r, _r);
}

QPoint Board::topLeft(int id)
{
return center(id) - QPoint(_r, _r);
}

QRect Board::cell(int row, int col)
{
return QRect(topLeft(row, col), QSize(_r * 2 - 1, _r * 2 - 1));
}

QRect Board::cell(int id)
{
return QRect(topLeft(id), QSize(_r * 2 - 1, _r * 2 - 1));
}

// 判断点击位置属于哪个顶点
// 返回值为bool类型是为了处理点击在棋盘外的情况
bool Board::getClickRowCol(QPoint pt, int &row, int &col)
{
row = pt.y() / (2 * _r) - 1;
col = pt.x() / (2 * _r) - 1;

QPoint c = center(row, col);
int dx = c.x() - pt.x();
int dy = c.y() - pt.y();
int dist = dx * dx + dy * dy; // 和鼠标所处矩形左上顶点的距离
if (dist < _r * _r) {
return true;
}

row += 1;
c = center(row, col);
dx = c.x() - pt.x();
dy = c.y() - pt.y();
dist = dx * dx + dy * dy; // 和鼠标所处矩形左下顶点的距离
if (dist < _r * _r) {
return true;
}

row -= 1;
col += 1;
c = center(row, col);
dx = c.x() - pt.x();
dy = c.y() - pt.y();
dist = dx * dx + dy * dy; // 和鼠标所处矩形右上顶点的距离
if (dist < _r * _r) {
return true;
}

row += 1;
c = center(row, col);
dx = c.x() - pt.x();
dy = c.y() - pt.y();
dist = dx * dx + dy * dy; // 和鼠标所处矩形右下顶点的距离
if (dist < _r * _r) {
return true;
}

return false;
}

QString Board::name(int id)
{
return _s[id].name();
}

bool Board::red(int id)
{
return _s[id]._red;
}

bool Board::sameColor(int id1, int id2)
{
if (id1 == -1 || id2 == -1) {
return false;
}
return red(id1) == red(id2);
}

int Board::getStoneId(int row, int col)
{
for (int i = 0; i < 32; ++i) {
if (_s[i]._row == row &&
_s[i]._col == col && !isDead(i)) {
return i;
}
}
return -1; // 如果不是棋子返回-1
}

void Board::killStone(int id)
{
if (id == -1) {
return;
}
_s[id]._dead = true;
}

void Board::reliveStone(int id)
{
if (id == -1) {
return;
}
_s[id]._dead = false;
}

void Board::moveStone(int moveid, int row, int col)
{
_s[moveid]._row = row;
_s[moveid]._col = col;

_bRedTurn = !_bRedTurn;
}

bool Board::isDead(int id)
{
if (id == -1) {
return true;
}
return _s[id]._dead;
}

void Board::saveStep(int moveid, int killid, int row, int col, QVector<Step*>& steps)
{
GetRowCol(row1, col1, moveid);
Step* step = new Step;
step->_colFrom = col1;
step->_colTo = col;
step->_rowFrom = row1;
step->_rowTo = row;
step->_moveid = moveid;
step->_killid = killid;

steps.append(step);
}

void Board::backOne()
{
if (this->_steps.size() == 0) {
return;
}

Step* step = this->_steps.last();
_steps.removeLast();
back(step);

update();
delete step;
}

void Board::back(Step* step)
{
reliveStone(step->_killid);
moveStone(step->_moveid, step->_rowFrom, step->_colFrom);
}

void Board::back()
{
backOne();
}

void Board::mouseReleaseEvent(QMouseEvent *ev)
{
if (ev->button() != Qt::LeftButton) { // 排除鼠标右键点击
return;
}

click(ev->pos());
}

void Board::click(QPoint pt)
{
// 看有没有点中象棋
// 将pt转化成象棋的行列值
// 判断这个行列值上面有没有棋子
int row, col;
bool bClicked = getClickRowCol(pt, row, col);
if (!bClicked) {
return;
}

int id = getStoneId(row, col);
click(id, row, col);

}

void Board::click(int id, int row, int col)
{
if (this->_selectid == -1) { // 如果点中的棋子之前未被选中
trySelectStone(id);
}
else {
tryMoveStone(id, row, col);
}
}

void Board::trySelectStone(int id)
{
if (id == -1) {
return;
}

if (!canSelect(id)) {
return;
}

_selectid = id;
update();
}

void Board::tryMoveStone(int killid, int row, int col)
{
if (killid != -1 && sameColor(killid, _selectid)) {
trySelectStone(killid);
return;
}

bool ret = canMove(_selectid, killid, row, col);
if (ret) {
moveStone(_selectid, killid, row, col);
_selectid = -1;
update();
}
}

void Board::moveStone(int moveid, int killid, int row, int col)
{
saveStep(moveid, killid, row, col, _steps);

killStone(killid);
moveStone(moveid, row, col);
}

bool Board::canMove(int moveid, int killid, int row, int col)
{
// 如果moveid和killid颜色相同,则不能移动,还需要换选择
if (_s[moveid]._red == _s[killid]._red) {
_selectid = killid;
update();
return false;
}

switch (_s[moveid]._type) {
case Stone::JIANG:
return canMoveJiang(moveid, killid, row, col);

case Stone::SHI:
return canMoveShi(moveid, killid, row, col);

case Stone::XIANG:
return canMoveXiang(moveid, killid, row, col);

case Stone::CHE:
return canMoveChe(moveid, killid, row, col);

case Stone::MA:
return canMoveMa(moveid, killid, row, col);

case Stone::PAO:
return canMovePao(moveid, killid, row, col);

case Stone::BING:
return canMoveBing(moveid, killid, row, col);
}

return true;

}

bool Board::canMoveJiang(int moveid, int killid, int row, int col)
{
// 可直接吃对方将
if (killid != -1 && _s[killid]._type == Stone::JIANG)
{
return canMoveChe(moveid, killid, row, col);
}

GetRowCol(row1, col1, moveid);
int r = relation(row1, col1, row, col);
if (r != 1 || r != 10) {
return false;
}

if (col < 3 || col > 5) {
return false;
}

if (isBottomSide(moveid)) {
if (row < 7) {
return false;
}
}
else {
if (row > 2) {
return false;
}
}

return true;
}

bool Board::canMoveShi(int moveid, int, int row, int col)
{

// 移动步长一个格子对角线
GetRowCol(row1, col1, moveid);
int r = relation(row1, col1, row, col);
if (r != 11) {
return false;
}

if (col < 3 || col > 5) {
return false;
}

if (isBottomSide(moveid)) {
if (row < 7) {
return false;
}
}
else {
if (row > 2) {
return false;
}
}

return true;
}

bool Board::canMoveXiang(int moveid, int, int row, int col)
{
GetRowCol(row1, col1, moveid);
int r = relation(row1, col1, row, col);
if (r != 22) { // 象走田,所以r应该等于22
return false;
}

// 看象眼有没有棋子
int rEye = (row + row1) / 2;
int cEye = (col + col1) / 2;
if (getStoneId(rEye, cEye) != -1) {
return false;
}

// 判断是否在棋盘的下方
if (isBottomSide(moveid)) {
if (row < 4) {
return false;
}
}
else {
if (row > 5) {
return false;
}
}

return true;
}

bool Board::canMoveChe(int moveid, int, int row, int col)
{
GetRowCol(row1, col1, moveid);
int ret = getStoneCountAtLine(row1, col1, row, col);
if (ret == 0) { // 在一行,并且中间没有棋子
return true;
}

return false;
}

bool Board::canMoveMa(int moveid, int, int row, int col)
{
GetRowCol(row1, col1, moveid);
int r = relation(row1, col1, row, col);
// 首先判断马要走马字
if (r != 12 && r != 21) {
return false;
}

// 判断有没有蹩马腿的情况
if (r == 12) { // 列相差等于2
if (getStoneId(row1, (col + col1) / 2) != -1) {
return false;
}
}
else { // 行相差等于2
if (getStoneId((row + row1) / 2, col1) != -1) {
return false;
}
}
return true;
}

bool Board::canMovePao(int moveid, int killid, int row, int col)
{
GetRowCol(row1, col1, moveid);
int ret = getStoneCountAtLine(row, col, row1, col1);
if (killid != -1) { // 如果炮要吃对方的棋子
if (ret == 1) { // 中间有一个棋子,可以走
return true;
}
}
else { // 如果炮不吃棋子
if (ret == 0) { // 中间没有棋子,可以走
return true;
}
}

return false;
}

bool Board::canMoveBing(int moveid, int, int row, int col)
{
GetRowCol(row1, col1, moveid);
int r = relation(row1, col1, row, col);
// 首先判断兵只能走一步
if (r != 1 && r != 10) {
return false;
}

if (isBottomSide(moveid)) { // 下面一方的棋子
if (row > row1) { // 如果目标行大于原始行,相当于并在后退
return false;
}
if (row1 >= 5 && row == row1) { // 还没有过河就想横着走
return false;
}
}
else { // 上面一方的棋子
if (row1 > row) { // 如果目标行小于原始行,相当于兵在后退
return false;
}
if (row <= 4 && row == row1) { // 还没有过河就想横着走
return false;
}
}
return true;
}

bool Board::canSelect(int id)
{
return _bRedTurn == _s[id]._red;
}

int Board::relation(int row1, int col1, int row, int col)
{
return qAbs(row1 - row) * 10 + qAbs(col1 - col);
}

bool Board::isBottomSide(int id)
{
return _bSide == _s[id]._red;
}

int Board::getStoneCountAtLine(int row1, int col1, int row2, int col2)
{
int ret = 0;

// 首先判断两个棋子是否在同一条直线上,如果不在同一条直线上,直接返回-1
if (row1 != row2 && col1 != col2) {
return -1;
}
if (row1 == row2 && col1 == col2) {
return -1;
}

// 计算两个棋子之间的有多少个棋子
if (row1 == row2) { // 在同一行
int min = col1 < col2 ? col1 : col2;
int max = col1 > col2 ? col1 : col2;
for (int col = min + 1; col < max; ++col) {
if (getStoneId(row1, col) != -1) {
++ret;
}
}
}
else { // 在同一列
int min = row1 < row2 ? row1 : row2;
int max = row1 > row2 ? row1 : row2;
for (int row = min + 1; row < max; ++row) {
if (getStoneId(row, col1) != -1) {
++ret;
}
}
}

return ret;
}

void Board::slotBack()
{
back();
}


#ifndef STEP_H
#define STEP_H

#include <QObject>

class Step : public QObject
{
Q_OBJECT
public:
explicit Step(QObject *parent = 0);
~Step();

int _moveid;
int _killid;
int _rowFrom;
int _colFrom;
int _rowTo;
int _colTo;

signals:

public slots:
};

#endif // STEP_H


// Stone.h
// 棋子类,存储了棋子的基础信息

#ifndef STONE_H
#define STONE_H

#include <QString>

class Stone
{
public:
Stone();
~Stone();

enum TYPE{JIANG, CHE, PAO, MA, BING, SHI, XIANG};
int _row;
int _col;
TYPE _type;

int _id;
bool _dead;
bool _red;

// 棋子的初始化
void init(int id);

// 判断_type返回相应字符串
QString name();

void rotate(); // 翻转棋盘

};

#endif // STONE_H


// Stone.cpp

#include "Stone.h"
#include <QDebug>

Stone::Stone()
{

}

Stone::~Stone()
{

}

void Stone::init(int id)
{
struct {
int row, col;
Stone::TYPE type;
} pos[16] = {
{0, 0, Stone::CHE},
{0, 1, Stone::MA},
{0, 2, Stone::XIANG},
{0, 3, Stone::SHI},
{0, 4, Stone::JIANG},
{0, 5, Stone::SHI},
{0, 6, Stone::XIANG},
{0, 7, Stone::MA},
{0, 8, Stone::CHE},

{2, 1, Stone::PAO},
{2, 7, Stone::PAO},
{3, 0, Stone::BING},
{3, 2, Stone::BING},
{3, 4, Stone::BING},
{3, 6, Stone::BING},
{3, 8, Stone::BING},
};

//_id = id;
_dead = false;
_red = id < 16;

if (id < 16) { // 上方的棋子
this->_row = pos[id].row;
this->_col = pos[id].col;
this->_type = pos[id].type;
}
else { // 下方的棋子
this->_row = 9 - pos[id - 16].row;
this->_col = 8 - pos[id - 16].col;
this->_type = pos[id - 16].type;
}
}

QString Stone::name()
{
switch (this->_type)
{
case CHE:
return "车";
case MA:
return "马";
case PAO:
return "炮";
case BING:
return "兵";
case JIANG:
return "将";
case SHI:
return "士";
case XIANG:
return "相";
}
return "错误";
}

void Stone::rotate()
{
this->_col = 8 - this->_col;
this->_row = 9 - this->_row;
}


// main.cpp
// Chess游戏主程序
//
// Created by Lucifer Zhang on 2015-07-21.
// Copyright (c) 2015 Lucifer Zhang. All rights reserved.

#include <QApplication>
#include "Board.h"

int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Board board;
board.show();
return app.exec();
}


工程代码详情:Github
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: