Subversion Repositories Games.Chess Giants

Rev

Rev 149 | Rev 153 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 149 Rev 150
Line 2... Line 2...
2
 
2
 
3
#include "common.h"
3
#include "common.h"
4
 
-
 
5
#include <fcntl.h>
-
 
6
 
-
 
7
// definitions used in this module only
-
 
8
#define RD 0
-
 
9
#define WR 1
-
 
10
#define CLOSE_PIPE(pipe) if (pipe[RD] != -1) _close (pipe[RD]); pipe[RD] = -1; if (pipe[WR] != -1) _close (pipe[WR]); pipe[WR] = -1
-
 
11
 
4
 
12
 
5
 
13
// global variables used in this module only
6
// global variables used in this module only
14
static wchar_t chessenginemodule_path[MAX_PATH];
7
static wchar_t chessengine_path[MAX_PATH];
15
static wchar_t chessenginemodule_pathname[MAX_PATH];
8
static wchar_t chessengine_shellcommand[MAX_PATH];
16
static wchar_t chessengineinitfile_pathname[MAX_PATH];
9
static wchar_t chessengineinitfile_pathname[MAX_PATH];
17
static PROCESS_INFORMATION PlayerEngine_pi;
-
 
18
static int chessgiants_to_engine[2]; // RD and WR
-
 
19
static int engine_to_chessgiants[2]; // RD and WR
-
 
20
static int current_obstinacy;
10
static int current_obstinacy;
21
static bool is_hint_pending;
11
static bool is_hint_pending;
-
 
12
FILE *fpipe;
22
 
13
 
