前面已经讲过了棋局表示、着法生成、搜索算法(包括搜索辅助), 在象棋程序中如果说搜索算法是心脏,那么局面评估就是大脑。搜索算法负责驱动整个程序,而局面评估则负责对搜索的内容进行判断评价。因而搜索与局面评估是整个程序的核心。

首先,先介绍一下在局面评估中需要考虑的因素。就不同的棋类可能要考虑的因素略有差异。在中国象棋中所要考虑的最基本的几个因素包括如下四点:

1、子力总和

子力是指某一棋子本身所具有的价值。通俗地讲就是一个棋子它值个什么价。例如,车值500的话,那可能马值300,卒值80等等。所以在评估局面时,我们首先要考虑双方的子力总和的对比。比如红方拥有士象全加车马炮,而黑方只有残士象加双马,则红方明显占优。

2、棋子位置(控制区域)

棋子位置(或称控制区域)是指某一方的棋子在棋盘上所占据(控制)的位置。例如,沉底炮、过河卒、以及车占士角等都是较好的棋子位置状态,而窝心马等则是较差的棋子位置状态。

3、棋子的机动性

棋子的机动性指棋子的灵活度(可移动性)。例如,起始位置的车机动性较差,所以我们下棋讲究早出车。同样四面被憋马腿的死马机动性也较差。

4、棋子的相互关系(包括攻击关系和保护关系)

这一点的分析较为复杂,因为一个棋子与不同的子之间往往存在多重关系。如:一个马可能在对方的炮的攻击之下同时它又攻击着对方的车。

在我的程序中,估值函数最后返回的是双方总分的差值,而各方的总分就是上面所提到的四个因素的打分的总和。

对于子力打分和控制区域打分,只要遍历棋盘,当遇到棋子时简单地去查事先定义好的“子力价值表”和“控制区域价值表”,取出相对应的值进行累加即可(这些具体值参考了前人的程序并作了适当的调整,今后仍应根据电脑下棋所反映出的实际问题对这些值作适当修改)。对于机动性打分,需要求出各个子总共有多少种走法,然后根据各个子所不同的机动性价值每多一种走法就加一次相应的分数。

对棋子相互关系的打分,我先定义了一个关系表的结构类型

typedef struct _relationtable{BYTE nCChessID ;int nUAttackCount ;int nUGuardCount ;BYTE UnderAttack[5];BYTE UnderGurad[5];
} RelationTable;
RelationTable RelationOfMan[9][10];  // 关系表

其中nCChessID表明棋子类型,nUAttackCount和nUGuardCount分别记录该正攻击该子的棋子数量和正保护该子的棋子数量,UnderAttack[5]和UnderGuard[5]则存放攻击该子和保护该子的具体棋子的类型。因考虑实战中很难出现同时有超过五个棋子攻击/保护一个子的情况,故数组下标设定为5。
当遍历一遍棋盘之后,子力打分、控制区域打分和机动性打分都可以完成,而关系表也可以填完。之后,再根据关系表来具体考察棋子的相互关系,进行关系打分。
分析关系时,首先,对王的攻击保护应分离出来单独考虑,因为对王的保护没有任何意义,一旦王被吃掉整个游戏就结束了。
其次,对一个普通子,当它既受到攻击又受到保护的时候要注意如下几个问题:
1、 攻击者子力小于被攻击者子力,攻击方将愿意换子。比如,一个车正遭受一个炮的攻击,那么任何对车的保护都将失去意义——对方肯定乐意用一个炮来换一个车。
2、 多攻击单保护的情况,并且攻击者最小子力小于被攻击者子力与保护者子力之和,则攻击方可能以一子换两子。
3、 三攻击两保护的情况,并且攻击者子力较小的二者之和小于被攻击者子力与保护者子力之和,则攻击方可能以两子换三子。
4、 攻击方与保护方数量相同,并且攻击者子力小于被攻击者子力与保护者子力之和再减去保护者中最大子力,则攻击方可能以n子换n子。

可能上面这几条说的大家有点晕,待会结合具体程序可能更易于理解。当然,上述四条只是覆盖了最常见的几种情况,覆盖并不全面。而且,并没有直接地重新考虑双方兑子之后的控制区域及机动性变化情况(之所以说没有直接考虑,是因为搜索继续展开结点后仍会考虑这些因素,只是目前小生尚不知这样效果是否受影响)。所以,如果下一步要对程序进行改进的话,应当在此多做文章……

