PieceType PtHorse::AppendToNextAttackerAndTryPromote( Bitboard& occupied, Bitboard& attackers, PieceType nextPT, const PieceTypeSeeEvent ptsEvent ) const { PieceType PT = PieceType::N13_Horse; if (ptsEvent.m_opponentAttackers.AndIsNot0(ptsEvent.m_pos.GetBbOf10(PT))) { // todo: 実際に移動した方向を基にattackersを更新すれば、template, inline を使用しなくても良さそう。 // その場合、キャッシュに乗りやすくなるので逆に速くなるかも。 const Bitboard bb = ptsEvent.m_opponentAttackers & ptsEvent.m_pos.GetBbOf10(PT); const Square from = bb.GetFirstOneFromI9(); g_setMaskBb.XorBit(&occupied, from); attackers |= (g_lanceAttackBb.GetControllBb(occupied, ConvColor::OPPOSITE_COLOR10b(ptsEvent.m_turn), ptsEvent.m_to) & ptsEvent.m_pos.GetBbOf20(N02_Lance, ptsEvent.m_turn)) | (g_lanceAttackBb.GetControllBb(occupied, ptsEvent.m_turn, ptsEvent.m_to) & ptsEvent.m_pos.GetBbOf20(N02_Lance, ConvColor::OPPOSITE_COLOR10b(ptsEvent.m_turn))) | (g_rookAttackBb.GetControllBb(occupied, ptsEvent.m_to) & ptsEvent.m_pos.GetBbOf20(N06_Rook, N14_Dragon)) | (g_bishopAttackBb.BishopAttack(occupied, ptsEvent.m_to) & ptsEvent.m_pos.GetBbOf20(N05_Bishop, N13_Horse)); // それ以外の駒種類は、そのまま返す☆ return PT; } return PiecetypePrograms::m_PIECETYPE_PROGRAMS[nextPT]->AppendToNextAttackerAndTryPromote( occupied, attackers, PieceType::N06_Rook, ptsEvent ); }
Score Evaluator::calculateMaterialScore(const Position& position) const { Score score = Score::zero(); auto& blackHand = position.getBlackHand(); score += material::Pawn * blackHand.get(PieceType::pawn ()); score += material::Lance * blackHand.get(PieceType::lance ()); score += material::Knight * blackHand.get(PieceType::knight()); score += material::Silver * blackHand.get(PieceType::silver()); score += material::Gold * blackHand.get(PieceType::gold ()); score += material::Bishop * blackHand.get(PieceType::bishop()); score += material::Rook * blackHand.get(PieceType::rook ()); auto& whiteHand = position.getWhiteHand(); score -= material::Pawn * whiteHand.get(PieceType::pawn ()); score -= material::Lance * whiteHand.get(PieceType::lance ()); score -= material::Knight * whiteHand.get(PieceType::knight()); score -= material::Silver * whiteHand.get(PieceType::silver()); score -= material::Gold * whiteHand.get(PieceType::gold ()); score -= material::Bishop * whiteHand.get(PieceType::bishop()); score -= material::Rook * whiteHand.get(PieceType::rook ()); Bitboard occ = nosseOr(position.getBOccupiedBitboard(), position.getWOccupiedBitboard()); occ.unset(position.getBlackKingSquare()); occ.unset(position.getWhiteKingSquare()); BB_EACH(square, occ) { auto piece = position.getPieceOnBoard(square); if (piece.isBlack()) { score += material::score(piece); } else { score -= material::score(piece); } }
static Square minAttacker(const Board &board, Bitboard atcks, ColorType side) { if (side == White) { Bitboard retval(atcks & board.pawn_bits[White]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.knight_bits[White]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.bishop_bits[White]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.rook_bits[White]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.queen_bits[White]); if (!retval.isClear()) return retval.firstOne(); if (atcks.isSet(board.kingSquare(White))) return board.kingSquare(White); else return InvalidSquare; } else { Bitboard retval(atcks & board.pawn_bits[Black]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.knight_bits[Black]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.bishop_bits[Black]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.rook_bits[Black]); if (!retval.isClear()) return retval.firstOne(); retval = (atcks & board.queen_bits[Black]); if (!retval.isClear()) return retval.firstOne(); if (atcks.isSet(board.kingSquare(Black))) return board.kingSquare(Black); else return InvalidSquare; } }
Key Book::GetBookKey(const Position& pos) { Key key = 0; Bitboard bb = pos.GetOccupiedBB(); while (bb.Exists1Bit()) { const Square sq = bb.PopFirstOneFromI9(); key ^= m_ZobPiece[pos.GetPiece(sq)][sq]; } const Hand hand = pos.GetHand(pos.GetTurn()); for (HandPiece hp = HPawn; hp < HandPieceNum; ++hp) { key ^= m_ZobHand[hp][hand.NumOf(hp)]; } if (pos.GetTurn() == White) { key ^= m_ZobTurn; } return key; }
Bitboard SEE::extractAttackers(const Position& position, Square from, Square to) { Bitboard occ = nosseOr(position.getBOccupiedBitboard(), position.getWOccupiedBitboard()); RotatedBitboard occ90 = position.get90RotatedBitboard(); RotatedBitboard occR45 = position.getRight45RotatedBitboard(); RotatedBitboard occL45 = position.getLeft45RotatedBitboard(); occ.unset(from); occ90.unset(from.rotate90()); occR45.unset(from.rotateRight45()); occL45.unset(from.rotateLeft45()); Bitboard bb = Bitboard::zero(); bb |= (Bitboard::mask(to) << 1) & position.getBPawnBitboard(); bb |= (Bitboard::mask(to) >> 1) & position.getWPawnBitboard(); bb |= MoveTables::whiteLance(occ, to) & position.getBLanceBitboard(); bb |= MoveTables::blackLance(occ, to) & position.getWLanceBitboard(); bb |= MoveTables::whiteKnight(to) & position.getBKnightBitboard(); bb |= MoveTables::blackKnight(to) & position.getWKnightBitboard(); bb |= MoveTables::whiteSilver(to) & position.getBSilverBitboard(); bb |= MoveTables::blackSilver(to) & position.getWSilverBitboard(); bb |= MoveTables::whiteGold(to) & position.getBGoldBitboard(); bb |= MoveTables::blackGold(to) & position.getWGoldBitboard(); bb |= (MoveTables::ver(occ, to) | MoveTables::hor(occ90, to)) & (position.getBRookBitboard() | position.getBDragonBitboard() | position.getWRookBitboard() | position.getWDragonBitboard()); bb |= (MoveTables::diagR45(occR45, to) | MoveTables::diagL45(occL45, to)) & (position.getBBishopBitboard() | position.getBHorseBitboard() | position.getWBishopBitboard() | position.getWHorseBitboard()); bb |= MoveTables::king(to) & (position.getBDragonBitboard() | position.getWDragonBitboard()); // TODO: king bb.unset(from); return bb; }
Bitboard operator ^= (const Bitboard& rhs) { #if defined (HAVE_SSE2) || defined (HAVE_SSE4) _mm_store_si128(&this->m_, _mm_xor_si128(this->m_, rhs.m_)); #else this->p_[0] ^= rhs.p(0); this->p_[1] ^= rhs.p(1); #endif return *this; }
void EvalList::set(const Position& pos) { const Hand handB = pos.hand(Black); const Hand handW = pos.hand(White); int nlist = 0; auto func = [&nlist, this](const Hand hand, const HandPiece hp, const int list0_index, const int list1_index, const Color c) { for (u32 i = 1; i <= hand.numOf(hp); ++i) { list0[nlist] = list0_index + i; list1[nlist] = list1_index + i; const Square squarehand = HandPieceToSquareHand[c][hp] + static_cast<Square>(i); listToSquareHand[nlist] = squarehand; squareHandToList[squarehand] = nlist; ++nlist; } }; func(handB, HPawn , f_hand_pawn , e_hand_pawn , Black); func(handW, HPawn , e_hand_pawn , f_hand_pawn , White); func(handB, HLance , f_hand_lance , e_hand_lance , Black); func(handW, HLance , e_hand_lance , f_hand_lance , White); func(handB, HKnight, f_hand_knight, e_hand_knight, Black); func(handW, HKnight, e_hand_knight, f_hand_knight, White); func(handB, HSilver, f_hand_silver, e_hand_silver, Black); func(handW, HSilver, e_hand_silver, f_hand_silver, White); func(handB, HGold , f_hand_gold , e_hand_gold , Black); func(handW, HGold , e_hand_gold , f_hand_gold , White); func(handB, HBishop, f_hand_bishop, e_hand_bishop, Black); func(handW, HBishop, e_hand_bishop, f_hand_bishop, White); func(handB, HRook , f_hand_rook , e_hand_rook , Black); func(handW, HRook , e_hand_rook , f_hand_rook , White); Bitboard bb = pos.bbOf(King).notThisAnd(pos.occupiedBB()); while (bb.isNot0()) { const Square sq = bb.firstOneFromSQ11(); const Piece pc = pos.piece(sq); listToSquareHand[nlist] = sq; squareHandToList[sq] = nlist; list0[nlist ] = kppArray[pc ] + sq; list1[nlist++] = kppArray[inverse(pc)] + inverse(sq); } }
// square の位置の rook, bishop それぞれのMagic Bitboard に使用するマジックナンバーを見つける。 // isBishop : true なら bishop, false なら rook のマジックナンバーを見つける。 u64 findMagic(const Square square, const bool isBishop) { Bitboard occupied[1<<14]; Bitboard attack[1<<14]; Bitboard attackUsed[1<<14]; Bitboard mask = (isBishop ? bishopBlockMaskCalc(square) : rookBlockMaskCalc(square)); int num1s = (isBishop ? BishopBlockBits[square] : RookBlockBits[square]); // n bit の全ての数字 (利きのあるマスの全ての 0 or 1 の組み合わせ) for (int i = 0; i < (1 << num1s); ++i) { occupied[i] = indexToOccupied(i, num1s, mask); attack[i] = attackCalc(square, occupied[i], isBishop); } for (u64 k = 0; k < UINT64_C(100000000); ++k) { const u64 magic = g_mt64bit.randomFewBits(); bool fail = false; // これは無くても良いけど、少しマジックナンバーが見つかるのが早くなるはず。 if (count1s((mask.merge() * magic) & UINT64_C(0xfff0000000000000)) < 6) continue; std::fill(std::begin(attackUsed), std::end(attackUsed), allZeroBB()); for (int i = 0; !fail && i < (1 << num1s); ++i) { const int shiftBits = (isBishop ? BishopShiftBits[square] : RookShiftBits[square]); const u64 index = occupiedToIndex(occupied[i], magic, shiftBits); if (attackUsed[index] == allZeroBB()) attackUsed[index] = attack[i]; else if (attackUsed[index] != attack[i]) fail = true; } if (!fail) return magic; } std::cout << "/***Failed***/\t"; return 0; }
int Evaluation::evaluateKingSafety(int color, Position& position) { assert(Color::isValid(color)); auto pawns = position.pieces[color][PieceType::PAWN].squares; auto rooks = position.pieces[color][PieceType::ROOK].squares; int square = Bitboard::next(position.pieces[color][PieceType::KING].squares); Bitboard pawnShield; if (color == Color::WHITE) { if (Square::isValid(square + Square::NE)) { pawnShield.add(square + Square::NE); } if (Square::isValid(square + Square::N)) { pawnShield.add(square + Square::N); } if (Square::isValid(square + Square::NW)) { pawnShield.add(square + Square::NW); } } else { if (Square::isValid(square + Square::SE)) { pawnShield.add(square + Square::SE); } if (Square::isValid(square + Square::S)) { pawnShield.add(square + Square::S); } if (Square::isValid(square + Square::SW)) { pawnShield.add(square + Square::SW); } } int pawnShieldCount = Bitboard::bitCount(pawnShield.squares & pawns); int rookShieldCount = 0; for (auto rookSquares = position.pieces[color][PieceType::ROOK].squares; rookSquares != 0; rookSquares = Bitboard::remainder(rookSquares)) { int rookSquare = Bitboard::next(rookSquares); for (auto rookDirection : Square::rookDirections) { int targetSquare = rookSquare + rookDirection; int cover = 0; while (Square::isValid(targetSquare)) { for (auto kingDirection : Square::kingDirections) { if (targetSquare == square + kingDirection) rookShieldCount++; } if (position.board[targetSquare] != Piece::NOPIECE) { cover++; } if (cover < 2) { targetSquare += rookDirection; } else { break; } } } } return pawnShieldCount * 20 + rookShieldCount * 10; }
Bitboard SEE::extractShadowAttacker(const Position& position, Bitboard bb, Square from, Square to) { Direction dir = from.dir(to); if (dir >= Direction::EndS) { return bb; } Bitboard occ = nosseOr(position.getBOccupiedBitboard(), position.getWOccupiedBitboard()); RotatedBitboard occ90 = position.get90RotatedBitboard(); RotatedBitboard occR45 = position.getRight45RotatedBitboard(); RotatedBitboard occL45 = position.getLeft45RotatedBitboard(); Bitboard masked; switch (dir) { case Direction::Up: case Direction::Down: masked = MoveTables::ver(occ, from); break; case Direction::Left: case Direction::Right: masked = MoveTables::hor(occ90, from); break; case Direction::RightUp: case Direction::LeftDown: masked = MoveTables::diagR45(occR45, to); break; case Direction::LeftUp: case Direction::RightDown: masked = MoveTables::diagL45(occL45, to); break; default: ASSERT(false); } BB_EACH(square, masked) { Piece piece = position.getPieceOnBoard(square); if (square.dir(from) == dir && MoveTables::isMovableInLongStep(piece, dir)) { bb.set(square); break; } }
MoveStack* generateDropMoves20151211(MoveStack* moveStackList, const Position& pos, const Bitboard& target) { const Hand hand = pos.hand(US); // まず、歩に対して指し手を生成 if (hand.exists<HPawn>()) { Bitboard toBB = target; // 一段目には打てない const Rank TRank9 = (US == Black ? Rank9 : Rank1); toBB.andEqualNot(rankMask<TRank9>()); // 二歩の回避 Bitboard pawnsBB = pos.bbOf(Pawn, US); Square pawnsSquare; foreachBB(pawnsBB, pawnsSquare, [&](const int part) { toBB.set(part, toBB.p(part) & ~squareFileMask(pawnsSquare).p(part)); }); // 打ち歩詰めの回避 const Rank TRank1 = (US == Black ? Rank1 : Rank9); const SquareDelta TDeltaS = (US == Black ? DeltaS : DeltaN); const Square ksq = pos.kingSquare(oppositeColor(US)); // 相手玉が九段目なら、歩で王手出来ないので、打ち歩詰めを調べる必要はない。 if (makeRank(ksq) != TRank1) { const Square pawnDropCheckSquare = ksq + TDeltaS; assert(isInSquare(pawnDropCheckSquare)); if (toBB.isSet(pawnDropCheckSquare) && pos.piece(pawnDropCheckSquare) == Empty) { if (!pos.isPawnDropCheckMate(US, pawnDropCheckSquare)) { // ここで clearBit だけして MakeMove しないことも出来る。 // 指し手が生成される順番が変わり、王手が先に生成されるが、後で問題にならないか? (*moveStackList++).move = makeDropMove(Pawn, pawnDropCheckSquare); } toBB.xorBit(pawnDropCheckSquare); } } Square to; FOREACH_BB(toBB, to, { (*moveStackList++).move = makeDropMove(Pawn, to); });
FORCE_INLINE MoveStack* generateBishopOrRookMoves(MoveStack* moveStackList, const Position& pos, const Bitboard& target, const Square /*ksq*/) { Bitboard fromBB = pos.bbOf(PT, US); while (fromBB.isNot0()) { const Square from = fromBB.firstOneFromI9(); const bool fromCanPromote = canPromote(US, makeRank(from)); Bitboard toBB = pos.attacksFrom<PT>(US, from) & target; while (toBB.isNot0()) { const Square to = toBB.firstOneFromI9(); const bool toCanPromote = canPromote(US, makeRank(to)); if (fromCanPromote | toCanPromote) { (*moveStackList++).move = makePromoteMove<MT>(PT, from, to, pos); if (MT == NonEvasion || ALL) (*moveStackList++).move = makeNonPromoteMove<MT>(PT, from, to, pos); } else // 角、飛車は成れるなら成り、不成は生成しない。 (*moveStackList++).move = makeNonPromoteMove<MT>(PT, from, to, pos); } } return moveStackList; }
// Bitboard関連の各種テーブルの初期化。 void Bitboards::init() { // ------------------------------------------------------------ // Bitboard関係のテーブルの初期化 // ------------------------------------------------------------ // 1) SquareWithWallテーブルの初期化。 for (auto sq : SQ) sqww_table[sq] = SquareWithWall(SQWW_11 + (int32_t)file_of(sq) * SQWW_L + (int32_t)rank_of(sq) * SQWW_D); // 2) direct_tableの初期化 for (auto sq1 : SQ) for (auto dir = Effect8::DIRECT_ZERO; dir < Effect8::DIRECT_NB; ++dir) { // dirの方角に壁にぶつかる(盤外)まで延長していく。このとき、sq1から見てsq2のDirectionsは (1 << dir)である。 auto delta = Effect8::DirectToDeltaWW(dir); for (auto sq2 = to_sqww(sq1) + delta; is_ok(sq2); sq2 += delta) Effect8::direc_table[sq1][sqww_to_sq(sq2)] = Effect8::to_directions(dir); } // 3) Square型のsqの指す升が1であるBitboardがSquareBB。これをまず初期化する。 for (auto sq : SQ) { Rank r = rank_of(sq); File f = file_of(sq); SquareBB[sq].p[0] = (f <= FILE_7) ? ((uint64_t)1 << (f * 9 + r)) : 0; SquareBB[sq].p[1] = (f >= FILE_8) ? ((uint64_t)1 << ((f - FILE_8) * 9 + r)) : 0; } // 4) 遠方利きのテーブルの初期化 // thanks to Apery (Takuya Hiraoka) // 引数のindexをbits桁の2進数としてみなす。すなわちindex(0から2^bits-1)。 // 与えられたmask(1の数がbitsだけある)に対して、1のbitのいくつかを(indexの値に従って)0にする。 auto indexToOccupied = [](const int index, const int bits, const Bitboard& mask_) { auto mask = mask_; auto result = ZERO_BB; for (int i = 0; i < bits; ++i) { const Square sq = mask.pop(); if (index & (1 << i)) result ^= sq; } return result; }; // Rook or Bishop の利きの範囲を調べて bitboard で返す。 // occupied 障害物があるマスが 1 の bitboard // n = 0 右上から左下 , n = 1 左上から右下 auto effectCalc = [](const Square square, const Bitboard& occupied, int n) { auto result = ZERO_BB; // 角の利きのrayと飛車の利きのray const SquareWithWall deltaArray[2][2] = { { SQWW_RU, SQWW_LD },{ SQWW_RD, SQWW_LU} }; for (auto delta : deltaArray[n]) { // 壁に当たるまでsqを利き方向に伸ばしていく for (auto sq = to_sqww(square) + delta; is_ok(sq); sq += delta) { result ^= sqww_to_sq(sq); // まだ障害物に当っていないのでここまでは利きが到達している if (occupied & sqww_to_sq(sq)) // sqの地点に障害物があればこのrayは終了。 break; } } return result; }; // pieceをsqにおいたときに利きを得るのに関係する升を返す auto calcBishopEffectMask = [](Square sq, int n) { Bitboard result; result = ZERO_BB; // 外周は角の利きには関係ないのでそこは除外する。 for (Rank r = RANK_2; r <= RANK_8; ++r) for (File f = FILE_2; f <= FILE_8; ++f) { auto dr = rank_of(sq) - r; auto df = file_of(sq) - f; // dr == dfとdr != dfとをnが0,1とで切り替える。 if (abs(dr) == abs(df) && (!!((int)dr == (int)df) ^ n )) result ^= (f | r); } // sqの地点は関係ないのでクリアしておく。 result &= ~Bitboard(sq); return result; }; // 角の利きテーブルの初期化 for (int n : { 0 , 1 }) { int index = 0; for (auto sq : SQ) { // sqの升に対してテーブルのどこを見るかのindex BishopEffectIndex[n][sq] = index; // sqの地点にpieceがあるときにその利きを得るのに関係する升を取得する auto& mask = BishopEffectMask[n][sq]; mask = calcBishopEffectMask(sq, n); // p[0]とp[1]が被覆していると正しく計算できないのでNG。 // Bitboardのレイアウト的に、正しく計算できるかのテスト。 // 縦型Bitboardであるならp[0]のbit63を余らせるようにしておく必要がある。 ASSERT_LV3(!(mask.cross_over())); // sqの升用に何bit情報を拾ってくるのか const int bits = mask.pop_count(); // 参照するoccupied bitboardのbit数と、そのbitの取りうる状態分だけ.. const int num = 1 << bits; for (int i = 0; i < num; ++i) { Bitboard occupied = indexToOccupied(i, bits, mask); // 初期化するテーブル BishopEffect[n][index + occupiedToIndex(occupied & mask, mask)] = effectCalc(sq, occupied, n); } index += num; } // 盤外(SQ_NB)に駒を配置したときに利きがZERO_BBとなるときのための処理 BishopEffectIndex[n][SQ_NB] = index; // 何番まで使ったか出力してみる。(確保する配列をこのサイズに収めたいので) // cout << index << endl; } // 5. 飛車の縦方向の利きテーブルの初期化 // ここでは飛車の利きを使わずに初期化しないといけない。 for (Rank rank = RANK_1; rank <= RANK_9 ; ++rank) { // sq = SQ_11 , SQ_12 , ... , SQ_19 Square sq = FILE_1 | rank; const int num1s = 7; for (int i = 0; i < (1 << num1s); ++i) { // iはsqに駒をおいたときに、その筋の2段~8段目の升がemptyかどうかを表現する値なので // 1ビットシフトして、1~9段目の升を表現するようにする。 int ii = i << 1; Bitboard bb = ZERO_BB; for (int r = rank_of(sq) - 1; r >= RANK_1; --r) { bb |= file_of(sq) | (Rank)r; if (ii & (1 << r)) break; } for (int r = rank_of(sq) + 1; r <= RANK_9; ++r) { bb |= file_of(sq) | (Rank)r; if (ii & (1 << r)) break; } RookFileEffect[rank][i] = bb.p[0]; // RookEffectFile[RANK_NB][x] には値を代入していないがC++の規約によりゼロ初期化されている。 } } // 飛車の横の利き for (File file = FILE_1 ; file <= FILE_9 ; ++file ) { // sq = SQ_11 , SQ_21 , ... , SQ_NBまで Square sq = file | RANK_1; const int num1s = 7; for (int i = 0; i < (1 << num1s); ++i) { int ii = i << 1; Bitboard bb = ZERO_BB; for (int f = file_of(sq) - 1; f >= FILE_1; --f) { bb |= (File)f | rank_of(sq); if (ii & (1 << f)) break; } for (int f = file_of(sq) + 1; f <= FILE_9; ++f) { bb |= (File)f | rank_of(sq); if (ii & (1 << f)) break; } RookRankEffect[file][i] = bb; // RookRankEffect[FILE_NB][x] には値を代入していないがC++の規約によりゼロ初期化されている。 } } // 6. 近接駒(+盤上の利きを考慮しない駒)のテーブルの初期化。 // 上で初期化した、香・馬・飛の利きを用いる。 for (auto sq : SQ) { // 玉は長さ1の角と飛車の利きを合成する KingEffectBB[sq] = bishopEffect(sq, ALL_BB) | rookEffect(sq, ALL_BB); } for (auto c : COLOR) for(auto sq : SQ) // 障害物がないときの香の利き // これを最初に初期化しないとlanceEffect()が使えない。 LanceStepEffectBB[sq][c] = rookFileEffect(sq,ZERO_BB) & ForwardRanksBB[c][rank_of(sq)]; for (auto c : COLOR) for (auto sq : SQ) { // 歩は長さ1の香の利きとして定義できる PawnEffectBB[sq][c] = lanceEffect(c, sq, ALL_BB); // 桂の利きは、歩の利きの地点に長さ1の角の利きを作って、前方のみ残す。 Bitboard tmp = ZERO_BB; Bitboard pawn = lanceEffect(c, sq, ALL_BB); if (pawn) { Square sq2 = pawn.pop(); Bitboard pawn2 = lanceEffect(c, sq2, ALL_BB); // さらに1つ前 if (pawn2) tmp = bishopEffect(sq2, ALL_BB) & RANK_BB[rank_of(pawn2.pop())]; } KnightEffectBB[sq][c] = tmp; // 銀は長さ1の角の利きと長さ1の香の利きの合成として定義できる。 SilverEffectBB[sq][c] = lanceEffect(c, sq, ALL_BB) | bishopEffect(sq, ALL_BB); // 金は長さ1の角と飛車の利き。ただし、角のほうは相手側の歩の行き先の段でmaskしてしまう。 Bitboard e_pawn = lanceEffect(~c, sq, ALL_BB); Bitboard mask = ZERO_BB; if (e_pawn) mask = RANK_BB[rank_of(e_pawn.pop())]; GoldEffectBB[sq][c]= (bishopEffect(sq, ALL_BB) & ~mask) | rookEffect(sq, ALL_BB); // 障害物がないときの角と飛車の利き BishopStepEffectBB[sq] = bishopEffect(sq, ZERO_BB); RookStepEffectBB[sq] = rookEffect(sq, ZERO_BB); // --- 以下のbitboard、あまり頻繁に呼び出さないので他のbitboardを合成して代用する。 // 盤上の駒がないときのqueenの利き // StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_QUEEN] = bishopEffect(sq, ZERO_BB) | rookEffect(sq, ZERO_BB); // 長さ1の十字 // StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_CROSS00] = rookEffect(sq, ALL_BB); // 長さ1の斜め // StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_CROSS45] = bishopEffect(sq, ALL_BB); } // 7) 二歩用のテーブル初期化 for (int i = 0; i < 0x80; ++i) { Bitboard b = ZERO_BB; for (int k = 0; k < 7; ++k) if ((i & (1 << k)) == 0) b |= FILE_BB[k]; PAWN_DROP_MASK_BB[i].p[0] = b.p[0]; // 1~7筋 } for (int i = 0; i < 0x4; ++i) { Bitboard b = ZERO_BB; for (int k = 0; k < 2; ++k) if ((i & (1 << k)) == 0) b |= FILE_BB[k+7]; PAWN_DROP_MASK_BB[i].p[1] = b.p[1]; // 8,9筋 } // 8) BetweenBB , LineBBの初期化 { u16 between_index = 1; // BetweenBB[0] == ZERO_BBであることを保証する。 for (auto s1 : SQ) for (auto s2 : SQ) { // 十字方向か、斜め方向かだけを判定して、例えば十字方向なら // rookEffect(sq1,Bitboard(s2)) & rookEffect(sq2,Bitboard(s1)) // のように初期化したほうが明快なコードだが、この初期化をそこに依存したくないので愚直にやる。 // これについてはあとで設定する。 if (s1 >= s2) continue; // 方角を用いるテーブルの初期化 if (Effect8::directions_of(s1, s2)) { Bitboard bb = ZERO_BB; // 間に挟まれた升を1に Square delta = (s2 - s1) / dist(s1, s2); for (Square s = s1 + delta; s != s2; s += delta) bb |= s; // ZERO_BBなら、このindexとしては0を指しておけば良いので書き換える必要ない。 if (!bb) continue; BetweenIndex[s1][s2] = between_index; BetweenBB[between_index++] = bb; } } ASSERT_LV1(between_index == 785); // 対称性を考慮して、さらにシュリンクする。 for (auto s1 : SQ) for (auto s2 : SQ) if (s1 > s2) BetweenIndex[s1][s2] = BetweenIndex[s2][s1]; } for (auto s1 : SQ) for (int d = 0; d < 4; ++d) { // BishopEffect0 , RookRankEffect , BishopEffect1 , RookFileEffectを用いて初期化したほうが // 明快なコードだが、この初期化をそこに依存したくないので愚直にやる。 const Square deltas[4] = { SQ_RU , SQ_R , SQ_RD , SQ_U }; const Square delta = deltas[d]; Bitboard bb = Bitboard(s1); // 壁に当たるまでs1から-delta方向に延長 for (Square s = s1; dist(s, s - delta) <= 1; s -= delta) bb |= (s - delta); // 壁に当たるまでs1から+delta方向に延長 for (Square s = s1; dist(s, s + delta) <= 1; s += delta) bb |= (s + delta); LineBB[s1][d] = bb; } // 9) 王手となる候補の駒のテーブル初期化(王手の指し手生成に必要。やねうら王nanoでは削除予定) #define FOREACH_KING(BB, EFFECT ) { for(auto sq : BB){ target|= EFFECT(sq); } } #define FOREACH(BB, EFFECT ) { for(auto sq : BB){ target|= EFFECT(them,sq); } } #define FOREACH_BR(BB, EFFECT ) { for(auto sq : BB) { target|= EFFECT(sq,ZERO_BB); } } for (auto Us : COLOR) for (auto ksq : SQ) { Color them = ~Us; auto enemyGold = goldEffect(them, ksq) & enemy_field(Us); Bitboard target; // 歩で王手になる可能性のあるものは、敵玉から2つ離れた歩(不成での移動) + ksqに敵の金をおいた範囲(enemyGold)に成りで移動できる target = ZERO_BB; FOREACH(pawnEffect(them, ksq), pawnEffect); FOREACH(enemyGold, pawnEffect); CheckCandidateBB[ksq][PAWN - 1][Us] = target & ~Bitboard(ksq); // 香で王手になる可能性のあるものは、ksqに敵の香をおいたときの利き。(盤上には何もないものとする) // と、王が1から3段目だと成れるので王の両端に香を置いた利きも。 target = lanceStepEffect(them, ksq); if (enemy_field(Us) & ksq) { if (file_of(ksq) != FILE_1) target |= lanceStepEffect(them, ksq + SQ_R); if (file_of(ksq) != FILE_9) target |= lanceStepEffect(them, ksq + SQ_L); } CheckCandidateBB[ksq][LANCE - 1][Us] = target; // 桂で王手になる可能性のあるものは、ksqに敵の桂をおいたところに移動できる桂(不成) + ksqに金をおいた範囲(enemyGold)に成りで移動できる桂 target = ZERO_BB; FOREACH(knightEffect(them, ksq) | enemyGold, knightEffect); CheckCandidateBB[ksq][KNIGHT - 1][Us] = target & ~Bitboard(ksq); // 銀も同様だが、2,3段目からの引き成りで王手になるパターンがある。(4段玉と5段玉に対して) target = ZERO_BB; FOREACH(silverEffect(them, ksq), silverEffect); FOREACH(enemyGold, silverEffect); // 移動先が敵陣 == 成れる == 金になるので、敵玉の升に敵の金をおいた利きに成りで移動すると王手になる。 FOREACH(goldEffect(them, ksq), enemy_field(Us) & silverEffect); // 移動元が敵陣 == 成れる == 金になるので、敵玉の升に敵の金をおいた利きに成りで移動すると王手になる。 CheckCandidateBB[ksq][SILVER - 1][Us] = target & ~Bitboard(ksq); // 金 target = ZERO_BB; FOREACH(goldEffect(them, ksq), goldEffect); CheckCandidateBB[ksq][GOLD - 1][Us] = target & ~Bitboard(ksq); // 角 target = ZERO_BB; FOREACH_BR(bishopEffect(ksq, ZERO_BB), bishopEffect); FOREACH_BR(kingEffect(ksq) & enemy_field(Us), bishopEffect); // 移動先が敵陣 == 成れる == 王の動き FOREACH_BR(kingEffect(ksq), enemy_field(Us) & bishopEffect); // 移動元が敵陣 == 成れる == 王の動き CheckCandidateBB[ksq][BISHOP - 1][Us] = target & ~Bitboard(ksq); // 飛・龍は無条件全域。 // ROOKのところには馬のときのことを格納 // 馬 target = ZERO_BB; FOREACH_BR(horseEffect(ksq, ZERO_BB), horseEffect); CheckCandidateBB[ksq][ROOK - 1][Us] = target & ~Bitboard(ksq); // 王(24近傍が格納される) target = ZERO_BB; FOREACH_KING(kingEffect(ksq), kingEffect); CheckCandidateKingBB[ksq] = target & ~Bitboard(ksq); } // 10. LONG_EFFECT_LIBRARYの初期化 #ifdef LONG_EFFECT_LIBRARY LongEffect::init(); #endif // 11. 1手詰めテーブルの初期化 #ifdef USE_MATE_1PLY Mate1Ply::init(); #endif }
bool Mate::isProtected_(const Board& board, const Square to, const Bitboard& occ, const Bitboard& occNoAttacker, const Square king) { // pawn Bitboard bb = (black ? board.getBPawn() : board.getWPawn()) & occNoAttacker; if (bb.check(black ? to.safetyDown() : to.safetyUp())) { return true; } // lance bb = (black ? board.getBLance() : board.getWLance()) & occNoAttacker; bb &= black ? MoveTables::wlance(to, occ) : MoveTables::blance(to, occ); if (bb) { return true; } // knight bb = (black ? board.getBKnight() : board.getWKnight()) & occNoAttacker; bb &= black ? MoveTables::wknight(to) : MoveTables::bknight(to); if (bb) { return true; } // silver bb = (black ? board.getBSilver() : board.getWSilver()) & occNoAttacker; bb &= black ? MoveTables::wsilver(to) : MoveTables::bsilver(to); if (bb) { return true; } // gold bb = (black ? board.getBGold() : board.getWGold()) & occNoAttacker; bb &= black ? MoveTables::wgold(to) : MoveTables::bgold(to); if (bb) { return true; } // bishop bb = (black ? board.getBBishop() : board.getWBishop()) & occNoAttacker; bb &= MoveTables::bishop(to, occ); if (bb) { return true; } // rook bb = (black ? board.getBRook() : board.getWRook()) & occNoAttacker; bb &= MoveTables::rook(to, occ); if (bb) { return true; } // tokin bb = (black ? board.getBTokin() : board.getWTokin()) & occNoAttacker; bb &= black ? MoveTables::wgold(to) : MoveTables::bgold(to); if (bb) { return true; } // promoted lance bb = (black ? board.getBProLance() : board.getWProLance()) & occNoAttacker; bb &= black ? MoveTables::wgold(to) : MoveTables::bgold(to); if (bb) { return true; } // promoted knight bb = (black ? board.getBProKnight() : board.getWProKnight()) & occNoAttacker; bb &= black ? MoveTables::wgold(to) : MoveTables::bgold(to); if (bb) { return true; } // promoted silver bb = (black ? board.getBProSilver() : board.getWProSilver()) & occNoAttacker; bb &= black ? MoveTables::wgold(to) : MoveTables::bgold(to); if (bb) { return true; } // horse bb = (black ? board.getBHorse() : board.getWHorse()) & occNoAttacker; bb &= MoveTables::horse(to, occ); if (bb) { return true; } // dragon bb = (black ? board.getBDragon() : board.getWDragon()) & occNoAttacker; bb &= MoveTables::dragon(to, occ); if (bb) { return true; } // king if (king.isValid()) { if (MoveTables::king(king).check(to) && (!recursive || !isProtected_<!black>(board, to, occ, occNoAttacker, Square::Invalid))) { return true; } } return false; }
void Notation::image(const Board & b, const Move & m, OutputFormat format, ostream &image) { if (format == UCI) { return UCIMoveImage(m,image); } else if (format == WB_OUT) { if (TypeOfMove(m) == KCastle) { image << "O-O"; } else if (TypeOfMove(m) == QCastle) { image << "O-O-O"; } else { image << FileImage(StartSquare(m)); image << RankImage(StartSquare(m)); image << FileImage(DestSquare(m)); image << RankImage(DestSquare(m)); if (TypeOfMove(m) == Promotion) { // N.b. ICS requires lower case. image << (char)tolower((int)PieceImage(PromoteTo(m))); } } return; } // format is SAN if (IsNull(m)) { image << "(null)"; return; } PieceType p = PieceMoved(m); ASSERT(p != Empty); if (TypeOfMove(m) == KCastle) { image << "O-O"; } else if (TypeOfMove(m) == QCastle) { image << "O-O-O"; } else { if (p == Pawn) { if (Capture(m) == Empty) { image << FileImage(DestSquare(m)); image << RankImage(DestSquare(m)); } else { image << FileImage(StartSquare(m)); image << 'x'; image << FileImage(DestSquare(m)); image << RankImage(DestSquare(m)); } if (TypeOfMove(m) == Promotion) { image << '='; image << PieceImage(PromoteTo(m)); } } else { image << PieceImage(p); Bitboard attacks = b.calcAttacks(DestSquare(m), b.sideToMove()); unsigned n = attacks.bitCount(); int dups = 0; int filedups = 0; int rankdups = 0; int files[9]; int ranks[9]; if (n > 1) { Square sq; while (attacks.iterate(sq)) { if (TypeOfPiece(b[sq]) == p) { files[dups] = File(sq); if (files[dups] == File(StartSquare(m))) filedups++; ranks[dups] = Rank(sq,White); if (ranks[dups] == Rank(StartSquare(m),White)) rankdups++; ++dups; } } } if (dups > 1) { // need to disambiguate move. if (filedups == 1) { image << FileImage(StartSquare(m)); } else if (rankdups == 1) { image << RankImage(StartSquare(m)); } else { // need both rank and file to disambiguate image << FileImage(StartSquare(m)); image << RankImage(StartSquare(m)); } } if (Capture(m) != Empty) { image << 'x'; } image << FileImage(DestSquare(m)); image << RankImage(DestSquare(m)); } } Board board_copy(b); board_copy.doMove(m); if (board_copy.checkStatus() == InCheck) { Move moves[Constants::MaxMoves]; MoveGenerator mg(board_copy); if (mg.generateEvasions(moves)) image << '+'; else image << '#'; // mate } }
Move Notation::value(const Board & board, ColorType side, InputFormat format, const string &image) { int rank = 0; int file = 0; PieceType piece = Empty; PieceType promotion = Empty; Square dest = InvalidSquare, start = InvalidSquare; int capture = 0; stringstream s(image); string::const_iterator it = image.begin(); int i = 0; while (it != image.end() && isspace(*it)) { it++; i++; } if (it == image.end() || !isalpha(*it)) return NullMove; string img(image,i); // string w/o leading spaces ASSERT(img.length()); it = img.begin(); if (*it == 'O' || *it == '0') { // castling, we presume return parseCastling(board, side, img); } else if (format == WB_IN) { if (img.length() < 4) return NullMove; Square start = SquareValue(img.substr(0,2)); if (!OnBoard(start)) return NullMove; Square dest = SquareValue(img.substr(2,2)); if (!OnBoard(dest)) return NullMove; PieceType promotion = Empty; if (img.length() > 4) { promotion = PieceCharValue(toupper(img[4])); } return CreateMove(board,start,dest,promotion); } int have_start = 0; if (isupper(*it)) { piece = PieceCharValue(*it); it++; } else { piece = Pawn; if ((it+1) != img.end()) { char next = *it; file = next-'a'+1; if (file < 1 || file > 8) return NullMove; char next2 = *(it+1); if (next2 == 'x' || is_file(next2)) { // allow "dc4" as in Informant, instead of dxc4 it++; capture = 1; } else if (isdigit(next2) && img.length()>2) { char next3 = *(it+2); if ((next3 == 'x' || next3 == '-') && img.length()>=5) { // long algebraic notation have_start++; start = SquareValue(next,next2); if (start == InvalidSquare) return NullMove; it+=3; // point to dest piece = TypeOfPiece(board[start]); } } } } if (piece == Empty) { return NullMove; } if (piece != Pawn && !have_start && it != img.end()) { char next = *it; char next2 = '\0'; if (it + 1 != img.end()) next2 = *(it+1); if (is_file(next) && isdigit(next2) && img.length()>=5) { // long algebraic notation, or a SAN move like Qd1d3 start = SquareValue(next,next2); if (IsInvalid(start)) return NullMove; it+=2; have_start++; } // also look for disambiguating rank, e.g. '2' in "N2e4". else if (isdigit(next)) { rank = next - '0'; if (rank < 1 || rank > 8) return NullMove; it++; } else if (is_file(next) && isalpha(next2)) { // disamiguating rank, e.g. "Rfd1" file = next - 'a' + 1; if (file < 1 || file > 8) return NullMove; it++; } } if (it != img.end() && *it == 'x') { capture = 1; it++; } if (it != img.end() && (it+1) != img.end()) { // remainder of move should be a square identifier, e.g. "g7" dest = SquareValue(*it,*(it+1)); it += 2; } if (IsInvalid(dest)) { return NullMove; } if (it != img.end() && *it == '=') { it++; if (it == img.end()) { return NullMove; } else { promotion = PieceCharValue(*it); if (piece != Pawn || promotion == Empty) return NullMove; it++; } } else if (piece == Pawn && it != img.end() && isupper(*it)) { // Quite a few "PGN" files have a8Q instead of a8=Q. promotion = PieceCharValue(*it); if (promotion == Empty || Rank(dest,side) != 8) return NullMove; } else if (piece == Pawn && Rank(dest,side) == 8) { // promotion but no piece specified, treat as error return NullMove; } // Informant does not use "x" for captures. Assume that if the destination // is occupied, this is a capture move. if (board[dest] != EmptyPiece) { capture = 1; } // Do a sanity check on capture moves: if (capture && !IsEmptyPiece(board[dest]) && PieceColor(board[dest]) == board.sideToMove()) { return NullMove; } // Ok, now we need to figure out where the start square is. For pawn // moves this is implicit. int dups = 0; if (!have_start) { if (capture && piece == Pawn && IsEmptyPiece(board[dest]) && Rank(dest,board.sideToMove()) != 8) { // en passant capture, special case int start_rank = (board.sideToMove() == White) ? Rank(dest,White) - 1 : Rank(dest,White) + 1; start = MakeSquare(file, start_rank, White); dups = 1; } else if (piece == Pawn && board[dest] == EmptyPiece) { start = MakeSquare(file,Rank(dest,board.sideToMove())-1,board.sideToMove()); if (board[start] == EmptyPiece && Rank(dest,board.sideToMove())==4) { start = MakeSquare(file,Rank(dest,board.sideToMove())-2,board.sideToMove()); } if (board[start] == EmptyPiece) return NullMove; dups = 1; } else { Bitboard attacks = board.calcAttacks(dest,side); Square maybe; while (attacks.iterate(maybe)) { if (TypeOfPiece(board[maybe]) == piece && PieceColor(board[maybe]) == board.sideToMove()) { if (file && File(maybe) != file) continue; if (rank && Rank(maybe,White) != rank) continue; if (PieceColor(board[maybe]) == board.sideToMove()) { // Possible move to this square. Make sure it is legal. Board board_copy(board); Move emove = CreateMove(board,maybe,dest, promotion); board_copy.doMove(emove); if (!board_copy.anyAttacks( board_copy.kingSquare(board_copy.oppositeSide()), board_copy.sideToMove())) { ++dups; start = maybe; } } } } } } if (dups == 1 || have_start) { if (start == InvalidSquare || board[start] == EmptyPiece) return NullMove; else return CreateMove(board, start, dest, promotion); } else // ambiguous move return NullMove; }
// Bitboard関連の各種テーブルの初期化。 void Bitboards::init() { // ------------------------------------------------------------ // Bitboard関係のテーブルの初期化 // ------------------------------------------------------------ // 1) SquareWithWallテーブルの初期化。 for (auto sq : SQ) sqww_table[sq] = SquareWithWall(SQWW_11 + (int32_t)file_of(sq) * SQWW_L + (int32_t)rank_of(sq) * SQWW_D); // 2) direct_tableの初期化 for (auto sq1 : SQ) for (auto dir = Effect8::DIRECT_ZERO; dir < Effect8::DIRECT_NB; ++dir) { // dirの方角に壁にぶつかる(盤外)まで延長していく。このとき、sq1から見てsq2のDirectionsは (1 << dir)である。 auto delta = Effect8::DirectToDeltaWW(dir); for (auto sq2 = to_sqww(sq1) + delta; is_ok(sq2); sq2 += delta) Effect8::direc_table[sq1][sqww_to_sq(sq2)] = Effect8::to_directions(dir); } // 3) Square型のsqの指す升が1であるBitboardがSquareBB。これをまず初期化する。 for (auto sq : SQ) { Rank r = rank_of(sq); File f = file_of(sq); SquareBB[sq].p[0] = (f <= FILE_7) ? ((uint64_t)1 << (f * 9 + r)) : 0; SquareBB[sq].p[1] = (f >= FILE_8) ? ((uint64_t)1 << ((f - FILE_8) * 9 + r)) : 0; } // 4) 遠方利きのテーブルの初期化 // thanks to Apery (Takuya Hiraoka) // 引数のindexをbits桁の2進数としてみなす。すなわちindex(0から2^bits-1)。 // 与えられたmask(1の数がbitsだけある)に対して、1のbitのいくつかを(indexの値に従って)0にする。 auto indexToOccupied = [](const int index, const int bits, const Bitboard& mask_) { auto mask = mask_; auto result = ZERO_BB; for (int i = 0; i < bits; ++i) { const Square sq = mask.pop(); if (index & (1 << i)) result ^= sq; } return result; }; // Rook or Bishop の利きの範囲を調べて bitboard で返す。 // occupied 障害物があるマスが 1 の bitboard auto effectCalc = [](const Square square, const Bitboard& occupied, const Piece piece) { auto result = ZERO_BB; // 角の利きのrayと飛車の利きのray const SquareWithWall deltaArray[2][4] = { { SQWW_RU, SQWW_RD, SQWW_LU, SQWW_LD },{ SQWW_U, SQWW_D, SQWW_R, SQWW_L } }; for (auto delta : deltaArray[(piece == BISHOP) ? 0 : 1]) // 壁に当たるまでsqを利き方向に伸ばしていく for (auto sq = to_sqww(square) + delta; is_ok(sq) ; sq += delta) { result ^= sqww_to_sq(sq); // まだ障害物に当っていないのでここまでは利きが到達している if (occupied & sqww_to_sq(sq)) // sqの地点に障害物があればこのrayは終了。 break; } return result; }; // pieceをsqにおいたときに利きを得るのに関係する升を返す auto calcEffectMask = [](Square sq, Piece piece) { Bitboard result; if (piece == BISHOP) { result = ZERO_BB; for (Rank r = RANK_2; r <= RANK_8; ++r) for (File f = FILE_2; f <= FILE_8; ++f) // 外周は角の利きには関係ないのでそこは除外する。 if (abs(rank_of(sq) - r) == abs(file_of(sq) - f)) result ^= (f | r); } else { ASSERT_LV3(piece == ROOK); result = RANK_BB[rank_of(sq)] ^ FILE_BB[file_of(sq)]; // 外周に居ない限り、その外周升は利きの計算には関係ない。 if (file_of(sq) != FILE_1) { result &= ~FILE1_BB; } if (file_of(sq) != FILE_9) { result &= ~FILE9_BB; } if (rank_of(sq) != RANK_1) { result &= ~RANK1_BB; } if (rank_of(sq) != RANK_9) { result &= ~RANK9_BB; } } // sqの地点は関係ないのでクリアしておく。 result &= ~Bitboard(sq); return result; }; // 角と飛車の利きテーブルの初期化 for (Piece pc : {BISHOP,ROOK} ) { // 初期化するテーブルのアドレス Bitboard* effects = (pc == BISHOP) ? BishopEffect : RookEffect; // sqの升に対してテーブルのどこを引くかのindex int* effectIndex = (pc == BISHOP) ? BishopEffectIndex : RookEffectIndex; // 利きを得るために関係する升 Bitboard* masks = (pc == BISHOP) ? BishopEffectMask : RookEffectMask; int index = 0; for (auto sq : SQ) { effectIndex[sq] = index; // sqの地点にpieceがあるときにその利きを得るのに関係する升を取得する masks[sq] = calcEffectMask(sq, pc); // p[0]とp[1]が被覆していると正しく計算できないのでNG。 // Bitboardのレイアウト的に、正しく計算できるかのテスト。 // 縦型Bitboardであるならp[0]のbit63を余らせるようにしておく必要がある。 ASSERT_LV3(!(masks[sq].p[0] & masks[sq].p[1])); // sqの升用に何bit情報を拾ってくるのか const int bits = masks[sq].pop_count(); // 参照するoccupied bitboardのbit数と、そのbitの取りうる状態分だけ.. const int num = 1 << bits; for (int i = 0; i < num; ++i) { Bitboard occupied = indexToOccupied(i, bits, masks[sq]); effects[index + occupiedToIndex(occupied & masks[sq], masks[sq])] = effectCalc(sq, occupied, pc); } index += num; } // 盤外(SQ_NB)に駒を配置したときに利きがZERO_BBとなるときのための処理 effectIndex[SQ_NB] = index; } // 5. 香の利きテーブルの初期化 // 上で初期化した飛車の利きを用いる。 for (auto c : COLOR) for (auto sq : SQ) { const Bitboard blockMask = FILE_BB[file_of(sq)] & ~(RANK1_BB | RANK9_BB); const int num1s = 7; for (int i = 0; i < (1 << num1s); ++i) { Bitboard occupied = indexToOccupied(i, num1s, blockMask); LanceEffect[c][sq][i] = rookEffect(sq, occupied) & InFrontBB[c][rank_of(sq)]; } } // 6. 近接駒(+盤上の利きを考慮しない駒)のテーブルの初期化。 // 上で初期化した、香・馬・飛の利きを用いる。 for (auto c : COLOR) for (auto sq : SQ) { // 歩は長さ1の香の利きとして定義できる StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_PAWN] = lanceEffect(c, sq, ALL_BB); // 障害物がないときの香の利き StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_LANCE] = lanceEffect(c, sq, ZERO_BB); // 桂の利きは、歩の利きの地点に長さ1の角の利きを作って、前方のみ残す。 Bitboard tmp = ZERO_BB; Bitboard pawn = lanceEffect(c, sq, ALL_BB); if (pawn) { Square sq2 = pawn.pop(); Bitboard pawn2 = lanceEffect(c, sq2, ALL_BB); // さらに1つ前 if (pawn2) tmp = bishopEffect(sq2, ALL_BB) & RANK_BB[rank_of(pawn2.pop())]; } StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_KNIGHT] = tmp; // 銀は長さ1の角の利きと長さ1の香の利きの合成として定義できる。 StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_SILVER] = lanceEffect(c, sq, ALL_BB) | bishopEffect(sq, ALL_BB); // 金は長さ1の角と飛車の利き。ただし、角のほうは相手側の歩の行き先の段でmaskしてしまう。 Bitboard e_pawn = lanceEffect(~c, sq, ALL_BB); Bitboard mask = ZERO_BB; if (e_pawn) mask = RANK_BB[rank_of(e_pawn.pop())]; StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_GOLD] = (bishopEffect(sq, ALL_BB) & ~mask) | rookEffect(sq, ALL_BB); // 障害物がないときの角と飛車の利き StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_BISHOP] = bishopEffect(sq, ZERO_BB); StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_ROOK] = rookEffect(sq, ZERO_BB); // 玉は長さ1の角と飛車の利きを合成する StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_HDK] = bishopEffect(sq, ALL_BB) | rookEffect(sq, ALL_BB); // 盤上の駒がないときのqueenの利き StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_QUEEN] = bishopEffect(sq, ZERO_BB) | rookEffect(sq, ZERO_BB); // 長さ1の十字 StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_CROSS00] = rookEffect(sq, ALL_BB); // 長さ1の斜め StepEffectsBB[sq][c][PIECE_TYPE_BITBOARD_CROSS45] = bishopEffect(sq, ALL_BB); } // 7) 二歩用のテーブル初期化 for (int i = 0; i <= 0x1ff; ++i) { Bitboard b = ZERO_BB; for (int k = 0; k < 9; ++k) if ((i & (1 << k)) == 0) b |= FILE_BB[k]; PAWN_DROP_MASK_BB[i][BLACK] = b & rank1_n_bb(WHITE, RANK_8); // 2~9段目まで PAWN_DROP_MASK_BB[i][WHITE] = b & rank1_n_bb(BLACK, RANK_8); // 1~8段目まで } // 8) BetweenBB , LineBBの初期化 for (auto s1 : SQ) for (auto s2 : SQ) { // 方角を用いるテーブルの初期化 if (Effect8::directions_of(s1,s2)) { // 間に挟まれた升を1に Square delta = (s2 - s1) / dist(s1 , s2); for (Square s = s1 + delta; s != s2; s += delta) BetweenBB[s1][s2] |= s; // 間に挟まれてない升も1に LineBB[s1][s2] = BetweenBB[s1][s2]; // 壁に当たるまでs1から-delta方向に延長 for (Square s = s1; dist(s, s + delta) <= 1; s -= delta) LineBB[s1][s2] |= s; // 壁に当たるまでs2から+delta方向に延長 for (Square s = s2; dist(s, s - delta) <= 1; s += delta) LineBB[s1][s2] |= s; } } // 9) 王手となる候補の駒のテーブル初期化(王手の指し手生成に必要。やねうら王nanoでは削除予定) #define FOREACH_KING(BB, EFFECT ) { for(auto sq : BB){ target|= EFFECT(sq); } } #define FOREACH(BB, EFFECT ) { for(auto sq : BB){ target|= EFFECT(them,sq); } } #define FOREACH_BR(BB, EFFECT ) { for(auto sq : BB) { target|= EFFECT(sq,ZERO_BB); } } for (auto Us : COLOR) for (auto ksq : SQ) { Color them = ~Us; auto enemyGold = goldEffect(them, ksq) & enemy_field(Us); Bitboard target; // 歩で王手になる可能性のあるものは、敵玉から2つ離れた歩(不成での移動) + ksqに敵の金をおいた範囲(enemyGold)に成りで移動できる target = ZERO_BB; FOREACH(pawnEffect(them, ksq), pawnEffect); FOREACH(enemyGold, pawnEffect); CheckCandidateBB[ksq][PAWN - 1][Us] = target & ~Bitboard(ksq); // 香で王手になる可能性のあるものは、ksqに敵の香をおいたときの利き。(盤上には何もないものとする) // と、王が1から3段目だと成れるので王の両端に香を置いた利きも。 target = lanceStepEffect(them, ksq); if (enemy_field(Us) & ksq) { if (file_of(ksq) != FILE_1) target |= lanceStepEffect(them, ksq + SQ_R); if (file_of(ksq) != FILE_9) target |= lanceStepEffect(them, ksq + SQ_L); } CheckCandidateBB[ksq][LANCE - 1][Us] = target; // 桂で王手になる可能性のあるものは、ksqに敵の桂をおいたところに移動できる桂(不成) + ksqに金をおいた範囲(enemyGold)に成りで移動できる桂 target = ZERO_BB; FOREACH(knightEffect(them, ksq) | enemyGold, knightEffect); CheckCandidateBB[ksq][KNIGHT - 1][Us] = target & ~Bitboard(ksq); // 銀も同様だが、2,3段目からの引き成りで王手になるパターンがある。(4段玉と5段玉に対して) target = ZERO_BB; FOREACH(silverEffect(them, ksq) , silverEffect); FOREACH(enemyGold, silverEffect); // 移動先が敵陣 == 成れる == 金になるので、敵玉の升に敵の金をおいた利きに成りで移動すると王手になる。 FOREACH(goldEffect(them, ksq), enemy_field(Us) & silverEffect); // 移動元が敵陣 == 成れる == 金になるので、敵玉の升に敵の金をおいた利きに成りで移動すると王手になる。 CheckCandidateBB[ksq][SILVER - 1][Us] = target & ~Bitboard(ksq); // 金 target = ZERO_BB; FOREACH(goldEffect(them, ksq), goldEffect); CheckCandidateBB[ksq][GOLD - 1][Us] = target & ~Bitboard(ksq); // 角 target = ZERO_BB; FOREACH_BR(bishopEffect(ksq,ZERO_BB), bishopEffect); FOREACH_BR(kingEffect(ksq) & enemy_field(Us), bishopEffect); // 移動先が敵陣 == 成れる == 王の動き FOREACH_BR(kingEffect(ksq) , enemy_field(Us) & bishopEffect); // 移動元が敵陣 == 成れる == 王の動き CheckCandidateBB[ksq][BISHOP - 1][Us] = target & ~Bitboard(ksq); // 飛・龍は無条件全域。 // ROOKのところには馬のときのことを格納 // 馬 target = ZERO_BB; FOREACH_BR(horseEffect(ksq, ZERO_BB), horseEffect); CheckCandidateBB[ksq][ROOK - 1][Us] = target & ~Bitboard(ksq); // 王(24近傍が格納される) target = ZERO_BB; FOREACH_KING(kingEffect(ksq) , kingEffect); CheckCandidateBB[ksq][HDK - 1][Us] = target & ~Bitboard(ksq); } // 10. LONG_EFFECT_LIBRARYの初期化 #ifdef LONG_EFFECT_LIBRARY LongEffect::init(); #endif // 11. 1手詰めテーブルの初期化 #ifdef USE_MATE_1PLY Mate1Ply::init(); #endif }