Advertisement

effectivenss of transposition table (chess)

Started by February 23, 2005 04:30 PM
3 comments, last by RPGeezus 20 years ago
Hey guys, I'm implementing transposition tables in my chess engine, but i'm noticing that i'm not getting the results i was expecting. On a search depth of 4 ply i'm getting around ~15000 nodes searched (on the first black move). This is on top of an alpha-beta implementation. does my alogirithm look rite? I cant think of anything else it could be. i'm using Zobrist method to generate the hash code for the board. What should the expected result be fore a depth of 4? thanks for any help or info. I put some counters in to see if the transposition table was saving from seraching various sub trees, but the numbers where very small, like under 100 nodes reduced. Could it be because of the hash table size? I have it set to 1000 rite now. what is a good number for the hash size?....I guess maybe i should bench to see the number of collisions i'm getting. Anywyas thanks soo much for the help.

    private boolean alphaBeta(Board pos)
    {  
        int alpha = -9999999;
        int beta  =  9999999;
        
        int val=0;

        getAllPossibleMoves(searchDepth, state, state.getCurrentMover());//places all moves into 'sorted buffer (captures first)'
        for all moves do:
        {
            if (pos.attemptMove(nextMove))
            {
                val = -alphaBeta(state, searchDepth - 1, -beta, -alpha);
                undoMove();
                
                if(val >= beta)  {
                	break;
                }
                
                if (val > alpha){
                    moveFound=true;
                    alpha=val;
                    bestMoveX=the move just made;
                }
                

            }
            
        }
        
        if (!moveFound) {
        	//probably check mate or state mate here
        }

        return moveFound;
    }

    int alphaBeta(Board pos, int depth, int alpha, int beta)
    { 
		TranspositionEntry entry = TranspositionTable.getEntry(pos)
		if (entry !=null ) 
		{
	        if (entry.depth >= depth) 
			{
	            if (entry.flags == TranspositionEntry.hashfEXACT) {
	                return entry.value;
	            }
	            if ((entry.flags == TranspositionEntry.hashfALPHA) &&(entry.value <= alpha))
	            {
	                return alpha;
	            }
	            if ((entry.flags == TranspositionEntry.hashfBETA) &&(entry.value >= beta))
	            {
	                return beta;
	            }
	        }
		}

        

        if (depth == 0) {
        	int result;
        	if ((searchDepth&1) == 1) {
        		result = -getRank(pos, depth, colour);
        	}
        	else 
        	{
        		result = getRank(pos, depth, colour);
        	}
            TranspositionTable..put(pos, depth, result, TranspositionEntry.hashfEXACT);
            
        	
        	if (result > alpha) {
        		if (result >= beta)
        			return beta;
        	}
        	
        	
        	return result;
        }
        
        
        getAllPossibleMoves(searchDepth, state, state.getCurrentMover());//places all moves into 'sorted buffer (captures first)'
   
        
        boolean hasMoved=false;
        final int check=state.getInCheck();
        int hashCode = TranspositionEntry.hashfALPHA;
       
        for (i=0; i<totalMoves; i++)
        {
            if (pos.attemptMove(nextMove))
            {
                hasMoved=true;
                val = -alphaBeta(pos, depth - 1, -beta, -alpha);
                pos.undoMove();
                
                if (val > alpha)
                {
	                if(val >= beta)  
	                {
	                	TranspositionTable.getInstnace().put(pos, depth, beta, TranspositionEntry.hashfBETA);
	                    return beta;
	                }

                	hashCode = TranspositionEntry.hashfEXACT;
                    alpha=val;
                }

            }
        }  
        TranspositionTable.getInstnace().put(pos, depth, alpha, hashCode); 
 

        
        //Make 'nearer' checkmates (checkmates that happen in fewer moves) more 
        //valuable than checkmates that happen in more moves.
        //need to use depth-1 cause if there is a check mate when depth ==0 then it still needs a score
        if (check==pos.getCurrentMover() && !hasMoved)
        {
            if (colour==check) return -5000*(depth+quiescenceDepth+1);
            return 5000*(depth+1);
        }
        
        
        if (!hasMoved)
        {
            //if this occures this is a stalemate leaf node
            return 0;
        }
        
        
        return alpha;
  }

Hi,

Congrats on getting this far. :)

#1. Your hash table is to small. It should more like 18,000,000. The larger the better.

#2. A three ply search will at most give you (num_moves / 2) hits. A four ply search would be (num_moves / 2)^2 I think. Thats only 400 moves (if my thinking is right).

