/* Pepito v1.59  -- file: hash.c --
   Copyright (c) 2000-02 Carlos del Cacho

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Contact the author: guggenjaimito@yahoo.com
*/
/*
   high :                                                    SHR
   -----
           24 bits => jugada.                                  0
           17 bits => puntuación jugada.                      24
            2 bits => LIM_INF_NULL, LIM_SUP, LIM_INF, EXACTO  41
           16 bits => profundidad                             43
            1 bits => mate si pasas                           59
            3 bits => edad (no usada todavía)                 60
            1 bit  => libre                                   63
           -----------------------------------------------------
            TOTAL                                             64

   low :
   -----
           64 bits => indice hash

*/

#ifndef UNIQUE_SOURCE_FILE
#  include "datos.h"
#endif

#define USE_HASH_TABLE     1

//-----------------------------------------------------------------------------------
// Mira a ver si hemos llegado a esta posición antes y podemos reutilizar información

int             ProbeHash(entero32 * ptrjug, int *eval, int *prof, int *tipo_valor, int *null_threat)
{
#if USE_HASH_TABLE
    register entrada_hash *temp;
    register entero64 *phigh;

    temp = tab.trans_tab + ((entero32)HashGlob & HASH_INDEX_MASK);

    if (temp->low == HashGlob) {
        phigh = &(temp->high);

        *ptrjug = (entero32) * phigh & 0xffffff;

         if (*ptrjug && JugadaNoValida(*ptrjug))
           return 0;
        tab.hash_hits++;
        *eval = ((int) (temp->high >> 24) & 0x1ffff) - MASCA_CHICLE;
        *tipo_valor = (int) (*phigh >> 41) & 0x3;
        *prof = ((int) (*phigh >> 43) & 0xffff);
        *null_threat = (int) (*phigh >> 59) & 1;
        if (temp->high & ENTRADA_VIEJA) {
            tab.hash_used++;
            temp->high &= (entero64) ~ ENTRADA_VIEJA;
        }
        return 1;
    }
#ifdef TWO_LEVEL_TABLE
    if ((++temp)->low == HashGlob) {
        phigh = &(temp->high);

        *ptrjug = (entero32) * phigh & 0xffffff;

         if (*ptrjug && JugadaNoValida(*ptrjug))
           return 0;
        tab.hash_hits++;
        *eval = ((int) (temp->high >> 24) & 0x1ffff) - MASCA_CHICLE;
        *tipo_valor = (int) (*phigh >> 41) & 0x3;
        *prof = ((int) (*phigh >> 43) & 0xffff);
        *null_threat = (int) (*phigh >> 59) & 1;;
        if (temp->high & ENTRADA_VIEJA) {
            tab.hash_used++;
            temp->high &= (entero64) ~ ENTRADA_VIEJA;
        }
        return 1;
    }
#endif
#endif
    return 0;
}

//------------------------------------------------------------------------------------------------
// Almacena la información de ún subárbol de búsqueda en la tabla de transposición,
// para poder reutilizarla en caso de q lleguemos a la misma posición más tarde

void            StuffHash(entero32 jug, int eval, int prof, int tipo_valor, int null_threat, int ply)
{
#if USE_HASH_TABLE
    register entrada_hash *temp;

    temp = tab.trans_tab + ((entero32)HashGlob & HASH_INDEX_MASK);

    if (eval > 9000)
        eval += ply;
    else if (eval < -9000)
        eval -= ply;

    if (temp->low == HashGlob) {
        if (!jug)
            jug = (entero32)(temp->high) & 0xffffff;
    } else if (temp->high & ENTRADA_VIEJA || prof >= ((temp->high >> 43) & 0xffff)) {
        if (!jug && (temp + 1)->low == HashGlob)
            jug = (entero32)((temp + 1)->high) & 0xffffff;

        (temp + 1)->low = temp->low;
        (temp + 1)->high = temp->high;
    } else
        temp++;

    if (temp->high & ENTRADA_VIEJA)
        tab.hash_used++;

    temp->high = jug | (entero64) ((eval + MASCA_CHICLE) & 0x1ffff) << 24 |
        (entero64) (tipo_valor & 0x3) << 41 | (entero64) (prof & 0xffff) << 43 |
        (entero64) (null_threat & 1) << 59;
    temp->low = HashGlob;
#endif

#ifdef DEBUG_HASHING
{
  entero32 jug2;
  int eval2, prof2, tipo2, null2;

  if (!ProbeHash(&jug2, &eval2, &prof2, &tipo2, &null2))
  {
    printf("fatal error!\n");
  }
  if (eval2 != eval || prof2 != prof || tipo2 != tipo_valor ||
      null2 != null_threat)
   {
    Print("error en en StuffHash\n");
    PintaTablero(); Print("Turno: %d\n",Turno);
    Print("Deberia ser... eval: %d, prof: %d, tipo: %d, null: %d\n",eval,prof,tipo_valor,null_threat);
    Print("Sin embargo... eval: %d, prof: %d, tipo: %d, null: %d\n",eval2,prof2,tipo2,null2);
    exit(1);
   }
}
#endif
}

