/*

Pseudocode for Simplified ABDADA

Copyright 2017 Tom Kerrigan


There are notes at the end of this file.

*/



#define DEFER_DEPTH    3


#define CS_SIZE        32768    // CS = "currently_searching"

#define CS_WAYS        4


volatile int currently_searching[CS_SIZE][CS_WAYS];



// return true if a move is being searched

bool defer_move(int move_hash, int depth)

{

int n, i;

if (depth < DEFER_DEPTH)  // note 1

return false;

n = move_hash & (CS_SIZE - 1);

for (i = 0; i < CS_WAYS; i++)  // note 2

{

if (currently_searching[n][i] == move_hash)

return true;

}

return false;

}


void starting_search(int move_hash, int depth)

{

int n, i;

if (depth < DEFER_DEPTH)

return;

n = move_hash & (CS_SIZE - 1);

for (i = 0; i < CS_WAYS; i++)

{

if (currently_searching[n][i] == 0)

{

currently_searching[n][i] = move_hash;

return;

}

if (currently_searching[n][i] == move_hash)  // note 3.1

return;

}

currently_searching[n][0] = move_hash;

}


void finished_search(int move_hash, int depth)

{

int n, i;

if (depth < DEFER_DEPTH)

return;

n = move_hash & (CS_SIZE - 1);

for (i = 0; i < CS_WAYS; i++)

{

if (currently_searching[n][i] == move_hash)  // note 3.2

currently_searching[n][i] = 0;

}

}


int search(int alpha, int beta, int depth)

{

...

generate_moves();

for (i = 0; i < moves; i++)

{

if (i == 0)  // first move

{

make_move(move[i]);

x = -search(-beta, -alpha, depth - 1);  // note 5.1

undo_move();

}

else

{

int move_hash;

move_hash = current_position_hash;

move_hash ^= (move[i] * 1664525) + 1013904223;  // note 4

if (defer_move(move_hash, depth))

{

deferred_move[deferred_moves++] = move[i];

continue;

}

make_move(move[i]);

starting_search(move_hash, depth);

x = -search(-alpha - 1, -alpha, depth - 1);  // null-window search

finished_search(move_hash, depth);

if (x > alpha && x < beta)

x = -search(-beta, -alpha, depth - 1);  // note 5.2

undo_move();

}

if (x > alpha)

...

}

for (i = 0; i < deferred_moves; i++)

{

make_move(deferred_move[i]);

x = -search(-alpha - 1, -alpha, depth - 1);  // note 5.3

if (x > alpha && x < beta)

x = -search(-beta, -alpha, depth - 1);

undo_move();

if (x > alpha)

...

}

...

}



/*


Note 1:

DEFER_DEPTH reduces memory traffic by preventing access to currently_searching at shallow depths.



Note 2:

currently_searching is an n-way hash table to address collisions.



Note 3:

It isn't necessary to synchronize access to currently_searching.

Nothing bad happens if defer_move occasionally returns the wrong value.

That being said, the code has to handle race conditions:

Note 3.1:

Two threads can call starting_search() with the same move.

Note 3.2:

Two threads may have written the same move to different places.

Don't stop after zeroing out one move.



Note 4:

This code assumes that move[i] is a binary representation of the move, e.g., one byte for the source

square, one byte for the destination square, etc.

To evenly distribute the move in the hash key space, imagine that it's the seed for a linear

congruential pseudo-random number generator:

https://en.wikipedia.org/wiki/Linear_congruential_generator



Note 5:

There are calls to search() that don't have corresponding calls to starting_search() and

finished_search().

This is because the practical effect of calling starting_search() is to make other threads search a

move later. In some cases, this is unnecessary or undesirable:

Note 5.1:

Threads always search the first move first.

Calling starting_search() on the first move would have no effect.

Note 5.2:

If a move fails a null-window search, it's probably going to fail high or change alpha.

Other threads should help search the move, not search it later.

Note 5.3:

Deferred moves have probably already been searched.

There's no reason to make other threads search a move later if it's already been searched.

If a deferred move is still being searched, it may be failing high, and other threads should

help search it.





VERSION HISTORY


Version 1.0, 8/24/17

- First version


Version 1.1, 8/28/17

- Switched currently_searching to an n-way hash table to address collisions.


*/