#3. Hash tables are best once they have been filled in. At the beginning your hash table is empty, after a few moves it's full(er). The more full the table the higher the likely-hood of a hit.

#4. To measure the effectiveness of your hash routines you need to compare nodes searched without hashing against nodes searched with hashing, otherwise you're only measuring hash hits (and not the number of nodes you saved).

Try those things out and see if you get an improvement.

Will

P.S. I've noticed your not using a killer-move heuristic.. This should be your very first optimization after AlphaBeta; the returns are _huge_ for very little work. Seriously, try it out and you should see at least a 20% speed boost.





------------------http://www.nentari.com
Advertisement
Also, you should fill up the hash table, by doing a search before the game starts (as your loading would be the time to do it), just do a few ply until you run out of space.

Another thing use the [ source ] and [ /source ] tags (without the spaces) please!

Something else to do for you too:

(this is experemental, so try it and see how well that it does).

Instead of having a hash table, have a binary searched array.
This will let you have more space actually used, rather then wasted (as well as you don't get lousy cache misses).

The bsa, is actually quite fast, requiring o(log2n) time (which is 64 for a quintrillion nodes and 32 for 4 billion nodes,and so on. (so your only doing a handful of instructions, to search a very large array).

This might actually be faster then your average hash table (you can compute grandchildren, greatgrandchildren, ect. and get them all at the same time, so you can quickly find out where a particular node is, without a major cache hit).

Another thing that you could use, is a sorted list, which has the worst performing nodes.

You keep a list, of say the worst 64K nodes.
Everytime you update a node, you check this list, to see if it is a bad performer. (one comparison minimum, two comparison maximum), then you add it to the list, removing the best performing node off the worst performer list.

When you run out of space, you use the worst performers list to figure out which nodes to replace.

From,
Nice coder


Click here to patch the mozilla IDN exploit, or click Here then type in Network.enableidn and set its value to false. Restart the browser for the patches to work.
great guys thanks for the replys!! I'll definetely give the killer hurstic a try. (or maybe history would be better??) The binary seach tree also seems like maybe a good idea cause i'm dealling with a limited environment. I'll give it a try when i after i implement the killer/history huristic. From my results so far (vs just my straight alpha-beta impl), the transposition tables seem to be elimiating about 10% of board calculation (so its finding cashed boards when depth=0) but during internal nodes, its not reducing many sub-tree's. Is this correect behavoir of the transposition mechinisim..or should it be elimiating more sub trees (my search depth is at 4). Maybe if i increased the search deapth more subtree (as opposed to just leaf nodes) will be culled out? I've increased my transposition table size to 10000 and it seems to be more effective.


Also on a side note, would you concider null move hursitic effect and worth while to implement. I think i read somewhere a while back that null move wasn't very accurate if the search depth is low. Is this correct? if so whats a good seach depth to use the null move at?

I think i'll implement quensencesearch as well... It doesnt seem like very much more work than i've already done.

Thanks again for your input.

w
Search depth of 4 should not give you much in terms of hash hits. Stop doing depth 4 searchs and do at least depth 6.

My own chess program uses verified null-move pruning and I have to say it's definately worth it.

I'm going to suggest you stop working on hash tables. Get quiscience working first; it's more important. Add killer-move next, since it's simple and gives immediate results.

Once you're done these things start fresh with move-transposition. Be _very_ careful here, because it is tricky to implement, and difficult to find bugs.

I would suggest you do the following:

1. Add key-generation routines to your move generator.
2. Write a function that will look at a single board and compute a hash key.
3. When you enter AlphaBeta, check the move-generators hash to the one returned from your 'checking' function. If they are not the same, stop and figure out why. You'll use this function to test later.


Once you have those things running:

1. Play 5 or 6 moves against the computer (without hasing). Write down all the responses and scores.
2. Add hasing ONLY at the evaluation function. That is, instead of calling 'evaluate', check the hash table, and if there is a hit, return the score recorded in the hash table.
3. Play the same 5 or 6 move game and verify that the results are identical. If they are not then you have a bug and must fix it. Repeate until this works perfectly.

Once this is good:

1. Turn hashing on,2. Play the same 5 or 6 move game you've been testing with and make sure the decision is still the same. If not then you have a bug.

Once this is good:
1. Download some mate in n problems (mate in 4, mate in 5).
2. Make sure your program is able to solve them properly. Many hash table errors occure when Castling, during Passant, pawn promotion, and mate situations (i.e. stalemates, checkmates). You want to be sure that you're code is working.

Good luck!
Will




------------------http://www.nentari.com

This topic is closed to new replies.

Advertisement