//-------------------------------------------------------------------------------------------------
void            ClearTables(int book_reset)
{
    entero32        i;

    if (book_reset)
      tab.book_retry = USE_BOOK;

    if (tab.trans_tab != NULL) {
        for (i = 0; i < HASH_SIZE; i++) {
            tab.trans_tab[i].high = ENTRADA_VIEJA;
            tab.trans_tab[i].low = 0;
        }
    }
    if (tab.trans_tab_p != NULL)
        memset(tab.trans_tab_p, 0, PAWN_HASH_SIZE * sizeof(entrada_hash_p));

    memset(tab.eval_cache, 0, CACHE_SIZE * sizeof(ecache));
    for (i = 0; i < 4096; i++) {
        tab.history[i][0] = 0;
        tab.history[i][1] = 0;
    }

    for (i = 0; i < MAX_PROF; i++)
        memset(tab.killer[i], 0, 2 * sizeof(killer_type));
}


//-------------------------------------------------------------------------------------------------
// función que comprueba la validez de una jugada extraída de la tabla de transposición
// en la posición actual. devuelve 0 en caso de ser válida


int             JugadaNoValida(register entero32 jug)
{
    register int    desde = jug & 0x3f;
    register int    hacia;
    register int    captura;

    if (!Tablero[desde] || (MASK[desde] & PiezasColor(Turno ^ 1)))
        return 1;

    captura = (jug >> 12) & 0xf;

    if (captura && (Turno ? captura < 7 : captura > 6))
        return 1;

    hacia = (jug >> 6) & 0x3f;

    if (Tablero[hacia] != captura && (!(jug & AL_PASO) || hacia != AlPaso))
        return 1;

    if (jug & PROMOCION && Tablero[desde] != (Turno ? PEON_B : PEON_N))
        return 1;

    if (Turno) {
        switch (Tablero[desde]) {
        case CABALLO_B:
            if (AtaquesCaballo(desde) & MASK[hacia])
                return 0;
            return 1;
        case ALFIL_B:
            if (AtaquesTeoricosAlfil(desde) & MASK[hacia])
                return 0;
            return 1;
        case TORRE_B:
            if (AtaquesTeoricosTorre(desde) & MASK[hacia])
                return 0;
            return 1;
        case DAMA_B:
            if ((AtaquesTeoricosAlfil(desde) | AtaquesTeoricosTorre(desde)) & MASK[hacia])
                return 0;
            return 1;
        case PEON_B:
            if (((desde - hacia == 8 || (desde - hacia == 16 && !Tablero[hacia + 8]))
                 && !Tablero[hacia]) || (AtaquesPeonB(desde) & MASK[hacia] && captura))
                return 0;
            else
                return 1;
        case REY_B:
            if (AtaquesRey(desde) & MASK[hacia])
                return 0;
            else if (jug & ENROQUE_CORTO && Enroque & E_CORTO_B && !((Blancas | Negras) & MASK_F1G1)
                     && !(Negras & AtaquesHacia(F1)))
                return 0;
            else if (jug & ENROQUE_LARGO && Enroque & E_LARGO_B && !((Blancas | Negras) & MASK_B1C1D1)
                     && !(Negras & AtaquesHacia(D1)))
                return 0;
        default:
            return 1;
        }
    } else {
        switch (Tablero[desde]) {
        case CABALLO_N:
            if (AtaquesCaballo(desde) & MASK[hacia])
                return 0;
            else
                return 1;
        case ALFIL_N:
            if (AtaquesTeoricosAlfil(desde) & MASK[hacia])
                return 0;
            else
                return 1;
        case TORRE_N:
            if (AtaquesTeoricosTorre(desde) & MASK[hacia])
                return 0;
            else
                return 1;
        case DAMA_N:
            if ((AtaquesTeoricosAlfil(desde) | AtaquesTeoricosTorre(desde)) & MASK[hacia])
                return 0;
            else
                return 1;
        case PEON_N:
            if (((hacia - desde == 8 || (hacia - desde == 16 && !Tablero[hacia - 8])) &&
                 !Tablero[hacia]) || (AtaquesPeonN(desde) & MASK[hacia]
                                      && captura))
                return 0;
            else
                return 1;

        case REY_N:
            if (AtaquesRey(desde) & MASK[hacia])
                return 0;
            else if (jug & ENROQUE_CORTO && Enroque & E_CORTO_N && !((Blancas | Negras) & MASK_F8G8)
                     && !(Blancas & AtaquesHacia(F8)))
                return 0;
            else if (jug & ENROQUE_LARGO && Enroque & E_LARGO_N && !((Blancas | Negras) & MASK_B8C8D8)
                     && !(Blancas & AtaquesHacia(D8)))
                return 0;
        default:
            return 1;
        }
    }
}
