diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..417fc403 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +stockfish +*.o +.depend diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 1c69dca9..826a504b 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -32,58 +32,58 @@ namespace { const vector Defaults = { "setoption name UCI_Chess960 value false", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", - "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", - "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", - "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", - "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6", - "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4", - "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", - "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", - "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", - "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", - "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", - "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", - "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", - "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", - "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", - "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", - "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", - "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", - "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3", - "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", - "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", - "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", - "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1", - "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1", - "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1", - "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1", - "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", - "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", - "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", - "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", + "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R[C-L-c-l-] w KQkq - 0 10", + "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8[C-L-c-l-] w - - 0 11", + "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1[C-L-c-l-] b - - 7 19", + "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R[C-L-c-l-] w - - 7 14 moves d4e6", + "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1[C-L-c-l-] w - - 2 14 moves g2g4", + "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1[C-L-c-l-] b - - 2 15", + "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R[C-L-c-l-] w kq - 0 13", + "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1[C-L-c-l-] w - - 1 16", + "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1[C-L-c-l-] w - - 1 17", + "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R[C-L-c-l-] b KQ - 0 11", + "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1[C-L-c-l-] w - - 1 16", + "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1[C-L-c-l-] b - - 6 22", + "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R[C-L-c-l-] w - - 2 18", + "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1[C-L-c-l-] b - - 3 22", + "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1[C-L-c-l-] b - - 4 26", + "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4[C-L-c-l-] b - - 0 1", + "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8[C-L-c-l-] w - - 0 1", + "2K5/p7/7P/5pR1/8/5k2/r7/8[C-L-c-l-] w - - 0 1 moves g5g6 f3e3 g6g5 e3f3", + "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4[C-L-c-l-] w - - 0 1", + "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8[C-L-c-l-] w - - 0 1", + "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8[C-L-c-l-] w - - 0 1", + "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8[C-L-c-l-] w - - 0 1", + "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8[C-L-c-l-] w - - 0 1", + "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2[C-L-c-l-] b - - 0 1", + "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8[C-L-c-l-] b - - 0 1", + "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1[C-L-c-l-] w - - 0 1", + "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4[C-L-c-l-] w - - 0 1", + "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1[C-L-c-l-] w - - 0 1", + "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1[C-L-c-l-] w - - 0 1", // 5-man positions - "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate - "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate - "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw + "8/8/8/8/5kp1/P7/8/1K1N4[C-L-c-l-] w - - 0 1", // Kc2 - mate + "8/8/8/5N2/8/p7/8/2NK3k[C-L-c-l-] w - - 0 1", // Na2 - mate + "8/3k4/8/8/8/4B3/4KB2/2B5[C-L-c-l-] w - - 0 1", // draw // 6-man positions - "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate - "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate - "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw + "8/8/1P6/5pr1/8/4R3/7k/2K5[C-L-c-l-] w - - 0 1", // Re5 - mate + "8/2p4P/8/kr6/6R1/8/8/1K6[C-L-c-l-] w - - 0 1", // Ka2 - mate + "8/8/3P3k/8/1p6/8/1P6/1K3n2[C-L-c-l-] b - - 0 1", // Nd2 - draw // 7-man positions - "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw + "8/R7/2q5/8/6k1/8/1P5p/K6R[C-L-c-l-] w - - 0 124", // Draw // Mate and stalemate positions - "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1", - "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1", - "8/8/8/8/8/6k1/6p1/6K1 w - -", - "7k/7P/6K1/8/3B4/8/8/8 b - -", + "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2[C-L-c-l-] b - - 0 1", + "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1[C-L-c-l-] w - - 0 1", + "8/8/8/8/8/6k1/6p1/6K1[C-L-c-l-] w - -", + "7k/7P/6K1/8/3B4/8/8/8[C-L-c-l-] b - -", // Chess 960 "setoption name UCI_Chess960 value true", - "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", + "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR[C-L-c-l-] w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", "setoption name UCI_Chess960 value false" }; diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 60048289..5ebf4440 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -117,7 +117,7 @@ namespace { if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 || ksq[WHITE] == psq || ksq[BLACK] == psq - || (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK]))) + || (us == WHITE && (PseudoAttacks[WHITE][PAWN][psq] & ksq[BLACK]))) result = INVALID; // Immediate win if a pawn can be promoted without getting captured @@ -125,13 +125,13 @@ namespace { && rank_of(psq) == RANK_7 && ksq[us] != psq + NORTH && ( distance(ksq[~us], psq + NORTH) > 1 - || (PseudoAttacks[KING][ksq[us]] & (psq + NORTH)))) + || (PseudoAttacks[us][KING][ksq[us]] & (psq + NORTH)))) result = WIN; // Immediate draw if it is a stalemate or a king captures undefended pawn else if ( us == BLACK - && ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq])) - || (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]]))) + && ( !(PseudoAttacks[us][KING][ksq[us]] & ~(PseudoAttacks[us][KING][ksq[~us]] | PseudoAttacks[~us][PAWN][psq])) + || (PseudoAttacks[us][KING][ksq[us]] & psq & ~PseudoAttacks[us][KING][ksq[~us]]))) result = DRAW; // Position will be classified later @@ -157,7 +157,7 @@ namespace { constexpr Result Bad = (Us == WHITE ? DRAW : WIN); Result r = INVALID; - Bitboard b = PseudoAttacks[KING][ksq[Us]]; + Bitboard b = PseudoAttacks[Us][KING][ksq[Us]]; while (b) r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)] diff --git a/src/bitboard.cpp b/src/bitboard.cpp index b19d401a..954e26dc 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -37,8 +37,8 @@ Bitboard DistanceRingBB[SQUARE_NB][8]; Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; -Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; -Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; +Bitboard PseudoAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; +Bitboard LeaperAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; Magic RookMagics[SQUARE_NB]; Magic BishopMagics[SQUARE_NB]; @@ -58,6 +58,24 @@ namespace { u = ((u >> 4) + u) & 0x0F0FU; return (u * 0x0101U) >> 8; } + + Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied, int max_dist = 7) { + + Bitboard attack = 0; + + for (int i = 0; directions[i]; ++i) + for (Square s = sq + directions[i]; + is_ok(s) && distance(s, s - directions[i]) == 1 && distance(s, sq) <= max_dist; + s += directions[i]) + { + attack |= s; + + if (occupied & s) + break; + } + + return attack; + } } @@ -119,43 +137,104 @@ void Bitboards::init() { DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2; } - int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } }; + // Piece moves + Direction RookDirections[5] = { NORTH, EAST, SOUTH, WEST }; + Direction BishopDirections[5] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; + + init_magics(RookTable, RookMagics, RookDirections); + init_magics(BishopTable, BishopMagics, BishopDirections); + + int steps[][17] = { + {}, // NO_PIECE_TYPE + { 7, 9 }, // Pawn + { -17, -15, -10, -6, 6, 10, 15, 17 }, // Knight + {}, // Bishop + {}, // Rook + {}, // Queen + { -16, -10, -9, -8, -7, -6, -2, -1, + 16, 10, 9, 8, 7, 6, 2, 1 }, // Cannon + { -17, -15, -10, -6, 6, 10, 15, 17 }, // Leopard + { -17, -15, -10, -6, 6, 10, 15, 17 }, // Archbishop + { -17, -15, -10, -6, 6, 10, 15, 17 }, // Chancellor + { -17, -16, -15, -10, -6, -2, + 17, 16, 15, 10, 6, 2 }, // Spider + { -17, -15, -10, -6, 6, 10, 15, 17 }, // Dragon + { -25, -23, -17, -15, -11, -10, -6, -5, + 25, 23, 17, 15, 11, 10, 6, 5}, // Unicorn + { -27, -24, -21, -18, -16, -14, -3, -2, + 27, 24, 21, 18, 16, 14, 3, 2 }, // Hawk + { -18, -16, -14, -9, -8, -7, -2, -1, + 18, 16, 14, 9, 8, 7, 2, 1 }, // Elephant + { -17, -16, -15, -2, + 17, 16, 15, 2 }, // Fortress + { -9, -8, -7, -1, 1, 7, 8, 9 } // King + }; + Direction slider[][9] = { + {}, // NO_PIECE_TYPE + {}, // Pawn + {}, // Knight + { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Bishop + { NORTH, EAST, SOUTH, WEST }, // Rook + { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Queen + {}, // Cannon + { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Leopard + { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Archbishop + { NORTH, EAST, SOUTH, WEST }, // Chancellor + { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Spider + { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Dragon + {}, // Unicorn + {}, // Hawk + {}, // Elephant + { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // Fortress + {} // King + }; + int slider_dist[] = { + 0, // NO_PIECE_TYPE + 0, // Pawn + 0, // Knight + 7, // Bishop + 7, // Rook + 7, // Queen + 0, // Cannon + 2, // Leopard + 7, // Archbishop + 7, // Chancellor + 2, // Spider + 7, // Dragon + 0, // Unicorn + 0, // Hawk + 0, // Elephant + 3, // Fortress + 0 // King + }; for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt : { PAWN, KNIGHT, KING }) + for (PieceType pt = PAWN; pt <= KING; ++pt) for (Square s = SQ_A1; s <= SQ_H8; ++s) + { for (int i = 0; steps[pt][i]; ++i) { Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]); - if (is_ok(to) && distance(s, to) < 3) + if (is_ok(to) && distance(s, to) < 4) { - if (pt == PAWN) - PawnAttacks[c][s] |= to; - else - PseudoAttacks[pt][s] |= to; + PseudoAttacks[c][pt][s] |= to; + LeaperAttacks[c][pt][s] |= to; } } - - Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; - Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; - - init_magics(RookTable, RookMagics, RookDirections); - init_magics(BishopTable, BishopMagics, BishopDirections); + PseudoAttacks[c][pt][s] |= sliding_attack(slider[pt], s, 0, slider_dist[pt]); + } for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { - PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); - PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); - for (PieceType pt : { BISHOP, ROOK }) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) { - if (!(PseudoAttacks[pt][s1] & s2)) + if (!(PseudoAttacks[WHITE][pt][s1] & s2)) continue; - LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; - BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]); + LineBB[s1][s2] = (attacks_bb(WHITE, pt, s1, 0) & attacks_bb(WHITE, pt, s2, 0)) | s1 | s2; + BetweenBB[s1][s2] = attacks_bb(WHITE, pt, s1, SquareBB[s2]) & attacks_bb(WHITE, pt, s2, SquareBB[s1]); } } } @@ -163,25 +242,6 @@ void Bitboards::init() { namespace { - Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) { - - Bitboard attack = 0; - - for (int i = 0; i < 4; ++i) - for (Square s = sq + directions[i]; - is_ok(s) && distance(s, s - directions[i]) == 1; - s += directions[i]) - { - attack |= s; - - if (occupied & s) - break; - } - - return attack; - } - - // init_magics() computes all rook and bishop attacks at startup. Magic // bitboards are used to look up attacks of sliding pieces. As a reference see // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we diff --git a/src/bitboard.h b/src/bitboard.h index 7ae1effd..25b061cf 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -73,8 +73,8 @@ extern Bitboard DistanceRingBB[SQUARE_NB][8]; extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; -extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; -extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; +extern Bitboard PseudoAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; +extern Bitboard LeaperAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; /// Magic holds all magic bitboards relevant data for a single square @@ -267,24 +267,15 @@ template<> inline int distance(Square x, Square y) { return distance(rank_ template inline Bitboard attacks_bb(Square s, Bitboard occupied) { + assert(Pt == BISHOP || Pt == ROOK); const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s]; return m.attacks[m.index(occupied)]; } -inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { - - assert(pt != PAWN); - - switch (pt) - { - case BISHOP: return attacks_bb(s, occupied); - case ROOK : return attacks_bb< ROOK>(s, occupied); - case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); - default : return PseudoAttacks[pt][s]; - } +inline Bitboard attacks_bb(Color c, PieceType pt, Square s, Bitboard occupied) { + return LeaperAttacks[c][pt][s] | (PseudoAttacks[c][pt][s] & (attacks_bb(s, occupied) | attacks_bb(s, occupied))); } - /// popcount() counts the number of non-zero bits in a bitboard inline int popcount(Bitboard b) { diff --git a/src/endgame.cpp b/src/endgame.cpp index 3e01259c..3cd5e5b3 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -403,8 +403,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && relative_rank(weakSide, pos.square(strongSide)) >= RANK_4 && relative_rank(weakSide, rsq) == RANK_3 && ( pos.pieces(weakSide, PAWN) - & pos.attacks_from(kingSq) - & pos.attacks_from(rsq, strongSide))) + & pos.attacks_from(weakSide, kingSq) + & pos.attacks_from(strongSide, rsq))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -547,7 +547,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // the corner if ( rk == RANK_6 && distance(psq + 2 * push, ksq) <= 1 - && (PseudoAttacks[BISHOP][bsq] & (psq + push)) + && (PseudoAttacks[strongSide][BISHOP][bsq] & (psq + push)) && distance(bsq, psq) >= 2) return ScaleFactor(8); } @@ -687,14 +687,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 - || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) + || (pos.attacks_from(weakSide, blockSq2) & pos.pieces(weakSide, BISHOP)) || distance(r1, r2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq1 - || (pos.attacks_from(blockSq1) & pos.pieces(weakSide, BISHOP)))) + || (pos.attacks_from(weakSide, blockSq1) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; @@ -759,7 +759,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. - if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from(bishopSq)) + if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from(weakSide, bishopSq)) return ScaleFactor(distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 01b5aa5b..81b1084e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -34,8 +34,8 @@ namespace Trace { enum Tracing { NO_TRACE, TRACE }; - enum Term { // The first 8 entries are reserved for PieceType - MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB + enum Term { // The first PIECE_TYPE_NB entries are reserved for PieceType + MATERIAL = PIECE_TYPE_NB, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB }; Score scores[TERM_NB][COLOR_NB]; @@ -101,7 +101,7 @@ namespace { // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // indexed by piece type and number of attacked squares in the mobility area. - constexpr Score MobilityBonus[][32] = { + constexpr Score MobilityBonus[PIECE_TYPE_NB - 2][32] = { { S(-75,-76), S(-57,-54), S( -9,-28), S( -2,-10), S( 6, 5), S( 14, 12), // Knights S( 22, 26), S( 29, 29), S( 36, 29) }, { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops @@ -159,7 +159,7 @@ namespace { constexpr int PassedDanger[RANK_NB] = { 0, 0, 0, 3, 6, 12, 21 }; // KingProtector[PieceType-2] contains a penalty according to distance from king - constexpr Score KingProtector[] = { S(3, 5), S(4, 3), S(3, 0), S(1, -1) }; + constexpr Score KingProtector[PIECE_TYPE_NB - 2] = { S(3, 5), S(4, 3), S(3, 0), S(1, -1) }; // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 5); @@ -263,7 +263,7 @@ namespace { mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); // Initialise attackedBy bitboards for kings and pawns - attackedBy[Us][KING] = pos.attacks_from(pos.square(Us)); + attackedBy[Us][KING] = pos.attacks_from(Us, pos.square(Us)); attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN]; @@ -310,7 +310,7 @@ namespace { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) - : pos.attacks_from(s); + : pos.attacks_from(Us, s); if (pos.blockers_for_king(Us) & s) b &= LineBB[pos.square(Us)][s]; @@ -381,7 +381,7 @@ namespace { { // Bonus for aligning rook with enemy pawns on the same rank/file if (relative_rank(Us, s) >= RANK_5) - score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); + score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[Us][ROOK][s]); // Bonus for rook on an open or semi-open file if (pe->semiopen_file(Us, file_of(s))) @@ -463,7 +463,7 @@ namespace { unsafeChecks |= b2; // Enemy knights checks - b = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; + b = pos.attacks_from(Us, ksq) & attackedBy[Them][KNIGHT]; if (b & safe) kingDanger += KnightSafeCheck; else @@ -603,12 +603,12 @@ namespace { Square s = pos.square(Them); safeThreats = mobilityArea[Us] & ~stronglyProtected; - b = attackedBy[Us][KNIGHT] & pos.attacks_from(s); + b = attackedBy[Us][KNIGHT] & pos.attacks_from(Us, s); score += KnightOnQueen * popcount(b & safeThreats); - b = (attackedBy[Us][BISHOP] & pos.attacks_from(s)) - | (attackedBy[Us][ROOK ] & pos.attacks_from(s)); + b = (attackedBy[Us][BISHOP] & pos.attacks_from(Us, s)) + | (attackedBy[Us][ROOK ] & pos.attacks_from(Us, s)); score += SliderOnQueen * popcount(b & safeThreats & attackedBy2[Us]); } @@ -675,7 +675,7 @@ namespace { // in the pawn's path attacked or occupied by the enemy. defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s); - bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); + bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(Us, s); if (!(pos.pieces(Us) & bb)) defendedSquares &= attackedBy[Us][ALL_PIECES]; diff --git a/src/movegen.cpp b/src/movegen.cpp index 35ca6e77..50b4d476 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -52,7 +52,7 @@ namespace { // Because we generate only legal castling moves we need to verify that // when moving the castling rook we do not discover some hidden checker. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN))) + if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & pos.pieces(~us))) return moveList; Move m = make(kfrom, rfrom); @@ -65,23 +65,29 @@ namespace { } - template - ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { + template + ExtMove* make_promotions(const Position& pos, ExtMove* moveList, Square to, Square ksq) { + + const MoveType T = (D == NORTH_WEST || D == SOUTH_WEST) ? PROMOTION_LEFT + : (D == NORTH_EAST || D == SOUTH_EAST) ? PROMOTION_RIGHT + : PROMOTION_STRAIGHT; if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - *moveList++ = make(to - D, to, QUEEN); + *moveList++ = make(to - D, to, QUEEN); if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { - *moveList++ = make(to - D, to, ROOK); - *moveList++ = make(to - D, to, BISHOP); - *moveList++ = make(to - D, to, KNIGHT); + *moveList++ = make(to - D, to, ROOK); + *moveList++ = make(to - D, to, BISHOP); + *moveList++ = make(to - D, to, KNIGHT); + for (Gate g = GATE_1; g < GATE_NB; g++) + *moveList++ = make(to - D, to, pos.gating_piece(g)); } // Knight promotion is the only promotion that can give a direct check // that's not already included in the queen promotion. - if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq)) - *moveList++ = make(to - D, to, KNIGHT); + if (Type == QUIET_CHECKS && (PseudoAttacks[c][KNIGHT][to] & ksq)) + *moveList++ = make(to - D, to, KNIGHT); else (void)ksq; // Silence a warning under MSVC @@ -128,8 +134,8 @@ namespace { { Square ksq = pos.square(Them); - b1 &= pos.attacks_from(ksq, Them); - b2 &= pos.attacks_from(ksq, Them); + b1 &= pos.attacks_from(Them, ksq); + b2 &= pos.attacks_from(Them, ksq); // Add pawn pushes which give discovered check. This is possible only // if the pawn is not on the same file as the enemy king, because we @@ -175,13 +181,13 @@ namespace { Square ksq = pos.square(Them); while (b1) - moveList = make_promotions(moveList, pop_lsb(&b1), ksq); + moveList = make_promotions(pos, moveList, pop_lsb(&b1), ksq); while (b2) - moveList = make_promotions(moveList, pop_lsb(&b2), ksq); + moveList = make_promotions(pos, moveList, pop_lsb(&b2), ksq); while (b3) - moveList = make_promotions(moveList, pop_lsb(&b3), ksq); + moveList = make_promotions(pos, moveList, pop_lsb(&b3), ksq); } // Standard and en-passant captures @@ -212,7 +218,7 @@ namespace { if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) return moveList; - b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); + b1 = pawnsNotOn7 & pos.attacks_from(Them, pos.ep_square()); assert(b1); @@ -225,30 +231,24 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, PieceType pt, Bitboard target) { - assert(Pt != KING && Pt != PAWN); + assert(pt != KING && pt != PAWN); - const Square* pl = pos.squares(us); + const Square* pl = pos.squares(us, pt); for (Square from = *pl; from != SQ_NONE; from = *++pl) { - if (Checks) - { - if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt))) - continue; + // Avoid generating discovered checks twice + if (Checks && (pos.blockers_for_king(~us) & from)) + continue; - if (pos.blockers_for_king(~us) & from) - continue; - } - - Bitboard b = pos.attacks_from(from) & target; + Bitboard b = pos.attacks_from(us, pt, from) & target; if (Checks) - b &= pos.check_squares(Pt); + b &= pos.check_squares(pt); while (b) *moveList++ = make_move(from, pop_lsb(&b)); @@ -264,15 +264,13 @@ namespace { constexpr bool Checks = Type == QUIET_CHECKS; moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, Us, target); - moveList = generate_moves(pos, moveList, Us, target); - moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target); + for (PieceType pt = KNIGHT; pt < KING; ++pt) + moveList = generate_moves(pos, moveList, Us, pt, target); if (Type != QUIET_CHECKS && Type != EVASIONS) { Square ksq = pos.square(Us); - Bitboard b = pos.attacks_from(ksq) & target; + Bitboard b = pos.attacks_from(Us, ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); } @@ -312,6 +310,15 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); assert(!pos.checkers()); + if (pos.game_phase() != GAMEPHASE_PLAYING) + { + if (Type == QUIETS || Type == NON_EVASIONS) + return pos.game_phase() == GAMEPHASE_SELECTION ? generate(pos, moveList) + : generate(pos, moveList); + else + return moveList; + } + Color us = pos.side_to_move(); Bitboard target = Type == CAPTURES ? pos.pieces(~us) @@ -333,6 +340,9 @@ template ExtMove* generate(const Position&, ExtMove*); template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { + if (pos.game_phase() != GAMEPHASE_PLAYING) + return moveList; + assert(!pos.checkers()); Color us = pos.side_to_move(); @@ -346,10 +356,10 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { if (pt == PAWN) continue; // Will be generated together with direct checks - Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces(); + Bitboard b = pos.attacks_from(us, pt, from) & ~pos.pieces(); if (pt == KING) - b &= ~PseudoAttacks[QUEEN][pos.square(~us)]; + b &= ~PseudoAttacks[~us][QUEEN][pos.square(~us)]; while (b) *moveList++ = make_move(from, pop_lsb(&b)); @@ -365,12 +375,15 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { + if (pos.game_phase() != GAMEPHASE_PLAYING) + return moveList; + assert(pos.checkers()); Color us = pos.side_to_move(); Square ksq = pos.square(us); Bitboard sliderAttacks = 0; - Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); + Bitboard sliders = pos.checkers(); // Find all the squares attacked by slider checkers. We will remove them from // the king evasions in order to skip known illegal moves, which avoids any @@ -378,11 +391,11 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { while (sliders) { Square checksq = pop_lsb(&sliders); - sliderAttacks |= LineBB[checksq][ksq] ^ checksq; + sliderAttacks |= attacks_bb(~us, type_of(pos.piece_on(checksq)), checksq, pos.pieces() ^ ksq); } // Generate evasions for king, capture and non capture moves - Bitboard b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; + Bitboard b = pos.attacks_from(us, ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); @@ -392,12 +405,47 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Generate blocking evasions or captures of the checking piece Square checksq = lsb(pos.checkers()); Bitboard target = between_bb(checksq, ksq) | checksq; + // Leaper attacks can not be blocked + if (LeaperAttacks[~us][type_of(pos.piece_on(checksq))][checksq] & ksq) + target = SquareBB[checksq]; return us == WHITE ? generate_all(pos, moveList, target) : generate_all(pos, moveList, target); } +/// generate generates all gating piece selection moves. +template<> +ExtMove* generate(const Position&, ExtMove* moveList) { + + for (PieceType pt = PieceType(QUEEN + 1); pt < KING; ++pt) + *moveList++ = make(SQ_A1, SQ_A1, pt); + + return moveList; +} + + +/// generate generates all moves for setting up gating pieces. +template<> +ExtMove* generate(const Position& pos, ExtMove* moveList) { + + Color us = pos.side_to_move(); + assert(pos.setup_count(us) < GATE_NB); + + Bitboard b = (us == WHITE? Rank1BB : Rank8BB) & ~pos.gates(); + // King and rook are mutually exclusive gates + if (pos.pieces(us, KING) & pos.gates()) + b &= ~pos.pieces(us, ROOK); + else if (pos.pieces(us, ROOK) & pos.gates()) + b &= ~pos.pieces(us, KING); + PieceType pt = pos.gating_piece(Gate(pos.setup_count(us) + 1)); + + while (b) + *moveList++ = make(SQ_A1, pop_lsb(&b), pt); + + return moveList; +} + /// generate generates all the legal moves in the given position template<> diff --git a/src/movegen.h b/src/movegen.h index 9bf2a468..8f8ab614 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -33,6 +33,8 @@ enum GenType { QUIET_CHECKS, EVASIONS, NON_EVASIONS, + SELECTIONS, + PLACEMENTS, LEGAL }; diff --git a/src/pawns.cpp b/src/pawns.cpp index 6901d7b7..8c5c1dc3 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -101,8 +101,8 @@ namespace { // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); stoppers = theirPawns & passed_pawn_mask(Us, s); - lever = theirPawns & PawnAttacks[Us][s]; - leverPush = theirPawns & PawnAttacks[Us][s + Up]; + lever = theirPawns & PseudoAttacks[Us][PAWN][s]; + leverPush = theirPawns & PseudoAttacks[Us][PAWN][s + Up]; doubled = ourPawns & (s - Up); neighbours = ourPawns & adjacent_files_bb(f); phalanx = neighbours & rank_bb(s); @@ -128,7 +128,7 @@ namespace { { b = shift(supported) & ~theirPawns; while (b) - if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)])) + if (!more_than_one(theirPawns & PseudoAttacks[Us][PAWN][pop_lsb(&b)])) e->passedPawns[Us] |= s; } diff --git a/src/position.cpp b/src/position.cpp index 2573fe84..8578bac2 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -38,11 +38,14 @@ using std::string; namespace PSQT { extern Score psq[PIECE_NB][SQUARE_NB]; + extern Score psq_gate[PIECE_NB][FILE_NB]; } namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; + Key psq_gate[PIECE_NB][FILE_NB]; + Key inhand[PIECE_TYPE_NB][GATE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; Key side, noPawns; @@ -50,11 +53,6 @@ namespace Zobrist { namespace { -const string PieceToChar(" PNBRQK pnbrqk"); - -constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, - B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; - // min_attacker() is a helper function used by see_ge() to locate the least // valuable attacker for the side to move, remove the attacker we just found // from the bitboards and scan for new X-ray attacks behind it. @@ -150,9 +148,19 @@ void Position::init() { PRNG rng(1070372); - for (Piece pc : Pieces) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[pc][s] = rng.rand(); + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = PAWN; pt <= KING; ++pt) + for (Square s = SQ_A1; s <= SQ_H8; ++s) + Zobrist::psq[make_piece(c, pt)][s] = rng.rand(); + + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = PAWN; pt <= KING; ++pt) + for (File f = FILE_A; f <= FILE_H; ++f) + Zobrist::psq_gate[make_piece(c, pt)][f] = rng.rand(); + + for (PieceType pt = PAWN; pt <= KING; ++pt) + for (Gate g = NO_GATE; g < GATE_NB; ++g) + Zobrist::inhand[pt][g] = rng.rand(); for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rng.rand(); @@ -173,10 +181,13 @@ void Position::init() { // Prepare the cuckoo tables int count = 0; - for (Piece pc : Pieces) + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = KNIGHT; pt <= QUEEN || pt == KING; pt != QUEEN ? ++pt: pt = KING) + { + Piece pc = make_piece(c, pt); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) - if (PseudoAttacks[type_of(pc)][s1] & s2) + if (PseudoAttacks[WHITE][type_of(pc)][s1] & s2) { Move move = make_move(s1, s2); Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; @@ -191,6 +202,7 @@ void Position::init() { } count++; } + } assert(count == 3668); } @@ -261,7 +273,39 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th put_piece(Piece(idx), sq); ++sq; } + + else if (token == '[') + break; // Stop before gating pieces } + // Gating pieces + if (!isspace(token)) + while ((ss >> token) && !isspace(token)) + { + // Allow various formats here. Rank and slash are optional. + if (token == ']' || token == '/') + continue; + else if ((idx = PieceToChar.find(token)) != string::npos) + { + Piece pc = Piece(idx); + Color c = color_of(pc); + PieceType pt = type_of(pc); + + if (c == WHITE) + set_gating_type(pt); + ss >> token; + if (token >= 'a' && token <= 'h') + { + put_gating_piece(c, make_square(File(token - 'a'), c == WHITE ? RANK_1 : RANK_8)); + // Consume next character in case of optionally specified rank + if (ss.peek() != EOF && isdigit(ss.peek())) + ss >> token; + } + else if (token == '-') + gatingSquares[c][++setupCount[c]] = SQ_NONE; + else if (token == '?') + continue; + } + } // 2. Active color ss >> token; @@ -362,11 +406,8 @@ void Position::set_check_info(StateInfo* si) const { Square ksq = square(~sideToMove); - si->checkSquares[PAWN] = attacks_from(ksq, ~sideToMove); - si->checkSquares[KNIGHT] = attacks_from(ksq); - si->checkSquares[BISHOP] = attacks_from(ksq); - si->checkSquares[ROOK] = attacks_from(ksq); - si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK]; + for (PieceType pt = PAWN; pt < KING; ++pt) + si->checkSquares[pt] = attacks_from(~sideToMove, pt, ksq); si->checkSquares[KING] = 0; } @@ -394,6 +435,21 @@ void Position::set_state(StateInfo* si) const { si->psq += PSQT::psq[pc][s]; } + for (Gate g = GATE_1; g <= gate_count(); g++) + si->key ^= Zobrist::inhand[gating_piece(g)][g]; + + for (Color c = WHITE; c <= BLACK; ++c) + for (Gate g = GATE_1; g <= setup_count(c); g++) + { + Square s = gating_square(c, g); + if (s != SQ_NONE) + { + Piece pc = make_piece(c, gating_piece(g)); + si->key ^= Zobrist::psq_gate[pc][file_of(s)]; + si->psq += PSQT::psq_gate[pc][file_of(s)]; + } + } + if (si->epSquare != SQ_NONE) si->key ^= Zobrist::enpassant[file_of(si->epSquare)]; @@ -408,14 +464,16 @@ void Position::set_state(StateInfo* si) const { si->pawnKey ^= Zobrist::psq[piece_on(s)][s]; } - for (Piece pc : Pieces) - { - if (type_of(pc) != PAWN && type_of(pc) != KING) - si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc]; + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = PAWN; pt <= KING; ++pt) + { + Piece pc = make_piece(c, pt); + if (pt != PAWN && pt != KING) + si->nonPawnMaterial[c] += pieceCount[pc] * PieceValue[MG][pc]; - for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) - si->materialKey ^= Zobrist::psq[pc][cnt]; - } + for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) + si->materialKey ^= Zobrist::psq[pc][cnt]; + } } @@ -466,6 +524,19 @@ const string Position::fen() const { ss << '/'; } + // List all gating pieces in the format [LaCblgch] + if (gateCount > NO_GATE) + { + ss << '['; + for (Color c = WHITE; c <= BLACK; ++c) + for (Gate i = GATE_1; i <= gateCount; i++) + ss << std::string{PieceToChar[make_piece(c, gating_piece(i))], + setupCount[c] < i ? '?' + : gating_square(c, i) != SQ_NONE ? char('a' + file_of(gating_square(c, i))) + : '-'}; + ss << ']'; + } + ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) @@ -503,8 +574,9 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners pinners = 0; // Snipers are sliders that attack 's' when a piece is removed - Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) - | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + Bitboard snipers = sliders + & attackers_to(s, 0) + & ~attackers_to(s); while (snipers) { @@ -527,12 +599,11 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners Bitboard Position::attackers_to(Square s, Bitboard occupied) const { - return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) - | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) - | (attacks_from(s) & pieces(KNIGHT)) - | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN)) - | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) - | (attacks_from(s) & pieces(KING)); + Bitboard b = 0; + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = PAWN; pt <= KING; ++pt) + b |= attacks_bb(~c, pt, s, occupied) & pieces(c, pt); + return b; } @@ -542,8 +613,17 @@ bool Position::legal(Move m) const { assert(is_ok(m)); + // Pseudo-legal moves during setup phase are legal + if (type_of(m) == SET_GATING_TYPE || type_of(m) == PUT_GATING_PIECE) + { + assert(gating_type(m) != NO_PIECE_TYPE); + return true; + } + Color us = sideToMove; Square from = from_sq(m); + Square to = to_sq(m); + Square ksq = square(us); assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); @@ -553,8 +633,6 @@ bool Position::legal(Move m) const { // the move is made. if (type_of(m) == ENPASSANT) { - Square ksq = square(us); - Square to = to_sq(m); Square capsq = to - pawn_push(us); Bitboard occupied = (pieces() ^ from ^ capsq) | to; @@ -563,8 +641,7 @@ bool Position::legal(Move m) const { assert(piece_on(capsq) == make_piece(~us, PAWN)); assert(piece_on(to) == NO_PIECE); - return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) - && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); + return !(attackers_to(ksq, occupied) & pieces(~us) & occupied); } // If the moving piece is a king, check whether the destination @@ -574,9 +651,9 @@ bool Position::legal(Move m) const { return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it - // is moving along the ray towards or away from the king. + // does not expose the king to attackers. return !(blockers_for_king(us) & from) - || aligned(from, to_sq(m), square(us)); + || !(attackers_to(ksq, (pieces() ^ from) | to) & pieces(~us) & ~SquareBB[to]); } @@ -595,8 +672,12 @@ bool Position::pseudo_legal(const Move m) const { if (type_of(m) != NORMAL) return MoveList(*this).contains(m); + // A normal move during setup phase can not be legal + if (game_phase() != GAMEPHASE_PLAYING) + return false; + // Is not a promotion, so promotion piece must be empty - if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE) + if (promotion_type(m) != NO_PIECE_TYPE) return false; // If the 'from' square is not occupied by a piece belonging to the side to @@ -616,7 +697,7 @@ bool Position::pseudo_legal(const Move m) const { if (rank_of(to) == relative_rank(us, RANK_8)) return false; - if ( !(attacks_from(from, us) & pieces(~us) & to) // Not a capture + if ( !(attacks_from(us, from) & pieces(~us) & to) // Not a capture && !((from + pawn_push(us) == to) && empty(to)) // Not a single push && !( (from + 2 * pawn_push(us) == to) // Not a double push && (rank_of(from) == relative_rank(us, RANK_2)) @@ -624,7 +705,7 @@ bool Position::pseudo_legal(const Move m) const { && empty(to - pawn_push(us)))) return false; } - else if (!(attacks_from(type_of(pc), from) & to)) + else if (!(attacks_from(us, type_of(pc), from) & to)) return false; // Evasions generator already takes care to avoid some kind of illegal moves @@ -639,7 +720,9 @@ bool Position::pseudo_legal(const Move m) const { return false; // Our move must be a blocking evasion or a capture of the checking piece - if (!((between_bb(lsb(checkers()), square(us)) | checkers()) & to)) + Square checksq = lsb(checkers()); + if ( !((between_bb(checksq, square(us)) | checkers()) & to) + || (LeaperAttacks[~us][type_of(piece_on(checksq))][checksq] & square(us))) return false; } // In case of king moves under check we have to remove king so as to catch @@ -659,6 +742,10 @@ bool Position::gives_check(Move m) const { assert(is_ok(m)); assert(color_of(moved_piece(m)) == sideToMove); + // There are no checks during setup phase + if (game_phase() != GAMEPHASE_PLAYING) + return false; + Square from = from_sq(m); Square to = to_sq(m); @@ -667,8 +754,14 @@ bool Position::gives_check(Move m) const { return true; // Is there a discovered check? + // We have to consider here that a piece could leap over a slider. if ( (st->blockersForKing[~sideToMove] & from) - && !aligned(from, to, square(~sideToMove))) + && ( !aligned(from, to, square(~sideToMove)) + || (attackers_to(square(~sideToMove), pieces() ^ from ^ to) & pieces(sideToMove)))) + return true; + + // Is there a check by a gated piece? + if ((gateBB & from) && (attacks_bb(sideToMove, gating_piece(from), from, pieces() ^ to) & square(~sideToMove))) return true; switch (type_of(m)) @@ -677,7 +770,7 @@ bool Position::gives_check(Move m) const { return false; case PROMOTION: - return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); + return attacks_bb(sideToMove, promotion_type(m), to, pieces() ^ from) & square(~sideToMove); // En passant capture with check? We have already handled the case // of direct checks and ordinary discovered check, so the only case we @@ -688,8 +781,7 @@ bool Position::gives_check(Move m) const { Square capsq = make_square(file_of(to), rank_of(from)); Bitboard b = (pieces() ^ from ^ capsq) | to; - return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) - | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); + return attackers_to(square(~sideToMove), b) & pieces(sideToMove) & b; } case CASTLING: { @@ -698,7 +790,12 @@ bool Position::gives_check(Move m) const { Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); - return (PseudoAttacks[ROOK][rto] & square(~sideToMove)) + // Is there a check by a gated piece on rook square? + if ( (gateBB & rfrom) + && ( attacks_bb(sideToMove, gating_piece(rfrom), rfrom, pieces() ^ kfrom ^ kto ^ rto) + & square(~sideToMove))) + return true; + return (PseudoAttacks[sideToMove][ROOK][rto] & square(~sideToMove)) && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); } default: @@ -727,153 +824,197 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { newSt.previous = st; st = &newSt; - // Increment ply counters. In particular, rule50 will be reset to zero later on - // in case of a capture or a pawn move. - ++gamePly; - ++st->rule50; - ++st->pliesFromNull; - Color us = sideToMove; Color them = ~us; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = piece_on(from); - Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); + switch (type_of(m)) + { + case SET_GATING_TYPE: + if (gateCount == NO_GATE || gating_type(m) != gatingPieces[gateCount]) + { + set_gating_type(gating_type(m)); + st->removedGatingType = NO_PIECE_TYPE; + k ^= Zobrist::inhand[gating_type(m)][gateCount]; + } + else + { + k ^= Zobrist::inhand[gating_type(m)][gateCount] + ^ Zobrist::inhand[CANNON][gateCount] ^ Zobrist::inhand[LEOPARD][gateCount + 1]; + unset_gating_type(); + set_gating_type(CANNON); + set_gating_type(LEOPARD); + st->removedGatingType = gating_type(m); + } + break; + case PUT_GATING_PIECE: + assert(gating_type(m) == gatingPieces[setupCount[us] + 1]); + put_gating_piece(us, to_sq(m)); + st->psq += PSQT::psq_gate[make_piece(us, gating_type(m))][file_of(to_sq(m))]; + k ^= Zobrist::psq_gate[make_piece(us, gating_type(m))][file_of(to_sq(m))]; + break; + default: + // Increment ply counters. In particular, rule50 will be reset to zero later on + // in case of a capture or a pawn move. + ++gamePly; + ++st->rule50; + ++st->pliesFromNull; - assert(color_of(pc) == us); - assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); - assert(type_of(captured) != KING); + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_on(from); + Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); - if (type_of(m) == CASTLING) - { - assert(pc == make_piece(us, KING)); - assert(captured == make_piece(us, ROOK)); + assert(color_of(pc) == us); + assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); + assert(type_of(captured) != KING); - Square rfrom, rto; - do_castling(us, from, to, rfrom, rto); + if (type_of(m) == CASTLING) + { + assert(pc == make_piece(us, KING)); + assert(captured == make_piece(us, ROOK)); - st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom]; - k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; - captured = NO_PIECE; - } + Square rfrom, rto; + do_castling(us, from, to, rfrom, rto, k); - if (captured) - { - Square capsq = to; + st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom]; + k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + captured = NO_PIECE; + } - // If the captured piece is a pawn, update pawn hash key, otherwise - // update non-pawn material. - if (type_of(captured) == PAWN) + if (captured) { - if (type_of(m) == ENPASSANT) + Square capsq = to; + + // If the captured piece is a pawn, update pawn hash key, otherwise + // update non-pawn material. + if (type_of(captured) == PAWN) { - capsq -= pawn_push(us); + if (type_of(m) == ENPASSANT) + { + capsq -= pawn_push(us); - assert(pc == make_piece(us, PAWN)); - assert(to == st->epSquare); - assert(relative_rank(us, to) == RANK_6); - assert(piece_on(to) == NO_PIECE); - assert(piece_on(capsq) == make_piece(them, PAWN)); + assert(pc == make_piece(us, PAWN)); + assert(to == st->epSquare); + assert(relative_rank(us, to) == RANK_6); + assert(piece_on(to) == NO_PIECE); + assert(piece_on(capsq) == make_piece(them, PAWN)); - board[capsq] = NO_PIECE; // Not done by remove_piece() - } + board[capsq] = NO_PIECE; // Not done by remove_piece() + } - st->pawnKey ^= Zobrist::psq[captured][capsq]; - } - else - st->nonPawnMaterial[them] -= PieceValue[MG][captured]; + st->pawnKey ^= Zobrist::psq[captured][capsq]; + } + else + st->nonPawnMaterial[them] -= PieceValue[MG][captured]; - // Update board and piece lists - remove_piece(captured, capsq); + // Update board and piece lists + remove_piece(captured, capsq); + if (gateBB & capsq) + { + st->psq -= PSQT::psq_gate[make_piece(~us, gating_piece(capsq))][file_of(capsq)]; + k ^= Zobrist::psq_gate[make_piece(~us, gating_piece(capsq))][file_of(capsq)]; + capture_gate(them, capsq); + } + else + st->capturedGate = NO_GATE; - // Update material hash key and prefetch access to materialTable - k ^= Zobrist::psq[captured][capsq]; - st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; - prefetch(thisThread->materialTable[st->materialKey]); + // Update material hash key and prefetch access to materialTable + k ^= Zobrist::psq[captured][capsq]; + st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; + prefetch(thisThread->materialTable[st->materialKey]); - // Update incremental scores - st->psq -= PSQT::psq[captured][capsq]; + // Update incremental scores + st->psq -= PSQT::psq[captured][capsq]; - // Reset rule 50 counter - st->rule50 = 0; - } + // Reset rule 50 counter + st->rule50 = 0; + } - // Update hash key - k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + // Update hash key + k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; - // Reset en passant square - if (st->epSquare != SQ_NONE) - { - k ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } - - // Update castling rights if needed - if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) - { - int cr = castlingRightsMask[from] | castlingRightsMask[to]; - k ^= Zobrist::castling[st->castlingRights & cr]; - st->castlingRights &= ~cr; - } + // Reset en passant square + if (st->epSquare != SQ_NONE) + { + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } - // Move the piece. The tricky Chess960 castling is handled earlier - if (type_of(m) != CASTLING) - move_piece(pc, from, to); + // Update castling rights if needed + if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) + { + int cr = castlingRightsMask[from] | castlingRightsMask[to]; + k ^= Zobrist::castling[st->castlingRights & cr]; + st->castlingRights &= ~cr; + } - // If the moving piece is a pawn do some special extra work - if (type_of(pc) == PAWN) - { - // Set en-passant square if the moved pawn can be captured - if ( (int(to) ^ int(from)) == 16 - && (attacks_from(to - pawn_push(us), us) & pieces(them, PAWN))) + // Move the piece. The tricky Chess960 castling is handled earlier + if (type_of(m) != CASTLING) { - st->epSquare = to - pawn_push(us); - k ^= Zobrist::enpassant[file_of(st->epSquare)]; + move_piece(pc, from, to); + if (gateBB & from) + { + Piece gated_piece = make_piece(us, gating_piece(from)); + st->psq += PSQT::psq[gated_piece][from] - PSQT::psq_gate[gated_piece][file_of(from)]; + k ^= Zobrist::psq[gated_piece][from] ^ Zobrist::psq_gate[gated_piece][file_of(from)]; + gate_piece(us, from); + } } - else if (type_of(m) == PROMOTION) + // If the moving piece is a pawn do some special extra work + if (type_of(pc) == PAWN) { - Piece promotion = make_piece(us, promotion_type(m)); + // Set en-passant square if the moved pawn can be captured + if ( (int(to) ^ int(from)) == 16 + && (attacks_from(us, to - pawn_push(us)) & pieces(them, PAWN))) + { + st->epSquare = to - pawn_push(us); + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + } - assert(relative_rank(us, to) == RANK_8); - assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); + else if (type_of(m) == PROMOTION) + { + Piece promotion = make_piece(us, promotion_type(m)); - remove_piece(pc, to); - put_piece(promotion, to); + assert(relative_rank(us, to) == RANK_8); + assert(type_of(promotion) >= KNIGHT && type_of(promotion) < KING); - // Update hash keys - k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; - st->pawnKey ^= Zobrist::psq[pc][to]; - st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] - ^ Zobrist::psq[pc][pieceCount[pc]]; + remove_piece(pc, to); + put_piece(promotion, to); - // Update incremental score - st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to]; + // Update hash keys + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; + st->pawnKey ^= Zobrist::psq[pc][to]; + st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] + ^ Zobrist::psq[pc][pieceCount[pc]]; - // Update material - st->nonPawnMaterial[us] += PieceValue[MG][promotion]; - } + // Update incremental score + st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to]; - // Update pawn hash key and prefetch access to pawnsTable - st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; - prefetch2(thisThread->pawnsTable[st->pawnKey]); + // Update material + st->nonPawnMaterial[us] += PieceValue[MG][promotion]; + } - // Reset rule 50 draw counter - st->rule50 = 0; - } + // Update pawn hash key and prefetch access to pawnsTable + st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + prefetch2(thisThread->pawnsTable[st->pawnKey]); - // Update incremental scores - st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; + // Reset rule 50 draw counter + st->rule50 = 0; + } - // Set capture piece - st->capturedPiece = captured; + // Update incremental scores + st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; - // Update the key with the final value - st->key = k; + // Set capture piece + st->capturedPiece = captured; + } // Calculate checkers bitboard (if move gives check) st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; + // Update the key with the final value + st->key = k; + sideToMove = ~sideToMove; // Update king attacks used for fast check detection @@ -893,55 +1034,76 @@ void Position::undo_move(Move m) { sideToMove = ~sideToMove; Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = piece_on(to); - - assert(empty(from) || type_of(m) == CASTLING); - assert(type_of(st->capturedPiece) != KING); - - if (type_of(m) == PROMOTION) + switch (type_of(m)) { - assert(relative_rank(us, to) == RANK_8); - assert(type_of(pc) == promotion_type(m)); - assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); + case SET_GATING_TYPE: + unset_gating_type(); + if (st->removedGatingType) + { + unset_gating_type(); + set_gating_type(st->removedGatingType); + } + break; + case PUT_GATING_PIECE: + assert(gating_type(m) == gatingPieces[setupCount[us]]); + remove_gating_piece(us, to_sq(m)); + break; + default: + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_on(to); - remove_piece(pc, to); - pc = make_piece(us, PAWN); - put_piece(pc, to); - } + assert(empty(from) || type_of(m) == CASTLING || color_of(pc) == us); + assert(type_of(st->capturedPiece) != KING); - if (type_of(m) == CASTLING) - { - Square rfrom, rto; - do_castling(us, from, to, rfrom, rto); - } - else - { - move_piece(pc, to, from); // Put the piece back at the source square + if (type_of(m) == PROMOTION) + { + assert(relative_rank(us, to) == RANK_8); + assert(type_of(pc) == promotion_type(m)); + assert(type_of(pc) >= KNIGHT && type_of(pc) < KING); + + remove_piece(pc, to); + pc = make_piece(us, PAWN); + put_piece(pc, to); + } - if (st->capturedPiece) + if (type_of(m) == CASTLING) { - Square capsq = to; + Square rfrom, rto; + do_castling(us, from, to, rfrom, rto, st->key); + } + else + { + if (pieces() & from) + ungate_piece(us, from); + move_piece(pc, to, from); // Put the piece back at the source square - if (type_of(m) == ENPASSANT) + if (st->capturedPiece) { - capsq -= pawn_push(us); + Square capsq = to; - assert(type_of(pc) == PAWN); - assert(to == st->previous->epSquare); - assert(relative_rank(us, to) == RANK_6); - assert(piece_on(capsq) == NO_PIECE); - assert(st->capturedPiece == make_piece(~us, PAWN)); - } + if (type_of(m) == ENPASSANT) + { + capsq -= pawn_push(us); + + assert(type_of(pc) == PAWN); + assert(to == st->previous->epSquare); + assert(relative_rank(us, to) == RANK_6); + assert(piece_on(capsq) == NO_PIECE); + assert(st->capturedPiece == make_piece(~us, PAWN)); + } - put_piece(st->capturedPiece, capsq); // Restore the captured piece + put_piece(st->capturedPiece, capsq); // Restore the captured piece + if (st->capturedGate > NO_GATE) + uncapture_gate(~us, capsq); + } } + + --gamePly; } // Finally point our state pointer back to the previous state st = st->previous; - --gamePly; assert(pos_is_ok()); } @@ -950,19 +1112,52 @@ void Position::undo_move(Move m) { /// Position::do_castling() is a helper used to do/undo a castling move. This /// is a bit tricky in Chess960 where from/to squares can overlap. template -void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { +void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto, Key& k) { bool kingSide = to > from; rfrom = to; // Castling is encoded as "king captures friendly rook" rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + // Ungate piece + if (!Do && (pieces() & (SquareBB[from] | rfrom))) + { + Square s = pieces() & from ? from : rfrom; + if (s != to && s != rto) + ungate_piece(us, s); + else if (st->capturedGate > NO_GATE) + uncapture_gate(us, s); + } + // Remove both pieces first since squares could overlap in Chess960 remove_piece(make_piece(us, KING), Do ? from : to); remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us put_piece(make_piece(us, KING), Do ? to : from); put_piece(make_piece(us, ROOK), Do ? rto : rfrom); + + // Gate piece + if (Do && (gateBB & (SquareBB[from] | rfrom))) + { + Square s = gateBB & from ? from : rfrom; + // In Chess960 only gate if square will be free, else remove gate + if (s != to && s != rto) + { + Piece gated_piece = make_piece(us, gating_piece(s)); + st->psq += PSQT::psq[gated_piece][s] - PSQT::psq_gate[gated_piece][file_of(s)]; + k ^= Zobrist::psq[gated_piece][s] ^ Zobrist::psq_gate[gated_piece][file_of(s)]; + gate_piece(us, s); + } + else + { + Piece gated_piece = make_piece(us, gating_piece(s)); + st->psq -= PSQT::psq_gate[gated_piece][file_of(s)]; + k ^= Zobrist::psq_gate[gated_piece][file_of(s)]; + capture_gate(us, s); + } + } + else + st->capturedGate = NO_GATE; } @@ -1265,8 +1460,8 @@ bool Position::pos_is_ok() const { constexpr bool Fast = true; // Quick (default) or full check? if ( (sideToMove != WHITE && sideToMove != BLACK) - || piece_on(square(WHITE)) != W_KING - || piece_on(square(BLACK)) != B_KING + || piece_on(square(WHITE)) != make_piece(WHITE, KING) + || piece_on(square(BLACK)) != make_piece(BLACK, KING) || ( ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)) assert(0 && "pos_is_ok: Default"); @@ -1274,14 +1469,14 @@ bool Position::pos_is_ok() const { if (Fast) return true; - if ( pieceCount[W_KING] != 1 - || pieceCount[B_KING] != 1 + if ( pieceCount[make_piece(WHITE, KING)] != 1 + || pieceCount[make_piece(BLACK, KING)] != 1 || attackers_to(square(~sideToMove)) & pieces(sideToMove)) assert(0 && "pos_is_ok: Kings"); if ( (pieces(PAWN) & (Rank1BB | Rank8BB)) - || pieceCount[W_PAWN] > 8 - || pieceCount[B_PAWN] > 8) + || pieceCount[make_piece(WHITE, PAWN)] > 8 + || pieceCount[make_piece(BLACK, PAWN)] > 8) assert(0 && "pos_is_ok: Pawns"); if ( (pieces(WHITE) & pieces(BLACK)) @@ -1300,16 +1495,18 @@ bool Position::pos_is_ok() const { if (std::memcmp(&si, st, sizeof(StateInfo))) assert(0 && "pos_is_ok: State"); - for (Piece pc : Pieces) - { - if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) - || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) - assert(0 && "pos_is_ok: Pieces"); - - for (int i = 0; i < pieceCount[pc]; ++i) - if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) - assert(0 && "pos_is_ok: Index"); - } + for (Color c = WHITE; c <= BLACK; ++c) + for (PieceType pt = PAWN; pt <= KING; ++pt) + { + Piece pc = make_piece(c, pt); + if ( pieceCount[pc] != popcount(pieces(c, pt)) + || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) + assert(0 && "pos_is_ok: Pieces"); + + for (int i = 0; i < pieceCount[pc]; ++i) + if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) + assert(0 && "pos_is_ok: Index"); + } for (Color c = WHITE; c <= BLACK; ++c) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) diff --git a/src/position.h b/src/position.h index a40687e7..507addbe 100644 --- a/src/position.h +++ b/src/position.h @@ -50,6 +50,9 @@ struct StateInfo { Key key; Bitboard checkersBB; Piece capturedPiece; + Gate gate; + Gate capturedGate; + PieceType removedGatingType; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; @@ -95,7 +98,12 @@ class Position { template int count(Color c) const; template int count() const; template const Square* squares(Color c) const; + const Square* squares(Color c, PieceType pt) const; template Square square(Color c) const; + Bitboard gates() const; + PieceType gating_piece(Gate gate) const; + PieceType gating_piece(Square s) const; + Square gating_square(Color c, Gate gate) const; // Castling int can_castle(Color c) const; @@ -111,9 +119,8 @@ class Position { // Attacks to/from a given square Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; - Bitboard attacks_from(PieceType pt, Square s) const; - template Bitboard attacks_from(Square s) const; - template Bitboard attacks_from(Square s, Color c) const; + Bitboard attacks_from(Color c, PieceType pt, Square s) const; + template Bitboard attacks_from(Color c, Square s) const; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; // Properties of moves @@ -158,6 +165,9 @@ class Position { Score psq_score() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; + GamePhase game_phase() const; + Gate gate_count() const; + Gate setup_count(Color c) const; // Position consistency check, for debugging bool pos_is_ok() const; @@ -170,17 +180,33 @@ class Position { void set_check_info(StateInfo* si) const; // Other helpers + void set_gating_type(PieceType pt); + void unset_gating_type(); + void add_gate(Color c, Square s, Gate gate); + void remove_gate(Color c, Square s, Gate gate); + void put_gating_piece(Color c, Square s); + void remove_gating_piece(Color c, Square s); + void capture_gate(Color c, Square s); + void uncapture_gate(Color c, Square s); + void gate_piece(Color c, Square s); + void ungate_piece(Color c, Square s); void put_piece(Piece pc, Square s); void remove_piece(Piece pc, Square s); void move_piece(Piece pc, Square from, Square to); template - void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); + void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto, Key& k); // Data members Piece board[SQUARE_NB]; + Gate gateBoard[SQUARE_NB]; + PieceType gatingPieces[GATE_NB]; + Square gatingSquares[COLOR_NB][GATE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; + Bitboard gateBB; int pieceCount[PIECE_NB]; + Gate gateCount; + Gate setupCount[COLOR_NB]; Square pieceList[PIECE_NB][16]; int index[SQUARE_NB]; int castlingRightsMask[SQUARE_NB]; @@ -208,6 +234,8 @@ inline Piece Position::piece_on(Square s) const { } inline Piece Position::moved_piece(Move m) const { + if (type_of(m) == SET_GATING_TYPE || type_of(m) == PUT_GATING_PIECE) + return make_piece(sideToMove, gating_type(m)); return board[from_sq(m)]; } @@ -247,11 +275,35 @@ template inline const Square* Position::squares(Color c) const { return pieceList[make_piece(c, Pt)]; } +inline const Square* Position::squares(Color c, PieceType pt) const { + return pieceList[make_piece(c, pt)]; +} + template inline Square Position::square(Color c) const { assert(pieceCount[make_piece(c, Pt)] == 1); return pieceList[make_piece(c, Pt)][0]; } +inline Bitboard Position::gates() const { + return gateBB; +} + +inline PieceType Position::gating_piece(Gate gate) const { + assert(gate >= NO_GATE && gate < GATE_NB); + return gatingPieces[gate]; +} + +inline PieceType Position::gating_piece(Square s) const { + assert(rank_of(s) == RANK_1 || rank_of(s) == RANK_8); + assert(gateBoard[s] > NO_GATE && gateBoard[s] < GATE_NB); + return gating_piece(gateBoard[s]); +} + +inline Square Position::gating_square(Color c, Gate gate) const { + assert(gate < GATE_NB); + return gatingSquares[c][gate]; +} + inline Square Position::ep_square() const { return st->epSquare; } @@ -273,20 +325,12 @@ inline Square Position::castling_rook_square(CastlingRight cr) const { } template -inline Bitboard Position::attacks_from(Square s) const { - assert(Pt != PAWN); - return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) - : Pt == QUEEN ? attacks_from(s) | attacks_from(s) - : PseudoAttacks[Pt][s]; +inline Bitboard Position::attacks_from(Color c, Square s) const { + return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]); } -template<> -inline Bitboard Position::attacks_from(Square s, Color c) const { - return PawnAttacks[c][s]; -} - -inline Bitboard Position::attacks_from(PieceType pt, Square s) const { - return attacks_bb(pt, s, byTypeBB[ALL_PIECES]); +inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const { + return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]); } inline Bitboard Position::attackers_to(Square s) const { @@ -338,6 +382,20 @@ inline Value Position::non_pawn_material() const { return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; } +inline GamePhase Position::game_phase() const { + return gateCount < GATE_NB - 1 ? GAMEPHASE_SELECTION + : setupCount[sideToMove] < GATE_NB - 1 ? GAMEPHASE_PLACING + : GAMEPHASE_PLAYING; +} + +inline Gate Position::gate_count() const { + return gateCount; +} + +inline Gate Position::setup_count(Color c) const { + return setupCount[c]; +} + inline int Position::game_ply() const { return gamePly; } @@ -347,8 +405,8 @@ inline int Position::rule50_count() const { } inline bool Position::opposite_bishops() const { - return pieceCount[W_BISHOP] == 1 - && pieceCount[B_BISHOP] == 1 + return pieceCount[make_piece(WHITE, BISHOP)] == 1 + && pieceCount[make_piece(BLACK, BISHOP)] == 1 && opposite_colors(square(WHITE), square(BLACK)); } @@ -375,6 +433,68 @@ inline Thread* Position::this_thread() const { return thisThread; } +inline void Position::set_gating_type(PieceType pt) { + assert(gateCount < GATE_NB); + gatingPieces[++gateCount] = pt; +} + +inline void Position::unset_gating_type() { + assert(gateCount > NO_GATE); + gatingPieces[gateCount--] = NO_PIECE_TYPE; +} + +inline void Position::add_gate(Color c, Square s, Gate gate) { + assert(gate > NO_GATE && gate < GATE_NB); + assert(!(gateBB & s)); + assert(gateBoard[s] == NO_GATE); + gateBoard[s] = gate; + gatingSquares[c][gate] = s; + gateBB |= s; +} + +inline void Position::remove_gate(Color c, Square s, Gate gate) { + assert(gate > NO_GATE && gate < GATE_NB); + assert(gateBB & s); + assert(gateBoard[s] > NO_GATE); + gateBoard[s] = NO_GATE; + gatingSquares[c][gate] = SQ_NONE; + gateBB ^= s; +} + +inline void Position::put_gating_piece(Color c, Square s) { + add_gate(c, s, ++setupCount[c]); +} + +inline void Position::remove_gating_piece(Color c, Square s) { + remove_gate(c, s, setupCount[c]--); +} + +inline void Position::capture_gate(Color c, Square s) { + assert(gatingPieces[gateBoard[s]] > NO_PIECE_TYPE); + st->capturedGate = gateBoard[s]; + remove_gate(c, s, gateBoard[s]); +} + +inline void Position::uncapture_gate(Color c, Square s) { + assert(board[s] != NO_PIECE); + add_gate(c, s, st->capturedGate); +} + +inline void Position::gate_piece(Color c, Square s) { + assert(gatingPieces[gateBoard[s]] > NO_PIECE_TYPE); + assert(board[s] == NO_PIECE); + st->gate = gateBoard[s]; + put_piece(make_piece(c, gatingPieces[gateBoard[s]]), s); + remove_gate(c, s, gateBoard[s]); +} + +inline void Position::ungate_piece(Color c, Square s) { + assert(board[s] != NO_PIECE); + assert(gatingPieces[st->gate] == type_of(board[s])); + add_gate(c, s, st->gate); + remove_piece(make_piece(c, gatingPieces[st->gate]), s); +} + inline void Position::put_piece(Piece pc, Square s) { board[s] = pc; diff --git a/src/psqt.cpp b/src/psqt.cpp index e0f15c06..3194621a 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -23,8 +23,8 @@ #include "types.h" Value PieceValue[PHASE_NB][PIECE_NB] = { - { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, - { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } + { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, CannonValueMg, LeopardValueMg, ArchbishopValueMg, ChancellorValueMg, SpiderValueMg, DragonValueMg, UnicornValueMg, HawkValueMg, ElephantValueMg, FortressValueMg }, + { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, CannonValueEg, LeopardValueEg, ArchbishopValueEg, ChancellorValueEg, SpiderValueEg, DragonValueEg, UnicornValueEg, HawkValueEg, ElephantValueEg, FortressValueEg } }; namespace PSQT { @@ -35,7 +35,7 @@ namespace PSQT { // type on a given square a (middlegame, endgame) score pair is assigned. Table // is defined for files A..D and white side: it is symmetric for black side and // second half of the files. -constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { +constexpr Score Bonus[PIECE_TYPE_NB][RANK_NB][int(FILE_NB) / 2] = { { }, { // Pawn { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, @@ -86,6 +86,106 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } }, + { // Cannon + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Leopard + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Archbishop + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Chancellor + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Spider + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Dragon + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Unicorn + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Hawk + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Elephant + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, + { // Fortress + { S( 0,-71), S(-4,-56), S(-3,-42), S(-1,-29) }, + { S(-4,-56), S( 6,-30), S( 9,-21), S( 8, -5) }, + { S(-2,-39), S( 6,-17), S( 9, -8), S( 9, 5) }, + { S(-1,-29), S( 8, -5), S(10, 9), S( 7, 19) }, + { S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 21) }, + { S(-2,-40), S( 6,-16), S( 8,-10), S(10, 3) }, + { S(-2,-55), S( 7,-30), S( 7,-21), S( 6, -6) }, + { S(-1,-74), S(-4,-55), S(-1,-43), S( 0,-30) } + }, { // King { S(267, 0), S(320, 48), S(270, 75), S(195, 84) }, { S(264, 43), S(304, 92), S(238,143), S(180,132) }, @@ -101,14 +201,17 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { #undef S Score psq[PIECE_NB][SQUARE_NB]; +Score psq_gate[PIECE_NB][FILE_NB]; // init() initializes piece-square tables: the white halves of the tables are // copied from Bonus[] adding the piece value, then the black halves of the // tables are initialized by flipping and changing the sign of the white scores. void init() { - for (Piece pc = W_PAWN; pc <= W_KING; ++pc) + for (PieceType pt = PAWN; pt <= KING; ++pt) { + Piece pc = make_piece(WHITE, pt); + PieceValue[MG][~pc] = PieceValue[MG][pc]; PieceValue[EG][~pc] = PieceValue[EG][pc]; @@ -120,6 +223,11 @@ void init() { psq[ pc][ s] = score + Bonus[pc][rank_of(s)][f]; psq[~pc][~s] = -psq[pc][s]; } + for (File f = FILE_A; f <= FILE_H; ++f) + { + psq_gate[ pc][ f] = score; + psq_gate[~pc][~f] = -psq_gate[pc][f]; + } } } diff --git a/src/search.cpp b/src/search.cpp index fb5dfd00..0fc20cf4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -110,10 +110,7 @@ namespace { void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus); inline bool gives_check(const Position& pos, Move move) { - Color us = pos.side_to_move(); - return type_of(move) == NORMAL && !(pos.blockers_for_king(~us) & pos.pieces(us)) - ? pos.check_squares(type_of(pos.moved_piece(move))) & to_sq(move) - : pos.gives_check(move); + return pos.gives_check(move); } // perft() is our utility to verify move generation. All the leaf nodes up @@ -737,6 +734,7 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode + && pos.game_phase() == GAMEPHASE_PLAYING && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 22500 && eval >= beta diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index da6dc49f..39511401 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1242,7 +1242,7 @@ void Tablebases::init(const std::string& paths) { if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0 { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - if ((PseudoAttacks[KING][s1] | s1) & s2) + if ((PseudoAttacks[WHITE][KING][s1] | s1) & s2) continue; // Illegal position else if (!off_A1H8(s1) && off_A1H8(s2) > 0) diff --git a/src/types.h b/src/types.h index ab5e1b88..dafb40cc 100644 --- a/src/types.h +++ b/src/types.h @@ -122,9 +122,14 @@ enum Move : int { enum MoveType { NORMAL, - PROMOTION = 1 << 14, - ENPASSANT = 2 << 14, - CASTLING = 3 << 14 + ENPASSANT = 1 << 12, + CASTLING = 2 << 12, + PROMOTION = 3 << 12, + PROMOTION_STRAIGHT = PROMOTION, + PROMOTION_LEFT = 4 << 12, + PROMOTION_RIGHT = 5 << 12, + SET_GATING_TYPE = 6 << 12, + PUT_GATING_PIECE = 7 << 12, }; enum Color { @@ -151,6 +156,13 @@ template struct MakeCastling { : S == QUEEN_SIDE ? BLACK_OOO : BLACK_OO; }; +enum GamePhase { + GAMEPHASE_SELECTION, + GAMEPHASE_PLACING, + GAMEPHASE_PLAYING, + GAMEPHASE_NB +}; + enum Phase { PHASE_ENDGAME, PHASE_MIDGAME = 128, @@ -182,28 +194,52 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, - PawnValueMg = 171, PawnValueEg = 240, - KnightValueMg = 764, KnightValueEg = 848, - BishopValueMg = 826, BishopValueEg = 891, - RookValueMg = 1282, RookValueEg = 1373, - QueenValueMg = 2500, QueenValueEg = 2670, + PawnValueMg = 171, PawnValueEg = 240, + KnightValueMg = 764, KnightValueEg = 848, + BishopValueMg = 826, BishopValueEg = 891, + RookValueMg = 1282, RookValueEg = 1373, + QueenValueMg = 2500, QueenValueEg = 2670, + CannonValueMg = 1710, CannonValueEg = 2239, + LeopardValueMg = 1648, LeopardValueEg = 2014, + ArchbishopValueMg = 2036, ArchbishopValueEg = 2202, + ChancellorValueMg = 2251, ChancellorValueEg = 2344, + SpiderValueMg = 2321, SpiderValueEg = 2718, + DragonValueMg = 3280, DragonValueEg = 2769, + UnicornValueMg = 1584, UnicornValueEg = 1772, + HawkValueMg = 1537, HawkValueEg = 1561, + ElephantValueMg = 1770, ElephantValueEg = 2000, + FortressValueMg = 1956, FortressValueEg = 2100, MidgameLimit = 15258, EndgameLimit = 3915 }; +const int PIECE_TYPE_BITS = 5; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS) +const int MAX_GATES = 2; + +enum Gate { + NO_GATE, GATE_1, GATE_2, + GATE_NB = MAX_GATES + 1 +}; + enum PieceType { - NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, + NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, + CANNON, LEOPARD, ARCHBISHOP, CHANCELLOR, SPIDER, + DRAGON, UNICORN, HAWK, ELEPHANT, FORTRESS, KING, ALL_PIECES = 0, - PIECE_TYPE_NB = 8 + + PIECE_TYPE_NB = 1 << PIECE_TYPE_BITS }; +static_assert(PIECE_TYPE_BITS <= 6, "PIECE_TYPE uses more than 6 bit"); +static_assert(!(PIECE_TYPE_NB & (PIECE_TYPE_NB - 1)), "PIECE_TYPE_NB is not a power of 2"); enum Piece { NO_PIECE, - W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, - B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, - PIECE_NB = 16 + PIECE_NB = 2 * PIECE_TYPE_NB }; +const std::string PieceToChar( " PNBRQCLAOSDUHEFK" + std::string(PIECE_TYPE_NB - KING - 1, ' ') + + " pnbrqclaosduhefk" + std::string(PIECE_TYPE_NB - KING - 1, ' ')); + extern Value PieceValue[PHASE_NB][PIECE_NB]; enum Depth : int { @@ -288,7 +324,9 @@ inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } #define ENABLE_INCR_OPERATORS_ON(T) \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ -inline T& operator--(T& d) { return d = T(int(d) - 1); } +inline T& operator--(T& d) { return d = T(int(d) - 1); } \ +inline T operator++(T& d, int) { T a = d; d = T(int(d) + 1); return a; } \ +inline T operator--(T& d, int) { T a = d; d = T(int(d) - 1); return a; } #define ENABLE_FULL_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \ @@ -304,6 +342,7 @@ ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Depth) ENABLE_FULL_OPERATORS_ON(Direction) +ENABLE_INCR_OPERATORS_ON(Gate) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Color) @@ -363,7 +402,7 @@ constexpr File operator~(File f) { } constexpr Piece operator~(Piece pc) { - return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT + return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece BLACK KNIGHT -> WHITE KNIGHT } constexpr CastlingRight operator|(Color c, CastlingSide s) { @@ -383,16 +422,16 @@ constexpr Square make_square(File f, Rank r) { } constexpr Piece make_piece(Color c, PieceType pt) { - return Piece((c << 3) + pt); + return Piece((c << PIECE_TYPE_BITS) + pt); } constexpr PieceType type_of(Piece pc) { - return PieceType(pc & 7); + return PieceType(pc & (PIECE_TYPE_NB - 1)); } inline Color color_of(Piece pc) { assert(pc != NO_PIECE); - return Color(pc >> 3); + return Color(pc >> PIECE_TYPE_BITS); } constexpr bool is_ok(Square s) { @@ -428,24 +467,45 @@ constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; } -constexpr Square from_sq(Move m) { - return Square((m >> 6) & 0x3F); +inline MoveType type_of(Move m) { + MoveType t = MoveType(m & (15 << 12)); + if (t == PROMOTION_STRAIGHT || t == PROMOTION_LEFT || t == PROMOTION_RIGHT) + return PROMOTION; + return t; } constexpr Square to_sq(Move m) { return Square(m & 0x3F); } -constexpr int from_to(Move m) { - return m & 0xFFF; +inline Square from_sq(Move m) { + if (type_of(m) == PROMOTION) + { + Square to = to_sq(m); + MoveType t = MoveType(m & (15 << 12)); + // Assume here that promotion occur only for relative ranks >= RANK_5. + Direction up = (to & 32) ? NORTH : SOUTH; + if (t == PROMOTION_STRAIGHT) + return to - up; + if (t == PROMOTION_LEFT) + return to - up - WEST; + if (t == PROMOTION_RIGHT) + return to - up - EAST; + assert(false); + } + return Square((m >> 6) & 0x3F); +} + +inline int from_to(Move m) { + return to_sq(m) + (from_sq(m) << 6); } -constexpr MoveType type_of(Move m) { - return MoveType(m & (3 << 14)); +inline PieceType promotion_type(Move m) { + return type_of(m) == PROMOTION ? PieceType((m >> 6) & 63) : NO_PIECE_TYPE; } -constexpr PieceType promotion_type(Move m) { - return PieceType(((m >> 12) & 3) + KNIGHT); +inline PieceType gating_type(Move m) { + return type_of(m) == SET_GATING_TYPE || type_of(m) == PUT_GATING_PIECE ? PieceType((m >> 6) & 63) : NO_PIECE_TYPE; } inline Move make_move(Square from, Square to) { @@ -453,12 +513,15 @@ inline Move make_move(Square from, Square to) { } template -constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { - return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); +inline Move make(Square from, Square to, PieceType pt = NO_PIECE_TYPE) { + if (T == PROMOTION_STRAIGHT || T == PROMOTION_LEFT || T == PROMOTION_RIGHT + || T == SET_GATING_TYPE || T == PUT_GATING_PIECE) + return Move(T + (pt << 6) + to); + return Move(T + (from << 6) + to); } -constexpr bool is_ok(Move m) { - return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE +inline bool is_ok(Move m) { + return from_sq(m) != to_sq(m) || type_of(m) == SET_GATING_TYPE || type_of(m) == PUT_GATING_PIECE; // Catch MOVE_NULL and MOVE_NONE } #endif // #ifndef TYPES_H_INCLUDED diff --git a/src/uci.cpp b/src/uci.cpp index 72eda4a0..189f4d09 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -291,10 +291,17 @@ string UCI::move(Move m, bool chess960) { if (type_of(m) == CASTLING && !chess960) to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); + if (type_of(m) == SET_GATING_TYPE) + return string(1, PieceToChar[make_piece(WHITE, gating_type(m))]); + + if (type_of(m) == PUT_GATING_PIECE) + return string{PieceToChar[make_piece(WHITE, gating_type(m))], '@'} + + string{char('a' + file_of(to)), rank_of(to) == RANK_8 ? '9' : '0'}; + string move = UCI::square(from) + UCI::square(to); if (type_of(m) == PROMOTION) - move += " pnbrqk"[promotion_type(m)]; + move += PieceToChar[make_piece(BLACK, promotion_type(m))]; return move; }