(Yes, CPW and Gerd's method of bit removal is much faster than my crap,
I did'nt even see that ^56 removes the lookup table, DOOH!!)
I only generate legal moves now, and I found some ways to improve the speed.
(I have searched CPW and TalkChess, but could not find anything about the stuff I do here)
I included the results for my staged move generators as well, since this counts in Sillycon (my engine).
These results are from my i7 2600K @ 3.4GHz (no turbo boost, no hashing, single core)
Popcounting at leaf nodes.
Code: Select all
r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w QKqk - -
Perft 1=48 t=0.00s 0.00MN/s
Perft 2=2039 t=0.00s 0.00MN/s
Perft 3=97862 t=0.00s 97.86MN/s
Perft 4=4085603 t=0.02s 226.98MN/s
Perft 5=193690690 t=0.50s 389.72MN/s
Perft 6=8031647685 t=24.72s 324.93MN/s
Perft 7=374190009323 t=985.89s 379.54MN/s
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - -
Perft 1=20 t=0.00s 0.00MN/s
Perft 2=400 t=0.00s 0.00MN/s
Perft 3=8902 t=0.00s 0.00MN/s
Perft 4=197281 t=0.00s 65.76MN/s
Perft 5=4865609 t=0.03s 152.05MN/s
Perft 6=119060324 t=0.60s 199.77MN/s
Perft 7=3195901860 t=14.90s 214.45MN/s
Perft 8=84998978956 t=395.18s 215.09MN/s
promotions, noncaptures separately)
Code: Select all
r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w QKqk - -
Perft2 1=48 t=0.00s 0.00MN/s
Perft2 2=2039 t=0.00s 0.00MN/s
Perft2 3=97862 t=0.00s 48.93MN/s
Perft2 4=4085603 t=0.04s 116.73MN/s
Perft2 5=193690690 t=1.16s 166.97MN/s
Perft2 6=8031647685 t=53.45s 150.25MN/s
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - -
Perft2 1=20 t=0.00s 0.00MN/s
Perft2 2=400 t=0.00s 0.00MN/s
Perft2 3=8902 t=0.00s 0.00MN/s
Perft2 4=197281 t=0.01s 39.46MN/s
Perft2 5=4865609 t=0.05s 99.30MN/s
Perft2 6=119060324 t=1.02s 116.50MN/s
Perft2 7=3195901860 t=27.55s 116.00MN/s
Code: Select all
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - -
Quick Perft by H.G. Muller
Perft mode: No hashing, bulk counting in horizon nodes
perft( 1)= 20 ( 0.000 sec)
perft( 2)= 400 ( 0.000 sec)
perft( 3)= 8902 ( 0.000 sec)
perft( 4)= 197281 ( 0.002 sec)
perft( 5)= 4865609 ( 0.038 sec)
perft( 6)= 119060324 ( 0.853 sec)
perft( 7)= 3195901860 (21.731 sec)
r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w QKqk - -
Quick Perft by H.G. Muller
Perft mode: No hashing, bulk counting in horizon nodes
perft( 1)= 48 ( 0.000 sec)
perft( 2)= 2039 ( 0.000 sec)
perft( 3)= 97862 ( 0.001 sec)
perft( 4)= 4085603 ( 0.029 sec)
perft( 5)= 193690690 ( 1.131 sec)
perft( 6)= 8031647685 (50.765 sec)
One might call it Very Lazy make/unmake functions.
One trick I used for IsQRBPMoveIllegal was to avoid using tr->Pieces[0..1][0]
for storing piece info, and by that, if the captured piece is empty (=zero), no disturbance.
Code: Select all
bool IsQRBPMoveIllegal(TTree *tr, int fr, int to, int ksqr)
// Assumes not in check. Detects if QRBP is pinned (not en-passant)
{
int CaptPcs = tr->Board[to] & 7;
tr->Pieces[!tr->wtm][CaptPcs] &= ClrBit(to);
U64 mask = SetBit(fr) | SetBit(to);
tr->Pieces[tr->wtm][ALL] ^= mask;
U64 Pinned = RookAttacks(ksqr,ALLPCS) & ALL_QR(!tr->wtm);
Pinned |= BishopAttacks(ksqr,ALLPCS) & ALL_QB(!tr->wtm);
tr->Pieces[tr->wtm][ALL] ^= mask;
tr->Pieces[!tr->wtm][CaptPcs] |= SetBit(to);
return Pinned > 0;
}
bool IsKnightMoveIllegal(TTree *tr, int fr, int ksqr)
// Assumes not in check. Determines if N is pinned
{
tr->Pieces[tr->wtm][ALL] ^= SetBit(fr);
U64 Pinned = RookAttacks(ksqr,ALLPCS) & ALL_QR(!tr->wtm);
Pinned |= BishopAttacks(ksqr,ALLPCS) & ALL_QB(!tr->wtm);
tr->Pieces[tr->wtm][ALL] ^= SetBit(fr);
return Pinned > 0;
}
bool IsKingMoveIllegal(TTree *tr, int fr, int to)
// Assumed not in check, no castling
{
U64 mask = SetBit(fr) | SetBit(to);
tr->Pieces[tr->wtm][ALL] ^= mask; // No need to remove captured piece
bool Attacked = IsAttacked(tr,to,tr->wtm);
tr->Pieces[tr->wtm][ALL] ^= mask;
return Attacked;
}
Code: Select all
// All 1-step and 2-step pawn moves
Atk = (tr->Pieces[tr->wtm][Pawn]<<8)>>(tr->wtm<<4);
if (Atk &= ~ALLPCS) {
Atk = Atk ^ ((Atk ^ bswap(Atk)) & m1);
do {
to = FirstOne(Atk) ^ o1;
fr = to - 8 + (tr->wtm<<4);
if (SetBit(fr) & KVA && IsQRBPMoveIllegal(tr, fr, to, ksqr)) continue;
if (SetBit(to) & (RANK_A8H8 | RANK_A1H1)) { // UnderPromotions
*M=(fr<<6)+to+(Pawn<<12)+(tr->wtm<<15)+(tr->wtm<<23); // Colour of prompiece added
*(M+1) = *M | (Bishop<<20); // Promote to RBN
*(M+2) = *M | (Knight<<20);
*M |= (Rook<<20); M += 3;
continue;
}
if (SetBit(fr) & RANK_2(tr->wtm)) { // Double pawn push
int to2 = fr+16-32*tr->wtm;
if (tr->Board[to2]==Empty) *M++=(fr<<6)+to2+(Pawn<<12)+(tr->wtm<<15);
}
*M++=(fr<<6)+to+(Pawn<<12)+(tr->wtm<<15); // Normal pawn push
} while (Atk &= Atk - 1ull);
}
// King
fr = ksqr;
if (Atk = KingAttacks[fr] & ~ALLPCS) {
Atk = Atk ^ ((Atk ^ bswap(Atk)) & m1);
fr = (fr<<6) + (King<<12) + (tr->wtm<<15);
do {
to = FirstOne(Atk) ^ o1;
if (IsKingMoveIllegal(tr, ksqr, to)) continue;
*M++ = fr+to;
} while (Atk &= Atk - 1ull);
}
Some pawn moves at leaves (PopCount version)
// En-passant
TMove Mtemp[2]; // Also used for MovgenCastling()
int EpSqr = tr->Game_EpSqr[tr->Game_Ply+ply];
if (EpSqr) {
U64 Atk = KingAttacks[EpSqr] & RANK_5(tr->wtm);
if (Atk & KVA) nSafeMoves += MovgenEnPassant(tr, ply, Mtemp) - Mtemp;
else nSafeMoves += PopCount(Atk & tr->Pieces[tr->wtm][Pawn]);
}
// All 1-step and 2-step moves
Pcs = tr->Pieces[tr->wtm][Pawn] & ~KVA; // Pawns outside pin range
Atk = ~ALLPCS & ((Pcs<<8)>>(tr->wtm<<4));
nSafeMoves += PopCount(Atk); // Safe 1-step moves
nSafeMoves += 3*PopCount(Atk & (RANK_A8H8 | RANK_A1H1)); // Promotions
Atk &= RANK_3(tr->wtm);
Atk = ~ALLPCS & ((Atk<<8)>>(tr->wtm<<4));
nSafeMoves += PopCount(Atk); // Safe 2-step moves
Pcs = tr->Pieces[tr->wtm][Pawn] & KVA; // Pawns inside pin range
Atk = ~ALLPCS & ((Pcs<<8)>>(tr->wtm<<4));
if (Atk) {
do {
to = FirstOne(Atk);
fr = to - 8 + (tr->wtm<<4);
if (SetBit(fr) & KVA && IsQRBPMoveIllegal(tr, fr, to, ksqr)) continue;
if (SetBit(to) & (RANK_A8H8 | RANK_A1H1)) { // Promotions
nSafeMoves += 4;
continue;
}
nSafeMoves += 1; // Single pawn push
if (SetBit(fr) & RANK_2(tr->wtm)) { // Double pawn push
int to2 = fr+16-32*tr->wtm;
if (tr->Board[to2]==Empty) nSafeMoves += 1;
}
} while (Atk &= Atk - 1ull);
}
Code: Select all
KVA = RookAttacks(ksqr,ALLPCS) | BishopAttacks(ksqr,ALLPCS);