23
// prototypes of local functions
14
// prototypes of local functions
24
static void PlayerEngine_Recv (player_t *player);
15
static void PlayerEngine_Recv (player_t *player);
25
static void PlayerEngine_Send (player_t *player);
16
static void PlayerEngine_Send (player_t *player);
26
static wchar_t *Move_BuildString (boardmove_t *move);
17
static wchar_t *Move_BuildString (boardmove_t *move);
Line 30... Line 21...
30
{
21
{
31
   // this function starts a chess engine as a child process. This process's stdin and
22
   // this function starts a chess engine as a child process. This process's stdin and
32
   // stdout are redirected to the handles we give it, so that we may read/write to it.
23
   // stdout are redirected to the handles we give it, so that we may read/write to it.
33
 
24
 
34
   engineprogram_t *program;
25
   engineprogram_t *program;
-
 
26
   wchar_t current_path[MAX_PATH];
35
   wchar_t widechar_buffer[256];
27
   wchar_t widechar_buffer[256];
36
   SYSTEM_INFO sysinfo;
28
   SYSTEM_INFO sysinfo;
37
   HANDLE hStdOut[2] = { NULL, NULL };
-
 
38
   HANDLE hStdIn[2] = { NULL, NULL };
-
 
39
   SECURITY_ATTRIBUTES saAttr;
-
 
40
   STARTUPINFO si;
-
 
41
   int try_index;
29
   int try_index;
42
   FILE *fp;
30
   FILE *fp;
43
 
31
 
44
   // reset stuff first
32
   // reset stuff first
45
   player->wants_hint = false;
33
   player->wants_hint = false;
46
 
34
 
47
   // quick access to engine program
35
   // quick access to engine program
48
   program = &options.engine.programs[options.engine.selected_program];
36
   program = &options.engine.programs[options.engine.selected_program];
49
 
37
 
50
   // build the chess engine module path and pathname
38
   // build the chess engine module path and pathname
51
   swprintf_s (chessenginemodule_path, WCHAR_SIZEOF (chessenginemodule_path), L"%s/engines/%s", app_path, program->folder);
39
   swprintf_s (chessengine_path, WCHAR_SIZEOF (chessengine_path), L"%s/engines/%s", app_path, program->folder);
52
   swprintf_s (chessenginemodule_pathname, WCHAR_SIZEOF (chessenginemodule_pathname), L"%s/engines/%s/%s", app_path, program->folder, program->cmdline);
40
   swprintf_s (chessengine_shellcommand, WCHAR_SIZEOF (chessengine_shellcommand), L"\"%s/%s\" %s", chessengine_path, program->executable_name, program->executable_arguments);
53
 
41
 
54
   // build the I/O pipes
-
 
55
   memset (&saAttr, 0, sizeof (saAttr)); // prepare the pipes' security attributes
-
 
56
   saAttr.nLength = sizeof (SECURITY_ATTRIBUTES);
42
   // start the engine behind a bidirectional I/O pipe
57
   saAttr.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited
-
 
58
   CreatePipe (&hStdIn[RD], &hStdIn[WR], &saAttr, 0); // create a pipe for the child process's stdin
-
 
59
   CreatePipe (&hStdOut[RD], &hStdOut[WR], &saAttr, 0); // create a pipe for the child process's stdout
43
   GetCurrentDirectory (WCHAR_SIZEOF (current_path), current_path); // save current directory
60
   SetHandleInformation (hStdIn[WR], HANDLE_FLAG_INHERIT, 0); // ensure the write handle to the pipe for STDIN is not inherited
44
   SetCurrentDirectory (chessengine_path); // temporarily set chess engine's startup directory as current directory
61
   SetHandleInformation (hStdOut[RD], HANDLE_FLAG_INHERIT, 0); // ensure the read handle to the pipe for STDOUT is not inherited
-
 
62
   engine_to_chessgiants[RD] = _open_osfhandle ((intptr_t) hStdOut[RD], _O_BINARY);
-
 
63
   engine_to_chessgiants[WR] = _open_osfhandle ((intptr_t) hStdOut[WR], _O_BINARY | _O_APPEND);
-
 
64
   chessgiants_to_engine[RD] = _open_osfhandle ((intptr_t) hStdIn[RD], _O_BINARY);
-
 
65
   chessgiants_to_engine[WR] = _open_osfhandle ((intptr_t) hStdIn[WR], _O_BINARY | _O_APPEND);
-
 
66
 
-
 
67
   // spawn the chess engine process with redirected input and output
-
 
68
   memset (&si, 0, sizeof (si));
-
 
69
   si.cb = sizeof (STARTUPINFOA);
-
 
70
   si.dwFlags = STARTF_USESTDHANDLES;
-
 
71
   si.hStdInput = hStdIn[RD];
-
 
72
   si.hStdOutput = hStdOut[WR];
-
 
73
   si.hStdError = hStdOut[WR];
-
 
74
   if (!CreateProcess (chessenginemodule_pathname, // module pathname
45
   fpipe = pipe_open (chessengine_shellcommand, L"r+");
75
                       program->cmdline, // command line
-
 
76
                       NULL, NULL,
-
 
77
                       true, // handles are inherited
46
   SetCurrentDirectory (current_path); // restore current directory
78
                       CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
-
 
79
                       NULL,
47
   if (fpipe == NULL)
80
                       chessenginemodule_path, // process path
-
 
81
                       &si, // STARTUPINFO pointer
-
 
82
                       &PlayerEngine_pi)) // receives PROCESS_INFORMATION
-
 
83
   {
48
   {
84
      messagebox.hWndParent = hMainWnd;
49
      messagebox.hWndParent = hMainWnd;
85
      wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
50
      wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage"));
86
      wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ChessEngineInitializationFailed"));
51
      wcscpy_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ChessEngineInitializationFailed"));
87
      messagebox.flags = MB_ICONWARNING | MB_OK;
52
      messagebox.flags = MB_ICONWARNING | MB_OK;
Line 112... Line 77...
112
 
77
 
113
      PlayerEngine_Shutdown (player); // on error, shutdown the engine
78
      PlayerEngine_Shutdown (player); // on error, shutdown the engine
114
      return (false);
79
      return (false);
115
   }
80
   }
116
 
81
 
117
   // eventually initialize the debug log file
82
   // optionally initialize the debug log file
118
   Debug_Init (L"Chess engine output.txt");
83
   Debug_Init (L"Chess engine output.txt");
119
 
84
 
120
   // build the init file full qualified path name and try to open it
85
   // build the init file full qualified path name and try to open it
121
   swprintf_s (chessengineinitfile_pathname, WCHAR_SIZEOF (chessengineinitfile_pathname), L"%s/engines/%s/init.txt", app_path, program->folder);
86
   swprintf_s (chessengineinitfile_pathname, WCHAR_SIZEOF (chessengineinitfile_pathname), L"%s/engines/%s/init.txt", app_path, program->folder);
122
   _wfopen_s (&fp, chessengineinitfile_pathname, L"r");
87
   _wfopen_s (&fp, chessengineinitfile_pathname, L"r");
Line 143... Line 108...
143
 
108
 
144
      fclose (fp); // finished, close the file
109
      fclose (fp); // finished, close the file
145
   }
110
   }
