Implement undo command
[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_plus( tree_t * restrict ptree, unsigned int move, int ply,
425                    int turn )
426 {
427   static char str[ 13 ];
428   const unsigned int *pmove_last;
429   unsigned int amove[ MAX_LEGAL_EVASION ];
430   char *p;
431   int is_promo, ipiece_cap, ipiece_move, ifrom, ito, turn_next;
432
433   is_promo    = (int)I2IsPromote(move);
434   ipiece_move = (int)I2PieceMove(move);
435   ifrom       = (int)I2From(move);
436   ito         = (int)I2To(move);
437   ipiece_cap  = (int)UToCap(move);
438   turn_next   = Flip( turn );
439
440   if ( is_promo && ipiece_cap )
441     {
442       snprintf( str, 13, "%d%d%d%d%spx%s",
443                9-aifile[ifrom], airank[ifrom]+1,
444                9-aifile[ito],   airank[ito]  +1,
445                astr_table_piece[ ipiece_move + promote ],
446                astr_table_piece[ ipiece_cap ] );
447       p = str + 10;
448     }
449   else if ( ipiece_cap )
450     {
451       snprintf( str, 13, "%d%d%d%d%sx%s",
452                9-aifile[ifrom], airank[ifrom]+1,
453                9-aifile[ito],   airank[ito]  +1,
454                astr_table_piece[ ipiece_move ],
455                astr_table_piece[ ipiece_cap ] );
456       p = str + 9;
457     }
458   else if ( is_promo )
459     {
460       snprintf( str, 13, "%d%d%d%d%sp",
461                9-aifile[ifrom], airank[ifrom]+1,
462                9-aifile[ito],   airank[ito]  +1,
463                astr_table_piece[ ipiece_move + promote ] );
464       p = str + 7;
465     }
466   else if ( ifrom < nsquare )
467     {
468       snprintf( str, 13, "%d%d%d%d%s",
469                9-aifile[ifrom], airank[ifrom]+1,
470                9-aifile[ito],   airank[ito]  +1,
471                astr_table_piece[ ipiece_move ] );
472       p = str + 6;
473     }
474   else {
475     snprintf( str, 13, "00%d%d%s", 9-aifile[ito], airank[ito]+1,
476              astr_table_piece[ From2Drop(ifrom) ] );
477     p = str + 6;
478   }
479
480   MakeMove( turn, move, ply );
481   if ( InCheck( turn_next ) )
482     {
483       pmove_last = GenEvasion( turn_next, amove );
484       if ( pmove_last == amove ) { *p++ = '#'; }
485       else                       { *p++ = '!'; }
486       *p   = '\0';
487     }
488   UnMakeMove( turn, move, ply );
489
490   return str;
491 }
492
493
494 const char *
495 str_CSA_move( unsigned int move )
496 {
497   static char str[7];
498   int ifrom, ito, ipiece_move, is_promote;
499
500   is_promote  = (int)I2IsPromote(move);
501   ipiece_move = (int)I2PieceMove(move);
502   ifrom       = (int)I2From(move);
503   ito         = (int)I2To(move);
504   
505   if ( is_promote )
506     {
507       snprintf( str, 7, "%d%d%d%d%s",
508                 9-aifile[ifrom], airank[ifrom]+1,
509                 9-aifile[ito],   airank[ito]  +1,
510                 astr_table_piece[ ipiece_move + promote ] );
511     }
512   else if ( ifrom < nsquare )
513     {
514       snprintf( str, 7, "%d%d%d%d%s",
515                 9-aifile[ifrom], airank[ifrom]+1,
516                 9-aifile[ito],   airank[ito]  +1,
517                 astr_table_piece[ ipiece_move ] );
518     }
519   else {
520     snprintf( str, 7, "00%d%d%s",
521               9-aifile[ito], airank[ito]+1,
522               astr_table_piece[ From2Drop(ifrom) ] );
523   }
524   
525   return str;
526 }
527
528
529 int
530 read_board_rep1( const char *str_line, min_posi_t *pmin_posi )
531 {
532   const char *p;
533   char str_piece[3];
534   int piece, ifile, irank, isquare;
535   signed char board[nsquare];
536
537   memcpy( board, &min_posi_no_handicap.asquare, nsquare );
538
539   for ( p = str_line + 2; p[0] != '\0'; p += 4 )
540     {
541       if ( p[1] == '\0' || p[2] == '\0' || p[3] == '\0' )
542         {
543           str_error = str_bad_board;
544           return -2;
545         }
546       str_piece[0] = p[2];
547       str_piece[1] = p[3];
548       str_piece[2] = '\0';
549       piece        = str2piece( str_piece );
550       ifile        = p[0]-'0';
551       irank        = p[1]-'0';
552       ifile        = 9 - ifile;
553       irank        = irank - 1;
554       isquare      = irank * nfile + ifile;
555       if ( piece == -2 || ifile < file1 || ifile > file9 || irank < rank1
556            || irank > rank9 || abs(board[isquare]) != piece )
557         {
558           str_error = str_bad_board;
559           return -2;
560         }
561       board[isquare] = empty;
562     }
563   
564   for ( isquare = 0; isquare < nsquare; isquare++ ) if ( board[isquare] )
565     {
566       if ( pmin_posi->asquare[isquare] )
567         {
568           str_error = str_bad_board;
569           return -2;
570         }
571       pmin_posi->asquare[isquare] = board[isquare];
572     }
573   
574   return 1;
575 }
576
577
578 static void
579 out_CSA_header( const tree_t * restrict ptree, record_t *pr )
580 {
581   time_t t;
582
583   fprintf( pr->pf, "'Bonanza version " BNZ_VER "\n" );
584
585   if ( pr->str_name1[0] != '\0' )
586     {
587       fprintf( pr->pf, "N+%s\n", pr->str_name1 );
588     }
589
590   if ( pr->str_name2[0] != '\0' )
591     {
592       fprintf( pr->pf, "N-%s\n", pr->str_name2 );
593     }
594
595   t = time( NULL );
596   if ( t == (time_t)-1 ) { out_warning( "%s time() faild." ); }
597   else {
598 #if defined(_MSC_VER)
599     struct tm tm;
600     localtime_s( &tm, &t );
601     fprintf( pr->pf, "$START_TIME:%4d/%02d/%02d %02d:%02d:%02d\n",
602              tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
603              tm.tm_hour, tm.tm_min, tm.tm_sec );
604 #else
605     struct tm *ptm;
606     ptm = localtime( &t );
607     fprintf( pr->pf, "$START_TIME:%4d/%02d/%02d %02d:%02d:%02d\n",
608              ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
609              ptm->tm_hour, ptm->tm_min, ptm->tm_sec );
610 #endif
611   }
612
613   if ( ! memcmp( BOARD, min_posi_no_handicap.asquare, nsquare )
614        && min_posi_no_handicap.turn_to_move == root_turn
615        && min_posi_no_handicap.hand_black   == HAND_B
616        && min_posi_no_handicap.hand_white   == HAND_W )
617     {
618       fprintf( pr->pf, "PI\n" );
619       pr->lines++;
620     }
621   else {
622     out_board( ptree, pr->pf, 0, 1 );
623     pr->lines += 10;
624   }
625   
626   if ( root_turn ) { fprintf( pr->pf, "-\n" ); }
627   else             { fprintf( pr->pf, "+\n" ); }
628   pr->lines++;
629 }
630
631
632 static int
633 in_CSA_header( tree_t * restrict ptree, record_t *pr, int flag )
634 {
635   min_posi_t min_posi;
636   const char *str_name1, *str_name2;
637   char str_line[ SIZE_CSALINE ];
638   int iret, is_rep1_done, is_rep2_done, is_all_done, i, j;
639
640   for ( i = 0; i < MAX_ANSWER; i++ ) { pr->info.str_move[i][0] = '\0'; }
641   str_name1 = str_name2 = NULL;
642
643   /* version and info */
644   for ( ;; )
645     {
646       iret = read_CSA_line( pr, str_line );
647       if ( iret < 0 ) { return iret; }
648
649       if ( str_line[0] != 'N'
650            && str_line[0] != 'V'
651            && str_line[0] != '$' ) { break; }
652
653       if ( ! memcmp( str_line, "$ANSWER:", 8 ) )
654         {
655           for ( i = 0; i < MAX_ANSWER; i++ )
656             {
657               for ( j = 0; j < 8; j++ )
658                 {
659                   pr->info.str_move[i][j] = str_line[8+i*8+j];
660                 }
661               pr->info.str_move[i][7] = '\0';
662               if ( str_line[8+i*8+7] == '\0' ) { break; }
663             }
664           if ( i == MAX_ANSWER )
665             {
666               snprintf( str_message, SIZE_MESSAGE, str_fmt_line, pr->lines,
667                        "The number of answers reached MAX_ANSWER." );
668               str_error = str_message;
669               return -2;
670             }
671         }
672       else if ( ! memcmp( str_line, "N+", 2 ) )
673         {
674           strncpy( pr->str_name1, str_line+2, SIZE_PLAYERNAME-1 );
675           pr->str_name1[SIZE_PLAYERNAME-1] = '\0';
676           str_name1 = pr->str_name1;
677         }
678       else if ( ! memcmp( str_line, "N-", 2 ) )
679         {
680           strncpy( pr->str_name2, str_line+2, SIZE_PLAYERNAME-1 );
681           pr->str_name2[SIZE_PLAYERNAME-1] = '\0';
682           str_name2 = pr->str_name2;
683         }
684     }
685   if ( ! iret )
686     {
687       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
688                 pr->lines, str_unexpect_eof );
689       str_error = str_message;
690       return -2;
691     }
692
693   /* board representation */
694   memset( &min_posi.asquare, empty, nsquare );
695   min_posi.hand_black = min_posi.hand_white = 0;
696   is_rep1_done = is_rep2_done = is_all_done = 0;
697   while ( str_line[0] == 'P' )
698     {
699       if ( str_line[1] == 'I' && ! is_rep2_done && ! is_all_done )
700         {
701           is_rep1_done = 1;
702           iret = read_board_rep1( str_line, &min_posi );
703         }
704       else if ( isdigit( (int)str_line[1] ) && str_line[1] != '0'
705                 && ! is_rep1_done && ! is_all_done )
706         {
707           is_rep2_done = 1;
708           iret = read_board_rep2( str_line, &min_posi );
709         }
710       else if ( str_line[1] == '+' || str_line[1] == '-' )
711         {
712           is_all_done = iret = read_board_rep3( str_line, &min_posi );
713         }
714       else { break; }
715       if ( iret < 0 )
716         {
717           snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
718                     pr->lines, str_error );
719           str_error = str_message;
720           return iret;
721         }
722
723       iret = read_CSA_line( pr, str_line );
724       if ( iret < 0 ) { return iret; }
725       if ( ! iret )
726         {
727           snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
728                     pr->lines, str_unexpect_eof );
729           str_error = str_message;
730           return -2;
731         }
732     }
733   
734   /* turn to move */
735   if ( strcmp( str_line, "+" ) && strcmp( str_line, "-" ) )
736     {
737       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
738                 pr->lines, str_bad_record );
739       str_error = str_message;
740       return -2;
741     }
742   min_posi.turn_to_move = (char)( ( str_line[0] == '+' ) ? black : white );
743
744   return ini_game( ptree, &min_posi, flag, str_name1, str_name2 );
745 }
746
747
748 static int
749 read_board_rep3( const char *str_line, min_posi_t *pmin_posi )
750 {
751   int is_all_done, irank, ifile, isquare, piece, n, color;
752   int npawn, nlance, nknight, nsilver, ngold, nbishop, nrook;
753   unsigned int handv, hand_white, hand_black;
754   char str_piece[3];
755
756   is_all_done = 0;
757   str_piece[2] = '\0';
758
759   color = str_line[1] == '+' ? black : white;
760   for ( n = 2; str_line[n] != '\0'; n += 4 ) {
761     if ( str_line[n+1] == '\0' || str_line[n+2] == '\0'
762          || str_line[n+3] == '\0' || is_all_done )
763       {
764         str_error = str_bad_board;
765         return -2;
766       }
767     if ( str_line[n] == '0' && str_line[n+1] == '0'
768          && str_line[n+2] == 'A' && str_line[n+3] == 'L' ) {
769       hand_black = pmin_posi->hand_black;
770       hand_white = pmin_posi->hand_white;
771       npawn   = (int)(I2HandPawn(hand_black)   + I2HandPawn(hand_white));
772       nlance  = (int)(I2HandLance(hand_black)  + I2HandLance(hand_white));
773       nknight = (int)(I2HandKnight(hand_black) + I2HandKnight(hand_white));
774       nsilver = (int)(I2HandSilver(hand_black) + I2HandSilver(hand_white));
775       ngold   = (int)(I2HandGold(hand_black)   + I2HandGold(hand_white));
776       nbishop = (int)(I2HandBishop(hand_black) + I2HandBishop(hand_white));
777       nrook   = (int)(I2HandRook(hand_black)   + I2HandRook(hand_white));
778       for ( isquare = 0; isquare < nsquare; isquare++ )
779         switch ( abs( pmin_posi->asquare[isquare] ) )
780           {
781           case pawn:    case pro_pawn:    npawn++;    break;
782           case lance:   case pro_lance:   nlance++;   break;
783           case knight:  case pro_knight:  nknight++;  break;
784           case silver:  case pro_silver:  nsilver++;  break;
785           case gold:                      ngold++;    break;
786           case bishop:  case horse:       nbishop++;  break;
787           case rook:    case dragon:      nrook++;    break;
788           default:
789             assert( pmin_posi->asquare[isquare] == empty );
790             break;
791           }
792       handv  = flag_hand_pawn   * ( npawn_max   -npawn );
793       handv += flag_hand_lance  * ( nlance_max  -nlance );
794       handv += flag_hand_knight * ( nknight_max -nknight );
795       handv += flag_hand_silver * ( nsilver_max -nsilver );
796       handv += flag_hand_gold   * ( ngold_max   -ngold );
797       handv += flag_hand_bishop * ( nbishop_max -nbishop );
798       handv += flag_hand_rook   * ( nrook_max   -nrook );
799       if ( color ) { pmin_posi->hand_white += handv; }
800       else         { pmin_posi->hand_black += handv; }
801       is_all_done = 1;
802       continue;
803     }
804     
805     ifile        = str_line[n+0]-'0';
806     irank        = str_line[n+1]-'0';
807     str_piece[0] = str_line[n+2];
808     str_piece[1] = str_line[n+3];
809     piece        = str2piece( str_piece );
810     
811     /* hand */
812     if ( ifile == 0 && ifile == 0 )
813       {
814         switch ( piece )
815           {
816           case pawn:    handv = flag_hand_pawn;    break;
817           case lance:   handv = flag_hand_lance;   break;
818           case knight:  handv = flag_hand_knight;  break;
819           case silver:  handv = flag_hand_silver;  break;
820           case gold:    handv = flag_hand_gold;    break;
821           case bishop:  handv = flag_hand_bishop;  break;
822           case rook:    handv = flag_hand_rook;    break;
823           default:
824             str_error = str_bad_board;
825             return -2;
826           }
827         if ( color ) { pmin_posi->hand_white += handv; }
828         else         { pmin_posi->hand_black += handv; }
829       }
830     /* board */
831     else {
832       ifile   = 9 - ifile;
833       irank   = irank - 1;
834       isquare = irank * nfile + ifile;
835       if ( piece == -2 || ifile < file1 || ifile > file9
836            || irank < rank1 || irank > rank9 || pmin_posi->asquare[isquare] )
837         {
838           str_error = str_bad_board;
839           return -2;
840         }
841       pmin_posi->asquare[isquare] = (signed char)( color ? -piece : piece );
842     }
843   }
844   
845   return is_all_done;
846 }
847
848
849 static int
850 read_board_rep2( const char * str_line, min_posi_t *pmin_posi )
851 {
852   int irank, ifile, piece;
853   char str_piece[3];
854
855   str_piece[2] = '\0';
856   
857   irank = str_line[1] - '1';
858
859   for ( ifile = 0; ifile < nfile; ifile++ )
860     if ( str_line[2+ifile*3] == '+' || str_line[2+ifile*3] == '-' )
861       {
862         str_piece[0] = str_line[2+ifile*3+1];
863         str_piece[1] = str_line[2+ifile*3+2];
864         piece = str2piece( str_piece );
865         if ( piece < 0 || pmin_posi->asquare[ irank*nfile + ifile ] )
866           {
867             str_error = str_bad_board;
868             return -2;
869           }
870         pmin_posi->asquare[ irank*nfile + ifile ]
871           = (signed char)( str_line[ 2 + ifile*3 ] == '-' ? -piece : piece );
872       }
873     else { pmin_posi->asquare[ irank*nfile + ifile ] = empty; }
874
875   return 1;
876 }
877
878
879 static int
880 str2piece( const char *str )
881 {
882   int i;
883
884   for ( i = 0; i < 16; i++ )
885     {
886       if ( ! strcmp( astr_table_piece[i], str ) ) { break; }
887     }
888   if ( i == 0 || i == piece_null || i == 16 ) { i = -2; }
889
890   return i;
891 }
892
893
894 /* reads a csa line in str, trancates trailing spases.
895  * return value:
896  *   0  EOF, no line is read.
897  *   1  a csa line is read in str
898  *  -2  buffer overflow
899  */
900 static int
901 read_CSA_line( record_t *pr, char *str )
902 {
903   int i, c, do_comma;
904
905   for ( ;; )
906     {
907       c = skip_comment( pr );
908       if ( isgraph( c ) || c == EOF ) { break; }
909     }
910   if ( c == EOF ) { return 0; }
911   
912   do_comma = ( c == 'N' || c == '$' ) ? 0 : 1;
913
914   for ( i = 0; i < SIZE_CSALINE-1; i++ )
915     {
916       if ( c == EOF || c == '\n' || ( do_comma && c == ',' ) ) { break; }
917       str[i] = (char)c;
918       c = read_char( pr );
919     }
920
921   if ( i == SIZE_CSALINE-1 )
922     {
923       snprintf( str_message, SIZE_MESSAGE, str_fmt_line,
924                 pr->lines, str_ovrflw_line );
925       return -2;
926     }
927
928   i--;
929   while ( isascii( (int)str[i] ) && isspace( (int)str[i] ) ) { i--; }
930   str[i+1] = '\0';
931
932   return 1;
933 }
934
935
936 static int
937 skip_comment( record_t *pr )
938 {
939   int c;
940
941   c = read_char( pr );
942   for ( ;; )
943     {
944       if ( c != '\'' ) { break; }
945       for ( ;; )
946         {
947           c = read_char( pr );
948           if ( c == EOF || c == '\n' ) { break; }
949         }
950     }
951   
952   return c;
953 }
954
955
956 static int
957 read_char( record_t *pr )
958 {
959   int c;
960
961   c = fgetc( pr->pf );
962   if ( c == '\n' ) { pr->lines++; }
963
964   return c;
965 }