/* GNU Chess 5.0 - iterate.c - iterative deepening code
   Copyright (c) 1999-2002 Free Software Foundation, Inc.

   GNU Chess is based on the two research programs 
   Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.

   GNU Chess 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.

   GNU Chess 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 GNU Chess; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Contact Info: 
     bug-gnu-chess@gnu.org
     cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "util.h"
#include "ttable.h"
#include "book.h"
#include "move.h"
#include "timer.h"
#include "ptable.h"
#include "input.h"
#include "searchparams.h"
#include "searchdata.h"
#include "searchstats.h"
#include "search.h"
#include "log.h"
#include "board.h"
#include "timer.h"
#include "genmove.h"
#include "sort.h"
#include "eval.h"
#include "search.h"
#include "input.h"

void IterateSetTime(board_t *board,
		    searchparams_t *searchparams, 
		    searchdata_t *searchdata,
		    searchstats_t *searchstats);


static int IterateCheckStop(searchparams_t *searchparams, 
			    searchdata_t *searchdata, 
			    searchstats_t *searchstats);

void Iterate (board_t *board, 
	      searchparams_t *searchparams, 
	      searchstats_t *searchstats){
    movelist_t movelist[1];
    searchdata_t searchdata[1];
    board_t board_org[1];


    TTableIncAge(TTable);
 
    Log("Entered Iterate().\n");
    Log("Current age is %u\n",TTable->Age);
    

    SearchInit(Option);

    /* We copy the original board in case there is a timeout deep in the
       tree.
    */
    BoardCopy(board_org,board);


    SearchDataInit(searchdata,board);

    searchdata->RootBoard=board_org;

    SearchStatsInit(searchstats);
    TimerStart(searchdata->timer);
    
    searchdata->lazydiff[white]=searchdata->lazydiff[black]=0;
    searchdata->posdiff[white]=searchdata->posdiff[black]=0;
    TTableResetStats(TTable);
    PTableResetStats(PTable);


    if(!searchparams->infinite){
	IterateSetTime(board,searchparams,searchdata,searchstats);
    }
    
    searchdata->InChk[1] = board->in_check;
    
    GenMoves (board,movelist);
    SortRoot (board,movelist);

    ASSERT(movelist->gencnt>0);
    
    if(movelist->gencnt==0){
	searchstats->RootPV=NOMOVE;
	return;
    }

    
    /* This is an essentially random move */
    searchstats->RootPV=movelist->moves->move;

    
    searchstats->RootPVScore = Evaluate (board,
    				 searchparams,
    				 searchdata,
    				 searchstats,
    				 -INFIN, 
    				 INFIN);

    searchdata->RootScore=searchstats->RootPVScore;
    
    switch(setjmp(searchdata->rootpointer)){
    case 0:
	
	while (1) {
	    /* Go to the next iteration depth. */
	    searchdata->RootDepth += 1; 

	    ASSERT(searchdata->RootScore==searchstats->RootPVScore);
    

	    searchdata->RootState=SCORE_INIT;
	    while(searchdata->RootState!=SCORE_EXACT){
		switch(searchdata->RootState){
		case SCORE_INIT:
		    if(search.UseAspiration){
			if (searchdata->RootScore > MATE-255) {
			    searchdata->RootAlpha = searchdata->RootScore-1;
			    searchdata->RootBeta = MATE;
			}else if (searchdata->RootScore < -MATE+255){
			    searchdata->RootAlpha = -MATE;
			    searchdata->RootBeta  = searchdata->RootScore+1;
			}else{
			    searchdata->RootAlpha = MAX (searchdata->RootScore - search.AspirationWindow, -MATE);
			    searchdata->RootBeta  = MIN (searchdata->RootScore + search.AspirationWindow,  MATE);
			}
		    }else{
			searchdata->RootAlpha=-INFIN;
			searchdata->RootBeta=INFIN;
		    }
		    break;
		case SCORE_FAIL_HIGH:
		    /* searchdata->RootAlpha = searchdata->RootAlpha; */
		    searchdata->RootBeta=searchdata->RootScore+search.AspirationWindow;
		    break;
		case SCORE_FAIL_LOW:
		    /* searchdata->RootBeta = searchdata->RootBeta; */
		    searchdata->RootAlpha=searchdata->RootScore-search.AspirationWindow;
		    break;
		}
	    
	    
		/* search new depth with specified window */
		
		SearchRoot (board, 
			    searchparams, 
			    searchdata, 
			    searchstats,
			    movelist);

		ASSERT(searchdata->RootScore!=-INFIN-1);

		if (searchdata->RootScore >= searchdata->RootBeta){ /* fail high */
		    searchdata->RootState=SCORE_FAIL_HIGH;
		}else if(searchdata->RootScore <= searchdata->RootAlpha) { /* fail low */
		    searchdata->RootState=SCORE_FAIL_LOW;
		}else{
		    searchdata->RootState=SCORE_EXACT;
		}
		
	    }
	    /*	    TTableFeedPV(TTable,
			 board,
			 searchdata->RootDepth,
			 searchstats->RootPVScore,
			 searchdata->RootPVSeq); 
	    */
	    if(IterateCheckStop(searchparams,searchdata,searchstats)){
		break;
	    }
	    
	}
	break;
	
    case 1:
	Log("Timeout. RootMove=%s\n",AlgbrMove(searchstats->RootPV));
	BoardCopy(board,board_org);
	break;
	
	
    }
    /* Final PV */
    ShowLine (board,
	      searchparams,
	      searchdata, 
	      searchstats,
	      SCORE_PV);


    // temporary hack

    if(searchparams->infinite){
	while(1){
	    char *input_cmd;
	    if((input_cmd=InputLook())){
		split_input(input_cmd);
		if(tokeneq(token[0],".")){
		    InputWakeup();
		    continue;
		}else{
		    break;
		}
	    }
	    my_sleep(200);
	}
    }
    
    searchstats->ElapsedTime = TimerElapsed(searchdata->timer); 
} 




