一,源码
board.h
#ifndef BOARD_H
#define BOARD_H
#include <QWidget>
#include "Stone.h"
class Board : public QWidget
{
Q_OBJECT
public:
explicit Board(QWidget *parent = 0);
bool _bRedTurn; // 红方先走
int _currentPlayer; // 当前玩家,1为红方,-1为黑方
Stone _s[32];
int _r; /* 棋子的半径 */
int _selectid;
/* 返回象棋棋盘行列对应的像素坐标 */
QPoint center(int row, int col);
QPoint center(int id);
bool getRowCol(QPoint pt, int& row, int& col);
void drawStone(QPainter& painter, int id);
void paintEvent(QPaintEvent *);
void mouseReleaseEvent(QMouseEvent *);
bool canMove(int moveid, int row, int col, int killid);
bool canMoveJIANG(int moveid, int row, int col, int killid);
bool canMoveSHI(int moveid, int row, int col, int killid);
bool canMoveXIANG(int moveid, int row, int col, int killid);
bool canMoveCHE(int moveid, int row, int col, int killid);
bool canMoveMA(int moveid, int row, int col, int killid);
bool canMovePAO(int moveid, int row, int col, int killid);
bool canMoveBING(int moveid, int row, int col, int killid);
bool isStoneAt(int row, int col);
void saveGameState();
signals:
public slots:
};
#endif // BOARD_H
Stone.h
#ifndef STONE_H
#define STONE_H
#include <QString>
class Stone
{
public:
Stone();
~Stone();
int getRow()const
{
return _row;
}
int getCol()const
{
return _col;
}
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)
{
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)
{
_row = pos[id].row;
_col = pos[id].col;
_type = pos[id].type;
}
else
{
_row = 9-pos[id-16].row;
_col = 8-pos[id-16].col;
_type = pos[id-16].type;
}
}
QString getText()
{
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 "错误";
}
};
#endif // STONE_H
board.cpp
#include "Board.h"
#include <QPainter>
#include <QMouseEvent>
#include <QMessageBox>
#include<QTextStream>
Board::Board(QWidget *parent) :
QWidget(parent)
{
for(int i=0; i<32; ++i)
{
_s[i].init(i);
}
_selectid = -1;
_bRedTurn = true;
}
void Board::paintEvent(QPaintEvent*)
{
QPainter painter(this);
int d = 40;
_r = d / 2;
// 画10横线
for (int i = 1; i <= 10; ++i)
{
painter.drawLine(QPoint(d, i * d), QPoint(9 * d, i * d));
}
// 画9竖线
for (int i = 1; i <= 9; ++i)
{
if (i == 1 || i == 9)
painter.drawLine(QPoint(i * d, d), QPoint(i * d, 10 * d));
else
{
painter.drawLine(QPoint(i * d, d), QPoint(i * d, 5 * d));
painter.drawLine(QPoint(i * d, 6 * d), QPoint(i * d, 10 * d));
}
}
// 九宫格
painter.drawLine(QPoint(4 * d, 1 * d), QPoint(6 * d, 3 * d));
painter.drawLine(QPoint(6 * d, 1 * d), QPoint(4 * d, 3 * d));
painter.drawLine(QPoint(4 * d, 8 * d), QPoint(6 * d, 10 * d));
painter.drawLine(QPoint(6 * d, 8 * d), QPoint(4 * d, 10 * d));
// 绘制32个棋子
for (int i = 0; i < 32; ++i)
{
drawStone(painter, i);
}
}
QPoint Board::center(int row, int col)
{
QPoint ret;
ret.rx() = (col + 1) * _r * 2;
ret.ry() = (row + 1) * _r * 2;
return ret;
}
QPoint Board::center(int id)
{
return center(_s[id]._row, _s[id]._col);
}
void Board::drawStone(QPainter& painter, int id)
{
if (_s[id]._dead)
return;
QPoint c = center(id);
QRect rect = QRect(c.x() - _r, c.y() - _r, _r * 2, _r * 2);
if (id == _selectid)
painter.setBrush(QBrush(Qt::gray));
else
painter.setBrush(QBrush(Qt::yellow));
painter.setPen(Qt::black);
painter.drawEllipse(center(id), _r, _r);
if (_s[id]._red)
painter.setPen(Qt::red);
painter.setFont(QFont("system", _r, 700));
painter.drawText(rect, _s[id].getText(), QTextOption(Qt::AlignCenter));
}
bool Board::getRowCol(QPoint pt, int& row, int& col)
{
for (row = 0; row <= 9; row++)
{
for (col = 0; col <= 8; col++)
{
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;
}
}
return false;
}
bool Board::isStoneAt(int row, int col)
{
for (int i = 0; i < 32; ++i)
{
if (_s[i]._row == row && _s[i]._col == col && !_s[i]._dead)
{
return true;
}
}
return false;
}
bool Board::canMoveXIANG(int moveid, int row, int col, int killid)
{
int fromRow = _s[moveid]._row;
int fromCol = _s[moveid]._col;
int toRow = row;
int toCol = col;
if (_s[moveid]._red && toRow > 4) return false; // 红方象不能过河
if (!_s[moveid]._red && toRow < 5) return false; // 黑方象不能过河
int rowDir = toRow - fromRow;
int colDir = toCol - fromCol;
if (abs(rowDir) != 2 || abs(colDir) != 2) return false;
int checkRow = fromRow + rowDir / 2;
int checkCol = fromCol + colDir / 2;
if (isStoneAt(checkRow, checkCol)) return false; // 路径上有棋子
if (killid != -1 && _s[killid]._red == _s[moveid]._red) return false;
return true;
}
bool Board::canMoveCHE(int moveid, int row, int col, int killid)
{
int fromRow = _s[moveid]._row;
int fromCol = _s[moveid]._col;
int toRow = row;
int toCol = col;
if (fromRow != toRow && fromCol != toCol) return false;
// 计算移动的方向
int rowDir = fromRow < toRow ? 1 : (fromRow > toRow ? -1 : 0);
int colDir = fromCol < toCol ? 1 : (fromCol > toCol ? -1 : 0);
int checkRow = fromRow + rowDir;
int checkCol = fromCol + colDir;
while (checkRow != toRow || checkCol != toCol)
{
if (isStoneAt(checkRow, checkCol)) return false; // 路径上有棋子
checkRow += rowDir;
checkCol += colDir;
}
// 如果有棋子被吃,检查是否是对方的棋子
if (killid != -1 && _s[killid]._red == _s[moveid]._red) return false;
return true;
}
bool Board::canMoveMA(int moveid, int row, int col, int killid)
{
int fromRow = _s[moveid]._row;
int fromCol = _s[moveid]._col;
int toRow = row;
int toCol = col;
int rowDiff = toRow - fromRow;
int colDiff = toCol - fromCol;
// 检查移动是否是“日”字形状
if ((abs(rowDiff) == 2 && abs(colDiff) == 1) || (abs(rowDiff) == 1 && abs(colDiff) == 2))
{
// 检查是否有棋子蹩马腿
int legRow = fromRow + (rowDiff > 0 ? (rowDiff / 2) : -(abs(rowDiff) / 2));
int legCol = fromCol + (colDiff > 0 ? (colDiff / 2) : -(abs(colDiff) / 2));
if (!isStoneAt(legRow, legCol)) // 如果没有棋子蹩马腿
{
// 如果有棋子被吃,检查是否是对方的棋子
if (killid != -1 && _s[killid]._red != _s[moveid]._red)
{
return true; // 可以吃掉对方的棋子
}
else if (killid == -1)
{
return true;
}
}
}
return false; // 不满足移动条件
}
bool Board::canMovePAO(int moveid, int row, int col, int killid)
{
int fromRow = _s[moveid]._row;
int fromCol = _s[moveid]._col;
int toRow = row;
int toCol = col;
if (fromRow != toRow && fromCol != toCol) return false;
int rowDir = fromRow < toRow ? 1 : (fromRow > toRow ? -1 : 0);
int colDir = fromCol < toCol ? 1 : (fromCol > toCol ? -1 : 0);
// 检查移动路径上是否有其他棋子
int checkRow = fromRow + rowDir;
int checkCol = fromCol + colDir;
int jumpCount = 0;
while (checkRow != toRow || checkCol != toCol)
{
if (isStoneAt(checkRow, checkCol)) jumpCount++;
checkRow += rowDir;
checkCol += colDir;
}
if (killid != -1)
{
if (_s[killid]._red == _s[moveid]._red) return false;
if (jumpCount != 1) return false; // 必须跳过一个棋子
}
else
{
if (jumpCount != 0) return false; // 如果没有吃子,不能跳过棋子
}
return true;
}
bool Board::canMoveBING(int moveid, int row, int col, int killid)
{
int fromRow = _s[moveid]._row;
int fromCol = _s[moveid]._col;
int toRow = row;
int toCol = col;
if (fromRow != toRow && fromCol != toCol) return false;
int rowDiff = toRow - fromRow;
int colDiff = toCol - fromCol;
// 检查是否过河
if (_s[moveid]._red && toRow >= 5) // 红方兵过河
{
if (abs(rowDiff) != 1) return false; // 过河后只能直走一格
}
else if (!_s[moveid]._red && toRow <= 4) // 黑方兵过河
{
if (abs(rowDiff) != 1) return false; // 过河后只能直走一格
}
else // 未过河
{
if (abs(rowDiff) != 1) return false; // 未过河只能直走一格
}
if (killid != -1 && _s[killid]._red == _s[moveid]._red) return false;
}
bool Board::canMoveJIANG(int moveid, int row, int col, int killid)
{
if (_s[moveid]._red)
{
if (row > 2)return false;
}
else
{
if (row < 7)return false;
}
if (col < 3) return false;
if (col > 5) return false;
int rowDir = _s[moveid]._row - row;
int colDir = _s[moveid]._col - col;
int d = abs(rowDir) * 10 + abs(colDir);
if (d == 1 || d == 10)
return true;
return false;
}
bool Board::canMoveSHI(int moveid, int row, int col, int killid)
{
if (_s[moveid]._red)
{
if (row > 2)return false;
}
else
{
if (row < 7)return false;
}
if (col < 3) return false;
if (col > 5) return false;
int rowDir = _s[moveid]._row - row;
int colDir = _s[moveid]._col - col;
int d = abs(rowDir) * 10 + abs(colDir);
if (d == 11)
return true;
return false;
}
bool Board::canMove(int moveid, int row, int col, int killid)
{
// 如果移动的棋子和被吃的棋子颜色相同,则切换选择
if (_s[moveid]._red == _s[killid]._red && killid != -1)
{
_selectid = killid;
update();
return false;
}
// 判断是否是主将被吃
if (_s[killid]._type == Stone::JIANG && _s[killid]._red != _s[moveid]._red)
{
QString winner = _s[moveid]._red ? "红方" : "黑方";
QMessageBox::information(this, "胜利", "恭喜," + winner + "赢了!");
return false; // 停止游戏
}
// 根据棋子类型调用相应的移动规则函数
switch (_s[moveid]._type)
{
case Stone::JIANG:
return canMoveJIANG(moveid, row, col, killid);
break;
case Stone::SHI:
return canMoveSHI(moveid, row, col, killid);
break;
case Stone::XIANG:
return canMoveXIANG(moveid, row, col, killid);
break;
case Stone::CHE:
return canMoveCHE(moveid, row, col, killid);
break;
case Stone::MA:
return canMoveMA(moveid, row, col, killid);
break;
case Stone::PAO:
return canMovePAO(moveid, row, col, killid);
break;
case Stone::BING:
return canMoveBING(moveid, row, col, killid);
break;
default:
return false;
}
return true;
}
void Board::saveGameState() {
// 假设你有一个数组来存储棋盘的状态,比如棋子的类型和位置
QString fileName = "game_state.txt";
QFile file("game.txt");
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(this, "保存棋局状态", "无法打开文件进行保存!");
return;
}
QTextStream out(&file);
for (int i = 0; i < 32; ++i) {
out << _s[i].getText() << "," << _s[i].getRow() << "," << _s[i].getCol() << "\n";
}
file.close();
QMessageBox::information(this, "保存棋局状态", "棋局状态已成功保存!");
}
void Board::mouseReleaseEvent(QMouseEvent *ev)
{
QPoint pt = ev->pos();
// 将pt转化成象棋的行列值
// 判断这个行列值上面有没有棋子
int row, col;
bool bRet = getRowCol(pt, row, col);
if(bRet == false) // 点到棋盘外
return;
int i;
int clickid = -1;
for(i=0;i<32;++i)
{
if(_s[i]._row == row && _s[i]._col == col && _s[i]._dead== false)
{
clickid = i;
break;
}
}
if(_selectid == -1)
{
if(clickid != -1)
{
if(_bRedTurn == _s[clickid]._red)
{
_selectid = clickid;
update();
}
}
}
else
{
if(canMove(_selectid, row, col, clickid))
{
/*走棋*/
_s[_selectid]._row = row;
_s[_selectid]._col = col;
if(clickid != -1)
{
_s[clickid]._dead = true;
}
_selectid = -1;
_bRedTurn = !_bRedTurn;
update();
}
else
{
// 不能移动棋子,给出提示
QMessageBox::information(this, "提示", "该棋子不能移动到指定的位置!");
// 如果点击的是另一个相同颜色的棋子,则切换选择
if(clickid != -1 && _s[clickid]._red == _s[_selectid]._red)
{
_selectid = clickid;
update();
}
else
{
_selectid = -1; // 取消选择
update();
}
}
}
}
main.cpp
#include <QApplication>
#include "Board.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Board board;
board.show();
return app.exec();
}
Stone.cpp
#include "Stone.h"
Stone::Stone()
{
}
Stone::~Stone()
{
}
二,具体分析
棋盘的绘制,棋子的绘制,以及选中棋子进行一系列操作都使用QT的库函数,所以在创建
board类时我们需要选择基于Qwidge作为父类
board.cpp是实现绘制棋盘,棋子,以及棋子运行逻辑的文件
Stone.h包含了棋子类