/*
 * 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 <stdio.h>
#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<M2E_MAXVAL ; ++i ) {
	    totcnt += sc_escval[i];
	    totval += sc_escval[i] * (double)i;
	}
	if( sc_m2f_castle || sc_m2f_prom || sc_m2f_noesc ) {
	    printf("m2 fail: %7lu prom, %7lu castle, %7lu noesc\n",
		(unsigned long) sc_m2f_prom,
		(unsigned long) sc_m2f_castle,
		(unsigned long) sc_m2f_noesc);
	}
	if( totcnt ) {
	    printf("m2e: avg %4.2f, cnt = %lu\n",
		    (double) (totval / totcnt) / M2E_VAL,
		    (unsigned long) totcnt);
# if (M2E_VAL >= 2) && (M2E_VAL <= 4)
	    {
		    register int	j;
		    register int	k;
		    register Counter	sum;
		for( j=0 ; j<M2E_MAXVAL ; j+=M2E_VAL ) {
		    sum = 0;
		    for( k=0 ; k<M2E_VAL ; ++k ) {
			i = j+k;
			cnt = ((i >= 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_VAL ; ++k ) {
			i = j+k;
			cnt = ((i >= M2E_MAXVAL) ? 0 : sc_escval[i]);
			show_nz(1, 7+!k, '-', cnt);
		    }
		    printf("  =");
		    for( k=0 ; k<M2E_VAL ; ++k ) {
			i = j+k;
			cnt = ((i >= 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<M2E_MAXVAL ; ++i ) {
		cnt = sc_escval[i];
		if( cnt ) {
		    printf("m2e v=%2d [%4.2f]: %7lu [%5.1f%%]\n",
			(int) i,
			(double) i / M2E_VAL,
			(unsigned long) cnt,
			percent(cnt, totcnt));
		}
	    }
# endif	/* M2E_VAL > 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 ; fig<MAX_FIGURES ; ++fig ) {
		sum = 0;
		for( i=0 ; i<9 ; ++i ) {
		    sum += sc_dirs_f[fig][i];
		}
		if( sum ) {
		    printf("m2dirs %c:", chr_Figure(fig));
		    for( i=0 ; i<9 ; ++i ) {
			show_nz(1, 6, '-', sc_dirs_f[fig][i]);
		    }
		    printf("\n");
		}
	    }
	}
# endif
    }
    sc_m2f_castle = 0;
    sc_m2f_prom   = 0;
    sc_m2f_noesc  = 0;
    for( i=0 ; i<M2E_MAXVAL ; ++i ) {
	sc_escval[i] = 0;
    }
    sc_m2c_dunno = 0;
    sc_m2c_ep    = 0;
    sc_m2c_illf  = 0;
    sc_m2c_illt  = 0;
    sc_m2c_from  = 0;
    sc_m2c_beat  = 0;
    sc_m2c_move  = 0;
    sc_m2c_frst  = 0;
    sc_m2c_othr  = 0;
    sc_m2c_thru  = 0;
    sc_m2c_rest  = 0;
    sc_m2c_succ  = 0;
    for( i=0 ; i < (1+MAX_ESCSETLIST) ; ++i ) {
	sc_m2ll_thru[i] = 0;
	sc_m2ll_move[i] = 0;
# if M2_CUT_STATS
	sc_m2ll_cuteq [i] = 0;
	sc_m2ll_cutcov[i] = 0;
	sc_m2ll_cutmax[i] = 0;
# endif
    }
# if M2_STEP1_STATS
    sc_step1_dirs   = 0;
    sc_step1_border = 0;
# endif
# if M2_DIRS_STATS
    {
	    register int	fig;
	for( fig=0 ; fig<MAX_FIGURES ; ++fig ) {
	    sc_dirs_f[fig][i] = 0;
	}
    }
# endif
#endif	/* M2_STATS */
}


/*
 * Select def-K target and the escapes from there.
 * Within the current board the target must be a legal move
 * (if the defK were to move).
 * We like most that target, where there is a maximal escape set.
 * Also, indirect attacks indicate a possibility to restrict
 * the effective destinations.  Hence, we
 * - give 4 points for an escape (M2E_VAL)
 * - take 1 point for iatts in an escape
 * and take the one with the most points.
 * FFS: bonus for beating a piece.
 *
 * Note, that att is to move, and hence there are currently no datt
 * within defK, and hence no datt obscured by defK.
 * FFS: take legal att-moves into account.
 *
 * With a naive approach, we evaluate 8*8 = 64 Fields, many of which
 * are evaluated twice or more.  There are at most 5*5 = 25 different
 * Fields to evaluate.  Borders can cut this down.
 *
 * Filled:
 *	m2_defKsrc
 *	m2_defKdst
 *	m2_defKdir
 *	m2_defKdidx
 *	m2_escs
 */
#define WITH_E25	1		/* CF */

#if WITH_E25

