/* Pepito v1.59  -- file: negascou.c --
   Copyright (c) 2000-01 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
*/

#ifndef UNIQUE_SOURCE_FILE
#  include "datos.h"
#  include <signal.h>
#endif

extern int ALPHA, BETA, RMat, easy_move, rply;

// cuando solo se quiere usar como evaluación el material

//#define Evaluacion(a, b)  TBalance

#define MAX_EXTEND        (2*MaxProf)

/* search definitions */
#define FAIL_SOFT         1
#define NULL_MOVE         1
#define NULL_AT_LAST_PLY  1
#define DEAL_PRUNE        1
#define VERIF_NULL_MOVE   0

#ifdef CASTLE_STRIKE
#undef CASTLE_STRIKE
#endif

#define FUTIL             1

#define ALPHA_REDUCTION   1
#define CASTLE_STRIKE     (UNIT_PROF/10)
#define CHECK_EXT         UNIT_PROF
#define RECAP_EXT         UNIT_PROF
#define PAWN_PUSH_EXT     UNIT_PROF
#define NULL_EXT          UNIT_PROF
#define RAZOR             1
#define IID               0
#define EGTB              1
#define HASH              1
#define KILLER_COUNT      0
#define UPDATE_BOUNDS     0
//#define DEBUG_IID

#define RED_POR_RAZOR     UNIT_PROF

#define MARGEN_QUIES      (PEON_VAL)
#define ALPHA_PRUNE       1
#define SEE_PRUNE         1
#define QCHECK_CHECK      0
#define HASH_QFRONTIER    0


static const int CHECK_NJUG[6] = { 0, UNIT_PROF, UNIT_PROF/10, 0, 0,
                               -UNIT_PROF/10 }; 

#if !KILLER_COUNT
#define UpdateKillers(jugada)                                                               \
               if (jugada != tab.killer[ply][0].mov.jug)                                      \
               {                                                                            \
                  tab.killer[ply][1].mov = tab.killer[ply][0].mov;                              \
                  tab.killer[ply][0].mov.jug = jugada;                                        \
                  tab.killer[ply][0].mov.pieza_mov = Tablero[jugada & 0x3f];                  \
               }                                                                            \
               else                                                                         \
               {                                                                            \
                 tab.killer[ply][1].mov.pieza_mov = Tablero[jugada & 0x3f];                   \
                 tab.killer[ply][1].mov.jug = jugada;                                         \
               }
#else				   
#define UpdateKillers(jugada)                                                                 \
               if (jugada == tab.killer[ply][0].mov.jug)                                        \
                 tab.killer[ply][0].rep_count++;                                                \
               else if (jugada == tab.killer[ply][1].mov.jug)                                   \
               {                                                                              \
                  tab.killer[ply][1].rep_count++;                                               \
                  if (tab.killer[ply][1].rep_count > tab.killer[ply][0].rep_count)                \
                  {                                                                           \
                    killer_type temp = tab.killer[ply][0];                                      \
                    tab.killer[ply][0] = tab.killer[ply][1];                                      \
                    tab.killer[ply][1] = temp;                                                  \
                  }                                                                           \
               }                                                                              \
               else if (tab.killer[ply][0].rep_count)                                           \
               {                                                                              \
                  tab.killer[ply][1].mov.jug = jugada;                                          \
                  tab.killer[ply][1].mov.pieza_mov = Tablero[jugada & 0x3f];                    \
                  tab.killer[ply][1].rep_count = 1;                                             \
               }                                                                              \
               else                                                                           \
               {                                                                              \
                  tab.killer[ply][0].mov.jug = jugada;                                          \
                  tab.killer[ply][0].mov.pieza_mov = Tablero[jugada & 0x3f];                    \
                  tab.killer[ply][0].rep_count = 1;                                             \
               }
#endif

#define MakeNullMove                                         \
             CambiaTurno();                                  \
             tab.jaque[ply+1] = 0;                           \
             tab.extensiones[ply+1] = 0;                     \
             tab.null_attempts++;                            \
             aux = AlPaso;                                   \
             Historial[NumJug].hash = HashGlob;              \
             HashGlob = ~HashGlob;                           \
             if (AlPaso != 64)\
             {\
               if (AlPaso > 32)\
                 HashGlob ^= Zobrist(PEON_B,56 + (AlPaso & 7));\
               else\
                 HashGlob ^= Zobrist(PEON_N,(AlPaso & 7));\
               AlPaso = 64;\
			 }\
             Historial[NumJug+1].last_cap = Historial[NumJug].last_cap;/*NumJug;*/          \
             Historial[NumJug++].jug = 0;                    


#define UnMakeNullMove                            \
             CambiaTurno();                       \
             AlPaso = aux;                        \
             HashGlob = Historial[--NumJug].hash; 
             
#define Pop_Last_Move(pos_stack)                        \
             *pos_stack = *(--tab.max_stack[ply]);

