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

news2024/11/24 20:41:41

一,源码

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

三,运行展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1835701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子期刊制作秘籍:如何让你的出版物脱颖而出?

​如何让你的电子期刊在众多出版物中脱颖而出&#xff0c;吸引读者的目光呢&#xff1f;在微信公众号这个平台上&#xff0c;让你的电子期刊内容更具吸引力、专业性和创新性&#xff0c;是至关重要的。下面&#xff0c;我将教你制作电子期刊一些方法&#xff0c;助你打造出一本…

在得物的小程序生态实践

一、前言 提起微信小程序&#xff0c;相信所有人都不陌生&#xff0c;下面这个典型使用场景你一定经历过&#xff1a; 餐馆落座——微信扫桌角小程序码——使用微信小程序点餐&#x1f354; 微信小程序&#xff08;下文简称&#xff1a;小程序&#xff09;作为一种在微信平台…

怎么样判断真假单北斗

国产化替代正在中国各行各业逐步提升中&#xff0c;特别涉及重点产业——国家安全&#xff01; 只有仅支持B1I和B3信号的芯片才是真正的单北斗芯片。但凡你支持了B1C、B2a、B2b中的一个就是假的单北斗。 B1C/L1/E1、B2a/ L5/E5a、B2b/G3/E5b这些频点与其他GNSS系统是完全重合的…

举个栗子!Tableau 技巧(277):创建径向 WIFI 信号图

之前为大家分享了 &#x1f330;&#xff1a;学做径向柱状图&#xff08;Radial Column Chart&#xff09;。在此基础上&#xff0c;我们又做了进一步的延展&#xff1a;径向 WIFI 信号图。 它的用法和径向柱状图一致&#xff0c;区别在于它将柱体分切成多个弧线&#xff08;内…

豆包高质量声音有望复现-Seed-TTS

我们介绍了 Seed-TTS&#xff0c;这是一个大规模自回归文本转语音 &#xff08;TTS&#xff09; 模型系列&#xff0c;能够生成与人类语音几乎没有区别的语音。Seed-TTS 作为语音生成的基础模型&#xff0c;在语音上下文学习方面表现出色&#xff0c;在说话人的相似性和自然性方…

pg表空间和mysql表空间的区别

一、表空间的定义 1、在pg中表空间实际上是为表指定一个存储的目录。并且在创建数据库时可以为数据库指定默认的表空间。创建表和索引时可以指定表空间&#xff0c;这样表和索引就可以存储到表空间对应的目录下了。 在pg中一个库中可以有多个表空间&#xff0c;一个表空间可以…

Linux实现: 客户端(cli01)通过TCP(或UDP)连接到聊天服务器(serv)进行聊天?(伪代码版本)

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

6月17(信息差)

1.马斯克最新预测&#xff1a;未来不再需要手机 将被脑机芯片替代 当地时间6月17日&#xff0c;马斯克高仿号“Not Elon Musk”发帖称&#xff1a;“你会在你的大脑上安装一个Neuralink接口&#xff0c;让你通过思考来控制你的新X手机吗&#xff1f;”对此&#xff0c;马斯克本…

东莞酷得:电子玩具嵌入式方案商

东莞市酷得智能科技有限公司&#xff0c;作为一家专业的玩具底层方案服务商&#xff0c;与国内外多家优秀制造企业有着深度合作&#xff0c;始终坚持以孩子为中心&#xff0c;以创新为动力&#xff0c;为孩子们打造独具特色的玩具产品。公司拥有一支专业的设计团队&#xff0c;…

SSM考研咨询app-计算机毕业设计源码05262

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设考研咨询app。 本设计…

大数据------JavaWeb------前端知识点汇总

额外知识点 W3C标准&#xff1a;W3C是万维网联盟&#xff0c;这个组成是用来定义标准的。他们规定了一个网页是由三部分组成 结构&#xff1a;对应的是 HTML 语言表现&#xff1a;对应的是 CSS 语言行为&#xff1a;对应的是 JavaScript 语言 HTML定义页面的整体结构&#xff1…

技术干货:同城O2O系统源码与外卖APP开发

本篇文章&#xff0c;小编将深入探讨同城O2O系统的源码结构&#xff0c;并详细介绍开发一款外卖APP的关键技术和步骤。 一、同城O2O系统概述 一个完整的O2O系统通常包括以下几个模块&#xff1a; 用户端 商家端 配送端 后台管理系统 二、O2O系统源码结构解析 一个完整的…

热重启(硬重启)获取Bitlocker密钥取证

计算机内存&#xff08;条&#xff09;在系统运行时存储了大量敏感信息&#xff0c;当断电后&#xff0c;内存中的数据荡然无存。反之&#xff0c;当一直通电的情况下&#xff0c;内存中的一些敏感数据一直存在。当然&#xff0c;正如某些人了解的&#xff0c;当断电后&#xf…

MES系统功能模块解析,MES系统源码

MES系统功能模块解析&#xff0c;MES系统源码 MES系统是一种用于协调和优化制造过程的信息管理系统&#xff0c;可以帮助企业实现生产计划的顺利执行&#xff0c;并提供全面的生产监控和数据分析功能。 MES系统常见的功能模块包括生产计划管理、物料管理、工艺管理、设备管理…

amr文件怎么转换成mp3?超好用的四种转换方法介绍!

amr文件怎么转换成mp3&#xff1f;在当今数字化时代&#xff0c;音频格式的多样性给我们带来了更广泛的选择&#xff0c;其中AMR格式就是其中之一&#xff0c;AMR格式在录音和通话领域得到广泛应用&#xff0c;但与此同时&#xff0c;它也存在一些挑战和局限性&#xff0c;尽管…

C++ 62 之 冒泡排序

#include <iostream> // #include <string> #include <cstring>using namespace std;// 冒泡排序:函数模板 template<typename T> void mySort(T arr[], int len){ // size参数是数组的个数&#xff0c;一定是int型的for (size_t i 0; i < len -1;…

C++ 61 之 函数模版

#include <iostream> #include <string> using namespace std;void swapInt(int &a,int &b){int temp a;a b;b temp; }void swapDou(double& a, double& b){double temp a;a b;b temp; }// T代表通用数据类型&#xff0c;紧接着后面的代码&a…

Kubernetes CSR 颁发的 MinIO Operator 证书

在当前的 Kubernetes 环境中&#xff0c;创建、管理和自动化 TLS 证书的标准方法是使用 kind: CertificateSigningRequest &#xff08;CSR&#xff09;资源。此原生 Kubernetes 资源提供了一种强大而高效的方式来处理集群中证书的整个生命周期。 通过利用 CSR 资源&#xff0…

做户用光伏代理需要多少钱?

随着全球对可再生能源和清洁能源的关注度日益提高&#xff0c;光伏技术作为其中的佼佼者&#xff0c;已经成为许多投资者和创业者关注的焦点。户用光伏系统作为其中的一个重要分支&#xff0c;其市场潜力巨大&#xff0c;吸引了越来越多的投资者和创业者进入这一领域。那么&…

MaxKB-无需代码,30分钟创建基于大语言模型的本地知识库问答系统

简介 MaxKB 是一个基于大语言模型 (LLM) 的智能知识库问答系统。它能够帮助企业高效地管理知识&#xff0c;并提供智能问答功能。想象一下&#xff0c;你有一个虚拟助手&#xff0c;可以回答各种关于公司内部知识的问题&#xff0c;无论是政策、流程&#xff0c;还是技术文档&a…