Qt实现中国象棋:(七)悔棋

一、下棋步骤的保存

在设置保存下棋步骤之前,先定义一个bool类型的变量player用于判定是哪一方下子,设定player=true时是红方下棋,player=false时是黑方下棋。
1:要想保存下棋的步骤,必须先弄明白需要保存的信息。一个下棋步骤需要记住棋子点,落子点,被移动的棋子,若存在吃子的行为,还要记下被吃掉的是哪个棋子。一个步骤至少要包含这四个信息。可根据这些新建一个名为steps的类(使用结构体定义也是可行的)。
2:steps.h文件

class steps
{
public:
    steps();
    steps(const steps &b);
    steps & operator=(const steps &);

    int getChessman();
    int getKilledChessman();
    QPoint getSP();
    QPoint getDP();

    void setChessman(int chessman);
    void setKilledChessman(int killedChessman);
    void setSP(QPoint start);
    void setDP(QPoint drop);

private:
    int chessman;//移动的棋子
    int killedChessman;//被吃掉的棋子
    QPoint startPoint;//起始点
    QPoint dropPoint;//落子点
};

3:steps.cpp

#include "steps.h"

steps::steps()
{//初始化
    this->chessman=-1;
    this->killedChessman=-1;
    this->startPoint.setX(-1);
    this->startPoint.setY(-1);
    this->dropPoint.setX(-1);
    this->dropPoint.setY(-1);
}

steps::steps( const steps &b)
{//拷贝构造函数
    this->chessman=b.chessman;
    this->killedChessman=b.killedChessman;
    this->startPoint=b.startPoint;
    this->dropPoint=b.dropPoint;
}

//运算符重载
steps & steps::operator=(const steps &b)
{
    this->chessman=b.chessman;
    this->killedChessman=b.killedChessman;
    this->startPoint=b.startPoint;
    this->dropPoint=b.dropPoint;
    return *this;
}

int steps::getChessman()
{
    return chessman;
}

int steps::getKilledChessman()
{
    return killedChessman;
}

QPoint steps::getSP()
{
    return startPoint;
}

QPoint steps::getDP()
{
    return dropPoint;
}
void steps::setChessman(int chessman)
{
    this->chessman=chessman;
}
void steps::setKilledChessman(int killedChessman)
{
    this->killedChessman=killedChessman;
}
void steps::setSP(QPoint start)
{
    this->startPoint=start;
}
void steps::setDP(QPoint drop)
{
    this->dropPoint=drop;
}

4:将steps类定义好后就可以来实现对下棋步骤的保存,考虑到悔棋就是将最新保存的下棋步骤删除,还原到原来的棋局状态。对于下棋步骤就是先进后出的操作,所以使用栈来对步骤进行保存。

QStack<steps> chessSteps; // 走棋步数

定义一个与之相关的函数

void myWidget::saveSteps(int chessman, QPoint start, QPoint drop)
{
    steps step;
    step.setChessman(chessman);//移动棋子
    step.setSP(start);//起始点
    step.setDP(drop);//落子点
    if(chessmap[drop.y()][drop.x()]>30)
        step.setKilledChessman(chessmap[drop.y()][drop.x()]);//被吃棋子
    chessSteps.push(step);//入栈
}

5:修改后的mousePressEvent函数,其中的test()函数的功能是查看下棋步骤是否保存成功,属于测试使用。

//鼠标点击(按压)事件
void myWidget::mousePressEvent(QMouseEvent *event)
{
    QPoint point=event->pos();
    int w=this->width()/col;
    int h=this->height()/row;
    X=point.x()/w;//x轴对应的下标
    Y=point.y()/h;//y轴所对应的下标
    emit clicked(X,Y,mou);//发出信号
    if(mou==0)
    {
        if((player&&chessmap[Y][X]>30&&chessmap[Y][X]<40)||(!player&&chessmap[Y][X]>40))
        {//player为真必须选红子,为假必须选黑子
            fromX=X;fromY=Y;//记下移动棋子初始位置下标
            chessmap[fromY][fromX]+=20;//修改为棋子点击效果图的值
            qDebug()<<QString("初始坐标:")<<"("<<Y<<","<<X<<")";
            mou=1;//第一次点击完成
            update();//展示出棋子的被点击效果
        }
    }
    else if(mou==1)
    {
        chessmap[fromY][fromX]-=20;//点击效果棋子值修改为棋子值

        if(game_moveRule(fromX,fromY,X,Y))
        {
            start.setX(fromX);start.setY(fromY);
            drop.setX(X);drop.setY(Y);
            saveSteps(chessmap[fromY][fromX],start,drop);
            test();
            chessmap[Y][X]=chessmap[fromY][fromX];//将目标位置的值改为被移动棋子的值
            chessmap[fromY][fromX]=chessboardVaule[fromY][fromX];//将棋子初始位置换位棋盘的值
            player=!player;//转换角色
            qDebug()<<QString("目的坐标:")<<"("<<Y<<","<<X<<")";
        }

        mou=0;//第二次点击完成
        update();//重绘
    }
}