146
 
111
 
147
   // everything's okay, set engine name as the player's name
112
   // everything's okay, set engine name as the player's name
148
   wcscpy_s (player->name, WCHAR_SIZEOF (player->name), program->name);
113
   wcscpy_s (player->name, WCHAR_SIZEOF (player->name), program->friendly_name);
149
 
114
 
150
   return (true); // finished
115
   return (true); // finished
151
}
116
}
152
 
117
 
153
 
118
 
154
void PlayerEngine_Shutdown (player_t *player)
119
void PlayerEngine_Shutdown (player_t *player)
155
{
120
{
156
   // this function terminates the chess engine process and releases all handles attached to it
121
   // this function terminates the chess engine process and releases all handles attached to it
157
 
122
 
158
   unsigned long exit_code;
-
 
159
   unsigned long handle_flags;
-
 
160
   int attempt_index;
123
   int attempt_index;
161
 
124
 
162
   // send the engine a quit command
125
   // send the engine a quit command
163
   Player_SendBuffer_Add (player, 1000, options.engine.programs[options.engine.selected_program].command_quit);
126
   Player_SendBuffer_Add (player, 1000, options.engine.programs[options.engine.selected_program].command_quit);
164
   Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
127
   Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
165
   PlayerEngine_Send (player);
128
   PlayerEngine_Send (player);
166
 
-
 
167
   // close the pipes
-
 
168
   CLOSE_PIPE (chessgiants_to_engine);
-
 
169
   CLOSE_PIPE (engine_to_chessgiants);
-
 
170
 
129
 
171
   // check 10 times if the engine process has ended cleanly
130
   // check 10 times if the engine process has ended cleanly
172
   for (attempt_index = 0; attempt_index < 10; attempt_index++)
131
   for (attempt_index = 0; attempt_index < 10; attempt_index++)
173
   {
132
   {
174
      if (GetExitCodeProcess (PlayerEngine_pi.hProcess, &exit_code) && (exit_code != STILL_ACTIVE))
133
      if (!pipe_isalive (fpipe))
175
         break; // break as soon as we've told the process exited cleanly
134
         break; // break as soon as we've told the process exited cleanly
176
 
135
 
177
      Sleep (100); // else wait one tenth second
136
      Sleep (100); // else wait one tenth second
178
   }
137
   }
179
 
138
 
180
   // has the engine NOT closen by itself yet ?
-
 
181
   if ((attempt_index == 10) && GetExitCodeProcess (PlayerEngine_pi.hProcess, &exit_code) && (exit_code == STILL_ACTIVE))
-
 
182
   {
-
 
183
      Debug_Log (L"===Engine not closen, terminating process manually===\n");
-
 
184
 
-
 
185
      // terminate the chess engine process using our smart technique ^^
139
   // close the pipe (this will terminate the child process if it hasn't terminated yet)
186
      if (!SafeTerminateProcess (PlayerEngine_pi.hProcess, 0))
-
 
187
         TerminateProcess (PlayerEngine_pi.hProcess, 0); // if process doesn't want to shutdown, kill it
-
 
188
   }
-
 
189
   else
-
 
190
      Debug_Log (L"===Engine closed cleanly===\n");
-
 
191
 
-
 
192
   if (PlayerEngine_pi.hProcess)
140
   pipe_close (fpipe);
193
      CloseHandle (PlayerEngine_pi.hProcess);
-
 
194
   PlayerEngine_pi.hProcess = NULL;
-
 
195
   if (GetHandleInformation (PlayerEngine_pi.hThread, &handle_flags))
-
 
196
      CloseHandle (PlayerEngine_pi.hThread);
-
 
197
   PlayerEngine_pi.hThread = NULL;
-
 
198
 
-
 
199
   // and finally reset the process information structure
-
 
200
   memset (&PlayerEngine_pi, 0, sizeof (PlayerEngine_pi));
-
 
201
 
141
 
-
 
142
   Debug_Log (L"===Engine closed===\n");
202
   return; // finished
143
   return; // finished
203
}
144
}
204
 
145
 
205
 
146
 
