/// <summary> /// 指し手が合法か /// </summary> /// <param name="m"></param> /// <returns></returns> public bool IsLegal(Move m) { Color Us = sideToMove; Square to = m.To(); Piece toPcType; if (m.IsDrop()) { Piece pr = toPcType = m.DroppedPiece(); // 打つ駒は適切か if (pr < Piece.PAWN || Piece.KING <= pr) { return(false); } // 駒を持っているか if (!Hand(Us).Exist(pr)) { return(false); } // 行き先に駒はないか if ((Pieces() & to).IsNotZero()) { return(false); } if (pr == Piece.PAWN) { var rank_1 = (Us == Color.BLACK) ? Rank.RANK_1 : Rank.RANK_5; if (to.ToRank() == rank_1) { return(false); } // 二歩と打ち歩詰めのチェック if (!LegalPawnDrop(Us, to)) { return(false); } } } else { Square from = m.From(); Piece moved_pc = PieceOn(from); if (moved_pc == Piece.NO_PIECE) { return(false); } // 手番側の駒か if (moved_pc.PieceColor() != sideToMove) { return(false); } // 行き先に味方の駒はないか if ((Pieces(Us) & to).IsNotZero()) /* to_pc != Piece.NO_PIECE && to_pc.PieceColor() == SideToMove */ { return(false); } // 駒の動きは適切か if ((Bitboard.EffectsFrom(moved_pc, from, Pieces()) & to).IsZero()) { return(false); } // 成りが適切か if (m.IsPromote()) { } // 王手している駒があるか if (InCheck()) { if (moved_pc.Type() != Piece.KING) { // 両王手の場合、玉を動かすほかない if (Checkers().PopCount() > 1) { return(false); } if (((Bitboard.BetweenBB(Checkers().Pop(), KingSquare(Us)) | Checkers()) & to).IsZero()) { return(false); } } } // 自殺手 if (moved_pc.Type() == Piece.KING) { // 玉の移動先に相手側の利きがあるか if (EffectedTo(Us.Not(), to, from)) { return(false); } } else { var b = (PinnedPieces(Us) & from).IsZero() || // ピンされていない駒の移動は自由である Util.IsAligned(from, to, KingSquare(Us)); // ピンされている方角への移動は合法 if (!b) { return(false); } } toPcType = moved_pc.Type(); } if (InCheck() && toPcType != Piece.KING) { Bitboard target = Checkers(); Square checkSq = target.Pop(); // 王手している駒を1個取り除いて、もうひとつあるということは王手している駒が // 2つあったということであり、両王手なので合い利かず。 if (target.IsNotZero()) { return(false); } // 王と王手している駒との間の升に駒を打っていない場合、それは王手を回避していることに // ならないので、これは非合法手。 // 王手している駒が1つなら、王手している駒を取る指し手であるか、 // 遮断する指し手でなければならない if (!((Bitboard.BetweenBB(checkSq, KingSquare(Us)) & to).IsNotZero() || checkSq == to)) { return(false); } } return(true); }
/// <summary> /// 合法な指し手を生成する。 /// moves[startIndex]から使っていく。返し値をendIndexとして、 /// moves[startIndex]...moves[endIndex-1]まで使うものとする。 /// </summary> /// <param name="pos"></param> /// <param name="moves"></param> /// <param name="startIndex"></param> /// <returns></returns> public static int LegalAll(Position pos, Move[] moves, int startIndex) { /// Position.IsLegal()が完璧に非合法手を弾くので、NonEvalsion()で指し手を生成して /// FilterNonLegalMoves()で排除する。少し遅くなるが、Evasionそんなにないからいいだろう…。 // 愚直に81升調べてもどうってことないはずだが最低限の高速化をしとく var endIndex = startIndex; var us = pos.sideToMove; var ourPieces = pos.Pieces(us); // 自駒 Square from, to; var enemyField = Bitboard.EnemyField(us); // 敵陣 // 自分から見た1段目 var rank1_for_us = us == Color.BLACK ? Rank.RANK_1 : Rank.RANK_5; var rank2_for_us = us == Color.BLACK ? Rank.RANK_2 : Rank.RANK_4; // 自駒がないところ(移動先の候補) var movable = ~pos.Pieces(us); while (ourPieces.IsNotZero()) { from = ourPieces.Pop(); Piece pc = pos.PieceOn(from); // 移動元の駒 Piece pt = pc.Type(); // pcに駒を置いたときの利きに移動できて、自駒があるところには移動できない var target = Bitboard.EffectsFrom(pc, from, pos.Pieces()) & movable; while (target.IsNotZero()) { to = target.Pop(); // pcをfromからtoに移動させる指し手を生成する var r = to.ToRank(); // 行き場のない升への移動は非合法手なのでそれを除外して指し手生成 if (! (((pt == Piece.PAWN || pt == Piece.LANCE) && r == rank1_for_us) || (pt == Piece.KNIGHT && (r == rank1_for_us || r == rank2_for_us))) ) { moves[endIndex++] = Util.MakeMove(from, to); } // 成れる条件 // 1.移動させるのが成れる駒 // 2.移動先もしくは移動元が敵陣 if ((Piece.PAWN <= pt && pt < Piece.GOLD) && (enemyField & (new Bitboard(from) | new Bitboard(to))).IsNotZero()) { moves[endIndex++] = Util.MakeMovePromote(from, to); } } } // 駒打ちの指し手 var h = pos.Hand(us); foreach (Piece pt in HandExtensions.HandPiece) { // その駒を持っていないならskip if (h.Count(pt) == 0) { continue; } for (to = Square.ZERO; to < Square.NB; ++to) { // 駒がない升にしか打てない if (pos.PieceOn(to) != Piece.NO_PIECE) { continue; } // 行き場のない駒は打てない var r = to.ToRank(); if (((pt == Piece.PAWN || pt == Piece.LANCE) && r == rank1_for_us) || (pt == Piece.KNIGHT && (r == rank1_for_us || r == rank2_for_us))) { continue; } // 二歩のチェックだけしとく if (pt == Piece.PAWN && (pos.Pieces(us, Piece.PAWN) & Bitboard.FileBB(to.ToFile())).IsNotZero()) { continue; } moves[endIndex++] = Util.MakeMoveDrop(pt, to); } } // 非合法手を除外する。 int p = startIndex; while (p < endIndex) { Move m = moves[p]; if (pos.IsLegal(m)) { ++p; } else { moves[p] = moves[--endIndex]; // 非合法手でなかったので最後の指し手をここに埋める } } return(endIndex); }