static DifferBy MoveDifferFromAllOthersBy(List <MGMove> moves, MGMove thisMove) { bool differByRank = true; bool differByFile = true; foreach (MGMove move in moves) { if (move != thisMove) { if (move.FromSquare.File == thisMove.FromSquare.File) { differByFile = false; } if (move.FromSquare.Rank == thisMove.FromSquare.Rank) { differByRank = false; } } } if (differByFile) { return(DifferBy.File); // takes precedence } else if (differByRank) { return(DifferBy.Rank); } else { return(DifferBy.RankFileComo); } }
/// <summary> /// Copy constructor. /// </summary> /// <param name="from"></param> /// <param name="to"></param> /// <param name="flags"></param> public MGMove(MGMove otherMove, bool flip) { if (!flip) { FromSquareIndex = otherMove.FromSquareIndex; ToSquareIndex = otherMove.ToSquareIndex; Flags = otherMove.Flags; } else { FromSquareIndex = FlipSquare(otherMove.FromSquareIndex); ToSquareIndex = FlipSquare(otherMove.ToSquareIndex); Flags = otherMove.Flags ^ MGChessMoveFlags.BlackToMove; } // Obviate requirement of definite assignment to all fields Unsafe.SkipInit <short>(out FromAndToCombined); }
/// <summary> /// Attempts to parse a move string in coordinate or long algebraic format. /// </summary> /// <param name="pos"></param> /// <param name="moveStr"></param> /// <param name="move"></param> /// <returns></returns> private static bool TryParseMoveCoordinateOrAlgebraic(MGPosition pos, string moveStr, out MGMove move) { moveStr = moveStr.ToLower(); // Sometimes promotions to Knight use the "k" instead of expected "n" if (moveStr.EndsWith("k")) { moveStr = moveStr.Substring(0, moveStr.Length - 1) + "n"; } MGMoveList moves = new MGMoveList(); MGMoveGen.GenerateMoves(in pos, moves); foreach (MGMove moveTry in moves.MovesArray) { // Accept moves in any of multiple formats, including Chess 960 (for castling variation) if (String.Equals(moveTry.MoveStr(MGMoveNotationStyle.LC0Coordinate), moveStr, StringComparison.OrdinalIgnoreCase) || String.Equals(moveTry.MoveStr(MGMoveNotationStyle.LC0Coordinate960Format), moveStr, StringComparison.OrdinalIgnoreCase) || String.Equals(moveTry.MoveStr(MGMoveNotationStyle.LongAlgebraic), moveStr, StringComparison.OrdinalIgnoreCase)) { move = moveTry; return(true); } } move = default; return(false); }
/// <summary> /// Worker method to make the move (but not switch sides). /// </summary> /// <param name="M"></param> void DoMakeMove(MGMove M) { Debug.Assert(M.Piece != MGPositionConstants.MCChessPositionPieceEnum.None); BitBoard O, To; To = 1UL << M.ToSquareIndex; O = ~((1UL << M.FromSquareIndex) | To); ulong nFromSquare = M.FromSquareIndex; ulong nToSquare = M.ToSquareIndex; // Increment ply count MoveNumber++; // Update Rule 50 count (upon any pawn move or capture) if (M.Capture || (byte)M.Piece == MGPositionConstants.WPAWN || (byte)M.Piece == MGPositionConstants.BPAWN) { Rule50Count = 0; } else { Rule50Count++; } // clear any enpassant squares BitBoard EnPassant = A & B & (~C); if (EnPassant != 0) { A &= ~EnPassant; B &= ~EnPassant; C &= ~EnPassant; D &= ~EnPassant; #if MG_USE_HASH ulong nEPSquare; nEPSquare = MGMoveGenFillFunctions.GetSquareIndex(EnPassant); if (BlackToMove) { HK ^= MGZobristKeySet.zkPieceOnSquare[MGChessPositionConstants.WENPASSANT][nEPSquare]; // Remove EP from nEPSquare } else { HK ^= MGZobristKeySet.zkPieceOnSquare[MGChessPositionConstants.BENPASSANT][nEPSquare]; // Remove EP from nEPSquare } #endif } // APPLY CASTLING MOVES: // we use magic XOR-tricks to do the job ! : // The following is for O-O: // // K..R .RK. // A: 1000 ^ 1010 = 0010 // B: 1000 ^ 1010 = 0010 // C: 1001 ^ 1111 = 0110 // For Black: // D: 1001 ^ 1111 = 0110 // For White: // D &= 0xfffffffffffffff0 (ie clear colour of affected squares from K to KR) // The following is for O-O-O: // // R... K... ..KR .... // A: 0000 1000 ^ 0010 1000 (0x28) = 0010 0000 // B: 0000 1000 ^ 0010 1000 (0x28) = 0010 0000 // C: 1000 1000 ^ 1011 1000 (0xB8) = 0011 0000 // For Black: // D: 1000 1000 ^ 1011 1000 (0xB8) = 0011 0000 // For White: // D &= 0xffffffffffffff07 (ie clear colour of affected squares from QR to K) if ((byte)M.Piece == MGPositionConstants.BKING) { if (M.CastleShort) { A ^= 0x0a00000000000000; B ^= 0x0a00000000000000; C ^= 0x0f00000000000000; D ^= 0x0f00000000000000; #if MG_USE_HASH HK ^= MGZobristKeySet.zkDoBlackCastle; if (BlackCanCastleLong) { HK ^= MGZobristKeySet.zkBlackCanCastleLong; // conditionally flip black castling long } #endif Flags |= FlagsEnum.BlackDidCastle; // bf = bf &= ~BitFields.DrawerPresenceSw; //attackType &= ~ AttackType.Fire; Flags &= ~FlagsEnum.BlackCanCastle; Flags &= ~FlagsEnum.BlackCanCastleLong; return; } else if (M.CastleLong) { A ^= 0x2800000000000000; B ^= 0x2800000000000000; C ^= 0xb800000000000000; D ^= 0xb800000000000000; #if MG_USE_HASH HK ^= MGZobristKeySet.zkDoBlackCastleLong; if (BlackCanCastle) { HK ^= MGZobristKeySet.zkBlackCanCastle; // conditionally flip black castling } #endif BlackDidCastleLong = true; BlackCanCastle = false; BlackCanCastleLong = false; return; } else { // ordinary king move if (BlackCanCastle || BlackCanCastleLong) { // Black could have castled, but chose to move the King in a non-castling move #if MG_USE_HASH if (BlackCanCastle) { HK ^= MGZobristKeySet.zkBlackCanCastle; // conditionally flip black castling } if (BlackCanCastleLong) { HK ^= MGZobristKeySet.zkBlackCanCastleLong; // conditionally flip black castling long } #endif BlackForfeitedCastle = true; BlackForfeitedCastleLong = true; BlackCanCastle = false; BlackCanCastleLong = false; } } } else if ((byte)M.Piece == MGPositionConstants.WKING) { // White if (M.CastleShort) { A ^= 0x000000000000000a; B ^= 0x000000000000000a; C ^= 0x000000000000000f; D &= 0xfffffffffffffff0; // clear colour of e1,f1,g1,h1 (make white) #if MG_USE_HASH HK ^= MGZobristKeySet.zkDoWhiteCastle; if (WhiteCanCastleLong) { HK ^= MGZobristKeySet.zkWhiteCanCastleLong; // conditionally flip white castling long } #endif WhiteDidCastle = true; WhiteCanCastle = false; WhiteCanCastleLong = false; return; } if (M.CastleLong) { A ^= 0x0000000000000028; B ^= 0x0000000000000028; C ^= 0x00000000000000b8; D &= 0xffffffffffffff07; // clear colour of a1,b1,c1,d1,e1 (make white) #if MG_USE_HASH HK ^= MGZobristKeySet.zkDoWhiteCastleLong; if (WhiteCanCastle) { HK ^= MGZobristKeySet.zkWhiteCanCastle; // conditionally flip white castling } #endif WhiteDidCastleLong = true; WhiteCanCastle = false; WhiteCanCastleLong = false; return; } else { // ordinary king move if (WhiteCanCastle) { #if MG_USE_HASH HK ^= MGZobristKeySet.zkWhiteCanCastle; // flip white castling #endif WhiteForfeitedCastle = true; WhiteCanCastle = false; } if (WhiteCanCastleLong) { #if MG_USE_HASH HK ^= MGZobristKeySet.zkWhiteCanCastleLong; // flip white castling long #endif WhiteForfeitedCastleLong = true; WhiteCanCastleLong = false; } } } // LOOK FOR FORFEITED CASTLING RIGHTS DUE to ROOK MOVES: else if ((byte)M.Piece == MGPositionConstants.BROOK) { // if((1LL<<nFromSquare) & BLACKKRPOS) if (nFromSquare == 56) { // Black moved K-side Rook and forfeits right to castle K-side if (BlackCanCastle) { BlackForfeitedCastle = true; #if MG_USE_HASH HK ^= MGZobristKeySet.zkBlackCanCastle; // flip black castling #endif BlackCanCastle = false; } } //else if ((1LL<<nFromSquare) & BLACKQRPOS) else if (nFromSquare == 63) { // Black moved the QS Rook and forfeits right to castle Q-side if (BlackCanCastleLong) { BlackForfeitedCastleLong = true; #if MG_USE_HASH HK ^= MGZobristKeySet.zkBlackCanCastleLong; // flip black castling long #endif BlackCanCastleLong = false; } } } else if ((byte)M.Piece == MGPositionConstants.WROOK) { //if((1LL<<nFromSquare) & WHITEKRPOS) if (nFromSquare == 0) { // White moved K-side Rook and forfeits right to castle K-side if (WhiteCanCastle) { WhiteForfeitedCastle = true; #if MG_USE_HASH HK ^= MGZobristKeySet.zkWhiteCanCastle; // flip white castling BROKEN !!! #endif WhiteCanCastle = false; } } // else if((1LL<<nFromSquare) & WHITEQRPOS) else if (nFromSquare == 7) { // White moved the QSide Rook and forfeits right to castle Q-side if (WhiteCanCastleLong) { WhiteForfeitedCastleLong = true; #if MG_USE_HASH HK ^= MGZobristKeySet.zkWhiteCanCastleLong; // flip white castling long #endif WhiteCanCastleLong = false; } } } // Ordinary Captures //// if (M.Capture) { ulong capturedpiece; #if NOWAY //defined (_WIN64) && defined (_USE_BITTEST_INSTRUCTION) const long long d = D; const long long c = C; const long long b = B; const long long a = A; // using BitTest Intrinsic: capturedpiece = _bittest64(&d, nToSquare) << 3 | _bittest64(&c, nToSquare) << 2 | _bittest64(&b, nToSquare) << 1 | _bittest64(&a, nToSquare); #else // find out which piece has been captured: // Branchless version: BitBoard bbCap = ((D & To) >> (int)nToSquare) << 3 | ((C & To) >> (int)nToSquare) << 2 | ((B & To) >> (int)nToSquare) << 1 | ((A & To) >> (int)nToSquare); capturedpiece = (ulong)bbCap; #endif #if MG_USE_HASH // Update Hash HK ^= MGZobristKeySet.zkPieceOnSquare[capturedpiece][nToSquare]; // Remove captured Piece #endif } // Render "ordinary" moves: A &= O; B &= O; C &= O; D &= O; // Populate new square (Branchless method): A |= (BitBoard)(((ulong)M.Piece & 1) << M.ToSquareIndex); B |= (BitBoard)((((ulong)M.Piece & 2) >> 1) << M.ToSquareIndex); C |= (BitBoard)((((ulong)M.Piece & 4) >> 2) << M.ToSquareIndex); D |= (BitBoard)((((ulong)M.Piece & 8) >> 3) << M.ToSquareIndex); #if MG_USE_HASH // Update Hash HK ^= MGZobristKeySet.zkPieceOnSquare[(int)M.Piece][nFromSquare]; // Remove piece at From square HK ^= MGZobristKeySet.zkPieceOnSquare[(int)M.Piece][nToSquare]; // Place piece at To Square #endif // Promotions - Change the piece: if (M.PromoteBishop) { A &= ~To; B |= To; #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BPAWN : MGChessPositionConstants.WPAWN][nToSquare]; // Remove pawn at To square HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BBISHOP : MGChessPositionConstants.WBISHOP][nToSquare]; // place Bishop at To square #endif M.Piece = M.BlackToMove ? MGPositionConstants.MCChessPositionPieceEnum.BlackBishop : MGPositionConstants.MCChessPositionPieceEnum.WhiteBishop; // return; } else if (M.PromoteKnight) { C |= To; #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BPAWN : MGChessPositionConstants.WPAWN][nToSquare]; // Remove pawn at To square HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BKNIGHT : MGChessPositionConstants.WKNIGHT][nToSquare]; // place Knight at To square #endif M.Piece = M.BlackToMove ? MGPositionConstants.MCChessPositionPieceEnum.BlackKnight : MGPositionConstants.MCChessPositionPieceEnum.WhiteKnight; // return; } else if (M.PromoteRook) { A &= ~To; C |= To; #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BPAWN : MGChessPositionConstants.WPAWN][nToSquare]; // Remove pawn at To square HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BROOK : MGChessPositionConstants.WROOK][nToSquare]; // place Rook at To square #endif M.Piece = M.BlackToMove ? MGPositionConstants.MCChessPositionPieceEnum.BlackRook : MGPositionConstants.MCChessPositionPieceEnum.WhiteRook; // return; } else if (M.PromoteQueen) { A &= ~To; B |= To; C |= To; #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BPAWN : MGChessPositionConstants.WPAWN][nToSquare]; // Remove pawn at To square HK ^= MGZobristKeySet.zkPieceOnSquare[M.BlackToMove ? MGChessPositionConstants.BQUEEN : MGChessPositionConstants.WQUEEN][nToSquare]; // place Queen at To square #endif M.Piece = M.BlackToMove ? MGPositionConstants.MCChessPositionPieceEnum.BlackQueen : MGPositionConstants.MCChessPositionPieceEnum.WhiteQueen; // return; } // For Double-Pawn Moves, set EP square: //// else if (M.DoublePawnMove) { // Set EnPassant Square if (M.BlackToMove) { To <<= 8; A |= To; B |= To; C &= ~To; D |= To; #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[MGChessPositionConstants.BENPASSANT][nToSquare + 8]; // Place Black EP at (To+8) #endif } else { To >>= 8; A |= To; B |= To; C &= ~To; D &= ~To; #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[MGChessPositionConstants.WENPASSANT][nToSquare - 8]; // Place White EP at (To-8) #endif } // return; } // En-Passant Captures //// else if (M.EnPassantCapture) { // remove the actual pawn (it is different to the capture square) if (M.BlackToMove) { To <<= 8; A &= ~To; // clear the pawn's square B &= ~To; C &= ~To; D &= ~To; // material -= 100; // perft doesn't care #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[MGChessPositionConstants.WPAWN][nToSquare + 8]; // Remove WHITE Pawn at (To+8) #endif } else { To >>= 8; A &= ~To; B &= ~To; C &= ~To; D &= ~To; // material += 100; // perft doesn't care #if MG_USE_HASH HK ^= MGZobristKeySet.zkPieceOnSquare[MGChessPositionConstants.BPAWN][nToSquare - 8]; // Remove BLACK Pawn at (To-8) #endif } } }
/// <summary> /// Modifies this position to reflect a specified move having been made. /// </summary> /// <param name="M"></param> public void MakeMove(MGMove M) { DoMakeMove(M); SwitchSides(); }
public static string AlgebraicMoveString(MGMove mgMove, Position pos) { string ret = null; bool white = pos.MiscInfo.SideToMove == SideType.White; MGPosition curPosition = MGChessPositionConverter.MGChessPositionFromFEN(pos.FEN); // Generate moves MGMoveList moves = new MGMoveList(); MGMoveGen.GenerateMoves(in curPosition, moves); //if (white) mgMove = new MGMove(mgMove, true); MGPosition newPosition = MGChessPositionConverter.MGChessPositionFromFEN(pos.FEN); newPosition.MakeMove(mgMove); bool isCheck = MGMoveGen.IsInCheck(newPosition, white); if (mgMove.CastleShort) { ret = "O-O"; } else if (mgMove.CastleLong) { ret = "O-O-O"; } else { Square fromSquare = mgMove.FromSquare; Square toSquare = mgMove.ToSquare; Piece fromType = pos.PieceOnSquare(fromSquare); Piece toPiece = pos.PieceOnSquare(toSquare); if (fromType.Type == PieceType.Pawn) { string promoteChar = ""; if (mgMove.PromoteQueen) { promoteChar = "Q"; } if (mgMove.PromoteBishop) { promoteChar = "B"; } if (mgMove.PromoteRook) { promoteChar = "R"; } if (mgMove.PromoteKnight) { promoteChar = "N"; } if (mgMove.EnPassantCapture) { int newRank = white ? 6 : 3; char newFile = char.ToLower(toSquare.FileChar); ret = fromSquare.ToString().Substring(0, 1).ToLower() + "x" + newFile + newRank; } else if (toPiece.Type == PieceType.None) { ret = toSquare.ToString().ToLower() + promoteChar; } else { ret = fromSquare.ToString().Substring(0, 1).ToLower() + "x" + toSquare.ToString().ToLower() + promoteChar; } } else { string captureChar = toPiece.Type == PieceType.None ? "" : "x"; List <MGMove> matchingCaptures = MovesByPieceTypeThatTakeOnSquare(mgMove.Piece, mgMove.ToSquareIndex, moves); if (matchingCaptures.Count == 1) { ret = char.ToUpper(fromType.Char) + captureChar + toSquare.ToString().ToLower(); } else { // Disambiguate DifferBy differBy = MoveDifferFromAllOthersBy(matchingCaptures, mgMove); string fileChar = fromSquare.FileChar.ToString().ToLower(); string rankChar = fromSquare.RankChar.ToString().ToLower(); if (differBy == DifferBy.File) { ret = char.ToUpper(fromType.Char) + fileChar + captureChar + toSquare.ToString().ToLower(); } else if (differBy == DifferBy.Rank) { ret = char.ToUpper(fromType.Char) + rankChar + captureChar + toSquare.ToString().ToLower(); } else { ret = char.ToUpper(fromType.Char) + fileChar + rankChar + captureChar + toSquare.ToString().ToLower(); } } } } if (isCheck) { return(ret + "+"); } else { return(ret); } }
/// <summary> /// Attempts to parse a move string in coordinate or long algebraic format. /// </summary> /// <param name="pos"></param> /// <param name="moveStr"></param> /// <param name="move"></param> /// <returns></returns> private static bool TryParseMoveCoordinateOrAlgebraic(MGPosition pos, string moveStr, out MGMove move) { moveStr = moveStr.ToLower(); // Sometimes promotions to Knight use the "k" instead of expected "n" if (moveStr.EndsWith("k")) { moveStr = moveStr.Substring(0, moveStr.Length - 1) + "n"; } MGMoveList moves = new MGMoveList(); MGMoveGen.GenerateMoves(in pos, moves); foreach (MGMove moveTry in moves.MovesArray) { if (moveTry.MoveStr(MGMoveNotationStyle.LC0Coordinate).ToLower() == moveStr || (moveTry.MoveStr(MGMoveNotationStyle.LongAlgebraic).ToLower() == moveStr)) { move = moveTry; return(true); } } move = default; return(false); }