protected void AssertMove(string expected, ChessboardCell from, ChessboardCell to) { var move = m_engine.GetLegalMove(from, to); Assert.IsNotNull(move); Factory.Instance.CreateCommand(Commands.PlayMove, move).Execute(m_engine); Assert.AreEqual(expected, move.ToString(Notation.LongAlgebraic)); }
public bool IsPieceAt(ChessboardCell cell, out Players player, out Exports.Pieces piece) { int data = m_cells[cell.Value]; player = (Players)DecodePlayer(data); piece = (Exports.Pieces)DecodePiece(data); return piece != Exports.Pieces.NoPiece; }
public PiecePosition(Players player, Pieces piece, ChessboardCell cell) { Debug.Assert(cell.Row >= Chessboard.ROW_MIN && cell.Row <= Chessboard.ROW_MAX); Debug.Assert(cell.Column >= Chessboard.COLUMN_MIN && cell.Column <= Chessboard.COLUMN_MAX); m_player = player; m_piece = piece; m_cell = cell; }
public MoveDesc GetLegalMove(ChessboardCell from, ChessboardCell to) { lock (m_legal_moves_lock) { return m_legal_moves?.Find(x => x.From.IsSame(from) && x.To.IsSame(to) && !x.IsPromotion); } }
// TODO - add parameters public MoveDesc(Pieces piece, ChessboardCell from, ChessboardCell to, Pieces promote_to, bool is_capture, bool is_check, bool is_checkmate) { Debug.Assert(piece != Pieces.NoPiece); Debug.Assert(!from.IsSame(to)); Debug.Assert(!(is_checkmate && !is_check)); m_piece = piece; m_from = from; m_to = to; m_promote_to = promote_to; m_is_capture = is_capture; m_is_check = is_check; m_is_checkmate = is_checkmate; }
private static void FillLines(ulong[] masks, int move_delta_row, int move_delta_column) { for (int i = 0; i < masks.Length; ++i) { var cell = new ChessboardCell(i); int cur_row = cell.Row; int cur_column = cell.Column; while (cur_row >= Chessboard.ROW_MIN && cur_row <= Chessboard.ROW_MAX && cur_column >= Chessboard.COLUMN_MIN && cur_column <= Chessboard.COLUMN_MAX) { masks[i] |= (1UL << cell.Value); cur_row += move_delta_row; cur_column += move_delta_column; } } }
// TODO - should we have this for a king as well? static PieceEvaluationMasks() { FillPawnValues(m_white_pawn, 1); FillPawnValues(m_black_pawn, -1); for (int i = 0; i < m_knight.Length; ++i) { var cell = new ChessboardCell(i); m_knight[i] = (1UL << i); foreach (var move_delta in Position.MovesGenerator.m_knight_moves) { int new_row = cell.Row + move_delta.m_delta_row; int new_column = cell.Column + move_delta.m_delta_column; if (new_row >= Chessboard.ROW_MIN && new_row <= Chessboard.ROW_MAX && new_column >= Chessboard.COLUMN_MIN && new_column <= Chessboard.COLUMN_MAX) { int new_index = ChessboardCell.CalculateValue(new_row, new_column); m_knight[i] |= (1UL << new_index); } } } FillLines(m_bishop, -1, -1); FillLines(m_bishop, -1, 1); FillLines(m_bishop, 1, -1); FillLines(m_bishop, 1, 1); FillLines(m_rook, -1, 0); FillLines(m_rook, 0, -1); FillLines(m_rook, 0, 1); FillLines(m_rook, 1, 0); for (int i = 0; i < m_queen.Length; ++i) { m_queen[i] = m_bishop[i] | m_rook[i]; } }
public static bool IsMyMovePutsMyKingInCheck(Players player_who_moved, Move m, Position.Position p, bool is_king_was_in_check_before_the_move) { Debug.Assert(!m.IsNullMove); if (is_king_was_in_check_before_the_move || m.Piece == Pieces.King) { // Cannot optimize - do a full check var king_cell = new ChessboardCell(p.GetPieces(player_who_moved).KingsCell); return PositionHelper.IsCellAttacked(king_cell, p.PlayerToMove, p); } else { // My king can only be attacked now along the line between the king // and the cell, from where my piece has moved var player_to_move = p.PlayerToMove; var king_cell = new ChessboardCell( p.GetPieces(Helper.GetOtherPlayer(player_to_move)).KingsCell); return PositionHelper.IsCellAttackedFromDir(king_cell, new ChessboardCell(m.FromCell), p.GetPieces(p.PlayerToMove), p.GetPieces(Helper.GetOtherPlayer(p.PlayerToMove))); } }
public static bool IsRookAttacks(ChessboardCell rook_cell, ChessboardCell cell_to_check, PlayerPieceSet my_pieces, PlayerPieceSet other_pieces) { return (rook_cell.Row == cell_to_check.Row || rook_cell.Column == cell_to_check.Column) && IsCellAttackedFromDir(cell_to_check, rook_cell, my_pieces, other_pieces); }
public static bool IsBishopAttacks(ChessboardCell bishop_cell, ChessboardCell cell_to_check, PlayerPieceSet my_pieces, PlayerPieceSet other_pieces) { return bishop_cell.IsSameDiagonal(cell_to_check) && IsCellAttackedFromDir(cell_to_check, bishop_cell, my_pieces, other_pieces); }
public static bool IsKnightAttacks(ChessboardCell knight_cell, ChessboardCell cell_to_check) { int delta_row = Math.Abs(knight_cell.Row - cell_to_check.Row); // Slightly optimized version of // if ((delta_row == 1 && delta_column == 2) // || (delta_row == 2 && delta_column == 1)) if (delta_row == 1 || delta_row == 2) { int delta_column = Math.Abs(knight_cell.Column - cell_to_check.Column); if (delta_row + delta_column == 3) { return true; } } return false; }
public static bool IsPawnAttacks(ChessboardCell pawn_cell, ChessboardCell cell_to_check, int pawn_move_row_delta) { return ((pawn_cell.Row + pawn_move_row_delta == cell_to_check.Row) && (cell_to_check.Column == pawn_cell.Column - 1 || cell_to_check.Column == pawn_cell.Column + 1)); }
public bool IsNeighbour(ChessboardCell other) { return Math.Abs(Row - other.Row) <= 1 && Math.Abs(Column - other.Column) <= 1; }
public MoveDesc GetLegalMove(ChessboardCell from, ChessboardCell to, Pieces promote_to) { return m_analyzer.GetLegalMove(from, to, promote_to); }
public static bool IsCellAttackedFromDir(ChessboardCell attacked_cell, ChessboardCell attack_origin, PlayerPieceSet attacker_pieces, PlayerPieceSet defender_pieces) { Debug.Assert(!attacked_cell.IsSame(attack_origin)); int delta_row = attack_origin.Row - attacked_cell.Row; int delta_column = attack_origin.Column - attacked_cell.Column; // Fast check if the two cells are on the same vertical or horizontal line // or one the same diagonal if (delta_row != 0 && delta_column != 0 && delta_row != delta_column && delta_row != -delta_column) { return false; } // Check the ray from attacked_cell towards attack_origin and possibly behind // attack_origin until we reach the edge of the chessboard bool result = false; int row_inc = 0; int column_inc = 0; int iter_count = int.MaxValue; if (delta_row < 0) { row_inc = -1; iter_count = attacked_cell.Row - Chessboard.ROW_MIN; } else if (delta_row > 0) { row_inc = 1; iter_count = Chessboard.ROW_MAX - attacked_cell.Row; } if (delta_column < 0) { column_inc = -1; iter_count = Math.Min(iter_count, attacked_cell.Column - Chessboard.COLUMN_MIN); } else if (delta_column > 0) { column_inc = 1; iter_count = Math.Min(iter_count, Chessboard.COLUMN_MAX - attacked_cell.Column); } int cur_row = attacked_cell.Row; int cur_column = attacked_cell.Column; for (int i = 0; i < iter_count; ++i) { cur_row += row_inc; cur_column += column_inc; Debug.Assert(cur_row >= Chessboard.ROW_MIN && cur_row <= Chessboard.ROW_MAX && cur_column >= Chessboard.COLUMN_MIN && cur_column <= Chessboard.COLUMN_MAX); int cur_cell = ChessboardCell.CalculateValue(cur_row, cur_column); if (defender_pieces.IsOccupiedCell(cur_cell)) { result = false; break; } if (attacker_pieces.IsOccupiedCell(cur_cell)) { if (attacker_pieces.IsQueenAtCell(cur_cell)) { result = true; } else if (row_inc == 0 || column_inc == 0) { result = attacker_pieces.IsRookAtCell(cur_cell); } else { result = attacker_pieces.IsBishopAtCell(cur_cell); } break; } } return result; }
public bool MakeChildNewRoot(ChessboardCell from, ChessboardCell to, Exports.Pieces promote_to = Exports.Pieces.NoPiece) { if (Root == null) { return false; } // Discard all root children and their subtrees, except the child // specified by the method parameters ReleaseChildren(child => { return child.ParentMove.FromCell != from.Value || child.ParentMove.ToCell != to.Value || child.ParentMove.PromoteToPiece != promote_to; }, Root); if (Root.Children.Count != 1) { Clean(); m_listener.RootPositionChanged(null); return false; } // Discard current root Interlocked.Decrement(ref m_node_count); // Set a new root Root = Root.Children[0]; m_listener.RootPositionChanged(Root.Position); GenerateAllChildren(Root, 2); return true; }
public MoveDesc GetLegalMove(ChessboardCell from, ChessboardCell to, Exports.Pieces promote_to) { lock (m_legal_moves_lock) { return m_legal_moves?.Find(x => x.From.IsSame(from) && x.To.IsSame(to) && x.IsPromotion && x.PromoteTo == promote_to); } }
public static string Encode(IReadOnlyPosition position, EncodingOptions options = EncodingOptions.None) { Debug.Assert(position != null); var s = new StringBuilder(80); // Encode the chessboard var board = Factory.Instance.CreateChessboardArray(position); for (int row = Chessboard.ROW_MAX; row >= Chessboard.ROW_MIN; --row) { int empty_cells = 0; for (int column = Chessboard.COLUMN_MIN; column <= Chessboard.COLUMN_MAX; ++column) { var cur_cell = new ChessboardCell(row, column); Players player; Pieces piece; if (board.IsPieceAt(cur_cell, out player, out piece)) { if (empty_cells != 0) { s.Append(empty_cells); empty_cells = 0; } s.Append(PieceToChar(player, piece)); } else { ++empty_cells; } } if (empty_cells != 0) { s.Append(empty_cells); empty_cells = 0; } if (row != Chessboard.ROW_MIN) { s.Append(ROWS_SEPARATOR); } } // Encode player to move s.Append(' '); s.Append(position.IsWhiteToMove ? FEN_WHITE_TO_MOVE : FEN_BLACK_TO_MOVE); // Encode castling availability s.Append(' '); if (!position.IsCanCastleShort(Players.White) && !position.IsCanCastleLong(Players.White) && !position.IsCanCastleShort(Players.Black) && !position.IsCanCastleLong(Players.Black)) { s.Append(FEN_NO_ONE_CAN_CASTLE); } else { if (position.IsCanCastleShort(Players.White)) { s.Append(FEN_WHITE_CAN_CASTLE_SHORT); } if (position.IsCanCastleLong(Players.White)) { s.Append(FEN_WHITE_CAN_CASTLE_LONG); } if (position.IsCanCastleShort(Players.Black)) { s.Append(FEN_BLACK_CAN_CASTLE_SHORT); } if (position.IsCanCastleLong(Players.Black)) { s.Append(FEN_BLACK_CAN_CASTLE_LONG); } } // Encode en passant caputre possibility s.Append(' '); int? capture_en_passant_column = position.CaptureEnPassantColumn; if (capture_en_passant_column.HasValue) { var cell = new ChessboardCell(position.IsWhiteToMove ? 5 : 2, capture_en_passant_column.Value); s.Append(cell.ToString()); } else { s.Append(FEN_CANNOT_CAPTURE_EN_PASSANT); } s.Append(' ').Append(position.HalfmoveClock); s.Append(' ').Append(options.HasFlag(EncodingOptions.SetMovesCountToOne) ? 1 : position.FullmoveNumber); return s.ToString(); }
public bool IsSameDiagonal(ChessboardCell other) { int delta_row = Row - other.Row; int delta_column = Column - other.Column; return delta_row == delta_column || delta_row == -delta_column; }
public bool IsSame(ChessboardCell other) { return m_row == other.m_row && m_column == other.m_column; }
public static bool IsQueenAttacks(ChessboardCell queen_cell, ChessboardCell cell_to_check, PlayerPieceSet my_pieces, PlayerPieceSet other_pieces) { return IsBishopAttacks(queen_cell, cell_to_check, my_pieces, other_pieces) || IsRookAttacks(queen_cell, cell_to_check, my_pieces, other_pieces); }
private static void FillPawnValues(ulong[] pawn, int move_row_delta) { for (int i = 0; i < pawn.Length; ++i) { var cell = new ChessboardCell(i); if (cell.Row != Chessboard.ROW_MIN && cell.Row != Chessboard.ROW_MAX) { if (cell.Column > Chessboard.COLUMN_MIN) { int new_cell = ChessboardCell.CalculateValue( cell.Row + move_row_delta, cell.Column - 1); pawn[i] |= (1UL << new_cell); } if (cell.Column < Chessboard.COLUMN_MAX) { int new_cell = ChessboardCell.CalculateValue( cell.Row + move_row_delta, cell.Column + 1); pawn[i] |= (1UL << new_cell); } } } }
public static bool IsKingAttacks(ChessboardCell king_cell, ChessboardCell cell_to_check) { return cell_to_check.IsNeighbour(king_cell); }
public static bool IsCellAttacked(ChessboardCell cell, Players attacking_player, Position position) { Debug.Assert(position != null); var my_pieces = position.GetPieces(attacking_player); var other_pieces = position.GetPieces( Helper.GetOtherPlayer(attacking_player)); int pawn_move_row_delta = attacking_player == Players.White ? 1 : -1; for (uint i = 0, e = my_pieces.PawnsCount; i < e; ++i) { if (IsPawnAttacks(new ChessboardCell(my_pieces.GetPawnCell(i)), cell, pawn_move_row_delta)) { return true; } } for (uint i = 0, e = my_pieces.KnightsCount; i < e; ++i) { if (IsKnightAttacks(new ChessboardCell(my_pieces.GetKnightCell(i)), cell)) { return true; } } for (uint i = 0, e = my_pieces.BishopsCount; i < e; ++i) { if (IsBishopAttacks(new ChessboardCell(my_pieces.GetBishopCell(i)), cell, my_pieces, other_pieces)) { return true; } } for (uint i = 0, e = my_pieces.RooksCount; i < e; ++i) { if (IsRookAttacks(new ChessboardCell(my_pieces.GetRookCell(i)), cell, my_pieces, other_pieces)) { return true; } } for (uint i = 0, e = my_pieces.QueensCount; i < e; ++i) { if (IsQueenAttacks(new ChessboardCell(my_pieces.GetQueenCell(i)), cell, my_pieces, other_pieces)) { return true; } } if (IsKingAttacks(new ChessboardCell(my_pieces.KingsCell), cell)) { return true; } return false; }
public Position PlayMove(Move move) { // 'My' refers to player to move, 'other' refers to other player var my_player = PlayerToMove; var other_player = Helper.GetOtherPlayer(my_player); var my_pieces = GetPieces(my_player); var other_pieces = GetPieces(other_player); PlayerPieceSet new_my_pieces = null; if (move.IsPromotion) { Debug.Assert(move.Piece == Exports.Pieces.Pawn); var tmp_pieces = PlayerPieceSet.RemovePiece(my_pieces, Exports.Pieces.Pawn, move.FromCell); new_my_pieces = PlayerPieceSet.InsertPiece(tmp_pieces, move.PromoteToPiece, move.ToCell); } else if (move.IsShortCastle || move.IsLongCastle) { int base_row = my_player == Players.White ? Chessboard.ROW_MIN : Chessboard.ROW_MAX; Debug.Assert(move.Piece == Exports.Pieces.King); var tmp_pieces = PlayerPieceSet.MovePiece(my_pieces, Exports.Pieces.King, move.FromCell, move.ToCell); if (move.IsShortCastle) { new_my_pieces = PlayerPieceSet.MovePiece(tmp_pieces, Exports.Pieces.Rook, ChessboardCell.CalculateValue(base_row, Chessboard.COLUMN_H), ChessboardCell.CalculateValue(base_row, Chessboard.COLUMN_F)); } else { new_my_pieces = PlayerPieceSet.MovePiece(tmp_pieces, Exports.Pieces.Rook, ChessboardCell.CalculateValue(base_row, Chessboard.COLUMN_A), ChessboardCell.CalculateValue(base_row, Chessboard.COLUMN_D)); } } else { new_my_pieces = PlayerPieceSet.MovePiece(my_pieces, move.Piece, move.FromCell, move.ToCell); } PlayerPieceSet new_other_pieces = null; if (move.IsCapture) { Exports.Pieces piece_to_remove = Exports.Pieces.NoPiece; int remove_piece_at = 0; if (move.IsEnPassantCapture) { var move_to_cell = new ChessboardCell(move.ToCell); remove_piece_at = ChessboardCell.CalculateValue( move_to_cell.Row + (IsWhiteToMove ? -1 : 1), move_to_cell.Column); // Only pawns can capture en passant piece_to_remove = Exports.Pieces.Pawn; } else { remove_piece_at = move.ToCell; piece_to_remove = other_pieces.GetPieceAtCell(remove_piece_at); } new_other_pieces = PlayerPieceSet.RemovePiece(other_pieces, piece_to_remove, remove_piece_at); } else { // No change to other player's pieces, re-use the object new_other_pieces = other_pieces; } // TODO - consider memory pooling here var result = new Position(); var new_white_pieces = IsWhiteToMove ? new_my_pieces : new_other_pieces; var new_black_pieces = IsWhiteToMove ? new_other_pieces : new_my_pieces; int? new_en_passant_capture_column = null; if (move.Piece == Exports.Pieces.Pawn) { var from = new ChessboardCell(move.FromCell); var to_row = new ChessboardCell(move.ToCell).Row; if (Math.Abs(from.Row - to_row) == 2) { new_en_passant_capture_column = from.Column; } } var new_fullmove_number = FullmoveNumber; if (other_player == Players.White) { ++new_fullmove_number; } var new_halfmove_clock = HalfmoveClock + 1; if (move.Piece == Exports.Pieces.Pawn || move.IsCapture) { new_halfmove_clock = 0; } var new_white_castling_options = CalcNewCastlingOptions(Players.White, new_white_pieces, Chessboard.ROW_MIN); var new_black_castling_options = CalcNewCastlingOptions(Players.Black, new_black_pieces, Chessboard.ROW_MAX); result.Reset(new_white_pieces, new_black_pieces, other_player, new_white_castling_options, new_black_castling_options, new_en_passant_capture_column, new_fullmove_number, (ushort)new_halfmove_clock); return result; }
public static bool IsMyMoveAttacksTheKing(Move m, Position position_after_move) { Debug.Assert(!m.IsSameCells(Move.NULL_MOVE)); var moved_to_cell = new ChessboardCell(m.ToCell); var kings_cell = new ChessboardCell(position_after_move.GetPieces( position_after_move.PlayerToMove).KingsCell); var attacking_player = Helper.GetOtherPlayer( position_after_move.PlayerToMove); var attacking_pieces = position_after_move.GetPieces(attacking_player); var defending_pieces = position_after_move.GetPieces( Helper.GetOtherPlayer(attacking_player)); // TODO // Check if the piece which moved attacks the king // Promotions shall be respected there switch (m.IsPromotion ? m.PromoteToPiece : m.Piece) { case Exports.Pieces.Pawn: int pawn_move_row_delta = attacking_player == Players.White ? 1 : -1; if (IsPawnAttacks(moved_to_cell, kings_cell, pawn_move_row_delta)) { return true; } break; case Exports.Pieces.Knight: if (IsKnightAttacks(moved_to_cell, kings_cell)) { return true; } break; case Exports.Pieces.Bishop: if (IsBishopAttacks(moved_to_cell, kings_cell, attacking_pieces, defending_pieces)) { return true; } break; case Exports.Pieces.Rook: if (IsRookAttacks(moved_to_cell, kings_cell, attacking_pieces, defending_pieces)) { return true; } break; case Exports.Pieces.Queen: if (IsQueenAttacks(moved_to_cell, kings_cell, attacking_pieces, defending_pieces)) { return true; } break; case Exports.Pieces.King: { // Castling can put opponent's king in check if (m.IsShortCastle || m.IsLongCastle) { var rook_cell = new ChessboardCell( moved_to_cell.Row, m.IsShortCastle ? Chessboard.COLUMN_F : Chessboard.COLUMN_C); if (IsRookAttacks(rook_cell, kings_cell, attacking_pieces, defending_pieces)) { return true; } } } break; } // Check if the piece behind the piece which moved attacks the king return IsCellAttackedFromDir(kings_cell, new ChessboardCell(m.FromCell), attacking_pieces, defending_pieces); }
private static void WritePawnMove(int from_cell, int to_cell, uint cell_index, Move.Flags flags, List<Move> moves) { var to_cell_obj = new ChessboardCell(to_cell); if (to_cell_obj.Row == Chessboard.ROW_MIN || to_cell_obj.Row == Chessboard.ROW_MAX) { foreach (var promotion in m_pawn_promotions) { moves.Add(new Move(from_cell, to_cell, flags | Move.Flags.Promotion, promotion)); } } else { moves.Add(new Move(Exports.Pieces.Pawn, from_cell, to_cell, cell_index, flags)); } }
public int CompareTo(ChessboardCell other) { return Value.CompareTo(other.Value); }
protected void PlayMove(ChessboardCell from, ChessboardCell to, Pieces promote_to) { Factory.Instance.CreateCommand(Commands.PlayMove, m_engine.GetLegalMove(from, to, promote_to)).Execute(m_engine); }
private static void MakePawnMoves(PlayerPieceSet my_pieces, PlayerPieceSet other_pieces, bool is_white_to_move, int? en_passant_capture_column, List<Move> moves) { int move_row_diff = is_white_to_move ? 1 : -1; for (uint i = 0, e = my_pieces.PawnsCount; i < e; ++i) { uint cell_index; var pawn_cell = new ChessboardCell(my_pieces.GetPawnCell(i, out cell_index)); var new_cell_value = ChessboardCell.CalculateValue( pawn_cell.Row + move_row_diff, pawn_cell.Column); // Moves without captures if (!my_pieces.IsOccupiedCell(new_cell_value) && !other_pieces.IsOccupiedCell(new_cell_value)) { WritePawnMove(pawn_cell.Value, new_cell_value, cell_index, 0, moves); // Two-rows move from the initial rank int initial_row = is_white_to_move ? Chessboard.ROW_MIN + 1 : Chessboard.ROW_MAX - 1; if (pawn_cell.Row == initial_row) { new_cell_value = ChessboardCell.CalculateValue( pawn_cell.Row + 2 * move_row_diff, pawn_cell.Column); if (!my_pieces.IsOccupiedCell(new_cell_value) && !other_pieces.IsOccupiedCell(new_cell_value)) { WritePawnMove(pawn_cell.Value, new_cell_value, cell_index, 0, moves); } } } // Captures except en passant captures // delta_column can be -1 or 1 for (int delta_column = -1; delta_column <= 1; delta_column += 2) { int new_column = pawn_cell.Column + delta_column; if (new_column >= Chessboard.COLUMN_MIN && new_column <= Chessboard.COLUMN_MAX) { new_cell_value = ChessboardCell.CalculateValue(pawn_cell.Row + move_row_diff, new_column); if (other_pieces.IsOccupiedCell(new_cell_value)) { WritePawnMove(pawn_cell.Value, new_cell_value, cell_index, Move.Flags.Capture, moves); } } } // En passant capture if (en_passant_capture_column.HasValue && ((is_white_to_move && pawn_cell.Row == 4) || (!is_white_to_move && pawn_cell.Row == 3)) && Math.Abs(pawn_cell.Column - en_passant_capture_column.Value) == 1) { new_cell_value = ChessboardCell.CalculateValue(pawn_cell.Row + move_row_diff, en_passant_capture_column.Value); // Since en passant capture is allowed, we know that the destination // cell is not occupied WritePawnMove(pawn_cell.Value, new_cell_value, cell_index, Move.Flags.Capture | Move.Flags.EnPassantCapture, moves); } // TODO - add en passant captures // TODO - uncomment //throw new NotImplementedException(); } }