int InsertSort(list_jug * ptrjug, list_jug * ptrlim);
int ShellSort(list_jug ptrjug[], list_jug ptrlim[]);

extern int time_extend, text_count;

#define tipo R

//--------------------------------------------------------
/*__forceinline int PopFrame(int ply)
{
          __asm                        
         {                             
            mov ecx, dword ptr ply   
            dec ecx                    
            cmp ecx, 0                 
            jz SALIR                   
POP_FRAME:                             
            mov esp, ebp               
            pop ebp                    
            loop POP_FRAME             
SALIR:                                 
         }
}*/
//--------------------------------------------------------

int NegaScout (int alpha, int beta, int prof, int ply, int can_null, int amenazas)
{
   int a, b, best, actual, namenazas;
   int aux, extension, trigger_ext;
   list_jug *pos_stack;
   entero32 mejor_jug;
   int futility, fut_cuts, margen_fut;
   int can_extend;
   int R;
   int null_threat;
   int searched;

//#define DEBUGPV
#ifdef DEBUGPV
   if (debughash[ply-1] == HashGlob)
   {
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" entrada  a: %d, b: %d, d: %0.2f, p: %d\n",alpha, beta, (float)prof/UNIT_PROF, ply);
   }
#endif

   if (tab.nodos_buscados > tab.nodos_control)
   {
      unsigned long tiempo;
      unsigned long nods = tab.nodos_buscados;

        while (FullInput())
        {
             ReadString(entrada);
             if (ComandoProcesable(entrada))
               continue;
             if (tab.pondering && (MatchesPonderMove(entrada) || !strcmp(entrada,"ponderhit")))
             {
                tab.time_limit = AllocTime(xb_info.mv_ctrl,NumJug-ply, xb_info.my_clock, xb_info.inc);
                tab.guessed = 1;
                break;
             }
          Print("Interrupting search...\n");
          tab.time_up = 2+tab.pondering;
          UnGetString();
//          PopFrame(ply);
          return 0;
        }

       tiempo = TimeDiff();

       if (UCI_MODE && (tab.last_report+140000) < nods)
       {
           Print("info nps %d hashfull %d tbhits %d\n",100*(tab.nodos_buscados)/tiempo,
                (unsigned)(1000*((double)tab.hash_used/HASH_SIZE)), tab.egtb_hits);
           tab.last_report = nods;
       }

       if (easy_move && tiempo >= tab.time_limit/3)
       {
          tab.time_up = 1;
//          PopFrame(ply);
          return 0;
       }
       if (tiempo >= tab.time_limit)
       {
          if (!(time_extend && text_count<2) || (xb_info.my_clock + 1000)
              < (long)3*tab.time_limit)
          {
             tab.time_up = 1;
//             PopFrame(ply);
             return 0;
          }
          time_extend = 0;
          text_count++;
          tab.time_limit += 2*tab.time_limit/3;
       }
       tab.nodos_control += CONTROL_INC;
     }

   tab.nodos_buscados++;


   if (alpha + ply == JAQUE_MATE)
     return alpha;

