// EXchess source code, (c) Daniel C. Homan  1997-2011
// Released under the GNU public license, see file license.txt

/*
  Functions dealing with the standard hash table and the pawn hash tables.
  The standard hash table is divided into two pieces.  The lower half uses
  a "smart" replace strategy, and the upper half uses an always replace
  strategy.
  The pawn hash is very much like the standard hash - but simpler.  Only
  the hash key and score is stored.  The pawn table is always replace.
*/


#include "define.h"
#include "chess.h"
#include "funct.h"
#include "const.h"
#include "hash.h"
#include "extern.h"

/* Function to set the hash table size */
void set_hash_size(unsigned int Mbytes)
{
  unsigned int Tab_entry_size = sizeof(hash_rec);
  unsigned int Pawn_entry_size = sizeof(pawn_rec);
  unsigned int Max_tab_size, Max_pawn_size;

  Max_pawn_size = Mbytes*1000000/(3*Pawn_entry_size);

  PAWN_SIZE = 2;
  while(2*PAWN_SIZE < Max_pawn_size) { PAWN_SIZE *= 2; }

  Max_tab_size = (Mbytes*1000000 - PAWN_SIZE*Pawn_entry_size)/Tab_entry_size;

  TAB_SIZE = 2;
  while(2*TAB_SIZE < Max_tab_size) { TAB_SIZE *= 2; }

  if(TAB_SIZE*2*Tab_entry_size+PAWN_SIZE*Pawn_entry_size/2 < Mbytes*1000000)
    { TAB_SIZE *= 2; PAWN_SIZE /= 2; }

  close_hash();
  open_hash();
}

/* Function with allocates space for the hash tables and generates
   the hash codes */
void open_hash()
{
 start_code();
 hash_table = new hash_rec[TAB_SIZE];
 pawn_table = new pawn_rec[PAWN_SIZE];
}

/* function to close the hash table */
void close_hash()
{
 delete [] hash_table;
 delete [] pawn_table;
}

/* function to stuff a hash entry into the hash table */
void put_hash(h_code (*h_key), int score, int alpha, int beta,
                            int depth, move hmove, int mate_ext)
{
 hash_rec *h, *r; int flag; 

 // is this an upper bound, lower bound, or exact score?
 if (score >= beta) { flag = FLAG_B; score = beta; }
 else if (score <= alpha) 
   { flag = FLAG_A; score = alpha; hmove.t = NOMOVE;  }
 else { flag = FLAG_P; }

 // adjust any mate score to be a bound
 if(score >= 32000) { 
  if(flag == FLAG_B || flag == FLAG_P) {
   score = 32000; flag = FLAG_B; 
  } else return;
 } else if(score <= -32000) { 
  if(flag == FLAG_A || flag == FLAG_P) {
   score = -32000; flag = FLAG_A; 
  } else return;
 }

 // find location in table
 h = hash_table + ((*h_key)&(TAB_SIZE-1));

 if(((*h_key)&(TAB_SIZE-1))&1) h--;

 // always replace if this is an exact match *and* not a qsearch hash result
 //  because the result from a current search should always be better than
 //  a previous search (if the previous search was better, we would have just
 //  returned the value from the hash table at the start of the node).
 if(h->key == (*h_key) && depth > -1) {
     h->score = short(score);
     h->flag = flag;
     h->depth = depth;
     h->hmove = hmove;
     h->id = h_id;
     h->mate_ext = mate_ext;
 } else {    
   // use "smart" replace strategy if possible
   if ((h->id != h_id) || (h->depth < depth)
       || (h->depth == depth &&
	   ((flag == FLAG_A && h->flag == FLAG_A && h->score > score) || 
	    (flag == FLAG_B && h->flag == FLAG_B && h->score < score) || 
	    (flag == FLAG_P))))
     {
       r = h + 1; (*r) = (*h);   // copy entry to upper half of table       
       h->key = (*h_key);
       h->score = short(score);
       h->flag = flag;
       h->depth = depth;
       h->hmove = hmove;
       h->id = h_id;
       h->mate_ext = mate_ext;
     }
   // or use always replace in the upper half of table
   else if((*h_key) != h->key)   
     {
       h++;           // move to upper half of table
       h->key = (*h_key);
       h->score = short(score);
       h->flag = flag;
       h->depth = depth;
       h->hmove = hmove;
       h->id = h_id;
       h->mate_ext = mate_ext;
     }
 }
}

