public ValidateMoveResult CanMove(ChessBoard board, string move, PlayerTypeEnum player,
            string lastMoveNotation)
        {
            Tuple<string, string> breakdown = NotationHelper.Breakdown(move);
            string source = breakdown.Item1;
            string destination = breakdown.Item2;

            bool playerInCheck = IsInCheck(player, board, lastMoveNotation).Item1;
            ValidateMoveResult canMoveResult = CanMove(board, source, destination, player, lastMoveNotation, playerInCheck);

            if (canMoveResult.Success)
            {
                // pawn to queen swap
                ChessPiece sourceBeforeSimulate = board.Get(source);
                bool pawnSwap =
                    sourceBeforeSimulate != null &&
                    sourceBeforeSimulate.Type == PieceTypeEnum.Pawn &&
                    (sourceBeforeSimulate.Direction == -1 ? (int.Parse(destination[1].ToString()) == 8) : (int.Parse(destination[1].ToString()) == 1));

                string swap = pawnSwap ? destination : null;
                canMoveResult.Swap = swap;

                ChessBoard boardAfterMove = board.SimulateMoves(move, canMoveResult.TakenPiece, swap);

                // did player move into check?
                var movedIntoCheckResult = IsInCheck(player, boardAfterMove, lastMoveNotation);
                if (movedIntoCheckResult.Item1)
                {
                    canMoveResult.Success = false;
                    return canMoveResult.SetMessage("cannot move into check.");
                }

                // did player put opponent in check?
                var isOpponentInCheckResult = IsInCheck(GetOppositePlayer(player), boardAfterMove, lastMoveNotation);
                bool isOpponentInCheck = isOpponentInCheckResult.Item1;
                canMoveResult.MoveType = isOpponentInCheck ? MoveTypeEnum.Check : MoveTypeEnum.Normal;

                // if player checked opponent, can opponent get out of check? if not, check mate
                if (isOpponentInCheck)
                {
                    string opponentKingNotation = boardAfterMove.GetKing(GetOppositePlayer(player));
                    Tuple<int, int> opponentKingTuple = NotationHelper.Translate(opponentKingNotation);

                    // can checked king move out of check?
                    bool canMoveOutOfCheck = CanMoveOutOfCheck(player, lastMoveNotation, boardAfterMove, opponentKingNotation, opponentKingTuple);

                    // can opponent kill piece that put king in check? is still in check afterwards?
                    bool canTakedownAttacker = false;
                    if (!canMoveOutOfCheck)
                        canTakedownAttacker = CanTakedownAttackers(player, lastMoveNotation, boardAfterMove, isOpponentInCheckResult.Item2);

                    // can block path of piece that put king in check? (brute force method)
                    bool canBlockPathOfAttackers = false;
                    if (!canMoveOutOfCheck && !canTakedownAttacker)
                        canBlockPathOfAttackers = CanMoveIntoAttackersPath(player, lastMoveNotation, boardAfterMove, isOpponentInCheckResult.Item2, opponentKingTuple);

                    if (!canMoveOutOfCheck && !canTakedownAttacker && !canBlockPathOfAttackers)
                        canMoveResult.MoveType = MoveTypeEnum.Checkmate;
                }
            }

            return canMoveResult;
        }