/*   if (!Piezas[!Turno])
   {
     if (Turno)
     {
       if (DamasB && MaterialB - MaterialN - 2*PEON_VAL > beta && !(PeonesN & FILA_6) && !tab.jaque[ply])
         prof -= UNIT_PROF;
     }
     else
     {
       if (DamasN && MaterialN - MaterialB - 2*PEON_VAL > beta && !(PeonesB & FILA_1) && !tab.jaque[ply])
         prof -= UNIT_PROF;
     }
   }
*/

   pv_long[ply] = ply;
   tab.jug_hash[ply] = 0;

   if (ProbeHash(tab.jug_hash+ply,&actual,&aux,&tipo,&null_threat))
     {
       if (aux >= prof || ((actual > JAQUE_MATE-400) && tipo != LIM_SUP))
       {
           if (actual > JAQUE_MATE-400)
             actual -= ply;
           else if (actual < -JAQUE_MATE+400)
             actual += ply;
           switch (tipo)
            {
               case LIM_INF_NULL: if (!can_null) break;
               case LIM_INF: if (actual >= beta)
                             {
#ifdef DEBUGPV
                               if (debughash[ply-1] == HashGlob)
                               {
                                  jugada_ext j;
                                  PintaJugada(&pvdebug[ply-1], Print);
                                  Print(" actual>=beta a: %d, b: %d, d: %0.2f, p: %f, hp: %d ",actual, beta, (float)prof/UNIT_PROF, ply,(float)aux/UNIT_PROF);
                                  j.jug = tab.jug_hash[ply];
                                  j.pieza_mov = Tablero[j.jug & 0x3f];
                                  PintaJugada(&j, Print);
                                  Print("\n");
                               }
#endif
                               return actual;
                             }

#if UPDATE_BOUNDS
                             if (actual > alpha)
                                alpha = actual-1;
#endif
                             break;
               case LIM_SUP: if (actual <= alpha)
               {
#ifdef DEBUGPV
   if (debughash[ply-1] == HashGlob)
   {
     jugada_ext j;
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" limsup a: %d, b: %d, d: %0.2f, p: %d, hp: %f ",actual, beta, (float)prof/UNIT_PROF, ply,(float)aux/UNIT_PROF);
     j.jug = tab.jug_hash[ply];
     j.pieza_mov = Tablero[j.jug & 0x3f];
     PintaJugada(&j, Print);
     Print("\n");
   }
#endif
                               return actual;
                               }

//#if UPDATE_BOUNDS
//                             if (actual < beta)
//                             {  beta = actual+1; } 
//#endif
                             break;
               case EXACTO:  if (actual > alpha && actual < beta && tab.jug_hash[ply])
							 {
#ifdef DEBUGPV
   if (debughash[ply-1] == HashGlob)
   {
     jugada_ext j;
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" sasto a: %d, b: %d, d: %0.2f, p: %d, hp: %f ",actual, beta, (float)prof/UNIT_PROF, ply,(float)aux/UNIT_PROF);
     j.jug = tab.jug_hash[ply];
     j.pieza_mov = Tablero[j.jug & 0x3f];
     PintaJugada(&j, Print);
     Print("\n");
   }
#endif                                                 
							   pv_long[ply] = ply+1;
							   pv[ply][ply].jug = tab.jug_hash[ply];
							   pv[ply][ply].pieza_mov = Tablero[0x3f & tab.jug_hash[ply]];
							 }
				             return actual;
            }
       }

       if (null_threat || (tipo != LIM_INF && tipo !=LIM_INF_NULL && actual < beta && prof <= (aux - (prof > 6*UNIT_PROF ? -3*UNIT_PROF : -2*UNIT_PROF))))
          can_null = 0;

       if (tab.jug_hash[ply])// && (alpha == (beta-1) || prof <= 3*UNIT_PROF || tipo != LIM_SUP))
         tab.proxima_fase[ply] = ELIGE_HASH;
       else if (tab.jaque[ply])
         tab.proxima_fase[ply] = GEN_JAQS;
       else
         tab.proxima_fase[ply] = GEN_CAPS;
     }
   else
     {
        null_threat = 0;
        if (tab.jaque[ply])
          tab.proxima_fase[ply] = GEN_JAQS;
        else
          tab.proxima_fase[ply] = GEN_CAPS;
#if EGTB
        if (Nalimov && (NumPeonesB + NumPeonesN + PiezasB + PiezasN) <= (Nalimov - 2) && (ply == 1 ||
           (Historial[NumJug-1].jug & (PROMOCION|CAPTURA) &&
        Worth_Probing_EGTB())) &&  (actual = ProbeTB(ply)) != -1)
        {
           tab.egtb_hits++;
           StuffHash(0, actual, 100*UNIT_PROF, EXACTO, 0, ply);
           return actual;
        }
#endif
     }


   tab.max_stack[ply] = tab.max_stack[ply-1];


#if NULL_MOVE
   if (can_null && !tab.jaque[ply] && Piezas[Turno]// && Piezas[0]
//         && Piezas[Turno] && (Piezas[Turno] > 4 || prof <= 7*UNIT_PROF)

#if NULL_AT_LAST_PLY
           )
#else
           && (MaxProf > 8*UNIT_PROF || prof > UNIT_PROF) && (TBalance >= beta-2*PEON_VAL || Piezas[Turno] < 2))
