1 /************************* UCI2WB by H.G.Muller ****************************/
16 # include <sys/time.h>
17 int GetTickCount() // with thanks to Tord
18 { struct timeval t
; gettimeofday(&t
, NULL
); return t
.tv_sec
*1000 + t
.tv_usec
/1000; }
24 #define SLEEP() Sleep(1)
26 #define SLEEP() usleep(10)
29 // Set VARIANTS for in WinBoard variant feature. (With -s option this will always be reset to use "shogi".)
30 # define VARIANTS "normal,xiangqi"
37 char move
[2000][10], checkOptions
[8192], iniPos
[256], hashOpt
[20], pause
, pondering
, ponder
, post
, hasHash
, c
, sc
='c', *suffix
;
38 int mps
, tc
, inc
, sTime
, depth
, myTime
, hisTime
, stm
, computer
= NONE
, memory
, oldMem
=0, cores
, moveNr
, lastDepth
, lastScore
, startTime
;
39 int statDepth
, statScore
, statNodes
, statTime
, currNr
, size
; char currMove
[20]; // for analyze mode
45 StartSearch(char *ponder
)
46 { // send the 'go' command to engine. Suffix by ponder.
47 int nr
= moveNr
+ (ponder
[0] != 0); // we ponder for one move ahead!
48 fprintf(toE
, "\ngo btime %d wtime %d", stm
== BLACK
^ sc
=='s' ? myTime
: hisTime
, stm
== WHITE
^ sc
=='s' ? myTime
: hisTime
);
49 printf( "\n# go btime %d wtime %d", stm
== BLACK
^ sc
=='s' ? myTime
: hisTime
, stm
== WHITE
^ sc
=='s' ? myTime
: hisTime
);
50 if(sTime
> 0) fprintf(toE
, " movetime %d", sTime
),printf(" movetime %d", sTime
); else
51 if(mps
) fprintf(toE
, " movestogo %d", mps
*(nr
/(2*mps
)+1)-nr
/2),printf(" movestogo %d", mps
*(nr
/(2*mps
)+1)-nr
/2);
52 if(inc
&& !suffix
) fprintf(toE
, " winc %d binc %d", inc
, inc
),printf(" winc %d binc %d", inc
, inc
);
53 if(depth
> 0) fprintf(toE
, " depth %d", depth
),printf(" depth %d", depth
);
54 if(suffix
) fprintf(toE
, suffix
, inc
),printf(suffix
, inc
);
55 fprintf(toE
, "%s\n", ponder
);
56 printf("%s\n", ponder
);
60 StopPonder(int pondering
)
62 if(!pondering
) return;
64 fprintf(toE
, "stop\n");printf("# stop\n"); // note: 'pondering' remains set until engine acknowledges 'stop' with 'bestmove'
65 while(pause
) SLEEP(); // wait for engine to acknowledge 'stop' with 'bestmove'.
72 fprintf(toE
, "%s moves", iniPos
);
73 printf( "# %s moves", iniPos
);
74 for(j
=0; j
<moveNr
; j
++) fprintf(toE
, " %s", move
[j
]),printf(" %s", move
[j
]);
77 char *Convert(char *pv
)
78 { // convert Shogi coordinates to WB
80 static char buf
[10000];
81 if(sc
!= 's') return pv
;
84 if(c
>= '0' && c
<= '9' || c
>= 'a' && c
<= 'z') *q
++ = 'a'+'0'+size
- c
; else *q
++ = c
;
93 char line
[1024], command
[256];
98 fflush(stdout
); fflush(toE
);
99 while((line
[i
] = x
= fgetc(fromE
)) != EOF
&& line
[i
] != '\n') i
++;
101 if(x
== EOF
) exit(0);
102 printf("# engine said: %s", line
);
103 sscanf(line
, "%s", command
);
104 if(!strcmp(command
, "bestmove")) {
105 if(pause
) { pondering
= pause
= 0; continue; } // bestmove was reply to ponder miss or analysis result; ignore.
106 // move was a move to be played
107 if(strstr(line
+9, "resign")) { printf("resign\n"); computer
= NONE
; }
108 if(strstr(line
+9, "(none)") || strstr(line
+9, "null") ||
109 strstr(line
+9, "0000")) { printf("%s\n", lastScore
< -99999 ? "resign" : "1/2-1/2 {stalemate}"); computer
= NONE
; }
110 sscanf(line
, "bestmove %s", move
[moveNr
++]);
111 myTime
-= (GetTickCount() - startTime
)*1.02 + inc
; // update own clock, so we can give correct wtime, btime with ponder
112 if(mps
&& ((moveNr
+1)/2) % mps
== 0) myTime
+= tc
; if(sTime
) myTime
= sTime
; // new session or move starts
113 // first start a new ponder search, if pondering is on and we have a move to ponder on
114 if(p
= strstr(line
+9, "ponder")) {
115 if(computer
!= NONE
&& ponder
) {
116 sscanf(p
+7, "%s", move
[moveNr
]);
117 printf("# ponder on %s\n", move
[moveNr
]);
118 LoadPos(moveNr
+1); // load position
119 // and set engine pondering
120 pondering
= 1; lastDepth
= 1;
121 StartSearch(" ponder");
123 p
[-1] = '\n'; *p
= 0; // strip off ponder move
126 // convert USI move to WB format
127 line
[11] = 'a'+'0'+size
- line
[11];
128 line
[12] = 'a'+'0'+size
- line
[12];
129 if(line
[10] == '*') { // drop
132 line
[9] = 'a'+'0'+size
- line
[9];
133 line
[10] = 'a'+'0'+size
- line
[10];
134 if((stm
== WHITE
? (line
[10]>'0'+size
-size
/3 || line
[12]>'0'+size
-size
/3)
135 : (line
[10] <= '0'+size
/3 || line
[12] <= '0'+size
/3)) && line
[13] != '+')
136 line
[13] = '=', line
[14] = 0;
139 printf("move %s\n", line
+9); // send move to GUI
140 if(lastScore
== 100001 && iniPos
[0] != 'f') { printf("%s {mate}\n", stm
== WHITE
? "1-0" : "0-1"); computer
= NONE
; }
141 stm
= WHITE
+BLACK
- stm
;
143 else if(!strcmp(command
, "info")) {
144 int d
=0, s
=0, t
=0, n
=0;
147 if(strstr(line
, "info string ") == line
) printf("%d 0 0 0 %s", lastDepth
, line
+12); else {
148 if(p
= strstr(line
+4, " depth ")) sscanf(p
+7, "%d", &d
), statDepth
= d
;
149 if(p
= strstr(line
+4, " score cp ")) sscanf(p
+10, "%d", &s
), statScore
= s
; else
150 if(p
= strstr(line
+4, " score mate ")) sscanf(p
+12, "%d", &s
), s
+= s
>0 ? 100000 : -100000, statScore
= s
; else
151 if(p
= strstr(line
+4, " score ")) sscanf(p
+7, "%d", &s
), statScore
= s
;
152 if(p
= strstr(line
+4, " nodes ")) sscanf(p
+7, "%d", &n
), statNodes
= n
;
153 if(p
= strstr(line
+4, " time ")) sscanf(p
+6, "%d", &t
), t
/= 10, statTime
= t
;
154 if(p
= strstr(line
+4, " currmove ")) sscanf(p
+10,"%s", currMove
);
155 if(p
= strstr(line
+4, " currmovenumber ")) sscanf(p
+16,"%d", &currNr
);
156 if(pv
= strstr(line
+4, " pv ")) // convert PV info to WB thinking output
157 printf("%3d %6d %6d %10d %s", lastDepth
=d
, lastScore
=s
, t
, n
, Convert(pv
+4));
160 else if(!strcmp(command
, "option")) { // USI option: extract data fields
161 char name
[80], type
[80], buf
[1024], val
[256], *q
;
163 if(p
= strstr(line
+6, " type ")) sscanf(p
+1, "type %s", type
), *p
= '\n';
164 if(p
= strstr(line
+6, " min ")) sscanf(p
+1, "min %d", &min
), *p
= '\n';
165 if(p
= strstr(line
+6, " max ")) sscanf(p
+1, "max %d", &max
), *p
= '\n';
166 if(p
= strstr(line
+6, " default ")) sscanf(p
+1, "default %[^\n]*", val
), *p
= '\n';
167 if(p
= strstr(line
+6, " name ")) sscanf(p
+1, "name %[^\n]*", name
);
168 if(!strcmp(name
, "Hash") || !strcmp(name
, "USI_Hash")) {
169 memory
= oldMem
= atoi(val
); hasHash
= 1;
170 strcpy(hashOpt
, name
);
173 // pass on engine-defined option as WB option feature
174 if(!strcmp(type
, "filename")) type
[4] = 0;
175 sprintf(buf
, "feature option=\"%s -%s", name
, type
); q
= buf
+ strlen(buf
);
176 if( !strcmp(type
, "file")
177 || !strcmp(type
, "string")) sprintf(q
, " %s\"\n", val
);
178 else if(!strcmp(type
, "spin")) sprintf(q
, " %d %d %d\"\n", atoi(val
), min
, max
);
179 else if(!strcmp(type
, "check")) sprintf(q
, " %d\"\n", strcmp(val
, "true") ? 0 : 1), strcat(checkOptions
, name
);
180 else if(!strcmp(type
, "button")) sprintf(q
, "\"\n");
181 else if(!strcmp(type
, "combo")) {
182 if(p
= strstr(line
+6, " default ")) sscanf(p
+1, "default %s", type
); // current setting
184 while(p
= strstr(p
, " var ")) {
185 sscanf(p
+= 5, "%s", val
); // next choice
186 sprintf(buf
+ strlen(buf
), "%s%s%s", min
++ ? " /// " : " ", strcmp(type
, val
) ? "" : "*", val
);
190 else buf
[0] = 0; // ignore unrecognized option types
191 if(buf
[0]) printf("%s", buf
);
193 else if(!strcmp(command
, "id")) {
195 if(sscanf(line
, "id name %s", name
) == 1) printf("feature myname=\"%s (U%cI2WB)\"\n", name
, sc
-32);
197 else if(!strcmp(command
, "readyok")) pause
= 0; // resume processing of GUI commands
198 else if(sscanf(command
, "u%ciok", &c
)==1 && c
==sc
) printf("feature smp=1 memory=%d done=1\n", hasHash
); // done with options
205 char line
[256], command
[256], *p
, *q
;
210 if(computer
== stm
|| computer
== ANALYZE
) {
211 printf("# start search\n");
212 LoadPos(moveNr
); // load position
213 // and set engine thinking (note USI swaps colors!)
214 startTime
= GetTickCount();
215 if(computer
== ANALYZE
) fprintf(toE
, "\ngo infinite\n"), printf("\ngo infinite\n");
216 else StartSearch("");
219 fflush(toE
); fflush(stdout
);
220 i
= 0; while((x
= getchar()) != EOF
&& (line
[i
] = x
) != '\n') i
++;
221 line
[++i
] = 0; if(x
== EOF
) { printf("# EOF\n"); exit(-1); }
222 sscanf(line
, "%s", command
);
223 while(pause
) SLEEP(); // wait for readyok
224 if(!strcmp(command
, "new")) {
225 computer
= BLACK
; moveNr
= 0; depth
= -1;
226 stm
= WHITE
; strcpy(iniPos
, "position startpos");
227 if(memory
!= oldMem
&& hasHash
) fprintf(toE
, "setoption name %s value %d\n", hashOpt
, memory
);
229 // we can set other options here
230 pause
= 1; // wait for option settings to take effect
231 fprintf(toE
, "isready\n");
232 fprintf(toE
, "u%cinewgame\n", sc
);
234 else if(!strcmp(command
, "usermove")) {
236 // convert input move to USI format
237 if(line
[10] == '@') { // drop
240 line
[9] = 'a'+'0'+size
- line
[9];
241 line
[10] = 'a'+'0'+size
- line
[10];
243 line
[11] = 'a'+'0'+size
- line
[11];
244 line
[12] = 'a'+'0'+size
- line
[12];
245 if(line
[13] == '=') line
[13] = 0; // no '=' in USI format!
246 else if(line
[13] != '\n') line
[13] = '+'; // cater to WB 4.4 bug :-(
248 sscanf(line
, "usermove %s", command
); // strips off linefeed
249 stm
= WHITE
+BLACK
- stm
;
250 // when pondering we either continue the ponder search as normal search, or abort it
251 if(pondering
|| computer
== ANALYZE
) {
252 if(pondering
&& !strcmp(command
, move
[moveNr
])) { // ponder hit
253 pondering
= 0; moveNr
++; startTime
= GetTickCount(); // clock starts running now
254 fprintf(toE
, "ponderhit\n"); printf("# ponderhit\n");
259 sscanf(line
, "usermove %s", move
[moveNr
++]); // possibly overwrites ponder move
261 else if(!strcmp(command
, "level")) {
263 sscanf(line
, "level %d %d:%d %d", &mps
, &tc
, &sec
, &inc
) == 4 ||
264 sscanf(line
, "level %d %d %d", &mps
, &tc
, &inc
);
265 tc
= (60*tc
+ sec
)*1000; inc
*= 1000; sTime
= 0;
267 else if(!strcmp(command
, "option")) {
269 if(p
= strchr(line
, '=')) {
271 if(strstr(checkOptions
, line
+7)) sprintf(p
, "%s\n", atoi(p
) ? "true" : "false");
272 fprintf(toE
, "setoption name %s value %s", line
+7, p
), printf("# setoption name %s value %s", line
+7, p
);
273 } else fprintf(toE
, "setoption name %s\n", line
+7), printf("# setoption name %s\n", line
+7);
275 else if(!strcmp(command
, "protover")) {
276 printf("feature variants=\"%s\" setboard=1 usermove=1 debug=1 reuse=0 done=0\n", sc
=='s' ? "shogi,5x5+5_shogi" : VARIANTS
);
277 fprintf(toE
, "u%ci\n", sc
); // this prompts UCI engine for options
279 else if(!strcmp(command
, "setboard")) {
280 if(strstr(line
+9, " b ")) stm
= BLACK
;
281 if(sc
== 's' && (p
= strchr(line
+9, '['))) { char c
;
282 *p
++ = ' '; q
= strchr(p
, ']'); c
= 'w' + 'b' - q
[2]; strcpy(q
+2, " 1\n"); while(*--q
!= ' ') q
[2] = *q
; *p
= c
; p
[1] = ' ';
284 sprintf(iniPos
, "%s%sfen %s", iniPos
[0]=='p' ? "position " : "", sc
=='s' ? "s" : "", line
+9);
285 iniPos
[strlen(iniPos
)-1] = 0;
287 else if(!strcmp(command
, "variant")) {
288 if(!strcmp(line
+8, "shogi\n")) size
= 9, strcpy(iniPos
, "position startpos");
289 if(!strcmp(line
+8, "5x5+5_shogi\n")) size
= 5, strcpy(iniPos
, "position startpos");
290 if(!strcmp(line
+8, "xiangqi\n")) strcpy(iniPos
, "fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r");
292 else if(!strcmp(command
, "undo") && (i
=1) || !strcmp(command
, "remove") && (i
=2)) {
293 if(pondering
|| computer
== ANALYZE
) StopPonder(1);
294 moveNr
= moveNr
> i
? moveNr
- i
: 0;
296 else if(!strcmp(command
, ".")) {
297 printf("stat01: %d %d %d %d 100 %s\n", statTime
, statNodes
, statDepth
, 100-currNr
, currMove
);
300 else if(!strcmp(command
, "xboard")) ;
301 else if(!strcmp(command
, "analyze"))computer
= ANALYZE
;
302 else if(!strcmp(command
, "exit")) computer
= NONE
, StopPonder(1);
303 else if(!strcmp(command
, "force")) computer
= NONE
, StopPonder(pondering
);
304 else if(!strcmp(command
, "go")) computer
= stm
;
305 else if(!strcmp(command
, "time")) sscanf(line
+4, "%d", &myTime
), myTime
*= 10;
306 else if(!strcmp(command
, "otim")) sscanf(line
+4, "%d", &hisTime
), hisTime
*= 10;
307 else if(!strcmp(command
, "post")) post
= 1;
308 else if(!strcmp(command
, "nopost")) post
= 0;
309 else if(!strcmp(command
, "easy")) ponder
= 0;
310 else if(!strcmp(command
, "hard")) ponder
= 1;
311 else if(!strcmp(command
, "memory")) sscanf(line
, "memory %d", &memory
);
312 else if(!strcmp(command
, "cores")) sscanf(line
, "cores %d", &cores
);
313 else if(!strcmp(command
, "sd")) sscanf(line
, "sd %d", &depth
);
314 else if(!strcmp(command
, "st")) sscanf(line
, "st %d", &sTime
), sTime
*= 1000, inc
= 0;
315 else if(!strcmp(command
, "quit")) fprintf(toE
, "quit\n"), fflush(toE
), exit(0);
320 StartEngine(char *cmdLine
, char *dir
)
323 HANDLE hChildStdinRd
, hChildStdinWr
,
324 hChildStdoutRd
, hChildStdoutWr
;
325 SECURITY_ATTRIBUTES saAttr
;
327 PROCESS_INFORMATION piProcInfo
;
328 STARTUPINFO siStartInfo
;
331 /* Set the bInheritHandle flag so pipe handles are inherited. */
332 saAttr
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
333 saAttr
.bInheritHandle
= TRUE
;
334 saAttr
.lpSecurityDescriptor
= NULL
;
336 /* Create a pipe for the child's STDOUT. */
337 if (! CreatePipe(&hChildStdoutRd
, &hChildStdoutWr
, &saAttr
, 0)) return GetLastError();
339 /* Create a pipe for the child's STDIN. */
340 if (! CreatePipe(&hChildStdinRd
, &hChildStdinWr
, &saAttr
, 0)) return GetLastError();
342 SetCurrentDirectory(dir
); // go to engine directory
344 /* Now create the child process. */
345 siStartInfo
.cb
= sizeof(STARTUPINFO
);
346 siStartInfo
.lpReserved
= NULL
;
347 siStartInfo
.lpDesktop
= NULL
;
348 siStartInfo
.lpTitle
= NULL
;
349 siStartInfo
.dwFlags
= STARTF_USESTDHANDLES
;
350 siStartInfo
.cbReserved2
= 0;
351 siStartInfo
.lpReserved2
= NULL
;
352 siStartInfo
.hStdInput
= hChildStdinRd
;
353 siStartInfo
.hStdOutput
= hChildStdoutWr
;
354 siStartInfo
.hStdError
= hChildStdoutWr
;
356 fSuccess
= CreateProcess(NULL
,
357 cmdLine
, /* command line */
358 NULL
, /* process security attributes */
359 NULL
, /* primary thread security attrs */
360 TRUE
, /* handles are inherited */
361 DETACHED_PROCESS
|CREATE_NEW_PROCESS_GROUP
,
362 NULL
, /* use parent's environment */
364 &siStartInfo
, /* STARTUPINFO pointer */
365 &piProcInfo
); /* receives PROCESS_INFORMATION */
367 if (! fSuccess
) return GetLastError();
369 // if (0) { // in the future we could trigger this by an argument
370 // SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
373 /* Close the handles we don't need in the parent */
374 CloseHandle(piProcInfo
.hThread
);
375 CloseHandle(hChildStdinRd
);
376 CloseHandle(hChildStdoutWr
);
378 process
= piProcInfo
.hProcess
;
379 pid
= piProcInfo
.dwProcessId
;
380 fromE
= (FILE*) _fdopen( _open_osfhandle((long)hChildStdoutRd
, _O_TEXT
|_O_RDONLY
), "r");
381 toE
= (FILE*) _fdopen( _open_osfhandle((long)hChildStdinWr
, _O_WRONLY
), "w");
383 char *argv
[10], *p
, buf
[200];
384 int i
, toEngine
[2], fromEngine
[2];
386 if (dir
&& dir
[0] && chdir(dir
)) { perror(dir
); exit(1); }
387 pipe(toEngine
); pipe(fromEngine
); // create two pipes
389 if ((pid
= fork()) == 0) { // Child
390 dup2(toEngine
[0], 0); close(toEngine
[0]); close(toEngine
[1]); // stdin from toE pipe
391 dup2(fromEngine
[1], 1); close(fromEngine
[0]); close(fromEngine
[1]); // stdout into fromE pipe
392 dup2(1, fileno(stderr
)); // stderr into frome pipe
394 strcpy(buf
, cmdLine
); p
= buf
;
395 for (i
=0;;) { argv
[i
++] = p
; p
= strchr(p
, ' '); if (p
== NULL
) break; *p
++ = 0; }
397 execvp(argv
[0], argv
); // startup engine
399 perror(argv
[0]); exit(1); // could not start engine; quit.
401 signal(SIGPIPE
, SIG_IGN
);
402 close(toEngine
[0]); close(fromEngine
[1]); // close engine ends of pipes in adapter
404 fromE
= (FILE*) fdopen(fromEngine
[0], "r"); // make into high-level I/O
405 toE
= (FILE*) fdopen(toEngine
[1], "w");
410 main(int argc
, char **argv
)
412 char *dir
= NULL
, *p
, *q
; int e
;
414 if(argc
== 2 && !strcmp(argv
[1], "-v")) { printf("UCI2WB " VERSION
" by H.G.Muller\n"); exit(0); }
415 if(argc
> 1 && argv
[1][0] == '-') { sc
= argv
[1][1]; argc
--; argv
++; }
416 if(argc
< 2) { printf("usage is: U%cI2WB [-s] <engine.exe> [<engine directory>]\n", sc
-32); exit(-1); }
417 if(argc
> 2) dir
= argv
[2];
418 if(argc
> 3) suffix
= argv
[3];
421 if(StartEngine(argv
[1], dir
) != NO_ERROR
) { perror(argv
[1]), exit(-1); }
423 // create separate thread to handle engine->GUI traffic
425 CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
) Engine2GUI
, (LPVOID
) NULL
, 0, &thread_id
);
427 { pthread_t t
; signal(SIGINT
, SIG_IGN
); pthread_create(&t
, NULL
, Engine2GUI
, NULL
); }
430 // handle GUI->engine traffic in original thread