static uint8	dirdirinx_inited	= 0;
static uint8	dirdirinx[8][8];
# if 0	/* FFS: how to compute */
static uint32	ddx_border1impl[8];	/* [dir] */
static uint32	ddx_border2impl[25];	/* [ddx] */
# endif

    static void
m2__init_dirdirinx(void)
{
    LegalDelta			deltas[25];
    register int		full;
    register int		inx;
    register rLegalDelta	tpos;
    register rLegalDelta	epos;
    register int		edir;
    register int		mvdir;

    full = 0;
    for( mvdir=MIN_D_DIR ; mvdir<MAX_D_DIR ; ++mvdir ) {
	tpos = dam_mov[mvdir];
	for( edir=MIN_D_DIR ; edir<MAX_D_DIR ; ++edir ) {
	    epos = tpos + dam_mov[edir];
	    for( inx=0 ; inx<full ; ++inx ) {
		if( epos == deltas[inx] ) {
		    break;
		}
	    }
	    if( inx == full ) {
		deltas[full++] = epos;
	    }
	    dirdirinx[mvdir][edir] = inx;
	}
    }
    if( full != 25 ) {
	panic("E25 full %d instead 25", full);
    }
    dirdirinx_inited	= 1;
}
# define CHK_DIRDIRINX()	if( !dirdirinx_inited ) m2__init_dirdirinx();
#else
# define CHK_DIRDIRINX()	/*empty*/
#endif

    static void
m2_set_defk(
    register Xconst Board*	bp,
    register M2State*		m2p )	/* OUT: 2-mate info */
{
    register rColour		att;
    register rPieceSet		attmask;
    register rEscSet		e;
    register int		v;
    register int		edir;
    register rColour		c;
    register Xconst Field*	ep;
    register Xconst Field*	tp;
    register Xconst Field*	fp;
    int				mvdir;
    int				vbest;
    EscSet			ebest;
    int				dbest;
#if WITH_E25
    register unsigned		inx;
    register uint8*		inxp;
    register ruint32		done25	= 0;
    register ruint32		block25	= 0;
    register ruint32		mask25;
    uint8			val25[25];
#endif

    CHK_DIRDIRINX()
    att = bp->b_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 ; mvdir<MAX_D_DIR ; ++mvdir ) {
	tp = fp + dam_mov[mvdir];				/* 158784 */
	c  = tp->f_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 ; edir<MAX_D_DIR ; ++edir ) {
#if WITH_E25
	    inx = *inxp++;					/* 197076 */
	    mask25 = SET1(inx);
	    if( block25 & mask25 ) {
		continue;
	    }
	    if( done25 & mask25 ) {
		e |= (1 << edir);
		v += val25[inx];
		continue;
	    }
	    ep = tp + dam_mov[edir];
	    if( ep != fp ) {
		c = ep->f_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( ; dir<dirmax ; ++dir ) {
		delta = dam_mov[dir];
		to = from;
		while(  (tp = &(bp->b_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<<dame) | (1<<springer));
	if( ! figs ) {
	    return FALSE;	/* no, cannot cover */
	}
    }

#define TEST_condF(fig)					\
	(   (figs & (1<<(fig)))				\
	 && SET_LE(escs, fig_cov[fig][to - ecent])	\
	)
#define TEST_condLTD()	\
	( ! (ebcp && IS_EBC64(ebcp, bp->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( ; dir<dirmax ; ++dir ) {
	    step = dam_mov[dir];				/* 197712 */
	    tpos = fpos;
	    tp   = fp;
#if M2_STEP1_STATS
	    scinc(sc_step1_dirs);
	    if( tp[step].f_c == border ) scinc(sc_step1_border);
#endif
	    for(;;) {
		c = (tp += step)->f_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 ; i<full ; ++i ) {
	if( SET_GE(esp[i], escbase) ) {
	    scadd(sc_m2ll_cutmax[full], full - (i+1));
	    break;
	}
	if( last ) {
	    if( last == esp[i] ) {
		scinc(sc_m2ll_cuteq[full]);
	    }else if( SET_GE(last, esp[i]) ) {
		scinc(sc_m2ll_cutcov[full]);
	    }else {
		last = esp[i];
	    }
	}else {
	    last = esp[i];
	}
    }
}
#else
# define m2__stat_cut(f,p,b)	/*empty*/
#endif


    static Bool				/* whether contains total E */
m2__make_thru(
    Xconst Board*	bp,		/* in this board */
    int8		idx,		/* through this piece */
    Position		epos,		/* happening to leave this */
    M2State*		m2p )		/* IN/OUT */
{
    int			full;
    EscSet*		ep;

    ep = &(m2p->m2_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<<ZERO_DIR)) ) {			/*  21141 */
	    register rPieceSet	atts;
	atts  = F_IATT(&(bp->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<<dame)|(1<<turm)|(1<<laeufer)) & (1 << mp->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 */
}