#endif
     {

#if DEAL_PRUNE
           if (prof <= UNIT_PROF && Piezas[Turno] > 3)
           {
               tab.max_stack[ply] = tab.max_stack[ply-1];
               if (DealPrune(beta,ply+1))
               {
                   StuffHash(beta, prof, LIM_INF_NULL, ply, 0, 0);
#ifdef DEBUGPV
                   if (debughash[ply-1] == HashGlob)
                   {
                      PintaJugada(&pvdebug[ply-1], Print);
                      Print("dealprune  b: %d, eval: %d, d: %0.2f, p: %d, s: %d\n", beta, Evaluacion(beta-1,INFINITO), (float)prof/UNIT_PROF, ply,actual);
                   }
#endif

                   return beta;
               }
            }
            else
#endif
      {
        /*if (prof > 10*UNIT_PROF)
          R = 5*UNIT_PROF;
        else*/ if (prof > 6*UNIT_PROF)
          R = 4*UNIT_PROF;
        else
          R = 3*UNIT_PROF;

        actual = beta;

        if (Piezas[Turno]<=2 && prof >= 7*UNIT_PROF)
        {
            if (alpha == beta-1)
            {
               aux = Historial[NumJug].jug;
               searched = tab.jug_hash[ply];
               actual = NegaScout(beta-1,beta,prof-5*UNIT_PROF,ply,0,amenazas);
               if (searched)
               {
                  tab.proxima_fase[ply] = ELIGE_HASH;
                  tab.jug_hash[ply] = searched;
               }
               else if (actual >= beta && Historial[NumJug].jug != (unsigned)aux)
               {
                  tab.proxima_fase[ply] = ELIGE_HASH;
                  tab.jug_hash[ply] = Historial[NumJug].jug;
               }
               else if (tab.jaque[ply])
                 tab.proxima_fase[ply] = GEN_JAQS;
               else
                 tab.proxima_fase[ply] = GEN_CAPS;
               tab.max_stack[ply] = tab.max_stack[ply-1];
            }
            else
              actual = beta - 1;
        }

        if (actual >= beta)
        {
           MakeNullMove;
           if (prof-R>0)
             actual = -NegaScout(-beta,-beta+1,prof-R,ply+1,0,PierdeAmenazas(Turno,amenazas));
           else
             actual = -Quiesce(-beta,-beta+1,prof-R,ply+1);

           UnMakeNullMove;

           if (tab.time_up)
              return  0;

            if ( actual >= beta)
            {
               tab.null_cuts++;
               StuffHash(0, actual, prof, LIM_INF_NULL, 0, ply);

#ifdef DEBUGPV
   if (debughash[ply-1] == HashGlob)
   {
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" nullexit  a: %d, b: %d, d: %0.2f, p: %d, s: %d\n",alpha, beta, (float)prof/UNIT_PROF, ply,actual);
   }
#endif
               return actual;
            }
            else if (actual == -JAQUE_MATE+ply+2)
              null_threat = 1;
        }
     }
   }
#endif

#if FUTIL
   do
   {
     futility = 0;
     if (!tab.jaque[ply] && prof <= 3*UNIT_PROF && Piezas[!Turno] > 1 &&
         !(tab.extensiones[ply] & TRIGGER_THRT))
       {
          if (prof <= 1*UNIT_PROF && Piezas[Turno]>1)
            margen_fut = 3*PEON_VAL;
          else if (prof <= 2*UNIT_PROF)
          {
            if (tab.extensiones[ply])
              break;
            margen_fut = 6*PEON_VAL;
          }
#if RAZOR
          else
          {
             if(TBalance + DAMA_VAL <= alpha &&
                !tab.extensiones[ply] &&
                Piezas[!Turno] > 3)
             {
                prof -= RED_POR_RAZOR;
                margen_fut = 6*PEON_VAL;
             }
             else
              break;
          }
#endif
          futility = (TBalance + margen_fut <= alpha);

#ifdef DEBUGPV
   if (futility && debughash[ply-1] == HashGlob)
   {
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" tryfutil  a: %d, b: %d, d: %0.2f, p: %d\n",alpha, beta, (float)prof/UNIT_PROF, ply);
   }
#endif           

       }
   } while(0);
#endif

#if IID
 while(beta != alpha+1 && tab.proxima_fase[ply] != ELIGE_HASH && prof > 3*UNIT_PROF
       && !tab.nulls)
   {
           if(ply&1)
		   {
             if(alpha != -BETA || beta != -ALPHA)
               break;
		   }
           else if(alpha != ALPHA || beta != BETA)
               break;

           tab.internal_iter++;
           pv[ply][ply].jug = 0;

           actual = NegaScout(alpha,beta,prof-3*UNIT_PROF,ply,0,amenazas);

           if(actual <= alpha && !tab.time_up)
             actual = NegaScout(-JAQUE_MATE,beta, prof-3*UNIT_PROF,ply,0,amenazas);

           if (tab.time_up)
              return  0;

           tab.proxima_fase[ply] = ELIGE_HASH;

           if (pv[ply][ply].jug)
              tab.jug_hash[ply] = pv[ply][ply].jug;
           else
              tab.jug_hash[ply] = Historial[NumJug].jug;

           break;
 }
