Fix Linux sigint problem
[bonanza.git] / proce.c
1 #include <ctype.h>
2 #include <limits.h>
3 #include <math.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include "shogi.h"
8
9 /* unacceptable when the program is thinking, or quit pondering */
10 #define AbortDifficultCommand                                              \
11           if ( game_status & flag_thinking )                               \
12             {                                                              \
13               str_error = str_busy_think;                                  \
14               return -2;                                                   \
15             }                                                              \
16           else if ( game_status & ( flag_pondering | flag_puzzling ) )     \
17             {                                                              \
18               game_status |= flag_quit_ponder;                             \
19               return 2;                                                    \
20             }
21
22 #if defined(MINIMUM)
23 #  define CmdBook(x,y) cmd_book(y);
24 static int cmd_book( char **lasts );
25 #else
26 #  define CmdBook(x,y) cmd_book(x,y);
27 static int cmd_learn( tree_t * restrict ptree, char **lasts );
28 static int cmd_book( tree_t * restrict ptree, char **lasts );
29 #endif
30
31 #if ! defined(NO_STDOUT)
32 static int cmd_stress( char **lasts );
33 #endif
34
35 #if defined(DEKUNOBOU)
36 static int cmd_dek( char **lasts );
37 #endif
38
39 #if defined(CSA_LAN)
40 static int proce_csalan( tree_t * restrict ptree );
41 static int cmd_connect( tree_t * restrict ptree, char **lasts );
42 #endif
43
44 #if defined(MNJ_LAN)
45 static int proce_mnj( tree_t * restrict ptree );
46 static int cmd_mnj( tree_t * restrict ptree, char **lasts );
47 static int cmd_mnjmove( tree_t * restrict ptree, char **lasts, int is_alter );
48 #endif
49
50 #if defined(TLP)
51 static int cmd_thread( char **lasts );
52 #endif
53
54 #if defined(MPV)
55 static int cmd_mpv( tree_t * restrict ptree, char **lasts );
56 #endif
57
58 static int proce_cui( tree_t * restrict ptree );
59 static int cmd_usrmove( tree_t * restrict ptree, const char *str_move,
60                         char **last );
61 static int cmd_move_now( void );
62 static int cmd_ponder( char **lasts );
63 static int cmd_limit( char **lasts );
64 static int cmd_quit( void );
65 static int cmd_beep( char **lasts );
66 static int cmd_peek( char **lasts );
67 static int cmd_hash( char **lasts );
68 static int cmd_ping( void );
69 static int cmd_suspend( void );
70 static int cmd_problem( tree_t * restrict ptree, char **lasts );
71 static int cmd_display( tree_t * restrict ptree, char **lasts );
72 static int cmd_move( tree_t * restrict ptree, char **lasts );
73 static int cmd_new( tree_t * restrict ptree, char **lasts );
74 static int cmd_read( tree_t * restrict ptree, char **lasts );
75 static int cmd_resign( tree_t * restrict ptree, char **lasts );
76 static int cmd_time( char **lasts );
77 static int cmd_undo( tree_t * restrict ptree );    // [HGM] undo
78 static int cmd_analyze( tree_t * restrict ptree ); // [HGM] analyze
79 static int cmd_exit( void );
80 static int is_move( const char *str );
81
82
83 int
84 procedure( tree_t * restrict ptree )
85 {
86 #if defined(CSA_LAN)
87   if ( sckt_csa != SCKT_NULL ) { return proce_csalan( ptree ); }
88 #endif
89 #if defined(MNJ_LAN)
90   if ( sckt_mnj != SCKT_NULL ) { return proce_mnj( ptree ); }
91 #endif
92
93   return proce_cui( ptree );
94 }
95
96 char *start_pos, start_data[512]; // [HGM] undo: for remembering start position
97 int move_list[1024], move_ptr;
98
99 #ifdef XBOARD
100 #define IF(X) else if(!strcmp(command, X))
101
102 int myTime, hisTime, movesPerSession, inc, plyNr;
103 char xboard_mode;
104 char analyze_mode;
105
106 void
107 xboard_to_CSA( tree_t * restrict ptree, char *in, char *out )
108 {
109   char fromX=in[0], fromY=in[1], toX=in[2], toY=in[3], promo=in[4];
110   int piece=0;
111   if(fromY == '@') { // drop (contains all info needed to convert it)
112     if(fromX >= 'a') fromX += 'A' - 'a';
113     switch(fromX) { // encode piece
114       case 'P': piece = pawn;   break;
115       case 'L': piece = lance;  break;
116       case 'N': piece = knight; break;
117       case 'S': piece = silver; break;
118       case 'G': piece = gold;   break;
119       case 'B': piece = bishop; break;
120       case 'R': piece = rook;   break;
121     }
122     sprintf(out, "00%c%c%s", 'a'+'9'-toX, '1'+'9'-toY, astr_table_piece[piece]);
123   } else { // board move (need to figure out moved piece)
124     int from = ('9' - fromY)*9 + (fromX - 'a');
125     int flag = (promo == '+' ? FLAG_PROMO : 0);
126     piece = abs( BOARD[from] ); // this only works when not searching!
127 printf("# piece from board: %d\n", piece);fflush(stdout);
128     if( game_status & ( flag_pondering | flag_puzzling | flag_thinking ) ) {
129       int i, to = ('9' - toY)*9 + (toX - 'a');
130       piece = 0; // kludge to force illegal CSA move
131       for( i = 0; i < root_nmove; i++ ) { // determine the piece from the move list
132         int move = root_move_list[i].move;
133         if( I2To(move) != to ) continue;
134         if( I2From(move) != from ) continue;
135         if( (move & FLAG_PROMO) != flag ) continue;
136         piece = I2PieceMove( move ); // we found the move; take the piece from it
137         break;
138       }
139 printf("# piece corrected to %d\n", piece);fflush(stdout);
140     }
141     if( promo ) piece += promote;
142     sprintf(out, "%c%c%c%c%s", 'a'+'9'-fromX, '1'+'9'-fromY, 'a'+'9'-toX, '1'+'9'-toY, astr_table_piece[piece]);
143   }
144 }
145
146 static void
147 SetTimes(void)
148 { // set white and black times from own and opponent time.
149   int moves;
150   if(movesPerSession <= 0) moves = 35; else {
151     moves = - plyNr/2;
152     while(moves <= 0) moves += movesPerSession;
153   }
154   time_limit = (myTime-inc-30)/(moves+2) + inc;
155   time_max_limit = 3*time_limit;
156   if(time_max_limit > myTime - 30)time_max_limit = myTime - 30; // keep 0.3 sec margin, as Bonanza reads the clock infrequently
157   time_limit *= 10; // msec
158   time_max_limit *= 10;
159 Out("# moves=%d, time=%d inc=%d t=%d, max=%d\n", moves, myTime, inc, time_limit, time_max_limit);
160 }
161
162 static int
163 proce_xboard(char *line, const char *command, tree_t * restrict ptree)
164 { // [HGM] added to make Bonanza.exe a native WinBoard engine
165   int value = -100000;
166   static char forceMode = 0;
167   sscanf(line + strlen(command) + 1, "%d", &value);
168 Out("# command = '%s'\n", line);
169   if(0) ;
170   IF("protover") {
171 #if defined(MPV)
172                    Out("feature option=\"MultiPV -spin 1 1 100\"\n");
173                    Out("feature option=\"centi-Pawn margin -spin 200 0 25000\"\n");
174 #endif
175                    Out("feature variants=\"shogi\" usermove=1 myname=\"Bonanza " BNZ_VER
176                        "\" memory=1 smp=1 debug=1 colors=0 setboard=1 ping=1 sigint=0 done=1\n");
177                  }
178   IF("new")      { forceMode = plyNr = 0; SetTimes(); return 0; }
179   IF("easy")     { strcpy(line, "ponder off"); return 0; }
180   IF("hard")     { strcpy(line, "ponder on");  return 0; }
181   IF("post")     { ; }
182   IF("nopost")   { ; }
183   IF("time")     { sscanf(line+5, "%d", &myTime); }
184   IF("otim")     { sscanf(line+5, "%d", &hisTime); }
185   IF("force")    { forceMode = 1; }
186   IF("go")       { forceMode = 0; SetTimes(); plyNr++; strcpy(line, "move"); return 0; }
187   IF("memory")   { ; }
188   IF("cores")    { sprintf(line, "tlp num %d", value); return 0; }
189   IF("sd")       { sprintf(line, "limit depth %d", value); return 0; }
190   IF("st")       { ; }
191   IF("quit")     { return 0; }
192   IF("analyze")  { return 0; }
193   IF("exit")     { return 0; }
194   IF("variant")  { /* ignore, since it must be Shogi */; }
195   IF("setboard") { ; }
196   IF("option")   {
197                    if(sscanf(line+7, "MultiPV=%d", &value) == 1) { sprintf(line, "mpv num %d", value); return 0; }
198                    if(sscanf(line+7, "centi-Pawn margin=%d", &value) == 1) { sprintf(line, "mpv width %d", value); return 0; }
199                  }
200   IF("level")    { int min, sec; float fsec=0.;
201                    if(sscanf(line+6, "%d %d:%d %f", &movesPerSession, &min, &sec, &fsec) != 4)
202                       sscanf(line+6, "%d %d %f", &movesPerSession, &min, &fsec);
203                    min = 60*min + sec; myTime = hisTime = 100*min; inc = 100 * fsec;
204                  }
205   IF("usermove") { char buf[20];
206                    xboard_to_CSA( ptree, line+9, buf );
207                    if(forceMode || analyze_mode) strcpy(line, "move "), line += 5; else plyNr++, SetTimes();
208                    strcpy( line, buf );
209                    plyNr++;
210                    return 0;
211                  }
212   IF("undo")     { return 0; }
213   IF("remove")   { ; }
214   IF("ping")     { Out("pong %d\n", value); }
215   return 1;
216 }
217 #endif
218
219 static int
220 proce_cui( tree_t * restrict ptree )
221 {
222   const char *token;
223   char *last;
224
225   token = strtok_r( str_cmdline, str_delimiters, &last );
226
227   if ( token == NULL || *token == '#' ) { return 1; }
228 #ifdef XBOARD
229   { 
230     if(xboard_mode) {
231       if( proce_xboard(str_cmdline, token, ptree) )  return 1; // command already processed
232       Out("# translated command '%s'\n", str_cmdline);         // command translated for processing by Bonanza
233       token = strtok_r( str_cmdline, str_delimiters, &last );  // redo parsing
234     } else
235     if ( ! strcmp( token, "xboard" ) )  { xboard_mode = 1; game_status |= flag_noprompt; return 1; }
236   }
237 #endif
238   if ( ! strcmp( token, "analyze" ) )   { return cmd_analyze( ptree ); } // [HGM] analyze
239   if ( ! strcmp( token, "exit" ) )      { return cmd_exit(); }           // [HGM] analyze
240   if ( ! strcmp( token, "move" ) )      { return cmd_move( ptree, &last ); }
241   if ( ! strcmp( token, "undo" ) )      { return cmd_undo( ptree ); }    // [HGM] undo
242 #if defined(MPV)
243   if ( ! strcmp( token, "mpv" ) )       { return cmd_mpv( ptree, &last ); }
244 #endif
245   analyze_mode = 0; // [HGM] analyze: all other commands terminate analysis
246   if ( is_move( token ) ) { return cmd_usrmove( ptree, token, &last ); }
247   if ( ! strcmp( token, "s" ) )         { return cmd_move_now(); }
248   if ( ! strcmp( token, "beep" ) )      { return cmd_beep( &last); }
249   if ( ! strcmp( token, "book" ) )      { return CmdBook( ptree, &last ); }
250   if ( ! strcmp( token, "display" ) )   { return cmd_display( ptree, &last ); }
251   if ( ! strcmp( token, "hash" ) )      { return cmd_hash( &last ); }
252   if ( ! strcmp( token, "limit" ) )     { return cmd_limit( &last ); }
253   if ( ! strcmp( token, "new" ) )       { return cmd_new( ptree, &last ); }
254   if ( ! strcmp( token, "peek" ) )      { return cmd_peek( &last ); }
255   if ( ! strcmp( token, "ping" ) )      { return cmd_ping(); }
256   if ( ! strcmp( token, "ponder" ) )    { return cmd_ponder( &last ); }
257   if ( ! strcmp( token, "problem" ) )   { return cmd_problem( ptree, &last ); }
258   if ( ! strcmp( token, "quit" ) )      { return cmd_quit(); }
259   if ( ! strcmp( token, "read" ) )      { return cmd_read( ptree, &last ); }
260   if ( ! strcmp( token, "resign" ) )    { return cmd_resign( ptree, &last ); }
261   if ( ! strcmp( token, "suspend" ) )   { return cmd_suspend(); }
262   if ( ! strcmp( token, "time" ) )      { return cmd_time( &last ); }
263 #if defined(CSA_LAN)
264   if ( ! strcmp( token, "connect" ) )   { return cmd_connect( ptree, &last ); }
265 #endif
266 #if defined(MNJ_LAN)
267   if ( ! strcmp( token, "mnj" ) )       { return cmd_mnj( ptree, &last ); }
268 #endif
269 #if defined(DEKUNOBOU)
270   if ( ! strcmp( token, "dekunobou" ) ) { return cmd_dek( &last ); }
271 #endif
272 #if defined(TLP)
273   if ( ! strcmp( token, "tlp" ) )       { return cmd_thread( &last ); }
274 #endif
275 #if ! defined(NO_STDOUT)
276   if ( ! strcmp( token, "stress" ) )    { return cmd_stress( &last ); }
277 #endif
278 #if ! defined(MINIMUM)
279   if ( ! strcmp( token, "learn" ) )     { return cmd_learn( ptree, &last ); }
280 #endif
281
282   str_error = str_bad_cmdline;
283   return -2;
284 }
285
286
287 #if defined(CSA_LAN)
288 static int
289 proce_csalan( tree_t * restrict ptree )
290 {
291   const char *token;
292   char *last;
293
294   token = strtok_r( str_cmdline, str_delimiters, &last );
295     
296   if ( token == NULL ) { return 1; }
297   if ( *token == ach_turn[client_turn] && is_move( token+1 ) )
298     {
299       char *ptr;
300       long l;
301
302       token = strtok_r( NULL, str_delimiters, &last );
303       if ( token == NULL || *token != 'T' )
304         {
305           str_error = str_bad_cmdline;
306           return -1;
307         }
308       
309       l = strtol( token+1, &ptr, 0 );
310       if ( token+1 == ptr || l == LONG_MAX || l < 1 )
311         {
312           str_error = str_bad_cmdline;
313           return -1;
314         }
315
316       adjust_time( (unsigned int)l, client_turn );
317       Out( "  elapsed: b%u, w%u\n", sec_b_total, sec_w_total );
318       return 1;
319     }
320   if ( *token == ach_turn[Flip(client_turn)] && is_move( token+1 ) )
321     {
322       return cmd_usrmove( ptree, token+1, &last );
323     }
324   if ( ! strcmp( token, str_resign ) ) { return cmd_resign( ptree, &last ); }
325   if ( ! strcmp( token, "#WIN" )
326        || ! strcmp( token, "#LOSE" )
327        || ! strcmp( token, "#DRAW" )
328        || ! strcmp( token, "#CHUDAN" ) )
329     {
330       if ( game_status & ( flag_thinking | flag_pondering | flag_puzzling ) )
331         {
332           game_status |= flag_suspend;
333           return 2;
334         }
335       
336       ShutdownClient;
337       
338       if ( client_ngame == client_max_game ) { return cmd_quit(); }
339
340       return client_next_game( ptree, client_str_addr, (int)client_port );
341     }
342   
343   return 1;
344 }
345 #endif
346
347
348 #if defined(MNJ_LAN)
349 static int
350 proce_mnj( tree_t * restrict ptree )
351 {
352   const char *token;
353   char *last;
354   int iret;
355
356   token = strtok_r( str_cmdline, str_delimiters, &last );
357   if ( token == NULL ) { return 1; }
358
359   if ( ! strcmp( token, "new" ) )
360     {
361       iret = cmd_suspend();
362       if ( iret != 1 ) { return iret; }
363
364       mnj_posi_id = 0;
365       iret = cmd_new( ptree, &last );
366       if ( iret < 0 ) { return iret; }
367
368       return analyze( ptree );
369     }
370   if ( ! strcmp( token, "idle" ) )  { return cmd_suspend(); }
371   if ( ! strcmp( token, "alter" ) ) { return cmd_mnjmove( ptree, &last, 1 ); }
372   if ( ! strcmp( token, "move" ) )  { return cmd_mnjmove( ptree, &last, 0 ); }
373
374   str_error = str_bad_cmdline;
375   return -2;
376 }
377
378
379 static int
380 cmd_mnjmove( tree_t * restrict ptree, char **lasts, int is_alter )
381 {
382   const char *str1 = strtok_r( NULL, str_delimiters, lasts );
383   const char *str2 = strtok_r( NULL, str_delimiters, lasts );
384   char *ptr;
385   long lid;
386   unsigned int move;
387   int iret;
388
389   if ( sckt_mnj == SCKT_NULL ||  str1 == NULL || str2 == NULL )
390     {
391       str_error = str_bad_cmdline;
392       return -1;
393     }
394
395   lid = strtol( str2, &ptr, 0 );
396   if ( ptr == str2 || lid == LONG_MAX || lid < 1 )
397     {
398       str_error = str_bad_cmdline;
399       return -1;
400     }
401
402   AbortDifficultCommand;
403  
404   if ( is_alter ) { unmake_move_root( ptree, mnj_move_last ); };
405
406   iret = interpret_CSA_move( ptree, &move, str1 );
407   if ( iret < 0 ) { return iret; }
408     
409   iret = get_elapsed( &time_turn_start );
410   if ( iret < 0 ) { return iret; }
411
412   mnj_posi_id   = (int)lid;
413   mnj_move_last = move;
414
415   iret = make_move_root( ptree, move, ( flag_history | flag_time | flag_rep
416                                         | flag_detect_hang
417                                         | flag_rejections ) );
418   if ( iret < 0 ) { return iret; }
419   
420 #  if ! defined(NO_STDOUT)
421   iret = out_board( ptree, stdout, 0, 0 );
422   if ( iret < 0 ) { return iret; }
423 #  endif
424
425   return analyze( ptree );
426 }
427 #endif
428
429
430 static int
431 do_analyze( tree_t * restrict ptree )
432 { // [HGM] analyze: do a ponder search on the current position
433   int iret;
434   if ( get_elapsed( &time_start ) < 0 ) { return -1; }
435   time_limit = time_max_limit = 1e9; // kludge: use huge time to mimic infinity
436 #ifdef XBOARD
437   Out("1 0 0 0 New Search\n"); // make sure lower depth is emitted, so XBoard undestand new search started
438 #endif
439   game_status |= flag_pondering;
440   iret         = iterate( ptree, 0 );
441   game_status &= ~flag_pondering;
442   return iret;
443 }
444
445
446 static int
447 cmd_undo( tree_t * restrict ptree )
448 { // [HGM] undo: restart the game, and feed all moves except the last
449   int i, last = move_ptr;
450   char *p = start_data;
451   if( move_ptr <= 0 ) {
452     str_error = "undo past start of game ignored";
453     return -2;
454   }
455
456   AbortDifficultCommand;
457
458   last--;
459   cmd_new( ptree, &p );
460   for(i=0; i<last; i++) {
461     make_move_root( ptree, move_list[i], 0);
462   }
463
464   if ( analyze_mode ) return do_analyze ( ptree ); // [HGM] analyze: analysis should continue after undo
465
466   return 1;
467 }
468
469
470 static int
471 cmd_analyze( tree_t * restrict ptree )
472 { // [HGM] analyze: switch on analyze mode, and start analyzing
473   AbortDifficultCommand;
474
475   analyze_mode = 1;
476   return do_analyze( ptree );
477 }
478
479
480 static int
481 cmd_exit( void )
482 { // [HGM] analyze: switch off analysis mode
483   if ( !analyze_mode ) {
484     str_error = "was not analyzing";
485     return -2;
486   }
487
488   if ( game_status & flag_pondering ) { game_status |= flag_quit_ponder; return 2; }
489   analyze_mode = 0;
490
491   return 1;
492 }
493
494
495 static int
496 is_move( const char *str )
497 {
498   if ( isdigit( (int)str[0] ) && isdigit( (int)str[1] )
499        && isdigit( (int)str[2] ) && isdigit( (int)str[3] )
500        && isupper( (int)str[4] ) && isupper( (int)str[5] )
501        && str[6] == '\0' ) { return 1; }
502
503   return 0;
504 }
505
506
507 static int
508 cmd_move_now( void )
509 {
510   if ( game_status & flag_thinking ) { game_status |= flag_move_now; }
511
512   return 1;
513 }
514
515
516 static int
517 cmd_usrmove( tree_t * restrict ptree, const char *str_move, char **lasts )
518 {
519   const char *str;
520   char *ptr;
521   long lelapsed;
522   unsigned int move;
523   int iret;
524
525   if ( game_status & mask_game_end )
526     {
527       str_error = str_game_ended;
528       return -2;
529     }
530   
531   if ( game_status & flag_thinking )
532     {
533       str_error = str_busy_think;
534       return -2;
535     }
536
537   str = strtok_r( NULL, str_delimiters, lasts );
538   if ( str == NULL ) { lelapsed = 0; }
539   else {
540     if ( *str != 'T' )
541       {
542         str_error = str_bad_cmdline;
543         return -2;
544       }
545     str += 1;
546     lelapsed = strtol( str, &ptr, 0 );
547     if ( ptr == str || lelapsed == LONG_MAX || lelapsed < 1 )
548       {
549         str_error = str_bad_cmdline;
550         return -2;
551       }
552   }
553
554   if ( game_status & ( flag_pondering | flag_puzzling ) )
555     {
556       int i;
557
558       for ( i = 0; i < ponder_nmove; i++ )
559         {
560           if ( ! strcmp( str_move, str_CSA_move(ponder_move_list[i]) ) )
561             {
562               break;
563             }
564         }
565       if ( i == ponder_nmove )
566         {
567 #if defined(CSA_LAN)
568           if ( sckt_csa != SCKT_NULL ) { AbortDifficultCommand; }
569 #endif
570
571 #if defined(DEKUNOBOU)
572           if ( dek_ngame ) { AbortDifficultCommand; }
573 #endif
574
575 #if defined(CSASHOGI)
576           AbortDifficultCommand;
577 #else
578           str_error = str_illegal_move;
579           return -2;
580 #endif
581         }
582
583       if ( ( game_status & flag_puzzling )
584            || strcmp( str_move, str_CSA_move(ponder_move) ) )
585         {
586           ponder_move  = MOVE_PONDER_FAILED;
587           game_status |= flag_quit_ponder;
588           return 2;
589         }
590       else {
591         iret = renovate_time( Flip(root_turn) );
592         if ( iret < 0 ) { return iret; }
593         if ( lelapsed )
594           {
595             adjust_time( (unsigned int)lelapsed, Flip(root_turn) );
596           }
597
598         history_book_learn[ record_game.moves ].move_played = ponder_move;
599         history_book_learn[ record_game.moves ].hand_played
600           = ptree->rep_hand_list[ root_nrep-1 ];
601         history_book_learn[ record_game.moves ].key_played
602           = (unsigned int)ptree->rep_board_list[ root_nrep-1 ];
603
604         out_CSA( ptree, &record_game, ponder_move );
605
606         game_status      &= ~flag_pondering;
607         game_status      |= flag_thinking;
608         n_nobook_move    += 1;
609 #ifdef XBOARD
610       if(!xboard_mode)
611 #endif
612         set_search_limit_time( root_turn );
613
614         OutCsaShogi( "info ponder end\n" );
615
616         str = str_time_symple( time_turn_start - time_start );
617         Out( "    %6s          MOVE PREDICTION HIT\n"
618              "  elapsed: b%u, w%u\n", str, sec_b_total, sec_w_total );
619         return 1;
620       }
621     }
622
623   iret = interpret_CSA_move( ptree, &move, str_move );
624   if ( iret < 0 ) { return iret; }
625   move_evasion_pchk = 0;
626   iret = make_move_root( ptree, move, ( flag_rep | flag_history | flag_time
627                                         | flag_rejections
628                                         | flag_detect_hang ) );
629   if ( iret < 0 )
630       {
631
632 #if defined(CSA_LAN)
633         if ( sckt_csa != SCKT_NULL )
634           {
635             if ( move_evasion_pchk )
636               {
637                 str  = str_CSA_move( move_evasion_pchk );
638                 iret = sckt_out( sckt_csa, "%c%s\n",
639                                  ach_turn[Flip(root_turn)], str );
640                 if ( iret < 0 ) { return iret; }
641               }
642             return cmd_suspend();
643           }
644 #endif
645
646 #if defined(DEKUNOBOU)
647         if ( dek_ngame )
648           {
649             if ( move_evasion_pchk )
650               {
651                 dek_win += 1;
652                 OutDek( "%%TORYO\n" );
653               }
654             return cmd_suspend();
655           }
656 #endif
657
658         if ( move_evasion_pchk )
659           {
660             str = str_CSA_move( move_evasion_pchk );
661 #if defined(CSASHOGI)
662             OutCsaShogi( "move%s\n", str );
663             return cmd_suspend();
664 #else
665             snprintf( str_message, SIZE_MESSAGE, "perpetual check (%c%s)",
666                       ach_turn[Flip(root_turn)], str );
667             str_error = str_message;
668             return -2;
669 #endif
670           }
671
672         return iret;
673       }
674
675   if ( lelapsed ) { adjust_time( (unsigned int)lelapsed, Flip(root_turn) ); }
676   Out( "  elapsed: b%u, w%u\n", sec_b_total, sec_w_total );
677
678 #if defined(CSA_LAN)
679   if ( sckt_csa != SCKT_NULL && ( game_status & flag_mated ) )
680     {
681       iret = sckt_out( sckt_csa, "%%TORYO\n" );
682       if ( iret < 0 ) { return iret; }
683     }
684 #endif
685
686 #if defined(DEKUNOBOU)
687   if ( dek_ngame && ( game_status & flag_drawn ) ) { OutDek( "%%TORYO\n" ); }
688 #endif
689
690   if ( ! ( game_status & mask_game_end ) )
691     {
692       iret = com_turn_start( ptree, 0 );
693       if ( iret < 0 ) { return iret; }
694     }
695
696   return 1;
697 }
698
699
700 static int
701 cmd_beep( char **lasts )
702 {
703   const char *str = strtok_r( NULL, str_delimiters, lasts );
704   if ( str == NULL )
705     {
706       str_error = str_bad_cmdline;
707       return -2;
708     }
709
710   if      ( ! strcmp( str, str_on )  ) {  game_status &= ~flag_nobeep; }
711   else if ( ! strcmp( str, str_off ) ) {  game_status |=  flag_nobeep; }
712   else {
713     str_error = str_bad_cmdline;
714     return -2;
715   }
716
717   return 1;
718 }
719
720
721 static int
722 cmd_peek( char **lasts )
723 {
724   const char *str = strtok_r( NULL, str_delimiters, lasts );
725
726   if ( str == NULL )
727     {
728       str_error = str_bad_cmdline;
729       return -2;
730     }
731
732   if      ( ! strcmp( str, str_on )  ) {  game_status &= ~flag_nopeek; }
733   else if ( ! strcmp( str, str_off ) ) {  game_status |=  flag_nopeek; }
734   else {
735     str_error = str_bad_cmdline;
736     return -2;
737   }
738
739   return 1;
740 }
741
742
743 static int
744 cmd_ponder( char **lasts )
745 {
746   const char *str = strtok_r( NULL, str_delimiters, lasts );
747
748   if ( str == NULL )
749     {
750       str_error = str_bad_cmdline;
751       return -2;
752     }
753
754   if      ( ! strcmp( str, str_on )  ) {  game_status &= ~flag_noponder; }
755   else if ( ! strcmp( str, str_off ) )
756     {
757       if ( game_status & ( flag_pondering | flag_puzzling ) )
758         {
759           game_status |= flag_quit_ponder;
760         }
761       game_status |= flag_noponder;
762     }
763   else {
764     str_error = str_bad_cmdline;
765     return -2;
766   }
767
768   return 1;
769 }
770
771
772 #if ! defined(NO_STDOUT)
773 static int
774 cmd_stress( char **lasts )
775 {
776   const char *str = strtok_r( NULL, str_delimiters, lasts );
777
778   if ( str == NULL )
779     {
780       str_error = str_bad_cmdline;
781       return -2;
782     }
783
784   if      ( ! strcmp( str, str_on  ) ) { game_status &= ~flag_nostress; }
785   else if ( ! strcmp( str, str_off ) ) { game_status |= flag_nostress; }
786   else {
787     str_error = str_bad_cmdline;
788     return -2;
789   }
790
791   return 1;
792 }
793 #endif
794
795
796 static int
797 #if defined(MINIMUM)
798 cmd_book( char **lasts )
799 #else
800 cmd_book( tree_t * restrict ptree, char **lasts )
801 #endif
802 {
803   const char *str = strtok_r( NULL, str_delimiters, lasts );
804   int iret = 1;
805
806   if ( str == NULL )
807     {
808       str_error = str_bad_cmdline;
809       return -2;
810     }
811   if      ( ! strcmp( str, str_on ) )   { iret = book_on(); }
812   else if ( ! strcmp( str, str_off ) )  { iret = book_off(); }
813   else if ( ! strcmp( str, "narrow" ) ) { game_status |= flag_narrow_book; }
814   else if ( ! strcmp( str, "wide" ) )   { game_status &= ~flag_narrow_book; }
815 #if ! defined(MINIMUM)
816   else if ( ! strcmp( str, "create" ) )
817     {
818       AbortDifficultCommand;
819
820       iret = book_create( ptree );
821       if ( iret < 0 ) { return iret; }
822
823       iret = ini_game( ptree, &min_posi_no_handicap, flag_history,
824                        NULL, NULL );
825       if ( iret < 0 ) { return iret; }
826
827       iret = get_elapsed( &time_turn_start );
828     }
829 #endif
830   else {
831     str_error = str_bad_cmdline;
832     iret = -2;
833   }
834
835   return iret;
836 }
837
838
839 static int
840 cmd_display( tree_t * restrict ptree, char **lasts )
841 {
842   const char *str = strtok_r( NULL, str_delimiters, lasts );
843   char *ptr;
844   long l;
845   int iret;
846
847   if ( str != NULL )
848     {
849       l = strtol( str, &ptr, 0 );
850       if ( ptr == str )
851         {
852           str_error = str_bad_cmdline;
853           return -2;
854         }
855       if      ( l == 1 ) { game_status &= ~flag_reverse; }
856       else if ( l == 2 ) { game_status |= flag_reverse; }
857       else {
858         str_error = str_bad_cmdline;
859         return -2;
860       }
861     }
862   
863   Out( "\n" );
864   iret = out_board( ptree, stdout, 0, 0 );
865   if ( iret < 0 ) { return iret; }
866 #if ! defined(NO_LOGGING)
867   iret = out_board( ptree, pf_log, 0, 0 );
868   if ( iret < 0 ) { return iret; }
869 #endif
870   Out( "\n" );
871
872   return 1;
873 }
874
875
876 static int
877 cmd_ping( void )
878 {
879   OutCsaShogi( "pong\n" );
880   Out( "pong\n" );
881   return 1;
882 }
883
884
885 static int
886 cmd_hash( char **lasts )
887 {
888   const char *str = strtok_r( NULL, str_delimiters, lasts );
889   char *ptr;
890   long l;
891
892   if ( str == NULL )
893     {
894       str_error = str_bad_cmdline;
895       return -2;
896     }
897
898   if ( ! strcmp( str, "learn" ) )
899     {
900       str = strtok_r( NULL, str_delimiters, lasts );
901       if ( str != NULL && ! strcmp( str, str_on ) )
902         {
903           return hash_learn_on();
904         }
905       else if ( str != NULL && ! strcmp( str, str_off ) )
906         {
907           return hash_learn_off();
908         }
909 #if ! defined(MINIMUM)
910       else if ( str != NULL && ! strcmp( str, "create" ) )
911         {
912           return hash_learn_create();
913         }
914 #endif
915       else {
916         str_error = str_bad_cmdline;
917         return -2;
918       }
919     }
920
921   l = strtol( str, &ptr, 0 );
922   if ( ptr == str || l == LONG_MAX || l < 1 || l > 31 )
923     {
924       str_error = str_bad_cmdline;
925       return -2;
926     }
927   
928   AbortDifficultCommand;
929   
930   log2_ntrans_table = (int)l;
931   memory_free( (void *)ptrans_table_orig );
932   return ini_trans_table();
933 }
934
935
936 static int
937 cmd_limit( char **lasts )
938 {
939   const char *str = strtok_r( NULL, str_delimiters, lasts );
940   char *ptr;
941   long l1, l2, l3;
942
943   if ( str == NULL )
944     {
945       str_error = str_bad_cmdline;
946       return -2;
947     }
948
949   AbortDifficultCommand;
950
951   if ( ! strcmp( str, "depth" ) )
952     {
953       str = strtok_r( NULL, str_delimiters, lasts );
954       if ( str == NULL )
955         {
956           str_error = str_bad_cmdline;
957           return -2;
958         }
959       l1 = strtol( str, &ptr, 0 );
960       if ( ptr == str || l1 == LONG_MAX || l1 < 1 )
961         {
962           str_error = str_bad_cmdline;
963           return -2;
964         }
965       sec_limit_up = UINT_MAX;
966       node_limit   = UINT64_MAX;
967       depth_limit  = (int)l1;
968     }
969   else if ( ! strcmp( str, "nodes" ) )
970     {
971       str = strtok_r( NULL, str_delimiters, lasts );
972       if ( str == NULL )
973         {
974           str_error = str_bad_cmdline;
975           return -2;
976         }
977       l1 = strtol( str, &ptr, 0 );
978       if ( ptr == str || l1 == LONG_MAX || l1 < 1 )
979         {
980           str_error = str_bad_cmdline;
981           return -2;
982         }
983       sec_limit_up = UINT_MAX;
984       depth_limit  = PLY_MAX;
985       node_limit   = (uint64_t)l1;
986     }
987   else if ( ! strcmp( str, "time" ) )
988     {
989       str = strtok_r( NULL, str_delimiters, lasts );
990       if ( str == NULL )
991         {
992           str_error = str_bad_cmdline;
993           return -2;
994         }
995
996       if ( ! strcmp( str, "extendable" ) )
997         {
998           game_status |= flag_time_extendable;
999         }
1000