To quickly find bugs in my generator, i'm trying to compile qperft as a cpp file. I'm too lazy to change anything in the code (for now) but i would suggest this simple changes, that don't impact in C compiling and lets the code compile in c++:
1) rename all "new" variable to "_new"
2) cast "unsigned char*" to "char*":
board_init(board); -> board_init((char*)board);
pboard(board, 12, 0); -> pboard((char*)board, 12, 0);
3) add parenthesis to avoid warnings on shifts:
PUSH(y + 1 << 8, z)
should be
PUSH(y + (1 << 8), z) or PUSH((y + 1) << 8, z)?
Now i'm trying to embed qperft in a C++ class... when finished, i'll post the modified code, for anybody would like to use it (if Muller agreed, of course).
Compiling qperft as c++ code
Moderators: hgm, Rebel, chrisw
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: Compiling qperft as c++ code
The first step seems to works. I've only added a single function, copying the main function:
Now i try to call the function from my engine, to compare perft results at any depth with those of the engine. Of course qperft uses global variables and this will let the function QPerft not reentrant (but this is not a problem, for my purpose).
Code: Select all
bool QPerft(int Depth, long long *Counts, char *FEN, int HashSize = 0, int Split = 0)
{
int i, j, k, Col;
clock_t t;
double sumx = 0, sumx2 = 0, tot = 0;
if (Dep < 0) return false;
if (HashSize>0)
{
HashSection = (1 << HashSize - 3) - 1; HashSize = (1 << HashSize) - 2;
Hash = (union _bucket *) calloc(HashSize + 4, sizeof(union _bucket));
Hash = (union _bucket *) (((int)Hash) + 63 & ~63);
HashFlag++;
for (i = 128; i<1040; i++) Keys[i] = rand() >> 6;
}
delta_init();
piece_init();
board_init((char*)board);
Col = ReadFEN(FEN);
setup();
pboard((char*)board, 12, 0);
if (Col < 0)
{
return false;
}
for (i = 1; i <= Dep; i++)
{
int n;
t = clock();
count = epcnt = xcnt = ckcnt = cascnt = promcnt = 0;
for (j = 0; j<10; j++) accept[j] = reject[j] = 0, ttt[j] = t;
if (i == 1) leaf_perft(Col, (epSqr ^ 16) << 24, i, 1); else
perft(Col, (epSqr ^ 16) << 24, i, 1);
t = clock() - t;
Counts[Dep - 1] = count;
}
return true;
}
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: Compiling qperft as c++ code
Here's the full qperft that compiles fine on Visual Studio 2013, 64 bit, as cpp file:
And here's a sample of usage of the code:
I hope that this could help debugging my engine... and yours too
Code: Select all
bool QPerft(int Depth, long long *Counts, char *sFEN, int HashSize, int Split)
{
int i, j, k, Col;
clock_t t;
double sumx = 0, sumx2 = 0, tot = 0;
Dep = Depth;
if (Dep < 0) return false;
if (HashSize>0)
{
HashSection = (1 << HashSize - 3) - 1; HashSize = (1 << HashSize) - 2;
Hash = (union _bucket *) calloc(HashSize + 4, sizeof(union _bucket));
Hash = (union _bucket *) (((int)Hash) + 63 & ~63);
HashFlag++;
for (i = 128; i<1040; i++) Keys[i] = rand() >> 6;
}
delta_init();
piece_init();
board_init((char*)board);
Col = ReadFEN(sFEN);
setup();
// pboard((char*)board, 12, 0);
if (Col < 0)
{
return false;
}
for (i = 1; i <= Dep; i++)
{
int n;
t = clock();
count = epcnt = xcnt = ckcnt = cascnt = promcnt = 0;
for (j = 0; j<10; j++) accept[j] = reject[j] = 0, ttt[j] = t;
if (i == 1) leaf_perft(Col, (epSqr ^ 16) << 24, i, 1); else
perft(Col, (epSqr ^ 16) << 24, i, 1);
t = clock() - t;
Counts[Dep - 1] = count;
}
return true;
}
Code: Select all
uint64_t clsEngineSimple2::PerftLoop(int iDepth, clsSimpleMove *pPreviousMove)
{
// tsq back_board[16 * 16]; memcpy(back_board, board, sizeof(board));
clsSimpleMove moves[256]; pMoves = pGenMove = moves;
uint64_t nMoves = 0;
uint64_t mkMoves = MakeMoves(pPreviousMove);
clsSimpleMove * pLocalLastMove = pGenMove;
for (clsSimpleMove *pLoopMove = moves; pLoopMove<pLocalLastMove; pLoopMove++)
{
if (pLoopMove->pTaken == &boKings)
return 0;
if (ExecuteMove(pLoopMove))
{
if (!IsInCheck(boKings & boFriends))
{
if (iDepth == 0)
{
++nMoves;
} else
{
SwapColor();
nMoves += PerftLoop(iDepth - 1, pLoopMove);
long long *Counts = new long long[iDepth];
clsString sFEN = GetFEN();
QPerft(iDepth, Counts, sFEN.GetString(), 0, 0);
uint64_t qMoves = Counts[iDepth - 1];
delete[] Counts;
if (nMoves != qMoves)
{
DebugBoard();
sfout.Push("engine: " + clsString(nMoves) + " qperft: " + clsString(qMoves));
return 0; // set breakpoint here
}
SwapColor();
}
}
UndoMove(pLoopMove);
}
}
return nMoves;
}
-
- Posts: 27808
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: Compiling qperft as c++ code
Why not simply compile it as C?
[Edit] Ah, OK, I see. You want it to be part of another C++ program. But I guess you could still have compiled it as C and then linked it.
If you would use -Wno-parentheses in the CCFLAGS you would get rid of the warnings without having to know which one to use here.stegemma wrote:3) add parenthesis to avoid warnings on shifts:
PUSH(y + 1 << 8, z)
should be
PUSH(y + (1 << 8), z) or PUSH((y + 1) << 8, z)?
[Edit] Ah, OK, I see. You want it to be part of another C++ program. But I guess you could still have compiled it as C and then linked it.
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: Compiling qperft as c++ code
Yes, of course... but i'm too lazy and i've compiled qperft in c++.hgm wrote:Why not simply compile it as C?
If you would use -Wno-parentheses in the CCFLAGS you would get rid of the warnings without having to know which one to use here.stegemma wrote:3) add parenthesis to avoid warnings on shifts:
PUSH(y + 1 << 8, z)
should be
PUSH(y + (1 << 8), z) or PUSH((y + 1) << 8, z)?
[Edit] Ah, OK, I see. You want it to be part of another C++ program. But I guess you could still have compiled it as C and then linked it.
For the parenthesis, you're right but maybe it could help to understand better the code... but still i'm not doing any try to understand, i just use it as it is, so it's enough that it works.
Thanks.
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: Compiling qperft as c++ code
A little correction in the sample code:
and here a screenshot of a bug found in an engine without en-passant:
Code: Select all
uint64_t clsEngineSimple2::PerftLoop(int iDepth)
{
uint64_t nMoves = 0;
uint64_t mkMoves = MakeMoves();
for (pNode->pMove = pNode->pFirstMove; pNode->pMove<(pNode + 1)->pFirstMove; pNode->pMove++)
{
if ((pNode->pMove->boFlags & flg_isTakenKing)!=flg_empty)
{
nMoves = 0;
break;
}
if (ExecuteMove(pNode->pMove))
{
if (!IsInCheck(board.boKings & board.boFriends))
{
// DebugBoard();
if (iDepth == 0)
{
++nMoves;
} else
{
SwapColor();
++pNode;
uint64_t kMoves = PerftLoop(iDepth - 1);
nMoves += kMoves;
--pNode;
#if 1
long long *Counts = new long long[iDepth];
clsString sFEN = GetFEN();
QPerft(iDepth, Counts, sFEN.GetString(), 0, 0);
uint64_t qMoves = Counts[iDepth - 1];
delete[] Counts;
if (kMoves != qMoves)
{
DebugBoard();
sfout.Push("engine: " + clsString(kMoves) + " qperft: " + clsString(qMoves));
return 0; // set breakpoint here
}
#endif
SwapColor();
}
}
UndoMove(pNode->pMove);
}
}
return nMoves;
}
Code: Select all
# Satana is running
rnbqkbnr
pppppppp
........
........
........
........
PPPPPPPP
RNBQKBNR
perft 1
Nodes: 20, Time: 0 ms, Nodes/s: 20000
perft 2
Nodes: 400, Time: 12 ms, Nodes/s: 30769
perft 3
Nodes: 8902, Time: 210 ms, Nodes/s: 42189
perft 4
Nodes: 197281, Time: 4617 ms, Nodes/s: 42720
perft 5
rnbqkbnr
ppppppp.
.......p
.......P
........
........
PPPPPPP.
RNBQKBNR
engine: 379 qperft: 380
[...]
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: Compiling qperft as c++ code
Embedding quick perft in my engine has speed-up development and this doesn't find bugs only in Satana, but in qperft too!!!
In this position, qperft gives me the wrong count, both in the embedded and in the command line version:
FEN: k7/8/8/8/8/8/3b1P2/4K3 w
Satana reports 4 while perft reports 6:
In this position, qperft gives me the wrong count, both in the embedded and in the command line version:
FEN: k7/8/8/8/8/8/3b1P2/4K3 w
Satana reports 4 while perft reports 6:
Code: Select all
D:\A\Cpp\Scacchi\AltriMotori>perft 1 "k7/8/8/8/8/8/3b1P2/4K3 w"
- - - - - - - - - - - -
- - - - - - - - - - - -
- - k . . . . . . . - -
- - . . . . . . . . - -
- - . . . . . . . . - -
- - . . . . . . . . - -
- - . . . . . . . . - -
- - . . . . . . . . - -
- - . . . b . P . . - -
- - . . . . K . . . - -
- - - - - - - - - - - -
- - - - - - - - - - - -
Quick Perft by H.G. Muller
Perft mode: No hashing, bulk counting in horizon nodes
perft( 1)= 6 ( 0.000 sec)
-
- Posts: 1563
- Joined: Thu Jul 16, 2009 10:47 am
- Location: Almere, The Netherlands
Re: Compiling qperft as c++ code
Funny! Gives Harm Geert something to work on.stegemma wrote:Embedding quick perft in my engine has speed-up development and this doesn't find bugs only in Satana, but in qperft too!!!
In this position, qperft gives me the wrong count, both in the embedded and in the command line version:
FEN: k7/8/8/8/8/8/3b1P2/4K3 w
Satana reports 4 while perft reports 6:
Code: Select all
D:\A\Cpp\Scacchi\AltriMotori>perft 1 "k7/8/8/8/8/8/3b1P2/4K3 w" - - - - - - - - - - - - - - - - - - - - - - - - - - k . . . . . . . - - - - . . . . . . . . - - - - . . . . . . . . - - - - . . . . . . . . - - - - . . . . . . . . - - - - . . . . . . . . - - - - . . . b . P . . - - - - . . . . K . . . - - - - - - - - - - - - - - - - - - - - - - - - - - Quick Perft by H.G. Muller Perft mode: No hashing, bulk counting in horizon nodes perft( 1)= 6 ( 0.000 sec)
-
- Posts: 27808
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: Compiling qperft as c++ code
I guess qperft does not work for positions where you are in check. Amazing that no one noticed this before. But check detection is done based on the previous move, passed to it from the parent node. But in the root there is no previous mode. So it fails to detect contact checks there. (Distant slider checks are always recognized as a side effect of pin detection.)
So some code should be added for detecting contact checks in the given position, and if there are, pass the location of the checker (as to-square of the 'lastPly' argument) to the perft call.
So some code should be added for detecting contact checks in the given position, and if there are, pass the location of the checker (as to-square of the 'lastPly' argument) to the perft call.
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: Compiling qperft as c++ code
In fact it is very strange that a software used for years have such a "simple" bug. The bug occurs anytime the actual color is in check and the piece that give check is near the king. This happens frequently if you call qperft at any ExecuteMove, as i have to do.hgm wrote:I guess qperft does not work for positions where you are in check. Amazing that no one noticed this before. But check detection is done based on the previous move, passed to it from the parent node. But in the root there is no previous mode. So it fails to detect contact checks there. (Distant slider checks are always recognized as a side effect of pin detection.)
So some code should be added for detecting contact checks in the given position, and if there are, pass the location of the checker (as to-square of the 'lastPly' argument) to the perft call.
For now, i've removed the embedded qperft function and i use an old version of satana, that is slower but with a good perft.