Compiling qperft as c++ code

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Compiling qperft as c++ code

Post by stegemma »

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).
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: Compiling qperft as c++ code

Post by stegemma »

The first step seems to works. I've only added a single function, copying the main function:

Code: Select all

bool QPerft&#40;int Depth, long long *Counts, char *FEN, int HashSize = 0, int Split = 0&#41;
&#123;
	int i, j, k, Col;
	clock_t t;
	double sumx = 0, sumx2 = 0, tot = 0;

	if &#40;Dep < 0&#41; return false;

	if &#40;HashSize>0&#41;
	&#123;
		HashSection = &#40;1 << HashSize - 3&#41; - 1; HashSize = &#40;1 << HashSize&#41; - 2;
		Hash = &#40;union _bucket *) calloc&#40;HashSize + 4, sizeof&#40;union _bucket&#41;);
		Hash = &#40;union _bucket *) ((&#40;int&#41;Hash&#41; + 63 & ~63&#41;;
		HashFlag++;
		for &#40;i = 128; i<1040; i++) Keys&#91;i&#93; = rand&#40;) >> 6;
	&#125;

	delta_init&#40;);

	piece_init&#40;);

	board_init&#40;&#40;char*&#41;board&#41;;

	Col = ReadFEN&#40;FEN&#41;;

	setup&#40;);

	pboard&#40;&#40;char*&#41;board, 12, 0&#41;;

	if &#40;Col < 0&#41;
	&#123;
		return false;
	&#125;

	for &#40;i = 1; i <= Dep; i++)
	&#123;
		int n;
		t = clock&#40;);
		count = epcnt = xcnt = ckcnt = cascnt = promcnt = 0;
		for &#40;j = 0; j<10; j++) accept&#91;j&#93; = reject&#91;j&#93; = 0, ttt&#91;j&#93; = t;
		if &#40;i == 1&#41; leaf_perft&#40;Col, &#40;epSqr ^ 16&#41; << 24, i, 1&#41;; else
			perft&#40;Col, &#40;epSqr ^ 16&#41; << 24, i, 1&#41;;
		t = clock&#40;) - t;
		Counts&#91;Dep - 1&#93; = count;
	&#125;
	return true;
&#125;
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).
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: Compiling qperft as c++ code

Post by stegemma »

Here's the full qperft that compiles fine on Visual Studio 2013, 64 bit, as cpp file:

Code: Select all

bool QPerft&#40;int Depth, long long *Counts, char *sFEN, int HashSize, int Split&#41;
&#123;
	int i, j, k, Col;
	clock_t t;
	double sumx = 0, sumx2 = 0, tot = 0;

	Dep = Depth;

	if &#40;Dep < 0&#41; return false;

	if &#40;HashSize>0&#41;
	&#123;
		HashSection = &#40;1 << HashSize - 3&#41; - 1; HashSize = &#40;1 << HashSize&#41; - 2;
		Hash = &#40;union _bucket *) calloc&#40;HashSize + 4, sizeof&#40;union _bucket&#41;);
		Hash = &#40;union _bucket *) ((&#40;int&#41;Hash&#41; + 63 & ~63&#41;;
		HashFlag++;
		for &#40;i = 128; i<1040; i++) Keys&#91;i&#93; = rand&#40;) >> 6;
	&#125;

	delta_init&#40;);

	piece_init&#40;);

	board_init&#40;&#40;char*&#41;board&#41;;

	Col = ReadFEN&#40;sFEN&#41;;

	setup&#40;);

	// pboard&#40;&#40;char*&#41;board, 12, 0&#41;;

	if &#40;Col < 0&#41;
	&#123;
		return false;
	&#125;

	for &#40;i = 1; i <= Dep; i++)
	&#123;
		int n;
		t = clock&#40;);
		count = epcnt = xcnt = ckcnt = cascnt = promcnt = 0;
		for &#40;j = 0; j<10; j++) accept&#91;j&#93; = reject&#91;j&#93; = 0, ttt&#91;j&#93; = t;
		if &#40;i == 1&#41; leaf_perft&#40;Col, &#40;epSqr ^ 16&#41; << 24, i, 1&#41;; else
			perft&#40;Col, &#40;epSqr ^ 16&#41; << 24, i, 1&#41;;
		t = clock&#40;) - t;
		Counts&#91;Dep - 1&#93; = count;
	&#125;
	return true;
&#125;

And here's a sample of usage of the code:

