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; }
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; }