/* * CHEST, chess analyst. For Copyright notice read file "COPYRIGHT". * * $Source: /home/heiner/ca/chest/RCS/mate2.c,v $ * $Id: mate2.c,v 1.29.1.1 2002/05/05 17:16:11 heiner Exp $ * * eliminate candidates for mate in 2 */ #include #include "stats.h" #include "output.h" #include "mate2.h" /* Execution-Count annotations are for Bayer 6# nnnnnn */ /* * The basic idea is to detect impossibilities for a mate in 2 moves * not by detecting aggressive defender moves (that is done by fac), * but rather by missing activity of the attacker. * * We assume that the defender will do a K-move (defender strategy). * We must guarantee the legality of the defender move. * * Then for all the legal attacker moves (move list, 2-mate candidates), * we try to verify, without really executing the move, that after this * move, * - the indended defender move is legal, and * - no further attacker move can complete the mate net around defK. * * The first part of the analysis is done when the list of legal * attacker moves is computed. We leave a state (info object), * which then is used for every attacker move, before it is executed, * whether a mate net can be completed. * * Global conditions: * - If there is a legal e.p. move for the attacker in its move list, * we skip this move as "may be 2-mate". It is too complex. * ==> no e.p. by attacker considered in first move * - The defender does normal K-moves, only. * ==> no castling by defender * ==> no B double step by defender * ==> no e.p. by attacker legal in second move * ==> no e.p. by defender * ==> no promotions by defender * - There must not be left any castling rights (of the attacker). * ==> no castling by attacker * OR: we have to assure, no castling can happen in the analyzed part. * - There may be B double steps by the attacker, but no problem * - There may happen promotions by the attacker in the first * and even in the second move. * * Simplified versions: * (S1) Possible promotions in the first or second attacker move * are considered "dunno". * - There is no attB on 7th line, else "all dunno". * - Attacker moves which place a B on the 7th line are "dunno". * This simplifies considerations about possibly opened attacks. * * Method: * - When we think about a single attacker move (2-mate candidate), * we compute an upper bound for the possible mate net around the defK. * - All attacker attacks before the move are considered permanent. * FFS: fields attacked by just 1 attacker * Note, that the defK must be considered opaque for attacks, * although he is not in the board! * - We determine a defK target field with most freedom (so far). * Move is defK: v-->w. * FFS: we may consider more than one defK target. * - The first legal attacker move, taken from the move list, is named * attX: f-->t * - If the intended defense defK: v-->w is not legal after attX: f-->t, * any more, this attacker move is "dunno" (might be mate in 2). * Such is possible, if any of * (i1) attXp: t --D--> u is a possible attack by Xp (after its move (prom!)) * (i2) attY: y --D--> f --D--> w by some other attY, which is LTD, * has datt@f and iatt@w (or @v, if v--D-->w). * - The move attX: f-->t and legal defK: v-->w, may be followed by either * (A) a further (pseudo-legal) move attXp: t-->z, or * (B) a (pseudo legal) move by some other (!) attacker piece. * If either might complete the mate net, "dunno". * - Some computations can be done once for all moves of the same * attacker piece attX@f. * * Case (A) & (B): * - Opening f may uncover attacks to defK-Env around w. * Restricted to pieces from (datt@f & iatt@E) (anywhere inside defK-Env) * * Case (A) [move same piece twice]: * - Beating a defender piece at t may later uncover attacks. * This must also contain double uncoverings. * Restricted to [LTD] (datt@t & (iatt@f | iatt@E)) | (datt@f & iatt@t). * - No further uncoverings are possible, especially the moving piece * cannot be uncovered, as only defK is moving also. * - Placing attXp at some target u (may also be a promotion) now must * cover E as far as not yet done by uncoverings. * * Case (B) [move two different pieces]: * - Xp stays at t, and has some active covering of E. * - Moving goes through f (left by attX) and through v (left by defK). * - If we do not want to compute the effects of the pseudo legal moves * of all attacker pieces separately, we may compute all of them, * including X as if it would have stayed at f. * FFS: one class for all BSL, and all T and D separately. * FFS * * Other special cases: * - If there is no legal attacker move, nothing is to be done. * - If there is just one legal attacker move, it might not be worth the * trouble (except if we are fast and successful). * * Castling considerations: * - Possible castling moves of the defender are just ignored. * - Possible castlings of the attacker are rather hard to account for. * If some castling right is still in effect, we either fail completely, * or we try to exclude pieces (stuff them into the dunno set), * such that no legal castling moves appear within our analysis. * - When there is a defender between the K and its T (whether it is * the K or another piece), that castling is blocked for at least * the two moves we consider here. * - When there are two or more attackers between the K and its T, * we know, that no castling can occur (even defK beating one * disables the respective castling). * - If no attacker is between K&T, we have to fail, since a castling * might occur as second (mate-) move, even if we exclude castling * as the first move. * FFS: first moves, which do not castle, but destroy the right to castle, * may still be considered. * - If exactly one attacker is between the K&T with castling right, * we can avoid a possible castling by excluding this piece from * the first attacker move. Then, for the second move it is still * there and blocks the castling from happening, like for the first move. * * Elements of computing: * - APS1: the set of all attacker pieces, which can legally move, first. * - APSU: the set of all LTD attacker pieces (may be uncovered) * - AE[f]: part of E around w, attackable by opening f (use piece index * of X to build the array?) * FFS * * Method of effect accounting: * - There are up to 3 moves to consider. * There are no special effects except perhaps promotions. * - The defenders move is fixed to be a king move. * + It changes the center of the environment E. * + It opens up the old position for attack promotion. * FFS: do we need to think about this? * - Besides promotion, each attacker move * + places a new piece to its destination, which may have * active attacks towards E. * + opens its old position, and probably uncovers attacks to E. * - We need a method to estimate the maximal possible attacks to E, * which is clear and rather independant of move order. * Especially, we need to account for effects of the second move, * before we know about the first move (i.e. independant of it). * - We must take care of combined effects like double uncovering, * and uncovering a moved piece. * * - The active effects of a moving piece (from its destination) * consider the board empty (maximal effect of uncoverings). * - Uncovering effects before moving pieces are accounted for, already. * - There are left uncoverings before non-moving pieces. * The set of possible pieces, which can be uncovered this way, * is computable as part of preparation. As they do not move, * they have valid attacks in the board. * For such an uncovering to really happen, the piece with * a direct attack in it must be moved. And this is the point, * where we account for the uncovering. * - The take-away is either a beaten defender (att piece moves twice) * or a moving attacker. */ #if PROD_LEV # define M2_STATS 0 # define M2_VERBOSE 0 # define M2_CUT_STATS 0 # define M2_STEP1_STATS 0 # define M2_DIRS_STATS 0 #else /* !PROD_LEV */ # define M2_STATS 1 /* CF: local statistics */ # if 0 /* CF: local debugging */ # define M2_VERBOSE 0x20 /* CF: mask for o_heiner */ # else # define M2_VERBOSE 0 # endif # define M2_CUT_STATS 0 /* CF */ # define M2_STEP1_STATS 0 /* CF */ # define M2_DIRS_STATS 0 /* CF */ #endif /* !PROD_LEV */ #undef THIS_STATS #define THIS_STATS M2_STATS #include "statsupp.h" #if M2_DIRS_STATS # define scDSinc(v) scinc(v) #else # define scDSinc(v) /*empty*/ #endif #if M2_VERBOSE # define M2_V(x) if( o_heiner & M2_VERBOSE ) { x } #else # define M2_V(x) #endif #define SET_GE(tot,part) (!((part) & ~(tot))) #define SET_LE(part,tot) (!((part) & ~(tot))) #define M2E_VAL 4 /* for defK escape, -=1 for iatt */ #define M2E_VBEAT 2 /* FFS: += for beating attacker */ #define M2E_MAXVAL (9*M2E_VAL + M2E_VBEAT + 1) /* exclusive */ /* * We compute sets (lists) of pseudo legal moves, * separately for a single piece, or for additional moves * by opening up a single position. * One piece: 4*7 dame moves * Empty pos: 8*1 springer + 4*7 dame */ #define MAX_ESL_MOVE (4 * 7) /* 28 */ #define MAX_ESL_THRU (8*1 + 4*7) /* 36 */ #define MAX_ESCSETLIST 64 /* crude upper bound */ #define MAX_PLM_TABS (MAX_PIECE * (MAX_ESL_MOVE + MAX_ESL_THRU)) /* 16 * (28+36) == 16*64 == 1024 */ typedef int16 M2PlmInx; /* -1 | index into plm-tables */ typedef rint16 rM2PlmInx; typedef uint8 M2PlmLen; /* length of one plm table */ typedef ruint8 rM2PlmLen; /* * Blocking concept: * A defender piece, while not beaten by an attacker, which * moves twice, may severely block attackers. * Given the center of E, and the position of a blocking defender, * we can compute a set-of-Pos64 (ebc table), * such that an attacker LTD there does not cover any part of E, * although fig_cov would indicate so. * In the preparation we collect a summary set of blocked targets. */ #define IS_EBC64(ebcp, pos64) \ ( ( ((unsigned)((ebcp)->fs_line[ LIN64((unsigned)(pos64)) ])) \ >> COL64((unsigned)(pos64)) \ ) & 01 \ ) #define WITH_E1DATT 0 /* CF: just 1 datt is E */ #define WITH_9E_FINE 1 /* CF: completely free immediately ok */ struct M2State { /* always filled: */ M2Info* m2_ip; /* -> callers handle */ const Movelist* m2_lp; /* all legal att-moves */ Bool m2_prepd; /* whether prepared */ /* first by prep: */ PieceSet m2_apscands; /* att-PS: candidates for 2-mate */ PieceSet m2_apsdunno; /* att-PS: unexcludable candidates */ /* If any non-dunno cands are left: */ #if 0 /*unused*/ PieceSet m2_apslm; /* att-PS: has legal move */ PieceSet m2_apsltd; /* att-PS: is LTD (uncoverable) */ #endif /* The selected defK move */ Position m2_defKsrc; /* source for def-K */ Position m2_defKdst; /* selected target for def-K */ EscSet m2_escs; /* environment around defKdst */ #if 0 /*unused*/ int8 m2_defKdir; /* move direction of def-K */ uint8 m2_ecard; /* |escs| */ #endif #if WITH_9E_FINE Flag m2_defKis9E; #endif int8 m2_defKdidx; /* -1 | piece index: may be beaten at dst */ #if WITH_E1DATT PieceSet m2_apsedatt; /* those make unique datt in E */ EscSet m2_escedatt; /* E-part: have unique datt */ EscSet m2_ecs1edatt[2*MAX_PIECE]; /* [idx] his edatt */ #endif /* blocking */ FieldSet m2_ebc_bydef; /* there any esc is blocked by some def */ /* uncovering towards defKdst (center of E) */ PieceSet m2_apsUltd; /* att-PS: is uncoverable LTD->E */ /* ... restricted by "ebc" */ PieceSet m2_apsultd; /* att-PS: is uncoverable LTD->E */ EscSet m2_ultdcov; /* max. E-cover by apsultd->E */ const EscSet* m2_ucovtab; /* [upos-ecent] for L|T|D */ /* effect sets */ PieceSet m2_apseffplm; /* att-PS: may have PLM with effect */ PieceSet m2_done_move; /* valid idx in plmx_move */ PieceSet m2_done_thru; /* valid idx in plmx_thru */ M2PlmLen m2_plml_move[2*MAX_PIECE]; /* [idx] len in plmtab[] */ M2PlmLen m2_plml_thru[2*MAX_PIECE]; /* [idx] len in plmtab[] */ M2PlmInx m2_plmx_move[2*MAX_PIECE]; /* [idx] -1 | -> plmtab[] */ M2PlmInx m2_plmx_thru[2*MAX_PIECE]; /* [idx] -1 | -> plmtab[] */ /* remembered search results */ int8 m2_ocan_idx; /* -1 | the others are known to cover */ int8 m2_onot_idx; /* -1 | the others are known to not cover */ EscSet m2_ocan_escs; /* ... cover this escape */ EscSet m2_onot_escs; /* ... not cover this escape */ /* EscSet table space */ M2PlmInx m2_plmfree; /* []plmtab: next free slot */ EscSet m2_plmtab[MAX_PLM_TABS]; /* all the tables */ }; #if M2_STATS static Counter sc_m2f_castle = 0; /* fail: may castle */ static Counter sc_m2f_prom = 0; /* fail: may promote */ static Counter sc_m2f_noesc = 0; /* fail: no escape */ static Counter sc_escval[ M2E_MAXVAL ]; /* [v] occurred */ static Counter sc_m2c_dunno = 0; /* cand: total piece */ static Counter sc_m2c_ep = 0; /* cand: e.p. move */ static Counter sc_m2c_illf = 0; /* cand: K:v->w bad, f open */ static Counter sc_m2c_illt = 0; /* cand: K:v->w bad, t->w */ static Counter sc_m2c_from = 0; /* cand: f open */ static Counter sc_m2c_beat = 0; /* cand: t open */ static Counter sc_m2c_move = 0; /* cand: t->x->E */ static Counter sc_m2c_frst = 0; /* cand: t->E */ static Counter sc_m2c_othr = 0; /* cand: other remembered */ static Counter sc_m2c_thru = 0; /* cand: with other thru f */ static Counter sc_m2c_rest = 0; /* cand: other move */ static Counter sc_m2c_succ = 0; /* no cand */ static Counter sc_m2ll_thru[1+MAX_ESCSETLIST]; /* [list length] occd */ static Counter sc_m2ll_move[1+MAX_ESCSETLIST]; /* [list length] occd */ # if M2_CUT_STATS static Counter sc_m2ll_cuteq [1+MAX_ESCSETLIST]; /* [list length] */ static Counter sc_m2ll_cutcov[1+MAX_ESCSETLIST]; /* [list length] */ static Counter sc_m2ll_cutmax[1+MAX_ESCSETLIST]; /* [list length] */ # endif # if M2_STEP1_STATS static Counter sc_step1_dirs = 0; static Counter sc_step1_border = 0; # endif # if M2_DIRS_STATS static Counter sc_dirs_f[MAX_FIGURES][9] ={0}; # endif #endif /* M2_STATS */ Eximpl void /*ARGSUSED*/ m2_stats( Bool print ) { #if M2_STATS register int i; register Counter cnt; register Counter totcnt; register double totval; if( print && (f_stats > 0) ) { totcnt = 0; totval = 0.0; for( i=0 ; i= 2) && (M2E_VAL <= 4) { register int j; register int k; register Counter sum; for( j=0 ; j= M2E_MAXVAL) ? 0 : sc_escval[i]); sum += cnt; } if( ! sum ) continue; printf("%2d+ %8lu %5.1f%%:", (int) (j / M2E_VAL), (unsigned long) sum, percent(sum, totcnt)); for( k=0 ; k= M2E_MAXVAL) ? 0 : sc_escval[i]); show_nz(1, 7+!k, '-', cnt); } printf(" ="); for( k=0 ; k= M2E_MAXVAL) ? 0 : sc_escval[i]); if( cnt ) { printf("%5.1f", percent(cnt, totcnt)); }else { printf("%5s", "- "); } } printf("\n"); } } # else /* M2E_VAL > 4 */ for( i=0 ; i 4 */ } { register Counter totm2; totm2 = 0; totm2 += sc_m2c_dunno; totm2 += sc_m2c_ep; totm2 += sc_m2c_illf; totm2 += sc_m2c_illt; totm2 += sc_m2c_from; totm2 += sc_m2c_beat; totm2 += sc_m2c_move; totm2 += sc_m2c_frst; totm2 += sc_m2c_othr; totm2 += sc_m2c_thru; totm2 += sc_m2c_rest; totm2 += sc_m2c_succ; if( totm2 ) { printf("m2c:"); show_scs(1, 7, sc_m2c_dunno, " dunno,"); show_scs(1, 7, sc_m2c_ep , " ep, "); show_scs(0, 8, sc_m2c_illf , " illf, "); show_scs(0, 9, sc_m2c_illt , " illt"); printf(" [%4.1f%%]\n", percent(sc_m2c_illt, totm2)); printf("m2c:"); show_scs(1, 7, sc_m2c_from , " from, "); show_scs(0, 8, sc_m2c_beat , " beat, "); show_scs(0, 8, sc_m2c_move , " move, "); show_scs(0, 9, sc_m2c_frst , " frst\n"); printf("m2c:"); show_scs(1, 7, sc_m2c_othr , " othr, "); show_scs(0, 8, sc_m2c_thru , " thru, "); show_scs(0, 8, sc_m2c_rest , " rest, "); show_scs(0, 9, sc_m2c_succ , " succ"); printf(" [%4.1f%%]\n", percent(sc_m2c_succ, totm2)); } } { register Counter totll; register Counter v1; register Counter v2; totll = 0; for( i=0 ; i < (1+MAX_ESCSETLIST) ; ++i ) { v1 = sc_m2ll_thru[i]; v2 = sc_m2ll_move[i]; totll += v1 + v2; } for( i=0 ; i < (1+MAX_ESCSETLIST) ; ++i ) { v1 = sc_m2ll_thru[i]; v2 = sc_m2ll_move[i]; if( v1 || v2 ) { printf("m2ll %2d t/m:", i); show_nz(1, 9, '-', v1); show_nz(1, 9, '-', v2); printf(" [%5.1f%% %5.1f%%]", percent(v1, totll), percent(v2, totll)); # if M2_CUT_STATS if( i > 1 ) { register Counter ceq; register Counter ccov; register Counter cmax; ceq = sc_m2ll_cuteq [i]; ccov = sc_m2ll_cutcov[i]; cmax = sc_m2ll_cutmax[i]; v1 = (v1 + v2) * i; /* total contents */ printf(" cut^=<: %5.2f%% %5.2f%% %5.2f%%", percent(cmax, v1), percent(ceq , v1), percent(ccov, v1)); } # endif printf("\n"); } } } # if M2_STEP1_STATS if( sc_step1_dirs ) { printf("m2s1:"); show_scs(1, 7, sc_step1_dirs, " dirs,"); show_scs(1, 7, sc_step1_border, " dir border"); printf(" [%5.2f%%]", percent(sc_step1_border, sc_step1_dirs)); printf("\n"); } # endif # if M2_DIRS_STATS { register int fig; register Counter sum; for( fig=0 ; figb_tomove; /* 19848 */ attmask = COLOUR_MASK(att); m2p->m2_defKsrc = K_POS(bp, opp_colour(att)); fp = &(bp->b_f[ m2p->m2_defKsrc ]); vbest = 0; ebest = 0; dbest = NO_DIR; for( mvdir=MIN_D_DIR ; mvdirf_c; if( (c != empty) && (c != att) ) { continue; /* target is blocked */ } if( F_DATT(tp) & attmask ) { continue; /* target is attacked */ } e = (1 << ZERO_DIR); /* central field is ok */ v = M2E_VAL; /* 78875 */ if( F_IATT(tp) & attmask ) { v -= (M2E_VAL-1); /* central iatt is bad */ } #if WITH_E25 inxp = dirdirinx[mvdir]; #endif for( edir=MIN_D_DIR ; edirf_c; if( ((c != empty) && (c != att)) || (F_DATT(ep) & attmask) ) { block25 |= SET1(inx); continue; } } done25 |= mask25; e |= (1 << edir); v += (val25[inx] = (M2E_VAL - !!(F_IATT(ep) & attmask))); #else ep = tp + dam_mov[edir]; if( ep != fp ) { c = ep->f_c; if( ((c != empty) && (c != att)) || (F_DATT(ep) & attmask) ) { continue; } } e |= (1 << edir); v += M2E_VAL - !!(F_IATT(ep) & attmask); #endif } if( e && (v > vbest) ) { ebest = e; vbest = v; dbest = mvdir; } } #if 0 m2p->m2_defKdir = dbest; #endif if( ! no_dir(dbest) ) { m2p->m2_defKdst = m2p->m2_defKsrc + dam_mov[dbest]; m2p->m2_defKdidx = ( (tp = &(bp->b_f[m2p->m2_defKdst])), ((tp->f_c == att) ? tp->f_idx : -1) ); m2p->m2_escs = ebest; #if 0 /*unused*/ m2p->m2_ecard = bitsum[ebest]; #endif #if WITH_9E_FINE m2p->m2_defKis9E = (vbest >= (9*M2E_VAL)); #endif }else { m2p->m2_defKdst = NO_POS; m2p->m2_defKdidx = -1; m2p->m2_escs = 0; #if 0 /*unused*/ m2p->m2_ecard = 0; #endif } scinc(sc_escval[vbest]); M2_V( printf("m2e: "); if( no_dir(dbest) ) { printf("-"); }else { printf("%03x d=%d v=%2d", (int) ebest, (int) dbest, (int) vbest); } printf("\n"); ) } static int /* NO_DIR | dir to move/beat */ dir_for_fig( Position from, /* from here */ Position to, /* to here */ Figure fig, /* by such a piece type */ Colour tom, /* of this side (for Bs) */ Bool mustatt) /* excludes B-moves */ { /* * This is a purely geometrical computation, * there is no board inspection, here. * FFS: most invocations have no att_dir: extract? * 134193 total, 46328 with dir (34.5%) */ int dir; int step; if( ! no_dir(dir = att_dir(from, to)) ) { switch( fig ) { case springer: if( ! spr_dir(dir) ) return NO_DIR; break; case laeufer: if( ! lfr_dir(dir) ) return NO_DIR; break; case turm: if( ! trm_dir(dir) ) return NO_DIR; break; case dame: if( ! dam_dir(dir) ) return NO_DIR; break; case koenig: if( ! dam_dir(dir) ) return NO_DIR; if( (from + dam_mov[dir]) != to ) return NO_DIR; break; case bauer: /* * We do expect that Bs are rather frequent. * Hence we try to be more exactly than usual. */ if( ! dam_dir(dir) ) return NO_DIR; if( ! lfr_dir(dir) ) { /* B-move */ if( mustatt ) return NO_DIR; step = dam_mov[dir]; if( step != bau_mov[tom] ) return NO_DIR; if( (from + step) != to ) { if( (from + step + step) != to ) return NO_DIR; if( LIN(from) != BAS_LIN(tom) ) return NO_DIR; } }else { /* B-beat */ step = dam_mov[dir]; if( (from + step) != to ) return NO_DIR; if( (step != bau_left [tom]) && (step != bau_right[tom]) ) { return NO_DIR; } } break; } } return dir; } static Bool /* whether intermediate positions are free */ m2_way_empty( const Board* bp, /* in this board */ Position from, /* from here (exclusively) */ Position to, /* to here (exclusively) */ int dir, /* using this direction */ Position e1pos, /* assuming this one empty */ Position e2pos ) /* assuming this one empty */ { /* 17854 */ /*FFS: from == to */ if( dam_dir(dir) ) { int delta; delta = dam_mov[dir]; while( (from += delta) != to ) { if( (from != e1pos) && (from != e2pos) && (bp->b_f[from].f_c != empty) ) { return FALSE; /* no, sorry, obstacle found */ } } }else if( no_dir(dir) ) { return FALSE; /* cannot be followed */ } return TRUE; /* no obstacle found */ } static Bool /* whether it may be possible */ m2_plm_act_cancov( /* pseudo-legally and actively */ register const Board* bp, /* in this board */ EscSet escs, /* to cover this set of escapes */ register rPosition ecent, /* around this center */ Figure fig, /* by moving such a piece type */ Position from, /* from here to somewhere */ register rPosition e1pos, /* assuming this one empty */ register rPosition e2pos, /* assuming this one empty */ const FieldSet* ebcp ) /* 0 | completely blocked targets */ { /* 13288 */ /* * We use this function for determining the attackers chances * arising from moving the same piece twice. * Clearly, the side to move is the current "b_tomove". * * In case of promotion in the move to consider here, * another function is called, not this one. * * FFS: This situation is special insofar as we know exactly * all moving attacker pieces and uncoverings. * We need not be combined with other move effects. * Hence could be more precise. */ const LegDelSet* ldsp; register int att; /* colour to move */ #if 0 /* FFS: while all callers do the test */ if( ! escs ) { return TRUE; /* nothing to be done */ } #endif att = bp->b_tomove; { const EscInfo* eip; eip = cov_esc[escs]; #if 0 /* FFS: while all callers do the test */ if( ! (eip->figcan & (1 << fig)) ) { return FALSE; /* impossible for such a fig */ } #endif ldsp = &(eip->fighow[fig]); } if( ldsp->count > 0 ) { /* there are explicit deltas */ /* * The deltas are from the center of the escape set. * K near K is not contained. */ register const LegalDelta* ldp; register rPosition to; register int dir; for( ldp=ldsp->deltas ; *ldp ; ++ldp ) { to = ecent + *ldp; if( bp->b_f[to].f_c == border ) { continue; /* non-existing */ } if( (to != e1pos) && (to != e2pos) && (bp->b_f[to].f_c == att) ) { continue; /* blocked */ } /* * FFS: expand (part of) dir_for_fig() & switch once */ if( no_dir(dir = dir_for_fig(from, to, fig, att, FALSE)) ) { continue; /* unreachable */ } if( ! m2_way_empty(bp, from, to, dir, e1pos, e2pos) ) { continue; /* blocked intermediate */ } switch( fig ) { case laeufer: case turm: case dame: if( ebcp ) { register unsigned to64; to64 = bp->b_f[to].f_pos64; if( IS_EBC64(ebcp, to64) ) { continue; /* blocked by def */ } } break; } /* FFS: way empty to->ecent */ /* FFS: def-K-back-beat */ M2_V( printf("m2move expl "); put_position(from); printf("->"); put_position(to); printf("\n"); ) return TRUE; /* looks like coverable */ } }else { /* no explicit deltas provided */ register rPosition to; register int delta; register rColour c; register const Field* tp; int dir; int dirmax; switch( fig ) { case bauer: /* should have explicit delta */ case springer: /* should have explicit delta */ case koenig: /* should have explicit delta */ M2_V( printf("m2move miss "); put_position(from); printf("\n"); ) return TRUE; /* looks like coverable */ case laeufer: dir = MIN_L_DIR; dirmax = MAX_L_DIR; goto ltd; case turm: dir = MIN_T_DIR; dirmax = MAX_T_DIR; goto ltd; case dame: dir = MIN_D_DIR; dirmax = MAX_D_DIR; ltd:; for( ; dirb_f[to += delta])), ((c = tp->f_c) != border) ) { if( (to == e1pos) || (to == e2pos) ) { c = empty; } if( c == att ) break; /* end-of-dir */ if( ! (ebcp && IS_EBC64(ebcp, tp->f_pos64)) && SET_LE(escs, fig_cov[fig][to - ecent]) ) { M2_V( printf("m2move impl "); put_position(from); printf("->"); put_position(to); printf("\n"); ) return TRUE; /* can cover */ } if( c != empty ) break; /* end-of-dir */ } } break; } /* switch(fig) */ } return FALSE; /* no, cannot cover */ } static Bool /* whether it may be possible */ m2_plm_prom_cancov( /* pseudo-legally and actively */ register const Board* bp, /* in this board */ register rEscSet escs, /* to cover this set of escapes */ register rPosition ecent, /* around this center */ Position from, /* promoting from here to somewhere */ register rPosition e1pos, /* assuming this one empty */ register rPosition e2pos, /* assuming this one empty */ const FieldSet* ebcp ) /* 0 | completely blocked targets */ { /* * The next move is a promotion. * We have to check for either a D or S as result. * Potentially, there are up to three target positions. */ register int att; /* colour to move */ register rPosition to; register rFigSet figs; att = bp->b_tomove; { const EscInfo* eip; eip = cov_esc[escs]; figs = eip->figcan & ((1<b_f[to].f_pos64)) ) #define TEST_ds() \ if( (TEST_condF(dame ) && TEST_condLTD()) \ || (TEST_condF(springer) ) ) {\ return TRUE; /* can cover */ \ } to = from + bau_mov[att]; if( (to == e1pos) || (to == e2pos) || ((to != ecent) && (bp->b_f[to].f_c == empty)) ) { TEST_ds() } to = from + bau_left[att]; if( (to != e1pos) && (to != e2pos) && (bp->b_f[to].f_c == opp_colour(att)) ) { TEST_ds() } to = from + bau_right[att]; if( (to != e1pos) && (to != e2pos) && (bp->b_f[to].f_c == opp_colour(att)) ) { TEST_ds() } #undef TEST_ds #undef TEST_condF #undef TEST_condLTD return FALSE; /* no, cannot cover */ } /* * Construction of lists of EscSet, which are the possible effects * towards the planned defK-environment E. * - null effects do not contribute. * - multiple equal values can be stored just once. * - logically all subsets of an effect are also contained, * hence subsets of already stored values need not be stored. * - the maximally possible set contains all others. * Hence, if it occurs, it will be the only list element. * The above considerations are used to reduce the effective list length, * as we later will search in such lists. * * We use some macros to fill in new EscSet candidates. * The following must be present, in order to use the macros: * full int counts stored candidates * canp EscSet* points to space for all the candidates * ready label jumped to when max-cand stored * escbase EscSet maximally possible E * covd EscSet current candidate * last EscSet 0 | last stored value * Now, ENOTE() enters a new candidate. * If present, the maximally possible element is stored as only element. * With * cbas EscSet basis for (a set of) current candidates * ENOTE_new(e) constructs "covd" from "cbas" and "e". * With * ecent Position center of defK-environment * ENOTE_ntab(tab,pos) uses "tab" as "fig_cov[]"-like table to * call ENOTE_new for the effect of "pos"-->"ecent". * ENOTE_ntab_c() uses a table pointer already centered with "ecent". * ENOTE_nfig(fig,pos) does this with the standard table "fig_cov[fig]". */ #define ENOTE() \ if( (covd &= escbase) && !SET_GE(last,covd) ) {\ if( covd == escbase ) { \ /* cannot improve */ \ canp[0] = covd; \ full = 1; \ goto ready; \ } \ last = covd; \ canp[full++] = covd; \ } #define ENOTE_cbas() { covd = cbas ; ENOTE() } #define ENOTE_new(e) { covd = cbas | (e); ENOTE() } #define ENOTE_c_tab(tab) (&( (tab)[-(ecent)] )) #define ENOTE_ntab( tab,pos) ENOTE_new( (tab)[(pos) - (ecent)] ) #define ENOTE_ntab_c(tab,pos) ENOTE_new( (tab)[ pos ] ) #define ENOTE_nfig( fig,pos) ENOTE_ntab( fig_cov[fig], pos) #define ENOTE_nfig_c(fig,pos) ENOTE_ntab_c(fig_cov[fig], pos) static int /* #(filled entries) */ m2_plm_lst_through( Xconst Board* bp, /* in this board */ register rEscSet escbase,/* to cover this set of escapes */ register rPosition ecent, /* around this center */ register rPosition e1pos, /* through this one */ register rPosition e2pos, /* this also empty */ register const FieldSet* ebcp, /* these are blocked completely */ PieceSet ultd, /* uncoverable LTDs */ EscSet ucov, /* uncoverable E-part */ const EscSet* ucovtab,/* [upos-ecent] for L|T|D */ EscSet* canp ) /* fill this array */ { /* 35471 */ register rPieceSet atts; register int inx; register int zeroes; register Bool ise; register const Field* tp; register rPosition tpos; register rEscSet covd; register rEscSet cbas; register rEscSet last = 0; register int step; register rColour def; register int full = 0; register Xconst Field* fp; register rPosition fpos; register Xconst Field* ep; register rColour att; /* * Fill a list of the escape coverings, which are (newly) possible * (pseudo legally) because "e1pos" now is empty (piece of moving * side were there, before). The attacks include active and * also inactive attacks, but the inactive ones (uncoverings), * which need be considered here, are only those, which are before * not yet moved pieces. Also, double uncoverings are not * considered here. * FFS: worst case could be handled here: e1pos/e2pos are all, * which are opened, also. * * Follow direct attacks of the moving side, * which appear within the "e1pos", considered empty. * This excludes normal B-beats (would need non-empty target). * e.p. moves are excluded globally. * Simpe and double B-moves are not indicated by datt, * and handled separately. * * Maximal output: 8*S (1 each) + 8*D (7 each) <= 64 */ att = bp->b_tomove; def = opp_colour(att); ep = &(bp->b_f[e1pos]); /* emptied */ ucov &= escbase; /* interesting part */ if( ! ucov ) { /* uncovering does not contribute */ ultd = 0; /* 15350 */ }else if( ultd && (ep->f_c == att) ) { ultd &= ~ SET1(ep->f_idx); /* that one considered elsewhere */ } atts = F_DATT(ep) & COLOUR_MASK(att); if( atts ) { inx = 0; do { while( ! (atts & 01) ) { /* 20289 */ zeroes = MIN_LOW_ZERO(atts); inx += zeroes; atts >>= zeroes; /* 14481 */ } fpos = bp->b_piece[inx]; fp = &(bp->b_f[fpos]); cbas = 0; if( ultd & F_DATT(fp) ) { cbas |= (ucov & ucovtab[fpos-ecent]); /* 650 */ } switch( fp->f_f ) { case bauer: /* done elsewhere */ break; case springer: /* just one more move */ ENOTE_nfig(springer,e1pos) /* 2447 */ break; case koenig: /* just one more move */ ENOTE_nfig(koenig ,e1pos) /* 4574 */ break; case laeufer: /*FALLTHROUGH*/ case turm: /*FALLTHROUGH*/ case dame: /* * Follow just one direction fpos->e1pos */ step = dam_mov[ att_dir(fpos, e1pos) ]; /* 12380 */ tpos = e1pos; tp = ep; ise = TRUE; do { /* 36616 */ if( IS_EBC64(ebcp, tp->f_pos64) ) { ENOTE_cbas() }else { ENOTE_nfig(fp->f_f, tpos) } if( ! ise ) { break; } tpos += step; tp += step; ise = (tpos == e2pos) || (tp->f_c == empty); }while( ise || (tp->f_c == def) ); break; } }while( ++inx, (atts >>= 1) ); } /* * Now search for unblocked B-moves (not beat). * There can be at most one interesting move source, * but the opening "e1pos" may enable a single and a double B-step. * And: a double step through "e1pos" may contribute to E. * On the other hand, as a B-move is not indicated by an attack * in the target, the defK at "ecent" may block. */ step = bau_mov[att]; fpos = e1pos - step; if( fpos != ecent ) { /* else no chance at all */ fp = &(bp->b_f[fpos]); if( (fpos == e2pos) || (fp->f_c == empty) ) { /* empty: try double */ fpos -= step; if( (LIN(fpos) == BAS_LIN(att)) && (fpos != e2pos) && (fpos != ecent) ) { fp = &(bp->b_f[fpos]); if( (fp->f_f == bauer) && (fp->f_c == att) ) { cbas = 0; if( ultd & F_DATT(fp) ) { cbas |= (ucov & ucovtab[fpos-ecent]); } ENOTE_nfig(bauer , e1pos) } } }else if( fpos != ecent ) { /* non-empty src, not blocked */ if( (fp->f_f == bauer) && (fp->f_c == att) ) { cbas = 0; if( ultd & F_DATT(fp) ) { cbas |= (ucov & ucovtab[fpos-ecent]); } if( LIN(e1pos) == PROM_LIN(att) ) { /* promotion: S or D */ ENOTE_nfig(dame , e1pos) ENOTE_nfig(springer, e1pos) }else { /* single or double step */ ENOTE_nfig(bauer , e1pos) if( LIN(fpos) == BAS_LIN(att) ) { tpos = e1pos + step; tp = &(bp->b_f[tpos]); if( (tpos != ecent) && ((tpos == e2pos) || (tp->f_c == empty)) ) { ENOTE_nfig(bauer, tpos) } } } } } } ready:; return full; } static int /* #(filled entries) */ m2_plm_lst_piece( Xconst Board* bp, /* in this board */ register rEscSet escbase, /* to cover this set of escapes */ register rPosition ecent, /* around this center */ register rPosition fpos, /* by moving this piece */ register rPosition e1pos, /* this one considered empty */ const FieldSet* ebcp, /* these are blocked completely */ PieceSet ultd, /* uncoverable LTDs */ EscSet ucov, /* uncoverable E-part */ const EscSet* ucovtab, /* [upos-ecent] for L|T|D */ EscSet* canp ) /* fill this array */ { /* 93597 */ register rPosition tpos; register const Field* tp; register rColour c; register rEscSet covd; register rEscSet cbas; register rEscSet last = 0; register const EscSet* covtab; register int step; register rColour att; register Xconst Field* fp; register int dir; #if 0 register int dirmax; #endif register unsigned dirs; register int full = 0; /* * First, determine uncoverings. They are common to all moves. * This is done by initializing 'cbas'. After this, 'uldt' is * not needed any more. * FFS: B staying within uncovering line. */ fp = &(bp->b_f[fpos]); att = fp->f_c; cbas = 0; /* if no uncovering */ ucov &= escbase; /* interesting part */ if( ucov && ultd ) { /* uncovering may contribute */ ultd &= ~ SET1(fp->f_idx); /* does not uncover itself */ if( ultd && (ultd & F_DATT(fp)) ) { cbas = ucov & ucovtab[fpos - ecent]; /* 4814 */ } } switch( fp->f_f ) { case bauer: /* 26938 */ { Bool prom; register rColour def; def = opp_colour(att); prom = (LIN(fpos) == BAS_LIN(def)); #define NOTE_bau(tp) \ if( prom ) { \ if( IS_EBC64(ebcp, (tp)->f_pos64) ) { \ ENOTE_cbas() \ }else { \ ENOTE_nfig(dame, tpos) \ } \ ENOTE_nfig(springer, tpos) \ }else { \ ENOTE_nfig(bauer , tpos) \ } /* B-beat */ for( dir=0 ; dir<2 ; ++dir ) { tp = &(bp->b_f[tpos = fpos + (dir?bau_left:bau_right)[att]]); if( (tp->f_c == def) && (tpos != e1pos) ) { NOTE_bau(tp) } } /* B-move */ step = bau_mov[att]; tp = &(bp->b_f[tpos = fpos + step]); if( (tp->f_c == empty) || (tpos == e1pos) ) { NOTE_bau(tp) if( LIN(fpos) == BAS_LIN(att) ) { tpos += step; tp += step; if( (tp->f_c == empty) || (tpos == e1pos) ) { NOTE_bau(tp) } } } #undef NOTE_bau } break; case springer: /* 10253 */ #if 0 covtab = fig_cov[springer]; for( dir=0 ; dir<8 ; ++dir ) { tp = &(bp->b_f[tpos = fpos + spr_mov[dir]]); /* 81940 */ c = tp->f_c; if( (c == border) || ((c == att) && (tpos != e1pos)) ) { continue; /* blocked */ } ENOTE_ntab(covtab, tpos) /* 71437 */ } #else dirs = LDF_ACOV_TAB(springer)[fpos-ecent] & pos64_nb_sdirs[fp->f_pos64]; scDSinc(sc_dirs_f[springer][bitsum[dirs]]); if( dirs ) { covtab = ENOTE_c_tab(fig_cov[springer]); /* 10152 */ dir = 0; do { if( ! (dirs & 01) ) { /* 40913 */ c = MIN_LOW_ZERO(dirs); /* 19346 */ dir += c; dirs >>= c; } tp = &(bp->b_f[tpos = fpos + spr_mov[dir]]); /* 40913 */ # if 0 if( tp->f_c == border ) panic("m2: S-border"); # endif if( (tp->f_c == att) && (tpos != e1pos) ) { continue; /* blocked */ } ENOTE_ntab_c(covtab, tpos) /* 39046 */ }while( ++dir, (dirs >>= 1) ); } /* 10222 */ ENOTE_cbas() /* note uncoverings without active attacks */ #endif break; case koenig: /* 12445 */ #if 0 covtab = fig_cov[koenig]; for( dir=0 ; dir<8 ; ++dir ) { tp = &(bp->b_f[tpos = fpos + dam_mov[dir]]); /* 99560 */ c = tp->f_c; if( (c == border) || ((c == att) && (tpos != e1pos)) ) { continue; /* blocked */ } ENOTE_ntab(covtab, tpos) /* 98244 */ } #else dirs = LDF_ACOV_TAB(koenig)[fpos-ecent] & pos64_nb_ddirs[fp->f_pos64]; scDSinc(sc_dirs_f[koenig][bitsum[dirs]]); /* 12445 */ if( dirs ) { covtab = ENOTE_c_tab(fig_cov[koenig]); /* 1523 */ dir = 0; do { if( ! (dirs & 01) ) { /* 3750 */ c = MIN_LOW_ZERO(dirs); /* 2281 */ dir += c; dirs >>= c; } tp = &(bp->b_f[tpos = fpos + dam_mov[dir]]); # if 0 if( tp->f_c == border ) panic("m2: K-border"); # endif if( (tp->f_c == att) && (tpos != e1pos) ) { continue; /* blocked */ } ENOTE_ntab_c(covtab, tpos) }while( ++dir, (dirs >>= 1) ); } /* 12445 */ ENOTE_cbas() /* note uncoverings without active attacks */ #endif break; #if 0 case laeufer: /* 11186 */ dir = MIN_L_DIR; dirmax = MAX_L_DIR; goto ltd; case turm: /* 25263 */ dir = MIN_T_DIR; dirmax = MAX_T_DIR; goto ltd; case dame: /* 7512 */ dir = MIN_D_DIR; dirmax = MAX_D_DIR; ltd:; /* 43961 */ covtab = fig_cov[fp->f_f]; for( ; dirf_c; /* 522187 */ if( c == border ) { break; /* 123438 */ } tpos += step; /* 398749 */ if( (c == att) && (tpos != e1pos) ) { break; /* blocked */ /* 27652 */ } /* 371097 */ if( IS_EBC64(ebcp, tp->f_pos64) ) { ENOTE_cbas() /* 124769 */ }else { ENOTE_ntab(covtab, tpos) /* 246328 */ } /* 369509 */ if( (c != empty) && (tpos != e1pos) ) { break; /* beat terminates dir */ } } } break; #else case laeufer: /* 11186 */ dirs = LDF_ACOV_TAB(laeufer)[fpos-ecent] & pos64_nb_ddirs[fp->f_pos64]; covtab = ENOTE_c_tab(fig_cov[laeufer]); scDSinc(sc_dirs_f[laeufer][bitsum[dirs]]); goto ltd; case turm: /* 25263 */ dirs = LDF_ACOV_TAB(turm )[fpos-ecent] & pos64_nb_ddirs[fp->f_pos64]; covtab = ENOTE_c_tab(fig_cov[turm]); scDSinc(sc_dirs_f[turm][bitsum[dirs]]); goto ltd; case dame: /* 7512 */ dirs = LDF_ACOV_TAB(dame )[fpos-ecent] & pos64_nb_ddirs[fp->f_pos64]; covtab = ENOTE_c_tab(fig_cov[dame]); scDSinc(sc_dirs_f[dame][bitsum[dirs]]); ltd:; /* 43961 */ for( dir=0 ; dirs ; ++dir, (dirs>>=1) ) { if( ! (dirs & 01) ) { /* 112563 */ c = MIN_LOW_ZERO(dirs); /* 46380 */ dir += c; dirs >>= c; } step = dam_mov[dir]; tpos = fpos; tp = fp; /* 112563 */ for(;;) { c = (tp += step)->f_c; /* 356996 */ if( c == border ) { break; } tpos += step; /* 304772 */ if( (c == att) && (tpos != e1pos) ) { break; /* blocked */ } if( ! IS_EBC64(ebcp, tp->f_pos64) ) { /* 282277 */ ENOTE_ntab_c(covtab, tpos) /* 192748 */ } if( (c != empty) && (tpos != e1pos) ) { break; /* beat terminates dir */ } } } ENOTE_cbas() /* note uncoverings without active attacks */ break; #endif } ready:; return full; } #if M2_CUT_STATS static void m2__stat_cut( register int full, register const EscSet* esp, register rEscSet escbase ) { register rEscSet last; register int i; last = 0; for( i=0 ; im2_plmtab[m2p->m2_plmfree]); full = m2_plm_lst_through( bp, /* in this board */ m2p->m2_escs, /* to cover this set of escapes */ m2p->m2_defKdst, /* around this center */ epos, /* through this one */ m2p->m2_defKsrc, /* this also empty */ &(m2p->m2_ebc_bydef), /* blocked completely */ m2p->m2_apsultd, /* uncoverable LTDs */ m2p->m2_ultdcov, /* uncoverable E-part */ m2p->m2_ucovtab, /* [upos-ecent] for L|T|D */ ep /* fill this array */ ); scinc(sc_m2ll_thru[full]); m2p->m2_plml_thru[idx] = full; m2p->m2_done_thru |= SET1(idx); if( full <= 0 ) { /* empty, nothing collected */ m2p->m2_plmx_thru[idx] = -1; /* codes empty set */ return FALSE; } m2__stat_cut(full, ep, m2p->m2_escs); m2p->m2_plmx_thru[idx] = m2p->m2_plmfree; m2p->m2_plmfree += full; /* and allocate */ return *ep == m2p->m2_escs; } static Bool /* whether contains total E */ m2__make_move( Xconst Board* bp, /* in this board */ int8 idx, /* move this piece */ M2State* m2p ) /* IN/OUT */ { int full; EscSet* ep; ep = &(m2p->m2_plmtab[m2p->m2_plmfree]); full = m2_plm_lst_piece( bp, /* in this board */ m2p->m2_escs, /* to cover this set of escapes */ m2p->m2_defKdst, /* around this center */ bp->b_piece[idx], /* by moving this piece */ m2p->m2_defKsrc, /* this one considered empty */ &(m2p->m2_ebc_bydef), /* blocked completely */ m2p->m2_apsultd, /* uncoverable LTDs */ m2p->m2_ultdcov, /* uncoverable E-part */ m2p->m2_ucovtab, /* [upos-ecent] for L|T|D */ ep /* fill this array */ ); scinc(sc_m2ll_move[full]); m2p->m2_plml_move[idx] = full; m2p->m2_done_move |= SET1(idx); if( full <= 0 ) { /* empty, nothing collected */ m2p->m2_plmx_move[idx] = -1; /* codes empty set */ return FALSE; } m2__stat_cut(full, ep, m2p->m2_escs); m2p->m2_plmx_move[idx] = m2p->m2_plmfree; m2p->m2_plmfree += full; /* and allocate */ return *ep == m2p->m2_escs; } static M2State g_m2state = { 0 }; Eximpl void m2_enter( Xconst Board* bp, /*ARGSUSED*/ const Movelist* lp, M2Info* m2ip ) { if( g_m2state.m2_ip ) { panic("missing m2_leave"); } g_m2state.m2_ip = m2ip; g_m2state.m2_lp = lp; g_m2state.m2_prepd = FALSE; m2ip->m2_apscands = ~(PieceSet)0; m2ip->m2_apsdunno = 0; } Eximpl void m2_leave(void) { if( g_m2state.m2_ip ) { g_m2state.m2_ip->m2_apsdunno = ~(PieceSet)0; g_m2state.m2_ip = 0; } } static Bool /* whether continue normally */ m2_prep_castle( register int att, /* C to move */ register PieceSet* apsdunnop, /* IN/OUT */ register Xconst Field* p, /* akp */ register int step, /* from K towards T */ register int nstep ) /* #field between K&T */ { register Xconst Field* xp; xp = 0; do { p += step; if( p->f_c != empty ) { if( p->f_c == att ) { if( xp ) { /* second attacker */ return TRUE; /* Hurra */ } xp = p; /* remember intermediate attacker */ }else { /* is defender */ return TRUE; /* Hurra */ } } }while( --nstep ); if( xp ) { /* just one att intermediate */ *apsdunnop |= SET1(xp->f_idx); /* exclude him */ return TRUE; /* and succeed */ } /* *apsdunnop = COLOUR_MASK(att); */ return FALSE; /* no chance, at all */ } static void m2_prep( register Xconst Board* bp, /* current board */ register M2State* m2p ) /* IN/OUT: internal 2-mate info */ { PieceSet apscands = 0; /* may start a 2-mate */ PieceSet apsdunno = 0; /* bad starters */ int att; att = bp->b_tomove; if( bp->b_castle[att] ) { /* complex & seldom */ /* * If there is exactly one attacker between K&T, * we throw that piece (index) into apsdunno, and go on. */ Xconst Field* akp; akp = K_FP(bp, att); if( ( (bp->b_castle[att] & castle00 ) && (!m2_prep_castle(att, &apsdunno, akp, MK_DELTA(1,0), 2)) ) || ( (bp->b_castle[att] & castle000) && (!m2_prep_castle(att, &apsdunno, akp, -MK_DELTA(1,0), 3)) ) ) { scinc(sc_m2f_castle); goto fail; } } if( empty_list(m2p->m2_lp) ) { goto tot_succ; } #if 0 /* unused */ { /* * Scan (legal) attacker moves. The list contains all true candidates. * Note, however, that the list may be constructed by "ala_move_gen()". */ register Move* ap; m2p->m2_apslm = 0; /* collect moving pieces */ formoves( m2p->m2_lp, ap ) { m2p->m2_apslm |= SET1(ap->m_idx); } if( ! m2p->m2_apslm ) { goto tot_succ; } } #endif #if 0 /* unused */ /* collect attackers LTD */ m2p->m2_apsltd = 0; { int8 idx; int8 minidx; Position fpos; minidx = COLOUR_IDX(att); for( idx = minidx + bp->b_max_piece[att] - 1 ; idx >= minidx ; --idx ) { fpos = bp->b_piece[idx]; if( no_pos(fpos) ) continue; switch( bp->b_f[fpos].f_f ) { case dame: case turm: case laeufer: m2p->m2_apsltd |= SET1(idx); break; } } } #endif m2_set_defk(bp, m2p); if( no_pos(m2p->m2_defKdst) ) { /* couldn't find a target */ scinc(sc_m2f_noesc); goto fail; } /* * Collect guaranteed complete blocking by defender pieces ... * Note, that the K itself does not block. */ m2p->m2_ebc_bydef.fs_long[0] = 0; m2p->m2_ebc_bydef.fs_long[1] = 0; #if 1 { register rint8 idx; register rint8 minidx; register rPosition fpos; register const uint16* ebcxp; register ruint16 x; register const FieldSet* p; ebcxp = &(ebc_inx[ bp->b_f[ m2p->m2_defKdst ].f_pos64 ][0]); minidx = COLOUR_IDX(opp_colour(att)); idx = minidx + bp->b_max_piece[opp_colour(att)] - 1; /* max (incl) */ #if KING_IDX == 0 minidx += 1; /* skip king outside loop */ #endif for( ; idx >= minidx ; --idx ) { #if KING_IDX != 0 if( idx == (minidx + KING_IDX) ) continue; #endif fpos = bp->b_piece[idx]; if( no_pos(fpos) ) continue; x = ebcxp[ bp->b_f[fpos].f_pos64 ]; if( x ) { p = &(ebc_tab[x]); m2p->m2_ebc_bydef.fs_long[0] |= p->fs_long[0]; m2p->m2_ebc_bydef.fs_long[1] |= p->fs_long[1]; } } } #endif /* * Now, as we have found and decided for a target, we compute * maximal uncovering effects to E (before non-moving pieces). * FFS: if there is at most one dir towards E, we may scan there. * By the way, also collect all PLM pieces of the attacker. */ m2p->m2_apsUltd = 0; m2p->m2_apsultd = 0; m2p->m2_ultdcov = 0; m2p->m2_ucovtab = 0; { register rint8 idx; register rint8 minidx; register rPosition fpos; register rPieceSet apseffplm; register rPosition ecent; register int ustyle; /* 01=L 02=T */ register rEscSet cov; register rFigure fig; register unsigned fpos64; ecent = m2p->m2_defKdst; ustyle = 0; apseffplm = 0; minidx = COLOUR_IDX(att); for( idx = minidx + bp->b_max_piece[att] - 1 ; idx >= minidx ; --idx ) { fpos = bp->b_piece[idx]; if( no_pos(fpos) ) continue; apseffplm |= SET1(idx); switch( fig = bp->b_f[fpos].f_f ) { case laeufer: ustyle |= 01; /* L */ goto ltd_cov; case turm: ustyle |= 02; /* T */ goto ltd_cov; case dame: ustyle |= 03; /* D */ ltd_cov:; cov = fig_cov[fig][fpos - ecent] & m2p->m2_escs; if( cov ) { m2p->m2_apsUltd |= SET1(idx); fpos64 = bp->b_f[fpos].f_pos64; if( ! IS_EBC64(&(m2p->m2_ebc_bydef), fpos64) ) { m2p->m2_apsultd |= SET1(idx); m2p->m2_ultdcov |= cov; } } break; } } switch( ustyle ) { case 00: m2p->m2_ucovtab = 0; break; case 01: m2p->m2_ucovtab = fig_cov[laeufer]; break; /*FFS block*/ case 02: m2p->m2_ucovtab = fig_cov[turm ]; break; /*FFS block*/ case 03: m2p->m2_ucovtab = fig_cov[dame ]; break; /*FFS block*/ } m2p->m2_apseffplm = apseffplm; } m2p->m2_plmfree = 0; m2p->m2_done_move = 0; m2p->m2_done_thru = 0; m2p->m2_ocan_idx = -1; m2p->m2_onot_idx = -1; apscands = COLOUR_MASK(att); /*FFS*/ /* * FFS: a piece is not a candidate, e.g. if it cannot contribute * itself, at all, there are no uncoverings, and all others * cannot cover completely. */ out:; M2_V( printf("m2 -> %8lx (dunno %8lx)\n", (long) apscands, (long) apsdunno); ) m2p->m2_apscands = apscands; m2p->m2_apsdunno = apsdunno; m2p->m2_prepd = TRUE; return; fail:; /* everybody is a candidate */ apscands = COLOUR_MASK(att); apsdunno = apscands; goto out; tot_succ:; /* nobody is a candidate */ apscands = 0; apsdunno = 0; goto out; } #if M2_VERBOSE static void m2_show_thru( const M2State* m2p, EscSet escs, int8 idx ) { register rM2PlmInx slot; printf("m2 escs %3x thru[%d]:", escs, idx); if( (slot = m2p->m2_plmx_thru[idx]) >= 0 ) { register const EscSet* canp; register int len; canp = &(m2p->m2_plmtab[slot]); for( len = m2p->m2_plml_thru[idx] ; len ; ++canp, --len ) { printf(" %3x%c", *canp, " *"[!!SET_GE(*canp, escs)]); } } printf("\n"); } static void m2_tell_fail( const Board* bp, const Move* mp, const M2State* m2p, const char* txt ) { printf("M2 fail %s for [idx %2d] ", txt, mp->m_idx); put_move(bp, mp); put_packed(&bp->b_packed, "m2\t"); printf("defKdst = "); put_position(m2p->m2_defKdst); printf(", escs = %3x", m2p->m2_escs); printf(", ucov = %3x", m2p->m2_ultdcov); printf(", ultd = %lx", (long) m2p->m2_apsultd); printf("\n"); printf("\n"); } # define M2_TELL_FAIL(bp,mp,m2p,t) M2_V( m2_tell_fail(bp,mp,m2p,t); ) #else # define M2_TELL_FAIL(bp,mp,m2p,t) /*empty*/ #endif Eximpl Bool /* whether this move is a candidate */ m2_cand( register Xconst Board* bp, /* current board */ register const Move* mp, /* att move to consider */ M2Info* m2ip ) /* IN/OUT */ { register M2State* m2p; register unsigned idx; register int att; register rEscSet escs; register rPosition mfrom; /* mp->m_from */ register rPosition mto; /* mp->m_to */ register rPosition kdst; /* m2p->m2_defKdst */ m2p = &g_m2state; if( m2p->m2_ip != m2ip ) { panic("m2_cand without m2_enter"); return TRUE; } if( ! m2p->m2_prepd ) { m2_prep(bp, m2p); /* FFS: ++apx_cost */ m2ip->m2_apscands = m2p->m2_apscands; m2ip->m2_apsdunno = m2p->m2_apsdunno; } idx = mp->m_idx; #if 1 /* FFS: all callers do these tests, FFS: just prepd */ if( ! (m2p->m2_apscands & SET1(idx)) ) { return FALSE; /* already known to be no cand */ } if( m2p->m2_apsdunno & SET1(idx) ) { scinc(sc_m2c_dunno); return TRUE; /* already known to be bad */ } #endif mfrom = mp->m_from; mto = mp->m_to; if( (mp->m_fig == bauer) /* results in a B */ && ! no_pos(bp->b_ep) /* some e.p. may be legal */ && (bp->b_f[mto].f_c == empty) /* empty target */ && lfr_dir(att_dir(mfrom, mto)) /* L-dir */ ) { /* e.p. is too complex */ scinc(sc_m2c_ep); return TRUE; /* looks like a candidate */ } att = bp->b_tomove; kdst = m2p->m2_defKdst; /* * If the move might attack the selected destination of the def-K, * we have to admit, that this move might be a candidate. * Currently we do not know about other def-K targets. * * Such an attack is done either indirectly or directly. * Note, that we check the target, not the source of the defK move. * FFS: cache first part [idx] */ if( dam_dir(att_dir(mfrom, kdst)) /* 121961 */ && (m2p->m2_ultdcov & (1<b_f[kdst])); /* 6143 */ atts |= F_IATT(&(bp->b_f[m2p->m2_defKsrc])); atts &= m2p->m2_apsultd; atts &= F_DATT(&(bp->b_f[mfrom])); atts &= COLOUR_MASK(att); if( atts ) { scinc(sc_m2c_illf); /* FFS: disable piece? */ M2_TELL_FAIL(bp, mp, m2p, "illf"); return TRUE; /* sorry */ } /* FFS: uncovering a D ? */ } { register int dir; #if 0 dir = dir_for_fig(mto, kdst, mp->m_fig, att, TRUE); if( ! no_dir(dir) && m2_way_empty(bp, mto, kdst, dir, mfrom, m2p->m2_defKsrc) ) { scinc(sc_m2c_illt); M2_TELL_FAIL(bp, mp, m2p, "illt"); return TRUE; } #else if( ! no_dir(dir = att_dir(mto, kdst)) ) { /*121439 */ switch( mp->m_fig ) { /* 40977 */ case koenig: /* 7321 */ if( dam_dir(dir) && ((mto + dam_mov[dir]) == kdst) ) { goto illt; /* 6721, 24 */ } break; case springer: /* 6754 */ if( spr_dir(dir) ) goto illt; /* 3187 */ break; case laeufer: /* 3994 */ if( lfr_dir(dir) ) goto illt_we; /* 268 */ break; case turm: /* 13747 */ if( trm_dir(dir) ) goto illt_we; break; case dame: if( dam_dir(dir) ) { /* 7846 */ illt_we:; /* 13363 */ if( m2_way_empty(bp, mto, kdst, dir, mfrom, m2p->m2_defKsrc) ) { illt: scinc(sc_m2c_illt); /* 9624 */ M2_TELL_FAIL(bp, mp, m2p, "illt"); /* FFS: this is the most annoying fail reason */ return TRUE; } } break; case bauer: /* 1315 */ dir = dir_for_fig(mto, kdst, bauer, att, TRUE); if( ! no_dir(dir) ) { goto illt_we; } break; } } #endif } /* * Ok, this move does not invalidate the selected def-K move. * * Now, the environment E around the def-K is restricted * by uncovering attacks through the source of the move. */ #if WITH_9E_FINE if( m2p->m2_defKis9E ) { goto succ; } #endif escs = m2p->m2_escs; /* 111815 */ if( F_DATT(&(bp->b_f[mfrom])) & m2p->m2_apsultd ) { /* 5523 */ escs &= ~ (m2p->m2_ucovtab[ mfrom - kdst ] & m2p->m2_ultdcov); } if( ! escs ) { scinc(sc_m2c_from); /* FFS: disable piece? */ M2_TELL_FAIL(bp, mp, m2p, "from"); /* 3 */ return TRUE; /* sorry */ } /* * If this is known to be possible by the other pieces, * we need not look further: it is a candidate. * This is even true for all the other moves of the same piece. */ if( (m2p->m2_ocan_idx == idx) && SET_GE(m2p->m2_ocan_escs, escs) ) { m2ip->m2_apsdunno = (m2p->m2_apsdunno |= SET1(idx)); scinc(sc_m2c_othr); M2_TELL_FAIL(bp, mp, m2p, "othr"); /* 54 */ return TRUE; } /* * Then, E is further restricted by the next move to come. * That will be either * (a) the same piece moving again, or * (b) some other piece moving from its original position. * * In case (a) we may have beaten some defender piece, * opening a second position. That is the only case, * where uncoverings and active coverings must not be restricted * by defenders (at least not by the beaten one). * * In case (b) we leave the first attacker where it is, * which actively may restrict E. * * Case (a): [move twice] * - if the moving piece is beaten by the defK, Case (a) is ok. * FFS: a pawn may be blocked (no second move) */ if( mto != kdst ) { /* non-beaten: may go on */ register rEscSet esave; register Xconst Field* tp; register Xconst Field* fp; const FieldSet* ebcp; esave = escs; /* save */ tp = &(bp->b_f[mto]); /* 111753 */ if( tp->f_c != empty ) { /* does beat: opens t */ fp = &(bp->b_f[mfrom]); if( (F_DATT(tp) | (F_IATT(tp) & F_DATT(fp))) & m2p->m2_apsUltd & ~SET1(idx) ) { escs &= ~ fig_cov[dame][ mto - kdst ]; if( ! escs ) { scinc(sc_m2c_beat); M2_TELL_FAIL(bp, mp, m2p, "beat"); return TRUE; /* sorry */ } } ebcp = 0; /* no known blocking */ }else { ebcp = &(m2p->m2_ebc_bydef); /* 93990 */ } /* * Scan all pseudo-legal moves of a "mp->m_fig" from "mp->m_to", * considering empty "m2p->m2_defKsrc" and "mp->m_from". * Tell, whether the active/direct attacks to defKdst, which * may result in any such move, can possibly cover "escs". * A promotion in this first move is no real problem. * A promotion in the second move must be handled differently. */ if( (mp->m_fig == bauer) && (LIN(mto) == BAS_LIN(opp_colour(att))) ) { /* * A promotion will follow in the next move. */ if( m2_plm_prom_cancov(bp, escs, kdst, mto, m2p->m2_defKsrc, mfrom, ebcp) ) { scinc(sc_m2c_move); M2_TELL_FAIL(bp, mp, m2p, "prom"); return TRUE; } }else { /* 111643 */ if( (cov_esc[escs]->figcan & (1 << mp->m_fig)) && m2_plm_act_cancov(bp, escs, kdst, mp->m_fig, mto, /*13288*/ m2p->m2_defKsrc, mfrom, ebcp) ) { scinc(sc_m2c_move); M2_TELL_FAIL(bp, mp, m2p, "move"); /* 3547 */ return TRUE; } } escs = esave; /* restore */ } /* * Case (b): [move other piece] * - remove from E, what may be actively attacked from m_to * - Check, that the resulting E is not coverable by any * pseudo-legal move by any other attacker piece, * through m_from through. * - Check, that the resulting E is not coverable by any * pseudo-legal move by any attacker piece, except the current. * For this use the original board contents, and consider * the defK has moved. */ { /* 108101 */ register unsigned to64; if( ! (((1<m_fig)) || ( (to64 = bp->b_f[mto].f_pos64), /* 61566 */ ! IS_EBC64(&(m2p->m2_ebc_bydef), to64) ) ) { escs &= ~ fig_cov[mp->m_fig][mto - kdst]; /* 86764 */ if( ! escs ) { scinc(sc_m2c_frst); M2_TELL_FAIL(bp, mp, m2p, "frst"); /* 4 */ return TRUE; } } } { /* 108097 */ register rM2PlmInx slot; register rPieceSet others; if( (m2p->m2_ocan_idx == idx) && SET_GE(m2p->m2_ocan_escs, escs) ) { scinc(sc_m2c_othr); /* 7729, 945 */ M2_TELL_FAIL(bp, mp, m2p, "othr2"); return TRUE; } /* 107152 */ if( ! (m2p->m2_done_thru & SET1(idx)) ) { /* 35471 */ if( m2__make_thru(bp, idx, mfrom, m2p) ) { /* covers E */ m2ip->m2_apsdunno = (m2p->m2_apsdunno |= SET1(idx)); scinc(sc_m2c_thru); M2_TELL_FAIL(bp, mp, m2p, "thru-tot"); /* 166 */ return TRUE; } } /* 106986 */ if( (slot = m2p->m2_plmx_thru[idx]) >= 0 ) { register EscSet* canp; register int len; canp = &(m2p->m2_plmtab[slot]); /* 22929 */ for( len = m2p->m2_plml_thru[idx] ; len ; ++canp, --len ) { if( SET_GE(*canp, escs) ) { /*~ 36490 */ /* FFS: could be remembered in ocan */ scinc(sc_m2c_thru); M2_V( m2_show_thru(m2p, escs, idx); ) M2_TELL_FAIL(bp, mp, m2p, "thru"); /*~ 440 */ return TRUE; } } } /* 106522 */ /* * Check the other pieces, which may have an effect. */ if( (m2p->m2_onot_idx == idx) && SET_GE(escs, m2p->m2_onot_escs) ) { goto rdy_others; /* 68052, 59616 */ } others = m2p->m2_apseffplm & ~SET1(idx); /* 46906 */ if( others && (m2p->m2_defKdidx >= 0) ) { /* 46785 */ others &= ~ SET1(m2p->m2_defKdidx); /* is beaten 2500 */ } if( others ) { M2_V( printf("m2rest, idx %2d, escs %3x, others %lx\n", (int)idx, (int)escs, (long)others); ) idx = 0; /* reuse for different purpose! */ do { while( ! (others & 01) ) { /* 171618 */ register unsigned z; z = MIN_LOW_ZERO(others); /* 69908 */ idx += z; others >>= z; } /* check "idx", another piece. */ if( ! (m2p->m2_done_move & SET1(idx)) ) { if( m2__make_move(bp, idx, m2p) ) { /* covers E 93597 */ m2ip->m2_apsdunno = (m2p->m2_apsdunno |= ~SET1(idx)); scinc(sc_m2c_rest); M2_TELL_FAIL(bp, mp, m2p, "rest-tot"); /* 1619 */ return TRUE; } } /* 169999 */ if( (slot = m2p->m2_plmx_move[idx]) >= 0 ) { register EscSet* canp; register int len; canp = &(m2p->m2_plmtab[slot]); /* 128685 */ for( len = m2p->m2_plml_move[idx] ; len ; ++canp, --len ) { if( SET_GE(*canp, escs) ) { /* 313615 */ m2p->m2_ocan_idx = mp->m_idx; m2p->m2_ocan_escs = *canp; scinc(sc_m2c_rest); M2_TELL_FAIL(bp, mp, m2p, "rest"); /* 1722 */ return TRUE; } } }else { /* empty: need not be searched, again */ m2p->m2_apseffplm &= ~SET1(idx); /* 41314 */ } /* 166020 */ }while( ++idx, (others >>= 1) ); m2p->m2_onot_idx = mp->m_idx; /* 41116 */ m2p->m2_onot_escs = escs; } rdy_others:; } succ:; scinc(sc_m2c_succ); /* 100924 */ return FALSE; /* hurra, cannot do 2-mate */ }