01 - 象能否移动
象走田:象走日字格,判断方法为目标坐标和原始坐标横纵坐标绝对值差都为2,另外,还要注意“别象脚”判断,依靠横纵坐标差的中值可以获取到中值坐标,根据该坐标值,调用existChess函数判断在该位置是否有棋子“别象脚”,如果有棋子,那么棋子象不能移动。
/**
*
* @brief : 判断象是否可以移动
* @param : moveID : 移动棋子ID
* killID : 目标棋子ID (如果无棋子,默认-1)
* x : x轴坐标
* y : y轴坐标
* @return: 可以走 : true, 不可以走 : false
*
**/
bool ChessArea::canMoveXIANG(int moveID, int killID, int x, int y)
{
if(killID > 31)
return false;
//象不能过河
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(!existChess(medium_x, medium_y))
return true;
}
return false;
}
02 - 马能否移动
马走日:马走日字格,分两种情况,其一是横坐标绝对值差为2,纵坐标绝对值差为 1;还有一种是纵坐标绝对值差为2,横坐标绝对值差为 1。另外,还要注意“别马脚”判断,依靠横纵坐标差的中值可以获取到中值坐标,根据这个坐标值,调用existChess函数判断在该位置是否有棋子“别马脚”,如果有棋子那么不能走。
/**
*
* @brief : 判断马是否可以移动
* @param : moveID : 移动棋子ID
* killID : 目标棋子ID (如果无棋子,默认-1)
* x : x轴坐标
* y : y轴坐标
* @return: 可以走 : true, 不可以走 : false
*
**/
bool ChessArea::canMoveMA(int moveID, int killID, int x, int y)
{
if(killID > 31)
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)==1&&abs(dy)==2) || (abs(dx)==2&&abs(dy)==1)){
if(abs(dx)==2){
//别马腿检验
if(existChess(medium_x, myChess[moveID].col)==false)
return true;
} else if(abs(dy)==2) {
//别马腿检验
if(existChess(myChess[moveID].row, medium_y)==false)
return true;
}
}
return false;
}
03 - 将能否移动
将不出营:将不出营指的是将不能跳出士保护范围(米字格)外,每次将只能移动一个步长。另外,当双方的将在同一直线上,而且没有隔着任何棋子时,那么将就可以吃掉对方的将。
/**
*
* @brief : 判断将是否可以移动
* @param : moveID : 移动棋子ID
* killID : 目标棋子ID (如果无棋子,默认-1)
* x : x轴坐标
* y : y轴坐标
* @return: 可以走 : true, 不可以走 : 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;
//判断步长是否为1
if((abs(dx)==1&&abs(dy)==0) || (abs(dx)==0&&abs(dy)==1))
step = 1;
//红棋
if(myChess[moveID].isRed){
//要杀掉的棋子必须是对面的老将且与对面的老将在同一列上
if(killID == 20 && myChess[moveID].row == myChess[20].row){
flag_be = middleChessNum(moveID, x, y);
if(flag_be == 0)
return true;
}
//在田字格里面行走
if(y<=9 && y>=7 && step==1)
return true;
} else{ //黑棋
//要杀掉的棋子必须是对面的老将且与对面的老将在同一列上
if(killID == 4 && myChess[moveID].row == myChess[4].row){
flag_be = middleChessNum(moveID, x, y);
if(flag_be == 0)
return true;
}
//在田字格里面行走
if(y>=0 && y<=2 && step==1)
return true;
}
return false;
}
04 - 兵能否移动
兵去不还:兵的走法比较复杂,考虑的细节较多。无论兵有没有过河都不能回头走,过河后,兵才能向左右走。根据这个规则代码可以分为红方和黑方的兵过河前和过河后四个方面进行处理,也比较容易理解。
/**
*
* @brief : 判断兵是否可以移动
* @param : moveID : 移动棋子ID
* killID : 目标棋子ID (如果无棋子,默认-1)
* x : x轴坐标
* y : y轴坐标
* @return: 可以走 : true, 不可以走 : false
*
**/
bool ChessArea::canMoveBING(int moveID, int killID, int x, int y)
{
if(killID > 31)
return false;
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;
}
05 - 判断象棋走棋规则函数
经过了两篇象棋走棋规则讲解,基本把象棋棋子走法规则理清楚,下面通过canMove函数调用判断各个棋子能否走棋的方法,如果能走棋返回true,否则返回false。
/**
*
* @brief : 判断棋子是否可以移动
*
* @param : moveID : 移动棋子ID
* killID : 目标棋子ID (如果无棋子,默认-1)
* x : x轴坐标
* y : y轴坐标
*
* @return: 可以走 : true, 不可以走 : false
*
**/
bool ChessArea::canMove(int moveID, int killID, int x, int y)
{
if(moveID < 0)
return false;
// killID 为 -1 表示该ID是没有棋子
if(killID==-1 || !sameColor(moveID, killID))
{
switch(myChess[moveID].chessType)
{
case Chess::JIANG:
return canMoveJIANG(moveID, killID, x, y);
break;
case Chess::SHI:
return canMoveSHI(moveID, killID, x, y );
break;
case Chess::XIANG:
return canMoveXIANG(moveID, killID, x, y);
break;
case Chess::CHE:
return canMoveCHE(moveID, killID, x, y);
break;
case Chess::MA:
return canMoveMA(moveID, killID, x, y);
break;
case Chess::PAO:
return canMovePAO(moveID, killID, x, y);
break;
case Chess::BING:
return canMoveBING(moveID, killID, x, y);
break;
default: break;
}
}
// 棋子颜色相同,不能走
if(sameColor(moveID, killID))
return false;
return false;
}
06 - 总结
到了这里,象棋走棋规则就制定完毕了,接下来需要结合鼠标按压事件,获取到走棋目标坐标,然后使用上象棋走棋规则判断点击选中的棋子是否能移动,然后在进行图标绘制和删除就能完成象棋游戏。
- 01_开发象棋游戏简介
- 02_绘画象棋棋盘
- 03_象棋棋子摆放
- 04_象棋走棋规则——車、炮、士
- 05_象棋走棋规则——象、马、将、兵
- 06_象棋游戏法则
- 07_人机博弈算法开端
- 08_人机博弈高阶算法