206
bool PlayerEngine_Think (player_t *player)
147
bool PlayerEngine_Think (player_t *player)
Line 359... Line 300...
359
      if (!is_hint_pending && (options.engine.blunder_chances > 0) && (rand () % 100 < options.engine.blunder_chances)
300
      if (!is_hint_pending && (options.engine.blunder_chances > 0) && (rand () % 100 < options.engine.blunder_chances)
360
          && Move_FindRandomMove (&the_board.moves[the_board.move_count - 1], player->color, &move))
301
          && Move_FindRandomMove (&the_board.moves[the_board.move_count - 1], player->color, &move))
361
      {
302
      {
362
         Player_SendBuffer_Add (player, 1000, program->command_force, Move_BuildString (&move)); // send the blunderous move to the engine
303
         Player_SendBuffer_Add (player, 1000, program->command_force, Move_BuildString (&move)); // send the blunderous move to the engine
363
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
304
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
364
         if (_wcsnicmp (program->name, L"Crafty", 6) == 0)
305
         if (_wcsnicmp (program->friendly_name, L"Crafty", 6) == 0)
365
            Player_SendBuffer_Add (player, 1000, L"disp\n");
306
            Player_SendBuffer_Add (player, 1000, L"disp\n"); // FIXME: we no longer need this Crafty-specific hack, do we?
366
         Debug_Log (L"===Discarding engine move, forcing a blunderous move (%s) instead===\n", Move_BuildString (&move)); // blunder
307
         Debug_Log (L"===Discarding engine move, forcing a blunderous move (%s) instead===\n", Move_BuildString (&move)); // blunder
367
      }
308
      }
368
 
309
 
369
      // mark the engine's selected and hovered squares
310
      // mark the engine's selected and hovered squares
370
      Board_SetSelectedAndHovered (&the_board, move.source[0], move.source[1], move.target[0], move.target[1]);
311
      Board_SetSelectedAndHovered (&the_board, move.source[0], move.source[1], move.target[0], move.target[1]);
Line 401... Line 342...
401
         Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth);
342
         Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth);
402
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
343
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
403
         Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string);
344
         Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string);
404
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
345
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
405
 
346
 
406
         if (_wcsnicmp (program->name, L"Crafty", 6) == 0)
347
         if (_wcsnicmp (program->friendly_name, L"Crafty", 6) == 0)
407
            Player_SendBuffer_Add (player, 1000, L"disp\n");
348
            Player_SendBuffer_Add (player, 1000, L"disp\n"); // FIXME: we no longer need this Crafty-specific hack, do we?
408
      }
349
      }
409
 
350
 
410
      do_update = true; // remember to update the 3D scene
351
      do_update = true; // remember to update the 3D scene
411
   }
352
   }
412
 
353
 
Line 447... Line 388...
447
      Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
388
      Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
448
 
389
 
449
      current_obstinacy = 0; // reset current obstinacy
390
      current_obstinacy = 0; // reset current obstinacy
450
      is_hint_pending = false; // no hint was requested so far
391
      is_hint_pending = false; // no hint was requested so far
451
 
392
 
452
      if (_wcsnicmp (program->name, L"Crafty", 6) == 0)
393
      if (_wcsnicmp (program->friendly_name, L"Crafty", 6) == 0)
453
         Player_SendBuffer_Add (player, 1000, L"disp\n");
394
         Player_SendBuffer_Add (player, 1000, L"disp\n"); // FIXME: we no longer need this Crafty-specific hack, do we?
454
   }
395
   }
455
 
396
 
456
   // have we been notified that players are swapping colors ? (N.B. when this happens in human vs. computer mode, it's always that the human player is *GIVING* his turn)
397
   // have we been notified that players are swapping colors ? (N.B. when this happens in human vs. computer mode, it's always that the human player is *GIVING* his turn)
457
   if (the_board.want_playerswap)
398
   if (the_board.want_playerswap)
458
   {
399
   {
Line 471... Line 412...
471
      {
412
      {
472
         // is it NOT a board setup AND has at least one move been played (meaning it was just the other's turn before) ?
413
         // is it NOT a board setup AND has at least one move been played (meaning it was just the other's turn before) ?
473
         if (!the_board.was_setup && (the_board.move_count > 1))
414
         if (!the_board.was_setup && (the_board.move_count > 1))
474
         {
415
         {
475
            Debug_Log (L"===Player just played, sending the chosen move to engine===\n");
416
            Debug_Log (L"===Player just played, sending the chosen move to engine===\n");
476
            if (_wcsnicmp (program->name, L"Crafty", 6) == 0)
417
            if (_wcsnicmp (program->friendly_name, L"Crafty", 6) == 0)
477
               Player_SendBuffer_Add (player, 1000, L"disp\n");
418
               Player_SendBuffer_Add (player, 1000, L"disp\n"); // FIXME: we no longer need this Crafty-specific hack, do we?
478
 
419
 
479
            // instruct it about its allowed search depth BEFORE each move (this ensures engine will be "ready" to handle the command)
420
            // instruct it about its allowed search depth BEFORE each move (this ensures engine will be "ready" to handle the command)
480
            // then build the move string, and send the move string to the engine
421
            // then build the move string, and send the move string to the engine
481
            Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth);
422
            Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth);
482
            Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
423
            Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
Line 534... Line 475...
534
         Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth);
475
         Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth);
535
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
476
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
536
         Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string);
477
         Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string);
