From 8d74aed8f6fb583c14bf2f6622811221672b8d23 Mon Sep 17 00:00:00 2001 From: H.G.Muller Date: Thu, 1 Dec 2016 21:17:11 +0100 Subject: [PATCH] Buffer 'setoption' commands until engine done thinking UCI does not allow setting of options during search after all. So all 'setoption' commands are now first collected in a memory buffer. During ponder or analysis the search is then stopped immediately to flush that buffer to the engine, in a new routine Release(). This routine will also (re)start a ponder search if one is called for. During thinking, finishing the search has priority. So only Release() the buffer contents when a search finishes (immediately after receiving 'bestmove'). To deal with race conditions we also flush the buffer in the GUI thread, after waiting for thinking to finish, just in case. The WB 'easy' and 'hard' command implicitly set the Ponder option, and this is treated by the hack of injecting fake 'option' commands in the input stream, which then are subjected to the same buffering scheme. Because waiting for thinking termination might now not happen at all (if we only receive commands that can be handled instantly), a test for search termination was added immediately after reading the command, so that every command can now be aware whether thinking is in progress. --- UCI2WB.c | 50 +++++++++++++++++++++++++++++++++----------------- 1 files changed, 33 insertions(+), 17 deletions(-) diff --git a/UCI2WB.c b/UCI2WB.c index 7f0223e..997423f 100644 --- a/UCI2WB.c +++ b/UCI2WB.c @@ -41,13 +41,14 @@ #define NONE 2 #define ANALYZE 3 -char move[2000][10], checkOptions[8192], iniPos[256], hashOpt[20], pause, pondering, suspended, ponder, post, hasHash, c, sc='c', suffix[81], *variants, varOpt; +char move[2000][10], iniPos[256], hashOpt[20], pause, pondering, suspended, ponder, post, hasHash, c, sc='c', suffix[81], *variants, varOpt, searching; int mps, tc, inc, sTime, depth, myTime, hisTime, stm, computer = NONE, memory, oldMem=0, cores, moveNr, lastDepth, lastScore, startTime, debug, flob; int statDepth, statScore, statNodes, statTime, currNr, size, collect, nr, sm, inex, on[500], frc, byo = -1, namOpt, comp; -char currMove[20], moveMap[500][10], /* for analyze mode */ canPonder[20], threadOpt[20], varList[8000], anaOpt[20]; +char currMove[20], moveMap[500][10], /* for analyze mode */ canPonder[20], threadOpt[20], varList[8000], anaOpt[20], backLog[10000], checkOptions[8192] = "Ponder"; char board[100]; // XQ board for UCCI char *nameWord = "name ", *valueWord = "value ", *wTime = "w", *bTime = "b", *wInc = "winc", *bInc = "binc", newGame; // keywords that differ in UCCI int unit = 1, drawOffer; +volatile int logLen, sentLen; FILE *toE, *fromE, *fromF; int pid; @@ -197,6 +198,17 @@ Analyze(char *val) if(*anaOpt) EPRINT((f, "# setoption %s%s %s%s\n", nameWord, anaOpt, valueWord, val)); } +int +Release() +{ // send setoption commands backlogged during thinking to engine, aborting ponder or analysis search if necessary + int len = logLen - sentLen, analyse = searching; + if(len <= 0) return 0; + StopPonder(pondering | searching); pondering = searching = 0; // force new search if settings change during analysis (multi-PV!) + fwrite(backLog + sentLen, 1, len, toE); sentLen += len; DPRINT("# release %d\n", len); + if(ponder && computer == 1 - stm) StartPonder(); // (re)start ponder search + return analyse; // return 1 if analysis search should be restarted +} + char *Convert(char *pv) { // convert Shogi coordinates to WB char *p, *q, c; @@ -247,7 +259,7 @@ Engine2GUI() if(fromF = fopen("DefectiveEngineOptions.ini", "r")) printf("# fake engine input\n"); while(1) { - int i=0, x; char *p, dummy; + int i=0, x; char *p, dummy, len; fflush(stdout); fflush(toE); while((line[i] = x = GetChar()) != EOF && line[i] != '\n') i++; @@ -265,6 +277,7 @@ Engine2GUI() if(strstr(line+9, "(none)") || strstr(line+9, "null") || strstr(line+9, "0000")) { printf("%s\n", lastScore < -99999 ? "resign" : "1/2-1/2 {stalemate}"); computer = NONE; } sscanf(line, "bestmove %s", move[moveNr++]); + Release(); // send setoption commands that arrived during search myTime -= (GetTickCount() - startTime)*1.02 + inc; // update own clock, so we can give correct wtime, btime with ponder if(mps && ((moveNr+1)/2) % mps == 0) myTime += tc; if(sTime) myTime = sTime; // new session or move starts stm = WHITE+BLACK - stm; @@ -402,7 +415,7 @@ Move4Engine(char *m) void GUI2Engine() { - char line[256], command[256], *p, *q, *r, mySide, searching = 0; + char line[256], command[256], *p, *q, *r, mySide; while(1) { int i, x, difficult, think=0; @@ -426,33 +439,36 @@ GUI2Engine() fflush(toE); fflush(stdout); i = 0; while((x = getchar()) != EOF && (line[i] = x) != '\n') i++; line[++i] = 0; if(x == EOF) { printf("# EOF\n"); sprintf(line, "quit -1\n"); } - sscanf(line, "%s", command); + if(think && !pause) Sync(PAUSE), think = 0, Release(); // if no longer thinking, take dummy pause + sscanf(line, "%s", command); DPRINT("# '%s' think=%d pause=%d log=%d sent=%d\n", command, think, pause, logLen, sentLen); + if(!strcmp(command, "easy")) { + if(*canPonder) ponder = 0, sprintf(command, "option"), sprintf(line, "option %s=0\n", canPonder); else continue; + } + else if(!strcmp(command, "hard")) { + if(*canPonder) ponder = 1, sprintf(command, "option"), sprintf(line, "option %s=1\n", canPonder); else continue; + } if(!strcmp(command, "offer")) drawOffer = 1; // backlogged anyway, so this can be done instantly else if(!strcmp(command, "post")) post = 1; else if(!strcmp(command, "nopost"))post = 0; else if(!strcmp(command, "option")) { - char name[80], *p; - if(searching) StopPonder(1), searching = 0; // force new search if settings change during analysis (multi-PV!) + char *p; + if(logLen == sentLen) logLen = 0, sentLen = 0; // engine is up to date; reset buffer if(sscanf(line+7, "UCI2WB debug output=%d", &debug) == 1) ; else if(sscanf(line+7, "Floating Byoyomi=%d", &flob) == 1) ; else if(sscanf(line+7, "Byoyomi=%d", &byo) == 1) ; else if(p = strchr(line, '=')) { *p++ = 0; if(strstr(checkOptions, line+7)) sprintf(p, "%s\n", atoi(p) ? "true" : "false"); - EPRINT((f, "# setoption %s%s %s%s", nameWord, line+7, valueWord, p)) - } else EPRINT((f, "# setoption %s%s\n", nameWord, line+7)) + snprintf(backLog+logLen, 9999-logLen, "setoption %s%s %s%s", nameWord, line+7, valueWord, p); + } else snprintf(backLog+logLen, 9999-logLen, "setoption %s%s\n", nameWord, line+7); + DPRINT("# backlog: %s", backLog+logLen); logLen += strlen(backLog+logLen); + if(!think && Release()) break; // break will restart analysis; pondering is restarted by Release itself } else if(!strcmp(command, "pause")) { if(computer == stm) myTime -= GetTickCount() - startTime; suspended = 1 + pondering; // remember if we were pondering, and stop search ignoring bestmove StopPonder(pondering || computer == stm); } - else if(!strcmp(command, "easy")) { - if(*canPonder) { ponder = 0; StopPonder(pondering); EPRINT((f, "# setoption %s%s %sfalse\n", nameWord, canPonder, valueWord)) } - } - else if(!strcmp(command, "hard")) { - if(*canPonder) { ponder = 1; EPRINT((f, "# setoption %s%s %strue\n", nameWord, canPonder, valueWord)) StartPonder(); } - } else difficult = 1; // difficult command; terminate loop for easy ones } // next command @@ -460,10 +476,10 @@ GUI2Engine() if(suspended == 2) StartPonder(); // restart interrupted ponder search suspended = think = 0; continue; // causes thinking to start in normal way if on move or analyzing } - if(think) { // command arrived during thinking; order abort for 'instant commands' + if(think) { // command arrived during thinking; order abort for 'instant commands' if(!strcmp(command, "?") || !strcmp(command, "quit") || !strcmp(command, "force") || !strcmp(command, "result")) { EPRINT((f, "# stop\n")); fflush(toE); } - Sync(PAUSE); // block processing of input during thinking + Sync(PAUSE); Release(); // block processing of difficult commands during thinking; send backlog left because of race } if(!strcmp(command, "new")) { computer = BLACK; moveNr = 0; depth = -1; move[0][0] = 0; -- 1.7.0.4