/// move_to_san() takes a position and a legal Move as input and returns its /// short algebraic notation representation. internal static string move_to_san(Position pos, int m) { if (m == MoveC.MOVE_NONE) { return "(none)"; } if (m == MoveC.MOVE_NULL) { return "(null)"; } Debug.Assert(pos.move_is_legal(m)); Bitboard others, b; Color us = pos.sideToMove; var san = new StringBuilder(); Square from = from_sq(m); Square to = to_sq(m); Piece pc = pos.piece_on(from); PieceType pt = type_of(pc); if (type_of_move(m) == MoveTypeC.CASTLING) { san.Append(to > from ? "O-O" : "O-O-O"); } else { if (pt != PieceTypeC.PAWN) { san.Append(PieceToChar[ColorC.WHITE][pt]); // Upper case // Disambiguation if we have more then one piece of type 'pt' that can // reach 'to' with a legal move. others = b = (pos.attacks_from_PS(pc, to) & pos.pieces_PTC(pt, us)) ^ (ulong)from; while (others != 0) { Move move = make_move(pop_lsb(ref b), to); if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) { others ^= (ulong)from_sq(move); } } if (others != 0) { if ((others & file_bb_S(from)) == 0) { san.Append(file_to_char(file_of(from))); } else if ((others & rank_bb_S(from)) == 0) { san.Append(rank_to_char(rank_of(from))); } else { san.Append(square_to_string(from)); } } } else if (pos.is_capture(m)) { san.Append(file_to_char(file_of(from))); } if (pos.is_capture(m)) { san.Append('x'); } san.Append(square_to_string(to)); if (type_of_move(m) == MoveTypeC.PROMOTION) { san.Append('='); san.Append(PieceToChar[ColorC.WHITE][promotion_type(m)]); } } var ci = CheckInfoBroker.GetObject(); ci.CreateCheckInfo(pos); if (pos.move_gives_check(m, ci)) { var st = new StateInfo(); pos.do_move(m, st); var mlist = MListBroker.GetObject(); mlist.pos = 0; Movegen.generate_legal(pos, mlist.moves, ref mlist.pos); san.Append(mlist.pos > 0 ? "+" : "#"); MListBroker.Free(); pos.undo_move(m); } CheckInfoBroker.Free(); return san.ToString(); }
// connected_moves() tests whether two moves are 'connected' in the sense // that the first move somehow made the second move possible (for instance // if the moving piece is the same in both moves). The first move is assumed // to be the move that was made to reach the current position, while the // second move is assumed to be a move from the current position. internal static bool connected_moves(Position pos, Move m1, Move m2) { Square f1, t1, f2, t2; Piece p1, p2; Square ksq; Debug.Assert(Utils.is_ok_M(m1)); Debug.Assert(Utils.is_ok_M(m2)); // Case 1: The moving piece is the same in both moves f2 = Utils.from_sq(m2); t1 = Utils.to_sq(m1); if (f2 == t1) return true; // Case 2: The destination square for m2 was vacated by m1 t2 = Utils.to_sq(m2); f1 = Utils.from_sq(m1); if (t2 == f1) return true; // Case 3: Moving through the vacated square p2 = pos.piece_on(f2); if (piece_is_slider(p2) && (Utils.bit_is_set(Utils.between_bb(f2, t2), f1) != 0)) return true; // Case 4: The destination square for m2 is defended by the moving piece in m1 p1 = pos.piece_on(t1); if ((Utils.bit_is_set(pos.attacks_from_PS(p1, t1), t2)) != 0) return true; // Case 5: Discovered check, checking piece is the piece moved in m1 ksq = pos.king_square(pos.sideToMove); if ( piece_is_slider(p1) && (Utils.bit_is_set(Utils.between_bb(t1, ksq), f2) != 0) && (Utils.bit_is_set(Position.attacks_from(p1, t1, Utils.xor_bit(pos.occupied_squares, f2)), ksq) != 0) ) return true; return false; }