/*
 
    Protector -- a UCI chess engine
 
 
 
    Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
 
 
 
    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 3 of the License, 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.  If not, see <http://www.gnu.org/licenses/>.
 
 
 
*/
 
 
 
#include <stdio.h>
 
#include <string.h>
 
#include <assert.h>
 
#include "matesearch.h"
 
#include "io.h"
 
#include "movegeneration.h"
 
#include "hash.h"
 
#include "coordination.h"
 
 
 
static int searchMate(Variation * variation, int alpha, int beta,
 
                      const int ply, const int restDepth,
 
                      PrincipalVariation * pv)
 
{
 
   const int oldAlpha = alpha;
 
   Position *position = &variation->singlePosition;
 
   const bool check = activeKingIsSafe(position) == FALSE;
 
   int best = VALUE_MATED;
 
   Movelist movelist;
 
   Hashentry *tableHit = NULL;
 
   UINT8 hashentryFlag;
 
   int i, historyLimit;
 
   Move hashmove = NO_MOVE;
 
   Move bestMove = NO_MOVE;
 
   Move currentMove;
 
   PrincipalVariation newPv;
 
 
 
   newPv.length = pv->length = 0;
 
 
 
   historyLimit = POSITION_HISTORY_OFFSET + variation->ply -
 
      variation->singlePosition.halfMoveClock;
 
 
 
 
 
   for (i = POSITION_HISTORY_OFFSET + variation->ply - 4;
 
        i >= historyLimit; i -= 2)
 
   {
 
      if (variation->singlePosition.hashKey == variation->positionHistory[i])
 
      {
 
         return 0;
 
      }
 
   }
 
 
 
   /* Probe the transposition table */
 
   /* ----------------------------- */
 
   tableHit = getHashentry(getSharedHashtable(),
 
                           variation->singlePosition.hashKey);
 
 
 
   if (tableHit != NULL)
 
   {
 
      const int importance = getHashentryImportance(tableHit);
 
 
 
      hashmove = (Move) getHashentryMove(tableHit);
 
 
 
      if (hashmove != NO_MOVE)
 
      {
 
         if (moveIsPseudoLegal(position, hashmove))
 
         {
 
            assert(moveIsLegal
(position
, hashmove
));  
 
 
            appendMoveToPv(&newPv, pv, hashmove);
 
         }
 
         else
 
         {
 
            hashmove = NO_MOVE;
 
         }
 
      }
 
 
 
      if (restDepth <= importance)
 
      {                         /* 99% */
 
         const int value = getHashentryValue(tableHit);
 
         const int hashValue = calcEffectiveValue(value, ply);
 
         const int flag = getHashentryFlag(tableHit);
 
 
 
         switch (flag)
 
         {
 
         case HASHVALUE_UPPER_LIMIT:
 
            if (hashValue <= alpha)
 
            {
 
               return hashValue;
 
            }
 
            break;
 
 
 
         case HASHVALUE_EXACT:
 
            return hashValue;
 
 
 
         case HASHVALUE_LOWER_LIMIT:
 
            if (hashValue >= beta)
 
            {
 
               return hashValue;
 
            }
 
            break;
 
 
 
         default:;
 
         }
 
      }
 
   }
 
 
 
   if (ply >= 2)
 
   {
 
      variation->plyInfo[ply].killerMove3 =
 
         variation->plyInfo[ply - 2].killerMove1;
 
      variation->plyInfo[ply].killerMove4 =
 
         variation->plyInfo[ply - 2].killerMove2;
 
   }
 
   else
 
   {
 
      variation->plyInfo[ply].killerMove3 = NO_MOVE;
 
      variation->plyInfo[ply].killerMove4 = NO_MOVE;
 
   }
 
 
 
   if (restDepth == 1)
 
   {
 
      initCheckMovelist(&movelist, position, &variation->historyValue[0]);
 
   }
 
   else
 
   {
 
      movelist.positionalGain = &(variation->positionalGain[0]);
 
      initStandardMovelist(&movelist, &variation->singlePosition,
 
                           &variation->plyInfo[ply],
 
                           &variation->historyValue[0], hashmove, check);
 
   }
 
 
 
   initializePlyInfo(variation);
 
 
 
   while ((currentMove = getNextMove(&movelist)) != NO_MOVE)
 
   {
 
      int value;
 
 
 
      variation->nodes++;
 
 
 
      if (makeMoveFast(variation, currentMove) != 0 ||
 
          passiveKingIsSafe(&variation->singlePosition) == FALSE ||
 
          (restDepth == 1 && activeKingIsSafe(&variation->singlePosition)))
 
      {
 
         unmakeLastMove(variation);
 
 
 
         continue;
 
      }
 
 
 
      if (restDepth > 0)
 
      {
 
         if (restDepth == 1)
 
         {
 
            if (kingCanEscape(position))
 
            {
 
               value = 0;
 
            }
 
            else
 
            {
 
               value = -(VALUE_MATED + ply + 1);
 
            }
 
         }
 
         else
 
         {
 
            value = -searchMate(variation, -beta, -alpha, ply + 1,
 
                                restDepth - 1, &newPv);
 
         }
 
      }
 
      else
 
      {
 
         value = 0;
 
      }
 
 
 
      unmakeLastMove(variation);
 
 
 
      if (value > best)
 
      {
 
         best = value;
 
         appendMoveToPv(&newPv, pv, currentMove);
 
 
 
         if (best > alpha)
 
         {
 
            alpha = best;
 
            bestMove = currentMove;
 
 
 
            if (ply == 0)
 
            {
 
               if (variation->threadNumber == 0 &&
 
                   variation->handleSearchEvent != 0)
 
               {
 
                  getGuiSearchMutex();
 
                  variation->handleSearchEvent(SEARCHEVENT_NEW_PV, variation);
 
                  releaseGuiSearchMutex();
 
               }
 
            }
 
 
 
            if (best >= beta)
 
            {
 
               goto finish;
 
            }
 
         }
 
      }
 
   }
 
 
 
   if (best == VALUE_MATED)
 
   {
 
      if (check)
 
      {
 
         /* mate */
 
 
 
         best = VALUE_MATED + ply;
 
      }
 
      else
 
      {
 
         /* stalemate */
 
 
 
         best = variation->drawScore[position->activeColor];
 
      }
 
   }
 
 
 
 finish:
 
 
 
   if (bestMove != NO_MOVE)
 
   {
 
      if (position->piece[getToSquare(bestMove)] == NO_PIECE &&
 
          getNewPiece(bestMove) == NO_PIECE &&
 
          (getToSquare(bestMove) != position->enPassantSquare ||
 
           pieceType(position->piece[getFromSquare(bestMove)]) != PAWN))
 
      {
 
         Move killerMove = bestMove;
 
         const Piece movingPiece = position->piece[getFromSquare(killerMove)];
 
         const int index = historyIndex(bestMove, position);
 
 
 
         setMoveValue(&killerMove, movingPiece);
 
         registerKillerMove(&variation->plyInfo[ply], killerMove);
 
 
 
         variation->historyValue[index] = (UINT16)
 
            (variation->historyValue[index] + (restDepth * restDepth));
 
 
 
         if (variation->historyValue[index] > HISTORY_MAX)
 
         {
 
            shrinkHistoryValues(variation);
 
         }
 
      }
 
   }
 
 
 
   /* Store the value in the transposition table. */
 
   /* ------------------------------------------- */
 
   if (best > oldAlpha)
 
   {
 
      hashentryFlag =
 
         (best >= beta ? HASHVALUE_LOWER_LIMIT : HASHVALUE_EXACT);
 
   }
 
   else
 
   {
 
      hashentryFlag = HASHVALUE_UPPER_LIMIT;
 
   }
 
 
 
   setHashentry(getSharedHashtable(), position->hashKey,
 
                calcHashtableValue(best, ply), (INT8) restDepth,
 
                packedMove(bestMove), hashentryFlag, 0);
 
 
 
   return best;
 
}
 
 
 
