Hello!大家好!我们前两次已经完成了对棋子基本走法与吃法的约束,今天我们要加入将军的概念,它牵扯到如下几个规则:
1.己方的王不能走(或吃)到对方棋子威胁到的格子
2.当己方被将军时,必须应对将军,不能走其他的棋
3.当己方一枚棋子挡在对方棋子与己方的王之间时,该棋子不能走开,以至于把王暴露给对方。
我本来想按照一般的思路来写出合法走棋的逻辑,但是后来我意识到还有第三条规则,它成为压倒我代码的最后一根稻草,如果按照普通逻辑来写的话将会非常麻烦,因此我用了另一种方法,就是在走棋的时候不考虑将军的问题,在走完棋之后再检查一下己方是否仍然被将军,如果仍然被将军说明走的棋是违例的,这是我们就要加入悔棋的功能,自动悔棋,返回上一步棋。
首先我们要写两个判断是否被将军的函数。
private static boolean ischecking(MyLabel[][] a) {
for(int i=1;i<rows+1;i++) {
for(int j=0;j<cols+1;j++) {对每一个格子进行循环
MyIcon icon=(MyIcon) a[i][j].getIcon();
if(icon!=null&&getname(icon)=="king") {找到有王的那个格子
for(int x=1;x<rows+1;x++) {再次对每个格子进行循环
for(int y=1;y<cols+1;y++) {
MyIcon icon1=(MyIcon) a[x][y].getIcon();
if(icon1!=null&&getside(icon1)!=getside(icon)判断该格子上有棋子且棋子与王不是同一势力
&&getside(icon1)==sidetomove
&&islegalmove(a[x][y],a[i][j])) { 判断走棋的合法性
return true;
}
}
}
}
}
}
return false;
}
private static boolean ischecking2(MyLabel[][] a) {该函数逻辑同上
for(int i=1;i<rows+1;i++) {
for(int j=0;j<cols+1;j++) {
MyIcon icon=(MyIcon) a[i][j].getIcon();
if(icon!=null&&getname(icon)=="king") {
for(int x=1;x<rows+1;x++) {
for(int y=1;y<cols+1;y++) {
MyIcon icon1=(MyIcon) a[x][y].getIcon();
if(icon1!=null&&getside(icon1)!=getside(icon)
&&getside(icon1)!=sidetomove
&&islegalmove(a[x][y],a[i][j])) {
return true;
}
}
}
}
}
}
return false;
}
大家在看完这两个函数之后可能看不出这两个函数的区别,我在这里给大家解释一下。
第一个函数,是用来判断走完一步棋之后是否还被将军;而第二个函数是用来判断在走棋之前是否被将军。
这两个函数代码上的区别就在于在最里面的一连串判断中第一个函数的条件是getside(icon1)==sidetomove,第二个函数的条件是getside(icon1)!=sidetomove。
大家还记得我们最初设定的move()函数吗?在点击第一下(选中棋子)的时候我们就已经换了sidetomove,打个比方,如果轮到白方走,在选中一枚白方的棋子后,sidetomove就会变成黑方,然后在点击第二下之后白方就完成了走子,这时候我们判断白方是否仍然被将军,就要判断是否有黑方的棋子直接威胁到王,所以getside(icon1)和sidetomove两者之间应该相等。反之,如果要在走棋之前就判断是否被将军的话,两者之间自然是不相等的。
在判断完将军之后,如果仍然被将军,我们就要自动悔棋。因此我们需要设计一个悔棋的函数。悔棋函数大概的逻辑就是,找出前一步棋的起始点和终止点,然后起始点上重新设置上movingpiece,即被移动的棋子。并在终止点上设置上capturedpiece,这样一来如果上