Moderator: Andres Valverde
Perft 4 in 10 seconds is very slow too. It looks like allocating and resizing std::vector of moves each time. Or passing large object copies as arguments instead of references.H.G.Muller wrote:You should count the number of nodes per second that you search. Pretty sure there is something wrong with your alpha-beta implementation. 4 ply in 10 seconds is ridiculously slow, by about a factor 1000 from what it should be!
H.G.Muller wrote:You should count the number of nodes per second that you search. Pretty sure there is something wrong with your alpha-beta implementation. 4 ply in 10 seconds is ridiculously slow, by about a factor 1000 from what it should be!
int CAlphaBeta::AlphaBeta(CReferee *chessboard, int iTime, int iAlpha,
int iBeta)
{
CEval HndEval;
CMovement64 HndMoves;
SMove current_move;
CMoveList tmp_move_list;
int iCurrentValue;
if (iTime == 0)
return HndEval.Evaluate(chessboard,chessboard->IsWhitesTurn());
tmp_move_list.ClearAllMoves();
HndMoves.FindAllLegalMoves(chessboard,&tmp_move_list);
int iMoveCount = tmp_move_list.GetMoveCount();
int i = 0;
while (i < iMoveCount)
{
current_move = tmp_move_list.GetMove(i);
chessboard->Move_Piece(current_move);
iCurrentValue = -AlphaBeta(chessboard,iTime - 1,-iBeta,-iAlpha);
chessboard->Undo_Move();
if (iCurrentValue > iAlpha)
{
iAlpha = iCurrentValue;
}
if (iCurrentValue >= iBeta)
{
return iBeta;
}
i++;
}
if (iMoveCount == 0)
{
int iStatus = chessboard->GetGameStatus();
if (iStatus == STALEMATE)
return 0;
return NEG_INFINITY;
}
return iAlpha;
}
int CEval::Evaluate(CReferee *chessboard, bool check_white)
{
CMovement64 HndMoves;
CMoveList our_move_list;
CMoveList opp_move_list;
int iOurMaterialValue = chessboard->pieces.GetMaterialCount(check_white);
int iOpponentsMaterialValue = chessboard->pieces.GetMaterialCount(!check_white);
bool bWhitesTurn = chessboard->bWhitesTurn;
chessboard->bWhitesTurn = check_white;
HndMoves.FindAllMoves(chessboard,&our_move_list);
chessboard->bWhitesTurn = !check_white;
HndMoves.FindAllMoves(chessboard,&opp_move_list);
chessboard->bWhitesTurn = bWhitesTurn;
int iOurMoveCount = our_move_list.GetMoveCount();
int iOppMoveCount = opp_move_list.GetMoveCount();
return (iOurMaterialValue - iOpponentsMaterialValue) +
(iOurMoveCount - iOppMoveCount);
}
Roman Hartmann wrote:And what I'm also not sure is what you mean with using function calls instead of memory references?
if (chessboard->board.GetPieceType(iSquare) == N_KNIGHT)
if ((chessboard->board.board[square] & N_KNIGHT) != 0)
Dann Corbit wrote:If I understand your eval() correctly, it is wood count + tempo.
It seems to me that tempo calculation would be easier by just using who's turn it is to move.
I have already done that. Much faster. The move quality suffers a little though.Aleks Peshkov wrote:Do not know how you implemented CMoveList, but your Evaluate is slow because of it. You create empty CMoveList, let it shrink in size and delete it bazillions of time. Test the speed of search with only material evaluation.
Is there another way? Should I only use one move list in the evaluate?Aleks Peshkov wrote:You create empty CMoveList, let it shrink in size and delete it bazillions of time
No not tempo, the number of moves. I am looking at which side has a greater number of moves. That person has greater mobility and has an advantage.Dann Corbit wrote:If I understand your eval() correctly, it is wood count + tempo.
It seems to me that tempo calculation would be easier by just using who's turn it is to move.
I don't use a profiler. I can't say I really know anything about them. They sound useful though.Dann Corbit wrote:When you run your code through a profiler, which functions dominate?
Standard library vector implementation is to allocate a small memory chunk for several elements, wait when it overflows then allocate another bigger one, copy all previous vector data to a new location and delete an old memory allocation... You can save some overhead if reserve a space for say 50 moves before filling move vector inside AlphaBeta(). Most programs store moves in a single huge shared array together and never allocate any dynamic memory during search.BrettVsop wrote:The code for CMoveList is not complicated. In fact it doesn't need a class. I hold the moves in a vector.
It will be better to precreate a single vector, and reuse it in every evaluation (vector will grow quickly to sufficient size and not overflow later). Faster to implement a special function in the move generator, that just counts number of moves it generates without any move list creation.Is there another way? Should I only use one move list in the evaluate?Aleks Peshkov wrote:You create empty CMoveList, let it shrink in size and delete it bazillions of time
Move lists are always sequential, you only need to keep end (move count, array index or iterator) of each depth move list -- the next list will begin just after the end of the previous.BrettVsop wrote:One object to hold all of the moves? I guess if I made a multi dimension array it would work. I would need the second dimension to separate move lists of different depths.
There is a known legal chess position with 218 legal moves, but allocating more then possibly needed in average chess game will create unused holes in memory that is generally not recommended for performance, but you may try. Hardware cache efficiency is a main reason to glue different move arrays together. Well, you have to think about target computer architecture when design a competitive chess program.How big of an array do you use? I am thinking 150 for each depth.
Clear function only claim allocated space empty without any memory operations, so it is very fast. If you decide to use dynamic sized lists I suggest you to try std::deque instead of std::vector.If I were to stick with vectors and use your idea. Would the clear function deallocate memory? Or would that still keep a reserve for when I fill it?
BrettVsop wrote:I am talking about 4ply search. [...] My move ordering is not great, but I am doing minor ordering. I move all captures to the start of the list.
Na3 Na6 ...
Na3 Nc6 ...
...
Nc3 ...
...
Nf3 ...
...
Nh3 ...
...
a3 Na6 ...
a3 Nc6 ...
...
h4 h6 ...
h4 h5 ...
One more note, I assume that an instance of your CReferee class stores the whole game history, is that right? Otherwise my question would be how you retrieve the information to undo the last move that happened.BrettVsop wrote:
- Code: Select all
chessboard->Undo_Move();
Sven Schüle wrote:One more note, I assume that an instance of your CReferee class stores the whole game history, is that right? Otherwise my question would be how you retrieve the information to undo the last move that happened.BrettVsop wrote:
- Code: Select all
chessboard->Undo_Move();
Sven
YvesLejeail wrote:Another ideas :
- how good is you move ordering? If it is very bad, your search result
would be at the speed of minimax (so that 4 plies is slow, but not so slow at this stage). In that case, having the best move ordering would give you a x2 in depth for the same time (depth 8 in 10 seconds).
- note that adding the quiescence would increase the nps of your engine. Cause it will also help in the move ordering in iterative deepening
Hope it helps,
Yves
Return to Programming and Technical Discussions
Users browsing this forum: No registered users and 35 guests