crafty-22.0
Moderators: hgm, Rebel, chrisw
-
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
crafty-22.0
We are now doing final testing to make sure nothing has been broken. The primary change for 22.0 is code size... Here is a comparison of lines of code for 21.7 vs 22.0:
crafty% cat *.h *.c | wc -l
39405
crafty% cd /home/old/crafty-21.7
crafty% cat *.h *.c | wc -l
43332
We dropped almost 4,000 lines of C from the engine kernel. There are a few places left with duplicated black/white code, but they are in places that are not used to actually play chess (initialization, for example). These will be cleaned up soon as well, but not in 22.0, so another 500+ line reduction will likely happen.
This will match the crafty-21.7 version we used in the last CCT event, except for bugfixes. In doing this conversion, several evaluation bugs were found in 21.7 and they were fixed there and in 22.0 so I could maintain exact node counts for debugging. I particularly found problems in EvaluatePassedPawnRaces() and still have some work I want to do there probably for 22.1, because the old code just had some non-obvious bugs that were problematic to say the least...
We will probably stick this on the ftp box tomorrow or wednesday once the final 20K game validation run completes...
crafty% cat *.h *.c | wc -l
39405
crafty% cd /home/old/crafty-21.7
crafty% cat *.h *.c | wc -l
43332
We dropped almost 4,000 lines of C from the engine kernel. There are a few places left with duplicated black/white code, but they are in places that are not used to actually play chess (initialization, for example). These will be cleaned up soon as well, but not in 22.0, so another 500+ line reduction will likely happen.
This will match the crafty-21.7 version we used in the last CCT event, except for bugfixes. In doing this conversion, several evaluation bugs were found in 21.7 and they were fixed there and in 22.0 so I could maintain exact node counts for debugging. I particularly found problems in EvaluatePassedPawnRaces() and still have some work I want to do there probably for 22.1, because the old code just had some non-obvious bugs that were problematic to say the least...
We will probably stick this on the ftp box tomorrow or wednesday once the final 20K game validation run completes...
Re: crafty-22.0
I also ditched the color dependent code in move generation with one exception, to generate rochade moves. Is it possible to generate these independant form the color? I have stuff there like if (fromsq(King) == E1) etc.
-
- Posts: 1808
- Joined: Wed Mar 08, 2006 9:19 pm
- Location: Oslo, Norway
Re: crafty-22.0
Yes, it is easy to make things like this color-independent:Guetti wrote:I also ditched the color dependent code in move generation with one exception, to generate rochade moves. Is it possible to generate these independant form the color? I have stuff there like if (fromsq(King) == E1) etc.
Code: Select all
if(fromsq(King) == (E1^(color*070)))
Code: Select all
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * 070));
}
Replace the constant 070 by 0x70 if you use a 128-square board.
Tord
Re: crafty-22.0
Interesting.Tord Romstad wrote:Yes, it is easy to make things like this color-independent:Guetti wrote:I also ditched the color dependent code in move generation with one exception, to generate rochade moves. Is it possible to generate these independant form the color? I have stuff there like if (fromsq(King) == E1) etc.I have found this to be useful so often that I have written a little function for it:Code: Select all
if(fromsq(King) == (E1^(color*070)))
With this function, relative_square(WHITE, SQ_E1) returns SQ_E1, while relative_square(BLACK, SQ_E1) returns E8.Code: Select all
inline Square relative_square(Color c, Square s) { return Square(int(s) ^ (int(c) * 070)); }
Replace the constant 070 by 0x70 if you use a 128-square board.
Tord
For pawns I simply made an array advance_one[2] = {8, -8} and advance_two[2] = {16, -16} to get fromsq+advance_one[WHITE] = fromsq+8, fromsq+advance_one[BLACK] = fromsq-8.
But your square conversion didn't come to my mind.
-
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: crafty-22.0
yes. if (fromsq(king) == Esquare[side]Guetti wrote:I also ditched the color dependent code in move generation with one exception, to generate rochade moves. Is it possible to generate these independant form the color? I have stuff there like if (fromsq(King) == E1) etc.
where Esquare is defined:
int Esquare[2] = { E8, E1 };
That's what I did..
-
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: crafty-22.0
I started off with Tord's approach, but I found many cases where that didn't work (your 8, -8 is one example. I finally defined an array "map_squares[side][square]" to do this for all squares.Guetti wrote:Interesting.Tord Romstad wrote:Yes, it is easy to make things like this color-independent:Guetti wrote:I also ditched the color dependent code in move generation with one exception, to generate rochade moves. Is it possible to generate these independant form the color? I have stuff there like if (fromsq(King) == E1) etc.I have found this to be useful so often that I have written a little function for it:Code: Select all
if(fromsq(King) == (E1^(color*070)))
With this function, relative_square(WHITE, SQ_E1) returns SQ_E1, while relative_square(BLACK, SQ_E1) returns E8.Code: Select all
inline Square relative_square(Color c, Square s) { return Square(int(s) ^ (int(c) * 070)); }
Replace the constant 070 by 0x70 if you use a 128-square board.
Tord
For pawns I simply made an array advance_one[2] = {8, -8} and advance_two[2] = {16, -16} to get fromsq+advance_one[WHITE] = fromsq+8, fromsq+advance_one[BLACK] = fromsq-8.
But your square conversion didn't come to my mind.
Now I just do map_squares[side][E1] and get E1 for side = wtm, and E8 for side = btm. You can bury that in a Macro to make it less intrusive if you want...
-
- Posts: 287
- Joined: Mon Mar 13, 2006 5:23 pm
- Location: Québec
Re: crafty-22.0
Hi M. Hyatt,bob wrote:There are a few places left with duplicated black/white code, but they are in places that are not used to actually play chess (initialization, for example).
May I ask what did motivate you to remove the black/white duplicated code from your engine? I know it is less bug prone to maintain one function instead of two almost identical ones. But I wonder if you expect to see any speed improuvments?
In my engine, that is in C++, I use templated functions, so I only have to write/maintain one fonction, but the compilator generates two. I always thought I was benefiting from the best of both world. However if having duplicated functions causes a performance penalty, I might be better to drop my templates.
For thoses interested here is an example of a templated function :
Code: Select all
template <PieceColor color, bool bCaptures, bool bRook, bool bQueen>
inline void MoveGenerator::GenerateRookLike ( MoveList& liste )
{
//...
}
GenWhiteRookMoves
GenWhiteQueenMoves
GenWhiteRookCaptures
GenWhiteQueenCaptures
GenBlackRookMoves
GenBlackQueenMoves
GenBlackRookCaptures
GenBlackQueenCaptures
Mathieu Pagé
mathieu@mathieupage.com
mathieu@mathieupage.com
-
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: crafty-22.0
There are two issues I wanted to address:mathmoi wrote:Hi M. Hyatt,bob wrote:There are a few places left with duplicated black/white code, but they are in places that are not used to actually play chess (initialization, for example).
May I ask what did motivate you to remove the black/white duplicated code from your engine? I know it is less bug prone to maintain one function instead of two almost identical ones. But I wonder if you expect to see any speed improuvments?
In my engine, that is in C++, I use templated functions, so I only have to write/maintain one fonction, but the compilator generates two. I always thought I was benefiting from the best of both world. However if having duplicated functions causes a performance penalty, I might be better to drop my templates.
For thoses interested here is an example of a templated function :This one function can replace theses 8 functions :Code: Select all
template <PieceColor color, bool bCaptures, bool bRook, bool bQueen> inline void MoveGenerator::GenerateRookLike ( MoveList& liste ) { //... }
GenWhiteRookMoves
GenWhiteQueenMoves
GenWhiteRookCaptures
GenWhiteQueenCaptures
GenBlackRookMoves
GenBlackQueenMoves
GenBlackRookCaptures
GenBlackQueenCaptures
(1) development. Duplicated code complicates testing since I have to di it twice, once for black, once for white, to make sure no asymmetry creeps in (Crafty's evaluation has been 100% symmetric for about 2 years and I want to make certain that happens. It is a far less daunting task to modify something when you know it will require less than 1/2 the effort it would take if the duplication was still there.
(2) cache footprint. Duplicated code has to reside in cache to execute, and it takes twice as much. I had originally thought that these changes would slightly slow things down since some constants and stuff are now indexed by [side] so that they work for both colors. But, in fact, the overall speed went up a bit and will probably go up more as I can now do twice as much work per unit of time since I am not doing everything twice...
A 3rd advantage is that it simply makes the code easier to read, not that removing over 4,000 lines of code was a bad thing by itself...
-
- Posts: 1808
- Joined: Wed Mar 08, 2006 9:19 pm
- Location: Oslo, Norway
Re: crafty-22.0
I do the same for pawns, except that I have hidden it in an inline function:Guetti wrote:For pawns I simply made an array advance_one[2] = {8, -8} and advance_two[2] = {16, -16} to get fromsq+advance_one[WHITE] = fromsq+8, fromsq+advance_one[BLACK] = fromsq-8.
Code: Select all
inline SquareDelta pawn_push(Color c) {
static const SquareDelta PawnPush[2] = {DELTA_N, DELTA_S};
return PawnPush[c];
}
Code: Select all
inline SquareDelta pawn_push(Color c) {
return DELTA_N + 2 * int(c) * DELTA_S;
}
I don't have a special function for double pawn pushes; just doing 2 * pawn_push(color) is simple enough for me.
Tord
-
- Posts: 1808
- Joined: Wed Mar 08, 2006 9:19 pm
- Location: Oslo, Norway
Re: crafty-22.0
Not, that's not an example, because they are two entirely different operations. Flipping a square vertically is obviously not the same as flipping a square delta.bob wrote:I started off with Tord's approach, but I found many cases where that didn't work (your 8, -8 is one example.Guetti wrote:Interesting.Tord Romstad wrote:Yes, it is easy to make things like this color-independent:Guetti wrote:I also ditched the color dependent code in move generation with one exception, to generate rochade moves. Is it possible to generate these independant form the color? I have stuff there like if (fromsq(King) == E1) etc.I have found this to be useful so often that I have written a little function for it:Code: Select all
if(fromsq(King) == (E1^(color*070)))
With this function, relative_square(WHITE, SQ_E1) returns SQ_E1, while relative_square(BLACK, SQ_E1) returns E8.Code: Select all
inline Square relative_square(Color c, Square s) { return Square(int(s) ^ (int(c) * 070)); }
Replace the constant 070 by 0x70 if you use a 128-square board.
Tord
For pawns I simply made an array advance_one[2] = {8, -8} and advance_two[2] = {16, -16} to get fromsq+advance_one[WHITE] = fromsq+8, fromsq+advance_one[BLACK] = fromsq-8.
But your square conversion didn't come to my mind.
By the way, doing pawn pushes for both colors without an array is also easy, as explained in one of my other posts. In any case, because the array will be so tiny, it doesn't matter at all which approach you use.
My approach of doing square ^ (side * 070) does exactly the same for all squares, as you can easily prove.I finally defined an array "map_squares[side][square]" to do this for all squares.
Now I just do map_squares[side][E1] and get E1 for side = wtm, and E8 for side = btm.
I agree, of course this should be done in a macro or inline function. My own preference would be to use an inline function, in order to avoid the pitfalls of preprocessor macros and to ensure some type safety. I honestly don't understand why people are still using preprocessor macros, but that's probably just because of my general ignorance about C/C++ programming.You can bury that in a Macro to make it less intrusive if you want...
Tord