#endif

   a = alpha;
   b = beta;
   best = -INFINITO;
   mejor_jug = 0;
   fut_cuts = 0;
   can_extend = rply >= ply;
   searched = 0;
   tab.purge_list[ply] = 0;
   
   while ((pos_stack = EligeJugada(ply)))
   {
       Mueve(pos_stack->jug);

       if ((!tab.jaque[ply] || (tab.jug_hash[ply] && searched == 0)) && Jaque(Turno))
         {
           DesMueve();
           Pop_Last_Move(pos_stack);
           continue;
         }

       CambiaTurno();
       searched++;
   
       if (Repeticion(1) || Historial[NumJug].last_cap + 100 < NumJug ||
          (MaterialInsuficiente(0) && MaterialInsuficiente(1)))
       {
          actual = TABLAS;
          pv_long[ply+1] = ply+1;
          goto SEE_IF_BEST;
       }

       tab.jaque[ply+1] = Jaque(Turno);
       extension = -UNIT_PROF;

#if FUTIL

       if (!tab.jaque[ply+1] && a == (b-1))
       {
          if (futility && -TBalance + margen_fut < a)
          {
            CambiaTurno();
            DesMueve();
            tab.fut_cuts++;
            Pop_Last_Move(pos_stack);
            fut_cuts=1;
#ifdef DEBUGPV
   if (debughash[ply-1] == HashGlob)
   {
     jugada_ext j;
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" futil  a: %d, b: %d, d: %0.2f, p: %d, s: %d ",alpha, beta, (float)prof/UNIT_PROF, ply,actual);
     j.jug = pos_stack->jug;
     j.pieza_mov = Tablero[j.jug & 0x3f];
     PintaJugada(&j, Print);
     Print("\n");
   }
#endif
            continue;
          }
        }
#endif

       trigger_ext = 0;

       do
       {
#if CASTLE_STRIKE

                  if (ply < 2)
                  if (Turno)
                  {
                     if (((pos_stack->jug & CAPTURA) >> 12) == PEON_B)
                     {
                        int hacia = (pos_stack->jug >> 6) & 0x3f;

                        if (PosStatus & REY_B_ENRQ_CORTO && 
                            MASK[hacia] & WSCASTLEZONE)
                          extension += CASTLE_STRIKE;
                        else if (PosStatus & REY_B_ENRQ_LARGO && 
                            MASK[hacia] & WLCASTLEZONE)
                          extension += CASTLE_STRIKE;
                     }
                  }
                  else
                  {
                     if (((pos_stack->jug & CAPTURA) >> 12) == PEON_N)
                     {
                        int hacia = (pos_stack->jug >> 6) & 0x3f;

                        if (PosStatus & REY_N_ENRQ_CORTO && 
                            MASK[hacia] & BSCASTLEZONE)
                          extension += CASTLE_STRIKE;
                        else if (PosStatus & REY_N_ENRQ_LARGO && 
                            MASK[hacia] & BLCASTLEZONE)
                          extension += CASTLE_STRIKE;
                     }
                  }
#endif

#if CHECK_EXT
         if (tab.jaque[ply+1])
         {
            int njug;

            extension += can_extend ? CHECK_EXT : CHECK_EXT/2;

            tab.chk_extensions++;
            trigger_ext |= TRIGGER_CHECK;

            if (tab.extensiones[ply-1] & TRIGGER_CSTRIKE &&
                (Historial[NumJug-2].jug & 0xfc0) ==
                (Historial[NumJug-3].jug & 0xfc0))
            {
              extension = 3*UNIT_PROF/2;
//              Print("CSTRIKE!!\n");
              break;
            }

            tab.max_stack[ply+1] = GenJaqJugs(tab.max_stack[ply]);

            njug = Min(tab.max_stack[ply+1] - tab.max_stack[ply],5);

            if (tab.jaque[ply-1] && (Historial[NumJug-1].jug & 0x3f) ==
               ((Historial[NumJug-3].jug >> 6) & 0x3f) &&
               EstaAmenazado(Turno,amenazas))
               {
                 extension += 5*CHECK_EXT/10;
                 break;
               }


            if (!njug)
            {
              CambiaTurno();
              DesMueve();

              actual = JAQUE_MATE - 1 - ply;
              StuffHash(pos_stack->jug, actual, prof, EXACTO, 0, ply);
              if (actual > a && actual < beta)
              {
                 pv[ply][ply].jug = pos_stack->jug;
                 pv[ply][ply].pieza_mov = Tablero[0x3f & pos_stack->jug];
                 pv_long[ply] = pv_long[ply+1];
                 for (aux = ply+1; aux < pv_long[ply+1]; aux++)
                    pv[ply][aux] = pv[ply+1][aux];
              }
              return actual;
            }

            extension += can_extend? CHECK_NJUG[njug] : (CHECK_NJUG[njug]/2);

            if (Historial[NumJug-2].jug & CAPTURA)
              extension += CHECK_EXT/10;

/*            if (prof <= UNIT_PROF && -TBalance < (alpha+3*PEON_VAL))
            {
               extension = Max(0, extension);
            }*/

            break;

         }
#endif

#if RECAP_EXT
          if (!(tab.extensiones[ply] & TRIGGER_RECAP) &&
              Historial[NumJug-2].jug & CAPTURA &&
              Historial[NumJug-1].jug & CAPTURA)

          {
              if (alpha != (beta-1) && searched == 1 && can_extend &&
                  VALOR[(Historial[NumJug-1].jug & CAPTURA)>>12] <
                  VALOR[(Historial[NumJug-2].jug & CAPTURA)>>12])
              {
                  extension += UNIT_PROF/10;
              }
              else
              if ((Historial[NumJug-2].jug & 0xfc0) ==
                  (Historial[NumJug-1].jug & 0xfc0) &&
                  VALOR[(Historial[NumJug-1].jug & CAPTURA)>>12] ==
                  VALOR[(Historial[NumJug-2].jug & CAPTURA)>>12] &&
                  VALOR[(Historial[NumJug-1].jug & CAPTURA)>>12] != PEON_VAL)
              {
                 trigger_ext |= TRIGGER_RECAP;
                 extension += can_extend ? RECAP_EXT:RECAP_EXT/2;
              }
          }
#endif


#if NULL_EXT
   if (null_threat && TBalance < 2*PEON_VAL)
   {
     extension += NULL_EXT/2;
     if (can_extend)
        extension += NULL_EXT/2;

     trigger_ext |= TRIGGER_THRT;
     tab.moreland_extensions++;
   }
#endif


#if PAWN_PUSH_EXT
          if (MASK[(Historial[NumJug-1].jug >> 6) & 0x3f] & (FILA_1 | FILA_6) &&
              Tablero[(Historial[NumJug-1].jug >> 6) & 0x3f] == (Turno ? PEON_N : PEON_B) &&
              Piezas[Turno] < 4)
          {
             extension += PAWN_PUSH_EXT;
             trigger_ext |= TRIGGER_PPUSH;
             tab.recap_extensions++;
          }
#endif

             if (extension > 0)
               extension = 0;

           } while (0);
       

