Fix force mode after setboard
[bonanza.git] / csa.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <ctype.h>
6 #include <limits.h>
7 #include <time.h>
8 #include "shogi.h"
9
10 static void out_CSA_header( const tree_t * restrict ptree, record_t *pr );
11 static int str2piece( const char *str );
12 static int skip_comment( record_t *pr );
13 static int read_char( record_t *pr );
14 static int read_CSA_line( record_t *pr, char *str );
15 static int in_CSA_header( tree_t * restrict ptree, record_t *pr, int flag );
16 static int read_board_rep2( const char *str_line, min_posi_t *pmin_posi );
17 static int read_board_rep3( const char *str_line, min_posi_t *pmin_posi );
18
19 int
20 read_record( tree_t * restrict ptree, const char *str_file,
21              unsigned int moves, int flag )
22 {
23   record_t record;
24   int iret;
25
26   iret = record_open( &record, str_file, mode_read, NULL, NULL );
27   if ( iret < 0 ) { return iret; }
28
29   if ( ! moves )
30     {
31       iret = in_CSA_header( ptree, &record, flag );
32       if ( iret < 0 )
33         {
34           record_close( &record );
35           return iret;
36         }
37     }
38   else do {
39     iret = in_CSA( ptree, &record, NULL, flag );
40     if ( iret < 0 )
41       {
42         record_close( &record );
43         return iret;
44       }
45   } while ( iret != record_next
46             && iret != record_eof
47             && moves > record.moves );
48   
49   return record_close( &record );
50 }
51
52
53 int
54 record_open( record_t *pr, const char *str_file, record_mode_t record_mode,
55              const char *str_name1, const char *str_name2 )
56 {
57   pr->games = pr->moves = pr->lines = 0;
58   pr->str_name1[0] = '\0';
59   pr->str_name2[0] = '\0';
60
61   if ( str_name1 )
62     {
63       strncpy( pr->str_name1, str_name1, SIZE_PLAYERNAME-1 );
64       pr->str_name1[SIZE_PLAYERNAME-1] = '\0';
65     }
66   
67   if ( str_name2 )
68     {
69       strncpy( pr->str_name2, str_name2, SIZE_PLAYERNAME-1 );
70       pr->str_name2[SIZE_PLAYERNAME-1] = '\0';
71     }
72
73   if ( record_mode == mode_write )
74     {
75       pr->pf = file_open( str_file, "w" );
76       if ( pr->pf == NULL ) { return -2; }
77     }
78   else if ( record_mode == mode_read_write )
79     {
80       pr->pf = file_open( str_file, "wb+" );
81       if ( pr->pf == NULL ) { return -2; }
82     }
83   else {
84     assert( record_mode == mode_read );
85
86     pr->pf = file_open( str_file, "rb" );
87     if ( pr->pf == NULL ) { return -2; }
88   }
89
90   return 1;
91 }
92
93
94 int
95 record_close( record_t *pr )
96 {
97   int iret = file_close( pr->pf );
98   pr->pf = NULL;
99   return iret;
100 }
101
102
103 void
104 out_CSA( tree_t * restrict ptree, record_t *pr, unsigned int move )
105 {
106   const char *str_move;
107   unsigned int sec;
108
109   /* print move */
110   if ( move == MOVE_RESIGN )
111     {
112       if ( ! pr->moves ) { out_CSA_header( ptree, pr ); }
113       fprintf( pr->pf, "%s\n", str_resign );
114       pr->lines++;
115     }
116   else {
117     if ( ! pr->moves )
118       {
119         root_turn = Flip(root_turn);
120         UnMakeMove( root_turn, move, 1 );
121         out_CSA_header( ptree, pr );
122         MakeMove( root_turn, move, 1 );
123         root_turn = Flip(root_turn);
124       }
125     str_move = str_CSA_move( move );
126     fprintf( pr->pf, "%c%s\n", ach_turn[Flip(root_turn)], str_move );
127     pr->lines++;
128     pr->moves++;
129   }
130
131   /* print time */
132   sec = root_turn ? sec_b_total : sec_w_total;
133
134   fprintf( pr->pf, "T%-7u,'%03u:%02u \n", sec_elapsed, sec / 60U, sec % 60U );
135   pr->lines++;
136
137   /* print repetition or mate status */
138   if ( game_status & flag_mated )
139     {
140       fprintf( pr->pf, "%%TSUMI\n" );
141       pr->lines++;
142     }
143   else if ( game_status & flag_drawn )
144     {
145       fprintf( pr->pf, "%s\n", str_repetition );
146       pr->lines++;
147     }
148
149   fflush( pr->pf );
150 }
151
152
153 int
154 record_wind( record_t *pr )
155 {
156   char str_line[ SIZE_CSALINE ];
157   int iret;
158   for (;;)
159     {
160       iret = read_CSA_line( pr, str_line );
161       if ( iret < 0 ) { return iret; }
162       if ( ! iret ) { return record_eof; }
163       if ( ! strcmp( str_line, "/" ) ) { break; }
164     }
165   pr->games++;
166   pr->moves = 0;
167   return record_next;
168 }
169
170
171 #if ! defined(MINIMUM)
172 int
173 record_rewind( record_t *pr )
174 {
175   pr->games = pr->moves = pr->lines = 0;
176   if ( fseek( pr->pf, 0, SEEK_SET ) ) { return -2; }
177
178   return 1;
179 }
180
181
182 int
183 record_getpos( record_t *pr, rpos_t *prpos )
184 {
185   if ( fgetpos( pr->pf, &prpos->fpos ) )
186     {
187       str_error = "fgetpos() failed.";
188       return -2;
189     }
190   prpos->games = pr->games;
191   prpos->moves = pr->moves;
192   prpos->lines = pr->lines;
193
194   return 1;
195 }
196
197
198 int
199 record_setpos( record_t *pr, const rpos_t *prpos )
200 {
201   if ( fsetpos( pr->pf, &prpos->fpos ) )
202     {
203       str_error = "fsetpos() failed.";
204       return -2;
205     }
206   pr->games = prpos->games;
207   pr->moves = prpos->moves;
208   pr->lines = prpos->lines;
209
210   return 1;
211 }
212 #endif /* no MINIMUM */
213
214
215 int
216 in_CSA( tree_t * restrict ptree, record_t *pr, unsigned int *pmove, int flag )
217 {
218   char str_line[ SIZE_CSALINE ];
219   char *ptr;
220   unsigned int move;
221   long l;
222   int iret;
223   
224   if ( pr->moves == 0 )
225     {
226       iret = in_CSA_header( ptree, pr, flag );
227       if ( iret < 0 ) { return iret; }
228     }
229
230   do {
231     iret = read_CSA_line( pr, str_line );
232     if ( iret < 0 ) { return iret; }
233     if ( ! iret ) { return record_eof; }
234     if ( ! strcmp( str_line, str_resign ) )
235       {
236         game_status |= flag_resigned;
237         return record_resign;
238       }
239     if ( ! strcmp( str_line, str_repetition )
240          || ! strcmp( str_line, str_jishogi ) )
241       {
242         game_status |= flag_drawn;
243         return record_drawn;
244       }
245     if ( ! strcmp( str_line, str_record_error ) )
246       {
247         return record_error;
248       }
249   } while ( str_line[0] == 'T' || str_line[0] == '%' );
250
251   if ( ! strcmp( str_line, "/" ) )
252     {
253       pr->games++;
254       pr->moves = 0;
255       return record_next;
256     }
257
258   if ( game_status & mask_game_end )
259     {
260       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
261                 pr->lines, str_bad_record );
262       str_error = str_message;
263       return -2;
264     }
265
266   iret = interpret_CSA_move( ptree, &move, str_line+1 );
267   if ( iret < 0 )
268     {
269       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
270                 pr->lines, str_error );
271       str_error = str_message;
272       return -2;
273     }
274   if ( pmove != NULL ) { *pmove = move; }
275
276   /* do time */
277   if ( flag & flag_time )
278     {
279       iret = read_CSA_line( pr, str_line );
280       if ( iret < 0 ) { return iret; }
281       if ( ! iret )
282         {
283           snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
284                     pr->lines, str_unexpect_eof );
285           str_error = str_message;
286           return -2;
287         }
288       if ( str_line[0] != 'T' )
289         {
290           snprintf( str_message, SIZE_MESSAGE, str_fmt_line, pr->lines,
291                    "Time spent is not available." );
292           str_error = str_message;
293           return -2;
294         }
295       l = strtol( str_line+1, &ptr, 0 );
296       if ( ptr == str_line+1 || l == LONG_MAX || l < 0 )
297         {
298           snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
299                     pr->lines, str_bad_record );
300           str_error = str_message;
301           return -2;
302         }
303     }
304   else { l = 0; }
305   sec_elapsed = (unsigned int)l;
306   if ( root_turn ) { sec_w_total += (unsigned int)l; }
307   else             { sec_b_total += (unsigned int)l; }
308
309   iret = make_move_root( ptree, move, flag & ~flag_time );
310   if ( iret < 0 )
311     {
312       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
313                 pr->lines, str_error );
314       str_error = str_message;
315       return iret;
316     }
317
318   pr->moves++;
319
320   return record_misc;
321 }
322
323
324 int
325 interpret_CSA_move( tree_t * restrict ptree, unsigned int *pmove,
326                     const char *str )
327 {
328   int ifrom_file, ifrom_rank, ito_file, ito_rank, ipiece;
329   int ifrom, ito;
330   unsigned int move;
331   unsigned int *pmove_last;
332   unsigned int *p;
333
334   ifrom_file = str[0]-'0';
335   ifrom_rank = str[1]-'0';
336   ito_file   = str[2]-'0';
337   ito_rank   = str[3]-'0';
338
339   ito_file   = 9 - ito_file;
340   ito_rank   = ito_rank - 1;
341   ito        = ito_rank * 9 + ito_file;
342   ipiece     = str2piece( str+4 );
343   if ( ipiece < 0 )
344     {
345       str_error = str_illegal_move;
346       return -2;
347     }
348
349   if ( ! ifrom_file && ! ifrom_rank )
350     {
351       move  = To2Move(ito) | Drop2Move(ipiece);
352       ifrom = nsquare;
353     }
354   else {
355     ifrom_file = 9 - ifrom_file;
356     ifrom_rank = ifrom_rank - 1;
357     ifrom      = ifrom_rank * 9 + ifrom_file;
358     if ( abs(BOARD[ifrom]) + promote == ipiece )
359       {
360         ipiece -= promote;
361         move    = FLAG_PROMO;
362       }
363     else { move = 0; }
364
365     move |= ( To2Move(ito) | From2Move(ifrom) | Cap2Move(abs(BOARD[ito]))
366               | Piece2Move(ipiece) );
367   }
368
369   *pmove = 0;
370   pmove_last = ptree->amove;
371   pmove_last = GenCaptures(root_turn, pmove_last );
372   pmove_last = GenNoCaptures(root_turn, pmove_last );
373   pmove_last = GenCapNoProEx2(root_turn, pmove_last );
374   pmove_last = GenNoCapNoProEx2(root_turn, pmove_last );
375   pmove_last = GenDrop( root_turn, pmove_last );
376   for ( p = ptree->amove; p < pmove_last; p++ )
377     {
378       if ( *p == move )
379         {
380           *pmove = move;
381           break;
382         }
383     }
384     
385   if ( ! *pmove )
386     {
387       str_error = str_illegal_move;
388       if ( ipiece == pawn
389            && ifrom == nsquare
390            && ! BOARD[ito]
391            && ( root_turn ? IsHandPawn(HAND_W) : IsHandPawn(HAND_B) ) )
392         {
393           unsigned int u;
394
395           if ( root_turn )
396             {
397               u = BBToU( BB_WPAWN_ATK );
398               if ( u & (mask_file1>>ito_file) )
399                 {
400                   str_error = str_double_pawn;
401                 }
402               else if ( BOARD[ito+nfile] == king )
403                 {
404                   str_error = str_mate_drppawn;
405                 }
406             }
407           else {
408             u = BBToU( BB_BPAWN_ATK );
409             if ( u & (mask_file1>>ito_file) ) { str_error = str_double_pawn; }
410             else if ( BOARD[ito-nfile] == -king )
411               {
412                 str_error = str_mate_drppawn;
413               }
414           }
415         }
416       return -2;
417     }
418   
419   return 1;
420 }
421
422
423 const char *
424 str_CSA_move( unsigned int move )
425 {
426   static char str[7];
427   int ifrom, ito, ipiece_move, is_promote;
428
429   is_promote  = (int)I2IsPromote(move);
430   ipiece_move = (int)I2PieceMove(move);
431   ifrom       = (int)I2From(move);
432   ito         = (int)I2To(move);
433   
434   if ( is_promote )
435     {
436       snprintf( str, 7, "%d%d%d%d%s",
437                 9-aifile[ifrom], airank[ifrom]+1,
438                 9-aifile[ito],   airank[ito]  +1,
439                 astr_table_piece[ ipiece_move + promote ] );
440     }
441   else if ( ifrom < nsquare )
442     {
443       snprintf( str, 7, "%d%d%d%d%s",
444                 9-aifile[ifrom], airank[ifrom]+1,
445                 9-aifile[ito],   airank[ito]  +1,
446                 astr_table_piece[ ipiece_move ] );
447     }
448   else {
449     snprintf( str, 7, "00%d%d%s",
450               9-aifile[ito], airank[ito]+1,
451               astr_table_piece[ From2Drop(ifrom) ] );
452   }
453   
454   return str;
455 }
456
457
458 int
459 read_board_rep1( const char *str_line, min_posi_t *pmin_posi )
460 {
461   const char *p;
462   char str_piece[3];
463   int piece, ifile, irank, isquare;
464   signed char board[nsquare];
465   int add = 0, color = -1;
466
467   if( *str_line == 'S' ) { // [HGM] setup: allow 'SU' representation that adds pieces in stead of removing them
468     add = 1;
469     for ( isquare = 0; isquare < nsquare; isquare++ ) board[isquare] = empty;
470     pmin_posi->hand_white = pmin_posi->hand_black = 0;
471   } else
472   memcpy( board, &min_posi_no_handicap.asquare, nsquare );
473
474   for ( p = str_line + 2; p[0] != '\0'; p += 4 )
475     {
476       if ( p[1] == '\0' || p[2] == '\0' || p[3] == '\0' )
477         {
478           str_error = str_bad_board;
479           return -2;
480         }
481       if( add && p[0] == '+' ) // [HGM] setup: change color
482         {
483           color = 1;
484           p -= 3;
485           continue;
486         }
487       if( add && p[0] == '-' )
488         {
489           color = -1;
490           p -= 3;
491           continue;
492         }
493       str_piece[0] = p[2];
494       str_piece[1] = p[3];
495       str_piece[2] = '\0';
496       piece        = str2piece( str_piece );
497       ifile        = p[0]-'0';
498       irank        = p[1]-'0';
499       ifile        = 9 - ifile;
500       irank        = irank - 1;
501       isquare      = irank * nfile + ifile;
502       if ( add && piece != -2 && p[0] == '0' && p[1] == '0' )
503         { // [HGM] setup: holdings
504             static int unit[] = {0, 1, 1<<5, 1<<8, 1<<11, 1<<14, 1<<17, 1<<19};
505             if(piece < 1 || piece > 7) { // only unpromoted in hand!
506                 str_error = str_bad_board;
507                 return -2;
508             }
509             if(color > 0) pmin_posi->hand_black += unit[piece];
510             else          pmin_posi->hand_white += unit[piece];
511             continue;
512         }
513       if ( piece == -2 || ifile < file1 || ifile > file9 || irank < rank1
514            || irank > rank9 || abs(board[isquare]) != (add ? empty : piece) )
515         {
516           str_error = str_bad_board;
517           return -2;
518         }
519       if( add ) board[isquare] = piece*color; else
520       board[isquare] = empty;
521     }
522   
523   for ( isquare = 0; isquare < nsquare; isquare++ ) if ( board[isquare] )
524     {
525       if ( pmin_posi->asquare[isquare] )
526         {
527           str_error = str_bad_board;
528           return -2;
529         }
530       pmin_posi->asquare[isquare] = board[isquare];
531     }
532   
533   return 1;
534 }
535
536
537 #if defined(USI)
538 int CONV
539 usi2csa( const tree_t * restrict ptree, const char *str_usi, char *str_csa )
540 {
541   int sq_file, sq_rank, sq, pc;
542
543   if ( '1' <= str_usi[0] && str_usi[0] <= '9'
544        && 'a' <= str_usi[1] && str_usi[1] <= 'i'
545        && '1' <= str_usi[2] && str_usi[2] <= '9'
546        && 'a' <= str_usi[3] && str_usi[3] <= 'i' )
547     {
548       str_csa[0] = str_usi[0];
549       str_csa[1] = (char)( str_usi[1] + '1' - 'a' );
550       str_csa[2] = str_usi[2];
551       str_csa[3] = (char)( str_usi[3] + '1' - 'a' );
552
553       sq_file = str_csa[0]-'0';
554       sq_file = 9 - sq_file;
555       sq_rank = str_csa[1]-'0';
556       sq_rank = sq_rank - 1;
557       sq      = sq_rank * 9 + sq_file;
558       pc      = abs(BOARD[sq]);
559       if ( str_usi[4] == '+' ) { pc += promote; }
560
561       str_csa[4] = astr_table_piece[pc][0];
562       str_csa[5] = astr_table_piece[pc][1];
563       str_csa[6] = '\0';
564
565       return 1;
566     }
567
568   if ( isascii( (int)str_usi[0] )
569        && isalpha( (int)str_usi[0] )
570        && str_usi[1] == '*'
571        && '1' <= str_usi[2] && str_usi[2] <= '9'
572        && 'a' <= str_usi[3] && str_usi[3] <= 'i' )
573     {
574       str_csa[0] = '0';
575       str_csa[1] = '0';
576       str_csa[2] = str_usi[2];
577       str_csa[3] = (char)( str_usi[3] - 'a' + '1' );
578       
579       switch ( str_usi[0] )
580         {
581         case 'P':  pc = pawn;    break;
582         case 'L':  pc = lance;   break;
583         case 'N':  pc = knight;  break;
584         case 'S':  pc = silver;  break;
585         case 'G':  pc = gold;    break;
586         case 'B':  pc = bishop;  break;
587         case 'R':  pc = rook;    break;
588         default:   return -1;
589         }
590
591       str_csa[4] = astr_table_piece[pc][0];
592       str_csa[5] = astr_table_piece[pc][1];
593       str_csa[6] = '\0';
594
595       return 1;
596     }
597
598   snprintf( str_message, SIZE_MESSAGE, "%s: %s", str_illegal_move, str_usi );
599   str_error = str_message;
600   return -1;
601 }
602
603
604 int CONV
605 csa2usi( const tree_t * restrict ptree, const char *str_csa, char *str_usi )
606 {
607   if ( str_csa[0] == '0' && str_csa[1] == '0'
608        && '1' <= str_csa[2] && str_csa[2] <= '9'
609        && '1' <= str_csa[3] && str_csa[3] <= '9'
610        && 'A' <= str_csa[4] && str_csa[4] <= 'Z'
611        && 'A' <= str_csa[5] && str_csa[5] <= 'Z' )
612     {
613       switch ( str2piece( str_csa + 4 ) )
614         {
615         case pawn:    str_usi[0] = 'P';  break;
616         case lance:   str_usi[0] = 'L';  break;
617         case knight:  str_usi[0] = 'N';  break;
618         case silver:  str_usi[0] = 'S';  break;
619         case gold:    str_usi[0] = 'G';  break;
620         case bishop:  str_usi[0] = 'B';  break;
621         case rook:    str_usi[0] = 'R';  break;
622         default:  return -1;  break;
623         }
624
625       str_usi[1] = '*';
626       str_usi[2] = str_csa[2];
627       str_usi[3] = (char)( str_csa[3] + 'a' - '1' );
628       str_usi[4] = '\0';
629
630       return 1;
631     }
632
633
634   if ( '1' <= str_csa[0] && str_csa[0] <= '9'
635        && '1' <= str_csa[1] && str_csa[1] <= '9'
636        && '1' <= str_csa[2] && str_csa[2] <= '9'
637        && '1' <= str_csa[3] && str_csa[3] <= '9'
638        && 'A' <= str_csa[4] && str_csa[4] <= 'Z'
639        && 'A' <= str_csa[5] && str_csa[5] <= 'Z' )
640     {
641       int sq_file, sq_rank, sq, pc;
642
643
644       str_usi[0] = str_csa[0];
645       str_usi[1] = (char)( str_csa[1] + 'a' - '1' );
646       str_usi[2] = str_csa[2];
647       str_usi[3] = (char)( str_csa[3] + 'a' - '1' );
648
649       sq_file = str_csa[0]-'0';
650       sq_file = 9 - sq_file;
651
652       sq_rank = str_csa[1]-'0';
653       sq_rank = sq_rank - 1;
654       sq      = sq_rank * 9 + sq_file;
655       pc      = abs(BOARD[sq]);
656
657       if ( pc + promote == str2piece( str_csa + 4 ) )
658         {
659           str_usi[4] = '+';
660           str_usi[5] = '\0';
661         }
662       else { str_usi[4] = '\0'; }
663
664
665       return 1;
666     }
667
668   str_usi[0] = '*';
669   str_usi[1] = '\0';
670   return 1;
671 }
672 #endif
673
674
675 static void
676 out_CSA_header( const tree_t * restrict ptree, record_t *pr )
677 {
678   time_t t;
679
680   fprintf( pr->pf, "'Bonanza version " BNZ_VER "\n" );
681
682   if ( pr->str_name1[0] != '\0' )
683     {
684       fprintf( pr->pf, "N+%s\n", pr->str_name1 );
685     }
686
687   if ( pr->str_name2[0] != '\0' )
688     {
689       fprintf( pr->pf, "N-%s\n", pr->str_name2 );
690     }
691
692   t = time( NULL );
693   if ( t == (time_t)-1 ) { out_warning( "%s time() faild." ); }
694   else {
695 #if defined(_MSC_VER)
696     struct tm tm;
697     localtime_s( &tm, &t );
698     fprintf( pr->pf, "$START_TIME:%4d/%02d/%02d %02d:%02d:%02d\n",
699              tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
700              tm.tm_hour, tm.tm_min, tm.tm_sec );
701 #else
702     struct tm *ptm;
703     ptm = localtime( &t );
704     fprintf( pr->pf, "$START_TIME:%4d/%02d/%02d %02d:%02d:%02d\n",
705              ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
706              ptm->tm_hour, ptm->tm_min, ptm->tm_sec );
707 #endif
708   }
709
710   if ( ! memcmp( BOARD, min_posi_no_handicap.asquare, nsquare )
711        && min_posi_no_handicap.turn_to_move == root_turn
712        && min_posi_no_handicap.hand_black   == HAND_B
713        && min_posi_no_handicap.hand_white   == HAND_W )
714     {
715       fprintf( pr->pf, "PI\n" );
716       pr->lines++;
717     }
718   else {
719     out_board( ptree, pr->pf, 0, 1 );
720     pr->lines += 10;
721   }
722   
723   if ( root_turn ) { fprintf( pr->pf, "-\n" ); }
724   else             { fprintf( pr->pf, "+\n" ); }
725   pr->lines++;
726 }
727
728
729 static int
730 in_CSA_header( tree_t * restrict ptree, record_t *pr, int flag )
731 {
732   min_posi_t min_posi;
733   const char *str_name1, *str_name2;
734   char str_line[ SIZE_CSALINE ];
735   int iret, is_rep1_done, is_rep2_done, is_all_done, i, j;
736
737   for ( i = 0; i < MAX_ANSWER; i++ ) { pr->info.str_move[i][0] = '\0'; }
738   str_name1 = str_name2 = NULL;
739
740   /* version and info */
741   for ( ;; )
742     {
743       iret = read_CSA_line( pr, str_line );
744       if ( iret < 0 ) { return iret; }
745
746       if ( str_line[0] != 'N'
747            && str_line[0] != 'V'
748            && str_line[0] != '$' ) { break; }
749
750       if ( ! memcmp( str_line, "$ANSWER:", 8 ) )
751         {
752           for ( i = 0; i < MAX_ANSWER; i++ )
753             {
754               for ( j = 0; j < 8; j++ )
755                 {
756                   pr->info.str_move[i][j] = str_line[8+i*8+j];
757                 }
758               pr->info.str_move[i][7] = '\0';
759               if ( str_line[8+i*8+7] == '\0' ) { break; }
760             }
761           if ( i == MAX_ANSWER )
762             {
763               snprintf( str_message, SIZE_MESSAGE, str_fmt_line, pr->lines,
764                        "The number of answers reached MAX_ANSWER." );
765               str_error = str_message;
766               return -2;
767             }
768         }
769       else if ( ! memcmp( str_line, "N+", 2 ) )
770         {
771           strncpy( pr->str_name1, str_line+2, SIZE_PLAYERNAME-1 );
772           pr->str_name1[SIZE_PLAYERNAME-1] = '\0';
773           str_name1 = pr->str_name1;
774         }
775       else if ( ! memcmp( str_line, "N-", 2 ) )
776         {
777           strncpy( pr->str_name2, str_line+2, SIZE_PLAYERNAME-1 );
778           pr->str_name2[SIZE_PLAYERNAME-1] = '\0';
779           str_name2 = pr->str_name2;
780         }
781     }
782   if ( ! iret )
783     {
784       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
785                 pr->lines, str_unexpect_eof );
786       str_error = str_message;
787       return -2;
788     }
789
790   /* board representation */
791   memset( &min_posi.asquare, empty, nsquare );
792   min_posi.hand_black = min_posi.hand_white = 0;
793   is_rep1_done = is_rep2_done = is_all_done = 0;
794   while ( str_line[0] == 'P' )
795     {
796       if ( str_line[1] == 'I' && ! is_rep2_done && ! is_all_done )
797         {
798           is_rep1_done = 1;
799           iret = read_board_rep1( str_line, &min_posi );
800         }
801       else if ( isdigit( (int)str_line[1] ) && str_line[1] != '0'
802                 && ! is_rep1_done && ! is_all_done )
803         {
804           is_rep2_done = 1;
805           iret = read_board_rep2( str_line, &min_posi );
806         }
807       else if ( str_line[1] == '+' || str_line[1] == '-' )
808         {
809           is_all_done = iret = read_board_rep3( str_line, &min_posi );
810         }
811       else { break; }
812       if ( iret < 0 )
813         {
814           snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
815                     pr->lines, str_error );
816           str_error = str_message;
817           return iret;
818         }
819
820       iret = read_CSA_line( pr, str_line );
821       if ( iret < 0 ) { return iret; }
822       if ( ! iret )
823         {
824           snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
825                     pr->lines, str_unexpect_eof );
826           str_error = str_message;
827           return -2;
828         }
829     }
830   
831   /* turn to move */
832   if ( strcmp( str_line, "+" ) && strcmp( str_line, "-" ) )
833     {
834       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
835                 pr->lines, str_bad_record );
836       str_error = str_message;
837       return -2;
838     }
839   min_posi.turn_to_move = (char)( ( str_line[0] == '+' ) ? black : white );
840
841   return ini_game( ptree, &min_posi, flag, str_name1, str_name2 );
842 }
843
844
845 static int
846 read_board_rep3( const char *str_line, min_posi_t *pmin_posi )
847 {
848   int is_all_done, irank, ifile, isquare, piece, n, color;
849   int npawn, nlance, nknight, nsilver, ngold, nbishop, nrook;
850   unsigned int handv, hand_white, hand_black;
851   char str_piece[3];
852
853   is_all_done = 0;
854   str_piece[2] = '\0';
855
856   color = str_line[1] == '+' ? black : white;
857   for ( n = 2; str_line[n] != '\0'; n += 4 ) {
858     if ( str_line[n+1] == '\0' || str_line[n+2] == '\0'
859          || str_line[n+3] == '\0' || is_all_done )
860       {
861         str_error = str_bad_board;
862         return -2;
863       }
864     if ( str_line[n] == '0' && str_line[n+1] == '0'
865          && str_line[n+2] == 'A' && str_line[n+3] == 'L' ) {
866       hand_black = pmin_posi->hand_black;
867       hand_white = pmin_posi->hand_white;
868       npawn   = (int)(I2HandPawn(hand_black)   + I2HandPawn(hand_white));
869       nlance  = (int)(I2HandLance(hand_black)  + I2HandLance(hand_white));
870       nknight = (int)(I2HandKnight(hand_black) + I2HandKnight(hand_white));
871       nsilver = (int)(I2HandSilver(hand_black) + I2HandSilver(hand_white));
872       ngold   = (int)(I2HandGold(hand_black)   + I2HandGold(hand_white));
873       nbishop = (int)(I2HandBishop(hand_black) + I2HandBishop(hand_white));
874       nrook   = (int)(I2HandRook(hand_black)   + I2HandRook(hand_white));
875       for ( isquare = 0; isquare < nsquare; isquare++ )
876         switch ( abs( pmin_posi->asquare[isquare] ) )
877           {
878           case pawn:    case pro_pawn:    npawn++;    break;
879           case lance:   case pro_lance:   nlance++;   break;
880           case knight:  case pro_knight:  nknight++;  break;
881           case silver:  case pro_silver:  nsilver++;  break;
882           case gold:                      ngold++;    break;
883           case bishop:  case horse:       nbishop++;  break;
884           case rook:    case dragon:      nrook++;    break;
885           default:
886             assert( pmin_posi->asquare[isquare] == empty );
887             break;
888           }
889       handv  = flag_hand_pawn   * ( npawn_max   -npawn );
890       handv += flag_hand_lance  * ( nlance_max  -nlance );
891       handv += flag_hand_knight * ( nknight_max -nknight );
892       handv += flag_hand_silver * ( nsilver_max -nsilver );
893       handv += flag_hand_gold   * ( ngold_max   -ngold );
894       handv += flag_hand_bishop * ( nbishop_max -nbishop );
895       handv += flag_hand_rook   * ( nrook_max   -nrook );
896       if ( color ) { pmin_posi->hand_white += handv; }
897       else         { pmin_posi->hand_black += handv; }
898       is_all_done = 1;
899       continue;
900     }
901     
902     ifile        = str_line[n+0]-'0';
903     irank        = str_line[n+1]-'0';
904     str_piece[0] = str_line[n+2];
905     str_piece[1] = str_line[n+3];
906     piece        = str2piece( str_piece );
907     
908     /* hand */
909     if ( ifile == 0 && ifile == 0 )
910       {
911         switch ( piece )
912           {
913           case pawn:    handv = flag_hand_pawn;    break;
914           case lance:   handv = flag_hand_lance;   break;
915           case knight:  handv = flag_hand_knight;  break;
916           case silver:  handv = flag_hand_silver;  break;
917           case gold:    handv = flag_hand_gold;    break;
918           case bishop:  handv = flag_hand_bishop;  break;
919           case rook:    handv = flag_hand_rook;    break;
920           default:
921             str_error = str_bad_board;
922             return -2;
923           }
924         if ( color ) { pmin_posi->hand_white += handv; }
925         else         { pmin_posi->hand_black += handv; }
926       }
927     /* board */
928     else {
929       ifile   = 9 - ifile;
930       irank   = irank - 1;
931       isquare = irank * nfile + ifile;
932       if ( piece == -2 || ifile < file1 || ifile > file9
933            || irank < rank1 || irank > rank9 || pmin_posi->asquare[isquare] )
934         {
935           str_error = str_bad_board;
936           return -2;
937         }
938       pmin_posi->asquare[isquare] = (signed char)( color ? -piece : piece );
939     }
940   }
941   
942   return is_all_done;
943 }
944
945
946 static int
947 read_board_rep2( const char * str_line, min_posi_t *pmin_posi )
948 {
949   int irank, ifile, piece;
950   char str_piece[3];
951
952   str_piece[2] = '\0';
953   
954   irank = str_line[1] - '1';
955
956   for ( ifile = 0; ifile < nfile; ifile++ )
957     if ( str_line[2+ifile*3] == '+' || str_line[2+ifile*3] == '-' )
958       {
959         str_piece[0] = str_line[2+ifile*3+1];
960         str_piece[1] = str_line[2+ifile*3+2];
961         piece = str2piece( str_piece );
962         if ( piece < 0 || pmin_posi->asquare[ irank*nfile + ifile ] )
963           {
964             str_error = str_bad_board;
965             return -2;
966           }
967         pmin_posi->asquare[ irank*nfile + ifile ]
968           = (signed char)( str_line[ 2 + ifile*3 ] == '-' ? -piece : piece );
969       }
970     else { pmin_posi->asquare[ irank*nfile + ifile ] = empty; }
971
972   return 1;
973 }
974
975
976 static int
977 str2piece( const char *str )
978 {
979   int i;
980
981   for ( i = 0; i < 16; i++ )
982     {
983       if ( ! strcmp( astr_table_piece[i], str ) ) { break; }
984     }
985   if ( i == 0 || i == piece_null || i == 16 ) { i = -2; }
986
987   return i;
988 }
989
990
991 /* reads a csa line in str, trancates trailing spases.
992  * return value:
993  *   0  EOF, no line is read.
994  *   1  a csa line is read in str
995  *  -2  buffer overflow
996  */
997 static int
998 read_CSA_line( record_t *pr, char *str )
999 {
1000   int i, c, do_comma;
1001
1002   for ( ;; )
1003     {
1004       c = skip_comment( pr );
1005       if ( isgraph( c ) || c == EOF ) { break; }
1006     }
1007   if ( c == EOF ) { return 0; }
1008   
1009   do_comma = ( c == 'N' || c == '$' ) ? 0 : 1;
1010
1011   for ( i = 0; i < SIZE_CSALINE-1; i++ )
1012     {
1013       if ( c == EOF || c == '\n' || ( do_comma && c == ',' ) ) { break; }
1014       str[i] = (char)c;
1015       c = read_char( pr );
1016     }
1017
1018   if ( i == SIZE_CSALINE-1 )
1019     {
1020       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
1021                 pr->lines, str_ovrflw_line );
1022       return -2;
1023     }
1024
1025   i--;
1026   while ( isascii( (int)str[i] ) && isspace( (int)str[i] ) ) { i--; }
1027   str[i+1] = '\0';
1028
1029   return 1;
1030 }
1031
1032
1033 static int
1034 skip_comment( record_t *pr )
1035 {
1036   int c;
1037
1038   c = read_char( pr );
1039   for ( ;; )
1040     {
1041       if ( c != '\'' ) { break; }
1042       for ( ;; )
1043         {
1044           c = read_char( pr );
1045           if ( c == EOF || c == '\n' ) { break; }
1046         }
1047     }
1048   
1049   return c;
1050 }
1051
1052
1053 static int
1054 read_char( record_t *pr )
1055 {
1056   int c;
1057
1058   c = fgetc( pr->pf );
1059   if ( c == '\n' ) { pr->lines++; }
1060
1061   return c;
1062 }