【Qt象棋游戏】04_象棋走棋规则——車、炮、士

01 - 象棋规则

  经过两篇前面棋盘和棋子绘制,象棋框架基本成型,紧接着要实现象棋走棋规则,让象棋游戏真正能“动起来”。
  象棋规则中:马走日,象走田;,车走直,炮隔一, 士走斜,将不出田;,兵去不还。这是象棋棋子行走规则的基本规则,下面看看详细分析象棋移动步骤。

02 - 棋子移动规则

  根据象棋规则制定棋子移动规则,简而言之就是通过代码判断选中棋子能不能移动?如果能移动,那么该棋子就把它所在QLabel绘画到目标坐标,否则不能绘画,这就是移动棋子的基本思路,具体实现方法在后面讲解。现在先看下有哪些相关方法。

 //-----------  判断各种类型棋子能不能移动 ------------//
   bool canMove(     int moveID, int killID, int x, int y);
   bool canMoveSHI(  int moveID, int killID, int x, int y);
   bool canMoveBING( int moveID, int killID, int x, int y);
   bool canMoveXIANG(int moveID, int killID, int x, int y);
   bool canMoveMA(   int moveID, int killID, int x, int y);
   bool canMoveCHE(  int moveID, int killID, int x, int y);
   bool canMovePAO(  int moveID, int killID, int x, int y);
   bool canMoveJIANG(int moveID, int killID, int x, int y);


   void getPicName();                   // 获取棋子图片名字
   bool existChess(int x, int y);       // 判断坐标上有没有棋子
   int  getChessID(int x, int y);       // 获取棋子的ID
   void showDeadChess(int  chessID);    // 显示阵亡棋子数目

   // 计算即将行走的棋子与某一坐标之间有几颗棋子
   int middleChessNum(int moveID, int x, int y);

   // 判断两个棋子是否同一方
   bool sameColor(int firstChessID, int secondChessID);

   // 棋子移动函数
   void moveChess(int moveID, int killID, int x, int y);

  在上诉方法中,首先实现两个与棋子移动规则密切相关的方法:判断坐标上有无棋子(existChess)和 计算两两棋子间有几个棋子(middleChessNum)。

/**
 *  @brief : 判断该位置是否有棋子
 *  @param : x : x轴坐标  y : y轴坐标
 *  @return: 有 : true, 无 : false
 **/
bool ChessArea::existChess(int x, int y)
{
    for(int i=0;i<32;i++){
        if(myChess[i].row==x && myChess[i].col==y&&!myChess[i].isDead){
           return true;
        }
    }

    return false;
}

/**
 *  @brief : 计算即将行走的棋子与某一坐标之间有几颗棋子
 *  @param : moveID : 即将行走棋子ID
 *           x      : x轴坐标
 *           y      : y轴坐标
 *  @return: 棋子与该坐标之间棋子数量
 **/
int ChessArea::middleChessNum(int moveID, int x, int y)
{
    int i;
    int sum = 0; //记录中间有几颗棋子
    if(myChess[moveID].row == x) {
        if(y-myChess[moveID].col > 0){   //计算下列
        
            for(i=myChess[moveID].col+1; i<y; i++) {
                if(existChess(myChess[moveID].row, i) == true)
                    sum++;    //记录中间有几颗棋子
            }
        }
        else /* 计算上列 */{
            for(i=myChess[moveID].col-1; i>y; i--) {
                if(existChess(myChess[moveID].row,i)==true)
                    sum++;
            }
        }

        return sum;
    } else if(myChess[moveID].col == y) {
        if(x - myChess[moveID].row > 0) {
            for(i=myChess[moveID].row+1; i<x; i++) {
                if(existChess(i, myChess[moveID].col)==true)
                    sum++;
            }
        } else {
            for(i=myChess[moveID].row-1; i>x; i--) {
                if(existChess(i, myChess[moveID].col)==true)
                    sum++;
            }
        }
        return sum;
    }

    //两个棋子不在一条直线上
    return -1;
}

  middleChessNum方法实现依赖existChess函数,existChess函数实现就是检查一遍输入参数的横纵坐标和棋子的横纵坐标是否相同。middleChessNum方法调用existChess方法,计算在同一直线移动前和移动后的横纵坐标差值,再根据差值检测该位置上面是否存在棋子,如果存在棋子那么sum就自加,如果没有,那么两个棋子就不在同一根直线,则返回-1。