6:测试效果,
在这里插入图片描述

二、悔棋

1:将在ui文件中的pushButton作为悔棋的按钮,右击该控件,选择”转到槽“。
在这里插入图片描述
2:在定义一个pushButton,界面以及其text如下图所示.两个按钮都设置点击后的槽函数。
在这里插入图片描述

3:查看widget.h文件中出现on_pushButton_clicked和on_pushButton_2_clicked函数,可知这两个函数都是QPushButton::clicked信号对应的槽函数。

private slots:
   void on_pushButton_clicked();
   void on_pushButton_2_clicked();

3:上述的槽函数定义在widget.h中,而悔棋操作是在对mywidget中的chessmap和chessSteps进行操作,所以在mywidget.中定义一个deleteStep()函数,在槽函数中直接调用该函数即可。悔棋时根据实际情况来看删除几个步骤

void myWidget::deleteStep(bool regret)
{
    int i=0;
    if(regret)
    {//红方悔棋
        if(!player)
        {//在对方落子之前悔棋,只恢复自己最近下的一步。且将当前落子者转换为自己
            i=1;
            player=!player;
        }
        else if(player)
        {//当前为红方落子,在自己落子前悔棋,恢复对方最近一步棋和自己最近的一步棋
           if(chessSteps.size()>1)
                i=2;
        }
        else
            i=1;
    }
    else
    {//黑方悔棋
        if(player&&chessSteps.size()>2)
        {//在对方落子之前悔棋,只恢复自己最近下的一步。且将当前落子者转换为自己
            i=1;
            player=!player;
        }
        else if(!player&&chessSteps.size()>2)
            i=2;
    }
    while(i>0)
    {
        if(chessSteps.size()!=0)
        {
            steps step=chessSteps.top();
            chessmap[step.getSP().y()][step.getSP().x()]=step.getChessman();//还原起始点位置棋子值
            chessmap[step.getDP().y()][step.getDP().x()]=chessboardVaule[step.getDP().y()][step.getDP().x()];//还原落子点位置棋盘值
            if(step.getKilledChessman()>30)
            {//存在吃子行为,就还原落子点出的棋子值
               chessmap[step.getDP().y()][step.getDP().x()]= step.getKilledChessman();
            }
            chessSteps.pop();//出栈

            //test
            QPoint s=step.getSP();QPoint d=step.getDP();
            qDebug()<<"deleted "<<i<<" step is("<<s.y()<<","<<s.x()<<")to("
                   <<d.y()<<","<<d.x()<<")";
        }
        i--;
    }
    update();//重绘
}

//红方悔棋
void Widget::on_pushButton_clicked()
{
    regret=true;//悔棋者为红方
    ui->widget->deleteStep(regret);
}

//黑方悔棋
void Widget::on_pushButton_2_clicked()
{
    regret=false;//悔棋者为黑方
    ui->widget->deleteStep(regret);
}

4:悔棋效果
下图中所圈的步骤是进行第一次悔棋后所下的一步,所展示的是悔棋后恢复的棋盘。
在这里插入图片描述
下图可以看到是红方和黑方一共下了四步进行的悔棋,被删除的是最近的两步。删除顺序没有出现错误,悔棋后棋盘也被正确恢复。到此悔棋的操作就完成啦
在这里插入图片描述
最后只剩下对棋局输赢的判定,这个双人的中国象棋游戏就基本完成啦。

Qt实现中国象棋
(八)输赢判定

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zmq1998

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

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

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

打赏作者

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

抵扣说明:

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

余额充值