static int searchBaseMoves(Variation * variation, const int alpha,
 
                           const int beta, const int restDepth,
 
                           Movelist * solutions)
 
{
 
   Position *position = &variation->singlePosition;
 
   const bool check = activeKingIsSafe(position) == FALSE;
 
   int best = VALUE_MATED, ply = 0;
 
   Movelist movelist;
 
   Move hashmove = NO_MOVE;
 
   Move currentMove;
 
   PrincipalVariation pv;
 
 
 
   if (restDepth == 1)
 
   {
 
      initCheckMovelist(&movelist, position, &variation->historyValue[0]);
 
   }
 
   else
 
   {
 
      movelist.positionalGain = &(variation->positionalGain[0]);
 
      initStandardMovelist(&movelist, &variation->singlePosition,
 
                           &variation->plyInfo[ply],
 
                           &variation->historyValue[0], hashmove, check);
 
   }
 
 
 
   initializePlyInfo(variation);
 
 
 
   while ((currentMove = getNextMove(&movelist)) != NO_MOVE)
 
   {
 
      int value;
 
 
 
      variation->nodes++;
 
 
 
      if (makeMoveFast(variation, currentMove) != 0 ||
 
          passiveKingIsSafe(&variation->singlePosition) == FALSE ||
 
          (restDepth == 1 && activeKingIsSafe(&variation->singlePosition)))
 
      {
 
         unmakeLastMove(variation);
 
 
 
         continue;
 
      }
 
 
 
      value =
 
         -searchMate(variation, -beta, -alpha, ply + 1, restDepth - 1, &pv);
 
 
 
      unmakeLastMove(variation);
 
 
 
      if (value >= best)
 
      {
 
         best = variation->pv[0].score = value;
 
         setMoveValue(¤tMove, value);
 
         appendMoveToPv(&pv, &variation->pv[0], currentMove);
 
 
 
         if (best > alpha)
 
         {
 
            variation->bestBaseMove = currentMove;
 
            solutions->moves[solutions->numberOfMoves++] = currentMove;
 
 
 
            if (variation->threadNumber == 0 &&
 
                variation->handleSearchEvent != 0)
 
            {
 
               getGuiSearchMutex();
 
               variation->handleSearchEvent(SEARCHEVENT_NEW_PV, variation);
 
               releaseGuiSearchMutex();
 
            }
 
         }
 
      }
 
   }
 
 
 
   if (best == VALUE_MATED)
 
   {
 
      if (check)
 
      {
 
         /* mate */
 
 
 
         best = VALUE_MATED + ply;
 
      }
 
      else
 
      {
 
         /* stalemate */
 
 
 
         best = variation->drawScore[position->activeColor];
 
      }
 
   }
 
 
 
   return best;
 
}
 
 
 
