/* Pepito v1.59  -- file: itera.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"
#endif


#ifdef CASTLE_STRIKE
#undef CASTLE_STRIKE
#endif

#define CASTLE_STRIKE     (UNIT_PROF/5)


#define GOOD_MOVE   1
#define BAD_MOVE    2

#define RWINDOW     (PEON_VAL/3)

extern list_jug *rpos_stack;
extern int RScore, RMat, SafetyFlags, easy_move, rply;
extern int ALPHA, time_extend, text_count;

typedef struct {

    jugada_ext *pv;
    int length;
    int score;
    int depth;
    unsigned long time;
    unsigned long nps;
    unsigned long nodes;

} pv_t;

void MultiPV(entero32 *eleccion, int ponder_on, entero32 time_limit, int *bk_flag, int num_pvs)
  {
     list_jug *mejor_jug;
     long i;
     float tim;
     int n, amenazas;
     int ACTUAL, a;
     int out_of_window, pinta_pv;
     int first, egtb_conv = 0;
     int extension;
     long total_nods_iter;
     long NodsIter[MAX_PROF];
     pv_t *mpv_info;
     pv_t *curr_pv;
     double bfactor = 0;
     int fh_inrow, fl_inrow;

     MaxPos[0] = Max(PEON_VAL, MaxPos[0]>>1);
     MaxPos[1] = Max(PEON_VAL, MaxPos[1]>>1);
     pv_long[0] = 0;

     if (bk_flag)
       *bk_flag = NO_BOOK_MOVES;

     TimeReset();
     tab.time_up = 0;

     for (i=0;i<4096;i++)
     {
       tab.history[i][0] >>= 8;
       tab.history[i][1] >>= 8;
     }

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

//     memset(tab.eval_cache, 0,CACHE_SIZE*sizeof(ecache));

     RTurno = Turno;
     SafetyFlags = 0;

     switch(GAME_STYLE)
     {
       case 0: SafetyFlags = 2 >> Turno; break;
       case 1: SafetyFlags = 0; break;
       case 2: SafetyFlags = 3; break;
     }

     tab.max_stack[0] = JugadasDeLibro(tab.max_stack[0]);

     mpv_info = malloc(num_pvs * sizeof(pv_t));

     for(i=0; i < num_pvs; i++)
     {
        mpv_info[i].pv = malloc((MAX_PROF+1)*sizeof(jugada_ext));
        mpv_info[i].length = 0;
        mpv_info[i].depth = -1;
        mpv_info[i].pv[0].jug = 0;
        mpv_info[i].score = -INFINITO;
     }


     if (tab.have_book_move && BOOK_RANDOM)
     {
        float rnd;
        int sum=0;
        jugada_ext j;

        if (bk_flag && (tab.max_stack[0]-JugStack) > 1)
            *bk_flag = SEVERAL_BOOK_MOVES;


        ShellSort(JugStack, tab.max_stack[0]);
        srand(time(NULL));

        for(i=0, rpos_stack = tab.max_stack[0]-1; rpos_stack >= JugStack && i < BOOK_WIDTH; rpos_stack--, i++)
          sum += rpos_stack->valor;

        for(i=0, rpos_stack = tab.max_stack[0]-1; rpos_stack >= JugStack && i < BOOK_WIDTH; rpos_stack--, i++)
        {
           j.jug = rpos_stack->jug;
           j.pieza_mov = Tablero[j.jug & 0x3f];
           PintaJugada(&j, Print);
           Print("(%.2f %%) ", (double)rpos_stack->valor*100/sum);
        }
        Print("\n");

        rnd = (float)sum*rand()/RAND_MAX;
        sum = (tab.max_stack[0]-1)->valor;

        for(i=0, rpos_stack = tab.max_stack[0]-1; sum < rnd && rpos_stack > JugStack && i < BOOK_WIDTH; rpos_stack--, i++)
           sum += rpos_stack->valor;

        *eleccion = rpos_stack->jug;

        return;
     }

     if (tab.max_stack[0] == (JugStack+1) && !ponder_on)
     {
        *eleccion = JugStack->jug;
        return;
     }

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

     tab.nodos_buscados = 0;
     tab.nodos_quies = 0;

     tab.null_attempts = 0;
     tab.null_cuts = 0;
     tab.hash_hits = 0;
     tab.hash_used = 0;
     tab.p_hash_trys = 0;
     tab.p_hash_hits = 0;
     tab.fut_cuts = 0;
     tab.nodos_control = CONTROL_INC;
     tab.last_report = 0;
     if (time_limit < 200)
       tab.nodos_control = 2000;
     tab.nodos_buscados = 0;
     tab.first_succ = 0;
     tab.beta_cutoffs = 0;
     tab.internal_iter=0;
     tab.chk_extensions = 0;
     tab.moreland_extensions = 0;
     tab.recap_extensions = 0;
     tab.pondering = ponder_on;
     tab.time_limit = time_limit;
     tab.egtb_probes = 0;
     tab.egtb_hits = 0;
     tab.evals = 0;
     tab.eval_hits = 0;
     tab.lazy_evals = 0;

MPV_START_OF_THINKING:

     total_nods_iter = 0;

     for(rpos_stack = tab.jug_stack; rpos_stack < tab.max_stack[0]; rpos_stack++)
     {
        rpos_stack->valor = SSE(rpos_stack->jug);
        Mueve(rpos_stack->jug);
        CambiaTurno();
        tab.jaque[1] = Jaque(Turno);
        rpos_stack->valor = -Quiesce(-INFINITO,INFINITO, UNIT_PROF,1);
        CambiaTurno();
        DesMueve();
     }

     ShellSort(tab.jug_stack, tab.max_stack[0]);
     RMat = TBalance;

     easy_move = 0;
     if (((tab.max_stack[0]-1)->valor -2*PEON_VAL)> (tab.max_stack[0]-2)->valor)
     {
       if (((tab.max_stack[0]-1)->jug & CAPTURA && RMat < -PEON_VAL)
           || !((tab.max_stack[0]-1)->jug & CAPTURA))
         easy_move = xb_info.my_clock && !ponder_on;
     }

     if (Nalimov && (NumPeonesB + NumPeonesN + PiezasB + PiezasN) <= (Nalimov - 2) &&
         (ACTUAL = ProbeTB(0)) != -1)
     {
        egtb_conv = ACTUAL > 0 ? ACTUAL+1 : 0;
     }

     RScore = (tab.max_stack[0]-1)->valor;

     time_extend = 0;
     text_count = 0;

     for (MaxProf = UNIT_PROF, rply = 2; MaxProf <= MAXPROF; MaxProf += UNIT_PROF, rply++)
     {
          out_of_window = 1;
          total_nods_iter =  tab.nodos_buscados;
          fh_inrow = 0, fl_inrow = 0;

          while (out_of_window)
          {
              first = 1;
              pinta_pv = 0;
              a = -INFINITO;

              for(rpos_stack = tab.max_stack[0]-1; rpos_stack >= tab.jug_stack; rpos_stack--)
              {   
                  if (tab.nodos_buscados > 140000)
                  {  
                     Print("info currmove ");
                     PintaJugadaCoord(rpos_stack->jug);
                     Print("currmovenumber %d\n",tab.max_stack[0]-rpos_stack);
                  }

                  tab.nodos_jug_raiz = tab.nodos_buscados + tab.fut_cuts;
                  Mueve(rpos_stack->jug);
                  CambiaTurno();

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

#if CASTLE_STRIKE

                  if (Turno)
                  {
                     if (((rpos_stack->jug & CAPTURA) >> 12) == PEON_B)
                     {
                        int hacia = (rpos_stack->jug >> 6) & 0x3f;

                        if (PosStatus & REY_B_ENRQ_CORTO && 
                            MASK[hacia] & WSCASTLEZONE)
                          tab.extensiones[1] |= TRIGGER_CSTRIKE;
                        else if (PosStatus & REY_B_ENRQ_LARGO && 
                            MASK[hacia] & WLCASTLEZONE)
                          tab.extensiones[1] |= TRIGGER_CSTRIKE;
                     }
                  }
                  else
                  {
                     if (((rpos_stack->jug & CAPTURA) >> 12) == PEON_N)
                     {
                        int hacia = (rpos_stack->jug >> 6) & 0x3f;

                        if (PosStatus & REY_N_ENRQ_CORTO && 
                            MASK[hacia] & BSCASTLEZONE)
                          tab.extensiones[1] |= TRIGGER_CSTRIKE;
                        else if (PosStatus & REY_N_ENRQ_LARGO && 
                            MASK[hacia] & BLCASTLEZONE)
                          tab.extensiones[1] |= TRIGGER_CSTRIKE;
                     }
                  }
#endif

                  pv_long[1] = 1;
                  if (Repeticion(0))
                    ACTUAL = TABLAS;
                  else
                  {
                     amenazas = IniAmenazas();
                     if (!tab.jaque[1] && !(rpos_stack->jug & (CAPTURA|PROMOCION)))
                       amenazas = PierdeUnaAmenaza(Turno, amenazas);

                     ACTUAL = -NegaScout(-INFINITO,-a, MaxProf+extension,1,1,amenazas);
                  }

                  CambiaTurno();
                  DesMueve();

                  if (tab.time_up)
		            goto MPV_END_OF_THINKING;

                  rpos_stack->valor = tab.nodos_jug_raiz;
                  if (xb_info.post || UCI_MODE)
                    pinta_pv |= (MaxProf >= DISPLAY_MARGIN);

                  if (ACTUAL > a)
                  {
                      for (i=0; i < num_pvs; i++)
                      {
                          if (mpv_info[i].depth < MaxProf || ACTUAL > mpv_info[i].score)
                          {
                              int j;
                              curr_pv = mpv_info + i;
                              if (mpv_info[i].pv[0].jug != rpos_stack->jug)
                              {
                                 jugada_ext *paux;

                                 for (j = num_pvs-2; j >= 0 && j >= i; j--)
                                 {
                                    mpv_info[j+1].depth = mpv_info[j].depth;
                                    mpv_info[j+1].length = mpv_info[j].length;
                                    mpv_info[j+1].nodes = mpv_info[j].nodes;
                                    mpv_info[j+1].score = mpv_info[j].score;
                                    paux = mpv_info[j+1].pv;
                                    mpv_info[j+1].pv = mpv_info[j].pv;;
                                    mpv_info[j].pv = paux;
                                 }
                              }
                              break;
                          }
                      }

                      curr_pv->time = TimeDiff();
                      curr_pv->nodes = tab.nodos_buscados;
                      curr_pv->length = pv_long[1];
                      curr_pv->pv[0].jug = rpos_stack->jug;
                      curr_pv->pv[0].pieza_mov = Tablero[rpos_stack->jug  & 0x3f];
                      curr_pv->depth = MaxProf;
                      curr_pv->score = ACTUAL;

                      a = mpv_info[num_pvs-1].depth == MaxProf ?
                          mpv_info[num_pvs-1].score : -INFINITO;
 
                      if (curr_pv == mpv_info)
                        mejor_jug = rpos_stack;

                      Mueve(rpos_stack->jug);
                      CambiaTurno();

                      for (i=1; i < pv_long[1]; i++)
                      {
                         curr_pv->pv[i].jug = pv[1][i].jug;
                         curr_pv->pv[i].pieza_mov = pv[1][i].pieza_mov;
                         Mueve(pv[1][i].jug);
                         CambiaTurno();
                      }

                      while(ProbeHash(&(tab.max_stack[0]->jug),&ACTUAL,&ACTUAL,&ACTUAL,&ACTUAL) &&
                            tab.max_stack[0]->jug && !Repeticion(0))
                      {
                          curr_pv->pv[i].jug = tab.max_stack[0]->jug;
                          curr_pv->pv[i].pieza_mov = Tablero[tab.max_stack[0]->jug & 0x3f];
                          Mueve(curr_pv->pv[i++].jug);
                          CambiaTurno();
                      }

                      curr_pv->length = i;

                      while (i-- > 0)
                      {
                          CambiaTurno();
                          DesMueve();
                      }


                      for (i=0; i < num_pvs; i++)
                      {
                          int j;
                          Print("info multipv %d ",i+1);
                          if (mpv_info[i].depth >= 0)
                          {
                              Print("score ");
                              if (mpv_info[i].score > JAQUE_MATE-1000)
                                  Print("mate %d ", (JAQUE_MATE-mpv_info[i].score+1)/2);
                              else if (ACTUAL < -JAQUE_MATE+1000)
                                  Print("mate %d ", (-JAQUE_MATE-mpv_info[i].score)/2);
                              else
                                  Print("cp %d ", mpv_info[i].score);
 
                              Print("depth %d nodes %d time %d nps %d pv ", mpv_info[i].depth/UNIT_PROF,
                                    mpv_info[i].nodes, mpv_info[i].time*10, 100*mpv_info[i].nodes/mpv_info[i].time);
                              for (j=0;j<mpv_info[i].length;j++)
                                 PintaJugada(mpv_info[i].pv+j, Print);
                          }
                          Print("\n");
                      }
                      Swap(rpos_stack, tab.max_stack[0]-1-(curr_pv-mpv_info));
                  }
               }

               total_nods_iter = tab.nodos_buscados /*+ tab.fut_cuts*/ - total_nods_iter;
               NodsIter[-1 + MaxProf / UNIT_PROF] = tab.nodos_buscados + tab.fut_cuts;

               total_nods_iter = tab.nodos_buscados /*+ tab.fut_cuts*/ - total_nods_iter;

               RScore = a;

               time_extend &= (time_extend-1);
               if (!(mejor_jug->jug & (CAPTURA|PROMOCION)) && !tab.jaque[0])
                   tab.history[mejor_jug->jug & 0xfff][Turno] += 1+(MaxProf*MaxProf)/(UNIT_PROF*UNIT_PROF);

               out_of_window = 0;
               LastProf = MaxProf-UNIT_PROF;

               tim = (float) TimeDiff();
               if (tim > 2*tab.time_limit/3 && !time_extend && xb_info.my_clock)
                   goto MPV_END_OF_THINKING;

