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