/* * 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; pos < B_SIZE; ++pos) { p = &(bp->b_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; i < MAX_FIGURES; ++i) { bp->b_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; }