X-Git-Url: http://hgm.nubati.net/cgi-bin/gitweb.cgi?p=uci2wb.git;a=blobdiff_plain;f=UCI2WB.c;h=7251f5df0aeba7130e4a87d73ce00a02d84c7a9e;hp=ae6cf2c147d0a8176910a5d56eafe768e55d3441;hb=HEAD;hpb=ed12bdda15cb49c351d1358a8d056a1f97c59f85 diff --git a/UCI2WB.c b/UCI2WB.c index ae6cf2c..5cb07e8 100644 --- a/UCI2WB.c +++ b/UCI2WB.c @@ -9,7 +9,7 @@ /* of which you should have received a copy together with this file. */ /****************************************************************************/ -#define VERSION "3.0" +#define VERSION "4.1" #include #include @@ -18,21 +18,29 @@ # include HANDLE process; DWORD thread_id; + void Bury(int s) { if(WaitForSingleObject(process, 1000*s+50) != WAIT_OBJECT_0) TerminateProcess(process, 0); } #else # include # include +# include # define NO_ERROR 0 # include +# include int GetTickCount() // with thanks to Tord { struct timeval t; gettimeofday(&t, NULL); return t.tv_sec*1000 + t.tv_usec/1000; } +//# include + int Sleep(int msec) { return usleep(1000*msec); } + int pid; void Bury(int msec) { Sleep(msec+50); if(waitpid(-1, NULL, WNOHANG) <= 0) kill(pid, SIGKILL); } #endif #include #include +#include // Set VARIANTS for in WinBoard variant feature. (With -s option this will always be reset to use "shogi".) #define VARIANTS ",normal,xiangqi" #define STDVARS "chess,chess960,crazyhouse,3check,giveaway,suicide,losers,atomic,seirawan,shogi,xiangqi" #define EGT ",gaviotaTbPath,syzygyPath,nalimovPath,robbotripleBaseDirectory,robbototalBaseDirectory,bitbases path," +#define XQPOS "position fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR" #define DPRINT if(debug) printf #define EPRINT(X) { char f[999]; sprintf X; DPRINT("%s", f); fprintf(toE, "%s", f + 2*(*f == '#')); /* strip optional # prefix */ } @@ -42,17 +50,15 @@ #define NONE 2 #define ANALYZE 3 -char move[2000][10], iniPos[256], hashOpt[20], pause, suspended, ponder, post, hasHash, c, sc='c', suffix[81], varOpt, searching, *binary; +char move[2000][10], iniPos[256], hashOpt[20], suspended, ponder, post, hasHash, c, sc=' ', sc2, suffix[81], varOpt, searching, *binary; 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], backLog[10000], checkOptions[8192] = "Ponder"; +int statDepth, statScore, statNodes, statTime, currNr, hsize=9, size=9, 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], checkOptions[8192] = "Ponder"; char pvs[99][999], 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, scores[99], mpvSP, maxDepth, ponderAlways; -volatile int logLen, sentLen; +int unit = 1, drawOffer, scores[99], mpvSP, maxDepth, ponderAlways, newCnt, priority, killDelay; FILE *toE, *fromE, *fromF; -int pid; char *strcasestr (char *p, char *q) { while(*p) { char *r=p++, *s=q; while(tolower(*r++) == tolower(*s) && *s) s++; if(!*s) return p-1; } return NULL; } @@ -86,12 +92,13 @@ Sync (int action) case PAUSE: ReadFile(hRd, &c, 1, &d, NULL); } #else - static int syncPipe[2]; char c; + static int syncPipe[2], res; char c; switch(action) { - case INIT: pipe(syncPipe); break; - case WAKEUP: write(syncPipe[1], "\n", 1); break; - case PAUSE: read(syncPipe[0], &c, 1); + case INIT: res = pipe(syncPipe); break; + case WAKEUP: res = write(syncPipe[1], "\n", 1); break; + case PAUSE: res = read(syncPipe[0], &c, 1); } + if(res < 0) printf("tellusererror UCI2WB: bad sync pipe\n"), exit(0); #endif } @@ -132,7 +139,7 @@ int Play(int nr) { int i, last = -1; - FromFEN(iniPos + 4); // in XQ iniPos always has just "fen " prefix + FromFEN(iniPos + 13); // FEN part for(i=0; i 0) EPRINT((f, " movetime %d", sTime)) else if(mps) EPRINT((f, " movestogo %d", mps*(nr/(2*mps)+1)-nr/2)) if(flob || byo >= 0) sprintf(suffix, " byoyomi %d", t); // for engines running purely on byoyomi - if(inc && !*suffix) EPRINT((f, " %s %d %s %d", wInc, inc, bInc, inc)) + if((inc || !mps && !sTime && sc == 'x') && !*suffix) EPRINT((f, " %s %d %s %d", wInc, inc, bInc, inc)) if(depth > 0) EPRINT((f, " depth %d", depth)) if(*suffix) EPRINT((f, suffix, inc)) EPRINT((f, "\n")); maxDepth = mpvSP = 0; } void -StopPonder(int pondering) +StopSearch(int discard) { - if(!pondering) return; - pause = 1; + if(!searching) return; + if(discard) searching = 0; // this causes bestmove to be ignored EPRINT((f, "# stop\n")) fflush(toE); // note: 'pondering' remains set until engine acknowledges 'stop' with 'bestmove' - Sync(PAUSE); // wait for engine to acknowledge 'stop' with 'bestmove'. } void @@ -176,7 +182,7 @@ LoadPos(int moveNr) if(sc == 'x') { // UCCI: send only reversible moves lastCapt = Play(moveNr); // find last capture (returns -1 if none!) Play(++lastCapt); // reconstruct board after last capture - stm = (!strstr(iniPos+4, " b ") ^ lastCapt & 1 ? 'w' : 'b'); + stm = (!strstr(iniPos+13, " b ") ^ lastCapt & 1 ? 'w' : 'b'); sprintf(buf, "position fen %s", ToFEN(stm)); pos = buf; // send it as FEN (with "position" in UCCI!) } EPRINT((f, "# %s moves", pos)) @@ -190,6 +196,7 @@ StartPonder(int moveNr) if(!move[moveNr][0]) return; // no ponder move LoadPos(moveNr+1); searching = 1; lastDepth = 1; + DPRINT("# ponder on %s\n", move[moveNr]); StartSearch(" ponder"); } @@ -199,17 +206,6 @@ 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 == 2); - if(len <= 0) return 0; - StopPonder(searching); 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(moveNr); // (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; @@ -217,7 +213,7 @@ char *Convert(char *pv) if(sc != 's') return pv; p = pv; q = buf; while(c = *p++) { - if(c >= '0' && c <= '9' || c >= 'a' && c <= 'z') *q++ = 'a'+'0'+size - c; else *q++ = c; + if(c >= '0' && c <= '9' || c >= 'a' && c <= 'z') *q++ = 'a'+'0' + (c >= 'a' ? size : hsize) - c; else *q++ = c; } *q++ = 0; return buf; @@ -228,12 +224,12 @@ Move4GUI(char *m) { if(sc == 's') { // convert USI move to WB format - m[2] = 'a'+'0'+size - m[2]; + m[2] = 'a'+'0'+hsize - m[2]; m[3] = 'a'+'0'+size - m[3]; if(m[1] == '*') { // drop m[1] = '@'; } else { - m[0] = 'a'+'0'+size - m[0]; + m[0] = 'a'+'0'+hsize - m[0]; m[1] = 'a'+'0'+size - m[1]; if((stm == WHITE ? (m[1]>'0'+size-size/3 || m[3]>'0'+size-size/3) : (m[1] <= '0'+size/3 || m[3] <= '0'+size/3)) && m[4] != '+') @@ -243,10 +239,10 @@ Move4GUI(char *m) } int -ReadLine (FILE *f, char *line) +ReadLine (FILE *f, char *line, int len) { int x, i = 0; - while((x = fgetc(f)) != EOF && (line[i] = x) != '\n') i++; line[++i] = 0; + while((x = fgetc(f)) != EOF && (line[i] = x) != '\n') i+=(i 2) { // done collecting - if(inex && collect == 2) printf("%d 0 0 0 OK to exclude\n", lastDepth); - collect = 3; continue; - } - collect = 2; on[nr=n] = 1; sscanf(pv+9, "%s", moveMap[n]); continue; // store move - } - } if(!post) continue; if(sscanf(line+5, "string %c", &dummy) == 1) printf("%d 0 0 0 %s", lastDepth, line+12); else { if(p = strstr(line+4, " depth ")) sscanf(p+7, "%d", &d), statDepth = d; @@ -327,8 +312,6 @@ HandleEngineOutput() if(p = strstr(line+4, " score ")) sscanf(p+7, "%d", &s), statScore = s; if(p = strstr(line+4, " nodes ")) sscanf(p+7, "%d", &n), statNodes = n; if(p = strstr(line+4, " time ")) sscanf(p+6, "%d", &t), t /= 10, statTime = t; - if(p = strstr(line+4, " currmove ")) sscanf(p+10,"%s", currMove); - if(p = strstr(line+4, " currmovenumber ")) sscanf(p+16,"%d", &currNr); if(pv = strstr(line+4, " pv ")) { // convert PV info to WB thinking output if(d > maxDepth) maxDepth = d, mpvSP = 0; else if(d < maxDepth) continue; // ignore depth regressions if(p = strstr(line+4, " upperbound ")) strcat(p, "?\n"); else @@ -338,6 +321,18 @@ HandleEngineOutput() printf("%3d %6d %6d %10d %s", lastDepth=d, lastScore=s, t, n, Convert(pv+4)); } else if(s == -100000) lastScore = s; // when checkmated score is valid even without PV (which might not come) } + if(collect && (pv = strstr(line+4, " currmove "))) { + sscanf(pv+10,"%s", currMove); + if(p = strstr(line+4, " currmovenumber ")) { + n = currNr = atoi(p+16); + if(collect == 1 && n != 1) continue; // wait for move 1 + if(collect + (n == 1) > 2) { // done collecting + if(inex && collect == 2) printf("%d 0 0 0 OK to exclude\n", lastDepth); + collect = 3; continue; + } + collect = 2; on[nr=n] = 1; strcpy(moveMap[n], currMove); continue; // store move + } + } } else if(!strcmp(command, "option")) { // USI option: extract data fields char name[80], type[80], buf[1024], val[256], *q; @@ -357,7 +352,7 @@ HandleEngineOutput() if(!strcasecmp(name, "Threads")) { strcpy(threadOpt, name); continue; } if(!strcasecmp(name, "Ponder") || !strcasecmp(name, "USI_Ponder")) { strcpy(canPonder, name); continue; } if(!strcasecmp(name, "Hash") || !strcasecmp(name, "USI_Hash") || !strcasecmp(name, "hashsize")) { - memory = oldMem = atoi(val); hasHash = 1; + memory = oldMem = atoi(val); hasHash = max; strcpy(hashOpt, name); continue; } @@ -386,6 +381,7 @@ HandleEngineOutput() sprintf(buf + strlen(buf), "%s%s%s", min++ ? " /// " : " ", strcmp(type, val) ? "" : "*", val); } strcat(q, "\"\n"); + } else buf[0] = 0; // ignore unrecognized option types if(buf[0]) printf("%s", buf); @@ -395,29 +391,24 @@ HandleEngineOutput() if(sscanf(line, "id name %[^\n]", name) == 1) printf("feature myname=\"%s (U%cI2WB)\"\n", name, sc-32); if(sscanf(line, "id version %[^\n]", version) == 1 && *name) printf("feature myname=\"%s %s (U%cI2WB)\"\n", name, version, sc-32); } - else if(!strcmp(command, "readyok")) { pause = 0; Sync(WAKEUP); } // resume processing of GUI commands + else if(!strcmp(command, "readyok")) return; // resume processing of GUI commands else if(sc == 'x'&& !strcmp(command, "ucciok") || sscanf(command, "u%ciok", &c)==1 && c==sc) { - char *p = varList, *q = varList; + char *p = varList, *q = p; while(*q && *q != '\n') if(!strncmp(q, " var ", 5)) *p++ = ',', q +=5; // replace var keywords by commas else if(!strncmp(q-1, " chess ", 7)) strcpy(p, "normal"), p += 6, q += 5; // 'chess' is called 'normal' in CECP else *p++ = *q++; // copy other variant names unmodified + *p = 0; if(frc) sprintf(p, ",normal,fischerandom"), printf("feature oocastle=%d\n", frc<0); // unannounced FRC uses O-O castling - if(*varList) printf("feature variants=\"%s\"\n", varList+1); // from UCI_Variant combo and/or UCI_Chess960 check options + if(!*varList) strcpy(varList, sc=='s' ? ",shogi,5x5+5_shogi" : VARIANTS); // without clue guess liberally + printf("feature variants=\"%s\"\n", varList+1); // from UCI_Variant combo and/or UCI_Chess960 check options if(*egts) printf("feature egt=\"%s\"\n", egts+1); - printf("feature smp=1 memory=%d done=1\n", hasHash); + printf("feature smp=1 memory=%d done=1\n", hasHash > 0); if(unit == 2) { unit = 1; EPRINT((f, "# setoption usemillisec true\n")) } - Sync(WAKEUP); // done with options + fflush(stdout); return; // done with options } } } -void * -Engine2GUI() -{ - if(fromF = fopen("DefectiveEngineOptions.ini", "r")) printf("# fake engine input\n"); - HandleEngineOutput(); -} - void Move4Engine(char *m) { @@ -426,29 +417,28 @@ Move4Engine(char *m) if(m[1] == '@') { // drop m[1] = '*'; } else { - m[0] = 'a'+'0'+size - m[0]; + m[0] = 'a'+'0'+hsize - m[0]; m[1] = 'a'+'0'+size - m[1]; } - m[2] = 'a'+'0'+size - m[2]; + m[2] = 'a'+'0'+hsize - m[2]; m[3] = 'a'+'0'+size - m[3]; if(m[4] == '=') m[4] = 0; // no '=' in USI format! else if(m[4]) m[4] = '+'; // cater to WB 4.4 bug :-( } } -int DoCommand (); +void DoCommand (); char mySide; -char queue[10000], *qStart, *qEnd; +volatile char queue[10000], *qStart, *qEnd; void -GUI2Engine() +LaunchSearch() { - char line[256], command[256], *p; + int i; - while(1) { - int i, difficult, think=0; + if(suspended || searching) return; - if((computer == stm || computer == ANALYZE && !searching) && !suspended) { + if(computer == stm || computer == ANALYZE && sm != 1) { DPRINT("# start search\n"); LoadPos(moveNr); fflush(stdout); // load position // and set engine thinking (note USI swaps colors!) @@ -460,25 +450,45 @@ GUI2Engine() for(i=1; i 0) EPRINT((f, "# setoption name UCI_Chess960 value true\n")) } } else if(!strcmp(command, "undo") && (i=1) || !strcmp(command, "remove") && (i=2)) { - if(searching) StopPonder(1), searching = 0; moveNr = moveNr > i ? moveNr - i : 0; collect = (computer == ANALYZE); sm = 0; } - else if(!strcmp(command, ".")) { - printf("stat01: %d %d %d %d 100 %s\n", statTime, statNodes, statDepth, 100-currNr, currMove); - return 1; - } else if(!strcmp(command+2, "clude") && collect > 2) { // include or exclude int all = !strcmp(line+8, "all"), in = command[1] == 'n'; inex = 1; line[strlen(line)-1] = sm = 0; // strip LF and clear sm flag for(i=1; i hasHash) memory = hasHash; } else if(!strcmp(command, "cores")&& !!*threadOpt) { sscanf(line, "cores %d", &cores); EPRINT((f, "# setoption %s%s %s%d\n", nameWord, threadOpt, valueWord, cores)) } else if(!strcmp(command, "egtpath")){ sscanf(line+8, "%s %[^\n]", type, command); @@ -629,14 +641,26 @@ DoCommand () else if(!strcmp(command, "sd")) sscanf(line, "sd %d", &depth); else if(!strcmp(command, "st")) sscanf(line, "st %d", &sTime), sTime = 1000*sTime - 30, inc = 0, sTime /= unit; else if(!strcmp(command, "name")) { if(namOpt) EPRINT((f, "# setoption name UCI_Opponent value none none %s %s", comp ? "computer" : "human", line+5)) } - else if(!strcmp(command, "computer")) comp = 1; else if(!strcmp(command, "result")) { if(sc == 's') EPRINT((f, "# gameover %s\n", line[8] == '/' ? "draw" : (line[7] == '0') == mySide ? "win" : "lose")) computer = NONE; } - else if(!strcmp(command, "quit")) { EPRINT((f, "# quit\n")) fflush(toE), exit(atoi(line+4)); } + else if(!strcmp(command, "quit")) { EPRINT((f, "# quit\n")) fflush(toE); } + else printf("Error (unknown command): %s\n", command); - return 0; + fflush(stdout); +} + +void * +Engine2GUI() +{ + if(fromF = fopen("DefectiveEngineOptions.ini", "r")) printf("# fake engine input\n"); + while(1) { + if(searching > 1) HandleEngineOutput(); // this could leave us (or fall through) pondering + while(qStart == qEnd && searching) HandleEngineOutput(); // relay ponder output until command arrives + Sync(PAUSE); // possibly wait for command silently if engine is idle + DoCommand(); LaunchSearch(); + } } int @@ -683,9 +707,9 @@ StartEngine(char *cmdLine, char *dir) if (! fSuccess) return GetLastError(); -// if (0) { // in the future we could trigger this by an argument -// SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines)); -// } + if (priority > 0) { // for now only implement all lowered priorityies the same way + SetPriorityClass(piProcInfo.hProcess, BELOW_NORMAL_PRIORITY_CLASS); + } /* Close the handles we don't need in the parent */ CloseHandle(piProcInfo.hThread); @@ -693,7 +717,6 @@ StartEngine(char *cmdLine, char *dir) CloseHandle(hChildStdoutWr); process = piProcInfo.hProcess; - pid = piProcInfo.dwProcessId; fromE = (FILE*) _fdopen( _open_osfhandle((long)hChildStdoutRd, _O_TEXT|_O_RDONLY), "r"); toE = (FILE*) _fdopen( _open_osfhandle((long)hChildStdinWr, _O_WRONLY), "w"); #else @@ -701,7 +724,8 @@ StartEngine(char *cmdLine, char *dir) int i, toEngine[2], fromEngine[2]; if (dir && dir[0] && chdir(dir)) { perror(dir); exit(1); } - pipe(toEngine); pipe(fromEngine); // create two pipes + i = pipe(toEngine) + pipe(fromEngine); // create two pipes + if(i < 0) printf("tellusererror UCI2WB: no engine pipe\n"), exit(0); if ((pid = fork()) == 0) { // Child dup2(toEngine[0], 0); close(toEngine[0]); close(toEngine[1]); // stdin from toE pipe @@ -711,6 +735,7 @@ StartEngine(char *cmdLine, char *dir) strcpy(buf, cmdLine); p = buf; for (i=0;;) { argv[i++] = p; p = strchr(p, ' '); if (p == NULL) break; *p++ = 0; } argv[i] = NULL; + if(priority) i = nice(priority); execvp(argv[0], argv); // startup engine perror(argv[0]); exit(1); // could not start engine; quit. @@ -724,22 +749,25 @@ StartEngine(char *cmdLine, char *dir) return NO_ERROR; } +int main(int argc, char **argv) { - char *dir = NULL, *p, *q; int e; + char *dir = NULL; if(argc == 2 && !strcmp(argv[1], "-v")) { printf("UCI2WB " VERSION " by H.G.Muller\n"); exit(0); } if(argc > 1 && !strcmp(argv[1], "debug")) { debug = 1; argc--; argv++; } if(argc > 1 && !strcmp(argv[1], "-var")) { strcpy(varList+1, argv[2]); *varList = ','; argc-=2; argv+=2; } + if(argc > 2 && !strcmp(argv[1], "-nice")) { sscanf(argv[2], "%d", &priority); argc-=2; argv+=2; } + if(argc > 2 && !strcmp(argv[1], "-kill")) { sscanf(argv[2], "%d", &killDelay); argc-=2; argv+=2; } if(argc > 1 && argv[1][0] == '-') { sc = argv[1][1]; argc--; argv++; } - if(argc < 2) { printf("usage is: U%cI2WB [debug] [-s] []\n", sc-32); exit(-1); } + if(argc < 2) { printf("usage is: %s [debug] [-s|-x|-c|-f] []\n", argv[0]); exit(-1); } if(argc > 2) dir = argv[2]; if(argc > 3) strncpy(suffix, argv[3], 80); if(sc == 'x') nameWord = valueWord = bTime = "", wTime = "opp", bInc = "increment", wInc = "oppincrement", unit = 1000; // switch to UCCI keywords else if(sc == 'f' ) frc = -1, sc = 'c'; // UCI for unannounced Chess960 - else if(sc == 'n') sc = 'c'; // UCI for normal Chess + else if(sc != 's') sc2 = sc, sc = 'c'; // UCI for normal Chess // spawn engine proc if(StartEngine(binary = argv[1], dir) != NO_ERROR) { perror(argv[1]), exit(-1); } @@ -755,4 +783,5 @@ main(int argc, char **argv) // handle GUI->engine traffic in original thread GUI2Engine(); + return 0; }