/* * CHEST, chess analyst. For Copyright notice read file "COPYRIGHT". * * $Source: /home/heiner/ca/chest/RCS/chest.c,v $ * $Id: chest.c,v 3.52 1999/12/09 22:00:16 heiner Exp $ * * supervisor */ #include "types.h" #include "board.h" #include "job.h" #include "analyse.h" #include #include "acm.h" #include "str.h" #include "input.h" #include "output.h" #include "trace.h" #include "dump.h" #include "timing.h" #include "solution.h" #include "refu.h" #include "fac.h" #include "mlsubr.h" #include "asw.h" #include "move.h" #include "move_gen.h" #include "sysdep.h" static char *myname = 0; #ifndef ANA_DFTDEP # define ANA_DFTDEP 5 /* CF: default analysis depth */ #endif #ifndef WITH_CHEST_OPTS # define WITH_CHEST_OPTS 1 /* CF: try to get Env-Var CHEST_OPTS */ #endif #ifndef MX_ENV_OPTS_LEN # define MX_ENV_OPTS_LEN 1024 /* CF: max len of CHEST_OPTS */ #endif #if WITH_CHEST_OPTS # if defined(__STDC__) || USG # include /* getenv() */ # else extern char *getenv(); # endif #endif static Flag f_movgen = FALSE; /* whether only "move_gen" test */ static Flag f_checkonly = FALSE; /* whether just input check */ static Flag f_defmfirst = FALSE; /* whether 1 defender move, first */ static Flag f_usemem = TRUE;/* whether to use "acm" */ static int f_longsol = 0; /* additional long solution depth */ static int f_solopt = FALSE; /* whether to optimize long solution */ static int f_norefu = FALSE; /* suppress refutation table */ static int f_treecnt = 0; /* max depth tree width counting */ static int f_depth = 0; /* depth of job (overrides input) */ static int f_dftdep = ANA_DFTDEP; /* default depth of job */ static void say_hello(void) { extern char version_string[]; static Bool did_hello = FALSE; if (!did_hello) { printf("CHEST %s\n", version_string); did_hello = TRUE; } } static void u_line(FILE * fp, const char *opt, const char *text) { fprintf(fp, "\t%s\t%s\n", opt, text); } static void usage(int xcode) { register FILE *fp; say_hello(); fp = stderr; fprintf(fp, "Usage: %s [options] [--] [file]\n", myname); u_line(fp, "-2 -A", "disable #2/anti heuristic"); u_line(fp, "-a -f", "disable answer/fac heuristic"); u_line(fp, "-b", "bulk mode operation"); u_line(fp, "-c", "just check input board"); #if PROD_LEV < 2 u_line(fp, "-d", "increment debug level"); #endif #if ! PROD_LEV u_line(fp, "-D", "increment special debug level"); #endif u_line(fp, "-g", "move generator test"); u_line(fp, "-G N", "count tree width N plies deep"); u_line(fp, "-l -L", "increment solution print depth / complete"); u_line(fp, "-m", "do not use adaptive memory"); #if ACM_DYN u_line(fp, "-M N", "use N MB memory (transposition table) (-1==dft)"); #endif u_line(fp, "-p", "increment: print positive partial solutions"); u_line(fp, "-r", "suppress refutation table"); u_line(fp, "-s", "increment statistics level"); u_line(fp, "-S", "optimize long solution"); u_line(fp, "-t", "level increment of move execution trace"); u_line(fp, "-T", "trace all move executions"); u_line(fp, "-u", "increment dual suppression level"); u_line(fp, "-U", "dual suppression except at top"); u_line(fp, "-V", "just print version info"); u_line(fp, "-x", "first execute a defender move"); #if WITH_EG u_line(fp, "-y", "ignore endgame theory tables"); #endif u_line(fp, "-z N", "depth of job (overrides file input)"); u_line(fp, "-Z N", "default depth of job (if missing in input)"); #if ! PROD_LEV u_line(fp, "-eE", "special for hEiner"); u_line(fp, "-hH", "special for tHorak"); u_line(fp, "-oO", "special for hOps"); #endif u_line(fp, "-?", "print this usage"); sys_exit(xcode); } /*---------------------------------------------------------------------------*/ /* Verify configuration */ static void bad_type(int bits, int signedness) { char *unpref; say_hello(); if (bits > 0) { unpref = "un"; } else { unpref = ""; bits = -bits; } fprintf(stderr, "(%s:) Configuration error: type %.1sint%d ", myname, unpref, bits); if (signedness) { /* signedness error */ fprintf(stderr, "is not %ssigned\n", unpref); } else { /* size error */ fprintf(stderr, "cannot hold %d bits\n", bits); } fflush(stderr); } static void verify_types(void) { #if defined(Int64) # define I64(x) x #else # define I64(x) /* EMPTY */ #endif #if defined(Uint64) # define U64(x) x #else # define U64(x) /* EMPTY */ #endif register int i; register int e; register uint8 u8 = 1; register uint16 u16 = 1; register uint32 u32 = 1; U64(uint64 u64 = 1; ) int8 i8 = 0; int16 i16 = 0; int32 i32 = 0; I64(int64 i64 = 0; ) for (i = 1; i < 8; ++i) u8 <<= 1; for (i = 1; i < 16; ++i) u16 <<= 1; for (i = 1; i < 32; ++i) u32 <<= 1; U64(for (i = 1; i < 64; ++i) u64 <<= 1;) --i8; --i16; --i32; I64(--i64; ) e = 0; if (!u8) { ++e; bad_type(8, 0); } if (!u16) { ++e; bad_type(16, 0); } if (!u32) { ++e; bad_type(32, 0); } U64(if (!u64) { ++e; bad_type(64, 0); }) if (i8 >= 0) { ++e; bad_type(-8, 1); } if (i16 >= 0) { ++e; bad_type(-16, 1); } if (i32 >= 0) { ++e; bad_type(-32, 1); } I64(if (i64 >= 0) { ++e; bad_type(-64, 1); }) u8 = 0; u16 = 0; u32 = 0; U64(u64 = 0; ) -- u8; --u16; --u32; U64(--u64; ) if (u8 <= 0) { ++e; bad_type(8, 1); } if (u16 <= 0) { ++e; bad_type(16, 1); } if (u32 <= 0) { ++e; bad_type(32, 1); } U64(if (u64 <= 0) { ++e; bad_type(64, 1); }) if (e) sys_exit(4); } /*---------------------------------------------------------------------------*/ /* Options */ static long stol(register const char *s) { register long v; register int base; register int i; register int neg; static char digs[] = "0123456789abcdef"; v = 0; neg = 0; /* not yet negative */ while ((*s == ' ') || (*s == '\t')) ++s; /* skip leading white space */ switch (*s) { case '-': neg = 1; /* FALLTHROUGH */ case '+': ++s; break; } if (*s == '0') { if (*++s == 'x') { /* hexadecimal */ base = 16; ++s; } else { /* octal */ base = 8; --s; } } else { /* decimal */ base = 10; } do { i = str_pos(digs, *s); if ((i < 0) || (i >= base)) { fprintf(stderr, "(%s:) not a base-%d-digit: '%c'\n", myname, base, *s); usage(1); } v *= base; v += i; } while (*++s); if (neg) v = -v; return v; } static int stoi(register const char *s) { return (int) stol(s); } static void put_opts(int first, int behind, char **argv) { #if 1 if (first < behind) { int ac; say_hello(); printf("Options ="); for (ac = first; ac < behind; ++ac) { printf(" %s", argv[ac]); } printf("\n"); } #endif } static void u_miss_arg(char optc) { say_hello(); printf("Missing argument for option -%c\n", optc); usage(1); } static void ini_opts(void) { /* Some options are not implemented here, but we want to control their * initial value, here. Hence some run time initializations. */ f_stats = 0; /* default: no statistics */ f_danswer = TRUE; /* default: do defender heuristic */ f_mvtrace = 0; /* default: no move tracing */ f_fac = TRUE; /* default: do fac */ f_mate2 = TRUE; /* default: do #2 heuristic */ f_nodualdep = 0; /* default: print all duals */ } static int /* not yet used "argc" */ scan_some_opts(register int ac, int argc, char **argv) { register char *opt; register char *arg; int ac1 = ac; while ((ac < argc) && (argv[ac][0] == '-')) { opt = argv[ac++]; if (str_equal(opt, "--")) { break; } #define GET_ARG \ if( opt[1] ) { \ arg = opt+1; \ opt = 0; \ }else if( ac < argc ) { \ arg = argv[ac++]; \ }else { \ u_miss_arg(*opt); \ } while (opt && *++opt) switch (*opt) { default: say_hello(); printf("Unknown option -%c\n", *opt); usage(1); /* NOTREACHED */ case '?': usage(0); /* NOTREACHED */ case '2': f_mate2 = FALSE; break; case 'A': f_noanti = TRUE; break; case 'a': f_danswer = FALSE; break; case 'b': f_bulkmode += 1; f_norefu = !f_norefu; break; case 'c': f_checkonly = TRUE; break; #if PROD_LEV < 2 case 'd': f_debug += 1; break; #endif #if ! PROD_LEV case 'D': f_xdebug += 1; break; #endif case 'f': f_fac = FALSE; break; case 'g': f_movgen = TRUE; break; case 'l': f_longsol += 1; break; case 'L': f_longsol = ANA_MANY; break; case 'm': f_usemem = FALSE; break; case 'p': f_pppsol += 1; break; case 'r': f_norefu = TRUE; break; case 's': f_stats += 1; break; case 'S': f_solopt = TRUE; break; case 't': f_mvtrace += 1; break; case 'T': f_mvtrace = 63 /* very high trace depth */ ; break; case 'u': f_nodualdep += 1; break; case 'U': f_nodualdep = 999; break; case 'V': say_hello(); sys_exit(0); /* NOTREACHED */ case 'x': f_defmfirst = TRUE; break; #if WITH_EG case 'y': f_eguse = FALSE; break; #endif #if ! PROD_LEV case 'e': o_heiner += 1; break; /* hEiner */ case 'o': o_hops += 1; break; /* hOps */ case 'h': o_thorak += 1; break; /* tHorak */ case 'E': GET_ARG; o_heiner = stoi(arg); break; /* hEiner */ case 'O': GET_ARG; o_hops = stoi(arg); break; /* hOps */ case 'H': GET_ARG; o_thorak = stoi(arg); break; /* tHorak */ #endif #if ACM_DYN case 'M': GET_ARG; f_megs_want = stol(arg); break; #endif case 'G': GET_ARG; f_treecnt = stoi(arg); break; case 'z': GET_ARG; f_depth = stoi(arg); break; case 'Z': GET_ARG; f_dftdep = stoi(arg); break; case 'Q': if (f_Qallowed > (2 * ANA_MANY)) { f_Qallowed = 0; } else { f_Qallowed += 1; } break; } #undef GET_ARG } if (!f_bulkmode) put_opts(ac1, ac, argv); return ac; } #if WITH_CHEST_OPTS static void scan_env_opts(char *envopts) { /* We have to split the option string at white space (blank and tab). We * are not exactly sure, whether we may modify the result of getenv(), * and hence copy into another local buffer. */ char sbuf[MX_ENV_OPTS_LEN]; char *argv[MX_ENV_OPTS_LEN / 2 + 1]; int argc = 0; register char *ip; register char *op; int ac; if (str_len(envopts) >= MX_ENV_OPTS_LEN) { fprintf(stderr, "(%s:) Env-Var CHEST_OPTS too long (max %ld)", myname, (long) MX_ENV_OPTS_LEN - 1); sys_exit(1); } ip = envopts; op = sbuf; while (*ip) { while ((*ip == ' ') || (*ip == '\t')) ++ip; if (!*ip) break; argv[argc++] = op; /* start new token */ while (*ip && (*ip != ' ') && (*ip != '\t')) { *op++ = *ip++; } *op++ = '\0'; /* terminate token */ } if (argc) { ac = scan_some_opts(0, argc, argv); if ((ac < argc) && !str_equal(argv[ac], "--")) { fprintf(stderr, "(%s:) non-option in Env-Var CHEST_OPTS: %s", myname, argv[ac]); sys_exit(1); } } } #endif static int /* not yet used "argc" */ scan_opts(int ac, int argc, char **argv) { #if WITH_CHEST_OPTS char *envopts; envopts = getenv("CHEST_OPTS"); if (envopts && *envopts) { scan_env_opts(envopts); } #endif return scan_some_opts(ac, argc, argv); } /*---------------------------------------------------------------------------*/ /* Job solving & support */ static const char * job_name(int jt) { switch (jt) { case JT_normal: return "mate"; case JT_stalemate: return "stalemate"; case JT_selfmate: return "selfmate"; case JT_selfstalemate: return "selfstalemate"; case JT_helpmate: return "helpmate"; case JT_helpstalemate: return "helpstalemate"; } return "undefined"; } static Bool job_dep_wants_acm(int jt, int depth) { /* For a repeated position we need at least 4 plies (two full moves). * Since we ask at the start of "do_ana()", where the attacker is to * move, an additional preply turns out to make no difference. */ switch (jt) { default: case JT_normal: case JT_stalemate: case JT_selfmate: case JT_selfstalemate: break; /* take default rule below */ case JT_helpmate: case JT_helpstalemate: return depth >= 3; /* since storing jobs of depth >= 1 */ } return depth >= 4; /* since storing jobs of depth >= 2 */ } static void put_secs(const char *prefix, TimeSecs secs) { if (prefix) { printf("%s (%s) =", prefix, timing_type()); } printf(" %.*f sec", timing_prec(), (double) secs); if (secs >= 60.) { printf(" (ca. %.1f min)", (double) secs / 60.); if (secs >= 3600.) { printf(" (ca. %.1f hrs)", (double) secs / 3600.); } } printf("\n"); } static void solv_header(Board * bp, int depth) { if (!f_bulkmode) { put_tit_lines(); put_control(bp); put_fen_eng(bp); printf("analysing (%s in %d moves)", job_name(f_jobtype), depth); if (f_Qallowed < ANA_MANY) { printf(" [restriction level %d]", f_Qallowed); } printf(":\n"); oflush(); } } static void solv_acm_start(Board * bp, int preply) { if (f_acmflags & F_ACM_DO) { int tom = bp->b_tomove; if (preply) tom = opp_colour(tom); if (!acm_start(tom, f_jobtype)) { /* allocation failed */ f_acmflags &= ~(F_ACM_USE | F_ACM_DO); /* sorry */ } /* FFS: when changing to another job, the existing facts are still * true, but most likely completely useless. They appear to be * useful, and cause really useful entries to be thrown out, instead. * Hence, it could be a good thing to clear the facts for a new job, * while retaining them for preply-analysis. FFS: if job is "big * enough" FFS: option: clear between jobs FFS: count jobs */ } } /* * When demanded (by option), append the complete solution tree. */ static Bool /* whether done something */ solv_pr_longsol(Board * bp, int depth, int preply, Movelist * resp) { int fsave; if (!f_longsol) { return FALSE; /* nothing done */ } fsave = f_mvtrace; f_mvtrace = 0; /* no more tracing; output would mix up */ printf("\n"); oflush(); print_solution(bp, depth, preply, resp, 1 + f_longsol, f_solopt); printf("\nend of solution tree"); if (f_nodualdep > 0) { if (f_nodualdep >= (depth - 1)) { /* FFS: help */ printf(" (no duals except at top)"); } else { printf(" (no duals in subtrees with depth <= %d)", f_nodualdep); } } printf("\n"); ana_sol_stats(); solution_stats(TRUE); f_mvtrace = fsave; return TRUE; /* did it */ } static void solv_stats(Bool doprint, int depth, TimeSecs secs) { if (doprint) { sum_stats(depth, secs); } ana_stats(doprint); if (doprint && (f_acmflags & F_ACM_DO)) { acm_stats(secs); } movegen_stats(doprint); mg_stats(doprint); move_stats(doprint); fac_stats(doprint); } typedef struct SolvCtx SolvCtx; struct SolvCtx { /* We do NOT include the Board* here, since some of our macros do need * the current board pointer to be named directly "bp", always! */ int depth; Movelist *resp; Move *movp; RefuList *refup; EpdJob *ejp; int anares; /* result from "analyse()" */ TimeSecs secs; Counter nbase; /* FFS */ double nodes; }; static void solv_ana_call(Board * bp, SolvCtx * p) { p->anares = analyse(bp, p->depth, p->resp, p->movp, p->refup); } static void solv_report_epd(Board * bp, SolvCtx * p, int preply /* == !ismate */ ) { char pv[1024]; int depth; if (!ANASUC(p->anares)) { return; /* no succ: no output! */ } p->nodes = (sc_move_exec - p->nbase); /* without PV FFS */ depth = ANADEP(p->anares); /* FFS: PV construction could follow an existing one, just verifying, * that it is legal, and a PV. */ pv[0] = 0; sol_pv_fill(bp, depth, preply, p->resp->l_first, pv, (int) sizeof(pv)); p->secs = gtimer_now(); /* including PV construction */ epd_set_result(p->ejp, !preply, depth, pv, (double) p->secs, p->nodes); if (!preply) { epd_set_bm(p->ejp, bp, p->resp); } epd_print(p->ejp); oflush(); } static void solv_core(Board * bp, SolvCtx * p, const char *pref, Bool applong, Bool timeit) { refu_clear(p->refup); solv_ana_call(bp, p); if (timeit) { p->secs = gtimer_now(); } trc_move_flush(); /* terminate tracing per analysis */ if (!f_bulkmode) { /* normal result report */ if (ANASUC(p->anares)) { printf("%sSolution (in %d moves):\n", pref, ANADEP(p->anares)); put_list(bp, p->resp); /* FFS: indent, style */ } else { printf("%sNo solution in %d moves.\n", pref, ANADEP(p->anares)); } } else { /* EPD result report */ solv_report_epd(bp, p, 0 /* preply */ ); } if (!f_norefu) { put_refulist(bp, p->refup); } if (applong && ANASUC(p->anares)) { (void) solv_pr_longsol(bp, ANADEP(p->anares), 0 /* preply */ , p->resp); } oflush(); } static void solv_set(Board * bp, SolvCtx * p) { SolvCtx subctx; EpdJob subepdjob; Movelist defs; register Move *dp; subctx = *p; subctx.ejp = 0; TRC_DEF_START(); TRC_RESTART(); (void) move_gen(bp, &defs); ml_ck_attr(bp, &defs); TRC_DEF_MOVES(bp, &defs); formoves(&defs, dp) { if (!f_bulkmode) { printf("After "); put__move(bp, dp); printf(" ...\n"); oflush(); } else { epd_copy_move(p->ejp, bp, dp, &subepdjob); subctx.ejp = &subepdjob; } move_execute(bp, dp); subctx.nbase = sc_move_exec; subctx.nodes = 0; if (subctx.ejp) epd_use_board(subctx.ejp, bp); solv_core(bp, &subctx, "\t", TRUE /* applong */ , FALSE /* timeit */ ); move_undo(bp); p->nodes += subctx.nodes; } TRC_DEF_END(); p->secs = gtimer_now(); trc_move_flush(); } static void solv_lost(Board * bp, SolvCtx * p) { /* This is a bit like "solv_core()", as it is one complete analysis. It * is a bit like "solv_set()", as at involves a preply. This also is a * bit like analyse(), as we have to build a result, for one ply deep, * and have to understand all special cases. Complete success is * indicated, iff on all moves, the subjob is succ. FFS: iterative * deepening here? */ SolvCtx subctx; Move submov; Movelist defs; Movelist defsafe; Move *dp; subctx = *p; subctx.refup = 0; subctx.resp = 0; subctx.movp = &submov; subctx.ejp = 0; subctx.nbase = sc_move_exec; subctx.nodes = 0; refu_clear(p->refup); if (p->resp) { clear_list(p->resp); } clear_list(&defsafe); if (!move_gen(bp, &defs)) { /* no initial moves */ /* JT_normal: if now mate: succ in 0, else fail in MANY */ if (JsuccDnomove(bp)) { /* (empty) part of sol-tree */ p->anares = ANARES(0, TRUE); /* FFS: ce-code !? */ } else { /* not part of sol-tree */ p->anares = ANARES(ANA_MANY, FALSE); } } else { /* has initial moves */ TRC_DEF_START(); TRC_RESTART(); p->anares = ANARES(0, TRUE); /* base for maximum */ ml_ck_attr(bp, &defs); if (f_danswer) { (void) sort_answer(subctx.depth + 1, bp, &defs); } TRC_DEF_MOVES(bp, &defs); formoves(&defs, dp) { move_execute(bp, dp); solv_ana_call(bp, &subctx); move_undo(bp); /* FFS: help: any succ implies total succ */ if (ANASUC(subctx.anares)) { refu_lost(subctx.refup, dp, &submov, subctx.anares); if (empty_list(&defsafe)) { /* not yet broken */ if (p->anares < subctx.anares) { /* build maximum */ p->anares = subctx.anares; } } } else { /* one fails: complete failure */ refu_safe(subctx.refup, dp, subctx.anares); p->anares = subctx.anares; app_move(dp, &defsafe); #if 1 /* FFS: option? */ if (f_bulkmode) { /* else output wants complete list */ break; /* formoves */ } #endif } } TRC_DEF_END(); } mg_link(&defsafe); p->secs = gtimer_now(); trc_move_flush(); /* terminate tracing per analysis */ if (ANASUC(p->anares)) { ml_copy(&defs, p->resp);/* for long solu & pv */ } if (!f_bulkmode) { /* standard result report */ if (ANASUC(p->anares)) { printf("Lost always (in at most %d moves).\n", ANADEP(p->anares)); } else { printf("Not lost in %d moves after: ", ANADEP(p->anares)); put_list(bp, &defsafe); } } else { /* EPD result report */ solv_report_epd(bp, p, 1 /* preply */ ); } if (!f_norefu) { put_refulist(bp, p->refup); } oflush(); } static Bool /* whether "solve()" is ready to do it */ solv_can_do(void) { /* Check, whether we can do that type of job... */ switch (f_jobtype) { case JT_helpmate: case JT_helpstalemate: case JT_selfmate: case JT_selfstalemate: case JT_stalemate: case JT_normal: break; /* ok */ default: printf("Cannot yet do %s jobs.\n", job_name(f_jobtype)); return FALSE; /* sorry */ } return TRUE; /* all checks ok */ } /* * solve() * Here we are going to call "analyse()", time it, * and present the result. * Per analysis initializations are done here, also. */ static void solve(Board * bp, int depth, EpdJob * ejp, Movelist * resp) { RefuList refulist; SolvCtx ctx; Bool trylong; int preply; if (f_defmfirst) f_jobprog = JTP__set; /* option overrides */ if (!f_jobprog) f_jobprog = JTP__normal; if (!solv_can_do()) { return; } ctx.depth = depth; ctx.resp = resp; ctx.movp = (Move *) 0; ctx.refup = (f_norefu ? 0 : &refulist); ctx.ejp = ejp; ctx.anares = ANARES(0, FALSE); ctx.secs = 0; ctx.nbase = sc_move_exec; ctx.nodes = 0; if (f_jobattr & JTA_help) { ctx.refup = 0; } trylong = FALSE; preply = 0; solv_header(bp, ctx.depth); gtimer_start(); /* start of analysis */ if (f_jobprog & JTP_DIR) { solv_acm_start(bp, 0); solv_core(bp, &ctx, "", FALSE /* applong */ , TRUE /* timeit */ ); trylong = TRUE; if (ANASUC(ctx.anares)) { if (f_jobprog & JTP_DIR_SUCCRDY) { goto rdy; } /* FFS: timeit & longsol here, if other jobs to do */ } } if (f_jobprog & JTP_DEP_REDU) { if (ctx.depth > 1) { /* FFS */ ctx.depth -= 1; } } if (f_jobprog & JTP_LOST) { preply = 1; solv_acm_start(bp, preply); solv_lost(bp, &ctx); /* FFS: f_bulkmode */ trylong = TRUE; } else if (f_jobprog & JTP_SET) { preply = 1; solv_acm_start(bp, preply); solv_set(bp, &ctx); /* FFS: f_bulkmode */ trylong = FALSE; } rdy:; if (!f_bulkmode) { put_secs("Time", ctx.secs); oflush(); /* timing */ } solv_stats(TRUE, depth, ctx.secs); /* stats: print & re-init */ if (trylong && ANASUC(ctx.anares)) { /* long solution */ if (solv_pr_longsol(bp, ANADEP(ctx.anares), preply, resp)) { ctx.secs = gtimer_now(); put_secs("Total Time", ctx.secs); oflush(); solv_stats(FALSE, 0, (TimeSecs) 0); /* just re-initialise */ } } } /*---------------------------------------------------------------------------*/ /* * Perform a move generator test and present the result. */ static void put_list_twice( /* Xconst */ Board * bp, Movelist * lp) { register Move *mp; put_list(bp, lp); printf("Short notation with check attributes...\n"); ml_ck_attr(bp, lp); formoves(lp, mp) { printf("\t%s\n", mvc_L7(bp, mp)); } } static void test_movgen( /* Xconst */ Board * bp, Movelist * lp) { int res; dump_board(bp); printf("move generator test:\n"); res = move_gen(bp, lp); printf("... returns %d\n", res); put_list_twice(bp, lp); printf("mate move generator test:\n"); res = move1gen(bp, lp); printf("... returns %d\n", res); put_list_twice(bp, lp); /* FFS: ala_move_gen() [if 2 pieces] */ printf("end of move generator test.\n"); } /*---------------------------------------------------------------------------*/ /* * Count width of (legal) tree N plies deep. */ #define MAX_TREECNT_DEPTH MAX_ANA_DEPTH static double cnt_nodes[1 + MAX_TREECNT_DEPTH + 1]; static void do_treecnt(register Board * bp, register int curdep, register int maxdep) { Movelist moves; register int subdep; register Move *mp; if (move_gen(bp, &moves)) { subdep = curdep + 1; cnt_nodes[subdep] += list_length(&moves); if (subdep < maxdep) { formoves(&moves, mp) { move_execute(bp, mp); do_treecnt(bp, subdep, maxdep); move_undo(bp); } } } } static void do_tree_width(Board * bp, int plies) { int i; TimeSecs secs; double v; double sum; double last; if ((plies < 0) || (plies > MAX_TREECNT_DEPTH)) { printf("depth %d is out of range (max %d)\n", plies, MAX_TREECNT_DEPTH); return; } if (!f_bulkmode) { put_tit_lines(); put_control(bp); put_fen_eng(bp); printf("Counting width of legal tree, %d plies deep...\n", plies); oflush(); } gtimer_start(); /* start of real work */ for (i = 0; i <= MAX_TREECNT_DEPTH; ++i) { cnt_nodes[i] = 0; } cnt_nodes[0] += 1; if (plies > 0) { do_treecnt(bp, 0, plies); } secs = gtimer_now(); /* end of real work */ put_secs("Time", secs); oflush(); last = 0; sum = 0; printf("%3s %15s %7s %15s\n", "dep", "#nodes", "quot", "sum nodes"); for (i = 0; i <= MAX_TREECNT_DEPTH; ++i) { v = cnt_nodes[i]; if (v) { sum += v; printf("%3d %15.0f [%7.3f] %15.0f\n", i, v, (last ? v / last : 0.), sum); } last = v; } } /*---------------------------------------------------------------------------*/ static void do_1_board(Board * bp, int depth, EpdJob * ejp) { Movelist result; if (f_checkonly) { if (!f_bulkmode) printf("Checking board ...\n"); if (ok_inp_board(bp)) { /* do also a full check */ chk_board(bp, __FILE__, __LINE__, 0 /* no core */ ); if (!f_bulkmode) printf("Board is OK.\n"); } return; } if (!ok_inp_board(bp)) { return; } CHK_BOARD(bp); if (f_usemem && job_dep_wants_acm(f_jobtype, depth)) { f_acmflags |= (F_ACM_USE | F_ACM_DO); } else { f_acmflags &= ~(F_ACM_USE | F_ACM_DO); } if (f_acmflags & F_ACM_DO) { acm_init(); /* per job initialisation */ } if (f_movgen) { test_movgen(bp, &result); } else if (f_treecnt) { do_tree_width(bp, f_treecnt); } else if (depth > MAX_ANA_DEPTH) { printf("depth %d is too large (max %d)\n", depth, MAX_ANA_DEPTH); } else { solve(bp, depth, ejp, &result); } oflush(); CHK_BOARD(bp); } static void do_1_filename(const char *filename) { Board b; int depth; int dftdep; EpdJob epdjob; dftdep = (f_depth > 0) ? f_depth : f_dftdep; inp_start_input(filename); epd_clear(&epdjob); /* FFS: CTOR should be elsewhere */ while ((depth = read_board(&b, &epdjob, dftdep))) { if (f_depth > 0) { depth = f_depth; /* overrides */ } do_1_board(&b, depth, &epdjob); } } /* * THE main program of chest. * Scan options, fetch jobs and initiate analysis. */ int CDECL_ main(int argc, char **argv) { int ac = 0; myname = argv[ac++]; trc_init(myname); verify_types(); /* should be rather early */ timing_init(); ini_extern(); ana_stats(FALSE); /* just initialisation */ fac_stats(FALSE); /* just initialisation */ move_stats(FALSE); /* just initialisation */ ini_opts(); ac = scan_opts(ac, argc, argv); if (!f_bulkmode) say_hello(); if (ac < argc) { /* use explicit filenames */ if ((ac + 1) < argc) { usage(1); } while (ac < argc) { do_1_filename(argv[ac++]); } } else { do_1_filename("-"); /* == stdin */ } XX__dump(); return sys_xcode(0); }