Qt实现中国象棋:(六)棋子移动规则

一、移动规则的制定

关于中国象棋游戏棋子移动规则有马走日,象走田等说法,下面就将各类棋子的移动规则进行说明及实现(开局时默认红棋在下方,黑棋在上方)。棋子移动规则中fromX、fromY为起始点坐标,toX、toY为落子点坐标。
1:兵/卒的移动规则,兵/卒在未越过楚河汉界之前只能前行,越过后可以左右移动和前行,不可后退,每次只移动一格位置。

bool myWidget::bing_moveRule(int fromX, int fromY, int toX, int toY)
{
    //红兵
    if(chessmap[fromY][fromX]==bing)
    {
        qDebug()<<QString("红兵行走");
        if(toY>fromY)
            return false;//兵不可回走
        if(toY>4&&(fromY==toY))
            return false;//过河前只可直走
        if(fromY-toY+abs(fromX-toX)>1)
            return false;//只可走一步直线
        else
            return true;
    }

    //黑卒
   else if(chessmap[fromY][fromX]==zhu)
    {
        if(toY<fromY)
            return false;//卒不可回走
        if(toY<5&&(fromY==toY))
            return false;//过河前只可直走
        if(toY-fromY+abs(fromX-toX)>1)
            return false;//只可走一步直线
        else
            return true;
    }
    return true;
}

2:炮的移动规则,炮是直行的,炮在吃子时,必须与被吃的棋子在同一直线上,且炮与该棋子之间有且只有另一个棋子。

bool myWidget::pao_moveRule(int fromX, int fromY, int toX, int toY)
{
    int numTemp=0;//记录炮吃子时路线上的棋子数

    if(toX<0||toX>=row||toY<0||toY>=line)
        return false;
    if(fromX!=toX&&fromY!=toY)
        return false;//炮只可走直线

    //炮不吃子时
    if(chessmap[toY][toX]<30)
    {
        
        if(toY<fromY)
        {//上移
            for(int i=fromY-1;i>toY;i--)
                if(chessmap[i][fromX]>30)
                    return false;
        }

        if(fromY<toY)
        {//下移
            for(int i=fromY+1;i<toY;i++)
                if(chessmap[i][fromX]>30)
                    return false;
        }
        if(fromX<toX)
        {//右移
            for(int i=fromX+1;i<toX;i++)
                if(chessmap[fromY][i]>30)
                    return false;
        }
        if(fromX>toX)
        {//左移
            for(int i=fromX-1;i>toX;i--)
                if(chessmap[fromY][i]>30)
                    return false;
        }
    }
    //炮吃子时
    else
    {
        if(toY<fromY)
        {//上移
            for(int i=fromY-1;i>toY;i--)
                if(chessmap[i][fromX]>30)
                    numTemp++;
        }
        if(fromY<toY)
        {//下移
            for(int i=fromY+1;i<toY;i++)
                if(chessmap[i][fromX]>30)
                    numTemp++;
        }
        if(fromX<toX)
        {//右移
            for(int i=fromX+1;i<toX;i++)
                if(chessmap[fromY][i]>30)
                    numTemp++;
        }
        if(fromX>toX)
        {//左移
            for(int i=fromX-1;i>toX;i--)
                if(chessmap[fromY][i]>30)
                    numTemp++;
        }

        if(numTemp!=1)
            return false;
    }
    return true;
}

3:车的移动规则,车的移动规则与炮的类似,只是车在吃子时,车与被吃子之间是没有其他棋子的。

bool myWidget::ju_moveRule(int fromX, int fromY, int toX, int toY)
{
    int numTemp=0;//记录车吃子时路线上的棋子数

    if(toX<0||toX>=row||toY<0||toY>=line)
        return false;
    if(fromX!=toX&&fromY!=toY)
        return false;//车只可走直线

    //车不吃子时
    if(chessmap[toY][toX]<30)
    {

        if(toY<fromY)
        {//上移
            for(int i=fromY-1;i>toY;i--)
                if(chessmap[i][fromX]>30)
                    return false;
        }

        if(fromY<toY)
        {//下移
            for(int i=fromY+1;i<toY;i++)
                if(chessmap[i][fromX]>30)
                    return false;
        }
        if(fromX<toX)
        {//右移
            for(int i=fromX+1;i<toX;i++)
                if(chessmap[fromY][i]>30)
                    return false;
        }
        if(fromX>toX)
        {//左移
            for(int i=fromX-1;i>toX;i--)
                if(chessmap[fromY][i]>30)
                    return false;
        }
    }
    //车吃子时
    else
    {
        if(toY<fromY)
        {//上移
            for(int i=fromY-1;i>toY;i--)
                if(chessmap[i][fromX]>30)
                    numTemp++;
        }
        if(fromY<toY)
        {//下移
            for(int i=fromY+1;i<toY;i++)
                if(chessmap[i][fromX]>30)
                    numTemp++;
        }
        if(fromX<toX)
        {//右移
            for(int i=fromX+1;i<toX;i++)
                if(chessmap[fromY][i]>30)
                    numTemp++;
        }
        if(fromX>toX)
        {//左移
            for(int i=fromX-1;i>toX;i--)
                if(chessmap[fromY][i]>30)
                    numTemp++;
        }

        if(numTemp!=0)
            return false;
    }
    return true;
}

4:马的移动规则,俗称马走日,同时要注意蹩马脚的情况。

