/* * CHEST, chess analyst. For Copyright notice read file "COPYRIGHT". * * $Source: /home/heiner/ca/chest/RCS/input.c,v $ * $Id: input.c,v 3.25 1999/12/25 12:43:29 heiner Exp $ * * read a board and a job */ #include #include "types.h" #include "str.h" #include "board.h" #include "xatt.h" #include "job.h" #include "move.h" #include "lang.h" #include "output.h" #include "epdio.h" #include "input.h" #define INP_BUF_SIZ 4096 /* * Initialize a board to the empty default state. */ Eximpl void empty_board( register Board* bp ) { register int pos; register int i; register int j; register Field* p; bp->b_tomove = white; bp->b_ep = NO_POS; bp->b_castle[white] = 0; bp->b_castle[black] = 0; bp->b_max_piece[white] = 0; bp->b_max_piece[black] = 0; bp->b_cur_piece[white] = 0; bp->b_cur_piece[black] = 0; bp->b_bau[white].fs_long[0] = 0; bp->b_bau[white].fs_long[1] = 0; bp->b_bau[black].fs_long[0] = 0; bp->b_bau[black].fs_long[1] = 0; bp->b_fig.fs_long[0] = 0; bp->b_fig.fs_long[1] = 0; for( i = 0 ; i < (2*MAX_PIECE) ; ++i ) { bp->b_piece[i] = NO_POS; } for( pos=0 ; posb_f[pos] ); p->f_c = border; p->f_f = no_figure; F_IATTioXX(bp, p, XATT_TP_intern) = 0; F_DATTioXX(bp, p, XATT_TP_intern) = 0; } for( i = 0 ; i < 8 ; ++i ) { for( j = 0 ; j < 8 ; ++j ) { bp->b_f[MK_POS(i,j)].f_c = empty; bp->b_f[MK_POS(i,j)].f_pos64 = MK_POS64(i,j); } } for( i=0 ; i<8 ; ++i ) { bp->b_packed.pb_long[i] = 0; } #if WITH_ZHASH bp->b_zhash = 0; #endif for( i=0 ; ib_fig_cnt[white][i] = 0; bp->b_fig_cnt[black][i] = 0; } /* spare the last element in the downdate stack: */ bp->b_ddstk.dds_ptr = &(bp->b_ddstk.dds_stack[MAX_DD_ELEMS - 1]); XATT_empty(bp); } static void set_nextfig( register Board* bp, Colour colour, Figure fig, Position pos ) { register Field* p; p = &(bp->b_f[pos]); p->f_c = colour; p->f_f = fig; p->f_idx = COLOUR_IDX(colour) + bp->b_max_piece[colour]; bp->b_piece[p->f_idx] = pos; bp->b_max_piece[colour] += 1; bp->b_cur_piece[colour] += 1; bp->b_fig.fs_line[LIN64(p->f_pos64)] |= (1 << COL64(p->f_pos64)); if( fig == bauer ) { bp->b_bau[colour].fs_line[LIN64(p->f_pos64)] |= (1 << COL64(p->f_pos64)); } ins_att(bp, p); pacb_sync(&(bp->b_packed), p); ZH_UPD(bp->b_zhash, colour, fig, p->f_pos64); bp->b_fig_cnt[colour][fig] += 1; } static int inp__lang = LANG_NONE; static void set_inp_lang( int lang ) { if( is_lang(lang) ) { inp__lang = lang; } } static FILE* inp_file = 0; /* current input stream */ static Bool inp_opened = 0; static long inp_lino = 0; /* current line number */ static char* inp_bufp = 0; /* current line buffer */ static Bool inp_eofseen = FALSE; /* physical | logical */ static void inp_tell_input( const char* filename ) { if( ! f_bulkmode ) { printf("Input file: "); if( filename ) { printf("'%s'\n", filename); }else { printf("STDIN\n"); } oflush(); } } Eximpl void inp_start_input( const char* filename ) { if( inp_file ) { /* check closing old input */ if( inp_opened ) { (void) fclose(inp_file); } inp_file = 0; } inp_opened = FALSE; inp_lino = 0; inp_bufp = 0; inp_eofseen = FALSE; if( str_equal(filename, "") || str_equal(filename, "-") ) { inp_file = stdin; inp_tell_input((char*)0); }else { inp_file = fopen(filename, "r"); if( ! inp_file ) { inp_eofseen = TRUE; /* do not continue to scan this input */ printf("Cannot open '%s' for reading, ignored.\n", filename); oflush(); }else { inp_opened = TRUE; inp_tell_input(filename); } } } Eximpl void inp_tell_error( const char* what ) { if( !what || !*what ) { what = "syntax error"; } printf("Input error in line %3ld: %s\n", (long)inp_lino, what); if( inp_bufp ) { printf("Input line = '%s'\n", inp_bufp); } } static Bool is_space( char c ) { return IS_SPACE(c); } static const char* skip_space( const char* p ) { SKIP_SPACE(p); return p; } static void trim_line( char* buf ) { if( buf ) { register char* p; p = buf + str_len(buf); /* -> 0-byte */ /* * Get rid of line terminator/separator \r\n ... * ... and also eat trailing white space ... */ while( (p > buf) && ((p[-1] == '\n') || (p[-1] == '\r') || IS_SPACE(p[-1])) ) { *--p = 0; } } } static Bool /* whether recognized & stored */ cis_lang( char c, int* langp ) { int lang; lang = lang_of_char(c); if( is_lang(lang) ) { *langp = lang; return TRUE; } return FALSE; } static Bool /* whether recognized & stored */ cis_colour( char c, Colour* colourp ) { Colour colour; int lang; int mi; int ma; if( inp__lang == LANG_NONE ) { mi = 1; ma = LANGS-1; }else { mi = ma = inp__lang; } for( lang=mi ; lang <= ma ; ++lang ) { if( (lang_chr_colour(lang, colour=white) == c) || (lang_chr_colour(lang, colour=black) == c) ) { *colourp = colour; return TRUE; /* yes, recognized */ } } return FALSE; /* not recognized */ } static Bool /* whether recognized & stored */ cis_figure( char c, Figure* figurep ) { Figure f; int lang; int mi; int ma; if( inp__lang == LANG_NONE ) { mi = 1; ma = LANGS-1; }else { mi = ma = inp__lang; } for( lang=mi ; lang <= ma ; ++lang ) { if( (lang_chr_figure(lang, f=bauer ) == c) || (lang_chr_figure(lang, f=springer) == c) || (lang_chr_figure(lang, f=laeufer ) == c) || (lang_chr_figure(lang, f=turm ) == c) || (lang_chr_figure(lang, f=dame ) == c) || (lang_chr_figure(lang, f=koenig ) == c) ) { *figurep = f; return TRUE; /* yes, recognized */ } } return FALSE; /* not recognized */ } static Bool /* whether recognized & stored */ cis_Figure( char c, Figure* figurep ) { Figure f; int lang; int mi; int ma; if( inp__lang == LANG_NONE ) { mi = 1; ma = LANGS-1; }else { mi = ma = inp__lang; } for( lang=mi ; lang <= ma ; ++lang ) { if( (lang_chr_Figure(lang, f=bauer ) == c) || (lang_chr_Figure(lang, f=springer) == c) || (lang_chr_Figure(lang, f=laeufer ) == c) || (lang_chr_Figure(lang, f=turm ) == c) || (lang_chr_Figure(lang, f=dame ) == c) || (lang_chr_Figure(lang, f=koenig ) == c) ) { *figurep = f; return TRUE; /* yes, recognized */ } } return FALSE; /* not recognized */ } static Bool /* whether recognized & stored */ cis_castle( char c, Castle* castlep ) /* FFS: lang */ { static Castle castles[] ={ castle00, castle000 }; static const char* c_ger ={ "kl" }; /* kurz, lang */ static const char* c_eng ={ "sl" }; /* short, long */ int inx; inx = str_pos(c_ger, c); if( inx < 0 ) { inx = str_pos(c_eng, c); } if( inx < 0 ) { return FALSE; /* not recognized */ } *castlep = castles[inx]; return TRUE; /* yes, recognized */ } static Bool /* whether recognized & stored */ cis_column( char c, int* columnp ) { int inx; inx = str_pos("abcdefgh", c); if( inx < 0 ) { return FALSE; /* not recognized */ } *columnp = inx; return TRUE; /* yes, recognized */ } static Bool /* whether recognized & stored */ cis_line( char c, int* linep ) { int inx; inx = str_pos("12345678", c); if( inx < 0 ) { return FALSE; /* not recognized */ } *linep = inx; return TRUE; /* yes, recognized */ } static Bool /* whether recognized & stored */ cis_pos( char c1, char c2, Position* posp ) { int column; int line; if( ! cis_column(c1, &column) || ! cis_line (c2, &line ) ) { return FALSE; /* not recognized */ } *posp = (Position) MK_POS(column, line); return TRUE; /* yes, recognized */ } static int dec_digval( char c ) { return str_pos("0123456789", c); } static Bool /* whether ok (so far) */ try_set_nextfig( register Board* bp, Colour colour, Figure fig, Position pos ) { if( bp->b_f[pos].f_c != empty ) { /* most frequent error */ inp_tell_error("duplicate piece"); return FALSE; /* sorry */ } if( bp->b_max_piece[colour] >= MAX_PIECE ) { inp_tell_error("too many pieces of one colour"); return FALSE; /* sorry */ } set_nextfig(bp, colour, fig, pos); return TRUE; /* ok */ } static Bool /* whether ok */ chk_eo_buf( const char* buf, const char* errtxt ) { buf = skip_space(buf); /* accept trailing white space */ if( buf && buf[0] ) { /* something unrecognized */ inp_tell_error(errtxt); return FALSE; /* sorry */ } return TRUE; /* ok, the buffer is exhausted */ } static Bool /* whether ok */ grok_fys_line( const char* buf, Board* bp ) { /* * As this notation does sort the pieces by their position, * we have to reorder at least for the kings, which are * needed at the first relative piece index. * Hence, we first build an 8x8 intermediate representation, * which we then scan for different types of pieces. */ #define FYS_CODE(c,f) (((((f) << 1) | ((c) & 01)) << 1) | 01) int8 fysb[8][8]; /* [lin][col] */ register const char* p; register int lin; register int col; register int spc; register rColour c; register int i; Figure fig = 0; /* init for lint */ static Figure figlist[] ={ koenig, /* MUST be first */ dame, turm, laeufer, springer, bauer, no_figure /* MUST be last */ }; for( lin=0 ; lin<8 ; ++lin ) { for( col=0 ; col<8 ; ++col ) { fysb[lin][col] = 0; } } p = buf; p = skip_space(p); /* skip over leading spaces */ lin = 7; col = 0; for( ; *p && !is_space(*p) ; ++p ) { spc = 0; c = empty; switch( *p ) { case '1': spc = 1; break; case '2': spc = 2; break; case '3': spc = 3; break; case '4': spc = 4; break; case '5': spc = 5; break; case '6': spc = 6; break; case '7': spc = 7; break; case '8': spc = 8; break; case '/': if( col != 0 ) { inp_tell_error("misplaced / in Forsyth"); return FALSE; } spc = 0; break; default: if( cis_Figure(*p, &fig) ) { /* upper case */ c = white; spc = 1; }else if( cis_figure(*p, &fig) ) { /* lower case */ c = black; spc = 1; }else { printf("@%c\n", *p); inp_tell_error("unrecognized char in Forsyth"); return FALSE; } break; } if( (col+spc) > 8 ) { inp_tell_error("spacing over line boundary"); return FALSE; /* sorry */ } if( c != empty ) { if( lin < 0 ) { inp_tell_error("Forsyth has >64 positions"); return FALSE; /* sorry */ } fysb[lin][col] = FYS_CODE(c,fig); } if( spc ) { col += spc; if( col >= 8 ) { col -= 8; lin -= 1; } } } if( (lin != -1) || (col != 0) ) { inp_tell_error("Forsyth has <64 positions"); return FALSE; /* sorry */ } for( i=0 ; (fig = figlist[i]) != no_figure ; ++i ) { for( c=0 ; c<2 ; ++c ) { register int cod; register int delta; cod = FYS_CODE(c,fig); for( col=0 ; col<8 ; ++col ) { if( c == white ) { lin = 0; delta = 1; }else { lin = 7; delta = -1; } for( ; (lin >=0) && (lin < 8) ; lin += delta ) { if( fysb[lin][col] == cod ) { if( ! try_set_nextfig(bp, c, fig, MK_POS(col,lin)) ) { return FALSE; /* sorry */ } } } } } } return chk_eo_buf(p, "garbage after Forsyth"); #undef FYS_CODE } static Bool /* whether ok */ grok_fen_tom( const char* buf, Board* bp ) { Colour colour; if( ! cis_colour(*buf++, &colour) ) { inp_tell_error("bad colour to move in FEN"); return FALSE; /* sorry */ } bp->b_tomove = colour; return chk_eo_buf(buf, "garbage after FEN colour to move"); } static Bool /* whether ok */ grok_fen_castle( const char* buf, Board* bp ) { int lang; lang = inp__lang; if( lang == LANG_NONE ) lang = LANG_ENGLISH; bp->b_castle[0] = 0; bp->b_castle[1] = 0; for( ; *buf ; ++buf ) { if( '-' == *buf ) { /* * We accept multiple and intermixed minus signes. */ }else if( lang_chr_Figure(lang, koenig) == *buf ) { bp->b_castle[white] |= castle00; /* king side (short) */ }else if( lang_chr_Figure(lang, dame ) == *buf ) { bp->b_castle[white] |= castle000; /* queen side (long) */ }else if( lang_chr_figure(lang, koenig) == *buf ) { bp->b_castle[black] |= castle00; /* king side (short) */ }else if( lang_chr_figure(lang, dame ) == *buf ) { bp->b_castle[black] |= castle000; /* queen side (long) */ }else { break; } } return chk_eo_buf(buf, "garbage after FEN castle info"); } static Bool /* whether ok */ grok_fen_ep( const char* buf, Board* bp ) { Position pos; if( '-' == *buf ) { buf += 1; }else if( buf[0] && buf[1] && cis_pos(buf[0], buf[1], &pos) ) { buf += 2; /* * FEN specifies the active target (just stepped over by pawn * double step), while we need the passive target (destination * of the double step). * FFS: not yet checked for plausibility/legality. */ switch( LIN(pos) ) { case 2: pos += MK_DELTA(0,1); break; case 5: pos -= MK_DELTA(0,1); break; default: inp_tell_error("bad FEN e.p. info"); return FALSE; } bp->b_ep = pos; }else { inp_tell_error("bad FEN e.p. info"); return FALSE; } return chk_eo_buf(buf, "garbage after FEN ep info"); } static Bool /* whether ok */ grok_fen( const char* buf, Board* bp ) { /* * We expect/accept the first 4 FEN fields: board, colour, castling, ep. * The next 2 fields are meaningless for us: we ignore all the rest. */ char fen[INP_BUF_SIZ]; /* cannot overflow */ const char* ip; char* op; char* part[4]; int i; ip = buf; op = fen; for( i=0 ; i<4 ; ++i ) { ip = skip_space(ip); part[i] = op; while( *ip && ! IS_SPACE(*ip) ) { *op++ = *ip++; } *op++ = '\0'; /* terminate that part */ if( ! part[i][0] ) { inp_tell_error("missing part in FEN"); return FALSE; } } return grok_fys_line (part[0], bp) && grok_fen_tom (part[1], bp) && grok_fen_castle(part[2], bp) && grok_fen_ep (part[3], bp); } static Bool /* whether job is complete */ grok_line( const char* buf, Board* bp, int* depthp ) { const char* p; Colour colour; Figure figure; Position pos; Castle castle; int dig; int dep; int lang; p = buf; p = skip_space(p); /* skip over leading spaces */ /* * The next character determines interpretation of the rest of the line: * O output language * I input language * L input & output language * j job type * z depth & colour to move * c allow castling * C FEN castling * e e.p. info * E FEN e.p. info * f Forsyth notation * F FEN * . eoj/eof * # comment * > title line * % ; copied "comment" (PGN escape & comment) */ switch( *p ) { case 'O': /* set output language */ p = skip_space(++p); if( ! cis_lang(*p++, &lang) ) { inp_tell_error("bad output language"); goto err; } set_out_lang(lang); break; case 'I': /* set input language */ p = skip_space(++p); if( ! cis_lang(*p++, &lang) ) { inp_tell_error("bad input language"); goto err; } set_inp_lang(lang); break; case 'L': /* set language (input & output) */ p = skip_space(++p); if( ! cis_lang(*p++, &lang) ) { inp_tell_error("bad input language"); goto err; } set_inp_lang(lang); set_out_lang(lang); break; case 'f': /* accept Forsyth notation of board contents */ p = skip_space(++p); if( ! grok_fys_line(p, bp) ) { goto err; } break; case 'F': /* accept FEN (first 4 fields) */ p = skip_space(++p); if( ! grok_fen(p, bp) ) { goto err; } break; default: if( cis_colour(*p, &colour) ) { /* new piece */ ++p; if( ! ( cis_figure(*p, &figure) || cis_Figure(*p, &figure)) ) { inp_tell_error("bad figure code"); goto err; } p += 1; if( ! cis_pos(p[0], p[1], &pos) ) { inp_tell_error("bad position for piece"); goto err; } p += 2; if( ! try_set_nextfig(bp, colour, figure, pos) ) { goto err; } break; } inp_tell_error("bad first character"); goto err; case 'j': ++p; switch( *p++ ) { default: inp_tell_error("bad job type"); goto err; case '\0': case 'o': /* orthodox */ case 'n': set_jobtype(JT_normal); break; case 'O': case 'N': set_jobtype(JT_stalemate); break; case 'h': set_jobtype(JT_helpmate); break; case 'H': set_jobtype(JT_helpstalemate); break; case 's': set_jobtype(JT_selfmate); break; case 'S': set_jobtype(JT_selfstalemate); break; } break; case 'z': ++p; if( (dig = dec_digval(*p)) >= 0 ) { ++p; dep = dig; while( (dig = dec_digval(*p)) >= 0 ) { ++p; dep *= 10; dep += dig; if( dep > 999 ) { inp_tell_error("job depth too large"); goto err; } } if( ! cis_colour(*p++, &colour) ) { inp_tell_error("bad colour to move"); goto err; } bp->b_tomove = colour; *depthp = dep; }else { inp_tell_error("missing job-depth"); goto err; } break; case 'c': /* allow a castling */ ++p; if( ! cis_colour(*p++, &colour) ) { inp_tell_error("bad colour for castling"); goto err; } if( ! cis_castle(*p++, &castle) ) { inp_tell_error("bad castling code"); goto err; } bp->b_castle[ colour ] |= castle; break; case 'C': /* FEN castling */ p = skip_space(++p); if( ! grok_fen_castle(p, bp) ) { goto err; } break; case 'e': /* ep-info */ ++p; if( ! cis_pos(p[0], p[1], &pos) ) { inp_tell_error("bad position"); goto err; } bp->b_ep = pos; p += 2; break; case 'E': /* FEN ep-info */ p = skip_space(++p); if( ! grok_fen_ep(p, bp) ) { goto err; } break; case '.': if( *++p == '.' ) { /* ".." */ inp_eofseen = TRUE; } goto rdy; /* this job is complete */ case '\0': /* empty line is comment */ case '#': /* guaranteed to be comment */ break; case '>': /* title line */ ++p; if( is_space(*p) ) ++p; /* skip 1 leading space/tab */ tit_put_line(p); break; case '%': /* PGN escape */ case ';': /* PGN comment line */ printf("%s\n", p); /* without leading white space */ break; } return FALSE; /* not yet complete */ rdy:; return TRUE; /* this job is complete */ err:; inp_eofseen = TRUE; /* do not continue to scan this input */ *depthp = 0; /* do not process this input */ return TRUE; /* this job is complete */ } static Bool /* whether job is complete */ grok_epd_line( const char* buf, Board* bp, EpdJob* ejp, int dftdep, int* depthp ) { int rc; set_inp_lang(LANG_ENGLISH); /* fixed by standard */ set_out_lang(LANG_ENGLISH); /* fixed by standard */ epd_clear(ejp); rc = epd_fill_line(ejp, buf); if( rc != TRUE ) { /* no job in this line */ if( rc < 0 ) { /* logical end-of-input */ inp_eofseen = TRUE; return TRUE; /* empty & complete: stop further reading */ } return FALSE; /* not complete: continue reading */ } /* * Now we have the data parsed, but not yet made effective. */ if( ! grok_fys_line (ejp->fys , bp) || ! grok_fen_tom (ejp->tom , bp) || ! grok_fen_castle(ejp->castle, bp) || ! grok_fen_ep (ejp->ep , bp) ) { empty_board(bp); /* clear garbage for next try */ return FALSE; /* not complete: continue reading */ } /* * The board is built. Remains to determine the job * from the additional EPD data. */ /* epd_print(ejp); */ *depthp = epd_get_dep_prog(ejp, dftdep); if( *depthp <= 0 ) { empty_board(bp); /* clear garbage for next try */ } #if 0 printf("# depth = %2d, jobtype %d, attr 0x%02x, prog 0x%02x\n", depth, f_jobtype, f_jobattr, f_jobprog); #endif return *depthp > 0; /* filled & complete: stop further reading */ } /* * read_board() * Read a board and return the depth of the wanted analysis. */ Eximpl int /* 0 | analysis depth */ read_board( Board* bp, EpdJob* ejp, int dftdep ) { char buf[INP_BUF_SIZ]; int depth = 0; empty_board(bp); set_jobtype(JT_normal); /* default */ tit_clear(); if( inp_eofseen ) { /* don't try to read further */ goto out; } if( ! inp_file ) { inp_start_input("-"); /* stdin */ if( ! inp_file ) { goto out; } } inp_bufp = buf; if( ! f_bulkmode ) { printf("Reading job:\n"); oflush(); } for(;;) { if( NULL == fgets(buf, sizeof(buf), inp_file) ) { inp_eofseen = TRUE; goto out; } inp_lino += 1; /* got one more input line */ trim_line(buf); if( f_bulkmode ) { /* bulk: EPD in/out */ if( grok_epd_line(buf, bp, ejp, dftdep, &depth) ) { goto out; /* this job is complete */ } }else { /* normal/old input */ if( grok_line(buf, bp, &depth) ) { goto out; /* this job is complete */ } } } out:; inp_bufp = 0; return depth; }