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

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

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

===完整基于QT的跨平台网络对战中国象棋的项目完整版大更新  2019-04-13

===QT5/C++项目:基于QT的跨平台网络对战象棋(一)

===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;  //棋子的类型
};

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

 

<br>

 本文初发于 "偕臧的小站" ifmet.cn,同步转载于此

<br>

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

链接:百度网盘 请输入提取码 密码:0o4l

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

完整基于QT的跨平台网络对战中国象棋的项目完整版大更新  2019-04-13

QT5/C++项目:基于QT的跨平台网络对战象棋(一)

QT5/C++项目:基于QT的跨平台网络对战象棋(二)

QT5/C++项目:基于QT的跨平台网络对战象棋(三)


==================================================================
源码下载

- GitHub: ChineseChess『荐』
Gitee: ChineseChess


你的 `star` 和 `fork` 是对我最大的支持。也欢迎学习这个系列的  QT/DTK 学习,附学习由浅入深的目录

打包体验程序,下载地址:releases

欢迎 star 和 fork 这个系列的 Qt / DTK 学习,附学习进阶的路线图。

  • 32
    点赞
  • 148
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
目 录 第一章 开发环境 1.1 Qt 简介5 1.2 下载安装 Qt Creator 6 1.3 第一个程序 Hello World 7 第二章 窗体应用 1.1 窗体基类说明 12 1.2 控制窗体大小 13 1.3 窗体初始位置及背景色 13 1.4 修改标题栏图标 14 1.5 移动无边框窗体 16 1.6 去掉标题栏中最大化、最小化按钮 17 1.7 多窗体调用 18 1.8 字体形状窗体 20 第三章 控件应用 1.1 QPushButton按钮 23 1.2 QLabel标签 23 1.3 QLineEdit单行文本 24 1.4 QTextEdit多行文本 25 1.5 QPlainTextEdit多行文本 26 1.6 QComboBox下拉列表框 26 1.7 QFontComboBox字体下拉列表框 27 1.8 QSpinBox控件 28 1.9 QTimeEdit时间控件 29 1.10 QDateEdit日期控件 30 1.11 QScrollBar控件 30 1.12 QRadioButton单选按钮 31 1.13 QCheckBox复选框 32 1.14 QListView 列表控件 34 1.15 QTreeView树控件 34 1.16 QTableView表格控件 35 1.17 QHBoxLayout横向布局 36 1.18 QGridLayout网格布局 37 1.19 QGroupBox控件 38 1.20 QTabWidget控件 39 1.21 QMenu、QToolBar控件 41 1.22 任务栏托盘菜单 43 第四章 组件应用 1.1日历组件 47 1.2登录窗口 48 1.3文件浏览对话框 50 1.4颜色选择对话框 51 1.5进度条实例53 1.6Timer实时更新时间 54 第五章 文件操作 1.1创建文件夹 57 1.2写入文件 58 1.3修改文件内容 60 1.4删除文件 62 1.5修改文件名 63 1.6 INI文件写入操作 65 1.7 INI文件读取操作 68 1.8创建XML文件 71 1.9读取XML文件 72 第六章 图形图像操作 1.1绘制文字 75 1.2绘制线条 75 1.3绘制椭圆 77 1.4显示静态图像 78 1.5显示动态图像 78 1.6图片水平移动 79 1.7图片翻转 80 1.8图片缩放 82 1.9图片中加文字 84 1.10图像扭曲 85 1.11模糊效果 85 1.12着色效果 86 1.13阴影效果 87 1.14透明效果 87 第七章 多媒体应用 1.1音频、视频播放器 90 1.2播放Flash动画 94 1.3播放图片动画 95 第八章 系统操作 1.1获取屏幕分辨率 98 1.2获取本机名、IP地址 98 1.3根据网址获取IP地址 99 1.4判断键盘按下键值 100 1.5获取系统环境变量 101 1.6执行系统命令 102 第九章 注册表 1.0简要说明注册表 105 1.1写入注册表 105 1.2查找注册表 106 1.3修改IE浏览器的默认主页 107 第十章 数据库基础 1.1查询数据库驱动 109 1.2Qodbc连接Access数据库 109 1.3插入数据 111 1.4数据列表 112 1.5操作SQLite数据库 113 1.6SQLite数据库视图管理器 115 第十一章 网络开发 1.1点对点聊天服务端 119 1.2点对点聊天客户端 123 1.3局域网广播聊天 128 1.4SMTP协议发送邮件 148 1.5调用系统DLL判断网络连接状态 152 第十二章 进程与线程 1.1进程管理器 155 1.2线程QThread应用 158 1.3线程QRunnable应用 159 第十三章 数据安全 1.1 QByteArray加密数据 163 1.2 AES加密数据 164 1.3 MD5 加密数据 165 1.4 生成随机数 166 第十四章 打包部署 1.1 FilePacker 打包 169 1.2 Inno Setup 打包 174

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偕臧x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值