/*
 
    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 <assert.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include "test.h"
 
#include "io.h"
 
#include "pgn.h"
 
#include "coordination.h"
 
#include "evaluation.h"
 
 
 
extern bool resetSharedHashtable;
 
 
 
static void handleSearchEvent(int eventId, void *var)
 
{
 
   Variation *variation = (Variation *) var;
 
   char *pvMoves;
 
 
 
   switch (eventId)
 
   {
 
   case SEARCHEVENT_SEARCH_FINISHED:
 
      time = variation
->finishTimeProcess 
- variation
->startTimeProcess
;  
 
 
      logReport
("%lld nodes in %ld msec\n", getNodeCount
(), time); 
 
 
      {
 
         logReport
("%ld knps\n", getNodeCount
() / max
(1, time)); 
      }
 
 
 
      break;
 
 
 
   case SEARCHEVENT_PLY_FINISHED:
 
   case SEARCHEVENT_NEW_PV:
 
      pvMoves = getPrincipalVariation(var);
 
      dumpPv(variation->iteration,
 
             (getTimestamp() - variation->startTime), pvMoves,
 
             variation->pv[0].score, variation->nodes,
 
             variation->startPosition.activeColor);
 
      break;
 
 
 
   default:
 
      break;
 
   }
 
}
 
 
 
static bool solveMateProblem(SearchTask * entry)
 
{
 
   bool result = TRUE;
 
   int i;
 
 
 
   completeTask(entry);
 
 
 
   if (entry->solutions.numberOfMoves !=
 
       entry->calculatedSolutions.numberOfMoves)
 
   {
 
      result = FALSE;
 
   }
 
 
 
   for (i = 0; i < entry->solutions.numberOfMoves; i++)
 
   {
 
      if (listContainsMove
 
          (&entry->calculatedSolutions, entry->solutions.moves[i]) == FALSE)
 
      {
 
         result = FALSE;
 
      }
 
   }
 
 
 
   return result;
 
}
 
 
 
static bool solveBestMoveProblem(SearchTask * entry)
 
{
 
   completeTask(entry);
 
 
 
   return listContainsMove(&entry->solutions, entry->bestMove);
 
}
 
 
 
static bool dumpEvaluation(SearchTask * entry)
 
{
 
   EvaluationBase base;
 
 
 
   prepareSearch(entry->variation);
 
   getValue(&entry->variation->startPosition,
 
            &base,
 
            entry->variation->pawnHashtable,
 
            entry->variation->kingsafetyHashtable);
 
 
 
   return TRUE;
 
}
 
 
 
int processTestsuite(const char *filename)
 
{
 
   PGNFile pgnfile;
 
   PGNGame *game;
 
   SearchTask entry;
 
   Gamemove *gamemove;
 
   long i;
 
   UINT64 overallNodes = 0;
 
   const char *fmt = "\nTestsuite '%s': %d/%d solved, %s nodes\n";
 
   char ons[32];
 
   int solved = 0;
 
   String notSolved = getEmptyString();
 
   Variation variation;
 
 
 
   if (openPGNFile(&pgnfile, filename) != 0)
 
   {
 
      return -1;
 
   }
 
 
 
   logReport("\nProcessing file '%s' [%ld game(s)]\n", filename,
 
             pgnfile.numGames);
 
 
 
   statCount1 = statCount2 = 0;
 
   variation.timeTarget = 60 * 1000;
 
   variation.timeLimit = 60 * 1000;
 
   variation.ponderMode = FALSE;
 
   entry.variation = &variation;
 
 
 
   for (i = 1; i <= pgnfile.numGames; i++)
 
   {
 
      game = getGame(&pgnfile, i);
 
 
 
      if (game == 0)
 
      {
 
         continue;
 
      }
 
 
 
      logReport("\n%ld (%ld): %s-%s\n", i, pgnfile.numGames,
 
                game->white, game->black);
 
      logPosition(&game->firstMove->position);
 
 
 
      entry.solutions.numberOfMoves = 0;
 
      gamemove = game->firstMove;
 
 
 
      while (gamemove != 0)
 
      {
 
         entry.solutions.moves
 
            [entry.solutions.numberOfMoves++] =
 
            getPackedMove(gamemove->from, gamemove->to, gamemove->newPiece);
 
 
 
         gamemove = gamemove->alternativeMove;
 
      }
 
 
 
      initializeVariation(&variation, game->fen);
 
      resetSharedHashtable = TRUE;
 
      variation.handleSearchEvent = &handleSearchEvent;
 
 
 
      if (strstr(game
->white
, "[#") != NULL
)  
      {
 
         char *tmp 
= strstr(game
->white
, "[#");  
 
 
         entry.type = TASKTYPE_TEST_MATE_IN_N;
 
         entry.
numberOfMoves = atoi(tmp 
+ 2); 
 
 
         if (solveMateProblem(&entry) != FALSE)
 
         {
 
            solved++;
 
         }
 
         else
 
         {
 
         }
 
 
 
         overallNodes += entry.nodes;
 
      }
 
      else
 
      {
 
         entry.type = TASKTYPE_TEST_BEST_MOVE;
 
         entry.numberOfMoves = 0;
 
 
 
         if (commandlineOptions.dumpEvaluation)
 
         {
 
            dumpEvaluation(&entry);
 
         }
 
         else
 
         {
 
            if (solveBestMoveProblem(&entry) != FALSE)
 
            {
 
               solved++;
 
            }
 
            else
 
            {
 
               appendToString(¬Solved, "%d ", i);
 
            }
 
 
 
            overallNodes += entry.nodes;
 
         }
 
      }
 
 
 
      freePgnGame(game);
 
   }
 
 
 
   formatLongInteger(overallNodes, ons);
 
   logReport(fmt, filename, solved, pgnfile.numGames, ons);
 
   logReport("Not solved: %s\n", notSolved.buffer);
 
   closePGNFile(&pgnfile);
 
 
 
   return 0;
 
}
 
 
 
/**
 
 * Initialize this module.
 
 *
 
 * @return 0 if no errors occurred.
 
 */
 
int initializeModuleTest(void)
 
{
 
   return 0;
 
}
 
 
 
int testModuleTest()
 
{
 
   int result;
 
 
 
   if ((result = processTestsuite("moduletest.pgn")) != 0)
 
   {
 
      return result;
 
   }
 
 
 
   return 0;
 
}