Code: Select all


	uint64_t clsEngineSimple2&#58;&#58;PerftLoop&#40;int iDepth, clsSimpleMove *pPreviousMove&#41;
	&#123;
		// tsq back_board&#91;16 * 16&#93;; memcpy&#40;back_board, board, sizeof&#40;board&#41;);
		clsSimpleMove moves&#91;256&#93;;		pMoves = pGenMove = moves;
		uint64_t nMoves = 0;
		uint64_t mkMoves = MakeMoves&#40;pPreviousMove&#41;;
		clsSimpleMove * pLocalLastMove = pGenMove;
		for &#40;clsSimpleMove *pLoopMove = moves; pLoopMove<pLocalLastMove; pLoopMove++)
		&#123;
			if &#40;pLoopMove->pTaken == &boKings&#41;
				return 0;
			if &#40;ExecuteMove&#40;pLoopMove&#41;)
			&#123;
				if (!IsInCheck&#40;boKings & boFriends&#41;)
				&#123;
					if &#40;iDepth == 0&#41;
					&#123;
						++nMoves;
					&#125;	else
					&#123;
						SwapColor&#40;);

						nMoves += PerftLoop&#40;iDepth - 1, pLoopMove&#41;;

						long long *Counts = new long long&#91;iDepth&#93;;
						clsString sFEN = GetFEN&#40;);
						QPerft&#40;iDepth, Counts, sFEN.GetString&#40;), 0, 0&#41;;
						uint64_t qMoves = Counts&#91;iDepth - 1&#93;;
						delete&#91;&#93; Counts;
						if &#40;nMoves != qMoves&#41;
						&#123;
							DebugBoard&#40;);
							sfout.Push&#40;"engine&#58; " + clsString&#40;nMoves&#41; + " qperft&#58; " + clsString&#40;qMoves&#41;);
							return 0; // set breakpoint here
						&#125;

						SwapColor&#40;);
					&#125;
				&#125;
				UndoMove&#40;pLoopMove&#41;;
			&#125;
		&#125;
		return nMoves;
	&#125;
I hope that this could help debugging my engine... and yours too ;)
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Compiling qperft as c++ code

Post by hgm »

Why not simply compile it as C?
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)?
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.

[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.
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: Compiling qperft as c++ code

Post by stegemma »

hgm wrote:Why not simply compile it as C?
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)?
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.

[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.
Yes, of course... but i'm too lazy and i've compiled qperft in c++.

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.
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: Compiling qperft as c++ code

Post by stegemma »

A little correction in the sample code:

Code: Select all

	uint64_t clsEngineSimple2&#58;&#58;PerftLoop&#40;int iDepth&#41;
	&#123;
		uint64_t nMoves = 0;
		uint64_t mkMoves = MakeMoves&#40;);
		for &#40;pNode->pMove = pNode->pFirstMove; pNode->pMove<&#40;pNode + 1&#41;->pFirstMove; pNode->pMove++)
		&#123;
			if (&#40;pNode->pMove->boFlags & flg_isTakenKing&#41;!=flg_empty&#41;
			&#123;
				nMoves = 0;
				break;
			&#125;
			if &#40;ExecuteMove&#40;pNode->pMove&#41;)
			&#123;
				if (!IsInCheck&#40;board.boKings & board.boFriends&#41;)
				&#123;
					// DebugBoard&#40;);
					if &#40;iDepth == 0&#41;
					&#123;
						++nMoves;
					&#125;	else
					&#123;
						
						SwapColor&#40;);
							++pNode;
								uint64_t kMoves = PerftLoop&#40;iDepth - 1&#41;;
								nMoves += kMoves;
							--pNode;
#if 1
						long long *Counts = new long long&#91;iDepth&#93;;
						clsString sFEN = GetFEN&#40;);
						QPerft&#40;iDepth, Counts, sFEN.GetString&#40;), 0, 0&#41;;
						uint64_t qMoves = Counts&#91;iDepth - 1&#93;;
						delete&#91;&#93; Counts;
						if &#40;kMoves != qMoves&#41;
						&#123;
							DebugBoard&#40;);
							sfout.Push&#40;"engine&#58; " + clsString&#40;kMoves&#41; + " qperft&#58; " + clsString&#40;qMoves&#41;);
							return 0; // set breakpoint here
						&#125;
#endif
						SwapColor&#40;);
					&#125;
				&#125;
				UndoMove&#40;pNode->pMove&#41;;
			&#125;
		&#125;
		return nMoves;
	&#125;
and here a screenshot of a bug found in an engine without en-passant:

Code: Select all

# Satana is running
rnbqkbnr
pppppppp
........
........
........
........
PPPPPPPP
RNBQKBNR

perft 1
Nodes&#58; 20, Time&#58; 0 ms, Nodes/s&#58; 20000
perft 2
Nodes&#58; 400, Time&#58; 12 ms, Nodes/s&#58; 30769
perft 3
Nodes&#58; 8902, Time&#58; 210 ms, Nodes/s&#58; 42189
perft 4
Nodes&#58; 197281, Time&#58; 4617 ms, Nodes/s&#58; 42720
perft 5
rnbqkbnr
ppppppp.
.......p
.......P
........
........
PPPPPPP.
RNBQKBNR

engine&#58; 379 qperft&#58; 380
&#91;...&#93;
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: Compiling qperft as c++ code

Post by stegemma »

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&#58;\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&#58; No hashing, bulk counting in horizon nodes

perft&#40; 1&#41;=            6 ( 0.000 sec&#41;
Joost Buijs
Posts: 1563
Joined: Thu Jul 16, 2009 10:47 am
Location: Almere, The Netherlands

Re: Compiling qperft as c++ code

Post by Joost Buijs »

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&#58;\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&#58; No hashing, bulk counting in horizon nodes

perft&#40; 1&#41;=            6 ( 0.000 sec&#41;
Funny! Gives Harm Geert something to work on.
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Compiling qperft as c++ code

Post by hgm »

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.
User avatar
stegemma
Posts: 859
Joined: Mon Aug 10, 2009 10:05 pm
Location: Italy
Full name: Stefano Gemma

Re: Compiling qperft as c++ code

Post by stegemma »

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.
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.

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.