bool myWidget::ma_moveRule(int fromX, int fromY, int toX, int toY)
{
    int i=-1,j=-1;
    //马走日字
    if(!((abs(toX-fromX)==1&&abs(toY-fromY)==2)||(abs(toX-fromX)==2&&abs(toY-fromY)==1)))
        return false;

    //蹩马脚的情况
    if(toX-fromX==2)
    {
        j=fromX+1;i=fromY;
    }
    else if(fromX-toX==2)
    {
        j=fromX-1;i=fromY;
    }
    else if(fromY-toY==2)
    {
        j=fromX;i=fromY-1;
    }
    else if(toY-fromY==2)
    {
        j=fromX;i=fromY+1;
    }
    if(i>=0&&j>=0&&chessmap[i][j]>30)
        return false;

    return true;
}

5:象/相的移动规则。其移动规则与马的移动规则类似,相走田字,也存在蹩脚的情况。同时需注意象/相不可越过楚河汉界。

bool myWidget::xiang_moveRule(int fromX, int fromY, int toX, int toY)
{
    //红相
    if(chessmap[fromY][fromX]==Rxiang)
    {
        if(toY<5)
            return false;//相不可过河
    }
    //黑象
    else if(chessmap[fromY][fromX]==Bxiang)
    {
        if(toY>4)
            return false;//象不可过河
    }
    if(abs(fromX-toX)!=2||abs(fromY-toY)!=2)
        return false;//相/象走田字
    if(chessmap[(fromY+toY)/2][(fromX+toX)/2]!=0)
        return false;//相/象眼被塞
    return  true;
}

6:士/仕的移动规则。士/仕不可以出九宫格,每次只沿对角线走动一格。

bool myWidget::shi_moveRule(int fromX, int fromY, int toX, int toY)
{
    if(chessmap[fromY][fromX]==Rshi)
    {
        if(toX<3||toX>5||toY<7)
            return false;//红士必须在九宫格内行走
    }
    else if(chessmap[fromY][fromX]==Bshi)
    {
        if(toX<3||toX>5||toY>2)
            return false;//黑士必须在九宫格内行走
    }
    if(abs(toX-fromX)!=1||abs(toY-fromY)!=1)
        return false;//士沿对角线走一步
    return true;
}

7:将/帅的移动规则,将/帅与士一样不可以出九宫格,但将/帅是走直线一步。

bool myWidget::jiang_moveRule(int fromX, int fromY, int toX, int toY)
{
    if(chessmap[fromY][fromX]==Rshuai)
    {
        if(toX<3||toX>5||toY<7)
            return false;//红帅必须在九宫格内行走
    }
    else if(chessmap[fromY][fromX]==Bjiang)
    {
        if(toX<3||toX>5||toY>2)
            return false;//黑将必须在九宫格内行走
    }
    if((abs(toX-fromX)+abs(toY-fromY))!=1)
        return false;//将/帅走一步直线
    return true;
}

二、移动规则的使用

1:为了更加方便使用以上的各类棋子移动规则,可定义一个函数,在里面使用switch函数根据具体的棋子来选择其对应的移动规则。

bool myWidget::game_moveRule(int fromX, int fromY, int toX, int toY)
{
    bool ans=false;
    //避免越界
    if(toX<0||toX>=col||toY<0||toY>=row)
        return ans;

    //避免吃掉己方棋子
    if(chessmap[fromY][fromX]>30&&chessmap[fromY][fromX]<40&&chessmap[toY][toX]>30&&chessmap[toY][toX]<40)
        return ans;
    if(chessmap[fromY][fromX]>40&&chessmap[toY][toX]>40)
        return ans;

    switch(chessmap[fromY][fromX])
    {
    case bing:case zhu:
        ans=bing_moveRule(fromX,fromY,toX,toY);break;
    case Rpao:case Bpao:
        ans=pao_moveRule(fromX,fromY,toX,toY);break;
    case Rju:case Bju:
        ans=ju_moveRule(fromX,fromY,toX,toY);break;
    case Rma:case Bma:
        ans=ma_moveRule(fromX,fromY,toX,toY);break;
    case Rxiang:case Bxiang:
        ans=xiang_moveRule(fromX,fromY,toX,toY);break;
    case Rshi:case Bshi:
        ans=shi_moveRule(fromX,fromY,toX,toY);break;
    case Rshuai:case Bjiang:
        ans=jiang_moveRule(fromX,fromY,toX,toY);break;
    }
    return ans;
}

2:因为加入了移动规则,所以需要对mousePressEvent函数内容进行改动。加入棋子的移动判定,同时加入棋子第一次被点击后的效果图的显示。
改动后的mousePressEvent函数

//鼠标点击(按压)事件
void myWidget::mousePressEvent(QMouseEvent *event)
{
    QPoint point=event->pos();
    int w=this->width()/cool;
    int h=this->height()/row;
    X=point.x()/w;//x轴对应的下标
    Y=point.y()/h;//y轴所对应的下标
    emit clicked(X,Y,mou);//发出信号
    if(mou==0&&chessmap[Y][X]>30)
    {
        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))
        {
            chessmap[Y][X]=chessmap[fromY][fromX];//将目标位置的值改为被移动棋子的值
            chessmap[fromY][fromX]=chessboardVaule[fromY][fromX];//将棋子初始位置换位棋盘的值
            qDebug()<<QString("目的坐标:")<<"("<<Y<<","<<X<<")";
        }

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

到此棋子的移动规则就添加完成。双人象棋游戏的基本需求也就只剩下一方每次只走一步的设定,悔棋的操作以及输赢的判定。实现这些功能双人象棋游戏就可以完成啦。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zmq1998

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

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

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

打赏作者

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

抵扣说明:

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

余额充值