Rev 116 | Rev 130 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 116 | Rev 119 | ||
---|---|---|---|
Line 24... | Line 24... | ||
24 | bool PlayerEngine_Init (player_t *player) |
24 | bool PlayerEngine_Init (player_t *player) |
25 | { |
25 | { |
26 | // this function starts a chess engine as a child process. This process's stdin and |
26 | // this function starts a chess engine as a child process. This process's stdin and |
27 | // stdout are redirected to the handles we give it, so that we may read/write to it. |
27 | // stdout are redirected to the handles we give it, so that we may read/write to it. |
28 | 28 | ||
- | 29 | engineprogram_t *program; |
|
29 | wchar_t widechar_buffer[256]; |
30 | wchar_t widechar_buffer[256]; |
30 | SYSTEM_INFO sysinfo; |
31 | SYSTEM_INFO sysinfo; |
31 | SECURITY_ATTRIBUTES saAttr; |
32 | SECURITY_ATTRIBUTES saAttr; |
32 | STARTUPINFO si; |
33 | STARTUPINFO si; |
33 | int try_index; |
34 | int try_index; |
Line 38... | Line 39... | ||
38 | hChessEngineStdinWr = NULL; |
39 | hChessEngineStdinWr = NULL; |
39 | hChessEngineStdoutRd = NULL; |
40 | hChessEngineStdoutRd = NULL; |
40 | hChessEngineStdoutWr = NULL; |
41 | hChessEngineStdoutWr = NULL; |
41 | player->wants_hint = false; |
42 | player->wants_hint = false; |
42 | 43 | ||
43 | // |
44 | // quick access to engine program |
44 |
|
45 | program = &options.engine.programs[options.engine.selected_program]; |
45 | 46 | ||
46 | // build the chess engine module path and pathname |
47 | // build the chess engine module path and pathname |
47 | swprintf_s (chessenginemodule_path, WCHAR_SIZEOF (chessenginemodule_path), L"%s/engines/%s", app_path, |
48 | swprintf_s (chessenginemodule_path, WCHAR_SIZEOF (chessenginemodule_path), L"%s/engines/%s", app_path, program->folder); |
48 | swprintf_s (chessenginemodule_pathname, WCHAR_SIZEOF (chessenginemodule_pathname), L"%s/engines/%s/%s", app_path, |
49 | swprintf_s (chessenginemodule_pathname, WCHAR_SIZEOF (chessenginemodule_pathname), L"%s/engines/%s/%s", app_path, program->folder, program->cmdline); |
49 | 50 | ||
50 | // prepare the pipes' security attributes |
51 | // prepare the pipes' security attributes |
51 | memset (&saAttr, 0, sizeof (saAttr)); |
52 | memset (&saAttr, 0, sizeof (saAttr)); |
52 | saAttr.nLength = sizeof (SECURITY_ATTRIBUTES); |
53 | saAttr.nLength = sizeof (SECURITY_ATTRIBUTES); |
53 | saAttr.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited |
54 | saAttr.bInheritHandle = true; // set the bInheritHandle flag so pipe handles are inherited |
Line 66... | Line 67... | ||
66 | si.dwFlags = STARTF_USESTDHANDLES; |
67 | si.dwFlags = STARTF_USESTDHANDLES; |
67 | si.hStdInput = hChessEngineStdinRd; |
68 | si.hStdInput = hChessEngineStdinRd; |
68 | si.hStdOutput = hChessEngineStdoutWr; |
69 | si.hStdOutput = hChessEngineStdoutWr; |
69 | si.hStdError = hChessEngineStdoutWr; |
70 | si.hStdError = hChessEngineStdoutWr; |
70 | if (!CreateProcess (chessenginemodule_pathname, // module pathname |
71 | if (!CreateProcess (chessenginemodule_pathname, // module pathname |
71 |
|
72 | program->cmdline, // command line |
72 | NULL, NULL, |
73 | NULL, NULL, |
73 | true, // handles are inherited |
74 | true, // handles are inherited |
74 | CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, |
75 | CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, |
75 | NULL, |
76 | NULL, |
76 | chessenginemodule_path, // process path |
77 | chessenginemodule_path, // process path |
Line 100... | Line 101... | ||
100 | // has the engine process not spoken yet ? |
101 | // has the engine process not spoken yet ? |
101 | if (player->recvbuffer[0] == 0) |
102 | if (player->recvbuffer[0] == 0) |
102 | { |
103 | { |
103 | messagebox.hWndParent = hMainWnd; |
104 | messagebox.hWndParent = hMainWnd; |
104 | wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); |
105 | wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"ImportantMessage")); |
105 |
|
106 | swprintf_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"Error_ChessEngineInitializationFailed"), program->folder); |
106 | messagebox.flags = MB_ICONWARNING | MB_OK; |
107 | messagebox.flags = MB_ICONWARNING | MB_OK; |
107 | DialogBox_Message (&messagebox); // display a modeless error message box |
108 | DialogBox_Message (&messagebox); // display a modeless error message box |
108 | 109 | ||
109 | PlayerEngine_Shutdown (player); // on error, shutdown the engine |
110 | PlayerEngine_Shutdown (player); // on error, shutdown the engine |
110 | return (false); |
111 | return (false); |
Line 112... | Line 113... | ||
112 | 113 | ||
113 | // eventually initialize the debug log file |
114 | // eventually initialize the debug log file |
114 | Debug_Init (L"Chess engine output.txt"); |
115 | Debug_Init (L"Chess engine output.txt"); |
115 | 116 | ||
116 | // build the init file full qualified path name and try to open it |
117 | // build the init file full qualified path name and try to open it |
117 | swprintf_s (chessengineinitfile_pathname, WCHAR_SIZEOF (chessengineinitfile_pathname), L"%s/engines/%s/init.txt", app_path, |
118 | swprintf_s (chessengineinitfile_pathname, WCHAR_SIZEOF (chessengineinitfile_pathname), L"%s/engines/%s/init.txt", app_path, program->folder); |
118 | _wfopen_s (&fp, chessengineinitfile_pathname, L"r"); |
119 | _wfopen_s (&fp, chessengineinitfile_pathname, L"r"); |
119 | 120 | ||
120 | // could the init file be opened ? |
121 | // could the init file be opened ? |
121 | if (fp != NULL) |
122 | if (fp != NULL) |
122 | { |
123 | { |
Line 139... | Line 140... | ||
139 | 140 | ||
140 | fclose (fp); // finished, close the file |
141 | fclose (fp); // finished, close the file |
141 | } |
142 | } |
142 | 143 | ||
143 | // everything's okay, set engine name as the player's name |
144 | // everything's okay, set engine name as the player's name |
144 | wcscpy_s (player->name, WCHAR_SIZEOF (player->name), |
145 | wcscpy_s (player->name, WCHAR_SIZEOF (player->name), program->name); |
145 | 146 | ||
146 | return (true); // finished |
147 | return (true); // finished |
147 | } |
148 | } |
148 | 149 | ||
149 | 150 | ||
Line 154... | Line 155... | ||
154 | unsigned long exit_code; |
155 | unsigned long exit_code; |
155 | unsigned long handle_flags; |
156 | unsigned long handle_flags; |
156 | int attempt_index; |
157 | int attempt_index; |
157 | 158 | ||
158 | // send the engine a quit command |
159 | // send the engine a quit command |
159 | Player_SendBuffer_Add (player, 1000, options.engine. |
160 | Player_SendBuffer_Add (player, 1000, options.engine.programs[options.engine.selected_program].command_quit); |
160 | 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 |
161 | 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 |
161 | PlayerEngine_Send (player); |
162 | PlayerEngine_Send (player); |
162 | 163 | ||
163 | // close the pipe handles |
164 | // close the pipe handles |
164 | if (hChessEngineStdinRd) |
165 | if (hChessEngineStdinRd) |
Line 211... | Line 212... | ||
211 | 212 | ||
212 | bool PlayerEngine_Think (player_t *player) |
213 | bool PlayerEngine_Think (player_t *player) |
213 | { |
214 | { |
214 | // this function reads and writes any necessary data from and to the chess engine. Returns TRUE if scene needs to be updated. |
215 | // this function reads and writes any necessary data from and to the chess engine. Returns TRUE if scene needs to be updated. |
215 | 216 | ||
- | 217 | engineprogram_t *program; |
|
216 | wchar_t line_buffer[256]; |
218 | wchar_t line_buffer[256]; |
217 | wchar_t *line_pointer; |
219 | wchar_t *line_pointer; |
218 | wchar_t *move_string; |
220 | wchar_t *move_string; |
219 | int char_index; |
221 | int char_index; |
220 | int length; |
222 | int length; |
221 | boardmove_t move; |
223 | boardmove_t move; |
222 | player_t *current_player; |
224 | player_t *current_player; |
223 | player_t *opposite_player; |
225 | player_t *opposite_player; |
224 | bool do_update; |
226 | bool do_update; |
- | 227 | ||
- | 228 | // quick access to chess engine program |
|
- | 229 | program = &options.engine.programs[options.engine.selected_program]; |
|
225 | 230 | ||
226 | // don't update the scene until told otherwise |
231 | // don't update the scene until told otherwise |
227 | do_update = false; |
232 | do_update = false; |
228 | 233 | ||
229 | // get current and opposite players |
234 | // get current and opposite players |
Line 331... | Line 336... | ||
331 | // has the game ended already ? |
336 | // has the game ended already ? |
332 | if (the_board.game_state > STATE_PLAYING) |
337 | if (the_board.game_state > STATE_PLAYING) |
333 | continue; // ignore all that the engine tells us. Game is over already. |
338 | continue; // ignore all that the engine tells us. Game is over already. |
334 | 339 | ||
335 | // else is it a normal move ? |
340 | // else is it a normal move ? |
336 | else if ((move_string = wcsstr (line_buffer, |
341 | else if ((move_string = wcsstr (line_buffer, program->replystring_move)) != NULL) |
337 | move_string += wcslen ( |
342 | move_string += wcslen (program->replystring_move); // go to the parsable data |
338 | 343 | ||
339 | // else it's any other sort of line |
344 | // else it's any other sort of line |
340 | else |
345 | else |
341 | continue; // skip lines that don't contain any valid move data |
346 | continue; // skip lines that don't contain any valid move data |
342 | 347 | ||
Line 359... | Line 364... | ||
359 | 364 | ||
360 | // is it NOT a hint, are blunders allowed, should we do one now AND can we do one now ? |
365 | // is it NOT a hint, are blunders allowed, should we do one now AND can we do one now ? |
361 | if (!is_hint_pending && (options.engine.blunder_chances > 0) && (rand () % 100 < options.engine.blunder_chances) |
366 | if (!is_hint_pending && (options.engine.blunder_chances > 0) && (rand () % 100 < options.engine.blunder_chances) |
362 | && Move_FindRandomMove (&the_board.moves[the_board.move_count - 1], player->color, &move)) |
367 | && Move_FindRandomMove (&the_board.moves[the_board.move_count - 1], player->color, &move)) |
363 | { |
368 | { |
364 | Player_SendBuffer_Add (player, 1000, |
369 | Player_SendBuffer_Add (player, 1000, program->command_force, Move_BuildString (&move)); // send the blunderous move to the engine |
365 | 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 |
370 | 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 |
366 | if ( |
371 | if (_wcsnicmp (program->name, L"Crafty", 6) == 0) |
367 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
372 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
368 | Debug_Log (L"===Discarding engine move, forcing a blunderous move (%s) instead===\n", Move_BuildString (&move)); // blunder |
373 | Debug_Log (L"===Discarding engine move, forcing a blunderous move (%s) instead===\n", Move_BuildString (&move)); // blunder |
369 | } |
374 | } |
370 | 375 | ||
371 | // mark the engine's selected and hovered squares |
376 | // mark the engine's selected and hovered squares |
Line 399... | Line 404... | ||
399 | Debug_Log (L"===Hint received, rebuilding board and telling engine to backup 1 move===\n"); |
404 | Debug_Log (L"===Hint received, rebuilding board and telling engine to backup 1 move===\n"); |
400 | Debug_Log (L"===setting up board using FEN string===\n"); |
405 | Debug_Log (L"===setting up board using FEN string===\n"); |
401 | 406 | ||
402 | // instruct it about its allowed search depth BEFORE each table set (this ensures engine will be "ready" to handle the command) |
407 | // instruct it about its allowed search depth BEFORE each table set (this ensures engine will be "ready" to handle the command) |
403 | // then get the current game state in FEN format and feed it to the engine |
408 | // then get the current game state in FEN format and feed it to the engine |
404 | Player_SendBuffer_Add (player, 1000, |
409 | Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth); |
405 | 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 |
410 | 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 |
406 | Player_SendBuffer_Add (player, 1000, |
411 | Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string); |
407 | 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 |
412 | 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 |
408 | 413 | ||
409 | if ( |
414 | if (_wcsnicmp (program->name, L"Crafty", 6) == 0) |
410 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
415 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
411 | } |
416 | } |
412 | 417 | ||
413 | do_update = true; // remember to update the 3D scene |
418 | do_update = true; // remember to update the 3D scene |
414 | } |
419 | } |
Line 434... | Line 439... | ||
434 | if (the_board.was_setup) |
439 | if (the_board.was_setup) |
435 | { |
440 | { |
436 | Debug_Log (L"===Got board setup notification from interface===\n"); |
441 | Debug_Log (L"===Got board setup notification from interface===\n"); |
437 | 442 | ||
438 | // send a new game command to the chess engine |
443 | // send a new game command to the chess engine |
439 | Player_SendBuffer_Add (player, 1000, |
444 | Player_SendBuffer_Add (player, 1000, program->command_new); |
440 | 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 |
445 | 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 |
441 | 446 | ||
442 | // just set up the board from its Forsyth-Edwards notation |
447 | // just set up the board from its Forsyth-Edwards notation |
443 | Debug_Log (L"===setting up board using FEN string===\n"); |
448 | Debug_Log (L"===setting up board using FEN string===\n"); |
444 | 449 | ||
445 | // instruct it about its allowed search depth BEFORE each table set (this ensures engine will be "ready" to handle the command) |
450 | // instruct it about its allowed search depth BEFORE each table set (this ensures engine will be "ready" to handle the command) |
446 | // then get the current game state in FEN format and feed it to the engine |
451 | // then get the current game state in FEN format and feed it to the engine |
447 | Player_SendBuffer_Add (player, 1000, |
452 | Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth); |
448 | 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 |
453 | 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 |
449 | Player_SendBuffer_Add (player, 1000, |
454 | Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string); |
450 | 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 |
455 | 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 |
451 | 456 | ||
452 | current_obstinacy = 0; // reset current obstinacy |
457 | current_obstinacy = 0; // reset current obstinacy |
453 | is_hint_pending = false; // no hint was requested so far |
458 | is_hint_pending = false; // no hint was requested so far |
454 | 459 | ||
455 | if ( |
460 | if (_wcsnicmp (program->name, L"Crafty", 6) == 0) |
456 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
461 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
457 | } |
462 | } |
458 | 463 | ||
459 | // 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) |
464 | // 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) |
460 | if (the_board.want_playerswap) |
465 | if (the_board.want_playerswap) |
461 | { |
466 | { |
462 | Debug_Log (L"===Got player SWAP notification from interface===\n"); |
467 | Debug_Log (L"===Got player SWAP notification from interface===\n"); |
463 | Player_SendBuffer_Add (player, 1000, |
468 | Player_SendBuffer_Add (player, 1000, program->command_go); // tell engine it's now the current player |
464 | 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 |
469 | 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 |
465 | } |
470 | } |
466 | 471 | ||
467 | // have we been notified that the current player just changed ? |
472 | // have we been notified that the current player just changed ? |
468 | if (the_board.has_playerchanged) |
473 | if (the_board.has_playerchanged) |
Line 474... | Line 479... | ||
474 | { |
479 | { |
475 | // is it NOT a board setup AND has at least one move been played (meaning it was just the other's turn before) ? |
480 | // is it NOT a board setup AND has at least one move been played (meaning it was just the other's turn before) ? |
476 | if (!the_board.was_setup && (the_board.move_count > 1)) |
481 | if (!the_board.was_setup && (the_board.move_count > 1)) |
477 | { |
482 | { |
478 | Debug_Log (L"===Player just played, sending the chosen move to engine===\n"); |
483 | Debug_Log (L"===Player just played, sending the chosen move to engine===\n"); |
479 | if ( |
484 | if (_wcsnicmp (program->name, L"Crafty", 6) == 0) |
480 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
485 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
481 | 486 | ||
482 | // instruct it about its allowed search depth BEFORE each move (this ensures engine will be "ready" to handle the command) |
487 | // instruct it about its allowed search depth BEFORE each move (this ensures engine will be "ready" to handle the command) |
483 | // then build the move string, and send the move string to the engine |
488 | // then build the move string, and send the move string to the engine |
484 | Player_SendBuffer_Add (player, 1000, |
489 | Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth); |
485 | 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 |
490 | 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 |
486 | Player_SendBuffer_Add (player, 1000, |
491 | Player_SendBuffer_Add (player, 1000, program->command_move, Move_BuildString (&the_board.moves[the_board.move_count - 1])); |
487 | Player_SendBuffer_Add (player, 1000, L"\n"); // end the send buffer with a carriage return |
492 | Player_SendBuffer_Add (player, 1000, L"\n"); // end the send buffer with a carriage return |
488 | } |
493 | } |
489 | 494 | ||
490 | // else game has not started yet, but it's our turn |
495 | // else game has not started yet, but it's our turn |
491 | else |
496 | else |
492 | { |
497 | { |
493 | Player_SendBuffer_Add (player, 1000, |
498 | Player_SendBuffer_Add (player, 1000, program->command_go); // so let's start the game |
494 | 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 |
499 | 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 |
495 | } |
500 | } |
496 | } |
501 | } |
497 | } |
502 | } |
498 | 503 | ||
Line 505... | Line 510... | ||
505 | // does our opponent want a hint ? |
510 | // does our opponent want a hint ? |
506 | if (current_player->wants_hint) |
511 | if (current_player->wants_hint) |
507 | { |
512 | { |
508 | current_player->wants_hint = false; // don't ask twice |
513 | current_player->wants_hint = false; // don't ask twice |
509 | Debug_Log (L"===Hint requested, asking engine for it===\n"); |
514 | Debug_Log (L"===Hint requested, asking engine for it===\n"); |
510 | Player_SendBuffer_Add (player, 1000, |
515 | Player_SendBuffer_Add (player, 1000, program->command_go); // tell the computer to analyze this position |
511 | 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 |
516 | 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 |
512 | is_hint_pending = true; // remember a hint is pending |
517 | is_hint_pending = true; // remember a hint is pending |
513 | 518 | ||
514 | // FIXME: move to scene.cpp |
519 | // FIXME: move to scene.cpp |
515 | Scene_SetText (&the_scene.gui.central_text, 50.0f, 40.0f, -1, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, centermsg_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191), |
520 | Scene_SetText (&the_scene.gui.central_text, 50.0f, 40.0f, -1, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, centermsg_fontindex, RGBA_TO_RGBACOLOR (255, 255, 255, 191), |
Line 532... | Line 537... | ||
532 | // just set up the board from its Forsyth-Edwards notation |
537 | // just set up the board from its Forsyth-Edwards notation |
533 | Debug_Log (L"===setting up board using FEN string===\n"); |
538 | Debug_Log (L"===setting up board using FEN string===\n"); |
534 | 539 | ||
535 | // instruct it about its allowed search depth BEFORE each table set (this ensures engine will be "ready" to handle the command) |
540 | // instruct it about its allowed search depth BEFORE each table set (this ensures engine will be "ready" to handle the command) |
536 | // then get the current game state in FEN format and feed it to the engine |
541 | // then get the current game state in FEN format and feed it to the engine |
537 | Player_SendBuffer_Add (player, 1000, |
542 | Player_SendBuffer_Add (player, 1000, program->command_sd, options.engine.depth); |
538 | 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 |
543 | 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 |
539 | Player_SendBuffer_Add (player, 1000, |
544 | Player_SendBuffer_Add (player, 1000, program->command_setboard, the_board.moves[the_board.move_count - 1].fen_string); |
540 | 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 |
545 | 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 |
541 | 546 | ||
542 | if ( |
547 | if (_wcsnicmp (program->name, L"Crafty", 6) == 0) |
543 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
548 | Player_SendBuffer_Add (player, 1000, L"disp\n"); |
544 | 549 | ||
545 | do_update = true; // remember to update the 3D scene |
550 | do_update = true; // remember to update the 3D scene |
546 | } |
551 | } |
547 | } |
552 | } |