Esempio n. 1
0
        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));
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
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)]));
 }
Esempio n. 5
0
 private bool IsLegalKingMove(int move)
 {
     return(!CheckUtil.IsInCheckIncludingKing(MoveUtil.GetToIndex(move), ColorToMove, Pieces[ColorToMoveInverse],
                                              AllPieces ^ Util.PowerLookup[MoveUtil.GetFromIndex(move)]));
 }
Esempio n. 6
0
        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);
            }
        }
Esempio n. 7
0
        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);
            }
        }