/// <summary> /// Returns the move specified by the given information. /// </summary> /// <param name="position">The position the move is to be played on.</param> /// <param name="from">The initial square of the move.</param> /// <param name="to">The final square of the move.</param> /// <returns>The move specified by the given information.</returns> private Int32 CreateMove(Position position, Int32 from, Int32 to) { foreach (Int32 move in position.LegalMoves()) { if (from == Move.From(move) && to == Move.To(move)) { Int32 special = Move.Special(move); if (Move.IsPromotion(move)) { switch (SelectionBox.Show("What piece would you like to promote to?", "Queen", "Rook", "Bishop", "Knight")) { case "Queen": special = position.SideToMove | Piece.Queen; break; case "Rook": special = position.SideToMove | Piece.Rook; break; case "Bishop": special = position.SideToMove | Piece.Bishop; break; case "Knight": special = position.SideToMove | Piece.Knight; break; } } return(Move.Create(position, from, to, special)); } } return(Move.Invalid); }
/// <summary> /// Returns the estimated material exchange value of the given move on the /// given position as determined by static analysis. /// </summary> /// <param name="position">The position the move is to be played on.</param> /// <param name="move">The move to evaluate.</param> /// <returns>The estimated material exchange value of the move.</returns> private static Int32 EvaluateStaticExchange(Position position, Int32 move) { Int32 from = Move.From(move); Int32 to = Move.To(move); Int32 piece = Move.Piece(move); Int32 capture = Move.Capture(move); position.Bitboard[piece] ^= 1UL << from; position.OccupiedBitboard ^= 1UL << from; position.Square[to] = piece; Int32 value = 0; if (Move.IsPromotion(move)) { Int32 promotion = Move.Special(move); position.Square[to] = promotion; value += PieceValue[promotion] - PieceValue[Piece.Pawn]; } value += PieceValue[capture] - EvaluateStaticExchange(position, 1 - position.SideToMove, to); position.Bitboard[piece] ^= 1UL << from; position.OccupiedBitboard ^= 1UL << from; position.Square[to] = capture; return(value); }
/// <summary> /// Draws an arrow for the given move with the given pen. Optionally draws /// a label if given. /// </summary> /// <param name="g">The graphics surface to draw on.</param> /// <param name="pen">The pen for drawing the arrow.</param> /// <param name="move">The move to draw an arrow for.</param> /// <param name="labelBrush">The brush for drawing the label.</param> /// <param name="label">The label to draw at the move's from square.</param> public static void DrawArrow(Graphics g, Pen pen, Int32 move, Brush labelBrush = null, String label = "") { if (!DrawArrows) { return; } Int32 from = Move.From(move); Int32 to = Move.To(move); Point initial = new Point( RotateIfNeeded(Position.File(from)) * SquareWidth + SquareWidth / 2, RotateIfNeeded(Position.Rank(from)) * SquareWidth + SquareWidth / 2); Point final = new Point( RotateIfNeeded(Position.File(to)) * SquareWidth + SquareWidth / 2, RotateIfNeeded(Position.Rank(to)) * SquareWidth + SquareWidth / 2); pen.StartCap = LineCap.NoAnchor; pen.EndCap = LineCap.ArrowAnchor; g.DrawLine(pen, initial, final); if (labelBrush != null && !String.IsNullOrEmpty(label)) { Point delta = new Point(final.X - initial.X, final.Y - initial.Y); Single mag = (Single)Math.Sqrt(delta.X * delta.X + delta.Y * delta.Y); Single factor = delta.Y > 0 ? LabelPullMagnitude : -LabelPullMagnitude; PointF pull = new PointF(factor * delta.Y / mag, factor * -delta.X / mag); PointF mid = new PointF((initial.X + final.X) / 2 + pull.X, (initial.Y + final.Y) / 2 + pull.Y); g.DrawString(label, LabelFont, labelBrush, mid, LabelFormat); } }
/// <summary> /// Unmakes the given move on the visual position. /// </summary> /// <param name="move">The move to unmake.</param> public static void Unmake(Int32 move) { Int32 from = Move.From(move); Int32 to = Move.To(move); Point initial = new Point(Position.File(to) * SquareWidth, Position.Rank(to) * SquareWidth); Point final = new Point(Position.File(from) * SquareWidth, Position.Rank(from) * SquareWidth); // Remove captured pieces. lock (PiecesLock) _pieces.Add(new VisualPiece( Move.Capture(move), Position.File(to) * SquareWidth, Position.Rank(to) * SquareWidth)); // Perform special moves. switch (Move.Special(move) & Piece.Mask) { case Piece.King: Point rookInitial = new Point((Position.File(to) / 2 + 2) * SquareWidth, Position.Rank(to) * SquareWidth); Point rookFinal = new Point(7 * (Position.File(to) - 2) / 4 * SquareWidth, Position.Rank(to) * SquareWidth); Animate(move, rookInitial, rookFinal); break; case Piece.Pawn: lock (PiecesLock) _pieces.Add(new VisualPiece( Move.Special(move), Position.File(to) * SquareWidth, Position.Rank(from) * SquareWidth)); break; } Animate(move, initial, final); }
/// <summary> /// Returns whether the given move puts the opponent in check. /// </summary> /// <param name="move">The move to test for check.</param> /// <returns>Whether the given move puts the opponent in check.</returns> public Boolean CausesCheck(Int32 move) { UInt64 fromBitboard = 1UL << Move.From(move); UInt64 toBitboard = 1UL << Move.To(move); Int32 piece = Move.Piece(move); Int32 special = Move.Special(move); UInt64 occupiedBitboardCopy = OccupiedBitboard; Boolean value = false; switch (special & Piece.Mask) { // Consider normal move. case Piece.Empty: Bitboard[piece] ^= fromBitboard | toBitboard; OccupiedBitboard ^= fromBitboard; OccupiedBitboard |= toBitboard; value = InCheck(1 - SideToMove); Bitboard[piece] ^= fromBitboard | toBitboard; OccupiedBitboard = occupiedBitboardCopy; break; // Consider castling. case Piece.King: UInt64 rookToBitboard = 1UL << ((toBitboard < fromBitboard ? 3 : 5) + Rank(Move.To(move)) * 8); Bitboard[SideToMove | Piece.Rook] ^= rookToBitboard; OccupiedBitboard ^= fromBitboard; value = InCheck(1 - SideToMove); Bitboard[SideToMove | Piece.Rook] ^= rookToBitboard; OccupiedBitboard = occupiedBitboardCopy; break; // Consider en passant. case Piece.Pawn: UInt64 enPassantPawnBitboard = Move.Pawn(EnPassantSquare, 1 - SideToMove); Bitboard[piece] ^= fromBitboard | toBitboard; OccupiedBitboard ^= fromBitboard | toBitboard | enPassantPawnBitboard; value = InCheck(1 - SideToMove); Bitboard[piece] ^= fromBitboard | toBitboard; OccupiedBitboard = occupiedBitboardCopy; break; // Consider pawn promotion. default: Bitboard[SideToMove | Piece.Pawn] ^= fromBitboard; Bitboard[special] ^= toBitboard; OccupiedBitboard ^= fromBitboard; OccupiedBitboard |= toBitboard; value = InCheck(1 - SideToMove); Bitboard[SideToMove | Piece.Pawn] ^= fromBitboard; Bitboard[special] ^= toBitboard; OccupiedBitboard = occupiedBitboardCopy; break; } return(value); }
/// <summary> /// Makes the given move on the visual position. /// </summary> /// <param name="move">The move to make.</param> public static void Make(Int32 move) { Int32 from = Move.From(move); Int32 to = Move.To(move); Point initial = new Point(Position.File(from) * SquareWidth, Position.Rank(from) * SquareWidth); Point final = new Point(Position.File(to) * SquareWidth, Position.Rank(to) * SquareWidth); // Remove captured pieces. lock (PiecesLock) for (Int32 i = 0; i < _pieces.Count; i++) { if (_pieces[i].IsAt(final)) { _pieces.RemoveAt(i); break; } } // Perform special moves. switch (Move.Special(move) & Piece.Mask) { case Piece.King: Point rookInitial = new Point(7 * (Position.File(to) - 2) / 4 * SquareWidth, Position.Rank(to) * SquareWidth); Point rookFinal = new Point((Position.File(to) / 2 + 2) * SquareWidth, Position.Rank(to) * SquareWidth); Animate(move, rookInitial, rookFinal); break; case Piece.Pawn: Point enPassant = new Point(Position.File(to) * SquareWidth, Position.Rank(from) * SquareWidth); lock (PiecesLock) for (Int32 i = 0; i < _pieces.Count; i++) { if (_pieces[i].IsAt(enPassant)) { _pieces.RemoveAt(i); break; } } break; } Animate(move, initial, final); }
/// <summary> /// Unmakes the given move from the position. /// </summary> /// <param name="move">The move to unmake.</param> public void Unmake(Int32 move) { Int32 from = Move.From(move); Int32 to = Move.To(move); Int32 piece = Move.Piece(move); Int32 capture = Move.Capture(move); Int32 special = Move.Special(move); // Rewind core board state. SideToMove = 1 - SideToMove; Square[from] = piece; Square[to] = capture; Bitboard[piece] ^= (1UL << from) | (1UL << to); Bitboard[SideToMove] ^= (1UL << from) | (1UL << to); OccupiedBitboard ^= (1UL << from) | (1UL << to); // Rewind metainformation. ZobristKey = ZobristKeyHistory[HalfMoves - 1]; EnPassantHistory[HalfMoves] = InvalidSquare; EnPassantSquare = EnPassantHistory[HalfMoves - 1]; FiftyMovesClock = FiftyMovesHistory[HalfMoves - 1]; HalfMoves--; // Rewind capture if applicable. switch (capture & Piece.Mask) { case Piece.Empty: break; case Piece.Rook: if ((SideToMove == Colour.White && to == 0) || (SideToMove == Colour.Black && to == 56)) { CastleQueenside[1 - SideToMove]++; } else if ((SideToMove == Colour.White && to == 7) || (SideToMove == Colour.Black && to == 63)) { CastleKingside[1 - SideToMove]++; } goto default; default: Bitboard[capture] ^= 1UL << to; Bitboard[1 - SideToMove] ^= 1UL << to; OccupiedBitboard |= 1UL << to; Material[1 - SideToMove] += Engine.PieceValue[capture]; break; } switch (special & Piece.Mask) { // Rewind regular move. case Piece.Empty: switch (piece & Piece.Mask) { // For rook move, restore castling on one side if applicable. case Piece.Rook: if ((SideToMove == Colour.White && from == 56) || (SideToMove == Colour.Black && from == 0)) { CastleQueenside[SideToMove]++; } else if ((SideToMove == Colour.White && from == 63) || (SideToMove == Colour.Black && from == 7)) { CastleKingside[SideToMove]++; } break; // For king move, restore castling on both sides if applicable. case Piece.King: CastleQueenside[SideToMove]++; CastleKingside[SideToMove]++; break; } break; // Rewind castling. case Piece.King: CastleQueenside[SideToMove]++; CastleKingside[SideToMove]++; Int32 rookFrom; Int32 rookTo; if (to < from) { rookFrom = Rank(to) * 8; rookTo = 3 + Rank(to) * 8; } else { rookFrom = 7 + Rank(to) * 8; rookTo = 5 + Rank(to) * 8; } Bitboard[SideToMove | Piece.Rook] ^= (1UL << rookFrom) | (1UL << rookTo); Bitboard[SideToMove] ^= (1UL << rookFrom) | (1UL << rookTo); OccupiedBitboard ^= (1UL << rookFrom) | (1UL << rookTo); Square[rookFrom] = SideToMove | Piece.Rook; Square[rookTo] = Piece.Empty; break; // Rewind en passant. case Piece.Pawn: Square[File(to) + Rank(from) * 8] = special; Bitboard[special] ^= 1UL << (File(to) + Rank(from) * 8); Bitboard[1 - SideToMove] ^= 1UL << (File(to) + Rank(from) * 8); OccupiedBitboard ^= 1UL << (File(to) + Rank(from) * 8); Material[1 - SideToMove] += Engine.PieceValue[special]; break; // Rewind pawn promotion. default: Bitboard[piece] ^= 1UL << to; Bitboard[special] ^= 1UL << to; Material[SideToMove] -= Engine.PieceValue[special] - Engine.PieceValue[piece]; break; } }
/// <summary> /// Makes the given move on the position. /// </summary> /// <param name="move">The move to make.</param> public void Make(Int32 move) { Int32 from = Move.From(move); Int32 to = Move.To(move); Int32 piece = Move.Piece(move); Int32 capture = Move.Capture(move); Int32 special = Move.Special(move); // Update core board state. Square[to] = piece; Square[from] = Piece.Empty; Bitboard[piece] ^= (1UL << from) | (1UL << to); Bitboard[SideToMove] ^= (1UL << from) | (1UL << to); OccupiedBitboard ^= (1UL << from) | (1UL << to); // Update metainformation. ZobristKey ^= Zobrist.PiecePosition[piece][from] ^ Zobrist.PiecePosition[piece][to]; ZobristKey ^= Zobrist.Colour; if (EnPassantSquare != InvalidSquare) { ZobristKey ^= Zobrist.EnPassant[EnPassantSquare]; EnPassantSquare = InvalidSquare; } FiftyMovesClock++; HalfMoves++; // Handle capture if applicable. switch (capture & Piece.Mask) { case Piece.Empty: break; case Piece.Rook: if ((SideToMove == Colour.White && to == 0) || (SideToMove == Colour.Black && to == 56)) { if (CastleQueenside[1 - SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleQueenside[1 - SideToMove]; } } else if ((SideToMove == Colour.White && to == 7) || (SideToMove == Colour.Black && to == 63)) { if (CastleKingside[1 - SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleKingside[1 - SideToMove]; } } goto default; default: Bitboard[capture] ^= 1UL << to; Bitboard[1 - SideToMove] ^= 1UL << to; OccupiedBitboard |= 1UL << to; ZobristKey ^= Zobrist.PiecePosition[capture][to]; Material[1 - SideToMove] -= Engine.PieceValue[capture]; FiftyMovesClock = 0; break; } switch (special & Piece.Mask) { // Handle regular move (not en passant, castling, or pawn promotion). case Piece.Empty: switch (piece & Piece.Mask) { // For pawn move, update fifty moves clock and en passant state. case Piece.Pawn: FiftyMovesClock = 0; if ((from - to) * (from - to) == 256) { ZobristKey ^= Zobrist.EnPassant[from]; EnPassantHistory[HalfMoves] = EnPassantSquare = (from + to) / 2; } break; // For rook move, disable castling on one side. case Piece.Rook: if ((SideToMove == Colour.White && from == 56) || (SideToMove == Colour.Black && from == 0)) { if (CastleQueenside[SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleQueenside[SideToMove]; } } else if ((SideToMove == Colour.White && from == 63) || (SideToMove == Colour.Black && from == 7)) { if (CastleKingside[SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleKingside[SideToMove]; } } break; // For king move, disable castling on both sides. case Piece.King: if (CastleQueenside[SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleQueenside[SideToMove]; } if (CastleKingside[SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleKingside[SideToMove]; } break; } break; // Handle castling. case Piece.King: if (CastleQueenside[SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleQueenside[SideToMove]; } if (CastleKingside[SideToMove]-- > 0) { ZobristKey ^= Zobrist.CastleKingside[SideToMove]; } Int32 rookFrom; Int32 rookTo; if (to < from) { rookFrom = Rank(to) * 8; rookTo = 3 + Rank(to) * 8; } else { rookFrom = 7 + Rank(to) * 8; rookTo = 5 + Rank(to) * 8; } Bitboard[SideToMove | Piece.Rook] ^= (1UL << rookFrom) | (1UL << rookTo); Bitboard[SideToMove] ^= (1UL << rookFrom) | (1UL << rookTo); OccupiedBitboard ^= (1UL << rookFrom) | (1UL << rookTo); ZobristKey ^= Zobrist.PiecePosition[SideToMove | Piece.Rook][rookFrom]; ZobristKey ^= Zobrist.PiecePosition[SideToMove | Piece.Rook][rookTo]; Square[rookFrom] = Piece.Empty; Square[rookTo] = SideToMove | Piece.Rook; break; // Handle en passant. case Piece.Pawn: Square[File(to) + Rank(from) * 8] = Piece.Empty; Bitboard[special] ^= 1UL << (File(to) + Rank(from) * 8); Bitboard[1 - SideToMove] ^= 1UL << (File(to) + Rank(from) * 8); OccupiedBitboard ^= 1UL << (File(to) + Rank(from) * 8); ZobristKey ^= Zobrist.PiecePosition[special][File(to) + Rank(from) * 8]; Material[1 - SideToMove] -= Engine.PieceValue[special]; break; // Handle pawn promotion. default: Bitboard[piece] ^= 1UL << to; Bitboard[special] ^= 1UL << to; ZobristKey ^= Zobrist.PiecePosition[piece][to]; ZobristKey ^= Zobrist.PiecePosition[special][to]; Material[SideToMove] += Engine.PieceValue[special] - Engine.PieceValue[piece]; Square[to] = special; break; } SideToMove = 1 - SideToMove; FiftyMovesHistory[HalfMoves] = FiftyMovesClock; ZobristKeyHistory[HalfMoves] = ZobristKey; }