C/C++中国象棋程序入门与提高
上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。