void IterateSetTime(board_t *board,
		    searchparams_t *searchparams, 
		    searchdata_t *searchdata,
		    searchstats_t *searchstats){
    int movelimit=0;
    float timelimit;
    searchstats->ElapsedTime = TimerElapsed(searchdata->timer);
    timelimit=searchparams->timelimit;
    if (!searchparams->ST) {
	movelimit=searchparams->movestogo;
	if(movelimit==0){ 
	    movelimit= 35;
	}
	Log("GameCnt=%d Moves left=%d Time left=%f\n",
	    board->GameCnt, 
	    movelimit,
	    timelimit);
	ASSERT(movelimit>0);
	
	if(searchparams->TCinc==0){ // sudden death or moves/period
	    searchparams->SearchTime = timelimit /  (movelimit+7);
	}else { // fisher increment
	    searchparams->SearchTime = searchparams->TCinc+(timelimit-7*searchparams->TCinc)/movelimit;
	}
	
	searchparams->maxtime = search.PanickTimeFactor * searchparams->SearchTime;
	
	/* Never allocate more than the remaining time */
	
	if((searchparams->maxtime>0.7*timelimit)){
	    searchparams->maxtime=0.7*timelimit;
	    Log("Reducing max. searchtime to %f.\n",searchparams->maxtime);
	    if(searchparams->SearchTime>searchparams->maxtime){
		searchparams->SearchTime=searchparams->maxtime;
	    }
	}
    }else{
	searchparams->SearchTime = searchparams->ST>5?searchparams->ST -0.5: 
	    0.9 *searchparams->ST ;
	searchparams->maxtime=searchparams->SearchTime;
	
    }
    Log("searchtime=%f maxtime=%f ElapsedTime=%f\n",
	searchparams->SearchTime,
	searchparams->maxtime,
	searchstats->ElapsedTime);
}


int IterateCheckStop(searchparams_t *searchparams, 
		     searchdata_t *searchdata, 
		     searchstats_t *searchstats){
       
    int ret;

    ret=false;

    /* Cut off before the ply counter overflows (uint8_t) */

    if(searchdata->RootDepth>=3*MAXPLYDEPTH/5){
	OutputConsole("Ending search at depth %d.\n",searchdata->RootDepth);
	ret=true;
    }
  
    searchstats->ElapsedTime=TimerElapsed(searchdata->timer);
  
  
    /* See if we have time to start another iteration */
    /* mcriley - was 2 * S / 3 */

    Log("ElapsedTime=%f searchtime=%f\n",
	searchstats->ElapsedTime,
	searchparams->SearchTime);
    if (searchparams->SearchDepth == 0 && 
	searchparams->ST == 0 &&
	!searchparams->infinite &&
	searchstats->ElapsedTime >= 2 * searchparams->SearchTime / 3){
	Log("Stopping search in Iterate() as 2/3 of the search time is used.\n");
	ret=true;
    }
    
    if (search.UseMateDistancePruning && (abs(searchdata->RootScore) + searchdata->RootDepth >= MATE + 1)) {
    	ret=true;
    }
    
    if (searchparams->infinite && searchdata->RootDepth == searchparams->SearchDepth) {
	ret=true;
    }
    return ret;
}