#if ALPHA_REDUCTION

/*          if (!tab.history[pos_stack->jug & 0xfff][!Turno] &&
              !(pos_stack->jug & (PROMOCION|CAPTURA)) && extension == UNIT_PROF)
          {
             extension = -2*UNIT_PROF;
          }*/

          if (searched > 15 && -TBalance > alpha-PEON_VAL && extension == -UNIT_PROF && a == alpha  && prof > UNIT_PROF && Piezas[Turno]>1 && ply > 1 && !(tab.extensiones[ply]|tab.extensiones[ply-2]))
          {
             extension = -2*UNIT_PROF;
          }
#endif
       tab.extensiones[ply+1] = trigger_ext;

       namenazas = ((trigger_ext & TRIGGER_CHECK) || (pos_stack->jug & (CAPTURA|PROMOCION))) ?
                   MantieneAmenaza(Turno,amenazas) : PierdeUnaAmenaza(Turno,amenazas);

       if (prof+extension>0)
         actual = -NegaScout(-b,-a,prof+extension,ply+1,1,namenazas);
       else
         actual = -Quiesce(-b,-a,prof+extension,ply+1);

       
       if (actual > a && ((actual < beta) || extension < -UNIT_PROF) && searched != 1 && !tab.time_up)
       {
          extension = extension < -UNIT_PROF ? -UNIT_PROF : extension;
          
          if (prof+extension>0)
            actual = -NegaScout(-beta,-a,prof+extension,ply+1,1,namenazas);
          else
            actual = -Quiesce(-beta,-a,prof+extension,ply+1);
       }

       SEE_IF_BEST:

       CambiaTurno();
       DesMueve();

#ifdef DEBUGPV
   if (debughash[ply-1] == HashGlob && pos_stack->jug == pvdebug[ply].jug)
   {
     jugada_ext j;
     PintaJugada(&pvdebug[ply-1], Print);
     Print(" jugnum: %d negascou  a: %d, b: %d, d: %0.2f, p: %d, s: %d ",
              searched, alpha, beta, (float)prof/UNIT_PROF, ply,actual);
     Print("restantes: %d ",tab.max_stack[ply]-tab.max_stack[ply-1]);
     j.jug = pos_stack->jug;
     j.pieza_mov = Tablero[j.jug & 0x3f];
     PintaJugada(&j, Print);
     Print("\n");
   }
#endif

        if (tab.time_up)
          return  0;

        if (actual > best)
        {
           best = actual;
           if (actual > a)
           {
               mejor_jug = Historial[NumJug].jug;

               if ( actual >= beta )
               {
                     if (!(pos_stack->jug & (CAPTURA|PROMOCION)))
                     {
                       UpdateKillers(mejor_jug);
                     }

                   tab.beta_cutoffs++;

                   if (searched == 1)
                      tab.first_succ++;

                   StuffHash(pos_stack->jug, actual, prof, LIM_INF, null_threat, ply);
                   if (!(pos_stack->jug & (CAPTURA|PROMOCION)) && !(tab.jaque[ply]))
                     tab.history[pos_stack->jug & 0xfff][Turno] += 1+(prof*prof)/(UNIT_PROF*UNIT_PROF);

                   return (actual);         //  fail high  (cut-off)
               }
               pv[ply][ply].jug = pos_stack->jug;
               pv[ply][ply].pieza_mov = Tablero[0x3f & pos_stack->jug];
               pv_long[ply] = pv_long[ply+1];
               for (aux = ply+1; aux < pv_long[ply+1]; aux++)
                  pv[ply][aux] = pv[ply+1][aux];
               a = best;
             }
//           if (searched == 1)
//             mejor_jug = Historial[NumJug].jug;
        }
       b = a + 1;
       Pop_Last_Move(pos_stack);
    }

   if (searched == 0 && !fut_cuts)
   {
      if (tab.jaque[ply])
        return -JAQUE_MATE + ply;
      else
        return TABLAS;
   }

