public static void UndoMove() { if (m_movesHistory.Count > 0) { Move moveUndo = m_movesHistory.Item(m_movesHistory.Count - 1); m_playerToPlay.Clock.Revert(); m_movesRedoList.Add(moveUndo); Move.Undo(moveUndo); m_playerToPlay = m_playerToPlay.OtherPlayer; if (m_movesHistory.Count > 1) { Move movePenultimate = m_movesHistory.Item(m_movesHistory.Count - 2); m_playerToPlay.Clock.TimeElapsed = movePenultimate.TimeStamp; } else { m_playerToPlay.Clock.TimeElapsed = new TimeSpan(0); } m_playerToPlay.Clock.Start(); } }
private int AlphaBeta(Player player, int depth, int alpha, int beta, bool verify, ref Move moveAnalysed) { int val = int.MinValue; HashTable.enmHashType hashType = HashTable.enmHashType.Alpha; Move moveBest = null; Move moveHash = null; bool blnPVNode = false; int intScoreAtEntry = 0; // bool failhigh = false; bool blnAllMovesWereGenerated; int intLegalMovesAttempted = 0; m_intPositionsSearched++; if ((val = HashTable.ProbeHash(Board.HashCodeA, Board.HashCodeB, depth, alpha, beta, player.Colour)) != HashTable.UNKNOWN) { // High values of "val" indicate that a checkmate has been found if (val > 1000000 || val < -1000000) { val /= (m_intMaxSearchDepth - depth); } return(val); } if (player.CanClaimThreeMoveRepetition) { return(-player.OtherPlayer.Score); } // Depth <=0 means we're into Quiescence searching if (depth <= 0) { if (depth < m_intMaxQDepth) { m_intMaxQDepth = depth; if (m_intMaxQDepth < 0) { m_intMaxQDepth += 0; } } intScoreAtEntry = val = -player.OtherPlayer.Score; m_intEvaluations++; if (val > 100000000 || val < -100000000) { val /= (m_intMaxSearchDepth - depth); } // Allow a deeper ply of search if a piece was captured or if a pawn was promoted, // or either side is in check. if (!( moveAnalysed.PieceTaken != null || moveAnalysed.Name == Move.enmName.PawnPromotion || (moveAnalysed.IsInCheck || moveAnalysed.IsEnemyInCheck) ) ) { return(val); } } if (Game.DisplayMoveAnalysisTree) { moveAnalysed.Moves = new Moves(); } Move moveThis = null; // Get last iteration's best move from the Transition Table moveHash = HashTable.ProbeForBestMove(player.Colour); // Verified Null-move forward pruning // if (false && !player.IsInCheck && (!verify || depth > 1)) // { // "Adaptive" Null-move forward pruning R = (depth > 6 && Game.Stage != Game.enmStage.End) ? 3 : 2; // << This is the "adaptive" bit // The rest is normal Null-move forward pruning if (depth >= 3) { Move moveNull = new Move(Game.TurnNo, 0, Move.enmName.NullMove, null, null, null, null, 0, 0); val = -AlphaBeta(player.OtherPlayer, depth - R - 1, -beta, -beta + 1, verify, ref moveNull); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeMaxAllowed) { goto TimeExpired; } if (val >= beta) { // if (verify) // { // depth--; /* reduce the depth by one ply */ // /* turn verification off for the sub-tree */ // verify = false; // /* mark a fail-high flag, to detect zugzwangs later*/ // failhigh = true; // } // else /* cutoff in a sub-tree with fail-high report */ // { // return val; return(beta); // } } } // Generate moves Moves movesPossible = new Moves(); blnAllMovesWereGenerated = (depth > 0 || (moveAnalysed.IsInCheck || moveAnalysed.IsEnemyInCheck)); if (blnAllMovesWereGenerated) { player.GenerateLazyMoves(depth, movesPossible, Moves.enmMovesType.All, null); } else { // Captures only player.GenerateLazyMoves(depth, movesPossible, Moves.enmMovesType.Recaptures_Promotions, moveAnalysed.To); } // Enhanced Transposition Cutoff foreach (Move movex in movesPossible) { if (((val = HashTable.ProbeHash(movex.HashCodeA, movex.HashCodeB, depth, alpha, beta, player.Colour)) != HashTable.UNKNOWN) && val >= beta) { return(beta); } } // 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) { // if (depth>=5) // { // SEE (Static Exchange Evaluation) // Move moveSee = movex.Piece.SEEMove(movex.Name, movex.To); // movex.Score += -SEE(player.OtherPlayer, int.MinValue, int.MaxValue, movex.To); // Move.SEEUndo(moveSee); // } // else // { // MVV/LVA (Most Valuable Victim/Least Valuable Attacker) movex.Score += (movex.PieceTaken.Value - movex.Piece.Value / 10); // } } else { movex.Score += (History.Retrieve(player.Colour, movex.From.Ordinal, movex.To.Ordinal) >> 6); } } movesPossible.SortByScore(); // ReSearch: foreach (Move move in movesPossible) { moveThis = move.Piece.Move(move.Name, move.To); if (moveThis.IsInCheck) { Move.Undo(moveThis); continue; } intLegalMovesAttempted++; if (moveBest == null) { moveBest = moveThis; } /* * // Futility Pruning * switch (depth) * { * case 2: * case 3: * if (move.PieceTaken==null && !moveThis.IsInCheck && !move.IsEnemyInCheck) * { * if ( (val = HashTable.ProbeHash(Board.HashCodeA, Board.HashCodeB, depth, alpha, beta, player.Colour)) == HashTable.UNKNOWN ) * { * val = -player.OtherPlayer.Score; m_intEvaluations++; * HashTable.RecordHash(Board.HashCodeA, Board.HashCodeB, depth, val, HashTable.enmHashType.Exact, -1, -1, Move.enmName.NullMove, player.Colour); * } * * switch (depth) * { * case 2: * // Standard Futility Pruning * if (val+2500<=alpha) * { * depth--; * } * break; * * case 3: * // Extended Futility Pruning * if (val+6500<=alpha) * { * depth--; * } * break; * } * } * break; * } */ if (Game.DisplayMoveAnalysisTree) { // Add moves to post-move analysis tree, if option set by user moveAnalysed.Moves.Add(moveThis); } if (blnPVNode) { val = -AlphaBeta(player.OtherPlayer, depth - 1, -alpha - 1, -alpha, verify, ref moveThis); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeMaxAllowed) { Move.Undo(moveThis); goto TimeExpired; } if ((val > alpha) && (val < beta)) /* fail */ { val = -AlphaBeta(player.OtherPlayer, depth - 1, -beta, -alpha, verify, ref moveThis); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeMaxAllowed) { Move.Undo(moveThis); goto TimeExpired; } } } else { val = -AlphaBeta(player.OtherPlayer, depth - 1, -beta, -alpha, verify, ref moveThis); if ((DateTime.Now - m_PlayerClock.TurnStartTime) > m_tsnThinkingTimeMaxAllowed) { Move.Undo(moveThis); goto TimeExpired; } } if (!blnAllMovesWereGenerated && val < intScoreAtEntry) { // This code is executed mostly in quiescence when not all moves are tried (maybe just captures) // and the best score we've got is worse than the score we had before we considered any moves // then revert to that score, because we dont want the computer to think that it HAS to make a capture val = intScoreAtEntry; } move.Score = moveThis.Score = val; Move.Undo(moveThis); if (val >= beta) { alpha = beta; moveThis.Beta = beta; hashType = HashTable.enmHashType.Beta; moveBest = moveThis; goto Exit; } if (val > alpha) { blnPVNode = true; /* This is a PV node */ alpha = val; hashType = HashTable.enmHashType.Exact; moveBest = moveThis; } moveThis.Alpha = alpha; moveThis.Beta = beta; } // Check for Stalemate if (intLegalMovesAttempted == 0) // depth>0 && !player.OtherPlayer.IsInCheck { // alpha = this.Score; alpha = -player.OtherPlayer.Score; } /* if(failhigh && alpha < beta) * { * depth++; * failhigh = false; * verify = true; * goto ReSearch; * } */ Exit: // Record best move if (moveBest != null) { History.Record(player.Colour, moveBest.From.Ordinal, moveBest.To.Ordinal, 0, 1 << (depth + 6)); HashTable.RecordHash(Board.HashCodeA, Board.HashCodeB, depth, alpha, hashType, moveBest.From.Ordinal, moveBest.To.Ordinal, moveBest.Name, player.Colour); } else { HashTable.RecordHash(Board.HashCodeA, Board.HashCodeB, depth, alpha, hashType, -1, -1, Move.enmName.NullMove, player.Colour); } TimeExpired: 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); }