项目实战:Qt5/C++:QT象棋【初版】

版权声明:欢迎个人转载,但是亲请注明转载原文地址,谢谢,么么哒,若用作商业用途,请联系作者授权 https://blog.csdn.net/qq_33154343/article/details/80931400

项目实战: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;  //棋子的类型

};

 

 

 

==================================================================

 

 

 

项目成品:

==================================================================

链接:https://pan.baidu.com/s/1Usuk6nEy4OxhMfEnERM6Wg 密码:0o4l

阅读更多

没有更多推荐了,返回首页