void searchForMate(Variation * variation, Movelist * foundSolutions,
 
                   int numMoves)
 
{
 
   int numSolutions = 0, i;
 
 
 
   variation->startTime = getTimestamp();
 
   variation->startTimeProcess = getProcessTimestamp();
 
   variation->timestamp = variation->startTime + 1;
 
   resetHashtable(getSharedHashtable());
 
   getLegalMoves(variation, foundSolutions);
 
   variation->numberOfBaseMoves = foundSolutions->numberOfMoves;
 
   variation->searchStatus = SEARCH_STATUS_RUNNING;
 
 
 
   foundSolutions->numberOfMoves = 0;
 
   resetHistoryValues(variation);
 
 
 
   for (variation->iteration = 1; variation->iteration <= numMoves;
 
        variation->iteration++)
 
   {
 
      const int restDepth = 2 * variation->iteration - 1;
 
      const int beta = -VALUE_MATED - restDepth;
 
 
 
      searchBaseMoves(variation, beta - 1, beta, restDepth, foundSolutions);
 
   }
 
 
 
   variation->finishTime = getTimestamp();
 
   variation->finishTimeProcess = getProcessTimestamp();
 
   variation->searchStatus = SEARCH_STATUS_TERMINATE;
 
 
 
   for (i = 0; i < foundSolutions->numberOfMoves; i++)
 
   {
 
      if (getMoveValue(foundSolutions->moves[i]) >=
 
          -VALUE_MATED - 2 * numMoves + 1)
 
      {
 
         numSolutions++;
 
      }
 
   }
 
 
 
   foundSolutions->numberOfMoves = numSolutions;
 
 
 
   getGuiSearchMutex();
 
 
 
   if (variation->threadNumber == 0 &&
 
       variation->handleSearchEvent != 0 &&
 
       variation->searchStatus == SEARCH_STATUS_TERMINATE)
 
   {
 
      variation->handleSearchEvent(SEARCHEVENT_SEARCH_FINISHED, variation);
 
   }
 
 
 
   releaseGuiSearchMutex();
 
}
 
 
 
int initializeModuleMatesearch()
 
{
 
   return 0;
 
}
 
 
 
int testModuleMatesearch()
 
{
 
   return 0;
 
}