private int SEE(Player player, int alpha, int beta, Square squareCaptured) { // Static Exchange Evaluator int val = int.MinValue; int intScoreAtEntry = 0; Move moveThis = null; // Generate moves Moves movesPossible = new Moves(); squareCaptured.AttackerMoveList(movesPossible, player); if (movesPossible.Count == 0) { // alpha = this.Score; alpha = -player.OtherPlayer.SEEScore; } else { // Sort moves foreach (Move movex in movesPossible) { movex.Score = (movex.PieceTaken.Value - movex.Piece.Value / 10); } movesPossible.SortByScore(); foreach (Move move in movesPossible) { moveThis = move.Piece.SEEMove(move.Name, move.To); if (moveThis.IsInCheck) { Move.SEEUndo(moveThis); continue; } val = -SEE(player.OtherPlayer, -beta, -alpha, squareCaptured); if (val < intScoreAtEntry) { val = intScoreAtEntry; } Move.SEEUndo(moveThis); if (val >= beta) { return(beta); } if (val > alpha) { alpha = val; } } } return(alpha); }
public Move ComputeBestMove() { m_blnIsThinking = true; Player player = this; m_moveBest = null; Move moveHash = null; int alpha_start = this.Score; /* Uncomment to switch-on opening book moves, once we have a decent opening book! * // Query Opening Book * if ((m_moveBest = OpeningBook.SearchForGoodMove(Board.HashKey, this.Colour) )!=null) * { * m_moveCurrent = m_moveBest; * this.MoveConsidered(); * return m_moveBest.Piece.Move(m_moveBest.Name, m_moveBest.To); * } */ Move moveDepthBest = null; int alpha = MIN_SCORE; int beta = MAX_SCORE; m_intRootScore = this.Score; m_tsnThinkingTimeAllotted = new TimeSpan(this.m_PlayerClock.TimeRemaining.Ticks / Math.Max(m_intGameMoves - (Game.TurnNo / 2), 1)); m_tsnThinkingTimeMaxAllowed = new TimeSpan(m_tsnThinkingTimeAllotted.Ticks * 3); m_tsnThinkingTimeHalved = new TimeSpan(m_tsnThinkingTimeAllotted.Ticks / 3); m_intEvaluations = 0; m_intPositionsSearched = 0; HashTable.ResetStats(); // HashTable.Clear(); Uncomment this to clear the hashtable at the beginning of each move HashTableCheck.ResetStats(); HashTablePawn.ResetStats(); History.Clear(); for (m_intSearchDepth = m_intMinSearchDepth; m_intSearchDepth <= m_intMaxSearchDepth; m_intSearchDepth++) { if (Game.DisplayMoveAnalysisTree) { Game.MoveAnalysis = new Moves(); } m_intMaxQDepth = m_intSearchDepth; // Get last iteration's best move from the HashTable moveHash = HashTable.ProbeForBestMove(player.Colour); // Generate and sort moves Moves movesPossible = new Moves(); player.GenerateLegalMoves(movesPossible); m_intTotalMoves = movesPossible.Count; // If only one move is available, then just play it! if (movesPossible.Count == 1) { moveDepthBest = m_moveCurrent = movesPossible.Item(0); goto MoveSelected; } // Sort moves foreach (Move movex in movesPossible) { movex.Score = 0; if (moveHash != null && movex.From.Ordinal == moveHash.From.Ordinal && movex.To.Ordinal == moveHash.To.Ordinal) { movex.Score += 1000000; } if (movex.Name == Move.enmName.PawnPromotion) { movex.Score += 10000; } if (movex.PieceTaken != null) { Move moveSee = movex.Piece.SEEMove(movex.Name, movex.To); movex.Score += -SEE(player.OtherPlayer, int.MinValue, int.MaxValue, movex.To); Move.SEEUndo(moveSee); } else { movex.Score += History.Retrieve(player.Colour, movex.From.Ordinal, movex.To.Ordinal); } } movesPossible.SortByScore(); alpha = MIN_SCORE; beta = MAX_SCORE; m_intCurrentMoveNo = 0; foreach (Move move in movesPossible) { m_moveCurrent = move.Piece.Move(move.Name, move.To); if (m_moveCurrent.IsInCheck) { Move.Undo(m_moveCurrent); continue; } move.Score = m_moveCurrent.Score = -AlphaBeta(player.OtherPlayer, m_intSearchDepth - 1, -alpha - 1, -alpha, true, ref m_moveCurrent); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeMaxAllowed) { Move.Undo(m_moveCurrent); goto TimeExpired; } if ((move.Score > alpha) && (move.Score < beta)) /* fail */ { move.Score = m_moveCurrent.Score = -AlphaBeta(player.OtherPlayer, m_intSearchDepth - 1, -beta, -alpha, true, ref m_moveCurrent); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeMaxAllowed) { Move.Undo(m_moveCurrent); goto TimeExpired; } } this.MoveConsidered(); if (Game.DisplayMoveAnalysisTree) { Game.MoveAnalysis.Add(m_moveCurrent); } Move.Undo(m_moveCurrent); m_intCurrentMoveNo++; if (m_moveCurrent.Score > alpha) { alpha = m_moveCurrent.Score; moveDepthBest = m_moveCurrent; History.Record(player.Colour, m_moveCurrent.From.Ordinal, m_moveCurrent.To.Ordinal, alpha - alpha_start, 1 << (m_intSearchDepth + 6)); // Update history heuristic data } m_moveCurrent.Alpha = alpha; m_moveCurrent.Beta = beta; } MoveSelected: m_moveBest = moveDepthBest; // Record best move HashTable.RecordHash(Board.HashCodeA, Board.HashCodeB, m_intSearchDepth, m_moveBest.Score, HashTable.enmHashType.Exact, m_moveBest.From.Ordinal, m_moveBest.To.Ordinal, m_moveBest.Name, player.Colour); this.MoveConsidered(); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeHalved && m_intSearchDepth >= m_intMinimumPlys) { goto TimeExpired; } if (m_moveBest.Score > 99999) { break; // Checkmate found so dont bother searching any deeper } } TimeExpired: this.MoveConsidered(); m_blnIsThinking = false; this.MoveConsidered(); return(m_moveBest); }