上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人
5.1 简单的局面评估算法
我们先设计一个最简单的评估函数。下过象棋的人对棋子的价值都有这样一种共识,可以用下面的不等式来表达:
将 > 车 > 马、炮 >士、象 >卒
将的价值最大,失去它也就意味着输棋。车的价值次之,进攻与防守的面积比较大。马炮价值基本相同,马有蹩脚的时候,炮只能隔子进攻。当然也有这样的认识,中前盘炮的威力大于马,残局马的威力大于炮。士象主要用于防守,价值差不多。卒的价值最低,当然在特殊的时候,尤其是残局威力也是巨大的。
那么具体每个棋子的价值应该设为多少合适呢?这是个见仁见智的问题,不同的人有不同的认识。棋子基本价值的相互关系,会直接影响兑子、吃子等走法。在这里也给棋子设一个价值,如表5-1所示。
表5-1 棋子价值表
根据基本价值,我们可以得到一个最简单的局面评估算法,那就是只考虑棋子的基本价值。
算法5-1:评估算法
输入:棋盘数组 输出:局面的估值 wValue:红方棋子价值的总和 bValue:黑方棋子价值的总和 wValue=bValue=0; 1 行变量i=3 to 12 2 列变量j=3 to 11 对应一维数组下标p = i<<4+j 如果p位置已出界,则 返回第2步 pc为p位置对应的棋子 如果pc为红方棋子,则 wValue = wValue + pc棋子对应的子力值 否则: bValue = bValue + pc棋子对应的子力值 return wValue - bValue
程序代码
const short PieceValue[8]={1000,20,20,40,90,45,10,0}; int IntToSubscript(int a) //棋子整数值转换成字符表示 { if(a<16 && a>=48)
return 7; if(a >=32) a = a-16; switch(a) { case 16: return 0; case 17: case 18: return 1; case 19: case 20: return 2; case 21: case 22: return 3; case 23: case 24: return 4; case 25: case 26: return 5; case 27: case 28: case 29: case 30: case 31: return 6; default: return 7; } } short Eval(void)//评估函数 { int i,j; int p; //棋子位置 short bValue,wValue; bValue = wValue = 0; for(i=3; i<13; i++)//10行 { for(j=3; j<12; j++) //9列 { p=(i<<4)+j; //棋子位置 if(board[p] == 0) //无棋子 continue; else if( board[p] <32 ) ==0) { wValue = wValue + PieceValue[IntToSubscript(board[p])]; } else { bValue = bValue + PieceValue[IntToSubscript(board[p])]; } } }
return wValue - bValue; }
代码技巧
棋子的价值用一个全局一维数组保存。下标0到6依次对应的是将、士、象、马、车、炮、卒的价值。特别注意的是,这个顺序以后还会经常用到,最好是记住。从开局状态下,将的位置向一边延伸,就依次得到了士、象、马、车,然后再向前延伸,就得到炮和卒。
实际中,常常需要将一个棋子的代码值转换成这样的一个顺序值。如把车的值(红车23,24,黑车39,40),转换成下标4。特别定义一个函数进行转换:
Int IntToSubscript(int a)
输入是棋子代码值,输出是棋子顺序值。
PieceValue[IntToSubscript(board[p])]的执行顺序,先用board[p]计算出位置p的棋子代码,IntToSubscript()函数求出棋子代码对应的棋子顺序值,最后用PieceValue[]数组得到该棋子的价值。
参见程序5-1.cpp。