/* function to find and return a hash entry */
int get_hash(h_code (*h_key), int *hflag, int *hdepth, int *mate_ext, move *gmove)
{
 hash_rec *h; 

 // find the right location in the table
 h = hash_table + ((*h_key)&(TAB_SIZE-1));

 if(((*h_key)&(TAB_SIZE-1))&1) h--;

 // check lower part of table first then upper part
 if (h->key != (*h_key)) { 
   h++; 
   if (h->key != (*h_key)) { 
     gmove->t = NOMOVE;  
     return HASH_MISS; 
   } 
 }

 (*mate_ext) = h->mate_ext;
 (*gmove) = h->hmove;
 (*hflag) = h->flag;
 (*hdepth) = h->depth;

 return h->score;

}

/* function to retrieve the hash move from the table */
int get_move(h_code (*h_key))
{
 hash_rec *h;
 h = hash_table + ((*h_key)&(TAB_SIZE-1));

 if(((*h_key)&(TAB_SIZE-1))&1) h--;

 // if the hit was in the upper half of the table, go there
 // check lower part of table first then upper part
 if (h->key != (*h_key)) { 
   h++; 
   if (h->key != (*h_key)) return NOMOVE; 
 }

 return h->hmove.t;
}

/* function to stuff a hash move into the table */
int put_move(h_code (h_key), int putmove)
{
 hash_rec *h;
 h = hash_table + ((h_key)&(TAB_SIZE-1));

 if(((h_key)&(TAB_SIZE-1))&1) h--;

 h->key = (h_key);
 h->hmove.t = putmove;
 h->score = HASH_MISS;      // will cause score to be ignored by get_hash()
 h->depth = -2;
 h->id = h_id;

 return 0;
}

/* generate the hash code for a given position */
// also calculate material score and fill piecelists
void position::gen_code()
{
  int i;
  hcode = ZERO;
  pcode = ZERO;
  material = 0;
  pieces[0] = 0; 
  pieces[1] = 0;

  for(i=0; i<=KING; i++) {
    plist[0][i][0] = 0;
    plist[1][i][0] = 0;
  }

  for(i = 0; i < 64; i++) {
   Or(hcode, hval[ID(sq[i])][i]);
   Or(pcode, hval[ID(sq[i])][i]);
   if(sq[i].side == wtm) material += value[sq[i].type];
   else material -= value[sq[i].type];
   if(sq[i].type > PAWN) pieces[sq[i].side]++;
   if(sq[i].side != -1) {
    plist[sq[i].side][sq[i].type][0]++;
    plist[sq[i].side][sq[i].type][plist[sq[i].side][sq[i].type][0]] = i;
   } 
  }

  if(wtm) { Or(hcode, hwtm); }
  else { Or(hcode, hbtm); }

  Or(hcode, castle_code[castle]);
  Or(hcode, ep_code[ep]);

}

/* function to generate hash codes for all pieces/positions */
void start_code()
{
  int i, j, k;
  unsigned int key, address, key2, address2;

  sgenrand(84324);
   
  for (i = 0; i < 64; i++)
   { hval[0][i] = ZERO; }
  for (j = 1; j < 13; j++)
   {
     for (i = 0; i < 64; i++)
      {
       key = 0;
       address = 0;
       for(k = 0; k < 32; k++)
        {
         key = key | (ibit() << k);
         address = address | (ibit() << k);
        }
       hval[j][i] = ZERO;
       hval[j][i] = hval[j][i]|key;
       hval[j][i] = (hval[j][i]<<32)|address;
      }
   }

   address = 0; key = 0;
   address2 = 0; key2 = 0;
   for(k = 0; k < 32; k++) {
    key = key | (ibit() << k);
    address = address | (ibit() << k);
    key2 = key2 | (ibit() << k);
    address2 = address2 | (ibit() << k);
   }

   hwtm = ZERO; hbtm = ZERO;
   hwtm = hwtm|key;  hbtm = hbtm|key2;
   hwtm = (hwtm<<32)|address; hbtm = (hbtm<<32)|address2;

   for(i = 0; i < 16; i++) {
    key = 0;
    address = 0;
    for(k = 0; k < 32; k++) {
     key = key | (ibit() << k);
     address = address | (ibit() << k);
    }
    castle_code[i] = ZERO;
    castle_code[i] = castle_code[i]|key;
    castle_code[i] = (castle_code[i]<<32)|address;
   }

   for(i = 0; i < 64; i++) {
    key = 0;
    address = 0;
    for(k = 0; k < 32; k++) {
     key = key | (ibit() << k);
     address = address | (ibit() << k);
    }
    ep_code[i] = ZERO;
    ep_code[i] = ep_code[i]|key;
    ep_code[i] = (ep_code[i]<<32)|address;
   }

   for(i = 0; i < 4; i++) {
    key = 0;
    address = 0;
    for(k = 0; k < 32; k++) {
     key = key | (ibit() << k);
     address = address | (ibit() << k);
    }
    stage_code[i] = ZERO;
    stage_code[i] = stage_code[i]|key;
    stage_code[i] = (stage_code[i]<<32)|address;
   }

}