//               if (tab.max_stack[0]-JugStack > num_pvs)
//                 ShellSort(JugStack, tab.max_stack[0]-num_pvs);

            }  // jugadas raiz

       }   // fin jugadas raiz

MPV_END_OF_THINKING:


if (egtb_conv)
{
   if (egtb_conv > 9000)
   {
      Log("Faltan EGTBs! Probando busqueda sin Nalimov... %d\n",egtb_conv);
      egtb_conv = Nalimov;
      Nalimov = 0;
      ClearTables(0);
      goto MPV_START_OF_THINKING;
   }
   else
     Nalimov = egtb_conv;
}

{
	double eratio;
     Log("\nTime spent: %0.2f\n",  tim = ((float)TimeDiff())/100);

     for (n = 1; n < MaxProf/UNIT_PROF - 1; n++)
       bfactor += (NodsIter[n-1] ? NodsIter[n] / NodsIter[n-1] : 0);
     bfactor /= (MaxProf/UNIT_PROF -2);

     if (rpos_stack - tab.jug_stack > 0)
       Log("Depth reached: %0.3f\n",MaxProf/UNIT_PROF-1+(float)(rpos_stack - tab.jug_stack)
                                                        /(tab.max_stack[0] - tab.jug_stack));
     else
       Log("Depth reached: %d\n",MaxProf/UNIT_PROF-1);

     Log("Nodes per second: %0.8f\n",(double) (tab.nodos_buscados)/tim);
     Log("Interior nodes: %ld\n",tab.nodos_buscados-tab.nodos_quies);
     Log("Quiescent nodes: %ld\n",tab.nodos_quies);

     Log("First_success: %ld/%ld  (%0.3f %%).\n",tab.first_succ,tab.beta_cutoffs,
             100*( (double) tab.first_succ / tab.beta_cutoffs ));
     Log("BranchingFactor: %0.3f\n",bfactor);
     Log("Null-moves: %ld/%ld  (%0.3f %%).\n",tab.null_cuts,tab.null_attempts,
             100*( (double) tab.null_cuts/tab.null_attempts));
     Log("Hash hits: %ld/%ld  (%0.3f %%).\n",tab.hash_hits,tab.nodos_buscados-tab.nodos_quies,
             100*( (double) tab.hash_hits / (tab.nodos_buscados-tab.nodos_quies)));
	 eratio = tab.egtb_probes > 0? 100*( (double) tab.egtb_hits / tab.egtb_probes) : 0;
     Log("EGTB hits: %ld/%ld  (%0.3f %%).\n",tab.egtb_hits,tab.egtb_probes, eratio);
     Log("Pawn Hash hits: %ld/%ld  (%0.3f %%).\n",tab.p_hash_hits,tab.p_hash_trys,
             100*( (double) tab.p_hash_hits / tab.p_hash_trys));
     Log("Futility cut-offs: %ld.\n",tab.fut_cuts);
     Log("IID: %ld.\n",tab.internal_iter);
     Log("Evals:  %ld.  Lazy:  %ld. (%0.3f %%). Cached:   %ld. (%0.3f %%).\n",
		     tab.evals, tab.lazy_evals, 100*( (double) tab.lazy_evals / tab.evals),
                         tab.eval_hits, 100*((double) tab.eval_hits / (tab.evals-tab.lazy_evals)));

     Log("Extensions:   CHK  %ld.  MRL  %ld.  RCP  %ld.\n",
             tab.chk_extensions, tab.moreland_extensions,tab.recap_extensions);

     Log("MaxPosB:   %d, MaxPosN: %d\n",MaxPos[0],MaxPos[1]);


/*     Log("Move ordering ratio:\n");
     for (i=0;i<80;i++)
      if (tab.fail_high_record[i])
        Log("%2d. %8d times (%5.3f)\n",i+1,tab.fail_high_record[i],
              100*(double) tab.fail_high_record[i] / tab.beta_cutoffs);
*/
}
     memcpy(pv[0], mpv_info[0].pv, sizeof(jugada_ext)*mpv_info[0].length);
     *eleccion = mpv_info[0].pv->jug;
     pv_long[0] = mpv_info[0].length;

     for(i=0; i < num_pvs; i++)
        free(mpv_info[i].pv);

     free(mpv_info);
  }
//----------------------------------------------------------------------------