03 - 車能否移动

  車走直:車能否移动,需要判断目标坐标与其坐标是否存在棋子,middleChessNum方法已经帮它实现该功能,所以車能否移动方法实现起来比较简单。

/**
 *
 *  @brief : 判断車是否可以移动
 *  @param : moveID : 移动棋子ID
 *           killID : 目标棋子ID (如果无棋子,默认-1)
 *           x      : x轴坐标
 *           y      : y轴坐标
 *  @return: 可以走 : true, 不可以走 : false
 *
 **/
bool ChessArea::canMoveCHE(int moveID, int killID, int x, int y)
{
    if(killID > 31)
        return false;

    if(middleChessNum(moveID, x, y) == 0)
        return true;

    return false;
}

04 - 炮能否移动

  炮隔一:炮移动规则和車相似,能沿着直线走,但炮和車唯一的不同是炮需要隔着一个棋子才能吃棋子。

/**
 *
 *  @brief : 判断炮是否可以移动
 *  @param : moveID : 移动棋子ID
 *           killID : 目标棋子ID (如果无棋子,默认-1)
 *           x      : x轴坐标
 *           y      : y轴坐标
 *  @return: 可以走 : true, 不可以走 : false
 *
 **/
bool ChessArea::canMovePAO(int moveID, int killID, int x, int y)
{

    if(middleChessNum(moveID, x, y)==0 && killID == -1)
        return true;

    if(middleChessNum(moveID,x, y)==1 && killID != -1)
        return true;

    return false;
}

05 - 士能否移动

  士走斜:士沿着对角线斜着走,代码实现思路是:士移动前后的横坐标差的绝对值和纵坐标差的绝对值都为1,否则士不能移动。

/**
 *
 *  @brief : 判断士是否可以移动
 *  @param : moveID : 移动棋子ID
 *           killID : 目标棋子ID (如果无棋子,默认-1)
 *           x      : x轴坐标
 *           y      : y轴坐标
 *  @return: 可以走 : true, 不可以走 : false
 *
 **/
bool ChessArea::canMoveSHI(int moveID, int killID, int x, int y)
{
    if(killID > 31)
        return false;

     //判断士行走是否超出米字格范围
    if(myChess[moveID].isRed){
        if(y<7 || x<3 || x>5) return false;
    }
    else {
        if(y>2 || x<3 || x>5) return false;
    }

    //判断是否为沿着对角线斜着行走
    int dx = myChess[moveID].row - x;
    int dy = myChess[moveID].col - y;

    /*
     * 要想让士沿着对角线斜着走,就是
     * 想让移动前后的横坐标差的绝对
     * 值和纵坐标差的绝对值都为1,
     * 横纵各移动一个单位长度。
     *
     */
    if(abs(dx)==1 && abs(dy)==1)
        return true;

    return false;
}

06 - 总结

  代码实现象棋走棋规则主要是判断象棋棋子能否移动,根据棋子属性类提供的棋子坐标与鼠标点击事件获取的目标坐标走棋规则处理,可以判断出各种棋子能否移动,值得注意的是代码实现过程中注意功能分块,下章继续实现其他棋子走棋规则。

  • 01_开发象棋游戏简介
  • 02_绘画象棋棋盘
  • 03_象棋棋子摆放
  • 04_象棋走棋规则——車、炮、士
  • 05_象棋走棋规则——象、马、将、兵
  • 06_象棋游戏法则
  • 07_人机博弈算法开端
  • 08_人机博弈高阶算法
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值