【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
    评论
好的,我理解您的问题是想要用 Qt 实现象棋中兵的规则,是吗?那我来为您说明一下。 在象棋中,兵是一种比较特殊的子,其规则与其他子不同。兵的基本法是直,但在特定情况下也可以斜着。 下面是兵的规则: 1. 兵只能向前,每次只能一格。 2. 兵在原位时,可以选择向前一格或者向前两格。 3. 兵到达对方底线后,可以升变成任何一种子。 4. 兵在横着的时候,可以斜着一格,但只能吃对面的子。 在 Qt 中,我们可以使用一个二维数组来表示盘,数组元素的值表示该位置上的子,0 表示该位置没有子,正数表示红方子,负数表示黑方子。 对于兵的法,我们可以在盘上进行判断,判断兵是否符合规则。比如,判断兵是否在原位、是否到达对方底线、是否在横着等。 具体实现方法可以参考以下代码: ```cpp // 兵的规则 bool Chessboard::canSoldierMove(const QPoint& from, const QPoint& to) { int row1 = from.y(); int col1 = from.x(); int row2 = to.y(); int col2 = to.x(); int chess = m_chessboard[row1][col1]; int target = m_chessboard[row2][col2]; // 兵只能向前 if (chess > 0 && row2 < row1) return false; if (chess < 0 && row2 > row1) return false; // 兵在原位时,可以选择向前一格或者向前两格 if (chess > 0 && row1 >= 5) { if (row2 != row1 - 1) return false; if (col1 != col2) return false; if (m_chessboard[row1 - 1][col1] != 0) return false; } if (chess < 0 && row1 <= 4) { if (row2 != row1 + 1) return false; if (col1 != col2) return false; if (m_chessboard[row1 + 1][col1] != 0) return false; } // 兵到达对方底线后,可以升变成任何一种子 if (chess > 0 && row1 <= 4 && row2 <= 4) return false; if (chess < 0 && row1 >= 5 && row2 >= 5) return false; // 兵在横着的时候,可以斜着一格,但只能吃对面的子 if (col1 == col2) { if (target != 0 && target * chess < 0) return false; } else { if (abs(col2 - col1) != 1 || row1 != row2) return false; if (chess > 0 && row1 > row2) return false; if (chess < 0 && row1 < row2) return false; if (col2 == 3 || col2 == 5) { int mid = (col1 + col2) / 2; if (m_chessboard[row1][mid] != 0) return false; } } return true; } ``` 希望这个代码片段可以帮助您实现兵的规则。如果您有其他问题,请随时问我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值