下面是CChessEvaluate.h的代码实现:

// CChessEvaluate.h    // Type Define     typedef struct _relationtable{    BYTE nCChessID ;int nUAttackCount ;int nUGuardCount ;BYTE UnderAttack[5];BYTE UnderGurad[5];    } RelationTable;    /// Data Define ///    POINT PointList[20];    // 目标点队列
int nPointCount;      // 目标点数目    RelationTable RelationOfMan[9][10];  // 关系表    const int MaxValue = 10000;  // 最大极值(最小极值为最大极值的负值)    //各子的基本价值(子力价值)
//                              将, 士, 象, 马, 车, 炮, 卒
const int BasicValues[15] = { 0, 0, 250, 250, 300, 500, 300, 80,0, 250, 250, 300, 500, 300, 80 };    //各子的机动性价值(每多一步走法所加的分)
//                                  将, 士, 象, 马, 车, 炮, 卒
const int MobilityValues[8] = {  0,  0,   1,   1,  12,   6,   6,  15 };    //各子的控制区域价值(所在的位置的价值)
const int PositionValues[8][90] = {{0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0},{ // 帅0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,1, -8, -9,  0,  0,  0,  0,  0,  0,  0,5, -8, -9,  0,  0,  0,  0,  0,  0,  0,1, -8, -9,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0},{ // 士0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  3,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0},{ // 相0,  0, -2,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  3,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0, -2,  0,  0,  0,  0,  0,  0,  0},{ // 马0, -3,  5,  4,  2,  2,  5,  4,  2,  2,-3,  2,  4,  6, 10, 12, 20, 10,  8,  2,2,  4,  6, 10, 13, 11, 12, 11, 15,  2,0,  5,  7,  7, 14, 15, 19, 15,  9,  8,2,-10,  4, 10, 15, 16, 12, 11,  6,  2,0,  5,  7,  7, 14, 15, 19, 15,  9,  8,2,  4,  6, 10, 13, 11, 12, 11, 15,  2,-3,  2,  4,  6, 10, 12, 20, 10,  8,  2,0, -3,  5,  4,  2,  2,  5,  4,  2,  2},{ // 车-6,  5, -2,  4,  8,  8,  6,  6,  6,  6,6,  8,  8,  9, 12, 11, 13,  8, 12,  8,4,  6,  4,  4, 12, 11, 13,  7,  9,  7,12, 12, 12, 12, 14, 14, 16, 14, 16, 13,0,  0, 12, 14, 15, 15, 16, 16, 33, 14,12, 12, 12, 12, 14, 14, 16, 14, 16, 13,4,  6,  4,  4, 12, 11, 13,  7,  9,  7,6,  8,  8,  9, 12, 11, 13,  8, 12,  8,-6,  5, -2,  4,  8,  8,  6,  6,  6,  6},{ // 炮0,  0,  1,  0, -1,  0,  0,  1,  2,  4,0,  1,  0,  0,  0,  0,  3,  1,  2,  4,1,  2,  4,  0,  3,  0,  3,  0,  0,  0,3,  2,  3,  0,  0,  0,  2, -5, -4, -5,3,  2,  5,  0,  4,  4,  4, -4, -7, -6,3,  2,  3,  0,  0,  0,  2, -5, -4, -5,1,  2,  4,  0,  3,  0,  3,  0,  0,  0,0,  1,  0,  0,  0,  0,  3,  1,  2,  4,0,  0,  1,  0, -1,  0,  0,  1,  2,  4},{ // 兵0,  0,  0, -2,  3, 10, 20, 20, 20,  0,0,  0,  0,  0,  0, 18, 27, 30, 30,  0,0,  0,  0, -2,  4, 22, 30, 45, 50,  0,0,  0,  0,  0,  0, 35, 40, 55, 65,  2,0,  0,  0,  6,  7, 40, 42, 55, 70,  4,0,  0,  0,  0,  0, 35, 40, 55, 65,  2,0,  0,  0, -2,  4, 22, 30, 45, 50,  0,0,  0,  0,  0,  0, 18, 27, 30, 30,  0,0,  0,  0, -2,  3, 10, 20, 20, 20,  0}
};    /// Function Prototype     // 估值函数,返回对当前局面的估值。fWhoseTurn标志当前轮到哪一方走棋
int Eveluate( int fWhoseTurn );    // 将目标点加入PointList队列
inline void AddPointToQueue( BYTE x, BYTE y );    // Programmer-Defined Function     int Eveluate( int fWhoseTurn )
{int RedValues  = 0;  // 红方总的分值int BlackValues  = 0;  // 黑方总的分值    int nBasicVal[2]  = { 0 , 0 };  // 双方的子力值int nMobilityVal[2]  = { 0 , 0 };  // 双方的机动性值int nPositionVal[2]  = { 0 , 0 };  // 双方的控制区域值int nRelationVal[2]  = { 0 , 0 };  // 双方的关系值(攻击或保护)    BYTE nCChessID;BYTE nTargetType;int fSide;int nPosition;int i;bool bHaveHalf;    //**** Reset RelationTable ****memset( RelationOfMan, 0, sizeof(RelationTable)*90 );    int x, y;for( x = 0; x <= 8; x ++ )for( y = 0; y <= 9; y ++ ){if( CChessBoard[x][y] != 0 ){nCChessID = CChessBoard[x][y];fSide = SideOfMan[ nCChessID ];    //+++++++++++求得棋子的基本价值以及它所占据的位置的价值+++++++++++++++++nPosition = x * 10 + y * 9 ;    if( fSide == RED ){nBasicVal[fSide]  += BasicValues[nCChessID];nPositionVal[fSide]  += PositionValues[nCChessID][nPosition];}else{nBasicVal[fSide]  += BasicValues[nCChessID];nPositionVal[fSide]  += PositionValues[nCChessID - 7][89 - nPosition];}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  //求得棋子的所有目标位置(移动到的、攻击的、保护的)nPointCount = 0;    switch( nCChessID ){    case RED_K:    // 将帅碰面i = IsKingFaceToFace( x, y, fSide );if( i != -1 )AddPointToQueue( x, i );    //向前if( y < 2 )AddPointToQueue( x, y + 1 );    //向后if( y > 0 )AddPointToQueue( x, y - 1 );    //向左if( x > 3 )AddPointToQueue( x - 1, y );    //向右if( x < 5 )AddPointToQueue( x + 1, y );    break;    case BLACK_K:    // 将帅碰面i = IsKingFaceToFace( x, y, fSide );if( i != -1 )AddPointToQueue( x, i );    //向前if( y > 7 )AddPointToQueue( x, y - 1 );    //向后if( y < 9 )AddPointToQueue( x, y + 1 );    //向左if( x < 5 )AddPointToQueue( x + 1, y );    //向右if( x > 3 )AddPointToQueue( x - 1, y );    break;    case RED_J:          // fall throughcase BLACK_J:    //纵向for( i = y + 1; i <= 9; i ++ ){AddPointToQueue( x, i );    if( HaveMan( x, i ) )break;}    for( i = y - 1; i >= 0; i -- ){AddPointToQueue( x, i );    if( HaveMan( x, i ) )break;}    //横向for( i = x - 1; i >= 0; i -- ){AddPointToQueue( i, y );    if( HaveMan( i, y ) )break;}    for( i = x + 1; i <= 8; i ++ ){AddPointToQueue( i, y );    if( HaveMan( i, y ) )break;}    break;    case RED_M:          // fall throughcase BLACK_M:    if( y <= 7 && ! HaveMan( x, y + 1 ) ){//11点方向(相对红马)if( x > 0 )AddPointToQueue( x - 1, y + 2 );    //1点方向if( x < 8 )AddPointToQueue( x + 1, y + 2 );}    if( x <= 6 && ! HaveMan( x + 1, y ) ){//2点方向if( y < 9 )AddPointToQueue( x + 2, y + 1 );    //4点方向if( y > 0 )AddPointToQueue( x + 2, y - 1 );}    if( y >= 2 && ! HaveMan( x, y - 1 ) ){//5点方向if( x < 8 )AddPointToQueue( x + 1, y - 2 );    //7点方向if( x > 0 )AddPointToQueue( x - 1, y - 2 );}    if( x >= 2 && ! HaveMan( x - 1, y ) ){//8点方向if( y > 0 )AddPointToQueue( x - 2, y - 1 );    //10点方向if( y < 9 )AddPointToQueue( x - 2, y + 1 );}    break;    case RED_P:        // fall throughcase BLACK_P:    //纵向bHaveHalf = false;  //标志尚未发现中间子for( i = y + 1; i <= 9; i ++ ){if( ! bHaveHalf ) // 无中间子{if( ! HaveMan( x, i ) ){AddPointToQueue( x, i );}else // if( HaveMan( x, i ) ){bHaveHalf = true;}}else // 已有中间子{if( HaveMan( x, i ) ){AddPointToQueue( x, i );break;}}}    bHaveHalf = false;  //标志尚未发现中间子for( i = y - 1; i >= 0; i -- ){if( ! bHaveHalf ) // 无中间子{if( ! HaveMan( x, i ) ){AddPointToQueue( x, i );}else // if( HaveMan( x, i ) ){bHaveHalf = true;}}else // 已有中间子{if( HaveMan( x, i ) ){AddPointToQueue( x, i );break;}}}    //横向bHaveHalf = false;  //标志尚未发现中间子for( i = x - 1; i >= 0; i -- ){if( ! bHaveHalf ) // 无中间子{if( ! HaveMan( i, y ) ){AddPointToQueue( i, y );}else // if( HaveMan( i, y ) ){bHaveHalf = true;}}else // 已有中间子{if( HaveMan( i, y ) ){AddPointToQueue( i, y );break;}}}    bHaveHalf = false;  //标志尚未发现中间子for( i = x + 1; i <= 8; i ++ ){if( ! bHaveHalf ) // 无中间子{if( ! HaveMan( i, y ) ){AddPointToQueue( i, y );}else // if( HaveMan( i, y ) ){bHaveHalf = true;}}else // 已有中间子{if( HaveMan( i, y ) ){AddPointToQueue( i, y );break;}}}    break;    case RED_X:    if( x == 0 ){if( ! HaveMan(1, 3) )AddPointToQueue( 2, 4 );    if( ! HaveMan(1, 1) )AddPointToQueue( 2, 0 );}else if( x == 2 ){if( y == 4 ){if( ! HaveMan(1, 3) )AddPointToQueue( 0, 2 );    if( ! HaveMan(3, 3) )AddPointToQueue( 4, 2 );}else // y == 0{if( ! HaveMan(1, 1) )AddPointToQueue( 0, 2 );    if( ! HaveMan(3, 1) )AddPointToQueue( 4, 2 );}}else if( x == 4 ){if( ! HaveMan(3, 3) )AddPointToQueue( 2, 4 );    if( ! HaveMan(3, 1) )AddPointToQueue( 2, 0 );    if( ! HaveMan(5, 3) )AddPointToQueue( 6, 4 );    if( ! HaveMan(5, 1) )AddPointToQueue( 6, 0 );}else if( x == 6 ){if( y == 4 ){if( ! HaveMan(5, 3) )AddPointToQueue( 4, 2 );    if( ! HaveMan(7, 3) )AddPointToQueue( 8, 2 );}else //  y == 0{if( ! HaveMan(5, 1) )AddPointToQueue( 4, 2 );    if( ! HaveMan(7, 1) )AddPointToQueue( 8, 2 );}}else // x == 8{if( ! HaveMan(7, 3) )AddPointToQueue( 6, 4 );    if( ! HaveMan(7, 1) )AddPointToQueue( 6, 0 );}    break;    case BLACK_X:    if( x == 0 ){if( ! HaveMan(1, 6) )AddPointToQueue( 2, 5 );    if( ! HaveMan(1, 8 ) )AddPointToQueue( 2, 9 );}else if( x == 2 ){if( y == 5 ){if( ! HaveMan(1, 6) )AddPointToQueue( 0, 7 );    if( ! HaveMan(3, 6) )AddPointToQueue( 4, 7 );}else // y == 9{if( ! HaveMan(1, 8 ) )AddPointToQueue( 0, 7 );    if( ! HaveMan(3, 8 ) )AddPointToQueue( 4, 7 );}}else if( x == 4 ){if( ! HaveMan(3, 6) )AddPointToQueue( 2, 5 );    if( ! HaveMan(3, 8 ) )AddPointToQueue( 2, 9 );    if( ! HaveMan(5, 6) )AddPointToQueue( 6, 5 );    if( ! HaveMan(5, 8 ) )AddPointToQueue( 6, 9 );}else if( x == 6 ){if( y == 5 ){if( ! HaveMan(5, 6) )AddPointToQueue( 4, 7 );    if( ! HaveMan(7, 6) )AddPointToQueue( 8, 7 );}else // y == 9{if( ! HaveMan(5, 8 ) )AddPointToQueue( 4, 7 );    if( ! HaveMan(7, 8 ) )AddPointToQueue( 8, 7 );}}else // x == 8{if( ! HaveMan(7, 6) )AddPointToQueue( 6, 5 );    if( ! HaveMan(7, 8 ) )AddPointToQueue( 6, 9 );}    break;    case RED_S:    if( x == 3 ){AddPointToQueue( 4, 1 );}else if( x == 4 ){AddPointToQueue( 3, 2 );    AddPointToQueue( 3, 0 );    AddPointToQueue( 5, 2 );    AddPointToQueue( 5, 0 );}else //  x == 5{AddPointToQueue( 4, 1 );}    break;    case BLACK_S:    if( x == 3 ){AddPointToQueue( 4, 8 );}else if( x == 4 ){AddPointToQueue( 3, 7 );    AddPointToQueue( 3, 9 );    AddPointToQueue( 5, 7 );    AddPointToQueue( 5, 9 );}else // x == 5{AddPointToQueue( 4, 8 );}    break;    case RED_B:    //向前if( y < 9 )AddPointToQueue( x, y + 1 );    if( y >= 5 ) //兵已过河{//向左if( x > 0 )AddPointToQueue( x - 1, y );    //向右if( x < 8 )AddPointToQueue( x + 1, y );}    break;    case BLACK_B:    //向前if( y > 0 )AddPointToQueue( x, y - 1 );    if( y <= 4 ) //兵已过河{//向右if( x > 0 )AddPointToQueue( x - 1, y );    //向左if( x < 8 )AddPointToQueue( x + 1, y );}    break;    }  // end switch    for( i = 0; i < nPointCount; i ++ ){//保存目标位置的棋子状况nTargetType = CChessBoard[ PointList[i].x ][ PointList[i].y ];     //++++++++++++++求得棋子的机动性价值++++++++++++++++++++++++++++++++++// 目标位置为空,机动性加分if( nTargetType == 0 ){if( fSide == RED )nMobilityVal[fSide] += MobilityValues[nCChessID];elsenMobilityVal[fSide] += MobilityValues[nCChessID - 7];}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 目标位置为对方棋子,将信息存入RelationOfMan中的UnderAttackelse if( SideOfMan[nTargetType] != fSide ){//对王受攻击的情况作单独处理if( nTargetType == RED_K ){if( fWhoseTurn == BLACK )  // 红帅即将被将死{return MaxValue - 10;  // 返回失败极值(已验证应为 MaxValue )// 减去10表示此种情况稍好于王已经被吃的情况}else            // 仅仅是将军而已,关系值扣一点分{nRelationVal[RED] -= 20;continue;}    }if( nTargetType == BLACK_K ){if( fWhoseTurn == RED )    // 黑将即将被将死{return MaxValue - 10;  // 返回失败极值(已验证应为 MaxValue )// 减去10表示此种情况稍好于王已经被吃的情况}else            // 仅仅是将军而已,关系值扣一点分{nRelationVal[BLACK] -= 20;continue;}}    RelationOfMan[ PointList[i].x ][ PointList[i].y ].nCChessID  =  nTargetType ;    RelationOfMan[ PointList[i].x ][ PointList[i].y ]
.UnderAttack[RelationOfMan[ PointList[i].x ][ PointList[i].y ].nUAttackCount++]=  nCChessID ;    }    // 目标位置为己方棋子,将信息存入RelationOfMan中的UnderGuardelse // if( SideOfMan[nTargetType] == fSide ){// 若受保护的是王,则不进行处理。因为王受保护毫无意义if( nTargetType == RED_K || nTargetType == BLACK_K )continue;    RelationOfMan[ PointList[i].x ][ PointList[i].y ].nCChessID  =  nTargetType ;    RelationOfMan[ PointList[i].x ][ PointList[i].y ].UnderGurad[RelationOfMan[ PointList[i].x ][ PointList[i].y ].nUGuardCount ++]=  nCChessID ;}    }  // end for( i = 0; i < nPointCount; i ++ )    }  // end if( CChessBoard[x][y] != 0 )    }  // end for x 0 to 8, y 0 to 9    //+++++++++++求得棋子的关系价值(受攻击和受保护)++++++++++++++++++++++for( x = 0; x <= 8; x ++ )for( y = 0; y <= 9; y ++ ){int nAttack  = 0;    //用于记录攻击方总子力int nGuard  = 0;    //用于记录保护方总子力int nMinAttack  = 777;  //用于记录攻击方最小子力int nMaxAttack  = 0;  //用于记录攻击方最大子力//int nMinGuard  = 777;  //用于记录保护方最小子力int nMaxGuard  = 0;  //用于记录保护方最大子力int nflagValue  = 777;  //用于表记是否有攻击方子力低于被攻击者,//若有则其值为攻击方中最低子力    int nUnitValue; // 单位价值    if( RelationOfMan[x][y].nCChessID != 0 ){nCChessID = RelationOfMan[x][y].nCChessID;fSide = SideOfMan[nCChessID];    nUnitValue = BasicValues[nCChessID] >> 3;  // 单位价值取基本价值的1/8    // 统计攻击方的子力for( i = 0; i < RelationOfMan[x][y].nUAttackCount; i ++ ){// 查看是否有攻击者子力小于被攻击者子力,若有记录其中子力最小的if( BasicValues[ RelationOfMan[x][y].UnderAttack[i] ]< BasicValues[nCChessID]&& BasicValues[ RelationOfMan[x][y].UnderAttack[i] ] < nflagValue ){nflagValue =  BasicValues[ RelationOfMan[x][y].UnderAttack[i] ] ;}    if( BasicValues[ RelationOfMan[x][y].UnderAttack[i] ] < nMinAttack )nMinAttack = BasicValues[ RelationOfMan[x][y].UnderAttack[i] ];    if( BasicValues[ RelationOfMan[x][y].UnderAttack[i] ] > nMaxAttack )nMaxAttack = BasicValues[ RelationOfMan[x][y].UnderAttack[i] ];    nAttack += BasicValues[ RelationOfMan[x][y].UnderAttack[i] ];}    // 统计防守方的子力for( i = 0; i < RelationOfMan[x][y].nUGuardCount; i ++ ){//if( BasicValues[ RelationOfMan[x][y].UnderGurad[i] ] < nMinGuard )//  nMinGuard = BasicValues[ RelationOfMan[x][y].UnderGurad[i] ];    if( BasicValues[ RelationOfMan[x][y].UnderGurad[i] ] > nMaxGuard )nMaxGuard = BasicValues[ RelationOfMan[x][y].UnderGurad[i] ];    nGuard += BasicValues[ RelationOfMan[x][y].UnderGurad[i] ];}    if( nAttack == 0 )  // 没受攻击,而是只有保护{nRelationVal[fSide] += 5 * RelationOfMan[x][y].nUGuardCount ;}else        // 遭受攻击{if( nGuard == 0)  // 没有保护{if( fWhoseTurn != fSide )  // 轮到对方行棋{nRelationVal[fSide] -= 5 * nUnitValue ;}else            // 轮到己方行棋{nRelationVal[fSide] -=  nUnitValue ;}}else        // 有保护{// 攻击者子力小于被攻击者子力,对方将愿意换子if( nflagValue != 777 ){if( fWhoseTurn != fSide )  // 轮到对方行棋{nRelationVal[fSide]    -= 5 * nUnitValue ;nRelationVal[1 - fSide]  -= 5 * ( nflagValue >> 3 );}else            // 轮到己方行棋{nRelationVal[fSide]    -=  nUnitValue ;nRelationVal[1 - fSide]  -=  ( nflagValue >> 3 );}}// 多攻击单保护的情况并且攻击者最小子力小于被攻击者子力与// 保护者子力之和,则对方可能以一子换两子else if( RelationOfMan[x][y].nUGuardCount == 1&& RelationOfMan[x][y].nUAttackCount > 1&& nMinAttack < BasicValues[nCChessID] + nGuard ){if( fWhoseTurn != fSide )  // 轮到对方行棋{nRelationVal[fSide]    -= 5 * nUnitValue ;nRelationVal[fSide]    -= 5 * ( nGuard >> 3 );    nRelationVal[1 - fSide]  -= 5 * ( nMinAttack >> 3 );}else            // 轮到己方行棋{nRelationVal[fSide]    -=  nUnitValue ;nRelationVal[fSide]    -=  ( nGuard >> 3 );    nRelationVal[1 - fSide]  -=  ( nMinAttack >> 3 );}}// 三攻击两保护的情况并且攻击者子力较小的二者之和小于被攻击者子力与// 保护者子力之和,则对方可能以两子换三子else if( RelationOfMan[x][y].nUGuardCount == 2&& RelationOfMan[x][y].nUAttackCount == 3&& (nAttack - nMaxAttack) < (BasicValues[nCChessID] + nGuard) ){if( fWhoseTurn != fSide )  // 轮到对方行棋{nRelationVal[fSide]    -= 5 * nUnitValue ;nRelationVal[fSide]    -= 5 * ( nGuard >> 3 );    nRelationVal[1 - fSide]  -= 5 * ((nAttack - nMaxAttack) >> 3);}else            // 轮到己方行棋{nRelationVal[fSide]    -=  nUnitValue ;nRelationVal[fSide]    -=  ( nGuard >> 3 );    nRelationVal[1 - fSide]  -=  ( ( nAttack - nMaxAttack ) >> 3 );}}// 攻击方与保护方数量相同并且攻击者子力小于被攻击者子力与保护者子力// 之和,再减去保护者中最大子力,则对方可能以n子换n子else if( RelationOfMan[x][y].nUAttackCount== RelationOfMan[x][y].nUGuardCount&& nAttack < BasicValues[nCChessID] + nGuard - nMaxGuard ){if( fWhoseTurn != fSide )  // 轮到对方行棋{nRelationVal[fSide]    -= 5 * nUnitValue ;nRelationVal[fSide]    -= 5 * ( ( nGuard - nMaxGuard ) >> 3 );    nRelationVal[1 - fSide]  -= 5 * ( nAttack >> 3 );}else            // 轮到己方行棋{nRelationVal[fSide]    -=  nUnitValue ;nRelationVal[fSide]    -=  ( ( nGuard - nMaxGuard ) >> 3 );    nRelationVal[1 - fSide]  -=  ( nAttack >> 3 );}}else{//上述情况已基本涵盖最常见情况,因而其它情况暂时不做考虑}    }  // end 有保护    }  // end 遭受攻击    }  // end if( RelationOfMan[x][y].nCChessID != 0 )    }  // end for x 0 to 8, y 0 to 9
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 统计红方总分值RedValues  = nBasicVal[RED] + nPositionVal[RED]+ nMobilityVal[RED] + nRelationVal[RED] ;    // 统计黑方总分值BlackValues  = nBasicVal[BLACK] + nPositionVal[BLACK]+ nMobilityVal[BLACK] + nRelationVal[BLACK] ;    if( fWhoseTurn == RED )return ( RedValues - BlackValues )  + rand()%5 ;  //此处 +rand()%X 是通过else                   //加一个不会对分值造成大的影响的随机量以期电脑在应对同return ( BlackValues - RedValues )  + rand()%5 ;  //一种下法时不会每次都有//相同的回应。这样做以及X取5是否合适还有待实践检验……
}    inline void AddPointToQueue( BYTE x, BYTE y )
{PointList[nPointCount].x = x ;PointList[nPointCount].y = y ;    nPointCount ++;
}    // end of CChessEvaluate.h

