首页 > 编程语言 >基于QT和C++实现的中国象棋

基于QT和C++实现的中国象棋

时间:2024-06-19 19:27:56浏览次数:12  
标签:中国象棋 return QT int Stone C++ moveid col row

一,源码

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包含了棋子类

三,运行展示

标签:中国象棋,return,QT,int,Stone,C++,moveid,col,row
From: https://blog.csdn.net/2303_76580416/article/details/139779961

相关文章

  • C++学习(22)
    #学习自用#计时计时可以计算出执行代码时花费了多长时间,对于同样的目的,我们可以通过不同的代码实现,而执行时间长短是评价一串代码性能如何的指标。#include<iostream>#include<string>#include<chrono>#include<thread>usingnamespacestd;intmain(){ autostar......
  • C/C++ 对文件目录进行操作的常用函数
    在C语言中,对目录进行操作的常用函数主要包括但不限于以下几个:opendir(constchar*name):功能:打开指定路径的目录。返回值:成功时返回一个指向DIR结构体的指针,失败则返回NULL。头文件:<dirent.h>readdir(DIR*dirp):功能:从打开的目录中读取下一个目录条目。返回值:成......
  • Visual Studio + Qt项目 数组超界不会报错。 堆栈 Cookie 检测代码检测到基于堆栈
    使用vs+Qt项目时,数组超界不会崩溃和报错的问题。 开启以下2个即可。  注意:1.启用了地址擦除系统会造成QT的异常崩溃,原因未知。2.有时会报cookie的错误,数组超界了,在退出函数时才会报错。   ......
  • centos7离线升级gcc , 报错:/lib64/libstdc++.so.6: version `CXXABI_1.3.8' not found
     因为需要依赖gcc高版本但是目前服务器版本是4.8.5的然后服务器又是内网所以只能离线升级gcc 分别下载https://ftp.gnu.org/gnu/gcc/gcc-8.3.0/gcc-8.3.0.tar.gzhttps://ftp.gnu.org/pub/gnu/gmp/gmp-6.1.0.tar.bz2https://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gzhttp:......
  • C/C++ 操作文件常用的函数
    C语言中操作文件常用的函数包括但不限于以下几种:打开和关闭文件fopen(constchar*path,constchar*mode):用于打开一个文件,返回一个指向FILE结构体的指针,path是文件路径,mode定义了文件的打开模式(如读、写、追加等)。fclose(FILE*stream):关闭由fopen()打开的文件,并刷新缓......
  • C++数据格式化3 - 格式化时间区间(使用时长)
    1.关键词2.strfmt.h3.strfmt.cpp4.测试代码5.运行结果6.源码地址1.关键词关键字:C++数据格式化字符串处理std::string时间区间跨平台应用场景:想对一个时间区间(如用时:2000s)进行格式化,转化成人类易读的时分秒的格式。2.strfmt.h#pragmaonce#include......
  • C++数据格式化4 - 格式化时间戳
    1.关键词2.strfmt.h3.strfmt.cpp4.测试代码5.运行结果6.源码地址1.关键词C++数据格式化字符串处理std::string时间戳跨平台2.strfmt.h#pragmaonce#include<string>#include<cstdint>#include<sstream>#include<iomanip>#include"timeutil.h&quo......
  • C++
    求一个三位数题目描述求这样一个三位数,该三位数等于其每位数字的阶乘之和。即abc=a!+b!+c!(n!表示n的阶乘)输入无输出输出这个数亲密数对题目描述键盘输入N,N在2至2000之间,求2至N中的亲密数对,就是A的因子和等于B,B的因子和等于A,且A≠B。如48......
  • C++数据格式化2 - 将文件大小转换为人类易读的格式
    1.关键词2.strfmt.h3.strfmt.cpp4.测试代码5.运行结果6.源码地址1.关键词C++数据格式化字符串处理std::string文件大小跨平台2.strfmt.h#pragmaonce#include<string>#include<cstdint>#include<sstream>#include<iomanip>namespacecutl{......
  • C++数据格式化1 - uint转换成字符串 & double转换成字符串
    1.关键词2.strfmt.h3.strfmt.cpp4.测试代码5.运行结果6.源码地址1.关键词C++数据格式化字符串处理std::stringintdouble跨平台2.strfmt.h#pragmaonce#include<string>#include<cstdint>#include<sstream>#include<iomanip>namespacecutl{......