1. 程式人生 > >中國象棋軟體-引擎實現(六)局面評估

中國象棋軟體-引擎實現(六)局面評估

前面已經講過了棋局表示、著法生成、搜尋演算法(包括搜尋輔助), 在象棋程式中如果說搜尋演算法是心臟,那麼局面評估就是大腦。搜尋演算法負責驅動整個程式,而局面評估則負責對搜尋的內容進行判斷評價。因而搜尋與局面評估是整個程式的核心。

首先,先介紹一下在局面評估中需要考慮的因素。就不同的棋類可能要考慮的因素略有差異。在中國象棋中所要考慮的最基本的幾個因素包括如下四點:

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 through
        case 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 through
        case 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 through
        case 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];
            else
              nMobilityVal[fSide] += MobilityValues[nCChessID - 7];
          }
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

          // 目標位置為對方棋子,將資訊存入RelationOfMan中的UnderAttack
          else 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中的UnderGuard
          else // 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