A few comments on your code:
* You probably shouldn't use bitboards for your first chess program. The way you are evaluating and generating moves is not making good use of the bitboards, and it would be faster and more clear if you had used a format like an array of 64, a 12x10 representation, 0x88 or 16x12.
* Instead of making a copy of the board in the search function, I recommend using a single board object on which you carefully do and undo moves. For this I keep a stack of all the moves made so far together with some information that you need to undo the moves (what were the castlings allowed before the move, was an en-passant capture possible, what piece was captured...).
* I find it easier to define a few funcions (place, remove, move) that operate on the board at the lowest level, and which update the board (bitboards or array), the piece list, the zobrist key, the material count... Basically anything that you want to keep updated incrementally while you do and undo moves.
* If you want to enable the part of SearchHash that you have dissabled (dealing with hash scores that are only bounds), you need to pass alpha and beta as references or pointers, so you can modify their values. If a lower bound is found, you can use it to improve (increase) the value of alpha to that bound; similarly, if an upeer bound is found, you can use it to improve (decrease) the value of beta. If the values of alpha and beta cross, you can return the hash score from MiniMax. Oh, and this should work just find regardless of whether it's white's turn or black's turn.
* RecordHash shouldn't write to the hash if what's already there has a higher depth, because with the current code you will be forgetting important information to learn unimportant information.
* Checking the clock in every node is too expensive. If you profile your program you'll see that you spend most of your time asking for the time. Instead, check it only every few thousand nodes. You can also poll the input when you check the time, to see if you got a command that you need to deal with (like the user asking to abort the search).
* You shouldn't use std::list to store moves, because allocating and deallocating memory is expensive. Instead, use a large array and use only the part of the array that you need.
* You can use the "lazy" mate detection, but I think you are complicating things too much. What I do is set the score in MiniMax to -Infinity instead of alpha (let's look at it from white's perspective to simplify things). At the end, score will still be -Infinity if no moves were legal, and then I decide between mate and stalemate at the end. I think setting the initial value of score to alpha or beta instead of -Infinity or +Infinity is the main problem with your code. Let me post some of my code:
int eval=-1000000000; Move m[256]; // I am not completely sure that 256 is enough, but // I haven't thought much about it. According to // http://www.xs4all.nl/~timkr/records/records.htm#Most%20possible%20moves // 75 is the maximum for positions that happened in real games. Move *best=m; Move *end=generate_moves(b, m); // b is a reference to the board // Main loop of the alphabeta function for(Move *i=m;i!=end && !abort_search;++i){ b.do_move(*i); if(is_legal(b)) eval=-alphabeta(b,-beta,-alpha,depth-1); b.undo_move(); if(alpha<eval){ alpha=eval; best=i; if(alpha>=beta) break; } } // This detects mate or stale-mate if(eval==-1000000000) return in_check?-1000000000+b.move_number-move_number_at_root:0;
This is actual code. As you see, using NegaMax simplifies things tremendously. `b.move_number-move_number_at_root' is how far we are from the root, and that piece of code makes the program prefer shorter mates when it wins and longer mates when it loses. It also guarantees that a score of "mate" is different from -Infinity. Otherwise you would think that you are in stalemate a move leads to being mated.
I haven't checked you RootSearch function or the last thing that deals with threads (I don't think you need threads unless you are using multiple processors).