537
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
478
         Player_SendBuffer_Add (player, 1000, L"\n"); // since the format string was read from the options, don't forget to end it with a carriage return
538
 
479
 
539
         if (_wcsnicmp (program->name, L"Crafty", 6) == 0)
480
         if (_wcsnicmp (program->friendly_name, L"Crafty", 6) == 0)
540
            Player_SendBuffer_Add (player, 1000, L"disp\n");
481
            Player_SendBuffer_Add (player, 1000, L"disp\n"); // FIXME: we no longer need this Crafty-specific hack, do we?
541
 
482
 
542
         do_update = true; // remember to update the 3D scene
483
         do_update = true; // remember to update the 3D scene
543
      }
484
      }
544
   }
485
   }
545
 
486
 
Line 563... Line 504...
563
   // get reception buffer's initial end position
504
   // get reception buffer's initial end position
564
   initial_pos = wcslen (player->recvbuffer);
505
   initial_pos = wcslen (player->recvbuffer);
565
   length = initial_pos;
506
   length = initial_pos;
566
 
507
 
567
   // as long as the pipe reports us there is data to read, read one byte at a time
508
   // as long as the pipe reports us there is data to read, read one byte at a time
568
   while (!_eof (engine_to_chessgiants[RD]))
509
   while (pipe_hasdata (fpipe))
569
   {
510
   {
570
      read_count = _read (engine_to_chessgiants[RD], player->ascii_recvbuffer, 1);
511
      read_count = pipe_read (fpipe, player->ascii_recvbuffer, 1);
571
      if (read_count == 0)
512
      if (read_count == 0)
572
         break; // read the pipe; if it fails, stop trying
513
         break; // read the pipe; if it fails, stop trying
573
      if ((player->ascii_recvbuffer[0] == '\r') || (player->ascii_recvbuffer[0] == '%'))
514
      if ((player->ascii_recvbuffer[0] == '\r') || (player->ascii_recvbuffer[0] == '%'))
574
         continue; // ignore carriage returns and percent signs
515
         continue; // ignore carriage returns and percent signs
575
 
516
 
Line 606... Line 547...
606
   line_pointer = player->sendbuffer; // start at the first character
547
   line_pointer = player->sendbuffer; // start at the first character
607
   while ((line_pointer = wcsgets (widechar_buffer, WCHAR_SIZEOF (widechar_buffer), line_pointer)) != NULL)
548
   while ((line_pointer = wcsgets (widechar_buffer, WCHAR_SIZEOF (widechar_buffer), line_pointer)) != NULL)
608
   {
549
   {
609
      wcscat_s (widechar_buffer, WCHAR_SIZEOF (widechar_buffer), L"\n"); // put the carriage return back
550
      wcscat_s (widechar_buffer, WCHAR_SIZEOF (widechar_buffer), L"\n"); // put the carriage return back
610
      ConvertTo7BitASCII (ascii_buffer, sizeof (ascii_buffer), widechar_buffer); // convert to ASCII
551
      ConvertTo7BitASCII (ascii_buffer, sizeof (ascii_buffer), widechar_buffer); // convert to ASCII
611
      amount_written = _write (chessgiants_to_engine[WR], ascii_buffer, strlen (ascii_buffer)); // send data
552
      amount_written = pipe_write (fpipe, ascii_buffer, strlen (ascii_buffer)); // send data
612
   }
553
   }
613
 
554
 
614
   player->sendbuffer[0] = 0; // what we had to send has been sent, reset the send buffer
555
   player->sendbuffer[0] = 0; // what we had to send has been sent, reset the send buffer
615
   player->sendbuffer_locked = false; // and unlock it
556
   player->sendbuffer_locked = false; // and unlock it
616
 
557