2017-05-17 12 views
1

私はチェスエンジンを静止状態にしてアルファベット検索を実装しました。しかし、ほとんどのポジションでは、プロファイラーに示されているように、静止時間の検索は実行時間の80-90%を占めています。私の剪定にバグがありますか?チェス:静止している検索ランタイムを支配する

私はalpha-betaルーチンと休止ルーチンの両方を含んでいます。

私の静止の検索は、直接this pseudocodeに基づいています。

// Perform the alpha-beta search. 
func ab(b *dragontoothmg.Board, alpha int16, beta int16, depth int8, halt chan bool, stop *bool) (int16, dragontoothmg.Move) { 
    nodeCount++ 

    if *stop { 
     return alpha, 0 
    } 

    found, tableMove, tableEval, tableDepth, tableNodeType := transtable.Get(b) 
    if found && tableDepth >= depth { 
     if tableNodeType == transtable.Exact { 
      return tableEval, tableMove 
     } else if tableNodeType == transtable.LowerBound { 
      alpha = max(alpha, tableEval) 
     } else { // upperbound 
      beta = min(beta, tableEval) 
     } 
     if alpha >= beta { 
      return tableEval, tableMove 
     } 
    } 
    if depth == 0 { 
     //return eval.Evaluate(b), 0 
     return quiesce(b, alpha, beta, stop), 0 
    } 

    alpha0 := alpha 
    bestVal := int16(negInf) 
    moves := b.GenerateLegalMoves() 
    var bestMove dragontoothmg.Move 
    if len(moves) > 0 { 
     bestMove = moves[0] // randomly pick some move 
    } 
    for _, move := range moves { 
     unapply := b.Apply(move) 
     var score int16 
     score, _ = ab(b, -beta, -alpha, depth-1, halt, stop) 
     score = -score 
     unapply() 
     if score > bestVal { 
      bestMove = move 
      bestVal = score 
     } 
     alpha = max(alpha, score) 
     if alpha >= beta { 
      break 
     } 
    } 

    if *stop { 
     return bestVal, bestMove 
    } 

    var nodeType uint8 
    if bestVal <= alpha0 { 
     nodeType = transtable.UpperBound 
    } else if bestVal >= beta { 
     nodeType = transtable.LowerBound 
    } else { 
     nodeType = transtable.Exact 
    } 
    transtable.Put(b, bestMove, bestVal, depth, nodeType) 
    return bestVal, bestMove 
} 

func quiesce(b *dragontoothmg.Board, alpha int16, beta int16, stop *bool) int16 { 
    nodeCount++ 
    if *stop { 
     return alpha 
    } 
    var standPat int16 
    found, _, evalresult, _, ntype := transtable.Get(b) 
    if found && ntype == transtable.Exact { 
     standPat = evalresult 
    } else { 
     standPat = eval.Evaluate(b) 
     transtable.Put(b, 0, standPat, 0, transtable.Exact) 
    } 
    if standPat >= beta { 
     return beta 
    } 
    if alpha < standPat { 
     alpha = standPat 
    } 
    moves := b.GenerateLegalMoves() 
    if len(moves) == 0 { // TODO(dylhunn): What about stalemate? 
     return negInf 
    } 
    for _, move := range moves { 
     if !isCapture(move, b) { 
      continue 
     } 
     unapply := b.Apply(move) 
     score := -quiesce(b, -beta, -alpha, stop) 
     unapply() 
     if score >= beta { 
      return beta 
     } 
     if score > alpha { 
      alpha = score 
     } 
    } 
    return alpha 
} 

func isCapture(m dragontoothmg.Move, b *dragontoothmg.Board) bool { 
    toBitboard := (uint64(1) << m.To()) 
    return (toBitboard&b.White.All != 0) || (toBitboard&b.Black.All != 0) 
} 

答えて

1

コードを正しく読み取れば、すべてのキャプチャを検索しています。あなたが仕事を保存するためにできることは、絶望的なキャプチャの動きを剪定することです。動きがとても悪いのでスキップするのが安全だというのはよくあることであるので、この技術は非常に安全です。

例えば、この位置を見てみましょう:

enter image description here

FEN:

  • Rxa7
  • Qxd7 +
  • Rxh7:rnbqkbnr/pppppppp/8/8/8/8/1PP1PPP1/RNBQKBNR w KQkq - 0 1

    を3つのキャプチャがあります。

エンジンがまずクイーンとのキャプチャ移動を試みるとします。ブラックは4つの方法で取り戻すことができますが、これらの動きのいずれかがカットオフにつながる可能性があります。

たとえば、ブラックはBxd7を再生します。現在、白は結果の位置にRxa7またはRxh7という2つのキャプチャを持っています。

ここで、ほとんどのエンジンは、白がすでにポーンを捕まえることさえ役立つものではない(ベータと比較して)素材に戻っていることを認識します。したがって、このルークの両方のキャプチャでは、カットオフが発生する可能性は低いです。

ここでは、現在の検索で引き続きこれらの移動を検索し続けます。そのようなケースを検出してこれらの動作をスキップすると、多くの作業が節約されます。

さらに最適化があります。例えば、static exchange evaluationの強いエンジンでは、Qxd7が1ポーンで勝つが、クイーンは緩んでしまう。これは悪い貿易だから、エンジンはすぐにこの動きをスキップすることができます。他の2つのルーキーキャプチャについても同じことが言えます。

いつもと同じように、トレードオフがあります。あなたがあまりにも積極的に剪定するなら、最終的には良い動きを刈るでしょう。一般的には、通常の検索では休止検索ではなく、積極的なプルーニングがうまくいくはずです。

関連する問題