项目实战:Qt5/C++:QT象棋【初版】
项目实战:Qt5/C++:QT象棋【初版】
编辑环境:win10_x64 /Qt5.4.1
项目:Qt 象棋
项目简介:一开始还在想,是准备使用Qt写一个IM即时通讯的,既可以增加自己对这个的方面的认知,也可以,但是想一想,时间也不是很多了,大三都快结束了,之前一个星期,写了一个象棋,计划是分成三个功能的:[1]人机对战(单人游戏); [2]双人模式(单PC端); [3]socket 网络双人游戏(多PC端)。而初始阶段,还只是完成了 [2]双人模式(单PC端)的功能,但是后面的功能是在这些基础上面,直接进行一些人工智能,电脑根据玩家的下法,通过指定的规则,来进行可能得算法,从而下一些可能是的的算法步骤。而最后的socket的双人PC端进行游戏,在现在现在基础上面加上Socket编程的类就可以了。
其他:后续还是会继续更新的,当有空的时候是会继续完善后面的功能的。
==================================================================
项目运行效果
==================================================================
==================================================================
项目思路分析:
==================================================================
步骤:
1.绘画棋盘
2.绘画棋子
3.棋盘行列值和屏幕之间的像素值之间进行切换
4.象棋轮流下
5.制定象棋的具体规则
6.屏幕重绘
==================================================================
项目主要源码部分:
==================================================================
/棋盘和象棋走法类
#ifndef BOARD_H
#define BOARD_H
#include <QWidget>
#include "Stone.h"
namespace Ui {
class Board;
}
class Board : public QWidget
{
Q_OBJECT
public:
explicit Board(QWidget *parent = 0);
~Board();
private:
bool isDead(int id);
int getStoneId(int row, int col);
//车 炮 的功能辅助函数 判断两个点是否在一个直线上面,且返回直线之间的棋子个数
int getStoneCountAtLine(int row1, int col1, int row2, int col2);
public:
//绘画棋盘
virtual void paintEvent(QPaintEvent *);
//象棋的棋盘的坐标转换成界面坐标
QPoint center(int row, int col);
QPoint center(int id);
//绘画单个具体的棋子
void drawStone(QPainter& painter, int id);
//界面坐标转换成棋盘的行列值[获取鼠标点击的像素坐标,是位于棋盘的哪一个行列值]
bool getRowCol(QPoint pt, int& row, int& col);
//鼠标点击事件
virtual void mousePressEvent(QMouseEvent *);
//象棋移动的规则[将 士 象 马 车 炮 兵]
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 killId, int row, int col);
bool canMoveXIANG(int moveId, int killId, int row, int col);
bool canMoveMA(int moveId, int killId, int row, int col);
bool canMoveCHE(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);
public:
Stone _stone[32];
int _r; //棋子半径
int _offset; //距离界面的边距
int _d; //间距为50px
int _selectId; //IS? 选中棋子[-1:选棋子 || 非-1:走棋子]
int _clickId; //点击鼠标选中棋子的ID
bool _bRedTrue; //红棋先下标志
private:
Ui::Board *ui;
};
#endif // BOARD_H
#include "Board.h"
#include "ui_Board.h"
#include <QPainter>
#include <QMouseEvent>
Board::Board(QWidget *parent) :
QWidget(parent),
ui(new Ui::Board)
{
//初始化32个象棋
for(int i = 0; i <= 31; i++)
{
_stone[i].initialize(i);
}
_selectId = -1; //IS? 选中棋子[-1:选棋子 || 非-1:走棋子]
_bRedTrue = true;
ui->setupUi(this);
}
Board::~Board()
{
delete ui;
}
int Board::getStoneId(int row, int col)
{
for(int i=0; i<32; ++i)
{
if(_stone[i]._row == row && _stone[i]._col == col && !isDead(i))
return i;
}
return -1;
}
bool Board::isDead(int id)
{
if(id == -1)return true;
return _stone[id]._deal;
}
int Board::getStoneCountAtLine(int row1, int col1, int row2, int col2)
{
int ret = 0;
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 ? col2 : col1;
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 ? row2 : row1;
for(int row = min+1; row<max; ++row)
{
if(getStoneId(row, col1) != -1) ++ret;
}
}
return ret;
}
//绘画棋盘
void Board::paintEvent(QPaintEvent *)
{
QPainter painter(this);
_offset = 60; //距离界面的边距
_d = 90; //间距为50px
_r = _d/2; //棋子半径为d/2
//*******************绘画棋盘*******************
//绘画10条横线
for(int i = 0; i <= 9; i++)
{
painter.drawLine(QPoint(_offset, _offset+i*_d), QPoint(_offset+8*_d, _offset+i*_d));
}
//绘画9条竖线
for(int i = 0; i <= 8; i++)
{
if(i==0 || i==8)
{
painter.drawLine(QPoint(_offset+i*_d, _offset), QPoint(_offset+i*_d, _offset+9*_d));
}
else
{
painter.drawLine(QPoint(_offset+i*_d, _offset), QPoint(_offset+i*_d, _offset+4*_d));
painter.drawLine(QPoint(_offset+i*_d, _offset+5*_d), QPoint(_offset+i*_d, _offset+9*_d));
}
}
//绘画4条斜线
painter.drawLine(QPoint(_offset+3*_d, _offset), QPoint(_offset+5*_d, _offset+2*_d));
painter.drawLine(QPoint(_offset+3*_d, _offset+2*_d), QPoint(_offset+5*_d, _offset));
painter.drawLine(QPoint(_offset+3*_d, _offset+7*_d), QPoint(_offset+5*_d, _offset+9*_d));
painter.drawLine(QPoint(_offset+3*_d, _offset+9*_d), QPoint(_offset+5*_d, _offset+7*_d));
QRect rect1(_offset+_d, _offset+4*_d, _d, _d);
QRect rect2(_offset+2*_d, _offset+4*_d, _d, _d);
QRect rect3(_offset+5*_d, _offset+4*_d, _d, _d);
QRect rect4(_offset+6*_d, _offset+4*_d, _d, _d);
painter.setFont(QFont("隶书", _r, 800));
painter.drawText(rect1, "楚", QTextOption(Qt::AlignCenter));
painter.drawText(rect2, "河", QTextOption(Qt::AlignCenter));
painter.drawText(rect3, "汉", QTextOption(Qt::AlignCenter));
painter.drawText(rect4, "界", QTextOption(Qt::AlignCenter));
//*******************绘画棋子*******************
for(int i = 0; i <= 31; i++)
{
drawStone(painter, i);
}
}
//象棋的棋盘的坐标转换成界面坐标
QPoint Board::center(int row, int col)
{
QPoint rePoint;
//这里注意坐标的转换
rePoint.rx() = col*_d+_offset;
rePoint.ry() = row*_d+_offset;
return rePoint;
}
//重载:坐标转换
QPoint Board::center(int id)
{
return center(_stone[id]._row, _stone[id]._col);
}
//绘画单个具体的棋子
void Board::drawStone(QPainter &painter, int id)
{
if(_stone[id]._deal)
return;
QPoint temp = center(id);
QRect rect(temp.x()-_r, temp.y()-_r, _d, _d);
if(_selectId == id)
painter.setBrush(QBrush(QColor(64,64,196, 80)));
else
painter.setBrush(QBrush(QColor(64,64,196, 10)));
painter.setPen(QColor(0, 0, 0));
painter.drawEllipse(center(id), _r, _r); //绘画圆形
painter.setFont(QFont("华文行楷", _r, 700));
if(id < 16)
{
painter.setPen(QColor(255, 0, 0));
}
else
{
painter.setPen(QColor(0, 0, 0));
}
painter.drawText(rect, _stone[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 temp = center(row, col);
int x = temp.x()-pt.x();
int y = temp.y()-pt.y();
if(x*x+y*y < _r*_r)
return true;
}
}
}
//鼠标点击事件
void Board::mousePressEvent(QMouseEvent *ev)
{
QPoint pt = ev->pos();
//将pt转化成棋盘的像行列值
//判断这个行列值上面有没有棋子
int row, col;
//点击棋盘外面就不做处理
if(!getRowCol(pt, row, col))
return;
_clickId = -1;
int i;
//判断是哪一个棋子被选中,根据ID(这里的局部i)来记录下来
for(i = 0; i <= 31; i++)
{
if(_stone[i]._row == row && _stone[i]._col == col && _stone[i]._deal == false)
break;
}
if(i < 32)
_clickId = i; //选中的棋子的ID
if(_selectId == -1)//选中棋子
{
if(_clickId != -1)
{
if(_bRedTrue == _stone[_clickId]._red)
{
_selectId = _clickId;
}
}
}
else//走棋子
{
if(canMove(_selectId, _clickId, row, col ))
{
//_selectId为第一次点击选中的棋子,
//_clickId为第二次点击||被杀的棋子ID,准备选中棋子下子的地方
_stone[_selectId]._row = row;
_stone[_selectId]._col = col;
if(_clickId != -1)
_stone[_clickId]._deal = true;
_selectId = -1;
_bRedTrue = !_bRedTrue;
}
}
update();
}
//总的移动规则,选中准备下的棋子,被杀的棋子, 准备移动到的目的行列值
bool Board::canMove(int moveId, int killId, int row, int col)
{
//1.确定是选择其它棋子还是走棋
//2.是否需要使用到canMoveXXX()来做限制
//3.罗列出所有情况,和需要的得到的结果值 ==> 然后进行中间的逻辑层判断※不要受到别人的代码框架的束缚※
if(_stone[moveId]._red == _stone[killId]._red) //选择其它棋子,返回false
{
if(killId == -1) //其中有一个特殊情况,黑+_stone[-1]._red ==> 也需要判断能否
{
switch (_stone[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::MA:
return canMoveMA(moveId, killId, row, col);
case Stone::CHE:
return canMoveCHE(moveId, killId, row, col);
case Stone::PAO:
return canMovePAO(moveId, killId, row, col);
case Stone::BING:
return canMoveBING(moveId, killId, row, col);
}
}
_selectId = killId;
update();
return false;
}
else //选择其走棋,返回true
{
switch (_stone[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::MA:
return canMoveMA(moveId, killId, row, col);
case Stone::CHE:
return canMoveCHE(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(_stone[moveId]._red) //红 将
{
if(row > 2 || col < 3 || col > 5) return false;
}
else //黑 将
{
if(row < 7 || col < 3 || col > 5) return false;
}
int dr = _stone[moveId]._row - row;
int dc = _stone[moveId]._col - col;
int d = abs(dr)*10 + abs(dc);
if(d == 1 || d == 10)
return true;
return false;
}
bool Board::canMoveSHI(int moveId, int killId, int row, int col)
{
if(_stone[moveId]._red) //红 士
{
if(row > 2 || col < 3 || col > 5) return false;
}
else //黑 士
{
if(row < 7 || col < 3 || col > 5) return false;
}
int dr = _stone[moveId]._row - row;
int dc = _stone[moveId]._col - col;
int d = abs(dr)*10 + abs(dc);
if(d == 11)
return true;
return false;
}
bool Board::canMoveXIANG(int moveId, int killId, int row, int col)
{
if(_stone[moveId]._red) //红
{
if(row > 4) return false;
}
else //黑
{
if(row < 5) return false;
}
int dr = _stone[moveId]._row - row;
int dc = _stone[moveId]._col - col;
int d = abs(dr)*10 + abs(dc);
int dr2 = (_stone[moveId]._row + row)/2;
int dc2 = (_stone[moveId]._col + col)/2;
//象眼被堵,就不能够调,就会有i属于0~31,返回false
int i = 0;
for(i = 0; i <= 31; i++)
{
if(_stone[i]._row == dr2 && _stone[i]._col == dc2 && _stone[i]._deal == false)
break;
}
if(0 <= i && i <= 31)
return false;
if(d == 22)
return true;
return false;
}
bool Board::canMoveMA(int moveId, int killId, int row, int col)
{
int dr = _stone[moveId]._row - row;
int dc = _stone[moveId]._col - col;
int d = abs(dr)*10 + abs(dc);
int dr2 = (_stone[moveId]._row + row)/2;
int dc2 = (_stone[moveId]._col + col)/2;
//蹩脚马
if(abs(dr) == 2 && abs(dc)==1)
{
int i = 0;
if(row < _stone[moveId]._row )
{
for(i = 0; i <= 31; i++)
{
if(_stone[i]._row == (_stone[moveId]._row-1) && _stone[i]._col == _stone[moveId]._col && _stone[i]._deal == false)
break;
}
}
else
{
for(i = 0; i <= 31; i++)
{
if(_stone[i]._row == (_stone[moveId]._row+1) && _stone[i]._col == _stone[moveId]._col && _stone[i]._deal == false)
break;
}
}
if(0 <= i && i <= 31)
return false;
}
if(abs(dr) == 1 && abs(dc)==2)
{
int i = 0;
if(col < _stone[moveId]._col)
{
for(i = 0; i <= 31; i++)
{
if(_stone[i]._row == _stone[moveId]._row && _stone[i]._col == (_stone[moveId]._col-1) && _stone[i]._deal == false)
break;
}
}
else
{
for(i = 0; i <= 31; i++)
{
if(_stone[i]._row == _stone[moveId]._row && _stone[i]._col == (_stone[moveId]._col+1) && _stone[i]._deal == false)
break;
}
}
if(0 <= i && i <= 31)
return false;
}
if(d == 12 || d == 21)
return true;
return false;
}
bool Board::canMoveCHE(int moveId, int killId, int row, int col)
{
int ret = getStoneCountAtLine(_stone[moveId]._row, _stone[moveId]._col, row, col);
if(ret == 0)
return true;
return false;
}
bool Board::canMovePAO(int moveId, int killId, int row, int col)
{
int ret = getStoneCountAtLine(row, col, _stone[moveId]._row, _stone[moveId]._col);
if(killId != -1)
{
if(ret == 1) return true;
}
else
{
if(ret == 0) return true;
}
return false;
}
bool Board::canMoveBING(int moveId, int killId, int row, int col)
{
int dr = _stone[moveId]._row - row;
int dc = _stone[moveId]._col - col;
int d = abs(dr)*10 + abs(dc);
if(d != 1 && d != 10) return false;
if(_stone[moveId]._red) //红 兵
{
if(row < _stone[moveId]._row) return false;
if(_stone[moveId]._row == 3 || _stone[moveId]._row == 4)
{
if(col == _stone[moveId]._col && row == (_stone[moveId]._row+1))
return true;
}
else
{
if((col == _stone[moveId]._col && row >= 5) || (row == _stone[moveId]._row && abs(col-_stone[moveId]._col)==1))
return true;
}
return false;
}
else //黑 兵
{
if(row > _stone[moveId]._row) return false;
if(_stone[moveId]._row == 5 || _stone[moveId]._row == 6)
{
if(col == _stone[moveId]._col && row == (_stone[moveId]._row-1))
return true;
}
else
{
if((col == _stone[moveId]._col && row <= 4) || (row == _stone[moveId]._row && abs(col-_stone[moveId]._col)==1))
return true;
}
return false;
}
return true;
}
//棋子类
class Stone
{
public:
Stone();
~Stone();
public:
QString getText(); //根据enum TYPE得类型决定棋子上面的汉字
void initialize(int id); //32个棋子的初始化
public:
enum TYPE{JIANG, SHI, XIANG, MA, CHE, PAO, BING };
int _row; //棋子在棋盘的行(不是界面的坐标)
int _col; //棋子在棋盘的列(不是界面的坐标)
int _id; //棋子的ID
bool _deal; //棋子是否死亡
bool _red; //棋子的颜色
TYPE _type; //棋子的类型
};
==================================================================
项目成品:
==================================================================
上一篇: 一位开发人员的生日愿望清单
下一篇: 白盒测试之Junit测试工具的使用