示例#1
0
BOOL FPrPonderHit(void * pv, char * szMove, unsigned long tmUs,
	unsigned long tmThem)
{
	PCON pcon = pv;

	if (pcon->smode != smodePONDER)
		return fFALSE;
	if (strcmp(pcon->aszPonder, szMove))
		return fFALSE;
	pcon->smode = smodeTHINK;
	pcon->ss.tmUs = tmUs;
	pcon->ss.tmThem = tmThem;
	VSetTime(pcon);
	return fTRUE;
}
示例#2
0
void VThink(PCON pcon, TM tmUs, TM tmThem)
{
	Assert(pcon->smode == smodeIDLE);
    //VPrSendComment("entering VThink\n");
	if (pcon->fLowPriority)
		VLowPriority();
    if( uciHash.has_changed() )
        FInitHashe(pcon);
    if( uciBookName.has_changed() )
        book_open( uciBookName.get_string() );
    if( uciUseEGBB.has_changed() || uciEGBBDir.has_changed() )
        prepareEGBB();
	//
	//	Set up time-out, and remind myself that I'm not dead.
	//
	pcon->ss.tmUs = tmUs;
	pcon->ss.tmThem = tmThem;
	pcon->fAbort = false;
	pcon->smode = (tmUs == tmANALYZE) ? smodeANALYZE : smodeTHINK;
	//
	//	This loop is going to execute at least once.  It might execute for the
	//	whole game, if the program continuously ponders correctly.
	//

	char	aszMove[32];
	char	aszPonder[32];
    aszMove[0] = aszPonder[0] = '\0';
	for (;;) {
		char	aszSanHint[32];
		SAN	sanHint;
		CM	cm;
		bool	f;

        //VPrSendComment("for (;;) mode=%d\n", pcon->smode);
        stats.raz();
        razKillers();
        History.raz();
        CounterMoves.raz();
        initMVVLVA( pcon );

		pcon->fTimeout = false;	    	// I'm not timed out yet.
		pcon->ss.tmStart = TmNow();		// Record when I started.
		if (pcon->smode ==				// If I'm pondering, I don't set the
			smodeTHINK)					//  time control stuff.  This will be
			VSetTime(pcon);				//  handled at the point it's clear
										//  that I picked the correct move.

        bool keepPV = false;
        if (pcon->smode ==smodeTHINK) {
//            printf("# mode is THINK\n");
            if( pcon->gc.argcm > 0 ) {
                CM lastMove = pcon->gc.argcm[pcon->gc.ccm - 1];
                char	asz[16];
                CmToSz(&lastMove, asz);
                if( lastMove == lastPV[2] ) {
                    for(int i = 0; i < csteMAX; i++)
                        lastPV[i] = lastPV[i+2];
                    keepPV = true;
                }
            }
        }
        PSTE	pste = &pcon->argste[0];
        pste->ccmPv = 0;
        if( !keepPV ) {
            // Clear the PV
            VSetPv(pcon);
        }

//        VSetRepHash(pcon);				// Pump game history into rep hash.
        if (pcon->smode == smodeTHINK)
            VDrawCheck(pcon);			// Try to claim appropriate 50-move
										//  and 3x rep draws before examining
										//  moves.
        //
        //	During the first 20 moves of the game I will try to find a book
        //	move.  If I find one I will play it.
        //
        if ((pcon->smode == smodeTHINK) && (uciOwnBook.get_check()) &&
            (pcon->gc.ccm < 40) && (book_find_move(pcon, &cm, true))) {
            char	asz[16];

            CmToSz(&cm, asz);	// Make book move.
            bool f = FAdvance(pcon, asz);
            Assert(f);
            VPrSendMove(asz, "");// I am not going to bother checking for draws
					            //  and mates that occur while in book.  If by
					            //  weird chance one of these happens, if
					            //  we're hooked up to the chess server, the
					            //  server might call it.
            break;
        }
        //	Increment sequence number.  The transposition hash system will
        //	overwrite nodes from old searches, and this is how it knows that
        //	this is a new search, rather than just more of the old one.
        //
        VIncrementHashSeq();
        //
        //	Search the position via iterative deepening.
        //
        pcon->ss.nodes = 0;			// Global node counter.
        pcon->ss.nodesNext = 2000;	// Time (in nodes) of next callback to
							        //  interface.
        if( pcon->tc.nodes )
            pcon->ss.nodesNext = pcon->tc.nodes;

        pcon->ss.tbhits = 0;

        pste->evaluated  = false;
        int staticVal = ValEval(pcon, pste, -20000, 20000);

        pste->plyRem = 0;
        pcon->ss.plyDepth = 0 + 1;

        pcon->ss.lastVal = staticVal;
        int oldVal = pcon->ss.lastVal;
//        pcon->ss.tmExtend = false;
        pcon->ss.easyMove = false;
//        pcon->ss.dontStop = false;
        pcon->ss.dontStopPrev = false;
        pcon->ss.canStop = false;

        pcon->ss.currBestMove = CM(0,0);
        int prevDepthVal = valMIN;
        int val = 0;
        int Alpha = valMIN;			// Initial window is wide open.
        int Beta = valMAX;
        for (int i = 0; i < plyMAX; i++)  {
            // TODO : re enable aspiration window !
            pcon->ss.plyMaxDepth = 0;
            pste->plyRem = i;
            pcon->ss.plyDepth = i + 1;
            pcon->ss.failed = false;
            pcon->ss.scoreDropped = false;
            pcon->ss.rootHasChanged = false;

            VPrSendUciDepth( pcon->ss );

            val = ValSearchRoot(pcon, pste, Alpha, Beta);

            if( pcon->ss.lastVal > oldVal )
                oldVal = pcon->ss.lastVal;
            else
                oldVal = (pcon->ss.lastVal + oldVal)/2;
            if( val > pcon->ss.lastVal || (pcon->ss.plyDepth <= 3))
                pcon->ss.lastVal = val;
            else
                pcon->ss.lastVal = (pcon->ss.lastVal + val)/2;
//            pcon->ss.lastVal = val;

/*            if( pcon->ss.dontStopPrev ) {
                printf("# dontStopPrev is set, delta = %d \n", val - oldVal);
                if( val - oldVal >= -35 ) {
                    printf("# reseting dontStopPrev to false\n");
                    pcon->ss.dontStopPrev = false;
                }
            }*/

//            pcon->ss.dontStop = pcon->ss.scoreDropped;
//            pcon->ss.canStop = (pcon->ss.plyDepth >=5) && 
//                !pcon->ss.scoreDropped && !pcon->ss.dontStopPrev; // && !pcon->ss.rootHasChanged;
            pcon->ss.canStop = (pcon->ss.plyDepth >=1) && 
                !pcon->ss.scoreDropped;// && !pcon->ss.dontStopPrev; // && !pcon->ss.rootHasChanged;
            printf("# scoreDropped=%d rootChanged=%d stopPrev=%d => canStop=%d\n", 
                pcon->ss.scoreDropped, pcon->ss.rootHasChanged, pcon->ss.dontStopPrev, pcon->ss.canStop);
//            pcon->ss.dontStopPrev = pcon->ss.dontStop;
            pcon->ss.dontStopPrev = pcon->ss.scoreDropped;

            // Initialize the PV with the result of the previous search
            VSetPv(pcon);
            if (pcon->fAbort || pcon->fTimeout)
                break;
            //
            //	This checks for a result outside the window, and if it finds
            //	one it re-searches with the window wide open.
            //
			if ((val <= Alpha) || (val >= Beta)) {
				if (val <= Alpha)	// Record a fail-low (fail-high is
										//  handled inside "ValSearch").
					//
					//	This function needs the root moves generated, and they
					//	are, because "ValSearch" does it.
					//
					VDumpPv(pcon, pcon->ss.plyDepth, val, prsaFAIL_LOW);
				val = ValSearchRoot(pcon, pste, valMIN, valMAX);
                // Initialize the PV with the result of the previous search
			    VSetPv(pcon);
				if (pcon->fAbort)
					break;
				if (pcon->fTimeout)
					break;
			}

//            VIncrementHashSeq();

            pcon->ss.prsa = prsaNORMAL;
            pcon->ss.val = val;
            //
            //	Mated now or this move mates, drop out of here.
            //
            if ((pcon->ss.val == valMATE - 1) || (pcon->ss.val == -valMATE))
                break;
            // found a mate in n, no need to iterate
            if( (val == prevDepthVal) && (val > /*50*100*/(valMATE - 500) ) && (pcon->smode != smodeANALYZE) )
                break;
            prevDepthVal = val;
            // stop thinking if there is only one move, no need to let the other guy
            // think longer
            if( pcon->ss.ccmLegalMoves == 1 ) {
                // we can still look a bit so we have a PV set for the next move
                if( i >= 5 && (pcon->smode == smodeTHINK))
                    break;
            }
            if( (pcon->smode == smodeTHINK) ) {
                int usedTime = TmNow() - pcon->ss.tmStart;
                int remaingingTime = pcon->ss.tmEnd  - TmNow();
                if( pcon->ss.canStop && !pcon->ss.rootHasChanged && (usedTime > remaingingTime) ) {
                    VPrSendComment("time : used=%d remaining =%d\n", usedTime, remaingingTime);
                    break;
                }
            }
#if 0
            if( (pcon->smode == smodeTHINK) ) {
                int usedTime = TmNow() - pcon->ss.tmStart;
                int remaingingTime = pcon->ss.tmEnd  + pcon->ss.tmExtra / 9  - TmNow();
                const int bf = int(1.9f * 32); // kind of branching factor
                if( (!pcon->ss.failed) && (usedTime * bf) / 32 > remaingingTime ) {
                    VPrSendComment("time : used=%d remaining =%d\n", usedTime, remaingingTime);
                    break;
                }
            }
#endif
            //
            //	Depth-restricted search.  Check for "timeout", meaning target
            //	depth met.
            //
            if (((pcon->smode == smodeTHINK) || (pcon->smode == smodePONDER)) 
                && (pcon->tc.plyDepth > 0) &&
                (pcon->ss.plyDepth >= pcon->tc.plyDepth))
                break;
            //
            //	Set up for next iteration, which will use a window centered
            //	around the current position value.
            //
//			Alpha = val - 50;
//			Beta = val + 50;
        }

        //	If abort or doing pondering, I'm done, because I don't need to
        //	make any moves or set up the next search.
        //
        //	NOTE: None of the stuff below happens in analysis mode, because
        //	the next line exits the routine!  This means that results won't
        //	be posted (draws, mates, etc.) if the game is in analysis mode,
        //	which seems to be the right thing to do according to Tim Mann,
        //	circa 26-Apr-2001 on the Yahoo chess engines mailing list.
        //
#ifdef _DEBUG
        stats.report();
#endif
        //
        //	If there is a move in the first element of the PV, I can make it.
        //	I don't make it now, because I want to look at the second element
        //	of the PV for a pondering move, and if I execute the first move,
        //	it will destroy the PV.
        //
        aszMove[0] = aszPonder[0] = '\0';
        if (pste->ccmPv >= 1)
            CmToSz(&pste->argcmPv[0], aszMove);
        CM	cmHint;
        if ((pste->ccmPv >= 2)) {
            CmToSz(&pste->argcmPv[1], aszPonder);
            cmHint = pste->argcmPv[1];
        }
        // this line was moved down for uci support
        // TODO : check this
        if ((pcon->fAbort) || (pcon->smode != smodeTHINK))
            break;

        //	As of this point, I have move to make in "aszMove", move to ponder
        //	in "aszPonder".  If I can't move, or I can't ponder, these strings
        //	are nulled out.
        //
        //	Check to see if I'm mated or stalemated, which will be the case
        //	if I can't move.
        //
        if (aszMove[0] == '\0') {
			if ((!pcon->fTimeout) && (pcon->ss.val == -valMATE)) {
				VMates(pste->side ^ 1);
				break;
			}
			VStalemate(pste->side);
			break;
		}
        //VPrSendComment("advancing my move=%s\n", aszMove);
        f = FAdvance(pcon, aszMove);
        Assert(f);
        //
        //	Deal with draw offers right before the move is sent to the
        //	interface.  This boolean is set when the opponent offers a draw.
        //	It is cleared when the interface tells us to set up a new
        //	position, and when the engine makes a move.
        //
        //	If this boolean is not cleared at the appropriate time, it's not
        //	critically bad, because all that will happen is that the program
        //	will offer an unsolicited draw in a position it thinks is drawn.
        //
        //	The engine will execute its planned move after trying to agree to
        //	the draw, in case something goes wrong with the draw offer.
        //
        //	If the interface can't handle this, there could be problems.
        //
        //	This code is before the resignation code in order to catch the
        //	one-in-a-million case where the opponent offers a draw before the
        //	engine can resign.
        //
        if (pcon->fDrawOffered) {
            if (FAcceptDraw(pcon, pste))
                VPrOfferDraw();
            pcon->fDrawOffered = false;
        }
        //	Check to see if I should resign, and do so if necessary.  The move
        //	the engine is planning to make is not executed in this case, in
        //	order to avoid the unattractive <move made> <resigns> sequence.
        //
        //	If the interface doesn't listen to the resignation, the program
        //	simply won't move.
        //
        else if (FCheckResign(pcon, pste))
            break;
        //	Inform the interface that we have a move.
        //
        if( ! pcon->isUCI ) 
            VPrSendMove(aszMove, aszPonder);
        //
        //	The score from the last search might indicate that this is mate.
        //
        if ((!pcon->fTimeout) && (val == valMATE - 1)) {
            VMates(pste->side ^ 1);	// This is backwards because we already
            break;					//  moved on the internal board.
        }
        //	Check for 50-move or 3x repetition draws.
        //
        VDrawCheck(pcon);
        //
        //	Check to see if I stalemated my opponent, and report the result as
        //	appropriate.
        //
        if (FStalemated(pcon))
            VStalemate(pste->side ^ 1);
        //
        //	If the PV contained at least two moves, and the "fPonder" boolean
        //	is set, the second one is sitting in "aszPonder".
        //
        //	If there is no move, we're done, and we're going to drop out of
        //	the bottom of this routine and leave.
        //
        //	If there was a move, I'm going to set our state to "smodePONDER",
        //	remember the move that we are pondering on, execute the move on
        //	the board, and loop around to the top of this think loop and start
        //	thinking again.
        //
        //	Later, if the the interface passes in the move that we are
        //	pondering on, the state info will be dummied up so the program
        //	thinks it's doing a normal search.  If the wrong move is passed in
        //	(the opponent made a move we didn't expect), we'll abort the
        //	search, undo the last move, exit this routine, and be called again
        //	and told to think.
        //
        //	So we get into this routine when we're told to think about
        //	something, and we stay in here until we fail to predict the
        //	opponent's move.
        //

        if (aszPonder[0] == '\0' || !pcon->fPonder )
            break;

        //
        //	Get the algebraic so I can send a hint move.  The Winboard
        //	interface will stick this at the beginning of the PV it displays
        //	while we are pondering.
        //
        bGenMoves(pcon,	pcon->argste);
        CmToSan(pcon, pcon->argste, &cmHint, &sanHint);
        SanToSz(&sanHint, aszSanHint);
        strcpy(pcon->aszPonder, aszPonder);
//        VPrSendComment("potential ponder move(2)=%s\n", aszSanHint);
        //
        //	Execute the move.  I'm now one move ahead of the game, which is
        //	tricky.  After that, send the hint move.
        //
//        VPrSendComment("ok let's advance for the ponder move %s\n", aszPonder);
        f = FAdvance(pcon, aszPonder);
        Assert(f);
        if (pcon->fPost)
            VPrSendHint(aszSanHint);
        VPrSendComment("switching to ponder mode, previous mode was=%d\n", pcon->smode);
        pcon->smode = smodePONDER;
    }
    //	Inform the interface that we have a move.
    //
    if( pcon->isUCI ) {
        VPrSendUciNodeCount(true, pcon->ss );
        VPrSendMove(aszMove, aszPonder);
    }
    //	If I broke out, and I'm in ponder mode, back up the search and pretend
    //	that I never pondered.  I could do some sneaky stuff having to do with
    //	remembering the move that would have been made if the pondered search
    //	had been converted into a normal search soon enough, but this would be
    //	annoying to do and it's kind of rare and pointless.
    //
    stats.report2();
    //VPrSendComment("end of VThink mode is %d\n", pcon->smode );
    if (pcon->smode == smodePONDER)
        VUndoMove(pcon);
    pcon->smode = smodeIDLE;
}