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