universecoder wrote:They will be different between runs still unless you switch to fixed seed. Even then it will depend on other parts of your program if you use rand() elsewhere. Using C++11's random number generation is much safer.
They are not different between runs, they are different in the SAME run, which they shouldn't be since the hash lists are now static.
So you are not talking about the zobrist keys (which you create and maintain once for the whole program) but about the hash key of the board which differs between "updated incrementally in playMove()" and "calculated from scratch", right?
If that is true then the problem is usually in the incremental update code (and your method chessboard::initUniqueKey() is trivial enough to trust it ...), so you could try to isolate the part of your incremental update code that does not work properly (assuming the root cause is actually there). Updating the hash key can be divided into four parts:
1) piece placement related part,
2) castling rights related part,
3) en passant square related part,
4) side to move related part.
What I do in such a case (which I already had a couple of times, at least once with each new engine) is to start with part 1 and strip down (by commenting out) the incremental hash key update code as well as the corresponding "calculate hash key from scratch" function so that it only contains code for part 1. If this works, i.e. now both versions of the hash key are identical, then the problem is in one of the other parts, so now enable part 2 in both functions and continue until you found out which part is responsible for the problem. In rare cases you find one problem but there is still a second one.
What you can do additionally is that you try to isolate the move type that causes the failure, e.g. normal move, en passant, castling, promotion, double step, capture, ... To increase the probability that the occurrence of the problem for, say, an ep capture does not happen by chance you could take different starting positions resp. move sequences and try to reach the same problem scenario. If it is always the same move type that triggers the bug then you have most probably found the "offending" piece of code.
If the problem already occurs with your current test code in main() where 1.e4 is the only move that is made from the initial opening position then isolating the move type of course does not help.
universecoder wrote:I am getting the following output:
----------------------------------------------------
#0 0x0000000000409acd in std::_Hashtable<int, int, std::allocator<int>, std::__detail::_Identity, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_M_find_before_node(unsigned long, int const&, unsigned long) const ()
#1 0x000000000040850c in std::_Hashtable<int, int, std::allocator<int>, std::__detail::_Identity, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_M_erase(std::integral_constant<bool, true>, int const&) ()
#2 0x000000000040720f in std::_Hashtable<int, int, std::allocator<int>, std::__detail::_Identity, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::erase(int const&) ()
#3 0x00000000004066f5 in std::unordered_set<int, std::hash<int>, std::equal_to<int>, std::allocator<int> >::erase(int const&) ()
#4 0x000000000040313f in chessboard::playMove(Move&) ()
#5 0x000000000040516e in chessboard::isMoveValid(Move&) ()
#6 0x000000000040522b in chessboard::addMove(Move&) ()
#7 0x000000000040c7f1 in chessboard::generateAllMoves() ()
#8 0x000000000040ea30 in main ()
----------------------------------------------------------
But this is not helping me, I knew that something is causing that infinite loop in isMoveValid(), I am unable to figure out what it is.
This stacktrace shows that chessboard::playMove() calls the erase() method of std::unordered_set<int, ...>, where the real template arguments instead of my "..." actually look quite awkward. Looking into your code reveals that you use std::unordered_set for your piece list. I have taken a brief look into playMove() and could not find an obvious bug in the erase() calls so maybe the problem is already triggered somewhere else (undoMove?).
Many people who are frequently reading this forum know very well what my next proposal will be ... You should consider adding some "assert(...)" statements to check certain conditions at runtime. For instance in playMove() and undoMove() you do a bunch of "atomic" operations which mainly depend on the move type. Now within each move-type-specific branch you could assert() conditions like these:
- when moving a piece from X to Y without capturing, square X must be occupied and Y empty;
- for an ep capture there must be a friendly pawn and an enemy pawn on well-defined squares, and also certain empty squares are required;
- similar for a castling move, a promotion, a pawn double step, ...
Most important for the problem above would also be to assert() that the piece (more exactly: the square number) that you want to erase from the piece list is actually contained in it, and the same vice versa for inserting.
I could imagine that undoMove() misses some special case and leaves the board in an inconsistent state which leads to a crash in playMove() in some other case. But it could as well be something else.