1 /* engine.c
2
3 GNU Chess frontend
4
5 Copyright (C) 2001-2021 Free Software Foundation, Inc.
6
7 GNU Chess is based on the two research programs
8 Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23 Contact Info:
24 bug-gnu-chess@gnu.org
25 cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
26 */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <signal.h>
32 #include <errno.h>
33 #include <assert.h>
34 #include <unistd.h>
35 #include <math.h>
36
37 #include "common.h"
38 #include "components.h"
39 #include "gettext.h"
40
41 #define _(str) gettext (str)
42
43 /* Message to be sent to the engine */
44 char inputstr[BUF_SIZE]="";
45
46 /* Message to be sent to the engine */
47 char dataToEngine[BUF_SIZE]="";
48
49 /* Whether an answer is expected from the engine or not, as a result of a command send to it. */
50 static int answerFromEngineExpected=false;
51
52 /* A BUF_SIZE long char string made up of null characters */
53 static char zerochar[BUF_SIZE];
54
55 /* Buffer storing the input entered by the user */
56 char userinputbuf[BUF_SIZE]="";
57
58 /* Buffer storing the input coming from the engine */
59 static char engineinputbuf[BUF_SIZE]="";
60
61 /* Whether the input is a move or not (if not, it is expected to be a command). */
62 static int userInputIsAMove=0;
63
64 /* Flags that the color must be changed */
65 static int changeColor=false;
66
67 /* Flags whether a go command must be sent after a user move. */
68 static int autoGo=false;
69
70 /*
71 * Forward declaration of functions
72 */
73
74 int SendToEngine( char msg[] );
75 static int GetNextLine( char buf[], char line[] );
76 static int GetDataToEngine( char data[] );
77 static int AnswerFromEngineExpected( void );
78 static int UserInputIsAValidMove( void );
79 void InitInputThread(void);
80 void input_wakeup( void );
81
82 /*
83 * Initializes data used in the frontend.
84 */
InitFrontend(void)85 void InitFrontend( void )
86 {
87 /* Initialized a null char string that will be used later on. */
88 int i;
89 for ( i=0; i<BUF_SIZE; ++i )
90 zerochar[i] = '\0';
91 InitInput();
92 InitInputThread();
93 }
94
95 /*
96 * Sends a char string message from the frontend to the adapter/engine.
97 * The message must be a command or a move.
98 */
SendToEngine(char msg[])99 int SendToEngine( char msg[] )
100 {
101 int outError=0;
102 int msg_size = strlen( msg );
103 int msg_count=0;
104
105 /* The trailing '\n' is necessary. Otherwise, Polyglot will not realise of
106 the new message. TODO: This should be improved. */
107 msg[msg_size] = '\n';
108 msg[msg_size+1] = '\0';
109
110 msg_count = write( pipefd_f2a[1], msg, msg_size+1 );
111
112 if (msg_count == -1)
113 outError = errno;
114 else
115 outError = 0;
116
117 if ( ( msg_count == msg_size+1 ) && ( outError == 0 ) ) {
118 return 1;
119 } else {
120 printf( "Error sending message to engine.\n" );
121 assert( 0 );
122 return 0;
123 }
124 }
125
126 /*
127 * Reads a char string message from the adapter/engine.
128 * The message must be a command or a move.
129 * The message is not processed straight away, but saved in buffer 'engineinputbuf'.
130 * The buffer will be read later from function nextEngineCmd();
131 */
ReadFromEngine(void)132 int ReadFromEngine( void )
133 {
134
135 int nread=0;
136 fd_set set[1];
137 struct timeval time_val[1];
138 int engineinputready=0;
139 char engineinputaux[BUF_SIZE]="";
140
141 /* Poll input from engine in non-blocking mode */
142 FD_ZERO(set);
143 FD_SET( pipefd_a2f[0],set);
144 time_val->tv_sec = 0;
145 time_val->tv_usec = 0;
146 engineinputready = select( pipefd_a2f[0]+1, set, NULL, NULL, time_val );
147
148 if ( engineinputready == -1 ) {
149 printf( "Error reading engine input.\n" );
150 } else if ( engineinputready > 0 ) {
151 /* There are some data from the engine. Store it in buffer */
152 strncpy( engineinputaux, zerochar, BUF_SIZE );
153 nread = read( pipefd_a2f[0], engineinputaux, BUF_SIZE );
154 /*write( STDOUT_FILENO, engineinputaux, BUF_SIZE );*/
155 strcat( engineinputbuf, engineinputaux );
156 engineinputbuf[strlen( engineinputbuf ) + nread] = '\0';
157 }
158
159 return ( engineinputready );
160 }
161
162 /*
163 * Reads a char string entered by the user.
164 * The string must be a command or a move.
165 * The string is not processed straight away, but saved in buffer 'userinputbuf'.
166 * The buffer will be read later from function nextUserCmd();
167 */
ReadFromUser(void)168 void ReadFromUser( void )
169 {
170 int nread=0;
171 fd_set set[1];
172 struct timeval time_val[1];
173 int userinputready=0;
174 char userinputaux[BUF_SIZE]="";
175
176 /* Poll input from user in non-blocking mode */
177 FD_ZERO(set);
178 FD_SET(pipefd_i2f[0],set);
179 time_val->tv_sec = 0;
180 time_val->tv_usec = 0;
181 userinputready = select( pipefd_i2f[0]+1, set, NULL, NULL, time_val );
182
183 if ( userinputready == -1 ) {
184 printf( "Error reading user input.\n" );
185 } else if ( userinputready > 0 ) {
186 /* There are some data from the user. Store it in buffer */
187 strncpy( userinputaux, zerochar, BUF_SIZE );
188 nread = read( pipefd_i2f[0], userinputaux, BUF_SIZE );
189 strcat( userinputbuf, userinputaux );
190 userinputbuf[strlen( userinputbuf ) + nread] = '\0';
191 }
192 }
193
194 /*
195 * Saves in global variable 'dataToEngine' the data to be sent to the engine.
196 */
SetDataToEngine(const char data[])197 void SetDataToEngine( const char data[] )
198 {
199 strcpy( dataToEngine, data );
200 }
201
202 /*
203 * Writes in the (output) parameter the data that must be sent to the engine.
204 *
205 * Return value:
206 * 1 - if there are some data to be sent
207 * 0 - if there are no data to be sent
208 */
GetDataToEngine(char data[])209 static int GetDataToEngine( char data[] )
210 {
211 if ( strcmp( dataToEngine, "" ) == 0 ) {
212 strcpy( data, "" );
213 return 0;
214 } else {
215 strcpy( data, dataToEngine );
216 return 1;
217 }
218 }
219
220 /*
221 * Stores in a global flag variable whether an answer is expected from
222 * the engine (1) or not (0).
223 */
ExpectAnswerFromEngine(int answerExpected)224 void ExpectAnswerFromEngine( int answerExpected )
225 {
226 answerFromEngineExpected = answerExpected;
227 }
228
229 /*
230 * Return value:
231 * 1 - an answer is expected from the engine
232 * 0 - an answer is not expected from the engine
233 */
AnswerFromEngineExpected(void)234 static int AnswerFromEngineExpected( void )
235 {
236 return ( answerFromEngineExpected );
237 }
238
239 /*
240 * Extracts a command from the user input buffer.
241 *
242 * The command is removed from the buffer.
243 * If the command must be sent to the engine, it is sent.
244 * This function relies on parse_input().
245 */
NextUserCmd(void)246 void NextUserCmd( void )
247 {
248 char userinput[BUF_SIZE]="";
249
250 if ( strlen( userinputbuf ) > 0 ) {
251 if ( flags & XBOARD ) {
252 fflush( stdout );
253 }
254 if ( GetNextLine( userinputbuf, userinput ) > 0 ) {
255 strcpy( inputstr, "" );
256 strcpy( inputstr, userinput );
257 dbg_printf( "USER >: %s\n", userinput );
258 parse_input();
259 /* Check if command/move must be sent to engine */
260 if ( GetDataToEngine( dataToEngine ) ) {
261 dbg_printf( "> ENGINE: %s\n", dataToEngine );
262 SendToEngine( dataToEngine );
263 if ( GetAutoGo() && UserInputIsAValidMove() ) {
264 strcpy( userinputbuf, "go\n" );
265 SetAutoGo( false );
266 }
267 }
268 /* Check if command was entered in manual mode */
269 if ( (flags & MANUAL) && UserInputIsAValidMove() ) {
270 RealGameCnt = GameCnt;
271 RealSide = board.side;
272 }
273 /* Check if the color must be changed, e.g. after an undo command. */
274 if ( changeColor ) {
275 RealGameCnt = GameCnt;
276 RealSide = board.side;
277 }
278 if ( !AnswerFromEngineExpected() || (flags & MANUAL) ) {
279 input_wakeup();
280 }
281 }
282 }
283 }
284
285 /*
286 * Extracts a command from the engine input buffer.
287 *
288 * The command is removed from the buffer.
289 * If the command is a move, the move is made.
290 */
NextEngineCmd(void)291 void NextEngineCmd( void )
292 {
293 char engineinput[BUF_SIZE]="";
294 char enginemovestr[BUF_SIZE]="";
295 leaf* enginemove;
296
297 if ( strlen( engineinputbuf ) > 0 ) {
298 if ( GetNextLine( engineinputbuf, engineinput ) > 0 ) {
299 dbg_printf("< ENGINE: %s\n", engineinput);
300 if ( strncmp( engineinput, "move", 4 ) == 0 ) {
301 /* Input from engine is a move */
302 sscanf( engineinput, "move %s", enginemovestr );
303 enginemove = ValidateMove( enginemovestr );
304 if ( enginemove == (leaf *) NULL ) {
305 dbg_printf( "Bad move from engine\n" );
306 } else {
307 dbg_printf( "Engine move: <%s> (%d,%d)\n", enginemovestr,
308 (enginemove!=NULL ? enginemove->move : -1),
309 (enginemove!=NULL ? enginemove->score : -1) );
310 SANMove (enginemove->move, 1);
311 MakeMove( board.side, &(enginemove->move) );
312 strcpy (Game[GameCnt].SANmv, SANmv);
313 if ( !(flags & XBOARD) ) {
314 ShowBoard();
315 dbg_printf("USER <: My move is : %s\n", SANmv);
316 printf( _("\nMy move is : %s\n"), SANmv );
317 fflush( stdout );
318 } else {
319 dbg_printf("USER <: %d. ... %s\n", GameCnt/2 + 1, enginemovestr );
320 printf ("%d. ... %s\n", GameCnt/2 + 1, enginemovestr );
321 fflush( stdout );
322 dbg_printf("USER <: My move is : %s\n", enginemovestr);
323 printf( "My move is : %s\n", enginemovestr );
324 fflush( stdout );
325 }
326 RealGameCnt = GameCnt;
327 /* Check if the color must be changed, e.g. after a go command. */
328 if ( changeColor ) {
329 RealGameCnt = GameCnt;
330 RealSide = board.side;
331 }
332 input_wakeup();
333 }
334 } else {
335 dbg_printf( "USER <: %s\n",engineinput );
336 printf( "%s", engineinput );
337 if ( flags & XBOARD ) {
338 fflush( stdout );
339 }
340 if ( AnswerFromEngineExpected() ) {
341 input_wakeup();
342 }
343 }
344 }
345 }
346 }
347
348 /*
349 * Stores in a global flag variable whether an input from the user is a
350 * valid move (1) or not (0).
351 */
SetUserInputValidMove(int valid)352 void SetUserInputValidMove( int valid )
353 {
354 userInputIsAMove = valid;
355 }
356
357 /*
358 * Return value:
359 * 1 - user input is a valid move
360 * 0 - user input is not a valid move
361 */
UserInputIsAValidMove(void)362 static int UserInputIsAValidMove(void)
363 {
364 return ( userInputIsAMove );
365 }
366
367 /*
368 * Extracts a line from 'buf' and copies it to 'line'.
369 *
370 * The end-of-line character is also copied.
371 */
GetNextLine(char buf[],char line[])372 static int GetNextLine( char buf[], char line[] )
373 {
374 char bufaux[BUF_SIZE]="";
375 unsigned int i=0;
376 int found=0;
377
378 for ( i=0; i<strlen( buf ); ++i ) {
379 line[i] = buf[i];
380 line[i+1] = '\0';
381 if ( line[i] == '\n' ) {
382 found = 1;
383 strcpy( bufaux, buf );
384 strcpy( buf, zerochar );
385 if ( strlen( &(bufaux[i+1]) ) > 0 ) {
386 strcpy( buf, &(bufaux[i+1]) );
387 }
388 break;
389 }
390 }
391 return found;
392 }
393
394 /*
395 * Gets a line from 'buf' and copies it to 'line'.
396 * Unlike GetNextLine, it will not remove the line from the
397 * original buffer.
398 *
399 * The end-of-line character is also copied.
400 */
GetNextLineNoRemove(char buf[],char line[])401 static int GetNextLineNoRemove( char buf[], char line[] )
402 {
403 unsigned int i=0;
404 int found=0;
405
406 for ( i=0; i<strlen( buf ); ++i ) {
407 line[i] = buf[i];
408 line[i+1] = '\0';
409 if ( line[i] == '\n' ) {
410 found = 1;
411 break;
412 }
413 }
414 return found;
415 }
416
417 /*
418 * Flags whether the color must be changed, e.g. due to an undo command.
419 */
ChangeColor(int change)420 void ChangeColor( int change )
421 {
422 changeColor = change;
423 }
424
425 /*
426 * Sets the autoGo flag, meaning that after a user move a go command will be
427 * sent to the engine. This may be necessary after an undo.
428 */
SetAutoGo(int go)429 void SetAutoGo( int go )
430 {
431 autoGo = go;
432 }
433
434 /*
435 * Checks whether the autoGo flag is set or not.
436 */
GetAutoGo(void)437 int GetAutoGo( void )
438 {
439 return autoGo;
440 }
441
442 /*
443 * Solves a position given in an EPD file.
444 */
SolvePosition(char move[],const char position[])445 void SolvePosition( char move[], const char position[] )
446 {
447 char msg[BUF_SIZE]="";
448 char engineinput[BUF_SIZE]="";
449 int solved = 0;
450
451 /* TODO Translatable or not? */
452 printf( "\nSolve position:\n\t%s\n", position );
453
454 /* Send position to adapter/engine. */
455 strcpy( msg, "setboard " );
456 strcat( msg, position );
457 msg[strlen(msg)-1] = '\0';
458 SendToEngine( msg );
459
460 /* Set adapter/engine to analyse. */
461 sprintf( msg, "st %d\ngo", (int)round( SearchTime ) );
462 SendToEngine( msg );
463
464 /* Read adapter/engine's answer (the move). */
465 while ( ( ! ReadFromEngine() ) || ( ! solved ) ) {
466 if ( strlen( engineinputbuf ) > 0 ) {
467 GetNextLineNoRemove( engineinputbuf, engineinput );
468 assert( strlen( engineinput ) > 0 );
469 if ( strncmp( engineinput, "move", 4 ) == 0 ) {
470 solved = 1;
471 break;
472 } else {
473 GetNextLine( engineinputbuf, engineinput );
474 printf( "%s\n", engineinput );
475 }
476 }
477 sleep( 0 );
478 }
479 NextEngineCmd();
480
481 strcpy( move, SANmv );
482 }
483
484 /*
485 * Reads a char string entered by the user.
486 * The string must be a command or a move.
487 * The string is sent to the engine straight away.
488 */
ForwardUserInputToEngine(void)489 void ForwardUserInputToEngine( void )
490 {
491 int nread=0;
492 fd_set set[1];
493 struct timeval time_val[1];
494 int userinputready=0;
495 char userinputaux[BUF_SIZE]="";
496
497 /* Poll input from user in non-blocking mode */
498 FD_ZERO(set);
499 FD_SET(STDIN_FILENO,set);
500 time_val->tv_sec = 0;
501 time_val->tv_usec = 0;
502 userinputready = select( STDIN_FILENO+1, set, NULL, NULL, time_val );
503
504 if ( userinputready == -1 ) {
505 printf( "Error reading user input.\n" );
506 } else if ( userinputready > 0 ) {
507 /* There are some data from the user. Read the data */
508 strncpy( userinputaux, zerochar, BUF_SIZE );
509 nread = read( STDIN_FILENO, userinputaux, BUF_SIZE );
510 /* Send the data to the engine */
511 assert( nread+1 < BUF_SIZE-1 );
512 if ( strcmp(userinputaux,"quit") == 0 || strcmp(userinputaux,"quit\n") == 0 ) {
513 SET (flags, QUIT);
514 }
515 userinputaux[nread] = '\n';
516 userinputaux[nread+1] = '\0';
517 int outError=0;
518 int msg_count=0;
519 msg_count = write( pipefd_a2e[1], userinputaux, nread+1 );
520 if (msg_count == -1)
521 outError = errno;
522 else
523 outError = 0;
524 if ( ! ( ( msg_count == nread+1 ) && ( outError == 0 ) ) ) {
525 printf( "Error sending message to engine.\n" );
526 assert( 0 );
527 }
528 }
529 }
530
531 /*
532 * Reads a char string message from the engine.
533 * The message is output'd straight away.
534 */
ForwardEngineOutputToUser(void)535 void ForwardEngineOutputToUser( void )
536 {
537
538 int nread=0;
539 fd_set set[1];
540 struct timeval time_val[1];
541 int engineinputready=0;
542 char engineinputaux[BUF_SIZE+1]="";
543
544 /* Poll input from engine in non-blocking mode */
545 FD_ZERO(set);
546 FD_SET( pipefd_e2a[0],set);
547 time_val->tv_sec = 0;
548 time_val->tv_usec = 0;
549 engineinputready = select( pipefd_e2a[0]+1, set, NULL, NULL, time_val );
550
551 if ( engineinputready == -1 ) {
552 printf( "Error reading engine input.\n" );
553 } else if ( engineinputready > 0 ) {
554 /* There are some data from the engine. Read the data */
555 strncpy( engineinputaux, zerochar, BUF_SIZE+1 );
556 nread = read( pipefd_e2a[0], engineinputaux, BUF_SIZE+1 );
557 /* Write data to output */
558 assert( nread <= BUF_SIZE+1 );
559 if (nread < BUF_SIZE+1) {
560 engineinputaux[nread] = '\0';
561 } else {
562 engineinputaux[BUF_SIZE] = '\0';
563 }
564 ssize_t r = write( STDOUT_FILENO, engineinputaux, nread );
565 if ( r == -1 ) {
566 printf( "Error sending message to engine.\n" );
567 }
568 }
569 }
570