bool FInitHashe(PCON pcon) { if( hashBuffer ) { free( hashBuffer ); hashBuffer = 0; } s_chashdMac = 15; int cbMaxHash = uciHash.get_spin(); if( cbMaxHash <= 0 || cbMaxHash > 1024 ) cbMaxHash = 32; cbMaxHash *= 1024*1024; chashdMax = 1; for (;;) { if (chashdMax * 2 * (int)sizeof(HASHD) > cbMaxHash) break; chashdMax *= 2; } VPrSendComment("%d Kbytes main hash memory (%d entries)", chashdMax * sizeof(HASHD) / 1024, chashdMax); Assert( sizeof(HASHD) == 16 ); hashBuffer = (RGHASHD) malloc( 64 + chashdMax * sizeof(HASHD)); if ( hashBuffer == NULL) { VPrSendComment("Can't allocate hash memory: %d bytes", chashdMax * sizeof(HASHD)); return false; } s_rghashd = (RGHASHD) ((U64(hashBuffer)+63) & (~63)); s_chashdMac = ((chashdMax / BUCKET_SIZE) - 1) * BUCKET_SIZE; stats.Hsize = chashdMax; VClearHashe(); return true; }
void VPrRating(void * pv, int eloUs, int eloThem) { PCON pcon = pv; VPrSendComment("My rating is %ld; opponent's rating is %ld.", eloUs, eloThem); }
void VPrBanner(void) { int i; for (i = 0; c_argszHelp[i] != NULL; i++) VPrSendComment(c_argszHelp[i]); }
void VAssertFailed(const char * szMod, int iLine) { VPrSendComment("Assert Failed: %s+%d\n", szMod, iLine); #ifdef _MSC_VER _asm { int 3 } #else asm ("int $3\n\t"); #endif // exit(1); int x = 0; }
EvalCache::EvalCache(int _numEntries) { VPrSendComment("%d Kbytes eval cache memory (%d entries)", _numEntries * sizeof(EvalCacheEntry) / 1024, _numEntries); numEntries = _numEntries; // TODO : alignment, sizeof(EvalCacheEntry) cache = new EvalCacheEntry[ numEntries ]; }
BOOL FGenBook(PCON pcon, char * szFile, int cbMaxBook, int bdm) { FILE * pfI; int cLine; BOOL f; CM argcmPrev[256]; BOOL argfAvoid[256]; int cplyPrev; BOOL fResult; int cbn = 0; // The book stuff is not high performance, so I'm going to use all the // book memory specified by the user, rather than trying to break it // down to the next lower power of two. I'll use at least one node. // s_cbnMax = cbMaxBook / sizeof(BN); if (s_cbnMax < 1) s_cbnMax = 1; // // Allocate the memory and open the file. This is not cleaned up if I // leave this routine early, because it's assumed that the program is // immediately ending. // if ((s_rgbn = malloc(s_cbnMax * sizeof(BN))) == NULL) { VPrSendComment("Can't allocate book memory: %d bytes", s_cbnMax * sizeof(BN)); return fFALSE; } memset(s_rgbn, 0, s_cbnMax * sizeof(BN)); if ((pfI = fopen(szFile, "r")) == NULL) { VPrSendComment("Can't open book: \"%s\"", szFile); return fFALSE; } // Eat the file. This loop is kind of complicated. // cplyPrev = 0; fResult = fTRUE; for (cLine = 0;;) { char aszBuf[512]; char aszOut[512]; char * szOut = aszOut; char * argsz[256]; int csz; PSTE pste = pcon->argste; int isz; int i; BOOL fFollowing; int cply; // Get a line from the file. // if (fgets(aszBuf, sizeof(aszBuf), pfI) != aszBuf) // Opening name. break; cLine++; VStrip(aszBuf); if ((aszBuf[0] == '/') || // Ignore some probable comment lines. (aszBuf[0] == '#') || (aszBuf[0] == '*') || (aszBuf[0] == ';') || (aszBuf[0] == '\0')) { if (bdm != bdmNONE) printf("%s\n", aszBuf); continue; } // Set up the board. // f = FInitCon(pcon, s_aszFenDefault); Assert(f); // // Break the line up. // csz = CszVectorizeBookLine(aszBuf, argsz); isz = cply = 0; // // Look at the first string and see if it's a move number. If it's // a move number other than one, it's assumed that the moves between // one and there are taken from the previous line. "cply" is set to // the proper ply value based upon the move number (0 is white's // first move, 1 is black's first, 2 is white's second, etc.). // // If the move number is "1.", it's assumed to be a white move. If // there are extra dots appended (as in "1.." or "1..."), it's // assumed that black is on the move. // if ((isz < csz) && (isdigit(argsz[isz][0]))) { int cmov; cmov = 0; for (i = 0; argsz[isz][i]; i++) if (isdigit(argsz[isz][i])) cmov = cmov * 10 + argsz[isz][i] - '0'; else break; cply = (cmov - 1) * 2; if ((argsz[isz][i++] == '.') && (argsz[isz][i] == '.')) cply++; isz++; } else cply = 0; // // If white is to move, check the next string for "...", which // indicates that it's black's move. // // The deal here is that I'm trapping the case "1. ..." and marking // it as black to move. // if ((isz < csz) && (!(cply & 1))) if (!strcmp(argsz[isz], "...")) { cply++; isz++; } // // If I set this up to start other than on move 1 for white, I have // to have enough previous context lying around from the previous // line. I gap is not allowed. // if (cplyPrev < cply) { VBookError(szFile, cLine, "Discontiguous opening line"); fResult = fFALSE; goto lblDone; } // Skip over any previous context. If I'm outputting in "full" mode, // write this stuff to stdout. If I'm outputting in "compact" mode, // I'm going to emit a couple of spaces per ply, so it indents in an // attractive way. // for (i = 0; i < cply; i++) { char aszMov[32]; BOOL f; if (bdm == bdmFULL) { char aszSan[32]; SAN san; VGenLegalMoves(pcon, pste); VCmToSan(pcon, pste, &argcmPrev[i], &san); VSanToSz(&san, aszSan); if (!(i & 1)) szOut += sprintf(szOut, "%d. ", i / 2 + 1); szOut += sprintf(szOut, "%s", aszSan); if (argfAvoid[i]) *szOut++ = '?'; *szOut++ = ' '; } else if (bdm != bdmNONE) szOut += sprintf(szOut, " "); VCmToSz(&argcmPrev[i], aszMov); f = FAdvance(pcon, aszMov); Assert(f); } // I'm going to take care of the new stuff now. // fFollowing = fTRUE; for (; isz < csz; isz++) { char aszSan[32]; char aszMov[32]; int j; PCM pcm; CM cm; SAN san; if (atoi(argsz[isz])) // Skip move numbers. continue; if (argsz[isz][0] == '(') // Variation name starts with "(" and break; // is last arg. The vectorize // routine used in here handles "(" // specially, so this works. // // If the move some even number of plies back was a "?" move, // this is a "?" move, too, otherwise it isn't. // if (cply < 2) // Too close to the start to have a move before. argfAvoid[cply] = fFALSE; else argfAvoid[cply] = argfAvoid[cply - 2]; // // Look for an explicit "?", which marks this as a move to avoid. // for (j = 0; argsz[isz][j]; j++) if (argsz[isz][j] == '?') { argsz[isz][j] = '\0'; argfAvoid[cply] = fTRUE; break; } // // Find the move in the list of legal moves, essentially // converting from SAN to internal format. // VGenLegalMoves(pcon, pste); pcm = pste->pcmFirst; for (; pcm < (pste + 1)->pcmFirst; pcm++) { VCmToSan(pcon, pste, pcm, &san); VSanToSz(&san, aszSan); if (!strcmp(aszSan, argsz[isz])) break; } if (pcm >= (pste + 1)->pcmFirst) { sprintf(aszBuf, "Illegal move for %s, token %d on line", (pste->coUs == coWHITE) ? "white" : "black", isz + 1); VBookError(szFile, cLine, aszBuf); fResult = fFALSE; goto lblDone; } cm = *pcm; // // Output the move if necssary. This is some tricky and nasty // code, depending upon what mode I'm in, what I've already // outputed on this line, and whether the book I'm reading in // is "full" or "compact". // if (bdm != bdmNONE) { if (fFollowing) if ((cplyPrev <= cply) || (memcmp(&cm, &argcmPrev[cply], sizeof(CM)))) { if ((cply & 1) && (bdm == bdmCOMPACT)) szOut += sprintf(szOut, "%d. ... ", cply / 2 + 1); fFollowing = fFALSE; } else if (bdm == bdmCOMPACT) szOut += sprintf(szOut, " "); if ((!fFollowing) || (bdm == bdmFULL)) { if (!(cply & 1)) szOut += sprintf(szOut, "%d. ", cply / 2 + 1); szOut += sprintf(szOut, "%s", aszSan); if (argfAvoid[cply]) *szOut++ = '?'; *szOut++ = ' '; } } // Make the move on the internal board. // VCmToSz(&cm, aszMov); f = FAdvance(pcon, aszMov); Assert(f); // // Hash it unless it is a "?" move. This is how "avoid" moves // are handled -- they are simply not hashed in the book file. // // What this means of course is that every instance of the // position after this move is made must be marked with a "?", // or it's like none of them are. // // With a "compact" book, this isn't a problem, unless there are // transpositions. // if (!argfAvoid[cply]) { HASHK hashkFull = HashkFull(pcon, pste); // Note that the previous line generates moves, which is // annoying. // // I'm hashing the "full" keys, so the en-passant and // castling don't cause problems. // if (FHashBn(hashkFull)) if (++cbn >= s_cbnMax * 3 / 4) { VBookError(szFile, cLine, "Book memory full"); fResult = fFALSE; goto lblDone; } } // Remember the move in the "last line" array, which will be used // to diff the next line against this one. // argcmPrev[cply++] = cm; } // Record the length of the current line. // cplyPrev = cply; // // Deal with the variation name field at the end of the line, which // means just printing it out if I'm in dump mode. // if (bdm != bdmNONE) { if (isz < csz) { // <-- "isz < csz" if I found a "(". // // I'm going to print the line in 128 characters, followed by // the variation name. This is pretty wide, but lots of // lines are longer than 80 characters. // while (szOut - aszOut < 128) *szOut++ = ' '; szOut += sprintf(szOut, "%s", argsz[isz]); } *szOut = '\0'; VStrip(szOut); printf("%s\n", aszOut); } } if (bdm == bdmNONE) VPrSendComment("%d book positions", cbn); else fResult = fFALSE; // If I dumped this book, I'm going to exit the lblDone: // program after this returns. fclose(pfI); return fResult; }
void VBookError(char * szFile, int cLine, char * sz) { VPrSendComment("Book error (%s, line %d) : %s.", szFile, cLine, sz); }
void VPrIcs(void * pv, char * szIcs) { PCON pcon = pv; VPrSendComment("I am playing on: %s", szIcs); }
void VPrOpponentName(void * pv, char * szName) { PCON pcon = pv; VPrSendComment("Opponent is: %s", szName); }
void VPrOpponentComputer(void * pv) { PCON pcon = pv; VPrSendComment("Opponent is a computer."); }
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; }