#if FUTIL
   if (alpha == a && fut_cuts)
      best = a;//TBalance+margen_fut;
#endif

   if (!tab.time_up)
   {
      StuffHash(mejor_jug, best, prof, a <= alpha ? LIM_SUP : EXACTO, null_threat, ply);

      if (!(mejor_jug & (PROMOCION|CAPTURA)) && !(tab.jaque[ply]))
        tab.history[mejor_jug & 0xfff][Turno] += 1+(prof*prof)/(UNIT_PROF*UNIT_PROF);
   }

   return best;
}
//-------------------------------------------------------------------------------------------------
int DealPrune(int beta, int ply)
{
  int actual;
  int eval;
  list_jug *pos_stack;

  eval = Evaluacion(beta-1,INFINITO);
//  Print("eval: %d\n",eval);

  if (eval >= beta)
  {
     CambiaTurno();
     if ((tab.max_stack[ply] = GenCaps(tab.max_stack[ply-1])) == tab.max_stack[ply-1])
     {
        CambiaTurno();
        return 1;
     }

     for (pos_stack = tab.max_stack[ply-1]; pos_stack < tab.max_stack[ply]; pos_stack++)
     {
        actual = VALOR[(pos_stack->jug >> 14) & 0xf] + VALOR[(pos_stack->jug >> 18) & 0xf];

//        PintaJugadaCoord(pos_stack->jug);
//        Print(" %d - %d < %d\n",eval, SSE(pos_stack->jug), beta);
        if (eval - SSE(pos_stack->jug) < beta)

//        if ((TBalance - 2*actual/3) < beta)
        {
            CambiaTurno();
            return 0;
        }
     }

     CambiaTurno();
     return 1;
  }

  return 0;
}
//-------------------------------------------------------------------------------------------------
#if HASH_QFRONTIER
int Quiesce(int alpha, int beta, int prof, int ply)
{
  int tipo;
  int la_mejor, actual, pos_eval;
  int val;
  int old_alpha = alpha;
  entero32 mejor_jug = 0;
  list_jug *pos_stack;

  pv_long[ply] = ply;
  tab.nodos_buscados++;
  tab.nodos_quies++;

  pos_eval = la_mejor = Evaluacion(alpha, beta);

  if (la_mejor > alpha)
  {
      if (tab.jaque[ply])
         return NegaScout(alpha, beta, UNIT_PROF, ply,1,0);
      if (la_mejor >= beta)
         return la_mejor;
      alpha = la_mejor;
  }
  else if (la_mejor == alpha)
     pos_eval = TBalance;

  if ((tab.max_stack[ply] = GenCaps(tab.max_stack[ply-1])) != tab.max_stack[ply-1])
    InsertSort(tab.max_stack[ply-1],tab.max_stack[ply]);

  for (pos_stack = tab.max_stack[ply-1]; pos_stack < tab.max_stack[ply]; pos_stack++)
  {
     if (Piezas[!Turno] > 1)
     {
        int val = VALOR[(pos_stack->jug >> 12) & 0xf];

#if ALPHA_PRUNE
        if (pos_stack->jug & CAPTURA)
        {
           if (val + VALOR[(pos_stack->jug >> 16) & 0xf] + pos_eval + MARGEN_QUIES < alpha)
              continue;
        }
#endif

#if SEE_PRUNE
        if (Material(!Turno) - val > 3*PEON_VAL && (val = SSE(pos_stack->jug)) < 0)
          continue;
#endif
     }


     Mueve(pos_stack->jug);
     if (Jaque(Turno))
     { DesMueve(); continue; }
     CambiaTurno();

     tab.jaque[ply+1] = Jaque(Turno);

     actual = -Quiesce(-beta,-alpha,prof-UNIT_PROF,ply+1);

     CambiaTurno();
     DesMueve();

//     if (tab.time_up)
//       return  0;

     if (actual > alpha)
     {
         la_mejor = actual;
         if (la_mejor >= beta)
            return la_mejor;

         alpha = actual;
         pv[ply][ply].jug = pos_stack->jug;
         pv[ply][ply].pieza_mov = Tablero[0x3f & pos_stack->jug];
         pv_long[ply] = pv_long[ply+1];
         for (val = ply+1; val < pv_long[ply+1]; val++)
           pv[ply][val] = pv[ply+1][val];
     }
  }

  if (!Historial[NumJug-1].jug)
     tab.max_stack[ply] = GenChecks(tab.max_stack[ply]);

  if (tab.max_stack[ply] == tab.max_stack[ply-1])
     return la_mejor;

  for (pos_stack = tab.max_stack[ply-1]; pos_stack < tab.max_stack[ply]; pos_stack++)
  {
     if (Piezas[!Turno] > 1)
     {
        int val = VALOR[(pos_stack->jug >> 12) & 0xf];

#if ALPHA_PRUNE
        if (pos_stack->jug & CAPTURA)
        {
           if (val + VALOR[(pos_stack->jug >> 16) & 0xf] + pos_eval + MARGEN_QUIES > alpha)
              continue;
        }
#endif

#if SEE_PRUNE
        if (SSE(pos_stack->jug) < 0)
          continue;
#endif
     }


     Mueve(pos_stack->jug);
     if (Jaque(Turno))
     { DesMueve(); continue; }
     CambiaTurno();

     tab.jaque[ply+1] = Jaque(Turno);
     if (Repeticion(1))
       actual = TABLAS;
     else
       actual = -Quiesce(-beta,-alpha,prof-UNIT_PROF,ply+1);

     CambiaTurno();
     DesMueve();

     if (actual > alpha)
     {
         la_mejor = actual;
         if (la_mejor >= beta)
            return la_mejor;

         alpha = actual;
         pv[ply][ply].jug = pos_stack->jug;
         pv[ply][ply].pieza_mov = Tablero[0x3f & pos_stack->jug];
         pv_long[ply] = pv_long[ply+1];
         for (val = ply+1; val < pv_long[ply+1]; val++)
           pv[ply][val] = pv[ply+1][val];
     }
  }

  return la_mejor;
}
//---------------------------------------------------------------------
int QuiescentSearch(int alpha, int beta, int prof, int ply)
#else
int Quiesce(int alpha, int beta, int prof, int ply)
#endif
{
  int actual, pos_eval;
  int val;
  list_jug *pos_stack;

  pv_long[ply] = ply;
  tab.nodos_buscados++;
  tab.nodos_quies++;

  pos_eval = Evaluacion(alpha, beta);
#if QCHECK_CHECK
  if (prof <= -UNIT_PROF || !tab.jaque[ply] || TBalance < -PEON_VAL)
#endif
  {
     if (prof <= (-MAX_QUIESCE*UNIT_PROF))
       return pos_eval;

     if (pos_eval > alpha)
     {
        if (pos_eval >= beta)
          return pos_eval;
        alpha = pos_eval;
     }

     if ((tab.max_stack[ply] = GenCaps(tab.max_stack[ply-1])) == tab.max_stack[ply-1])
        return pos_eval;
     InsertSort(tab.max_stack[ply-1],tab.max_stack[ply]);
     if (tab.max_stack[ply-1]->valor > 30*PEON_VAL)
       return INFINITO;
  }
#if QCHECK_CHECK
  else
  {
     if ((tab.max_stack[ply] = GenJaqJugs(tab.max_stack[ply-1])) == tab.max_stack[ply-1])
        return -JAQUE_MATE+ply;
  }
#endif


  for (pos_stack = tab.max_stack[ply-1]; pos_stack < tab.max_stack[ply]; pos_stack++)
  {
     val = VALOR[(pos_stack->jug >> 12) & 0xf];

     if (Piezas[!Turno] > 1 && Material(!Turno) - val > 4*PEON_VAL)
     {
#if ALPHA_PRUNE
        if (val + VALOR[(pos_stack->jug >> 16) & 0xf] + pos_eval + MARGEN_QUIES < alpha)
           continue;
#endif

#if SEE_PRUNE
        if ((val = SSE(pos_stack->jug)) < 0)
          continue;

        val +=  2*MARGEN_QUIES + pos_eval;
        if (val < alpha)
          continue;
#endif

     }

     Mueve(pos_stack->jug);
     if (Jaque(Turno))
     { DesMueve(); continue; }
     CambiaTurno();

#if HASH_QFRONTIER
    actual = -QuiescentSearch(-beta, -alpha,prof-UNIT_PROF,ply+1);
#else
    actual = -Quiesce(-beta, -alpha,prof-UNIT_PROF,ply+1);
#endif

    CambiaTurno();
    DesMueve();

    if (actual > alpha)
    {
         if (actual >= beta)
            return actual;
         alpha = actual;
         pv[ply][ply].jug = pos_stack->jug;
         pv[ply][ply].pieza_mov = Tablero[0x3f & pos_stack->jug];
         pv_long[ply] = pv_long[ply+1];
         for (val = ply+1; val < pv_long[ply+1]; val++)
           pv[ply][val] = pv[ply+1][val];
    }
  }
  return alpha;
}


