既然Alpha-Beta搜索算法是在「最大-最小」的基礎上引入「樹的裁剪」的思想以期提升效率,那麼它的效率就取決於樹的結構——若是搜索了沒多久就發現能夠「裁剪」了,那麼須要分析的工做量將大大減小,效率天然也就大大提升;而若是直到分析了全部的可能性以後才能做出「裁剪」判斷,那此時「裁剪」也已經失去它原有的價值(由於你已經分析了全部狀況)。於是,要想保證Alpha-Beta搜索算法的效率就須要調整樹的結構,即調整待搜索的結點的順序,保證「裁剪」能夠儘量早地發生。算法

咱們能夠根據部分已經搜索的結果來調整將要搜索的結點的順序。由於一般一個局面經搜索被認爲較好時,在其後繼結點中每每有一些類似的局面(如某些可有可無的棋子位置有所不一樣)也是較好的。「歷史啓發」就是創建在這樣一種觀點之上的。在搜索的過程當中,每當找到一個好的走法,咱們就給該走法累加一個增量以記錄其「歷史得分」,一個屢次被搜索並認爲是好的走法的「歷史得分」就會較高。對於即將搜索的結點,按 「歷史得分」的高低對它們進行排序,保證較好的走法(「歷史得分」高的走法)排在前面,這樣Alpha-Beta搜索就能夠儘量早地進行「裁剪」,從而保證了搜索的效率。編程

下面分別是歷史啓發部分HistoryHeuristic.h以及着法排序部分SortMove.h的代碼實現,着法排序可使用各類排序算法,這裏我直接借用了王小春的《PC 遊戲編程(人機博弈)》中採用的「歸併排序」。數組

歷史啓發:數據結構

// HistoryHeuristic.h

#include     // For  void *memset( void *dest, int c, size_t count );

/////////////////// Data Define ///////////////////////////////////////////////

int HistoryTable[90][90];      //歷史記錄表

/////////////////// Function Prototype ////////////////////////////////////////

// 清空歷史記錄表(全置0)
inline void ResetHistoryTable();

// 取給定走法(move)的歷史得分,返回該得分
inline int GetHistoryScore( CCHESSMOVE *move );

// 爲一最佳走法(move)增添歷史記錄得分,nDepth標誌該走法所屬的搜索層數
inline void EnterHistoryScore( CCHESSMOVE *move, int nDepth );

////////////////// Programmer-Defined Function ////////////////////////////////

inline void ResetHistoryTable()
{
  memset( HistoryTable, 0, sizeof(int)*8100 );
}

inline int GetHistoryScore( CCHESSMOVE *move )
{
  int nFrom, nTo ;

  // 將位置座標轉換爲數組下標
  nFrom  = move->ptFrom.x * 9 + move->ptFrom.y;
  nTo    = move->ptTo.x   * 9 + move->ptTo.y;

  return HistoryTable[nFrom][nTo];  // 返回歷史紀錄表中的分數
}

inline void EnterHistoryScore( CCHESSMOVE *move, int nDepth )
{
  int nFrom, nTo;

  // 將位置座標轉換爲數組下標
  nFrom  = move->ptFrom.x * 9 + move->ptFrom.y;
  nTo    = move->ptTo.x   * 9 + move->ptTo.y;

  HistoryTable[nFrom][nTo] += 2 << nDepth;  // 增量爲2的nDepth次方
}

// end of HistoryHeuristic.h

着法排序:函數

// SortMove.h

/////////////////// Data Define ///////////////////////////////////////////////

CCHESSMOVE cmTargetBuffer[80];    //排序用的緩衝隊列

/////////////////// Function Prototype ////////////////////////////////////////

// 對長度爲nCount的着法隊列cmSource進行歸併排序
void MergeSort( CCHESSMOVE *cmSource, int nCount );    

// 爲MergeSort函數所調用,歸併排序首尾相接的兩個數組,
void MergePass( CCHESSMOVE *cmSource, CCHESSMOVE *cmTarget,
 int nLength, int nCount  ); 

// 爲MergePass函數所調用,歸併排序首尾相接的兩個數組(這兩個數組已分別排好序)
//!!!降序 !!!
// 兩個數組爲cmSource[beginOne]--cmSource[endOne];
//cmSource[beginTwo]--cmSource[endTwo]. 其中beginTwo = endOne + 1
inline void Merge_Desc( CCHESSMOVE *cmSource, CCHESSMOVE *cmTarget,
 int beginOne ,int endOne, int endTwo );

////////////////// Programmer-Defined Function ////////////////////////////////

void MergeSort( CCHESSMOVE *cmSource, int nCount )
{
  int nLength = 1;

  // 藉助cmTargetBuffer對cmSource進行歸併排序
  while( nLength < nCount )
  {
    MergePass( cmSource , cmTargetBuffer , nLength , nCount );
    nLength += nLength ;
    MergePass( cmTargetBuffer , cmSource , nLength , nCount );
    nLength += nLength ;
  }
}

void MergePass( CCHESSMOVE *cmSource, CCHESSMOVE *cmTarget,
 int nLength, int nCount  )
{
  int i = 0;  // 表記待排序的第一個數組的起點

  while( i + 2 * nLength <= nCount ) // 剩餘的元素個數大於等於兩個nLength長度
  {
    // 歸併排序長度爲 nLength 的首尾相接的兩個數組
    Merge_Desc( cmSource, cmTarget, i, i + nLength - 1, i + 2 * nLength - 1 );

    i += 2 * nLength ;
  }

  if( i + nLength < nCount )  //剩餘的元素個數小於兩個nLength長度,
  {                                      //但大於一個nLength長度
    // 歸併排序長度爲 nLength 的數組和與其相接的剩下的全部元素組成的數組
    Merge_Desc( cmSource, cmTarget, i, i + nLength - 1, nCount - 1 );
  }
  else            // 剩餘的元素個數小於等於一個nLength長度
  {
    int j;
    // 接上剩下的已經排序好的數組
    for ( j = i ; j < nCount ; j ++ )
      cmTarget[j] = cmSource[j];
  }

}

inline void Merge_Desc( CCHESSMOVE *cmSource, CCHESSMOVE *cmTarget,
 int beginOne ,int endOne, int endTwo )
{
  int i = beginOne ,
    j = endOne + 1,// j = beginTwo
    k = beginOne ;

  while( i <= endOne  &&  j <= endTwo )  //取兩個數組中值大的一個放入cmTarget
  {                                                       //(降序)
    if( cmSource[i].nScore >= cmSource[j].nScore )
      cmTarget[k++] = cmSource[i++];
    else
      cmTarget[k++] = cmSource[j++];
  }

  if( i <= endOne )  // 第一個數組還沒有完,則接上第一個數組的剩餘部分
  {
    int q;
    for( q = i; q <= endOne; q ++ )
      cmTarget[k++] = cmSource[q];
  }
  else // j <= endTwo    第二個數組還沒有完,則接上第二個數組的剩餘部分
  {
    int q;
    for( q = j; q <= endTwo; q ++ )
      cmTarget[k++] = cmSource[q];
  }
}

// end of SortMove.h