/* * CHEST, chess analyst. For Copyright notice read file "COPYRIGHT". * * $Source: /home/heiner/ca/chest/RCS/debug.c,v $ * $Id: debug.c,v 3.22 1999/10/14 00:11:54 heiner Exp $ * * check various data structures for consistency */ #if defined(__STDC__) # define USE_STDARG 1 #else # define USE_STDARG 0 #endif #include "types.h" #include "board.h" #include "xatt.h" #include #include "dump.h" #include "sysdep.h" #if USE_STDARG # include #else # include #endif #include "output.h" #if PROD_LEV < 2 Eximpl Flag f_debug = 0; /* whether debug mode */ #endif #if ! PROD_LEV Eximpl Flag f_xdebug = 0; /* whether extended/special debug mode */ #endif /*---------------------------------------------------------------------------*/ static void panic_hdr(void) { (void) fflush(stdout); fprintf(stderr, "Panic: "); } static void panic_ftr(void) { fprintf(stderr, "\n"); (void) fflush(stderr); #if ! PROD_LEV sys_abort(); #else sys_exit(5); #endif } #if 0 /* CF */ # define DOPRNT(fp, fmt, ap) _doprnt(fmt, &(ap), fp) #else # define DOPRNT(fp, fmt, ap) vfprintf(fp, fmt, ap); #endif #if USE_STDARG Eximpl void /* VARARGS1 */ CDECL_ panic(const char *fmt,...) { va_list ap; panic_hdr(); va_start(ap, fmt); DOPRNT(stderr, fmt, ap); va_end(ap); panic_ftr(); } #else /* ! USE_STDARG */ Eximpl void /* VARARGS1 */ CDECL_ panic(va_alist) va_dcl { va_list ap; char *fmt; panic_hdr(); va_start(ap); fmt = va_arg(ap, char *); DOPRNT(stderr, fmt, ap); va_end(ap); panic_ftr(); } #endif /* ! USE_STDARG */ /*---------------------------------------------------------------------------*/ /* * Board checking for legality and consistency. * This is used for debugging and for input verification. * When we detect a problem during input checking, we do not want to take * really drastic actions. Otherwise, we may be in a complex context, * and have to die just here (and not return). */ static int my_easy = 0; /* whether just input checking */ static int my_err = 0; static Xconst Board *my_bp; static const char *my_filename; static int my_lineno; static Bool my_wantcore = 1; static void err(const char *errtxt) { ++my_err; if (my_easy) { printf("Illegal board: %s\n", errtxt); } else { printf("Board PANIC: from '%s' line %d: %s\n", my_filename, my_lineno, errtxt); } } static void die_err(void) { if (my_err) { fflush(stdout); fflush(stderr); if (!my_easy) { put_control(my_bp); dump_board(my_bp); if (my_wantcore) { panic("illegal board"); } } sys_exit(5); } } static void err_datt(void) { err("missing datt"); } static void err_iatt(void) { err("missing iatt"); } #if WITH_ZHASH static void err_zhash(Zhash zh) { char ebuf[150]; sprintf(ebuf, "Zhash should be %08lx", (long) zh); err(ebuf); } #endif /*...........................................................................*/ static const char * c_str(Colour c) { return ((c == white) ? "white" : "black"); } static void chk_castle(Xconst Board * bp, Colour c) { /* for easy & full */ if (bp->b_castle[c] & (castle00 | castle000)) { char ebuf[150]; int lin = STRT_LIN(c); if (K_POS(bp, c) != MK_POS(4, lin)) { sprintf(ebuf, "missing %s king for castling", c_str(c)); err(ebuf); } if (bp->b_castle[c] & castle00) { if ((bp->b_f[MK_POS(7, lin)].f_c != c) || (bp->b_f[MK_POS(7, lin)].f_f != turm)) { sprintf(ebuf, "missing %s rook for O-O", c_str(c)); err(ebuf); } } if (bp->b_castle[c] & castle000) { if ((bp->b_f[MK_POS(0, lin)].f_c != c) || (bp->b_f[MK_POS(0, lin)].f_f != turm)) { sprintf(ebuf, "missing %s rook for O-O-O", c_str(c)); err(ebuf); } } } } static void chk_ep(register const Board * bp) { /* for easy & full */ register const Field *fp; if (!no_pos(bp->b_ep)) { if ((bp->b_ep < 0) || (bp->b_ep >= B_SIZE)) { err("illegal b_ep"); } else { fp = &(bp->b_f[bp->b_ep]); if ((fp->f_c != opp_colour(bp->b_tomove)) || (fp->f_f != bauer)) { err("missing e.p. target"); } if ((fp[bau_mov[bp->b_tomove]].f_c != empty) || (fp[2 * bau_mov[bp->b_tomove]].f_c != empty)) { err("bad e.p. target"); } } } } /*...........................................................................*/ /* * chk_board() * Check the specified board, whether it is legal and consistent. * If not, print a message and die with a core. * - at least value range check for all components. * + border * + tomove * + max_piece * + colour & index: piecelist -> Field * + colour & index: Field -> piecelist * + emptyness of other Fields * + pos64 * + datt / iatt * + king not to move must not be in check (have datt.tomove) * This function is by purpose as independant as possible of * other modules. Therefore, it is language independant. * Hence we use English. * * This function is not only called for the initial board, * but may also be called for any derived board. Therefore, * it tries hard to find any and all bad data in the board. */ Eximpl void chk_board( register const Board * bp_x, const char *fn, int lineno, Bool wantcore) { register const Field *fp; register Field *xfp; register Field *xtp; register rPosition pos; register int col; register int lin; register rPieceSet mask; register int delta; register int i; int imin; int imax; int idx; int pieces[2]; FieldSet figset; int8 fig_cnt[2][MAX_FIGURES]; Board b_sync; Board *bp = &b_sync; Board b; #if WITH_ZHASH Zhash zhash = 0; #endif my_easy = 0; /* we do a full check */ my_err = 0; my_bp = bp; my_filename = fn; my_lineno = lineno; my_wantcore = wantcore; COPY_BOARD(bp_x, bp); XATT_sync(bp); if ((bp->b_tomove != white) && (bp->b_tomove != black)) { err("illegal tomove"); } if ((bp->b_max_piece[white] <= 0) || (bp->b_max_piece[white] > MAX_PIECE)) { err("illegal white max_piece"); } if ((bp->b_max_piece[black] <= 0) || (bp->b_max_piece[black] > MAX_PIECE)) { err("illegal black max_piece"); } pieces[white] = 0; pieces[black] = 0; figset.fs_long[0] = 0; figset.fs_long[1] = 0; for (i = 0; i < MAX_FIGURES; ++i) { fig_cnt[white][i] = 0; fig_cnt[black][i] = 0; } for (fp = bp->b_f, pos = 0; pos < B_SIZE; ++fp, ++pos) { col = COL(pos); lin = LIN(pos); if ((col < 0) || (col >= 8) || (lin < 0) || (lin >= 8)) { if (fp->f_c != border) { err("destroyed border"); } if (fp->f_f != (Figure) no_figure) { err("figure on border"); } if (F_DATTioXX(bp, fp, XATT_TP_intern)) { err("datt in border"); } if (F_IATTioXX(bp, fp, XATT_TP_intern)) { err("iatt in border"); } } else { if (fp->f_pos64 != MK_POS64(col, lin)) { err("bad pos64"); } switch (fp->f_c) { case empty: if (fp->f_f != (Figure) no_figure) { err("empty field with figure"); } break; case white: case black: ++(pieces[fp->f_c]); figset.fs_line[lin] |= (1 << col); switch (fp->f_f) { case bauer: case springer: case laeufer: case turm: case dame: if (fp->f_idx == K_IDX(fp->f_c)) { err("non-king with KING_IDX"); } break; case koenig: if (fp->f_idx != K_IDX(fp->f_c)) { err("illegal king idx"); } break; default: err("illegal figure"); } if ((fp->f_idx < 0) || (fp->f_idx >= (2 * MAX_PIECE))) { err("f_idx out of range"); } else if (IDX_COLOUR(fp->f_idx) != fp->f_c) { err("f_idx <-> f_c"); } else if (fp->f_idx >= (COLOUR_IDX(fp->f_c) + bp->b_max_piece[fp->f_c])) { err("f_idx >= max_piece"); } else if (bp->b_piece[fp->f_idx] != pos) { err("pos <-> piece[idx]"); } break; default: err("illegal field colour"); } } } if (bp->b_max_piece[white] < pieces[white]) { err("too many white pieces"); } if (bp->b_max_piece[black] < pieces[black]) { err("too many black pieces"); } if (bp->b_cur_piece[white] != pieces[white]) { err("current white piece count"); } if (bp->b_cur_piece[white] != pieces[white]) { err("current black piece count"); } if ((figset.fs_long[0] != bp->b_fig.fs_long[0]) || (figset.fs_long[1] != bp->b_fig.fs_long[1])) { err("bad b_fig"); } for (idx = 0; idx < (2 * MAX_PIECE); ++idx) { pos = bp->b_piece[idx]; if (!no_pos(pos)) { --(pieces[IDX_COLOUR(idx)]); if ((pos < 0) || (pos >= B_SIZE)) { err("piece pos out of range"); continue; } fp = &(bp->b_f[pos]); if (fp->f_c != IDX_COLOUR(idx)) { err("bad colour of piece"); } if (fp->f_idx != idx) { err("idx <-> b_f[piece[idx]].f_idx"); } ZH_UPD(zhash, fp->f_c, fp->f_f, fp->f_pos64); } } if (pieces[white]) { err("white piece count"); } if (pieces[black]) { err("black piece count"); } #if WITH_ZHASH if (zhash != bp->b_zhash) { err_zhash(zhash); } #endif die_err(); for (lin = 0; lin < 8; ++lin) { uint8 bset[2]; register unsigned want; register unsigned have; bset[white] = 0; bset[black] = 0; for (col = 0; col < 8; ++col) { fp = &(bp->b_f[MK_POS(col, lin)]); switch (fp->f_c) { case white: case black: if (fp->f_f == bauer) { bset[fp->f_c] |= (1 << col); } break; } want = PB_fp_val(fp); have = ULSHR(*PB_64_P(&bp->b_packed, fp->f_pos64), PB_64_shift(fp->f_pos64) ) & 0x0f; if (have != want) { err("bad b_packed"); } } if (bp->b_bau[white].fs_line[lin] != bset[white]) { err("white b_bau"); } if (bp->b_bau[black].fs_line[lin] != bset[black]) { err("black b_bau"); } } die_err(); COPY_BOARD(bp, &b); for (idx = 0; idx < (2 * MAX_PIECE); ++idx) { pos = b.b_piece[idx]; if (no_pos(pos)) continue; mask = SET1(idx); #define datt(bp,fp) \ if( (fp)->f_c != border ) { \ if( ! (F_DATTioXX(bp,fp,XATT_TP_intern) & mask) ) err_datt(); \ else F_DATTioXX(bp,fp,XATT_TP_intern) &= ~mask; \ } #define iatt(bp,fp) \ if( (fp)->f_c != border ) { \ if( ! (F_IATTioXX(bp,fp,XATT_TP_intern) & mask) ) err_iatt(); \ else F_IATTioXX(bp,fp,XATT_TP_intern) &= ~mask; \ } xfp = &(b.b_f[pos]); fig_cnt[xfp->f_c][xfp->f_f] += 1; switch (xfp->f_f) { case bauer: xtp = xfp + bau_left[xfp->f_c]; datt(&b, xtp); xtp = xfp + bau_right[xfp->f_c]; datt(&b, xtp); break; case springer: for (i = 0; i < 8; ++i) { xtp = xfp + spr_mov[i]; datt(&b, xtp); } break; case laeufer: imin = MIN_L_DIR; imax = MAX_L_DIR; goto ltd; case turm: imin = MIN_T_DIR; imax = MAX_T_DIR; goto ltd; case dame: imin = MIN_D_DIR; imax = MAX_D_DIR; ltd: ; for (i = imin; i < imax; ++i) { delta = dam_mov[i]; xtp = xfp; do { xtp += delta; if (xtp->f_c != border) { datt(&b, xtp); } } while (xtp->f_c == empty); if (xtp->f_c == border) continue; do { xtp += delta; if (xtp->f_c != border) { iatt(&b, xtp); } } while (xtp->f_c == empty); } break; case koenig: for (i = 0; i < 8; ++i) { xtp = xfp + dam_mov[i]; datt(&b, xtp); } break; } #undef datt #undef iatt } for (i = 0; i < MAX_FIGURES; ++i) { if (bp->b_fig_cnt[white][i] != fig_cnt[white][i]) { err("bad white fig_cnt"); } if (bp->b_fig_cnt[black][i] != fig_cnt[black][i]) { err("bad black fig_cnt"); } } for (fp = b.b_f, pos = 0; pos < B_SIZE; ++fp, ++pos) { if (F_DATTioXX(&b, fp, XATT_TP_intern)) { err("too many datt"); } if (F_IATTioXX(&b, fp, XATT_TP_intern)) { err("too many iatt"); } } if (XATT_check(bp)) { err("invalid XATT"); } die_err(); /* check e.p. info: */ if (!no_pos(bp->b_ep)) { chk_ep(bp); } /* Check castling rights: */ if ((bp->b_castle[white] & ~(castle00 | castle000)) || (bp->b_castle[black] & ~(castle00 | castle000))) { err("stray castle bit"); } chk_castle(bp, (Colour) white); chk_castle(bp, (Colour) black); /* Check whether king can be beaten: */ if (F_DATTioXX(bp, K_FP(bp, opp_colour(bp->b_tomove)), XATT_TP_intern) & COLOUR_MASK(bp->b_tomove)) { err("can beat king"); } die_err(); } /*---------------------------------------------------------------------------*/ /* * ok_inp_board() * We expect the board to be just built by "input", in a sane way. * We do not expect any data badly corrupted: dup pieces and * overrunning the piece file is already checked, there. * But we need more assertions to do our job right: * - exactly 1 king for each side * - those must have the proper piece index * - king & rooks must be present for castle rights * - e.p. target needs a pawn that just did a double step. * - the side to move must not be able to beat the other king. * FFS: pawns on base or prom line. * Since this is a real, normal board, we should not need to * consider any special XATT conditions. */ static void chk_piece_cnt(const Board * bp, Colour c) { char ebuf[150]; if ((bp->b_max_piece[c] <= 0) || (bp->b_cur_piece[c] <= 0)) { sprintf(ebuf, "too few %s pieces (need >= 1)", c_str(c)); err(ebuf); } if ((bp->b_max_piece[c] > MAX_PIECE) || (bp->b_cur_piece[c] > MAX_PIECE)) { sprintf(ebuf, "too many %s pieces (max is %d)", c_str(c), MAX_PIECE); err(ebuf); } } static void chk_kings(const Board * bp, Colour c) { char ebuf[150]; if (bp->b_fig_cnt[c][koenig] != 1) { sprintf(ebuf, "need 1 %s king, but got %d", c_str(c), bp->b_fig_cnt[c][koenig]); err(ebuf); } else { /* exactly 1 K counted */ if ((K_FP(bp, c)->f_c != c) || (K_FP(bp, c)->f_f != koenig)) { sprintf(ebuf, "%s king is not first %s piece", c_str(c), c_str(c)); err(ebuf); } } } Eximpl Bool /* whether the board is OK for us */ ok_inp_board(Xconst Board * bp) { my_easy = 1; /* we do just an input check */ my_err = 0; my_bp = bp; my_filename = ""; my_lineno = 0; my_wantcore = FALSE; chk_piece_cnt(bp, (Colour) white); chk_piece_cnt(bp, (Colour) black); if (my_err) goto rdy; chk_kings(bp, (Colour) white); chk_kings(bp, (Colour) black); if (my_err) goto rdy; chk_ep(bp); chk_castle(bp, (Colour) white); chk_castle(bp, (Colour) black); { Colour opp = opp_colour(bp->b_tomove); char ebuf[150]; if (F_DATT(K_FP(bp, opp)) & COLOUR_MASK(bp->b_tomove)) { sprintf(ebuf, "can beat %s king", c_str(opp)); err(ebuf); } } rdy:; my_easy = 0; /* end of "just an input check" */ return !my_err; }