中国象棋软件-引擎实现(六)局面评估相关推荐

  1. 中国象棋软件-引擎实现(七)测试程序

    之前我们已经讲了实现一个中国象棋软件的所有要素,本篇我们只是粗略地建一个工程再添加一点文件使得我们能看到程序的运行情况如何. 在界面完成之前,我先建了一个Win32控制台项目(学生朋友们对这个最熟悉也 ...

  2. [转载] 中国象棋软件-引擎实现(一)概述

    2005年6月我系第二批科技小组的项目正式确定为实现一款中国象棋对弈软件.基本功能包括人机对战.网络对战.我负责开发人机对战的引擎部分,也就是让计算机下棋.经过了暑假整整两个月的学习与实践,我终于初步 ...

  3. [转载] 中国象棋软件-引擎实现(二)棋局表示

    对于棋盘的表示当前比较先进的思想是"位棋盘"."位棋盘"用于国际象棋非常便捷,因为国际象棋的棋盘正好有64个格子,可以将一个棋盘的信息用一个64位的变量来表示. ...

  4. 中国象棋软件-引擎实现(一)概述

    程序的基本框架: 从程序的结构上讲,大体上可以将本程序划分为四大部分: 棋局表示. 着法生成. 搜索算法. 局面评估 程序的大概的思想是: 首先使用一个数据结构来描述棋局信息,对某一特定的棋局信息由着 ...

  5. 中国象棋软件-引擎实现(三)着法生成

    我们的程序需要让电脑能够在轮到它走子的时候执行一个它认为对它最有利的着法,那前提就是它要有诸多(也可能是唯一)可供选择的着法,提供所有可选着法的"清单"就是我们的着法生成器所要完成 ...

  6. 中国象棋软件-引擎实现(二)棋局表示

    对于棋盘的表示当前比较先进的思想是"位棋盘"."位棋盘"用于国际象棋非常便捷,因为国际象棋的棋盘正好有64个格子,可以将一个棋盘的信息用一个64位的变量来表示. ...

  7. 中国象棋软件-引擎实现(四)搜索算法

    对于棋类软件的搜索算法经前人的努力已形成了较为成熟的Alpha-Beta搜索算法以及其它一些辅助增强算法.所以小生在自己的程序中直接借鉴了Alpha-Beta搜索算法并辅以了历史启发.对此两者王小春的 ...

  8. 【C++课程设计】期末大作业 - 基于Qt开发的中国象棋软件

    1 题目 基于Qt的中国象棋软件开发 本博客只是简略的描述论文的大致结构.框架,以及软件开发的大致过程.有兴趣的参考原文档资料. 2总体设计与主要功能 2.1概述 中国象棋是起源于中国的一种棋,属于二 ...

  9. 中国象棋通用引擎协议

    一.概述 中国象棋通用引擎协议(Universal Chinese Chess Protocol,简称UCCI),是一种象棋界面和象棋引擎之间的基于文本的通讯协议.设立中国象棋通用引擎协议的目的有: ...

最新文章

  1. 【C++】用类来处理排序问题
  2. sqlserver去掉密码强度验证_安装sql2008数据库引擎配置时,提示sa密码强度不满足要求怎么办?...
  3. 干货:阅读跟踪 Java 源码的几个小技巧!
  4. php读取文本写入数据库,php读取txt文本文档数据库转入mysql数据库
  5. [Qt教程] 第13篇 2D绘图(三)绘制文字
  6. javascript字典中添加数组_在javascript中合并两个字典数组
  7. HashOperations
  8. 文本标点符号在前面怎么改_这三个标点符号,为什么写错的人越来越多?
  9. 中国象棋口诀及要领精髓
  10. 荣耀Magic 2 3D感光版来了,和苹果Face ID系统有何不同?
  11. IE6不兼容 CSS 改CSS没效果 原因
  12. 计算机无法安装蓝牙驱动,win10蓝牙驱动装不了怎么办_win10电脑蓝牙驱动无法安装处理方法-win7之家...
  13. 【WinCE】WinCE通过USB与PC同步
  14. python之pyautogui实现鼠标键盘控制
  15. html + layui 打开word文档
  16. word查找参考文献引用格式
  17. Python开发环境Spyder3安装方法
  18. outlook 日历共享_如何共享您的Outlook日历
  19. 2015年3月12日
  20. 宋晓丽20190919-1 每周例行报告

热门文章

  1. word excel 应用程序无法正常启动(0xc0000142)
  2. 浅谈 Quartz2D 在开发中的用处 - 图形的状态
  3. 为什么截图粘贴到WORD里看不见呢
  4. 高等数学(第七版)同济大学 习题1-6 个人解答
  5. php 挂qq,挂QQ的网页源代码[ASP/PHP/JS]
  6. 小橙序之家:小程序一定要进行微信认证吗
  7. 锁屏解锁对象-OPPO事例
  8. gcIPtoHostQueue.exe
  9. wmplayer参数说明
  10. 【面试总结】小灰灰求职进行曲(三)TCP/IP方向