600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Qt开发之中国象棋篇(四):棋子移动(上)

Qt开发之中国象棋篇(四):棋子移动(上)

时间:2019-08-23 08:39:04

相关推荐

Qt开发之中国象棋篇(四):棋子移动(上)

有了前两篇的象棋棋盘和象棋棋子的铺垫,那么基本的象棋的雏形就出来,接下来要完善各个棋子行走的规则就可以让棋子移动起来,在贴代码之前,让我们回想一下象棋棋子的行走规则吧!在象棋中:士走斜线,象走田字格,马走日子格,車和炮都能直接移动,兵只能前进,不能后退,过河不能左右移动,过河后可以移动,将只能在规定的方格中行走。这是象棋棋子行走规则的基本规则,下面看看详细的移动步骤实现。

在 chessarea.h头文件中添加相关函数声明:

// 判断一颗棋子能不能移动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);// 输入行列坐标判断该坐标上有没有棋子bool beChess(int row, int col);// 输入行列坐标获取棋子的idint getChessId(int row, int col);// 计算即将行走的棋子与某一坐标之间有几颗棋子int numChess(int moveid, int x, int y);// 判断两个棋子是否是同一方的bool sameColor(int id1, int id2);

接着在 chessarea.cpp 中实现具体的方法描述:

// 输入行列坐标判断该坐标上有没有棋子bool ChessArea::beChess(int row, int col){for(int i=0;i<32;i++)if(myChess[i].row==row && myChess[i].col==col&&!myChess[i].isDead)return true;return false;}// 计算即将行走的棋子与某一坐标之间有几颗棋子int ChessArea::numChess(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(beChess(myChess[moveid].row, i) == true)sum++; //记录中间有几颗棋子}}else /* 计算上列 */{for(i=myChess[moveid].col-1; i>y; i--){if(beChess(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(beChess(i, myChess[moveid].col)==true)sum++;}}else {for(i=myChess[moveid].row-1; i>x; i--){if(beChess(i, myChess[moveid].col)==true)sum++;}}return sum;}//两个棋子不在一条直线上return -1;}

numChess 函数的实现要依赖beChess函数,beChess函数实现就是检查一遍输入参数的横纵坐标和棋子的横纵坐标是否相同。numChess函数借用beChess函数,在同一直线上,计算移动前和移动后的横纵坐标差值,再根据差值检测该位置上面是否存在棋子,如果存在棋子那么sum就自加,如果没有,那么两个棋子就不在同一根直线,则返回-1。numChess函数在后面的車和炮行走规则中起关键作用,下面开始实现各个棋子的行走规则。

士的走法:

bool ChessArea::canMoveSHI(int moveid, int killid, int x, int y){// 判断士行走是否超了出米字格范围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;if(abs(dx)==1 && abs(dy)==1)return true;return false;}

要想让士沿着对角线斜着走,那么就要让士移动前后的横坐标差的绝对值和纵坐标差的绝对值都为1,否则士不能移动。

兵的走法:

bool ChessArea::canMoveBING(int moveid, int killid, int x, int y){int dx = myChess[moveid].row - x;int dy = myChess[moveid].col - y;if (myChess[moveid].isRed) {// 红方棋子,过河前的行走规则if(myChess[moveid].col>=5 && myChess[moveid].col<=6) {if(dy==1 && dx==0) //竖着走,不回头return true;else//横着走,走不通return false;}else /* 过河后 */{if (abs(dy)==1 && abs(dx)==0||(abs(dx)==1 && abs(dy)==0)) {if(dy == -1) //竖着走return false; //竖着走走了回头路就要返回错误else //横着走return true;}else return false;}}else /* 黑棋 */{// 黑方棋子,过河前的行走规则if (myChess[moveid].col>= 3 && myChess[moveid].col<=4) {if(dy == -1 && dx==0) //竖着走,不回头return true;else//横着走,走不通return false;}else /* 过河后 */{if(abs(dx)==1&&abs(dy)==0||(abs(dy)==1&&abs(dx)==0)){if(dy == 1) //竖着回头走,走不通return false;else//横着走,一样走得通return true;}else return false;}}return false;}

兵的走法比士的走法复杂,考虑的细节也较多。首先无论兵有没有过河都不能回头走,过河后,兵才能向左右走。根据这个规则代码可以分为红方和黑方的兵过河前和过河后四个方面进行处理,这样子逻辑比较容易理解。

马的走法:

bool ChessArea::canMoveMA(int moveid, int killid, int x, int y){// 获取移动前和移动后的坐标差int dx = myChess[moveid].row - x;int dy = myChess[moveid].col - y;// 获取移动前和移动后的坐标差的中值坐标int medium_x = (myChess[moveid].row + x)/2;int medium_y = (myChess[moveid].col + y)/2;if(abs(dx)==1&&abs(dy)==2 || (abs(dx)==2&&abs(dy)==1)){if(abs(dx)==2){// 别马腿检验if(beChess(medium_x, myChess[moveid].col)==false)return true;}else if(abs(dy)==2){// 别马腿检验if(beChess(myChess[moveid].row, medium_y)==false)return true;}}return false;}

马的走法是走日字格,那么就有两种情况,一种是横坐标绝对值差为2,纵坐标绝对值差为 1,还有一种是纵坐标绝对值差为2,横坐标绝对值差为 1。除了这样之后,还要注意别马脚判断,那么依靠横纵坐标差的中值坐标可以获取到中值坐标,根据这个坐标值,调用beChess函数判断在该位置是否有棋子别马脚,如果有棋子那么不能走。

象的走法:

bool ChessArea::canMoveXIANG(int moveid, int killid, int x, int y){// 象不能过河 if (myChess[moveid].isRed) {if(y<5)return false;}else/* 黑象 */{if(y>4)return false;}//走田字格int dx = myChess[moveid].row - x;int dy = myChess[moveid].col - y;int medium_x = (myChess[moveid].row + x)/2;int medium_y = (myChess[moveid].col + y)/2;if(abs(dx)==2 && abs(dy)==2){//别象眼检验if(!beChess(medium_x, medium_y))return true;}return false;}

如果懂了前面马的走法,那么象的走法就比较简单了,基本相似。注意象也是需要做别象脚检测。

車的走法:

bool ChessArea::canMoveCHE(int moveid, int killid, int x, int y){if(numChess(moveid, x, y) == 0)return true;return false;}

車的走法有了前面numChess的铺垫,那么就可以变得很简洁。

炮的走法:

bool ChessArea::canMovePAO(int moveid, int killid, int x, int y){// 不吃棋子 if(numChess(moveid, x, y)==0 && killid==-1)return true;// 吃棋子if( numChess(moveid,x, y)==1 && killid!=-1)return true;return false;}

炮的走法和車的很相似,能走沿着直线走,但炮和車唯一的不同就是:炮是隔着一个棋子才能吃棋子。

将的走法:

bool ChessArea::canMoveJIANG(int moveid, int killid, int x, int y){//flag_be用来存放位于同一列上的两个将之间棋子的个数int flag_be = -1;int step = 0;//列超出范围if(x<3 || x>5)return false;//行、列坐标差int dx = myChess[moveid].row - x;int dy = myChess[moveid].col - y;//判断步长是否为1if((abs(dx)==1&&abs(dy)==0) || (abs(dx)==0&&abs(dy)==1))step = 1;if(myChess[moveid].isRed) // 红棋{// 要杀掉的棋子必须是对面的老将且与对面的老将在同一列上if(killid == 16 && myChess[moveid].row == myChess[16].row){flag_be = numChess(moveid, x, y);if(flag_be == 0)return true;}// 在田字格里面行走if(y<=9 && y>=7 && step==1)return true;}//黑棋else{//要杀掉的棋子必须是对面的老将且与对面的老将在同一列上if(killid == 0 && myChess[moveid].row == myChess[0].row){flag_be =numChess(moveid, x, y);if(flag_be == 0)return true;}//在田字格里面行走if(y>=0 && y<=2 && step==1)return true;}return false;}

棋子将的走法只能在米字格里面走,每次只能走一个步长,另外注意,当双方的将在同一直线上,而且没有隔着任何棋子时,那么将就可以吃掉对方的将。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。