public static int GetSeeCaptureScore(ChessBoard cb, int move) { if (EngineConstants.Assert) { if (MoveUtil.GetAttackedPieceIndex(move) == 0) { Assert.IsTrue(MoveUtil.GetMoveType(move) != 0); } } var index = MoveUtil.GetToIndex(move); var allPieces = cb.AllPieces & ~Util.PowerLookup[MoveUtil.GetFromIndex(move)]; var slidingMask = MagicUtil.GetQueenMovesEmptyBoard(index) & allPieces; // add score when promotion if (MoveUtil.IsPromotion(move)) { return(EvalConstants.PromotionScore[MoveUtil.GetMoveType(move)] + EvalConstants.Material[MoveUtil.GetAttackedPieceIndex(move)] - GetSeeScore(cb, cb.ColorToMoveInverse, index, MoveUtil.GetMoveType(move), allPieces, slidingMask)); } return(EvalConstants.Material[MoveUtil.GetAttackedPieceIndex(move)] - GetSeeScore(cb, cb.ColorToMoveInverse, index, MoveUtil.GetSourcePieceIndex(move), allPieces, slidingMask)); }
public static int CalculateBestMove(ChessBoard cb, ThreadData threadData, int ply, int depth, int alpha, int beta, int nullMoveCounter) { if (!IsRunning) { return(ChessConstants.ScoreNotRunning); } if (EngineConstants.Assert) { Assert.IsTrue(depth >= 0); Assert.IsTrue(alpha >= Util.ShortMin && alpha <= Util.ShortMax); Assert.IsTrue(beta >= Util.ShortMin && beta <= Util.ShortMax); } var alphaOrig = alpha; // get extensions depth += Extensions(cb); /* mate-distance pruning */ if (EngineConstants.EnableMateDistancePruning) { alpha = Math.Max(alpha, Util.ShortMin + ply); beta = Math.Min(beta, Util.ShortMax - ply - 1); if (alpha >= beta) { return(alpha); } } // TODO JITWatch unpredictable branch if (depth == 0) { return(QuiescenceUtil.CalculateBestMove(cb, threadData, alpha, beta)); } /* transposition-table */ var ttEntry = TtUtil.GetEntry(cb.ZobristKey); var score = ttEntry.GetScore(ply); if (ttEntry.Key != 0) { if (!EngineConstants.TestTtValues) { if (ttEntry.Depth >= depth) { switch (ttEntry.Flag) { case TtUtil.FlagExact: return(score); case TtUtil.FlagLower: if (score >= beta) { return(score); } break; case TtUtil.FlagUpper: if (score <= alpha) { return(score); } break; } } } } if (Statistics.Enabled) { Statistics.AbNodes++; } var eval = Util.ShortMin; var isPv = beta - alpha != 1; if (!isPv && cb.CheckingPieces == 0) { eval = EvalUtil.GetScore(cb, threadData); /* use tt value as eval */ if (EngineConstants.UseTtScoreAsEval) { if (TtUtil.CanRefineEval(ttEntry, eval, score)) { eval = score; } } /* static null move pruning */ if (EngineConstants.EnableStaticNullMove && depth < StaticNullmoveMargin.Length) { if (eval - StaticNullmoveMargin[depth] >= beta) { if (Statistics.Enabled) { Statistics.StaticNullMoved[depth]++; } return(eval); } } /* razoring */ if (EngineConstants.EnableRazoring && depth < RazoringMargin.Length && Math.Abs(alpha) < EvalConstants.ScoreMateBound) { if (eval + RazoringMargin[depth] < alpha) { score = QuiescenceUtil.CalculateBestMove(cb, threadData, alpha - RazoringMargin[depth], alpha - RazoringMargin[depth] + 1); if (score + RazoringMargin[depth] <= alpha) { if (Statistics.Enabled) { Statistics.Razored[depth]++; } return(score); } } } /* null-move */ if (EngineConstants.EnableNullMove) { if (nullMoveCounter < 2 && eval >= beta && MaterialUtil.HasNonPawnPieces(cb.MaterialKey, cb.ColorToMove)) { cb.DoNullMove(); // TODO less reduction if stm (other side) has only 1 major piece var reduction = depth / 4 + 3 + Math.Min((eval - beta) / 80, 3); score = depth - reduction <= 0 ? -QuiescenceUtil.CalculateBestMove(cb, threadData, -beta, -beta + 1) : -CalculateBestMove(cb, threadData, ply + 1, depth - reduction, -beta, -beta + 1, nullMoveCounter + 1); cb.UndoNullMove(); if (score >= beta) { if (Statistics.Enabled) { Statistics.NullMoveHit++; } return(score); } if (Statistics.Enabled) { Statistics.NullMoveMiss++; } } } } var parentMove = ply == 0 ? 0 : threadData.Previous(); var bestMove = 0; var bestScore = Util.ShortMin - 1; var ttMove = 0; var counterMove = 0; var killer1Move = 0; var killer2Move = 0; var movesPerformed = 0; threadData.StartPly(); var phase = PhaseTt; while (phase <= PhaseQuiet) { switch (phase) { case PhaseTt: if (ttEntry.Key != 0) { ttMove = ttEntry.Move; if (cb.IsValidMove(ttMove)) { threadData.AddMove(ttMove); } // else { // throw new RuntimeException("invalid tt-move found: " + new MoveWrapper(ttMove)); // } } break; case PhaseAttacking: MoveGenerator.GenerateAttacks(threadData, cb); threadData.SetMvvlvaScores(); threadData.Sort(); break; case PhaseKiller1: killer1Move = threadData.GetKiller1(ply); if (killer1Move != 0 && killer1Move != ttMove && cb.IsValidMove(killer1Move)) { threadData.AddMove(killer1Move); break; } phase++; goto case PhaseKiller2; case PhaseKiller2: killer2Move = threadData.GetKiller2(ply); if (killer2Move != 0 && killer2Move != ttMove && cb.IsValidMove(killer2Move)) { threadData.AddMove(killer2Move); break; } phase++; goto case PhaseCounter; case PhaseCounter: counterMove = threadData.GetCounter(cb.ColorToMove, parentMove); if (counterMove != 0 && counterMove != ttMove && counterMove != killer1Move && counterMove != killer2Move && cb.IsValidMove(counterMove)) { threadData.AddMove(counterMove); break; } phase++; goto case PhaseQuiet; case PhaseQuiet: MoveGenerator.GenerateMoves(threadData, cb); threadData.SetHhScores(cb.ColorToMove); threadData.Sort(); break; } while (threadData.HasNext()) { var move = threadData.Next(); switch (phase) { case PhaseQuiet when move == ttMove || move == killer1Move || move == killer2Move || move == counterMove || !cb.IsLegal(move): case PhaseAttacking when move == ttMove || !cb.IsLegal(move): continue; } // pruning allowed? if (!isPv && cb.CheckingPieces == 0 && movesPerformed > 0 && threadData.GetMoveScore() < 100 && !cb.IsDiscoveredMove(MoveUtil.GetFromIndex(move))) { if (phase == PhaseQuiet) { /* late move pruning */ if (EngineConstants.EnableLmp && depth <= 4 && movesPerformed >= depth * 3 + 3) { if (Statistics.Enabled) { Statistics.Lmped[depth]++; } continue; } /* futility pruning */ if (EngineConstants.EnableFutilityPruning && depth < FutilityMargin.Length) { if (!MoveUtil.IsPawnPush78(move)) { if (eval == Util.ShortMin) { eval = EvalUtil.GetScore(cb, threadData); } if (eval + FutilityMargin[depth] <= alpha) { if (Statistics.Enabled) { Statistics.Futile[depth]++; } continue; } } } } /* SEE Pruning */ else if (EngineConstants.EnableSeePruning && depth <= 6 && phase == PhaseAttacking && SeeUtil.GetSeeCaptureScore(cb, move) < -20 * depth * depth) { continue; } } cb.DoMove(move); movesPerformed++; /* draw check */ if (cb.IsRepetition(move) || MaterialUtil.IsDrawByMaterial(cb)) { score = EvalConstants.ScoreDraw; } else { score = alpha + 1; // initial is above alpha var reduction = 1; if (depth > 2 && movesPerformed > 1 && MoveUtil.IsQuiet(move) && !MoveUtil.IsPawnPush78(move)) { reduction = LmrTable[Math.Min(depth, 63)][Math.Min(movesPerformed, 63)]; if (threadData.GetMoveScore() > 40) { reduction -= 1; } if (move == killer1Move || move == killer2Move || move == counterMove) { reduction -= 1; } if (!isPv) { reduction += 1; } reduction = Math.Min(depth - 1, Math.Max(reduction, 1)); } /* LMR */ if (EngineConstants.EnableLmr && reduction != 1) { score = -CalculateBestMove(cb, threadData, ply + 1, depth - reduction, -alpha - 1, -alpha, 0); } /* PVS */ if (EngineConstants.EnablePvs && score > alpha && movesPerformed > 1) { score = -CalculateBestMove(cb, threadData, ply + 1, depth - 1, -alpha - 1, -alpha, 0); } /* normal bounds */ if (score > alpha) { score = -CalculateBestMove(cb, threadData, ply + 1, depth - 1, -beta, -alpha, 0); } } cb.UndoMove(move); if (score > bestScore) { bestScore = score; bestMove = move; if (ply == 0 && IsRunning) { threadData.SetBestMove(cb, bestMove, alphaOrig, beta, bestScore, depth); } alpha = Math.Max(alpha, score); if (alpha >= beta) { if (Statistics.Enabled) { Statistics.FailHigh[Math.Min(movesPerformed - 1, Statistics.FailHigh.Length - 1)]++; } /* killer and history */ if (MoveUtil.IsQuiet(move) && cb.CheckingPieces == 0) { threadData.AddCounterMove(cb.ColorToMove, parentMove, move); threadData.AddKillerMove(move, ply); threadData.AddHhValue(cb.ColorToMove, move, depth); } phase += 10; break; } } if (MoveUtil.IsQuiet(move)) { threadData.AddBfValue(cb.ColorToMove, move, depth); } } phase++; } threadData.EndPly(); /* checkmate or stalemate */ if (movesPerformed == 0) { if (cb.CheckingPieces == 0) { if (Statistics.Enabled) { Statistics.StaleMateCount++; } return(EvalConstants.ScoreDraw); } if (Statistics.Enabled) { Statistics.MateCount++; } return(Util.ShortMin + ply); } if (EngineConstants.Assert) { Assert.IsTrue(bestMove != 0); } // set tt-flag var flag = TtUtil.FlagExact; if (bestScore >= beta) { flag = TtUtil.FlagLower; } else if (bestScore <= alphaOrig) { flag = TtUtil.FlagUpper; } if (IsRunning) { TtUtil.AddValue(cb.ZobristKey, bestScore, ply, depth, flag, bestMove); } Statistics.SetBestMove(cb, bestMove, ttMove, ttEntry, flag, counterMove, killer1Move, killer2Move); if (EngineConstants.TestTtValues) { SearchTestUtil.TestTtValues(score, bestScore, depth, bestMove, flag, ttEntry, ply); } return(bestScore); }
public bool IsValidMove(int move) { // check piece at from square var fromIndex = MoveUtil.GetFromIndex(move); var fromSquare = Util.PowerLookup[fromIndex]; if ((Pieces[ColorToMove][MoveUtil.GetSourcePieceIndex(move)] & fromSquare) == 0) { return(false); } // check piece at to square var toIndex = MoveUtil.GetToIndex(move); var toSquare = Util.PowerLookup[toIndex]; var attackedPieceIndex = MoveUtil.GetAttackedPieceIndex(move); if (attackedPieceIndex == 0) { if (PieceIndexes[toIndex] != Empty) { return(false); } } else { if ((Pieces[ColorToMoveInverse][attackedPieceIndex] & toSquare) == 0 && !MoveUtil.IsEpMove(move)) { return(false); } } // check if move is possible switch (MoveUtil.GetSourcePieceIndex(move)) { case Pawn: if (MoveUtil.IsEpMove(move)) { return(toIndex == EpIndex && IsLegalEpMove(fromIndex)); } else { if (ColorToMove == White) { if (fromIndex > toIndex) { return(false); } // 2-move if (toIndex - fromIndex == 16 && (AllPieces & Util.PowerLookup[fromIndex + 8]) != 0) { return(false); } } else { if (fromIndex < toIndex) { return(false); } // 2-move if (fromIndex - toIndex == 16 && (AllPieces & Util.PowerLookup[fromIndex - 8]) != 0) { return(false); } } } break; case Knight: break; case Bishop: // fall-through case Rook: // fall-through case Queen: if ((InBetween[fromIndex][toIndex] & AllPieces) != 0) { return(false); } break; case King: if (!MoveUtil.IsCastlingMove(move)) { return(IsLegalKingMove(move)); } var castlingIndexes = CastlingUtil.GetCastlingIndexes(this); while (castlingIndexes != 0) { if (toIndex == BitOperations.TrailingZeroCount(castlingIndexes)) { return(CastlingUtil.IsValidCastlingMove(this, fromIndex, toIndex)); } castlingIndexes &= castlingIndexes - 1; } return(false); } if ((fromSquare & PinnedPieces) != 0) { if ((PinnedMovement[fromIndex][KingIndex[ColorToMove]] & toSquare) == 0) { return(false); } } if (CheckingPieces == 0) { return(true); } if (attackedPieceIndex == 0) { return(IsLegalNonKingMove(move)); } if (BitOperations.PopCount((ulong)CheckingPieces) == 2) { return(false); } return((toSquare & CheckingPieces) != 0); }
private bool IsLegalNonKingMove(int move) { return(!CheckUtil.IsInCheck(KingIndex[ColorToMove], ColorToMove, Pieces[ColorToMoveInverse], AllPieces ^ Util.PowerLookup[MoveUtil.GetFromIndex(move)] ^ Util.PowerLookup[MoveUtil.GetToIndex(move)])); }
private bool IsLegalKingMove(int move) { return(!CheckUtil.IsInCheckIncludingKing(MoveUtil.GetToIndex(move), ColorToMove, Pieces[ColorToMoveInverse], AllPieces ^ Util.PowerLookup[MoveUtil.GetFromIndex(move)])); }
public void UndoMove(int move) { var fromIndex = MoveUtil.GetFromIndex(move); var toIndex = MoveUtil.GetToIndex(move); var toMask = 1L << toIndex; var fromToMask = (1L << fromIndex) ^ toMask; var sourcePieceIndex = MoveUtil.GetSourcePieceIndex(move); var attackedPieceIndex = MoveUtil.GetAttackedPieceIndex(move); PopHistoryValues(); // undo move Pieces[ColorToMoveInverse][All] ^= fromToMask; Pieces[ColorToMoveInverse][sourcePieceIndex] ^= fromToMask; PieceIndexes[fromIndex] = sourcePieceIndex; PsqtScore += EvalConstants.Psqt[sourcePieceIndex][ColorToMoveInverse][fromIndex] - EvalConstants.Psqt[sourcePieceIndex][ColorToMoveInverse][toIndex]; switch (sourcePieceIndex) { case Empty: // not necessary but provides a table-index break; case Pawn: PawnZobristKey ^= Zobrist.Piece[ColorToMoveInverse][Pawn][fromIndex]; if (MoveUtil.IsPromotion(move)) { Phase += EvalConstants.Phase[MoveUtil.GetMoveType(move)]; MaterialKey -= MaterialUtil.Values[ColorToMoveInverse][MoveUtil.GetMoveType(move)] - MaterialUtil.Values[ColorToMoveInverse][Pawn]; Pieces[ColorToMoveInverse][Pawn] ^= toMask; Pieces[ColorToMoveInverse][MoveUtil.GetMoveType(move)] ^= toMask; PsqtScore += EvalConstants.Psqt[Pawn][ColorToMoveInverse][toIndex] - EvalConstants.Psqt[MoveUtil.GetMoveType(move)][ColorToMoveInverse][toIndex]; } else { PawnZobristKey ^= Zobrist.Piece[ColorToMoveInverse][Pawn][toIndex]; } break; case King: if (MoveUtil.IsCastlingMove(move)) { CastlingUtil.UncastleRookUpdatePsqt(this, toIndex); } KingIndex[ColorToMoveInverse] = fromIndex; break; } // undo hit switch (attackedPieceIndex) { case Empty: break; case Pawn: if (MoveUtil.IsEpMove(move)) { PieceIndexes[toIndex] = Empty; toIndex += ColorFactor8[ColorToMove]; toMask = Util.PowerLookup[toIndex]; } PawnZobristKey ^= Zobrist.Piece[ColorToMove][Pawn][toIndex]; goto default; // fall-through default: PsqtScore += EvalConstants.Psqt[attackedPieceIndex][ColorToMove][toIndex]; Phase -= EvalConstants.Phase[attackedPieceIndex]; MaterialKey += MaterialUtil.Values[ColorToMove][attackedPieceIndex]; Pieces[ColorToMove][All] |= toMask; Pieces[ColorToMove][attackedPieceIndex] |= toMask; break; } PieceIndexes[toIndex] = attackedPieceIndex; AllPieces = Pieces[ColorToMove][All] | Pieces[ColorToMoveInverse][All]; EmptySpaces = ~AllPieces; ChangeSideToMove(); SetCheckingPinnedAndDiscoPieces(); if (EngineConstants.Assert) { ChessBoardTestUtil.TestValues(this); } }
public void DoMove(int move) { MoveCount++; var fromIndex = MoveUtil.GetFromIndex(move); var toIndex = MoveUtil.GetToIndex(move); var toMask = 1L << toIndex; var fromToMask = (1L << fromIndex) ^ toMask; var sourcePieceIndex = MoveUtil.GetSourcePieceIndex(move); var attackedPieceIndex = MoveUtil.GetAttackedPieceIndex(move); if (EngineConstants.Assert) { Assert.IsTrue(move != 0); Assert.IsTrue(attackedPieceIndex != King); Assert.IsTrue(attackedPieceIndex == 0 || (Util.PowerLookup[toIndex] & Pieces[ColorToMove][All]) == 0); Assert.IsTrue(IsValidMove(move)); } PushHistoryValues(); ZobristKey ^= Zobrist.Piece[ColorToMove][sourcePieceIndex][fromIndex] ^ Zobrist.Piece[ColorToMove][sourcePieceIndex][toIndex] ^ Zobrist.SideToMove; if (EpIndex != 0) { ZobristKey ^= Zobrist.EpIndex[EpIndex]; EpIndex = 0; } Pieces[ColorToMove][All] ^= fromToMask; Pieces[ColorToMove][sourcePieceIndex] ^= fromToMask; PieceIndexes[fromIndex] = Empty; PieceIndexes[toIndex] = sourcePieceIndex; PsqtScore += EvalConstants.Psqt[sourcePieceIndex][ColorToMove][toIndex] - EvalConstants.Psqt[sourcePieceIndex][ColorToMove][fromIndex]; switch (sourcePieceIndex) { case Pawn: PawnZobristKey ^= Zobrist.Piece[ColorToMove][Pawn][fromIndex]; if (MoveUtil.IsPromotion(move)) { Phase -= EvalConstants.Phase[MoveUtil.GetMoveType(move)]; MaterialKey += MaterialUtil.Values[ColorToMove][MoveUtil.GetMoveType(move)] - MaterialUtil.Values[ColorToMove][Pawn]; Pieces[ColorToMove][Pawn] ^= toMask; Pieces[ColorToMove][MoveUtil.GetMoveType(move)] |= toMask; PieceIndexes[toIndex] = MoveUtil.GetMoveType(move); ZobristKey ^= Zobrist.Piece[ColorToMove][Pawn][toIndex] ^ Zobrist.Piece[ColorToMove][MoveUtil.GetMoveType(move)][toIndex]; PsqtScore += EvalConstants.Psqt[MoveUtil.GetMoveType(move)][ColorToMove][toIndex] - EvalConstants.Psqt[Pawn][ColorToMove][toIndex]; } else { PawnZobristKey ^= Zobrist.Piece[ColorToMove][Pawn][toIndex]; // 2-move if (InBetween[fromIndex][toIndex] != 0) { if ((StaticMoves.PawnAttacks[ColorToMove][ BitOperations.TrailingZeroCount(InBetween[fromIndex][toIndex])] & Pieces[ColorToMoveInverse][Pawn]) != 0) { EpIndex = BitOperations.TrailingZeroCount(InBetween[fromIndex][toIndex]); ZobristKey ^= Zobrist.EpIndex[EpIndex]; } } } break; case Rook: if (CastlingRights != 0) { ZobristKey ^= Zobrist.Castling[CastlingRights]; CastlingRights = CastlingUtil.GetRookMovedOrAttackedCastlingRights(CastlingRights, fromIndex); ZobristKey ^= Zobrist.Castling[CastlingRights]; } break; case King: KingIndex[ColorToMove] = toIndex; if (CastlingRights != 0) { if (MoveUtil.IsCastlingMove(move)) { CastlingUtil.CastleRookUpdateKeyAndPsqt(this, toIndex); } ZobristKey ^= Zobrist.Castling[CastlingRights]; CastlingRights = CastlingUtil.GetKingMovedCastlingRights(CastlingRights, fromIndex); ZobristKey ^= Zobrist.Castling[CastlingRights]; } break; } // piece hit? switch (attackedPieceIndex) { case Empty: break; case Pawn: if (MoveUtil.IsEpMove(move)) { toIndex += ColorFactor8[ColorToMoveInverse]; toMask = Util.PowerLookup[toIndex]; PieceIndexes[toIndex] = Empty; } PawnZobristKey ^= Zobrist.Piece[ColorToMoveInverse][Pawn][toIndex]; PsqtScore -= EvalConstants.Psqt[Pawn][ColorToMoveInverse][toIndex]; Pieces[ColorToMoveInverse][All] ^= toMask; Pieces[ColorToMoveInverse][Pawn] ^= toMask; ZobristKey ^= Zobrist.Piece[ColorToMoveInverse][Pawn][toIndex]; MaterialKey -= MaterialUtil.Values[ColorToMoveInverse][Pawn]; break; case Rook: if (CastlingRights != 0) { ZobristKey ^= Zobrist.Castling[CastlingRights]; CastlingRights = CastlingUtil.GetRookMovedOrAttackedCastlingRights(CastlingRights, toIndex); ZobristKey ^= Zobrist.Castling[CastlingRights]; } goto default; // fall-through default: Phase += EvalConstants.Phase[attackedPieceIndex]; PsqtScore -= EvalConstants.Psqt[attackedPieceIndex][ColorToMoveInverse][toIndex]; Pieces[ColorToMoveInverse][All] ^= toMask; Pieces[ColorToMoveInverse][attackedPieceIndex] ^= toMask; ZobristKey ^= Zobrist.Piece[ColorToMoveInverse][attackedPieceIndex][toIndex]; MaterialKey -= MaterialUtil.Values[ColorToMoveInverse][attackedPieceIndex]; break; } AllPieces = Pieces[ColorToMove][All] | Pieces[ColorToMoveInverse][All]; EmptySpaces = ~AllPieces; ChangeSideToMove(); SetCheckingPinnedAndDiscoPieces(); if (EngineConstants.Assert) { ChessBoardTestUtil.TestValues(this); } }