欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-01-18 08:35:46
...

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


编辑环境:win10_x64 /Qt5.4.1

项目:Qt 象棋

项目简介:一开始还在想,是准备使用Qt写一个IM即时通讯的,既可以增加自己对这个的方面的认知,也可以,但是想一想,时间也不是很多了,大三都快结束了,之前一个星期,写了一个象棋,计划是分成三个功能的:[1]人机对战(单人游戏); [2]双人模式(单PC端); [3]socket 网络双人游戏(多PC端)。而初始阶段,还只是完成了 [2]双人模式(单PC端)的功能,但是后面的功能是在这些基础上面,直接进行一些人工智能,电脑根据玩家的下法,通过指定的规则,来进行可能得算法,从而下一些可能是的的算法步骤。而最后的socket的双人PC端进行游戏,在现在现在基础上面加上Socket编程的类就可以了。

其他:后续还是会继续更新的,当有空的时候是会继续完善后面的功能的。

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

项目运行效果

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

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

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

项目思路分析:

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

步骤:

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