/*
	TODO:
			- enpassant hashing (ok)
			- u64 flags (ok)
			- include check flag + remove slow check (ok)
			- fix mate detection in eval(): king movement should be restricted by protected enemy pieces! (?ok)
			- fix a bug in movegen (AGAIN!!!) -- pawn opening a check (ok)
*/

#include "board.h"
#include "zobrist.h"
#include "bittables.h"
#include "core/core.h"
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <algorithm>

//#define CHENG_MT

//#define REAL_PIN						// real_pin opts... FIXME: BUGGY!!!

/*#define STOP_RAY(i)	\
			(i) = ((i)+7) / 7 * 7;	\
			(i)--;*/
#define STOP_RAY(i)	(i) = stopRay[i];

#define NEW_MOVEGEN						// new movgen-tables! (now mandatory!)

#ifdef CHENG_MT
Mutex evalMutex, movMutex;
#endif

#define SEE_PAWNS						// expensive SEE for pawns?!
//#define SEE_ALL						// see noncaps?! (if disabled, seem a bit better in most [not all!] cases)
//#define DETECT_HANGING					// detect hanging pieces (+add bonus?)

i32 underpromotions = 1*1;				// support underpromotions

static const i32 matTab[] = {
    0, 1000, 3250, 3250, 5000, 9750, 0, 0
};

static const i32 tmatTab[] = {
    0, 0, 3250, 3250, 5000, 9750, 0, 0
};

enum trop_scale_value {
    trop_scale = 2
};

// slider piece type flags
static const i8 diagSlider[] = { 0, 0, 0, 1, 0, 1, 0, 0 };
static const i8 updnSlider[] = { 0, 0, 0, 0, 1, 1, 0, 0 };

// king tropism table
// TODO: finetune
static const i32 tropTab[8][8] = {
    { 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 2, 1, 0, 0, 0, 0, 0 },			// pawn
    { 0, 0, 4, 3, 2, 1, 0, 0 },			// knight
    { 0, 3, 3, 2, 2, 1, 0, 0 },			// bishop
    { 0, 4, 3, 2, 1, 0, 0, 0 },			// rook
    { 0, 5, 4, 3, 2, 1, 0, 0 },			// queen
    { 0, 0, 0, 0, 0, 0, 0, 0 }			// king
};

static const i32 kingMateTab[8] = {
    0, 0, 200, 150, 100, 50, -100, -200,
};

static const i8 sqrColor[64] = {
    0, 1, 0, 1, 0, 1, 0, 1,
    1, 0, 1, 0, 1, 0, 1, 0,
    0, 1, 0, 1, 0, 1, 0, 1,
    1, 0, 1, 0, 1, 0, 1, 0,
    0, 1, 0, 1, 0, 1, 0, 1,
    1, 0, 1, 0, 1, 0, 1, 0,
    0, 1, 0, 1, 0, 1, 0, 1,
    1, 0, 1, 0, 1, 0, 1, 0
};

// kbn value table for knbk endgames where
// (values valid when mating in white corner
/*static i32 knbTable[64] = {
	+999, +500, +250, +000, -200, -250, -500, -999,
	+500, +250, +000, -100, -250, -200, -250, -500,
	+250, +000, -100, -500, -500, -250, -200, -250,
	+000, -100, -500, -999, -999, -500, -250, -200,
	-100, -250, -500, -999, -999, -500, -100, +000,
	-250, -200, -250, -500, -500, -100, +000, +250,
	-500, -250, -200, -250, -100, +000, +250, +500,
	-999, -500, -250, -100, +000, +250, +500, +999
};*/

static const i32 knbTable[64] = {
    +999, +500, +250, +100, -100, -250, -500, -999,
    +500, +400, +250, +100, -100, -250, -500, -400,
    +250, +250, +150, + 80, - 80, -150, -250, -250,
    +100, +100, + 80, + 50, - 50, - 80, -100, -100,
    -100, -100, - 80, - 50, + 50, + 80, +100, +100,
    -250, -250, -150, - 80, + 80, +150, +250, +250,
    -500, -400, -250, -100, +100, +250, +400, +500,
    -999, -500, -250, -100, +100, +250, +500, +999
};

static const i32 centerTab[ 64 ] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 1, 1, 1, 1, 0, 0,
    0, 1, 2, 2, 2, 2, 1, 0,
    0, 1, 2, 3, 3, 2, 1, 0,
    0, 1, 2, 3, 3, 2, 1, 0,
    0, 1, 2, 2, 2, 2, 1, 0,
    0, 0, 1, 1, 1, 1, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

static const i32 kingCenterTab[ 64 ] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 1, 1, 0, 0,
    0, 1, 2, 2, 2, 2, 1, 0,
    0, 1, 2, 3, 3, 2, 1, 0,
    0, 1, 2, 3, 3, 2, 1, 0,
    0, 1, 2, 2, 2, 2, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

inline i32 iabs( i32 x )
{
    return x < 0 ? -x : x;
}

u8 random64k[ 65536 ];
static u8 rndTab[ 256 ];
static u8 rndMoveTab[257][ 256 ];
static u8 skipTab[ 65536 ];
static bool skipTabInit = false;
static u8 distTab[64][64];			// each-to-each square index distance table (0..7)
static u64 kingCheckTab[64];		// king check pattern table for each position

static void initNewMovTab();

// is(piece.packed) bishop or queen of a given color
static i8 isPawn[2*256];
static i8 isKnight[2*256];
static i8 isBishopOrQueen[2*256];
static i8 isRookOrQueen[2*256];

void initSkipTab()
{
    if ( !skipTabInit )
    {
        initNewMovTab();
        i32 i, j;
        for (i=0; i<65536; i++)
        {
            i32 t = i;
            for (j=0; j<16; j++)
            {
                if ( t & 1 )
                    break;
                t >>= 1;
            }
            skipTab[ i ] = j;
        }
        srand( 0 );
        // init rndTab as well...
        for ( i=0; i<65536; i++)
        {
            random64k[i] = rand() & 255;
        }
        srand( 0 );
        for (i=0; i<256; i++)
        {
            rndTab[ i ] = i;
        }
        for ( i=0; i<512; i++)
        {
            i32 a = rand() & 255;
            i32 b = rand() & 255;
            u8 t = rndTab[ a ];
            rndTab[ a ] = rndTab[ b ];
            rndTab[ b ] = t;
        }
        for ( j=1; j<=256; j++ )
        {
            for (i=0; i<j; i++)
            {
                rndMoveTab[ j ][ i ] = i;
            }
            for ( i=0; i<j+j; i++)
            {
                i32 a = (rand() & 32767) % j;
                i32 b = (rand() & 32767) % j;
                u8 t = rndMoveTab[ j ][ a ];
                rndMoveTab[ j ][ a ] = rndMoveTab[ j ][ b ];
                rndMoveTab[ j ][ b ] = t;
            }
        }
        // side effect: init distance table!
        for (i=0; i<64; i++)
        {
            i32 j;
            i32 x0, y0;
            x0 = i & 7;
            y0 = i >> 3;
            for (j=0; j<64; j++)
            {
                i32 x1, y1;
                x1 = j & 7;
                y1 = j >> 3;
                i32 dx = iabs( x1 - x0 );
                i32 dy = iabs( y1 - y0 );
                distTab[ i ][ j ] = (dx >= dy) ? dx : dy;
            }
        }
        // side effect: init king check table
        for (i=0; i<64; i++)
        {
            u64 m = 0;
            u64 t = 0, b = (u64)1 << i;
            t |= (b << 17) & L1MASK;
            t |= (b << 15) & R1MASK;
            t |= (b >> 17) & R1MASK;
            t |= (b >> 15) & L1MASK;
            t |= (b << 10) & L2MASK;
            t |= (b << 6) & R2MASK;
            t |= (b >> 10) & R2MASK;
            t |= (b >> 6) & L2MASK;
            m |= t;
            i32 j;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= (t << 1) & L1MASK;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= t << 8;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= t >> 8;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= (t >> 1) & R1MASK;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= (t << 9) & L1MASK;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= (t << 7) & R1MASK;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= (t >> 9) & R1MASK;
            }
            m |= t;

            t = b;
            for (j=0; j<8; j++)
            {
                t |= (t >> 7) & L1MASK;
            }
            m |= t;

            kingCheckTab[ i ] = m;
        }
        skipTabInit = true;
    }
}

static i32 xiyi[256][64];
static i32 xiinit = 0;

// for debugging only!
bool Board::comparePieces( const Board &o ) const
{
    i32 i;
    for (i=0; i<64; i++)
    {
        const Piece &p = pieces[i];
        const Piece &q = o.pieces[i];
        if ( p.type != q.type )
            return false;
        if ( p.type != none && p.color != q.color )
            return false;
        if ( p.moved != q.moved )
            return false;
    }
    return flags.ep == o.flags.ep;
}

void Board::update( i32 turn )
{
    i32 i;
    dmat = tmat = 0;
    mask = 0;
    for (i=0; i<64; i++)
    {
        const Piece &p = pieces[ i ];
        if ( p.type == none )
            continue;
        mask |= oneShlTab[ i ];
        if ( p.type == king )
        {
            flags.kingPos[ p.color ] = i;
        }
        if ( p.color == white )
            dmat += matTab[ p.type ];
        else
            dmat -= matTab[ p.type ];
        tmat += tmatTab[ p.type ];
    }
    flags.check =
        check( flags.kingPos[ white ] ) ||
        check( flags.kingPos[ black ] );
    curHash = hash( turn );
}

bool Board::isDraw() const
{
    // check material...
    /*	if ( tmat > (tmatTab[ knight ]<<1) || tmat > (tmatTab[ bishop ]<<1) )
    		return false;*/
    if ( tmat > (tmatTab[ knight ]<<1) )		// FIXME: assume tmatTab[knight] == tmatTab[bishop]
        return false;
    i32 i, i_;
    i32 nc[2] = {0, 0};
    i32 bc[2] = {0, 0};
    i32 tnc = 0, tbc = 0;
    i32 bcolmask = 0;
    for (i_=0; i_<64; i_++)
    {
        i = xiinit ? xiyi[0][i_] : i_;
        const Piece &p = pieces[i];
        switch ( p.type )
        {
        case none:
            break;
        case bishop:
            if ( ++bc[ p.color ] >= 2 )
                return false;		// 2+ bishops of same color => not draw
            tbc++;
            if ( tnc )
                return false;
            {
                i32 x = i & 7;
                i32 y = i >> 3;
                bcolmask |= 1<<((x + y) & 1);
                if ( bcolmask == 3 )
                    return false;
            }
            break;
        case knight:
            if ( ++nc[ p.color ] >= 2 )
                return false;		// 2+ knights of same color => not draw
            tnc++;
            if ( tbc )
                return false;
            break;
        case king:
            break;
        default:
            return false;
        }
    }
    if ( !tbc && !tnc )
        return true;				// king against king => draw
    if ( tbc )
    {
        if ( !bc[ white ] || !bc[ black ] )
            return true;						// king + bighop against king => draw
        // two opposing bishops on same color => draw
        return bcolmask == 1 || bcolmask == 2;
    }
    if ( tnc )
    {
        return !nc[ white ] || !nc[ black ];	// king + knight against king => draw
    }
    return false;
}

/*// clear enpassant flags
void Board::clearEnpassant( Undo *undo )
{
	if ( undo && flags.ep )
	{
		undo->x = flags.ep & 7;
		undo->y = flags.ep >> 3;
		undo->packed = pieces[ flags.ep ].packed;
	}
	flags.ep = 0;
}*/

// try to perform an uci move (+parse)
// returns true on success
bool Board::ucimove( i32 turn, const char *mvtext )
{
    if ( !mvtext || !*mvtext )
        return false;
    Move m[ maxMoves ];
    i32 vm = moves( m, turn );
    if ( vm <= 0 )
        return false;		// no valid moves => failed...
    // parse move...
    if ( strlen( mvtext ) < 4 )
        return false;		// bad move length
    const char *c = mvtext;
    Move tm;
    tm.clear();
    i32 fx, fy, tx, ty;
    fx = (c[0]|32) - 'a';
    fy = '8' - c[1];
    tx = (c[2]|32) - 'a';
    ty = '8' - c[3];
    tm.from = (fy << 3) | fx;
    tm.to = (ty << 3) | tx;
    tm.promo = 0;
    if ( c[4] == 'q' )
    {
        tm.promo = queen;
    }
    if ( c[4] == 'r' )
    {
        tm.promo = rook;
    }
    if ( c[4] == 'n' )
    {
        tm.promo = knight;
    }
    if ( c[4] == 'b' )
    {
        tm.promo = bishop;
    }
    i32 i;
    for (i=0; i<vm; i++)
    {
        if ( tm.equalUCI( m[i] ) )
            break;
    }
    if ( i >= vm )
        return false;
    move( m[i] );
    return true;
}

// do move; optionally create undo buffer[ maxUndo ]
void Board::move( const Move &m, Undo *undo )
{
    if ( !skipTabInit )
        initSkipTab();

    u8 from = m.from;
    u8 to = m.to;

    flags.lastMoved = to != from ? to : 255;
    flags.check = m.dangerous && !m.promo && !m.castling && !m.capture;

    Piece &p = pieces[ from ];
    i32 turn = p.color;
    i32 type = p.type;
    Piece &dp = pieces[ to ];

////	u64 om = mask;

    // update current hash value
    bool rehash = m.castling || m.enpassant;
    if ( !rehash )
    {
        u64 fm = oneShlTab[ from ];
        mask ^= fm;
        if ( !m.capture )
        {
            u64 tm = oneShlTab[ to ];
            mask ^= tm;		// no matter if capture/std move
        }
        curHash ^= hashPiece( from );
        curHash ^= hashPiece( to );
    }

    if ( m.capture || p.type == pawn )
        flags.fiftyCounter = 0;
    else
        flags.fiftyCounter++;

    // update material difference...
    if ( m.capture )
    {
        if ( !m.enpassant )
        {
            tmat -= tmatTab[ dp.type ];
            i32 d = matTab[ dp.type ];
            if ( p.color == white )
                dmat += d;
            else
                dmat -= d;
        }
        else
        {
            // ASSERT: p.type == pawn
            i32 d = matTab[ pawn ];
            if ( p.color == white )
                dmat += d;
            else
                dmat -= d;
        }
    }
    if ( m.promo != none )
    {
        tmat += tmatTab[ m.promo ];
        i32 d = matTab[ m.promo ] - matTab[ pawn ];
        if ( p.color == white )
            dmat += d;
        else
            dmat -= d;
    }

    // update king positions
    if ( p.type == king )
    {
        flags.kingPos[ p.color ] = to;
    }

    if ( undo )
    {
        undo->pos = from;
        undo->packed = p.packed;
        undo++;
        undo->pos = to;
        undo->packed = dp.packed;
        undo++;
    }

    if ( !rehash && flags.ep )
    {
        curHash ^= (posHash)zobristEP[ flags.ep ];
    }
    flags.ep = 0;

    if ( p.type == king )
    {
        // FIXME/TODO: better!
        i32 tx = to & 7;
        i32 ty = to >> 3;
        i32 fx = from & 7;
        i32 fy = from >> 3;
        if ( iabs( tx - fx ) > 1 )
        {
            // handle castling!
            flags.castled |= 1 << p.color;
            dp.packed = p.packed;				// TODO/FIXME: this will preserve king_moved flag (0) [could be useful in eval]
            p.packed = 0;
            if ( tx < 4 )
            {
                // left
                Piece &rp = pieces[ to+1  ];
                if ( undo )
                {
                    undo->pos = to+1;
                    undo->packed = rp.packed;
                    undo++;
                }
                rp.moved = 1;
                rp = piece[ty][0];
                if ( undo )
                {
                    undo->pos = ty<<3;
                    undo->packed = piece[ty][0].packed;
                    undo++;
                }
                piece[ty][0].packed = 0;
            }
            else
            {
                // right
                Piece &rp = pieces[ to-1 ];
                if ( undo )
                {
                    undo->pos = to-1;
                    undo->packed = rp.packed;
                    undo++;
                }
                rp.moved = 1;
                rp = piece[ty][7];
                if ( undo )
                {
                    undo->pos = (ty<<3) | 7;
                    undo->packed = piece[ty][7].packed;
                    undo++;
                }
                piece[ty][7].packed = 0;
            }
        }
        else
        {
            // simple move
            dp.packed = p.packed;
            dp.moved = 1;
            p.packed = 0;
        }
    }
    else if ( p.type == pawn )
    {
        i32 ty = to >> 3;
        if ( ty == 0 || ty == 7 )					// promotion!
        {
            dp.packed = p.packed;
            dp.moved = 1;
            p.packed = 0;
            dp.type = m.promo;
        }
        else
        {
            i32 tx = to & 7;
            i32 fy = from >> 3;
            // handle enpassant capture!
            if ( dp.type == none && m.enpassant )
            {
                Piece &ep = piece[ fy ][ tx ];
                if ( undo )
                {
                    undo->pos = (fy << 3) | tx;
                    undo->packed = ep.packed;
                    undo++;
                }
                ep.packed = 0;
            }
            dp.packed = p.packed;
            if ( iabs( ty - fy ) > 1 )
            {
                flags.ep = to;
            }
            dp.moved = 1;
            p.packed = 0;
        }
    }
    else
    {
        // simple move
        dp.packed = p.packed;
        dp.moved = 1;
        p.packed = 0;
    }
    if ( undo )
        undo->pos = 255;
    /*	{
    		i32 dm = dmat;
    		i32 tm = tmat;
    		update();
    		if ( dm != dmat || tm != tmat )
    		{
    			printf( "dmat/tmat error!\n" );
    			abort();
    		}
    	}*/
    if ( !rehash )
    {
        curHash ^= hashPiece( from );
        curHash ^= hashPiece( to );

        /*		u64 ms = mask;
        		update();
        		if ( ms != mask )
        		{
        			printf( "mask error!\n" );
        			abort();
        		}*/
        /*		u64 oh = curHash;
        		u64 h = hash( turn );
        		if ( h != oh )
        		{
        			printf( "hash error!\n" );
        			abort();
        		}*/
    }
    else
    {
        // rehash
        i32 i;
        mask = 0;
        curHash = (posHash)zobristTurn[ turn ];
        for (i=0; i<64; i++)
        {
            const Piece &p = pieces[ i ];
            if ( p.type == none )
                continue;
            u8 pp = p.packed;
            curHash ^= (posHash)zobristKeys[ pp | ( i<<8 ) ];
            mask |= oneShlTab[ i ];
        }
////		curHash = hash( turn );
    }
    curHash ^= (posHash)zobristTurn[ 1 ];
    if ( flags.ep )
    {
        curHash ^= (posHash)zobristEP[ flags.ep ];
    }
#if 0
    if ( 1 /*!rehash*/ )
    {
        // validate!!!
        u64 h = curHash;
        curHash = hash( turn ^ 1 );
        if ( h != curHash )
        {
            abort();
        }
    }
#endif
    if ( !flags.check && (m.capture || m.castling || m.promo) )
    {
        // slow -- validate!
        u8 kp = flags.kingPos[ turn ^ 1 ];
        if ( m.castling || m.enpassant || m.promo )
        {
            flags.check = check( kp );
        }
        else
        {
            u64 km = kingCheckTab[ kp ];
            u64 m = oneShlTab[ to ];
            u64 m2 = oneShlTab[ from ];
            if ( (m & km) || (m2 & km) )
            {
//				bool knightPawn = (type == knight || type == pawn);
                flags.check = check( kp/*, knightPawn, !knightPawn*/ );
            }
        }
    }
}

// undo move using undo buffer
void Board::undoMove( const Undo *undo )
{
    while ( undo->pos != 255 )
    {
        pieces[ undo->pos ].packed = undo->packed;
        undo++;
    }
}

// returns position of either king
u8 Board::kingPosition( i32 turn ) const
{
#if 0
    PiecePosition pp;
    i32 i, j;
    for (i=0; i<8; i++)
    {
        for (j=0; j<8; j++)
        {
            const Piece &p = piece[ i ][ j ];
            if ( p.type == king && p.color == turn )
            {
                pp.x = j;
                pp.y = i;
                return pp;
            }
        }
    }
    // assume unreachable
    pp.x = pp.y = 8;
    return pp;
#endif
    return flags.kingPos[ turn ];
}

void Board::clear( i32 turn )
{
    reset();
    memset( pieces, 0, sizeof(pieces) );
    update( turn );
}

void Board::reset()
{
    flags.packed = 0;
    flags.lastMoved = 255;
    flags.kingPos[ white ] = (7<<3) | 4;
    flags.kingPos[ black ] = (0<<3) | 4;

    i32 i;
    for (i=0; i<64; i++)
    {
        pieces[i].packed = 0;
    }
    for (i=0; i<8; i++)
    {
        piece[0][i].color = piece[1][i].color = black;
        piece[6][i].color = piece[7][i].color = white;
        piece[1][i].type = piece[6][i].type = pawn;
    }
    // place rooks
    piece[0][0].type = piece[0][7].type = piece[7][0].type = piece[7][7].type = rook;
    // place knights
    piece[0][1].type = piece[0][6].type = piece[7][1].type = piece[7][6].type = knight;
    piece[0][1].moved = piece[0][6].moved = piece[7][1].moved = piece[7][6].moved = 1;	// hint: should help transposition table
    // place bishops
    piece[0][2].type = piece[0][5].type = piece[7][2].type = piece[7][5].type = bishop;
    piece[0][2].moved = piece[0][5].moved = piece[7][2].moved = piece[7][5].moved = 1;	// hint: should help transposition table
    // place queens
    piece[0][3].type = piece[7][3].type = queen;
    // place kings
    piece[0][4].type = piece[7][4].type = king;

    update( white );
}

static const i8 nmtab[][2] =
{
    { -1, +2 },
    { -1, -2 },
    { -2, +1 },
    { -2, -1 },
    { +1, +2 },
    { +1, -2 },
    { +2, +1 },
    { +2, -1 },
    { 127, 127 }
};

static const i8 bmtab[][2] =
{
    { +1, +1 },
    { +2, +2 },
    { +3, +3 },
    { +4, +4 },
    { +5, +5 },
    { +6, +6 },
    { +7, +7 },
    { +1, -1 },
    { +2, -2 },
    { +3, -3 },
    { +4, -4 },
    { +5, -5 },
    { +6, -6 },
    { +7, -7 },
    { -1, +1 },
    { -2, +2 },
    { -3, +3 },
    { -4, +4 },
    { -5, +5 },
    { -6, +6 },
    { -7, +7 },
    { -1, -1 },
    { -2, -2 },
    { -3, -3 },
    { -4, -4 },
    { -5, -5 },
    { -6, -6 },
    { -7, -7 },
    { 127, 127 }
};

static const i8 rmtab[][2] =
{
    { +0, +1 },
    { +0, +2 },
    { +0, +3 },
    { +0, +4 },
    { +0, +5 },
    { +0, +6 },
    { +0, +7 },
    { +0, -1 },
    { +0, -2 },
    { +0, -3 },
    { +0, -4 },
    { +0, -5 },
    { +0, -6 },
    { +0, -7 },
    { +1, +0 },
    { +2, +0 },
    { +3, +0 },
    { +4, +0 },
    { +5, +0 },
    { +6, +0 },
    { +7, +0 },
    { -1, +0 },
    { -2, +0 },
    { -3, +0 },
    { -4, +0 },
    { -5, +0 },
    { -6, +0 },
    { -7, +0 },
    { 127, 127 }
};

static const i8 qmtab[][2] =
{
    { +1, +1 },
    { +2, +2 },
    { +3, +3 },
    { +4, +4 },
    { +5, +5 },
    { +6, +6 },
    { +7, +7 },
    { +1, -1 },
    { +2, -2 },
    { +3, -3 },
    { +4, -4 },
    { +5, -5 },
    { +6, -6 },
    { +7, -7 },
    { -1, +1 },
    { -2, +2 },
    { -3, +3 },
    { -4, +4 },
    { -5, +5 },
    { -6, +6 },
    { -7, +7 },
    { -1, -1 },
    { -2, -2 },
    { -3, -3 },
    { -4, -4 },
    { -5, -5 },
    { -6, -6 },
    { -7, -7 },
    { +0, +1 },
    { +0, +2 },
    { +0, +3 },
    { +0, +4 },
    { +0, +5 },
    { +0, +6 },
    { +0, +7 },
    { +0, -1 },
    { +0, -2 },
    { +0, -3 },
    { +0, -4 },
    { +0, -5 },
    { +0, -6 },
    { +0, -7 },
    { +1, +0 },
    { +2, +0 },
    { +3, +0 },
    { +4, +0 },
    { +5, +0 },
    { +6, +0 },
    { +7, +0 },
    { -1, +0 },
    { -2, +0 },
    { -3, +0 },
    { -4, +0 },
    { -5, +0 },
    { -6, +0 },
    { -7, +0 },
    { 127, 127 }
};

static const i8 kmtab[][2] =
{
    { -1, -1 },
    { +0, -1 },
    { +1, -1 },
    { +1, +0 },
    { +1, +1 },
    { +0, +1 },
    { -1, +1 },
    { -1, +0 },
    { 127, 127 }
};

// [original][current]
MovTab qmnew[64][64];
MovTab rmnew[64][64];
MovTab bmnew[64][64];
MovTab nmnew[64][64];	// note: only next used
MovTab kmnew[64][64];	// note: only next used
u8 kprox[64][64];		// king proximity table

static i32 findSkip( i32 x, i32 y, i32 i, i32 num, const i8 mtab[][2] )
{
    while ( i < num )
    {
        STOP_RAY(i);
        i++;
        if ( i >= num )
            break;
        i32 nx = x + mtab[i][0], ny = y + mtab[i][1];
        if ( (nx & ~7) || (ny & ~7) )
        {
            continue;
        }
        return (ny << 3) + nx;
    }
    return -1;
}

static void initNewMovTab()
{
    memset( isPawn, 0, sizeof( isPawn ) );
    memset( isKnight, 0, sizeof( isKnight ) );
    memset( isBishopOrQueen, 0, sizeof( isBishopOrQueen ) );
    memset( isRookOrQueen, 0, sizeof( isRookOrQueen ) );
    {
        i32 i, j;
        for (i=0; i<2; i++)
        {
            // i = color
            Piece p;
            for (j=0; j<256; j++)
            {
                p.packed = j;
                i32 base = i << 8;
                isPawn[base + j] = p.color == i && p.type == pawn;
                isKnight[base + j] = p.color == i && p.type == knight;
                isBishopOrQueen[base + j] = p.color == i && (p.type == bishop || p.type == queen);
                isRookOrQueen[base + j] = p.color == i && (p.type == rook || p.type == queen);
            }
        }
    }

    memset( qmnew, -1, sizeof(qmnew) );
    memset( rmnew, -1, sizeof(rmnew) );
    memset( bmnew, -1, sizeof(bmnew) );
    memset( nmnew, -1, sizeof(nmnew) );
    memset( kmnew, -1, sizeof(kmnew) );
    memset( kprox, 0, sizeof(kprox) );
    i32 i;
    for (i=0; i<64; i++)
    {
        i32 x = i & 7;
        i32 y = i >> 3;
        i32 j;
        i32 nextpos, nextposj;

        for (j=0; j<64; j++)
        {
            i32 nx = j & 7;
            i32 ny = j >> 3;
            i32 dx = abs( nx - x );
            i32 dy = abs( ny - y );
            kprox[ i ][ j ] = (dx <= 1 && dy <= 1);
        }

        // queen
        for ( nextpos = i, nextposj = j=0; j<8*7; j++)
        {
            i32 nx = x + qmtab[j][0], ny = y + qmtab[j][1];
            if ( (nx & ~7) || (ny & ~7 ) )
            {
                STOP_RAY(j);
                continue;
            }
            i32 ni = (ny << 3) | nx;
            qmnew[ i ][ nextpos ].next = ni;
            qmnew[ i ][ nextpos ].skip = findSkip( x, y, nextposj, 8*7, qmtab );
            nextposj = j;
            nextpos = ni;
        }
        // rook
        for ( nextpos = i, nextposj = j=0; j<4*7; j++)
        {
            i32 nx = x + rmtab[j][0], ny = y + rmtab[j][1];
            if ( (nx & ~7) || (ny & ~7 ) )
            {
                STOP_RAY(j);
                continue;
            }
            i32 ni = (ny << 3) | nx;
            rmnew[ i ][ nextpos ].next = ni;
            rmnew[ i ][ nextpos ].skip = findSkip( x, y, nextposj, 4*7, rmtab );
            nextposj = j;
            nextpos = ni;
        }
        // bishop
        /*		bool deb = false;
        		if ( i == 51 )
        		{
        			logFmt("");
        			deb = true;
        		}*/
        for ( nextpos = i, nextposj = j=0; j<4*7; j++)
        {
            i32 nx = x + bmtab[j][0], ny = y + bmtab[j][1];
            if ( (nx & ~7) || (ny & ~7 ) )
            {
                STOP_RAY(j);
                continue;
            }
            i32 ni = (ny << 3) | nx;
            bmnew[ i ][ nextpos ].next = ni;
            bmnew[ i ][ nextpos ].skip = findSkip( x, y, nextposj, 4*7, bmtab );
            nextposj = j;
            /*			if ( deb )
            			{
            				logFmt("[%c%c][%c%c] next=%c%c skip=%c%c]\n",
            					'a' + x,
            					'8' - y,
            					'a' + (nextpos & 7),
            					'8' - (nextpos >> 3),
            					'a' + (bmnew[i][nextpos].next & 7),
            					'8' - (bmnew[i][nextpos].next >> 3),
            					'a' + (bmnew[i][nextpos].skip & 7),
            					'8' - (bmnew[i][nextpos].skip >> 3)
            				);
            			}*/
            nextpos = ni;
        }
        // knight
        for ( nextpos = i, j=0; j<8; j++)
        {
            i32 nx = x + nmtab[j][0], ny = y + nmtab[j][1];
            if ( (nx & ~7) || (ny & ~7 ) )
            {
                continue;
            }
            i32 ni = (ny << 3) | nx;
            nmnew[ i ][ nextpos ].next = ni;
            nextpos = ni;
        }
        // king
        for ( nextpos = i, j=0; j<8; j++)
        {
            i32 nx = x + kmtab[j][0], ny = y + kmtab[j][1];
            if ( (nx & ~7) || (ny & ~7 ) )
            {
                continue;
            }
            i32 ni = (ny << 3) | nx;
            kmnew[ i ][ nextpos ].next = ni;
            nextpos = ni;
        }
    }
}

i32 Board::LVA( i32 who, i32 sq ) const
{
    // first, check pawns!
    i32 x = (sq & 7);
    i32 y = (sq >> 3);
    i32 ny = who == black ? y-1 : y+1;
    if ( !(ny & ~7) )
    {
        if ( x > 0 )
        {
            const Piece &p = piece[ ny ][ x - 1 ];
            if ( p.type == pawn && p.color == who )
                return (ny << 3) | (x - 1);
        }
        if ( x < 7 )
        {
            const Piece &p = piece[ ny ][ x + 1 ];
            if ( p.type == pawn && p.color == who )
                return (ny << 3) | (x + 1);
        }
    }

    {
        // knight
        i8 cur = nmnew[ sq ][ sq ].next;
        while ( cur >= 0 )
        {
            const Piece &p = pieces[ cur ];
            if ( p.type == knight && p.color == who )
                return cur;
            cur = nmnew[ sq ][ cur ].next;
        }
    }

    // bishop/queen-check
    i32 res = -1;
    i32 piece = king;
    {
        i8 cur = bmnew[ sq ][ sq ].next;
        while ( cur >= 0 )
        {
            const Piece &p = pieces[ cur ];
            if ( p.type != none )
            {
                if ( p.color == who && ( p.type == bishop || p.type == queen ) )
                {
                    if ( p.type == bishop )
                    {
                        return cur;
                    }
                    if ( queen < piece )
                    {
                        res = cur;
                        piece = queen;
                    }
                }
                // stop_ray
                cur = bmnew[ sq ][ cur ].skip;
            }
            else
            {
                cur = bmnew[ sq ][ cur ].next;
            }
        }
    }
    // rook/queen-check
    {
        i8 cur = rmnew[ sq ][ sq ].next;
        while ( cur >= 0 )
        {
            const Piece &p = pieces[ cur ];
            if ( p.type != none )
            {
                if ( p.color == who && ( p.type == rook || p.type == queen ) )
                {
                    if ( p.type == rook )
                    {
                        return cur;
                    }
                    if ( queen < piece )
                    {
                        res = cur;
                        piece = queen;
                    }
                }
                // stop_ray
                cur = rmnew[ sq ][ cur ].skip;
            }
            else
            {
                cur = rmnew[ sq ][ cur ].next;
            }
        }
    }

    // finally: check king!
    u8 ksq = flags.kingPos[ who ];
    if ( piece >= king && kprox[ sq ][ ksq ] )
    {
        res = ksq;
    }

    return res;
}

// FIXME: this doesn't work!
i32 Board::SEE( i32 turn, i32 sq, i32 fromsq ) const
{
    static i32 cqtab[] = { 0, 2*1, 2*3, 2*3, 2*5, 2*9, 0, 0 };
    // find_least_valuable_attacker...
    i32 lva = (fromsq >= 0) ? fromsq : LVA( turn, sq );
    if ( lva < 0 )
    {
        return 0;
    }
    i32 piece = pieces[ lva ].type;
    // now: try to make capture
    Piece oldsq = pieces[ sq ];
    Piece oldlva = pieces[ lva ];
    pieces[ sq ] = pieces[ lva ];
    pieces[ lva ].type = none;
    i32 see = -SEE( turn ^ 1, sq );

    i32 res = 0;
    if ( see >= 0 )
    {
        res = cqtab[ oldsq.type ] + see;
    }

    // undo capture
    pieces[ sq ] = oldsq;
    pieces[ lva ] = oldlva;
    return res;
}

// returns capture quality in pawn units (this is commonly known as SEE)
i32 Board::captureQuality( const Move &tm ) const
{
#ifndef SEE_ALL
    if ( !tm.capture )
        return 0;			// paranoia
#endif
//	return 1;
    static i32 cqtab[] = { 0, 2*1, 2*3, 2*3, 2*5, 2*9, 0, 0 };

    Piece what = pieces[ tm.to ];
    const Piece &who = pieces[ tm.from ];

    // FIXME: MVV/LVA?!
//	return cqtab[ what.type ] - cqtab[ who.type ];
#if 0
    {
        i32 see = SEE( who.color, (tm.ty << 3) | tm.tx, (tm.fy << 3) | tm.fx );
        return see;
    }
#endif

    if ( tm.to == tm.from )
    {
        if ( who.type == king )
            return 0;
        what.type = none;
    }
    i32 c = who.color ^ 1;			// c == captured piece color
    i32 ny;

    /*	if ( who.type == pawn )
    	{
    		ny = 0;
    	}*/

//	return cqtab[ what.type ] - cqtab[ who.type ];

    i32 attpawns = 0, defpawns = 0;

    // defenders/attackers
    i32 att[ 8 ][ 8 ], def[ 8 ][ 8 ];		// att{def}[ round ][ type ]
    i32 defs = 0;			// any defenders?
    i32 atts = 0;

    memset( att, 0, sizeof(att) );
    memset( def, 0, sizeof(def) );

    // determine attackers/defenders
    i32 x, y;
    i32 dx, dy;

    i32 rmax = 2;

    /*	if ( who.type == knight && c == white && tm.tx == 5 && tm.ty == 4 )
    	{
    		logFmt("");
    	}*/
    /*	if ( who.type == knight && c == white && tm.tx == 3 && tm.ty == 4 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == bishop && c == white && tm.tx == 0 && tm.ty == 5 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == rook && c == white && tm.tx == 5 && tm.ty == 6 )
    	{
    		logFmt("");
    	}

    	if ( who.type == bishop && c == white && tm.tx == 2 && tm.ty == 5 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == rook && c == white && tm.tx == 4 && tm.ty == 5 )
    	{
    		logFmt("");
    	}

    	if ( who.type == rook && c == white && tm.tx == 6 && tm.ty == 5 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == rook && c == white && tm.tx == 3 && tm.ty == 2 )
    	{
    		logFmt("");
    	}*/

    // attacking pawns
    dy = c == white ? -1 : +1;
    i32 tmty = tm.to >> 3;
    i32 tmtx = tm.to & 7;
    i32 tmfy = tm.from >> 3;
    i32 tmfx = tm.from & 7;
    ny = tmty + dy;
    if ( !(ny & ~7) )
    {
        if ( tmtx > 0 )
        {
            dx = -1;
            const Piece &p = piece[ ny ][ tmtx - 1 ];
            if ( p.type == pawn && p.color != c && &p != &what && &p != &who )
            {
                // now trace hidden attackers...
                x = tmtx - 1 + dx;
                y = ny + dy;

                i32 i, r = 1;			// r = "round"
                if ( x != tmfx || y != tmfy )
                {
                    att[ 0 ][ pawn ]++;
                    atts++;
                }
                else r--;
                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
        if ( tmtx < 7 )
        {
            dx = +1;
            const Piece &p = piece[ ny ][ tmtx + 1 ];
            if ( p.type == pawn && p.color != c && &p != &what && &p != &who )
            {
                // now trace hidden attackers...
                x = tmtx + 1 + dx;
                y = ny + dy;

                i32 i, r = 1;			// r = "round"
                if ( x != tmfx || y != tmfy )
                {
                    att[ 0 ][ pawn ]++;
                    atts++;
                }
                else r--;

                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
    }

    // defending pawns
    dy = c == white ? +1 : -1;
    ny = tmty + dy;
    if ( !(ny & ~7) )
    {
        if ( tmtx > 0 )
        {
            dx = -1;
            const Piece &p = piece[ ny ][ tmtx - 1 ];
            if ( p.type == pawn && p.color == c && &p != &what && &p != &who )
            {
                // now trace hidden attackers/defenders...
                x = tmtx - 1 + dx;
                y = ny + dy;

////				if ( x != tm.fx || y != tm.fy )
                def[ 0 ][ pawn ]++;
                defs++;

                i32 i, r = 1;			// r = "round"
                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
        if ( tmtx < 7 )
        {
            dx = +1;
            const Piece &p = piece[ ny ][ tmtx + 1 ];
            if ( p.type == pawn && p.color == c && &p != &what && &p != &who )
            {
                // now trace hidden attackers...
                x = tmtx + 1 + dx;
                y = ny + dy;

////				if ( x != tm.fx || y != tm.fy )
                def[ 0 ][ pawn ]++;
                defs++;

                i32 i, r = 1;			// r = "round"
                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
    }

#if 0
    if ( def[0][pawn] && who.type > pawn  )
    {
        i32 tmp = cqtab[ what.type ] - cqtab[ who.type ]/* + cqtab[ pawn ]*/;
        if ( tmp < 0 )
            return tmp;
    }
#endif

    // now: knights! (round 0 only...)
    i32 i;
    for (i=0; i<8; i++)
    {
        i32 nx = tmtx + nmtab[i][0], ny = tmty + nmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
            continue;
        if ( nx == tmfx && ny == tmfy )
            continue;			// don't include move_from_piece!
        const Piece &p = piece[ ny ][ nx ];
        if (  &p == &what || &p == &who )
            continue;
        if ( p.type == knight )
        {
            if ( p.color == c )
            {
                def[ 0 ][ knight ]++;
                defs++;
            }
            else
            {
                att[ 0 ][ knight ]++;
                atts++;
            }
        }
    }
    i32 ra = 0;					// round
    i32 rd = 0;
    // rook/queen check
    for (i=0; i<4*7; i++)
    {
        i32 nx = tmtx + rmtab[i][0], ny = tmty + rmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none || &p == &what || &p == &who )
            continue;
        if ( !updnSlider[ p.type ] )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        if ( p.color != c )
        {
            if ( nx != tmfx || ny != tmfy )
            {
                att[ ra ][ p.type ]++;
                atts++;
            }
            if ( ++ra > rmax )
                rmax = ra;
        }
        else
        {
            def[ rd ][ p.type ]++;
            defs++;
            if ( ++rd > rmax )
                rmax = rd;
        }
    }
    ra = rd = 0;					// round
    // bishop/queen check
    for (i=0; i<4*7; i++)
    {
        i32 nx = tmtx + bmtab[i][0], ny = tmty + bmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none || &p == &what || &p == &who )
            continue;
        if ( !diagSlider[ p.type ] )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        if ( p.color != c )
        {
            if ( nx != tmfx || ny != tmfy )
            {
                att[ ra ][ p.type ]++;
                atts++;
            }
            if ( ++ra > rmax )
                rmax = ra;
        }
        else
        {
            def[ rd ][ p.type ]++;
            defs++;
            if ( ++rd > rmax )
                rmax = rd;
        }
    }

    // and finally: calculate score!
    i32 score = cqtab[ /*what.type ?*/ what.type /*: who.type*/ ];			// initially the score is a simple win
////	i32 score = cqtab[ what.type ? what.type : who.type ];			// initially the score is a simple win
    i32 r;
    i32 value = what.type ? score : cqtab[ who.type ];

    bool defended = false;
    if ( defs )
    {
        /*		if ( !what.type )
        			score = 1;						// FIXME: hack but necessary...*/

        value = cqtab[ who.type ];			// value of capturing piece

        i8 action = 0;							// 0 = defending, 1 = attacking
        for (r=0; r<8; r++)
        {
            bool anydefs = false;				// any defenders this round?
            bool anyatts = false;
            bool repeat = true;
            i32 bestscore = -0x7fffffff, bestvalue = -0x7fffffff;
            i32 defbeg = pawn, attbeg = pawn;
            while ( repeat )
            {
                repeat = false;
                anydefs = false;
                anyatts = false;
                i32 oscore = score;
                if ( action == 0 )
                {
                    if ( defs && (score > 0 || (r == 0 && !what.type)) )
                    {
                        for (i=defbeg; i < king; i++)
                        {
                            if ( !def[ r ][ i ] )
                                continue;
                            // defending!
                            anydefs = true;
                            def[ r ][ i ]--;
                            defs--;
                            if ( cqtab[ i ] < value )
                                defended = true;
                            score -= value;
                            value = -cqtab[ i ];
                            action = 1;
                            repeat = true;
                            if ( /*score >= 0 &&*/ score < oscore /*score > bestscore*/ )
                            {
                                bestscore = score;
                                bestvalue = value;
                            }
                            /*							else if ( bestscore != -0x7fffffff )
                            							{
                            								score = bestscore;
                            								value = bestvalue;
                            							}*/
                            break;
                        }
                        defbeg = i;
                    }
                }
                if ( action == 1 )
                {
                    oscore = score;
                    if ( atts && score < 0 )
                    {
                        for (i=attbeg; i < king; i++)
                        {
                            if ( !att[ r ][ i ] )
                                continue;
                            // attacking!
                            anyatts = true;
                            att[ r ][ i ]--;
                            atts--;
                            score -= value;
                            value = +cqtab[ i ];
                            action = 0;
                            repeat = true;
                            if ( /*score <= 0 &&*/ score > oscore /*score > bestscore*/ )
                            {
                                bestscore = score;
                                bestvalue = value;
                            }
                            /*							else if ( bestscore != -0x7fffffff )
                            							{
                            								score = bestscore;
                            								value = bestvalue;
                            							}*/
                            break;
                        }
                        attbeg = i;
                    }
                }
                /*				if ( defended )
                					break;*/
            }
            if ( bestscore != -0x7fffffff )
            {
                score = bestscore;
                value = bestvalue;
            }
            if ( /*defended || */(!defs && !atts) || (action == 1 && !atts) || (action == 0 && !defs) )
                break;
            /*			if ( !anydefs && !anyatts )
            			{
            				if ( action == 1 )
            				{
            					// continue next round
            				}
            				else
            				{
            					// finished => return score
            					break;
            					////				return score;
            				}
            			}
            			else
            			{
            				// continue next round
            			}*/
        }
    }
    if ( what.type == none )
    {
////		score &= ~1;		// FIXME: hack
        if ( score >= 0 && defended )
        {
            score = -cqtab[ who.type ];		// FIXME: nasty hack...
        }
        if ( score > 0)
            score = 0;
    }
    if ( score >= 0 && !atts )
    {
        // last thing to do: check if it can be recaptured by enemy king
        u8 ekp = flags.kingPos[ c ];
        if ( !kprox[ tm.to ][ ekp ] )
            goto noenemy;
        u8 kp = flags.kingPos[ c ^ 1 ];
        if ( kprox[ tm.to ][ kp ] )
            goto noenemy;
        /*	{
        i32 r;
        for ( r=0; r<8; r++)
        {
        i32 i;
        for (i=pawn; i < king; i++)
        {
        if ( att[ r ][ i ] )
        return score;
        }
        }
        }*/
        // yes it can be recaptured => adjust score!
        score -= cqtab[ who.type ];
    }
noenemy:
    if ( score <= 0 && !defs )
    {
        // last thing to do: check if it can be recaptured by our king
        u8 kp = flags.kingPos[ c ^ 1 ];
        if ( !kprox[ kp ][ tm.to ] )
            return score;
        u8 ekp = flags.kingPos[ c ];
        if ( kprox[ tm.to ][ ekp ] )
            return score;
        /*	{
        i32 r;
        for ( r=0; r<8; r++)
        {
        i32 i;
        for (i=pawn; i < king; i++)
        {
        if ( att[ r ][ i ] )
        return score;
        }
        }
        }*/
        // yes it can be recaptured => adjust score!
        score += abs( value )/*cqtab[ who.type ]*/;
    }
    return score;
}

// returns capture quality in pawn units (this is commonly known as SEE)
i32 Board::captureQuality( const PMove &tm ) const
{
//	return 1;
    static i32 cqtab[] = { 0, 2*1, 2*3, 2*3, 2*5, 2*9, 0, 0 };

    Piece what = pieces[ tm.to ];
    const Piece &who = pieces[ tm.from ];
    if ( tm.to == tm.from )
    {
        if ( who.type == king )
            return 0;
        what.type = none;
    }
    i32 c = who.color ^ 1;			// c == captured piece color
    i32 ny;

    /*	if ( who.type == pawn )
    	{
    		ny = 0;
    	}*/

//	return cqtab[ what.type ] - cqtab[ who.type ];

    i32 attpawns = 0, defpawns = 0;

    // defenders/attackers
    i32 att[ 8 ][ 8 ], def[ 8 ][ 8 ];		// att{def}[ round ][ type ]
    i32 defs = 0;			// any defenders?
    i32 atts = 0;

    memset( att, 0, sizeof(att) );
    memset( def, 0, sizeof(def) );

    // determine attackers/defenders
    i32 x, y;
    i32 dx, dy;

    i32 rmax = 2;

    /*	if ( who.type == knight && c == white && tm.tx == 5 && tm.ty == 4 )
    	{
    		logFmt("");
    	}*/
    /*	if ( who.type == knight && c == white && tm.tx == 3 && tm.ty == 4 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == bishop && c == white && tm.tx == 0 && tm.ty == 5 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == rook && c == white && tm.tx == 5 && tm.ty == 6 )
    	{
    		logFmt("");
    	}

    	if ( who.type == bishop && c == white && tm.tx == 2 && tm.ty == 5 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == rook && c == white && tm.tx == 4 && tm.ty == 5 )
    	{
    		logFmt("");
    	}

    	if ( who.type == rook && c == white && tm.tx == 6 && tm.ty == 5 )
    	{
    		logFmt("");
    	}*/

    /*	if ( who.type == rook && c == white && tm.tx == 3 && tm.ty == 2 )
    	{
    		logFmt("");
    	}*/

    i8 tm_fx = tm.from & 7;
    i8 tm_fy = tm.from >> 3;
    i8 tm_tx = tm.to & 7;
    i8 tm_ty = tm.to >> 3;

    // attacking pawns
    dy = c == white ? -1 : +1;
    ny = tm_ty + dy;
    if ( !(ny & ~7) )
    {
        if ( tm_tx > 0 )
        {
            dx = -1;
            const Piece &p = piece[ ny ][ tm_tx - 1 ];
            if ( p.type == pawn && p.color != c && &p != &what && &p != &who )
            {
                // now trace hidden attackers...
                x = tm_tx - 1 + dx;
                y = ny + dy;

                i32 i, r = 1;			// r = "round"
                if ( x != tm_fx || y != tm_fy )
                {
                    att[ 0 ][ pawn ]++;
                    atts++;
                }
                else r--;
                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
        if ( tm_tx < 7 )
        {
            dx = +1;
            const Piece &p = piece[ ny ][ tm_tx + 1 ];
            if ( p.type == pawn && p.color != c && &p != &what && &p != &who )
            {
                // now trace hidden attackers...
                x = tm_tx + 1 + dx;
                y = ny + dy;

                i32 i, r = 1;			// r = "round"
                if ( x != tm_fx || y != tm_fy )
                {
                    att[ 0 ][ pawn ]++;
                    atts++;
                }
                else r--;

                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
    }

    // defending pawns
    dy = c == white ? +1 : -1;
    ny = tm_ty + dy;
    if ( !(ny & ~7) )
    {
        if ( tm_tx > 0 )
        {
            dx = -1;
            const Piece &p = piece[ ny ][ tm_tx - 1 ];
            if ( p.type == pawn && p.color == c && &p != &what && &p != &who )
            {
                // now trace hidden attackers/defenders...
                x = tm_tx - 1 + dx;
                y = ny + dy;

////				if ( x != tm_fx || y != tm_fy )
                def[ 0 ][ pawn ]++;
                defs++;

                i32 i, r = 1;			// r = "round"
                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
        if ( tm_tx < 7 )
        {
            dx = +1;
            const Piece &p = piece[ ny ][ tm_tx + 1 ];
            if ( p.type == pawn && p.color == c && &p != &what && &p != &who )
            {
                // now trace hidden attackers...
                x = tm_tx + 1 + dx;
                y = ny + dy;

////				if ( x != tm_fx || y != tm_fy )
                def[ 0 ][ pawn ]++;
                defs++;

                i32 i, r = 1;			// r = "round"
                for (i=0; i<7; i++)
                {
                    if ( (x & ~7) || (y & ~7) )
                        break;
                    const Piece &tp = piece[ y ][ x ];
                    if ( tp.type != none )
                    {
                        if ( !diagSlider[ tp.type ] )
                            break;					// not a diagonal slider => break
                        if ( tp.color == c )
                        {
                            def[ r ][ tp.type ]++;
                            defs++;
                        }
                        else
                        {
                            att[ r ][ tp.type ]++;
                            atts++;
                        }
                        if ( ++r > rmax )
                            rmax = r;
                    }
                    x += dx;
                    y += dy;
                }
            }
        }
    }

#if 0
    if ( def[0][pawn] && who.type > pawn  )
    {
        i32 tmp = cqtab[ what.type ] - cqtab[ who.type ]/* + cqtab[ pawn ]*/;
        if ( tmp < 0 )
            return tmp;
    }
#endif

    // now: knights! (round 0 only...)
    i32 i;
    for (i=0; i<8; i++)
    {
        i32 nx = tm_tx + nmtab[i][0], ny = tm_ty + nmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
            continue;
        if ( nx == tm_fx && ny == tm_fy )
            continue;			// don't include move_from_piece!
        const Piece &p = piece[ ny ][ nx ];
        if (  &p == &what || &p == &who )
            continue;
        if ( p.type == knight )
        {
            if ( p.color == c )
            {
                def[ 0 ][ knight ]++;
                defs++;
            }
            else
            {
                att[ 0 ][ knight ]++;
                atts++;
            }
        }
    }
    i32 ra = 0;					// round
    i32 rd = 0;
    // rook/queen check
    for (i=0; i<4*7; i++)
    {
        i32 nx = tm_tx + rmtab[i][0], ny = tm_ty + rmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none || &p == &what || &p == &who )
            continue;
        if ( !updnSlider[ p.type ] )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        if ( p.color != c )
        {
            if ( nx != tm_fx || ny != tm_fy )
            {
                att[ ra ][ p.type ]++;
                atts++;
            }
            if ( ++ra > rmax )
                rmax = ra;
        }
        else
        {
            def[ rd ][ p.type ]++;
            defs++;
            if ( ++rd > rmax )
                rmax = rd;
        }
    }
    ra = rd = 0;					// round
    // bishop/queen check
    for (i=0; i<4*7; i++)
    {
        i32 nx = tm_tx + bmtab[i][0], ny = tm_ty + bmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none || &p == &what || &p == &who )
            continue;
        if ( !diagSlider[ p.type ] )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        if ( p.color != c )
        {
            if ( nx != tm_fx || ny != tm_fy )
            {
                att[ ra ][ p.type ]++;
                atts++;
            }
            if ( ++ra > rmax )
                rmax = ra;
        }
        else
        {
            def[ rd ][ p.type ]++;
            defs++;
            if ( ++rd > rmax )
                rmax = rd;
        }
    }

    // and finally: calculate score!
    i32 score = cqtab[ /*what.type ?*/ what.type /*: who.type*/ ];			// initially the score is a simple win
////	i32 score = cqtab[ what.type ? what.type : who.type ];			// initially the score is a simple win
    i32 r;
    i32 value = what.type ? score : cqtab[ who.type ];

    bool defended = false;
    if ( defs )
    {
        /*		if ( !what.type )
        			score = 1;						// FIXME: hack but necessary...*/

        value = cqtab[ who.type ];			// value of capturing piece

        i8 action = 0;							// 0 = defending, 1 = attacking
        for (r=0; r<8; r++)
        {
            bool anydefs = false;				// any defenders this round?
            bool anyatts = false;
            bool repeat = true;
            i32 bestscore = -0x7fffffff, bestvalue = -0x7fffffff;
            i32 defbeg = pawn, attbeg = pawn;
            while ( repeat )
            {
                repeat = false;
                anydefs = false;
                anyatts = false;
                i32 oscore = score;
                if ( action == 0 )
                {
                    if ( defs && (score > 0 || (r == 0 && !what.type)) )
                    {
                        for (i=defbeg; i < king; i++)
                        {
                            if ( !def[ r ][ i ] )
                                continue;
                            // defending!
                            anydefs = true;
                            def[ r ][ i ]--;
                            defs--;
                            if ( cqtab[ i ] < value )
                                defended = true;
                            score -= value;
                            value = -cqtab[ i ];
                            action = 1;
                            repeat = true;
                            if ( /*score >= 0 &&*/ score < oscore /*score > bestscore*/ )
                            {
                                bestscore = score;
                                bestvalue = value;
                            }
                            /*							else if ( bestscore != -0x7fffffff )
                            							{
                            								score = bestscore;
                            								value = bestvalue;
                            							}*/
                            break;
                        }
                        defbeg = i;
                    }
                }
                if ( action == 1 )
                {
                    oscore = score;
                    if ( atts && score < 0 )
                    {
                        for (i=attbeg; i < king; i++)
                        {
                            if ( !att[ r ][ i ] )
                                continue;
                            // attacking!
                            anyatts = true;
                            att[ r ][ i ]--;
                            atts--;
                            score -= value;
                            value = +cqtab[ i ];
                            action = 0;
                            repeat = true;
                            if ( /*score <= 0 &&*/ score > oscore /*score > bestscore*/ )
                            {
                                bestscore = score;
                                bestvalue = value;
                            }
                            /*							else if ( bestscore != -0x7fffffff )
                            							{
                            								score = bestscore;
                            								value = bestvalue;
                            							}*/
                            break;
                        }
                        attbeg = i;
                    }
                }
                /*				if ( defended )
                					break;*/
            }
            if ( bestscore != -0x7fffffff )
            {
                score = bestscore;
                value = bestvalue;
            }
            if ( /*defended || */(!defs && !atts) || (action == 1 && !atts) || (action == 0 && !defs) )
                break;
            /*			if ( !anydefs && !anyatts )
            			{
            				if ( action == 1 )
            				{
            					// continue next round
            				}
            				else
            				{
            					// finished => return score
            					break;
            					////				return score;
            				}
            			}
            			else
            			{
            				// continue next round
            			}*/
        }
    }
    if ( what.type == none )
    {
////		score &= ~1;		// FIXME: hack
        if ( score >= 0 && defended )
        {
            score = -cqtab[ who.type ];		// FIXME: nasty hack...
        }
        if ( score > 0)
            score = 0;
    }
    if ( score >= 0 && !atts )
    {
        // last thing to do: check if it can be recaptured by enemy king
        u8 ekp = flags.kingPos[ c ];
        if ( !kprox[ ekp ][ tm.to ] )
            goto noenemy;
        u8 kp = flags.kingPos[ c ^ 1 ];
        if ( kprox[ tm.to ][ kp ] )
            goto noenemy;
        /*	{
        i32 r;
        for ( r=0; r<8; r++)
        {
        i32 i;
        for (i=pawn; i < king; i++)
        {
        if ( att[ r ][ i ] )
        return score;
        }
        }
        }*/
        // yes it can be recaptured => adjust score!
        score -= cqtab[ who.type ];
    }
noenemy:
    if ( score <= 0 && !defs )
    {
        // last thing to do: check if it can be recaptured by our king
        u8 kp = flags.kingPos[ c ^ 1 ];
        if ( !kprox[ kp ][ tm.to ] )
            return score;
        u8 ekp = flags.kingPos[ c ];
        if ( kprox[ ekp ][ tm.to ] )
            return score;
        /*	{
        i32 r;
        for ( r=0; r<8; r++)
        {
        i32 i;
        for (i=pawn; i < king; i++)
        {
        if ( att[ r ][ i ] )
        return score;
        }
        }
        }*/
        // yes it can be recaptured => adjust score!
        score += abs( value )/*cqtab[ who.type ]*/;
    }
    return score;
}

// returns true if piece/square at [x,y] attacked by enemy; optionally don't checks knights and pawns
bool Board::attacked( u8 pos, i32 turn, bool checkKnightsPawns, bool distant ) const
{
    if ( !skipTabInit )
        initSkipTab();
////	u64 m = (u64)1 << (( y << 3) | x);
    i32 i;
    i32 base = (turn^1) << 8;
    if ( checkKnightsPawns )
    {
        i32 y = (pos >> 3);
        i32 x = pos & 7;
        // pawn-check
        i32 ny = turn == white ? y-1 : y+1;
        if ( !(ny & ~7) )
        {
            if ( x > 0 )
            {
                const Piece &p = piece[ ny ][ x - 1 ];
                if ( isPawn[ base + p.packed ] /*p.type == pawn && p.color != turn*/ )
                    return true;
            }
            if ( x < 7 )
            {
                const Piece &p = piece[ ny ][ x + 1 ];
                if ( isPawn[ base + p.packed ]/*p.type == pawn && p.color != turn*/ )
                    return true;
            }
        }
        // knight-check
#ifdef NEW_MOVEGEN
        {
            i8 cur = nmnew[ pos ][ pos ].next;
            while ( cur >= 0 )
            {
                const Piece &p = pieces[ cur ];
                if ( isKnight[ base + p.packed ] /*p.type == knight && p.color != turn*/ )
                    return true;
                cur = nmnew[ pos ][ cur ].next;
            }
        }
#else
        for (i=0; i<8; i++)
        {
            i32 nx = x + nmtab[i][0], ny = y + nmtab[i][1];
            if ( (nx & ~7) || (ny & ~7 ) )
                continue;
            const Piece &p = piece[ ny ][ nx ];
            if ( p.type == knight && p.color != turn )
                return true;
        }
#endif
    }
    if ( !distant )
        return false;
#ifdef NEW_MOVEGEN
//	bool newres = false;
    // bishop/queen-check
    {
        i8 cur = bmnew[ pos ][ pos ].next;
        while ( cur >= 0 )
        {
            const Piece &p = pieces[ cur ];
            if ( p.type != none )
            {
                if ( isBishopOrQueen[ base + p.packed ] /*p.color != turn && ( p.type == bishop || p.type == queen )*/ )
                {
//					newres = true;
//					break;
                    return true;
                }
                // stop_ray
                cur = bmnew[ pos ][ cur ].skip;
            }
            else
            {
                cur = bmnew[ pos ][ cur ].next;
            }
        }
    }
    // rook/queen-check
//	if ( !newres )
    {
        i8 cur = rmnew[ pos ][ pos ].next;
        while ( cur >= 0 )
        {
            const Piece &p = pieces[ cur ];
            if ( p.type != none )
            {
                if ( isRookOrQueen[ base + p.packed ]/*p.color != turn && ( p.type == rook || p.type == queen )*/ )
                {
//					newres = true;
//					break;
                    return true;
                }
                // stop_ray
                cur = rmnew[ pos ][ cur ].skip;
            }
            else
            {
                cur = rmnew[ pos ][ cur ].next;
            }
        }
    }
#else
//	bool oldres = false;
    // bishop/queen-check
    for (i=0; i<4*7; i++)
    {
        i32 nx = x + bmtab[i][0], ny = y + bmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none )
            continue;
        if ( p.color != turn && ( p.type == bishop || p.type == queen ) )
        {
            /*			oldres = true;
            			break;*/
            return true;
        }
        // stop ray in one direction
        STOP_RAY(i);
    }
    // rook/queen-check
    for (i=0; i<4*7; i++)
    {
        i32 nx = x + rmtab[i][0], ny = y + rmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none )
            continue;
        if ( p.color != turn && ( p.type == rook || p.type == queen ) )
        {
            /*			oldres = true;
            			break;*/
            return true;
        }
        // stop ray in one direction
        STOP_RAY(i);
    }
    /*	if ( oldres != newres )
    	{
    		// BUG_FOUND!!!
    		this->dump();
    		//		return attacked( x | (y<<3), turn, checkKnightsPawns, distant );
    	}*/
#endif
    // new: enemy king check! almost forgot!
    u8 pp = flags.kingPos[ turn ^ 1 ];
#if 1
    return kprox[ pos ][ pp ];
#else
    i32 dx = abs( (i32)pp.x - x );
    if ( dx > 1 )
        return false;
    i32 dy = abs( (i32)pp.y - y );
    return dy <= 1;	//return false;
#endif
}

// returns true if king at kx, ky in check; optionally don't checks knights and pawns
bool Board::check( u8 pos, bool checkKnightsPawns, bool distant ) const
{
    i32 turn = pieces[ pos ].color;
    return attacked( pos, turn, checkKnightsPawns, distant );
}

// returns potential pin mask (because of movegen)
u64 Board::potPins( u8 kp ) const
{
    i32 turn = pieces[ kp ].color;
#if 1
//	u64 newmsk;
    {
        // NEW! potPins: turn doesn't matter!
        // works but no speed gain :(
        // values 0-8: l, ul, u, ur, r, dr, d, dl, none
        u64 msk = 0;
        i32 i;
        for (i=0; i<8; i++)
        {
            u64 dm = diagMask[i][ kp ] & mask;
			if ( dm )
			{
				i8 bit = (i<4) ? getMSB(dm) : getLSB(dm);
                if ( pieces[ bit ].color == turn )
                {
                    const u64 pmsk = oneShlTab[ bit ];
                    dm &= ~pmsk;
					if ( dm )
					{
						i8 bit2 = (i<4) ? getMSB(dm) : getLSB(dm);
                        const Piece &pc = pieces[ bit2 ];
                        if ( pc.color != turn )
                        {
                            // now handle type!
                            switch ( pc.type )
                            {
                            case bishop:
                                if ( i & 1 )
                                {
                                    // diag only
                                    msk |= pmsk;
                                }
                                break;
                            case rook:
                                if ( !(i & 1) )
                                {
                                    // hv only
                                    msk |= pmsk;
                                }
                                break;
                            case queen:
                                msk |= pmsk;
                                break;
                            }
////							msk |= pmsk;
                        }
                    }
                }
            }
        }
        return msk;
//		newmsk = msk;
    }
#else
    i32 i;
    u64 msk = 0;

    i32 rayhit = 0;			// ray hit friendly piece?
    i8 raysh = -1;
#if 0
    i8 pos = (ky<<3)+kx;
    i8 cur = qmnew[pos][pos].next;
    while ( cur >= 0 )
    {
        i32 nx = kx + qmtab[i][0], ny = ky + qmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            rayhit = 0;
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none )
            continue;
        if ( p.color == turn )
        {
            if ( ++rayhit == 1 )
            {
                raysh = (ny << 3) | nx;
                continue;		// ray hit but continue!
            }
        }
        else
        {
            // enemy piece hit
            if ( p.type == queen || (p.type == rook && (ny == ky || nx == kx)) ||
                    (p.type == bishop && (ny != ky && nx != kx)) )
            {
                if ( rayhit )
                {
                    msk |= oneShlTab[ raysh ];
                }
                else
                {
                    if ( incheck )
                        *incheck = true;
                }
            }
        }
        // stop ray in one direction
        STOP_RAY(i);
        rayhit = 0;
    }
#else
    static u8 resetTbl[8*7] =
    {
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0,
        1, 0, 0, 0, 0, 0, 0
    };
    for (i=0; i<8*7; i++)
    {
        /*		switch(i)
        		{
        		case 0*7:
        		case 1*7:
        		case 2*7:
        		case 3*7:
        		case 4*7:
        		case 5*7:
        		case 6*7:
        		case 7*7:
        		case 8*7:
        			rayhit = 0;			// IMPORTANT BUGFIX!
        			break;
        		}*/
        if ( resetTbl[i] )
            rayhit = 0;
        i32 nx = kx + qmtab[i][0], ny = ky + qmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            rayhit = 0;
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none )
            continue;
        if ( p.color == turn )
        {
            if ( ++rayhit == 1 )
            {
                raysh = (ny << 3) | nx;
                continue;		// ray hit but continue!
            }
        }
        else
        {
            // enemy piece hit
            if ( p.type == queen || (p.type == rook && (ny == ky || nx == kx)) ||
                    (p.type == bishop && (ny != ky && nx != kx)) )
            {
                if ( rayhit )
                {
                    msk |= oneShlTab[ raysh ];
                }
            }
        }
        // stop ray in one direction
        STOP_RAY(i);
        rayhit = 0;
    }
#endif
#if 0
    if ( msk != newmsk )
    {
        u64 tmsk = 0;
        i32 i;
        for (i=0; i<64; i++)
        {
            if ( pieces[i].type != none )
            {
                tmsk |= oneShlTab[i];
            }
        }
        if ( tmsk != mask )
        {
            logFmt("mask bug found!\n");
        }
        String st;
        this->dump( &st );
        logFmt("board:\n%s\n", st.ansi());
        logFmt("potpins bug found!\n");
    }
#endif
    return msk;
#endif
}

// returns potential check mask (because of movegen)
u64 Board::potChecks( u8 pos, u64 *rq, u64 *bq ) const
{
    i32 turn = pieces[ pos ].color;
    i32 i;
    u64 msk = 0;
    if ( rq )
        *rq = 0;
    if ( bq )
        *bq = 0;

#ifdef NEW_MOVEGEN
    i8 cur = rmnew[ pos ][ pos ].next;
    while ( cur >= 0 )
    {
        const Piece &p = pieces[ cur ];
        if ( p.type == none || p.color != turn )
        {
            u64 v = oneShlTab[ cur ];
            msk |= v;
            if ( rq )
            {
                *rq |= v;
            }
            if ( p.type == none )
            {
                cur = rmnew[ pos ][ cur ].next;
                continue;
            }
        }
        // stop ray in one direction
        cur = rmnew[ pos ][ cur ].skip;
    }
    cur = bmnew[ pos ][ pos ].next;
    while ( cur >= 0 )
    {
        const Piece &p = pieces[ cur ];
        if ( p.type == none || p.color != turn )
        {
            u64 v = oneShlTab[ cur ];
            msk |= v;
            if ( bq )
            {
                *bq |= v;
            }
            if ( p.type == none )
            {
                cur = bmnew[ pos ][ cur ].next;
                continue;
            }
        }
        // stop ray in one direction
        cur = bmnew[ pos ][ cur ].skip;
    }
#else
    for (i=0; i<8*7; i++)
    {
        i32 nx = kx + qmtab[i][0], ny = ky + qmtab[i][1];
        if ( (nx & ~7) || (ny & ~7 ) )
        {
            // stop ray in one direction
            STOP_RAY(i);
            continue;
        }
        const Piece &p = piece[ ny ][ nx ];
        if ( p.type == none || p.color != turn )
        {
            u64 v = oneShlTab[ ( (ny<<3) | nx) ];
            msk |= v;
            if ( bq && i < 4*7 )
            {
                *bq |= v;
            }
            if ( rq && i >= 4*7 )
            {
                *rq |= v;
            }
            if ( p.type == none )
                continue;
        }
        // stop ray in one direction
        STOP_RAY(i);
    }
#endif
    return msk;
}

// capture quality table (for move ordering)
static const i32 capQualTab[] =
{
    0, 1, 2, 2, 3, 4, 0, 0
};

bool tryPawnMove( Move *&m, const Board &b, i32 turn, i32 x, i32 y, i32 nx, i32 ny, u8 kp,
                  u8 ekp, bool checked, bool pin, u64 chkMsk, bool under )
{
    bool res = false;
    if ( (nx & ~7) || (ny & ~7) )
        return res;
    const Piece &p = b.piece[ ny ][ nx ];
    if ( p.type != none && p.color == turn )
        return res;		// avoid self-capures

    // last thing to check for: avoid check

    /*	i32 dx = x - kp.x;
    	i32 dy = y - kp.y;*/

#ifdef REAL_PIN
    if ( pin /*&& p.type == none*/ )
    {
        i32 dx = iabs( nx - kp.x );
        i32 dy = iabs( ny - kp.y );
        if ( !(dx == dy || (dx && dy)) )
        {
            return res;					// new: avoid (real pin)
        }
    }
#endif

    if ( checked || pin /*!dx || !dy || iabs(dx) == iabs(dy)*/ )
    {
        i32 ex, ey;
        Piece p0, p1, p2;
        p0 = b.piece[y][x];
        p1 = b.piece[ny][nx];

        bool enp = ( p1.type == none && iabs(x - nx) > 0 );

        if ( !enp )
        {
            u64 m = oneShlTab[ ((ny << 3) | nx) ];
            if ( !(m & kingCheckTab[ kp ]) )
                return res;
        }

        if (enp)
        {
            ex = nx;
            ey = y;
            p2 = b.piece[ ey ][ ex ];
            b.piece[ ey ][ ex ].packed = 0;
        }

        b.piece[ny][nx] = p0;
        b.piece[y][x].packed = 0;

        // check
        bool bad = b.check( kp, checked );

        // restore
        b.piece[y][x] = p0;
        b.piece[ny][nx] = p1;
        if (enp)
        {
            b.piece[ ey ][ ex ] = p2;
        }

        if ( bad )
            return res;
    }

    // valid move -> add!
    res = true;
    Move mov;
    mov.clear();
    mov.val = normalMove;
    bool promo = (ny == 0 || ny == 7);
    // TODO: fixme: add all allowed promotion types (especially to enable mating knight-promotions)
    mov.promo = promo ? queen : 0;
    mov.from = (y<<3) | x;
    mov.to = (ny<<3) | nx;
    mov.capture = (mov.cappiece = b.piece[ ny ][ nx ].type) != none;
    if ( mov.capture || x != nx )
    {
        mov.lastcap = ((ny<<3)|nx) == b.flags.lastMoved;
    }
    mov.dangerous = promo || mov.capture;		// capture/promotion => dangerous

    if ( !mov.capture && mov.promo == queen )
    {
        mov.val = queenPromo;
    }

    if ( !mov.dangerous && (chkMsk & ( oneShlTab[ ((ny<<3) | nx ) ] | (oneShlTab[ ((y<<3) | x ) ] ) )) )
    {
        /*		i32 dx = iabs( nx - ekp.x );
        		i32 dy = iabs( ny - ekp.y );*/

////		if ( dx == 1 && dy == 1 )
        {
            Piece p0, p1;
            p0 = b.piece[y][x];
            p1 = b.piece[ny][nx];
            b.piece[ny][nx] = p0;
            b.piece[y][x].packed = 0;

            // check
            if ( b.check( ekp ) )
            {
                if ( mov.val == normalMove )
                    mov.val = checkingMove;
                mov.dangerous = true;
            }

            // restore
            b.piece[y][x] = p0;
            b.piece[ny][nx] = p1;
        }
    }

#ifdef SEE_PAWNS
    if ( mov.val == normalMove || mov.val == checkingMove )
    {
        i32 cq = b.captureQuality( mov );
        if ( cq < 0 )
            mov.val = clampb( normalMove + cq + (mov.val == checkingMove ? 1 : 0) );
    }
#endif

    *m++ = mov;
    if ( under && promo )				// NEW: underpromotions only if desired
    {
        mov.val = underPromo;
        mov.promo = rook;
        /*		mov.fx = x;
        		mov.fy = y;
        		mov.tx = nx;
        		mov.ty = ny;
        		mov.dangerous = true;*/
        *m++ = mov;
        mov.promo = knight;
        /*		mov.fx = x;
        		mov.fy = y;
        		mov.tx = nx;
        		mov.ty = ny;
        		mov.dangerous = true;*/
        *m++ = mov;
        mov.promo = bishop;
        /*		mov.fx = x;
        		mov.fy = y;
        		mov.tx = nx;
        		mov.ty = ny;
        		mov.dangerous = true;*/
        *m++ = mov;
    }
    return res;
}

// generate pawn's moves
bool pawnMove( Move *&m, const Board &b, i32 turn, u8 pos, u8 kp, u8 ekp,
               bool checked, bool quietOnly, bool pin, u64 chkMsk )
{
    i32 fwd;
    i32 base;
    i32 enp;
    bool res = false;
    bool under = underpromotions && !quietOnly;
    i32 x = pos & 7;
    i32 y = pos >> 3;
    if ( turn == white )
    {
        if ( y == 1 )
        {
            // new: check promotions in qs!
            quietOnly = false;
        }
        fwd = -1;
        base = 6;
        enp = 3;
    }
    else
    {
        if ( y == 6 )
        {
            // new: check promotions in qs!
            quietOnly = false;
        }
        fwd = +1;
        base = 1;
        enp = 4;
    }
    if ( (y + fwd) & ~7 )
        return res;					// should never happen!
    if ( (checked || !quietOnly) && b.piece[ y + fwd ][ x ].type == none )
    {
        if ( tryPawnMove( m, b, turn, x, y, x+0, y + fwd, kp, ekp, checked, pin, chkMsk, under ) )
            res = true;
        if ( y == base )
        {
            // not moved yet!
            if ( b.piece[ y + fwd + fwd ][ x ].type == none )
            {
                if ( tryPawnMove( m, b, turn, x, y, x, y + fwd + fwd, kp, ekp, checked, pin, chkMsk, under ) )
                    res = true;
            }
        }
    }
    // direct capture check
    if ( x > 0 )
    {
        const Piece &p = b.piece[ y + fwd ][ x - 1 ];
        if ( p.type != none && p.color != turn )
        {
            Move *tm = m;
            tryPawnMove( m, b, turn, x, y, x-1, y + fwd, kp, ekp, checked, pin, chkMsk, under );
            if ( m != tm )
            {
                i32 cq = b.captureQuality( *tm );
                if ( cq > 0 )
                    tm->val = winCap + cq;
                else if ( cq == 0 )
                    tm->val = goodCap + pawn;
                else
                    tm->val = clampb( normalCap + cq );
//				tm->val = (tm->cappiece == pawn) ? goodCap : winCap + capQualTab[tm->cappiece] - 1;
                if ( tm->lastcap )
                    tm->val++;
                /*				tm->capture = 1;
                				tm->cappiece = pawn;
                				tm->dangerous = 1;*/
            }
        }
    }
    if ( x < 7 )
    {
        const Piece &p = b.piece[ y + fwd ][ x + 1 ];
        if ( p.type != none && p.color != turn )
        {
            Move *tm = m;
            tryPawnMove( m, b, turn, x, y, x+1, y + fwd, kp, ekp, checked, pin, chkMsk, under );
            if ( m != tm )
            {
                i32 cq = b.captureQuality( *tm );
                if ( cq > 0 )
                    tm->val = winCap + cq;
                else if ( cq == 0 )
                    tm->val = goodCap + pawn;
                else
                    tm->val = clampb( normalCap + cq );
//				tm->val = (tm->cappiece == pawn) ? goodCap : winCap + capQualTab[tm->cappiece] - 1;
                if ( tm->lastcap )
                    tm->val++;
                /*				tm->capture = 1;
                				tm->cappiece = pawn;
                				tm->dangerous = 1;*/
            }
        }
    }

    if ( b.flags.ep )
    {
        if ( y == enp )
        {
            // enpassant capture check!
            if ( x > 0 )
            {
                const Piece &p = b.piece[ y ][ x-1 ];
                if ( p.type == pawn && p.color == (turn ^ 1) && ((y << 3) | (x-1)) == b.flags.ep  )
                {
                    Move *tm = m;
                    tryPawnMove( m, b, turn, x, y, x-1, y + fwd, kp, ekp, checked, 1/*pin*/, chkMsk, under );
                    if ( m != tm )
                    {
                        tm->val = goodCap;
                        tm->capture = 1;
                        tm->cappiece = pawn;
                        tm->enpassant = 1;
                        tm->dangerous = 1;
                        if ( tm->lastcap )
                        {
                            tm->val++;
                        }
                    }
                }
            }
            if ( x < 7 )
            {
                const Piece &p = b.piece[ y ][ x+1 ];
                if ( p.type == pawn && p.color == (turn ^ 1) && ((y << 3) | (x+1)) == b.flags.ep )
                {
                    Move *tm = m;
                    tryPawnMove( m, b, turn, x, y, x+1, y + fwd, kp, ekp, checked, 1/*pin*/, chkMsk, under );
                    if ( m != tm )
                    {
                        tm->val = goodCap;
                        tm->capture = 1;
                        tm->cappiece = pawn;
                        tm->enpassant = 1;
                        tm->dangerous = 1;
                        if ( tm->lastcap )
                        {
                            tm->val++;
                        }
                    }
                }
            }
        }
    }
    return res;
}

// generate knight's moves
// rewritten using new movegen
bool knightMove( Move *&m, const Board &b, i32 turn, u8 pos, u8 kp, u8 ekp,
                 bool checked, bool quietOnly, bool pin, u64 chkMsk )
{
    i32 i;
    i32 c = turn;
    bool res = false;
    if ( pin )
        return false;			// new: real pin support!

    Move tmov;
    tmov.clear();
    tmov.val = normalMove;
//	tmov.promo = 0;
    tmov.from = pos;
    u64 osht = (oneShlTab[ pos ]);
    i8 cur = nmnew[ pos ][ pos ].next;
    while ( cur >= 0 )
    {
        i8 npos = cur;
        const Piece &p = b.pieces[ npos ];
        cur = nmnew[ pos ][ cur ].next;
        if ( p.type != none && p.color == c )
            continue;		// avoid self-capures
        if ( !checked && quietOnly && p.type == none )
        {
            if ( !checked && !pin )
                res = true;		// at least one valid
            continue;
        }

        // last thing to check for: avoid check


        if ( checked || pin /*!dx || !dy || iabs(dx) == iabs(dy)*/ )
        {
            u64 m = oneShlTab[ npos ];
            if ( !(m & kingCheckTab[ kp ]) )
                continue;

            Piece p0, p1;
            p0 = b.pieces[pos];
            p1 = b.pieces[npos];
            b.pieces[npos] = p0;
            b.pieces[pos].packed = 0;

            // check
            bool bad = b.check( kp, checked );

            // restore
            b.pieces[pos] = p0;
            b.pieces[npos] = p1;

            if ( bad )
                continue;
        }

        res = true;

        // valid move -> add!
        Move mov;
        mov.packed = tmov.packed;
//		mov.clear();
////		mov.promo = 0;
//		mov.fx = x;
//		mov.fy = y;
        mov.to = npos;
        mov.dangerous = mov.capture = (mov.cappiece = b.pieces[ npos ].type) != none;		// capture => dangerous
        if ( mov.capture )
        {
            mov.lastcap = ( npos == b.flags.lastMoved );
            /*			i32 cq = capQualTab[ mov.cappiece ];
            			i32 mq = capQualTab[ knight ];*/
            i32 cq = b.captureQuality( mov );
            if ( cq > 0 )
                mov.val = winCap + cq;
            else if ( cq == 0 )
                mov.val = goodCap + knight;
            else mov.val = clampb( normalCap + cq );
            if ( mov.lastcap )
                mov.val++;
        }

        if ( !mov.dangerous )
        {
            if ( knightAttk[ npos ][ ekp ] )
            {
                if ( mov.val == normalMove )
                    mov.val = checkingMove + 2;			// extremely dangerous
                mov.dangerous = true;
            }
            else
            {
                if ( (chkMsk & ( (oneShlTab[ npos ]) | osht )) )
                {
                    Piece p0, p1;
                    p0 = b.pieces[pos];
                    p1 = b.pieces[npos];
                    b.pieces[npos] = p0;
                    b.pieces[pos].packed = 0;

                    // check
                    if ( b.check( ekp ) )
                    {
                        if ( mov.val == normalMove )
                            mov.val = checkingMove;
                        mov.dangerous = true;
                    }

                    // restore
                    b.pieces[pos] = p0;
                    b.pieces[npos] = p1;
                }
            }
        }

#ifdef SEE_ALL
        if ( mov.val == normalMove || mov.val == checkingMove || mov.val == checkingMove + 2 )
        {
            i32 cq = b.captureQuality( mov );
            if ( cq < 0 )
                mov.val = clampb( normalMove + cq + (mov.val >= checkingMove ? mov.val - checkingMove : 0) );
        }
#endif

        *m++ = mov;
    }
    return res;
}

// generate bishop's moves
// rewritten using new movegen
bool bishopMove( Move *&m, const Board &b, i32 turn, u8 pos, u8 kp, u8 ekp,
                 bool checked, bool quietOnly, bool pin, u64 chkMsk )
{
    i32 i;
    i32 c = turn;
    bool res = false;
    bool chkm = (chkMsk & (oneShlTab[ pos ])) != 0;
    Move tmov;
    tmov.clear();
    tmov.val = normalMove;
//	tmov.promo = 0;
    tmov.from = pos;
    i8 cur = bmnew[ pos ][ pos ].next;
    while ( cur >= 0 )
    {
        i8 npos = cur;
        const Piece &p = b.pieces[ npos ];
        if ( p.type != none )
        {
            // stop ray in one direction
            cur = bmnew[ pos ][ cur ].skip;
            if ( p.color == c )
                continue;		// avoid self-capures
        }
        else cur = bmnew[ pos ][ cur ].next;
        if ( !checked && quietOnly && p.type == none )
        {
            if ( !checked && !pin )
                res = true;		// at least one valid
            continue;
        }

        // last thing to check for: avoid check

#ifdef REAL_PIN
        if ( pin /*&& p.type == none*/ )
        {
            i32 dx = iabs( nx - kp.x );
            i32 dy = iabs( ny - kp.y );
            if ( dx != dy )
            {
                // stop ray in one direction
                STOP_RAY(i);
                continue;					// new: avoid (real pin)
            }
        }
#endif

        if ( checked || pin /*!dx || !dy || iabs(dx) == iabs(dy)*/ )
        {
            u64 m = oneShlTab[ npos ];
            if ( !(m & kingCheckTab[ kp ]) )
                continue;

            Piece p0, p1;
            p0 = b.pieces[pos];
            p1 = b.pieces[npos];
            b.pieces[npos] = p0;
            b.pieces[pos].packed = 0;

            // check
            bool bad = b.check( kp, checked );

            // restore
            b.pieces[pos] = p0;
            b.pieces[npos] = p1;

            if ( bad )
                continue;
        }

        res = true;

        // valid move -> add!
        Move mov;
        mov.packed = tmov.packed;
//		mov.clear();
//		mov.promo = 0;
//		mov.fx = x;
//		mov.fy = y;
        mov.to = npos;
        mov.dangerous = mov.capture = (mov.cappiece = b.pieces[ npos ].type) != none;		// capture => dangerous
        if ( mov.capture )
        {
            mov.lastcap = b.flags.lastMoved == npos;
            /*			i32 cq = capQualTab[ mov.cappiece ];
            			i32 mq = capQualTab[ bishop ];*/
            i32 cq = b.captureQuality( mov );
            if ( cq > 0 )
                mov.val = winCap + cq;
            else if ( cq == 0 )
                mov.val = goodCap + knight;			// fixme: hack
            else mov.val = clampb( normalCap + cq );
            if ( mov.lastcap )
                mov.val++;
        }

        bool dang = false;
        if ( !mov.dangerous )
            dang = (chkMsk & (oneShlTab[ npos ])) != 0;
        if ( !mov.dangerous && (dang || chkm) )
        {
            mov.dangerous = dang && bishopDiag[ npos ][ ekp ];
            if ( !mov.dangerous )
            {
                Piece p0, p1;
                p0 = b.pieces[pos];
                p1 = b.pieces[npos];
                b.pieces[npos] = p0;
                b.pieces[pos].packed = 0;

                // in any case, it should not be necessary to check for knights/pawns!
                // check
                if ( b.check( ekp, false ) )
                {
                    if ( mov.val == normalMove )
                        mov.val = checkingMove;
                    mov.dangerous = true;
                }

                // restore
                b.pieces[pos] = p0;
                b.pieces[npos] = p1;
            }
        }

#ifdef SEE_ALL
        if ( mov.val == normalMove || mov.val == checkingMove )
        {
            i32 cq = b.captureQuality( mov );
            if ( cq < 0 )
                mov.val = clampb( normalMove + cq + (mov.val == checkingMove ? 1 : 0) );
        }
#endif

        *m++ = mov;
    }
    return res;
}

// generate rook's moves
// rewritten using new movegen
bool rookMove( Move *&m, const Board &b, i32 turn, u8 pos, u8 kp, u8 ekp,
               bool checked, bool quietOnly, bool pin, u64 chkMsk )
{
    i32 i;
    i32 c = turn;
    bool res = false;
    bool chkm = (chkMsk & (oneShlTab[ pos ])) != 0;
    Move tmov;
    tmov.clear();
    tmov.val = normalMove;
//	tmov.promo = 0;
    tmov.from = pos;

    i8 cur = rmnew[ pos ][ pos ].next;

    while ( cur >= 0 )
    {
        i8 npos = cur;
        const Piece &p = b.pieces[ npos ];
        if ( p.type != none )
        {
            // stop ray in one direction
            cur = rmnew[ pos ][ cur ].skip;
            if ( p.color == c )
                continue;		// avoid self-capures
        } else cur = rmnew[ pos ][ cur ].next;
        if ( !checked && quietOnly && p.type == none )
        {
            if ( !checked && !pin )
                res = true;		// at least one valid
            continue;
        }

        // last thing to check for: avoid check
        /*		i32 dx = x - kp.x;
        		i32 dy = y - kp.y;*/

#ifdef REAL_PIN
        if ( pin /*&& p.type == none*/ )
        {
            i32 dx = iabs( nx - kp.x );
            i32 dy = iabs( ny - kp.y );
            if ( dx && dy )
            {
                // stop ray in one direction
                STOP_RAY(i);
                continue;					// new: avoid (real pin)
            }
        }
#endif

        if ( checked || pin /*!dx || !dy || iabs(dx) == iabs(dy)*/ )
        {
            u64 m = oneShlTab[ npos ];
            if ( !(m & kingCheckTab[ kp ]) )
            {
                continue;
            }

            Piece p0, p1;
            p0 = b.pieces[pos];
            p1 = b.pieces[ npos ];
            b.pieces[npos] = p0;
            b.pieces[pos].packed = 0;

            // check
            bool bad = b.check( kp, checked );

            // restore
            b.pieces[pos] = p0;
            b.pieces[npos] = p1;

            if ( bad )
            {
                continue;
            }
        }

        res = true;

        // valid move -> add!
        Move mov;
        mov.packed = tmov.packed;
//		mov.clear();
//		mov.promo = 0;
//		mov.fx = x;
//		mov.fy = y;
        mov.to = npos;
        mov.dangerous = mov.capture = (mov.cappiece = b.pieces[ npos ].type) != none;		// capture => dangerous
        if ( mov.capture )
        {
            mov.lastcap = npos == b.flags.lastMoved;
            /*			i32 cq = capQualTab[ mov.cappiece ];
            			i32 mq = capQualTab[ rook ];*/
            i32 cq = b.captureQuality( mov );
            if ( cq > 0 )
                mov.val = winCap + cq;
            else if ( cq == 0 )
                mov.val = goodCap + rook;
            else mov.val = clampb( normalCap + cq );
            if ( mov.lastcap )
                mov.val++;
        }

        bool dang = false;
        if ( !mov.dangerous )
            dang = (chkMsk & (oneShlTab[ npos ])) != 0;
        if ( !mov.dangerous && (dang || chkm) )
        {
            mov.dangerous = dang && rookDiag[ npos ][ ekp ];
            if ( !mov.dangerous )
            {
                Piece p0, p1;
                p0 = b.pieces[pos];
                p1 = b.pieces[npos];
                b.pieces[npos] = p0;
                b.pieces[pos].packed = 0;

                // in any case, it should not be necessary to check for knights/pawns!
                // check
                if ( b.check( ekp, false ) )
                {
                    if ( mov.val == normalMove )
                        mov.val = checkingMove;
                    mov.dangerous = true;
                }

                // restore
                b.pieces[pos] = p0;
                b.pieces[npos] = p1;
            }
        }

#ifdef SEE_ALL
        if ( mov.val == normalMove || mov.val == checkingMove )
        {
            i32 cq = b.captureQuality( mov );
            if ( cq < 0 )
                mov.val = clampb( normalMove + cq + (mov.val == checkingMove ? 1 : 0) );
        }
#endif

        *m++ = mov;
    }
    return res;
}

// generate queen's moves
// rewritten using new movegen
bool queenMove( Move *&m, const Board &b, i32 turn, u8 pos, u8 kp, u8 ekp,
                bool checked, bool quietOnly, bool pin, u64 chkMsk )
{
    i32 i;
    i32 c = turn;
    bool res = false;
    bool chkm = (chkMsk & (oneShlTab[ pos ])) != 0;

    Move tmov;
    tmov.clear();
    tmov.val = normalMove;
//	tmov.promo = 0;
    tmov.from = pos;

    i8 cur = qmnew[ pos ][ pos ].next;
    while ( cur >= 0 )
    {
        i8 npos = cur;
        const Piece &p = b.pieces[ npos ];
        if ( p.type != none )
        {
            // stop ray in one direction
            cur = qmnew[ pos ][ cur ].skip;
            if ( p.color == c )
                continue;		// avoid self-capures
        }
        else cur = qmnew[ pos ][ cur ].next;
        if ( !checked && quietOnly && p.type == none )
        {
            if ( !checked && !pin )
                res = true;		// at least one valid
            continue;
        }

        // last thing to check for: avoid check
        /*		i32 dx = x - kp.x;
        		i32 dy = y - kp.y;*/

#ifdef REAL_PIN
        if ( pin /*&& p.type == none*/ )
        {
            i32 dx = iabs( nx - kp.x );
            i32 dy = iabs( ny - kp.y );
            if ( !(dx == dy || !dx || !dy) )
            {
                // stop ray in one direction
                STOP_RAY(i);
                continue;					// new: avoid (real pin)
            }
        }
#endif

        if ( checked || pin /*!dx || !dy || iabs(dx) == iabs(dy)*/ )
        {
            u64 m = oneShlTab[ npos ];
            if ( !(m & kingCheckTab[ kp ]) )
                continue;
            Piece p0, p1;
            p0 = b.pieces[pos];
            p1 = b.pieces[npos];
            b.pieces[npos] = p0;
            b.pieces[pos].packed = 0;

            // check
            bool bad = b.check( kp, checked );

            // restore
            b.pieces[pos] = p0;
            b.pieces[npos] = p1;

            if ( bad )
                continue;
        }

        res = true;

        // valid move -> add!
        Move mov;
        mov.packed = tmov.packed;
//		mov.clear();
//		mov.promo = 0;
//		mov.fx = x;
//		mov.fy = y;
        mov.to = npos;
        mov.dangerous = mov.capture = (mov.cappiece = b.pieces[ npos ].type) != none;		// capture => dangerous
        if ( mov.capture )
        {
            mov.lastcap = npos == b.flags.lastMoved;
            /*			i32 cq = capQualTab[ mov.cappiece ];
            			i32 mq = capQualTab[ queen ];*/
            i32 cq = b.captureQuality( mov );
            if ( cq > 0 )
                mov.val = winCap + cq;
            else if ( cq == 0 )
                mov.val = goodCap + queen;
            else mov.val = clampb( normalCap + cq );
            if ( mov.lastcap )
                mov.val++;
        }

        bool dang = false;
        if ( !mov.dangerous )
            dang = (chkMsk & (oneShlTab[ npos ])) != 0;
        if ( !mov.dangerous && (dang || chkm) )
        {
            mov.dangerous = dang;
            if ( !dang )
            {
                /*
                	opt-note! a queen cannot reveal a hidden check while any other piece can.
                	thus it's only necessary to validate a check in one direction!
                	either hv/diagonal, depending on ekp and nx, ny!
                	in any case, it should not be necessary to check for knights/pawns!
                */

                // rather, start at enemy king's pos
                {
                    i32 nx = npos & 7, ny = npos >> 3;
                    i32 cx = ekp & 7;
                    i32 cy = ekp >> 3;
                    i32 ix = cx < nx ? +1 : (cx == nx) ? 0 : -1;
                    i32 iy = cy < ny ? +1 : (cy == ny) ? 0 : -1;
                    for (;;)
                    {
                        cx += ix;
                        cy += iy;
                        if ( (cx & ~7) || (cy & ~7) )
                            break;
                        const Piece &p = b.piece[ cy ][ cx ];
                        if ( p.type != none )
                            break;
                    }
                    if ( cx == nx && cy == ny )
                    {
                        if ( mov.val == normalMove )
                            mov.val = checkingMove;
                        mov.dangerous = true;
                    }
                }
            }
        }

#ifdef SEE_ALL
        if ( mov.val == normalMove || mov.val == checkingMove )
        {
            i32 cq = b.captureQuality( mov );
            if ( cq < 0 )
                mov.val = clampb( normalMove + cq + (mov.val == checkingMove ? 1 : 0) );
            mov.val = clampb( (i32)mov.val - 1 );				// don't search queenmoves first...
        }
#endif
        /*		// FIXME: new... queens suck when someone outsearches us...
        		if ( mov.val == normalMove || mov.val == checkingMove )
        		{
        			mov.val = clampb( (i32)mov.val - 1 );				// don't search queenmoves first...
        		}*/

        *m++ = mov;
    }
    return res;
}

// generate king's moves
// rewritten using new movegen
bool kingMove( Move *&m, const Board &b, i32 turn, u8 pos, u8 kp, bool checked, bool quietOnly )
{
    i32 i;
    i32 c = turn;
////	PiecePosition kp = b.kingPosition( turn ^ 1 );		// enemy king's position!
    bool res = false;
    i8 cur = kmnew[ pos ][ pos ].next;
    while ( cur >= 0 )
    {
        i8 npos = cur;
        cur = kmnew[ pos ][ cur ].next;
        if ( kprox[ npos ][ kp ] )
            continue;		// can't approach enemy king

        const Piece &p = b.pieces[ npos ];
        if ( p.type != none && p.color == c )
            continue;		// avoid self-capures

        if ( !checked && quietOnly && p.type == none )
        {
////			res = true;		// at least one valid
            continue;
        }

        // last thing to do: avoid placing king in check
        Piece p0 = b.pieces[ pos ];
        b.pieces[ pos ].packed = 0;

        bool bad = b.attacked( npos, c );

        b.pieces[ pos ] = p0;

        if ( bad )
            continue;

        // valid move -> add!
        Move mov;
        mov.clear();
        mov.val = normalMove;
//		mov.promo = 0;
        mov.from = pos;
        mov.to = npos;
        mov.dangerous = mov.capture = (mov.cappiece = b.pieces[ npos ].type) != none;		// capture => dangerous

        if ( mov.capture )
        {
            mov.lastcap = npos == b.flags.lastMoved;
            i32 cq = capQualTab[ mov.cappiece ];
            i32 mq = capQualTab[ king ];
            if ( cq > mq )
                mov.val = winCap + cq - mq;
            else if ( cq == mq )
                mov.val = goodCap;		// fixme: never happens
            else mov.val = clampb( normalCap + cq );
            if ( mov.lastcap )
                mov.val++;
        }

        if ( !mov.dangerous )
        {
            Piece p0, p1;
            p0 = b.pieces[pos];
            p1 = b.pieces[npos];
            b.pieces[npos] = p0;
            b.pieces[pos].packed = 0;

            // check
            if ( b.check( kp, false ) )
            {
                if ( mov.val == normalMove )
                    mov.val = checkingMove;
                mov.dangerous = true;
            }

            // restore
            b.pieces[pos] = p0;
            b.pieces[npos] = p1;
        }

        *m++ = mov;
    }
    i32 x = pos & 7;
    i32 y = pos >> 3;
    if ( checked || x != 4 || (y != 0 && y != 7) || b.pieces[ pos ].moved )
    {
        return res;		// king moved/checked -> castling not possible
    }

    if ( b.flags.castled & (1 << c) )
        return res;		// already castled

    // right castling
    const Piece *p;
    p = &b.piece[ y ][ 7 ];
    if ( p->color == c && p->type == rook && !p->moved )
    {
        if (
            b.piece[ y ][ 5 ].type == none &&
            b.piece[ y ][ 6 ].type == none &&
            !b.attacked( 5 | (y<<3), c ) && !b.attacked( 6 | (y<<3), c ) )
        {
            // right castling allowed
            Move mov;
            mov.clear();
//			mov.promo = 0;
            mov.from = pos;
            mov.to = (y<<3) + 6;
            mov.dangerous = 1;		// new: verify castling
            mov.castling = 1;
//			mov.capture = 0;
            mov.val = castVal;
            *m++ = mov;
        }
    }
    p = &b.piece[ y ][ 0 ];
    if ( p->color == c && p->type == rook && !p->moved )
    {
        if (
            b.piece[ y ][ 1 ].type == none &&
            b.piece[ y ][ 2 ].type == none &&
            b.piece[ y ][ 3 ].type == none &&
            // FIXED: rook is allowed to move through attacked square => Peruvian immortal IS valid!
            /*!b.attacked( 1, y, c ) &&*/ !b.attacked( 2 | (y<<3), c ) && !b.attacked( 3 | (y<<3), c ) )
        {
            // left castling allowed
            Move mov;
            mov.clear();
//			mov.promo = 0;
            mov.from = pos;
            mov.to = (y<<3) | 2;
            mov.dangerous = 1;		// new: verify castling
            mov.castling = 2;
//			mov.capture = 0;
            mov.val = castVal;
            *m++ = mov;
        }
    }
    return res;
}

enum cache_limits {
    maxMoveCache = 1,//3*65531 + 7,
    maxCacheMoves = 65		// max # of moves to cache
};

typedef struct MoveCache
{
    posHash h;
    u32 hits;				// hit count
    i32 num;				// number of moves
    Move m[ maxCacheMoves ];
    i8 type;				// type: -1 = none, 0 = normal, 1 = quiet
    i8 turn;				// turn: white or black (should be already contained in h!)
} MoveCache;

static MoveCache moveCache[ maxMoveCache ];
static bool moveCacheInit = false;
static i32 moveTicks;
static u32 mcHits = 0;
static u32 mcMisses = 0;
static u32 mcOther = 0;

// get 'fake' null move (king to same pos)
Move Board::nullMove( i32 turn ) const
{
    Move m;
    m.clear();
    if ( turn == white )
    {
        m.from = m.to = flags.kingPos[ white ];
    }
    else
    {
        m.from = m.to = flags.kingPos[ black ];
    }
    return m;
}

i32 Board::moves( Move *m, i32 turn, i32 flags ) const
{
    bool quietOnly = (flags & Board::quietOnly) != 0, noSort = (flags & Board::noSort) != 0,
                     useHash = (flags & Board::useMoveHash) != 0;
    /*	static Mutex mtx;
    	MutexLock _( mtx );*/
//	PROFILE( "moves" );

    if ( useHash && !moveCacheInit )
    {
#ifdef CHENG_MT
        MutexLock _( movMutex );
#endif
        // initialize move cache
        i32 i;
        for ( i=0; i<maxMoveCache; i++ )
        {
            moveCache[i].h = 0;
            moveCache[i].type = -1;
        }
        moveCacheInit = true;
        moveTicks = getMillisec();
    }

#if 0
    static u32 chk_ = 0, nchk_ = 0;
    if ( flags.check )
        chk_++;
    else
        nchk_++;

    {
        i32 tc = getMilisec();
        if ( tc - moveTicks >= 1000 )
        {
            moveTicks = tc;
            printf( "chk: %d nchk: %d mc stats: %d hits, %d misses, %d other\n",
                    chk_, nchk_,
                    mcHits, mcMisses, mcOther );
        }
    }
#endif

    if ( quietOnly )
        noSort = true;			// TODO/FIXME: ok?!

    u32 hi = (u32)curHash % maxMoveCache;
    MoveCache *mc = moveCache + hi;
    if ( useHash && !quietOnly && !noSort )
    {
#ifdef CHENG_MT
        MutexLock _( movMutex );
#endif
        if ( mc->turn == turn && mc->h == curHash && mc->type == 0 )
        {
            mcHits++;
            // lookup cache!
            if ( !++mc->hits )
                mc->hits--;				// avoid overflow
            memcpy( m, mc->m, mc->num * sizeof(Move) );
            return mc->num;
        }
    } else mcOther++;

////	quietOnly = false;
    Move *om = m;

    u8 kp = kingPosition( turn );
    u8 ekp = kingPosition( turn ^ 1 );

    /*	bool checked = false;
    	u64 pinMsk = potPins( kp.x, kp.y, &checked );	// new: now real pins!
    	if ( !checked )
    		checked = check( kp.x, kp.y, true, false );*/
    u64 pinMsk = potPins( kp );	// new: now real pins!
    bool checked = this->flags.check;

    u64 rqMsk, bqMsk;								// check masks (hv, diag)
    u64 chkMsk = potChecks( ekp );		//, &rqMsk, &bqMsk );
    /*	// extra: pawn chkMsk!
    	u64 pMsk = (u64)1 << ((ekp.y << 3) | ekp.x);
    	if ( turn == black )
    	{
    		pMsk = (( pMsk >> 7 ) & L1MASK) | (( pMsk << 9 ) & R1MASK );
    	}
    	else
    	{
    		pMsk = (( pMsk << 7 ) & R1MASK) | (( pMsk << 9 ) & L1MASK );
    	}
    	pMsk &= bqMsk;*/

    if ( checked )
    {
        if ( quietOnly )
            noSort = true;
        quietOnly = false;
    }

    i8 ctype = (!quietOnly && !noSort) ? 0 : (quietOnly ? 1 : -1);
    if ( useHash && ctype >= 0 )
    {
#ifdef CHENG_MT
        MutexLock _( movMutex );
#endif
        if ( (ctype == mc->type || (ctype == -1 && mc->type ==0)) && mc->turn == turn && mc->h == curHash )
        {
            mcHits++;
            mcOther--;
            // lookup cache!
            if ( !++mc->hits )
                mc->hits--;				// avoid overflow
            i32 num = mc->num;
            if ( ctype == -1 && num > 1 )
                num = 1;
            memcpy( m, mc->m, num * sizeof(Move) );
            return num;
        }
    }

    /*	pinMsk = chkMsk = 0;
    	pinMsk--;
    	chkMsk--;*/

    i32 x, y;
//	i32 xi[8] = {0, 1, 2, 3, 4, 5, 6, 7}, yi[8] = {0, 1, 2, 3, 4, 5, 6, 7};
//	static i32 xi[8] = {0, 1, 2, 3, 4, 5, 6, 7}, yi[8] = {0, 1, 2, 3, 4, 5, 6, 7};
    if ( !skipTabInit )
        initSkipTab();
    if ( !xiinit )
    {
#if 1
        i32 z;
        for (z=0; z<256; z++)
        {
            for (y=0; y<64; y++)
            {
                xiyi[z][y] = y;
            }
        }
        srand(0);
        for (z=0; z<256; z++)
        {
            for (y=0; y<8*64; y++)
            {
                i32 s = rand() & 63;
                i32 t = rand() & 63;
                i32 v;
                v = xiyi[z][s];
                xiyi[z][s] = xiyi[z][t];
                xiyi[z][t] = v;
            }
        }
#endif
        xiinit = 1;
    }

    mcMisses++;

    static u8 sz_ = 0;

    u8 sz = /*(u8)curHash;	//*/(turn == white ? 0 : 1);

    i32 i;
    u64 ms = mask;
    bool onevalid = false;						// at least one valid move? (should speed up quiescence_movegen)
    bool oneDoes = !quietOnly && noSort;		// one valid move will do
    u8 psn[64];
    i32 ipsn = 0, npsn = getPos( mask, psn );

    i32 hanging = -1;							// hanging piece position (-1 = none)
    i32 hangmin = 0;

    for ( ; ipsn < npsn; ipsn++ )
    {
        i = psn[ ipsn ];
        /*	for (i=0; i<64; i++, ms >>= 1 )
        	{
        		u8 skp = skipTab[ (u16)ms ];
        		if ( skp )
        		{
        			if ( --skp )
        			{
        				i += skp;
        				if ( !(ms >>= skp) )
        					break;
        			}
        			continue;
        		}*/
        x = /*xiyi[sz][i]*/i & 7;
        y = /*xiyi[sz][i]*/i >> 3;

        const Piece &p = pieces[ i ];	//[ y ][ x ];
        if ( p.type == none || p.color != turn )
            continue;
        u64 pm = oneShlTab[ i/*( (y<<3) | x)*/ ];
        bool pin = (pinMsk & pm) != 0;

#ifdef DETECT_HANGING
        // new: too expensive for non-pawns!
        if ( 1 && /*turn == white && */!checked
#ifndef SEE_PAWNS
                && p.type != pawn
#endif
           )
        {
            Move hm;
            hm.packed = 0;
            hm.fx = hm.tx = x;
            hm.fy = hm.ty = y;
            i32 cq = captureQuality( hm );
            if ( cq < hangmin )
            {
                hanging = i;
                hangmin = cq;
            }
        }
#endif
        switch ( p.type )
        {
        case pawn:
            if ( pawnMove( m, *this, turn, i, kp, ekp, checked, quietOnly, pin, chkMsk /*pMsk*/ ) )
            {
                onevalid = true;
                if ( oneDoes )
                    return m - om;
            }
            break;
        case knight:
            if ( knightMove( m, *this, turn, i, kp, ekp, checked, quietOnly, pin, chkMsk ) )
            {
                onevalid = true;
                if ( oneDoes )
                    return m - om;
            }
            break;
        case bishop:
            if ( bishopMove( m, *this, turn, i, kp, ekp, checked, quietOnly, pin, chkMsk /*bqMsk*/ ) )
            {
                onevalid = true;
                if ( oneDoes )
                    return m - om;
            }
            break;
        case rook:
            if ( rookMove( m, *this, turn, i, kp, ekp, checked, quietOnly, pin, chkMsk /*rqMsk*/ ) )
            {
                onevalid = true;
                if ( oneDoes )
                    return m - om;
            }
            break;
        case queen:
            if ( queenMove( m, *this, turn, i, kp, ekp, checked, quietOnly, pin, chkMsk ) )
            {
                onevalid = true;
                if ( oneDoes )
                    return m - om;
            }
            break;
        case king:
            if ( kingMove( m, *this, turn, i, ekp, checked, quietOnly ) )
            {
                onevalid = true;
                if ( oneDoes )
                    return m - om;
            }
            break;
        }
        if ( oneDoes && m != om )
            return m - om;
    }
    // sort moves, 'dangerous' first
#if 0
    Move qm[ maxMoves ];	// quiet moves
    Move dm[ maxMoves ];	// dangerous moves

    Move *qmp = qm, *dmp = dm;
    i32 num = m - om;
#if 1
    for (i=0; i<num; i++)
    {
        if ( om[i].dangerous )
            *dmp++ = om[i];
        else
            *qmp++ = om[i];
    }
    if ( dmp != dm && qmp != qm )
    {
        memcpy( om, dm, (dmp - dm)*sizeof(Move) );
        memcpy( om + (dmp - dm), qm, (qmp - qm)*sizeof(Move) );
    }
#endif
#else
    // sort moves!
    i32 num = m - om;
    if ( num > 1 /* && !noSort*/ )	// FIXME: currently: always sorted!
    {
#ifdef DETECT_HANGING
        if ( hanging >= 0 )
        {
            // add some bonus to moves
            i32 i;
            i32 hx = hanging & 7;
            i32 hy = hanging >> 3;
            u64 am = allAttMask[ hanging ];
            for (i=0; i<num; i++)
            {
                if ( om[i].fx != hx || om[i].fy != hy )
                {
                    i32 h = hangmin;
                    u64 dm = oneShlTab[ (om[i].ty << 3) | om[i].tx ];

                    if ( (dm & am) != 0 )
                    {
                        h >>= 1;
                    }

                    om[i].val = clampb( (i32)om[i].val + h );
                }
            }
        }
#endif
        Move tm[ maxMoves ];
        for (i=0; i<num; i++)
        {
////			om[i].rnd = rndTab[ i ];
            tm[ i ].packed = om[ rndMoveTab[ num ][ i ] ].packed;
        }
        memcpy( om, tm, num * sizeof(Move) );
        std::sort( om, m );
    }
#endif

    sz_++;

    if ( quietOnly && !num )
    {
        if ( !onevalid )
        {
            num = moves( m, turn, (useHash ? useMoveHash : 0) | (noSort ? Board::noSort : 0) );
            if ( !num )
                return 0;
        }
        num = 1;
        om->clear();
    }

    if ( useHash && (1 || ctype >= 0) )
    {
#ifdef CHENG_MT
        MutexLock _( movMutex );
#endif
        if ( ctype == -1 && mc->h == curHash && mc->type == 0 )
            return num;			// never overwrite!
        if ( num <= maxCacheMoves /*&& (!mc->h || mc->hits <= 1)*/ )
        {
            // store => cache!
            mc->type = ctype;
            mc->h = curHash;
            mc->hits = 1;
            mc->num = num;
            mc->turn = turn;
            memcpy( mc->m, om, num * sizeof(Move) );
        }
        /*		else
        		{
        			printf("numMoves = %d!\n", num);
        		}*/
    }
    return num;
}

posHash Board::hash( i32 turn ) const
{
    i32 i;
    posHash h = 0;
    for (i=0; i<64; i++)
    {
        u8 pp = pieces[i].packed;
        if ( pp )
            h ^= (posHash)zobristKeys[ pp | ( i<<8 ) ];
    }
    if ( flags.ep )
    {
        h ^= (posHash)zobristEP[ flags.ep ];
    }
    return curHash = h ^ (posHash)zobristTurn[ turn ];
}

posHash Board::hashPiece( u8 pos ) const
{
    u8 pp = pieces[ pos ].packed;
    return pp ? (posHash)zobristKeys[ pp | ( pos<<8 ) ] : 0;
}

/*i32 Board::eval() const
{
	static i32 matTab[] = {
		0, 100, 325, 325, 500, 975, 0, 0
	};
	i32 i;
	i32 mat = 0;
	for (i=0; i<64; i++)
	{
		const Piece &p = pieces[ i ];
		if ( p.color == white )
			mat += matTab[ p.type ];
		else
			mat -= matTab[ p.type ];
	}
	return mat;
}*/

// bonus tables
static const i16 pawnOpeningBonusBlack[64] =
{
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +01, +03, +05, +05, +06, +05, +03, +01,
    +01, +01, +05, +16, +14, +05, +01, +01,
    +10, +10, +10, +10, +10, +10, +10, +10,
    +33, +33, +33, +33, +33, +33, +33, +33,
    +440, +440, +440, +440, +440, +440, +440, +440,
    +55, +55, +55, +55, +55, +55, +55, +55
};

static const i16 pawnOpeningBonusWhite[64] =
{
    +55, +55, +55, +55, +55, +55, +55, +55,
    +440, +440, +440, +440, +440, +440, +440, +440,
    +33, +33, +33, +33, +33, +33, +33, +33,
    +10, +10, +10, +10, +10, +10, +10, +10,
    +01, +01, +05, +16, +14, +05, +01, +01,
    +01, +03, +05, +05, +06, +05, +03, +01,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00
};

static const i16 pawnBonus[64] =
{
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +05, +05, +05, +05, +05, +05, +05, +05,
    +10, +10, +10, +10, +10, +10, +10, +10,
    +50, +50, +50, +50, +50, +50, +50, +50,
    +100, +100, +100, +100, +100, +100, +100, +100,
    +500, +500, +500, +500, +500, +500, +500, +500,
    +900, +900, +900, +900, +900, +900, +900, +900
};

static const i16 knightOpeningBonusBlack[64] =
{
    -90, -50, -10, -10, -10, -10, -50, -90,
    -10, -05, -05, +05, +05, -05, -05, -10,
    -05, -05, +20, -05, -05, +23, -05, -05,
    -05, -05, -05, -05, -05, -05, -05, -05,
    -15, -15, -15, -15, -15, -15, -15, -15,
    -15, -15, -15, -15, -15, -15, -15, -15,
    -15, -15, -15, -15, -15, -15, -15, -15,
    -95, -15, -15, -15, -15, -15, -15, -95
};

static const i16 knightOpeningBonusWhite[64] =
{
    -95, -15, -15, -15, -15, -15, -15, -95,
    -15, -15, -15, -15, -15, -15, -15, -15,
    -15, -15, -15, -15, -15, -15, -15, -15,
    -15, -15, -15, -15, -15, -15, -15, -15,
    -05, -05, -05, -05, -05, -05, -05, -05,
    -05, -05, +20, -05, -05, +23, -05, -05,
    -10, -05, -05, +05, +05, -05, -05, -10,
    -90, -50, -10, -10, -10, -10, -50, -90
};

static const i16 knightBonus[64] =
{
    -20, -10, -10, -10, -10, -10, -10, -20,
    -05, +00, +00, +05, +05, +00, +00, -05,
    -05, +00, +05, +06, +06, +05, +00, -05,
    -05, +00, +06, +07, +07, +06, +00, -05,
    -05, +00, +06, +07, +07, +06, +00, -05,
    -05, +00, +05, +06, +06, +05, +00, -05,
    -05, +00, +00, +05, +05, +00, +00, -05,
    -20, -10, -10, -10, -10, -10, -10, -20
};

static const i16 bishopOpeningBonusWhite[64] =
{
    -06, +00, +00, +00, +00, +00, +00, -06,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, -16, -16, +00, +00, +00,
    +00, +07, +00, +05, +05, +00, +07, +00,
    -16, +00, -19, +00, +00, -20, +00, -16
};

static const i16 bishopOpeningBonusBlack[64] =
{
    -16, +00, -19, +00, +00, -20, +00, -16,
    +00, +07, +00, +05, +05, +00, +07, +00,
    +00, +00, +00, -16, -16, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    -06, +00, +00, +00, +00, +00, +00, -06
};

static const i16 bishopBonus[64] =
{
    -06, +00, -05, +00, +00, -06, +00, -06,
    +00, +05, +00, +05, +05, +00, +05, +00,
    +00, +00, +00, +05, +05, +00, +00, +00,
    +00, +03, +06, +07, +07, +06, +03, +00,
    +00, +03, +06, +07, +07, +06, +03, +00,
    +00, +00, +00, +05, +05, +00, +00, +00,
    +00, +05, +00, +05, +05, +00, +05, +00,
    -06, +00, -05, +00, +00, -06, +00, -06
};

static const i16 rookOpeningBonus[64] =
{
    -06, - 8, +00, +05, +00, +06, - 8, -06,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +00, +00, +00, +00, +00, +00, +00,
    -06, - 8, +00, +05, +00, +06, - 8, -06
};

static const i16 queenOpeningBonus[64] =
{
    -20, -20, -20, +00, -20, -20, -20, -20,
    -20, -20, -20, -18, -18, -20, -20, -20,
    -20, -20, -40, -19, -19, -40, -20, -20,
    -20, -20, -20, -20, -20, -20, -20, -20,
    -20, -20, -20, -20, -20, -20, -20, -20,
    -20, -20, -40, -19, -19, -40, -20, -20,
    -20, -20, -20, -18, -18, -20, -20, -20,
    -20, -20, -20, +00, -20, -20, -20, -20
};

static const i16 queenOpeningBonusMoved[64] =
{
    -20, -20, -20, -20, -20, -20, -20, -20,
    -20, -20, -20, -18, -18, -20, -20, -20,
    -20, -20, -40, -19, -19, -40, -20, -20,
    -20, -20, -20, -20, -20, -20, -20, -20,
    -20, -20, -20, -20, -20, -20, -20, -20,
    -20, -20, -40, -19, -19, -40, -20, -20,
    -20, -20, -20, -18, -18, -20, -20, -20,
    -20, -20, -20, -20, -20, -20, -20, -20
};

static const i32 kingOpeningBonus[64] =
{
    -80, -70, -30, -99, -30, -99, + 8, -70,
    -80, -70, -60, -99, -99, -99, -60, -70,
    -80, -70, -60, -60, -60, -60, -60, -70,
    -80, -70, -70, -70, -70, -70, -70, -70,
    -80, -70, -70, -70, -70, -70, -70, -70,
    -80, -70, -60, -60, -60, -60, -60, -70,
    -80, -70, -60, -99, -99, -99, -60, -70,
    -80, -70, -30, -99, -30, -99, + 8, -70
};

static const i16 kingEndingBonus[64] =
{
    +00, +00, +00, +00, +00, +00, +00, +00,
    +00, +03, +03, +03, +03, +03, +03, +00,
    +00, +03, +10, +10, +10, +10, +03, +00,
    +00, +03, +10, +20, +20, +10, +03, +00,
    +00, +03, +10, +20, +20, +10, +03, +00,
    +00, +03, +10, +10, +10, +10, +03, +00,
    +00, +03, +03, +03, +03, +03, +03, +00,
    +00, +00, +00, +00, +00, +00, +00, +00
};

i32 Board::gameStage() const
{
    i32 stage;
    if ( tmat > 45850 /*35000*/ )		// 45850
    {
        stage = 0;
    }
    else
        if ( tmat < 22000 /*17000*/ )		// ????
        {
            stage = 2;
        }
        else
        {
            stage = 1;
        }
    return stage;
}

i32 Board::stage() const
{
    /*	i32 stage = 0;
    	i32 i;
    	i32 tmat = 0;
    	for (i=0; i<64; i++)
    	{
    		const Piece &p = pieces[ i ];
    		tmat += tmatTab[ p.type ];
    	}*/
    return 100 - 100 * tmat / 65500;
}

enum eval_Max {
    evalMax = 1//3*1048571+7
};

static posHash evalHash[ evalMax ];
static i32 evalVal[ evalMax ];
static i8 evalSpec[ evalMax ];		// eval specflags; bit 7 = turn!
static bool evalCacheInit = false;
static u32 ecHits = 0, ecMisses = 0;

// returns true if eval cached for this board and turn...
bool Board::evalCached( i32 turn, i32 &ev, i32 *specFlags ) const
{
    if ( specFlags )
        *specFlags = 0;
#ifdef CHENG_MT
    MutexLock _( evalMutex );
#endif
    if ( !evalCacheInit )
        return false;
    posHash h = curHash;	//zobristTurn[ turn ];

    // lookup eval hash!
    u32 hi = (u32)h % evalMax;
    if ( evalHash[ hi ] == h )
    {
        ecHits++;
        ev = evalVal[ hi ];
        if ( specFlags )
            *specFlags = evalSpec[ hi ] & 0x7f;
        return true;
    }
    return false;
}

i32 Board::eval( i32 turn, bool quiescence, u64 *hangMsk, i32 *specFlags, i32 alpha, i32 beta ) const
{
    i32 sf = 0;
    if ( specFlags )
        *specFlags = 0;
//	PROFILE( "eval" );

    if ( quiescence )
    {
        i32 res = dmat;
        /*		if ( flags.castled & (1<<white) )
        			res += 250;
        		if ( flags.castled & (1<<black) )
        			res -= 250;*/
        return (turn == black) ? -res : res;	// + (rand() & 7) - 3;
    }

#ifdef CHENG_MT
    evalMutex.lock();
#endif

    if ( !evalCacheInit )
    {
        evalCacheInit = true;
        memset( evalHash, 0, sizeof(evalHash) );
        memset( evalVal, 0, sizeof(evalVal) );
        memset( evalSpec, 0, sizeof(evalSpec) );
    }
    posHash h = curHash;	//zobristTurn[ turn ];

    // lookup eval hash!
    u32 hi = (u32)h % evalMax;
    if ( evalHash[ hi ] == h )
    {
        bool sameTurn = (turn & 1) == ((evalSpec[hi] >> 7) & 1);
        ecHits++;
        if ( specFlags )
            *specFlags = evalSpec[ hi ] & 0x7f;
        return evalVal[ hi ] * (sameTurn ? +1 : -1);
    }
    ecMisses++;
#ifdef CHENG_MT
    evalMutex.unlock();
#endif

#if 0
    {
        static i32 ecntr = 0;
        if ( ++ecntr >= 1000 )
        {
            logFmt("eval hits: %d misses: %d\n", ecHits, ecMisses );
            ecntr = 0;
        }
    }
#endif

    i32 stage = 0;			// 0 = opening, 1 = midgame, 2 = endgame

    if ( tmat > 45850 /*35000*/ )		// 45850
    {
        stage = 0;
    }
    else
        if ( tmat < 22000 /*17000*/ )		// ????
        {
            stage = 2;
        }
        else
        {
            stage = 1;
        }

#if 1
    if ( stage < 2 && !flags.check )
    {
        i32 res = dmat;
        enum
        {
            margin = 1*3000				// cutoff threshold margin
        };
        if ( turn == black ) res = -res;
        if ( res + margin < alpha )
            return res;
        if ( res - margin > beta )
            return res;
    }
#endif

    bool mateThreat = false;	// mate/stalemate threat
    bool inCheck = false;		// king (current turn) in check

    u64 bpiece[ 2 ][ 7 ];		// bit-piece masks
    u64 bmob[ 2 ][ 7 ];			// bit-mobility masks
    u64 batt[ 2 ][ 7 ];			// bit-attack masks
    u64 cmask[2] = { 0, 0 };	// all-color masks
    u64 bpass[ 2 ] = { 0, 0 };	// passed pawn masks

    i8 rookOn7th[ 2 ] = { 0, 0 };	// rooks on 7th rank (2nd for black)
    u8 colorPawns[2][2] = { {0,0}, {0,0} };
    u8 colorBishops[2][2] = { {0,0}, {0,0} };

    memset( bpiece, 0, sizeof(bpiece) );
    memset( bmob, 0, sizeof(bmob) );
    memset( batt, 0, sizeof(batt) );

    Move m[ maxMoves ];
    i32 nm;
    u8 kp;

    // phase 1: determine material and stage
    // mobility bonus table
    static i32 mobTab[] = {
        0, 1, 3, 2, 4, 4, 8, 0
    };

    i32 i;
    i32 wmat = 0, bmat = 0, mat = 0;
    // mobility bonus (this)
    i32 mob = 0;
    i32 tmat = 0;
#if 0
    for (i=0; i<64; i++)
    {
        const Piece &p = pieces[ i ];
        if ( p.type == none )
            continue;
        h ^= hashPiece( i & 7, i >> 3 );
        /*		if ( p.type == king && p.color == turn )
        		{
        			kp.x = (i & 7);
        			kp.y = (i >> 3);
        		}*/
        if ( p.color == white )
        {
            wmat += matTab[ p.type ];
        }
        else
        {
            bmat += matTab[ p.type ];
        }
        tmat += tmatTab[ p.type ];
    }
#endif

    /*	if ( this->dmat != wmat - bmat || tmat != this->tmat )
    	{
    		printf( "dmat, tmat error!\n" );
    		abort();
    	}*/

////	return wmat - bmat;
    wmat = this->dmat;
    tmat = this->tmat;

#if 0
    kp = kingPosition( turn );
    nm = moves( m, turn, false, true );

    /*	if ( !nm )
    	{
    		i32 val;
    		if ( check( kp.x, kp.y ) )
    		{
    			val = (turn == white ? -mate / 4 : +mate / 4);
    		}
    		else val = 0;
    		evalHash[ hi ] = h;
    		evalVal[ hi ] = val;
    		return val;
    	}*/

    for (i=0; i<nm; i++)
    {
        const Move &tm = m[i];
        mob += mobTab[ piece[ tm.fy ][ tm.fx ].type ];
        if ( tm.dangerous )
        {
            mob += 2;			// dangerous move bonus (i.e. captures etc.)
        }
        if ( tm.capture )
        {
            mob += 3*tm.cappiece;	// possible capture bonus
        }
    }
////	mob >>= 1;

#if 0
    nm = moves( m, turn^1, false, true );

    for (i=0; i<nm; i++)
    {
        const Move &tm = m[i];
        mob -= mobTab[ piece[ tm.fy ][ tm.fx ].type ];
        if ( tm.dangerous )
        {
            mob -= 2;			// dangerous move bonus (i.e. captures etc.)
        }
        if ( tm.capture )
        {
            mob -= 3*tm.cappiece;	// possible capture bonus
        }
    }
#endif
    mob >>= 1;

#endif
    if ( turn == black )
        mob = -mob;

    if ( quiescence )
        return wmat - bmat;

    i32 orgmat = wmat - bmat;

    i8 bishopPair[2] = {0, 0};
    i8 rookPair[2] = {0, 0};

    i8 hasPawns[2] = {0, 0};

    i8 rookCount[2] = {0, 0};				// piece counts (useful in endgames)
    i8 knightCount[2] = {0, 0};
    i8 bishopCount[2] = {0, 0};

    bool loneKing[2] = {true, true};
    bool matingMat[2] = {false, false};		// mating material

    bool minor[2] = {false, false};			// either side has ANY minor pieces?
    bool major[2] = {false, false};			// either side has ANY major pieces?

    // pawn count for each file (better dblpwn detection)
    i8 fpawns[2][8] = {
        { 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0 }
    };
    u8 filepawns[2] = { 0, 0 };			// file-pawn masks
    u8 filerooks[2] = { 0, 0 };			// file-rook masks
    i32 dblpawns[2] = { 0, 0 };			// doubled pawn count for each color
    i32 tdblpawns[2] = { 0, 0 };		// true doubled pawn count for each color

    if ( !skipTabInit )
        initSkipTab();
    u8 kpos[2] = {
        flags.kingPos[ white ],
        flags.kingPos[ black ]
    };

    i32 matVal[2] = { 0, 0 };			// non-pawn material values

////	u64 ms = mask;
    u8 psn[64];
    i32 ipsn = 0, npsn = getPos( mask, psn );
    // phase 2: finetune
    /*	for (i=0; i<64; i++, ms >>= 1 )
    	{
    		u8 skp = skipTab[ (u16)ms ];
    		if ( skp )
    		{
    			if ( --skp )
    			{
    				i += skp;
    				if ( !(ms >>= skp) )
    					break;
    			}
    			continue;
    		}*/
    for ( ; ipsn < npsn; ipsn++ )
    {
        i = psn[ ipsn ];
        const Piece &p = pieces[ i ];
        if ( p.type == none )
            continue;

        // calculate king tropism
        if ( stage <= 1 )
        {
            if ( p.color == white )
            {
                // white (black king)
                i32 dist = distTab[ kpos[ black ] ][ i ];
                bmat -= trop_scale*tropTab[ p.type ][ dist ];
                // center control (white)
                i32 cdist = centerTab[ i ];
                wmat += cdist;
            }
            else
            {
                // black (white king)
                i32 dist = distTab[ kpos[ white ] ][ i ];
                wmat -= trop_scale*tropTab[ p.type ][ dist ];
                // center control (black)
                i32 cdist = centerTab[ i ];
                bmat += cdist;
            }
        }

        // build bit-piece masks
        bpiece[ p.color ][ p.type ] |= oneShlTab[ i ];

        switch ( p.type )
        {

        case pawn:
        {
            loneKing[ p.color ] = 0;
            hasPawns[ p.color ]++;
            colorPawns[ p.color ][ i & 1 ]++;
            filepawns[ p.color ] |= 1 << (i & 7);
            if ( ++fpawns[ p.color ][ i & 7 ] > 1 )
            {
                dblpawns[ p.color ]++;
            }
            i32 y = i >> 3;
            i32 x = i & 7;
            i32 backy = (p.color == white) ? y + 1 : y - 1;
            if ( !(backy & ~7) )
            {
                const Piece &bp = piece[ backy ][ x ];
                if ( bp.type == pawn && bp.color == p.color )
                    tdblpawns[ p.color ]++;
            }
#if 0
            i32 x, y;
            x = i & 7;
            y = i >> 3;
            i32 backy;
            i32 *pmat;
            i32 fwd;
            if ( p.color == white )
            {
                pmat = &wmat;
                backy = y + 1;
                fwd = -1;
            }
            else
            {
                pmat = &bmat;
                backy = y - 1;
                fwd = +1;
            }
            if ( /*y != 1 && y != 6 &&*/ !(backy & ~7) )
            {
                /*
                // check if doubled pawn
                while ( !(backy & ~7) )
                {
                const Piece &bp = piece[ backy ][ x ];
                if ( bp.type == none )
                {
                backy -= fwd;
                continue;
                }
                if ( bp.color != p.color || bp.type > pawn )
                break;
                ////					if ( bp.color == p.color && bp.type == pawn )
                {
                // doubled pawn detected! (penalize)
                *pmat -= stage == 0 ? 40 : 20;
                }
                backy -= fwd;
                }*/
                bool prot = false;			// protected by pawn on either side
                bool neigh = false;			// has neighbour on either side
                bool passed = true;			// is passed pawn
                i32 ty;
                static u8 tyvalid[8]={0, 1, 1, 1, 1, 1, 1, 0};
                for (ty = y + fwd; !(ty & ~7) && tyvalid[ ty & 7 ]; ty += fwd )
                {
                    const Piece &pp = piece[ ty ][ x ];
                    if ( pp.color != p.color && pp.type == pawn )
                    {
                        passed = false;
                        break;
                    }
                }
                if ( x > 0 )
                {
                    i32 ty;
                    for (ty = y + fwd; passed && !(ty & ~7) && tyvalid[ ty & 7 ]; ty += fwd )
                    {
                        const Piece &pp = piece[ ty ][ x - 1 ];
                        if ( pp.color != p.color && pp.type == pawn )
                        {
                            passed = false;
                            break;
                        }
                    }
                    const Piece &pp = piece[ backy ][ x - 1 ];
                    if ( pp.color == p.color && pp.type == pawn )
                        prot = true;
                    else {
                        const Piece &bp = piece[ y ][ x - 1 ];
                        if ( bp.color == p.color && bp.type == pawn )
                        {
                            neigh = true;
                        }
                    }
                }
                if ( x < 7 )
                {
                    i32 ty;
                    for (ty = y + fwd; passed && !(ty & ~7) && tyvalid[ ty & 7 ]; ty += fwd )
                    {
                        const Piece &pp = piece[ ty ][ x + 1 ];
                        if ( pp.color != p.color && pp.type == pawn )
                        {
                            passed = false;
                            break;
                        }
                    }
                    if ( !passed && !prot )
                    {
                        const Piece &pp = piece[ backy ][ x + 1 ];
                        if ( pp.color == p.color && pp.type == pawn )
                            prot = true;
                        else {
                            const Piece &bp = piece[ y ][ x + 1 ];
                            if ( bp.color == p.color && bp.type == pawn )
                            {
                                neigh = true;
                            }
                        }
                    }
                }
                if ( passed )
                {
                    bpass[ p.color ] |= (u64)1 << i;
                    // passed -- bonus!
                    *pmat += 100;//matTab[ pawn ] >> 3;
                    if ( prot )
                    {
                        *pmat += 40;//matTab[ pawn ] >> 5;
                    }
                }
                else
                    if ( !prot && !neigh )
                    {
                        // isolated -- penalize (but: might be a passed pawn!)
                        *pmat -= 200;//matTab[ pawn ] >> 2;
                    }
                    else if ( !prot )
                    {
                        // backward -- penalize less (but: might be a passed pawn!)
                        if ( y != 6 && y != 1 )
                        {
                            *pmat -= 40;//matTab[ pawn ] >> 4;
                        }
                    }
            }
#endif
            if ( stage == 0 /*&& turn == p.color*/ )
            {
                if ( p.color == black )
                    bmat += pawnOpeningBonusBlack[i];
                else
                    wmat += pawnOpeningBonusWhite[i];
            }
            else
            {
                if ( p.color == black )
                    bmat += pawnBonus[i];
                else
                    wmat += pawnBonus[i ^ 63];
            }
        }
        break;
        case bishop:
        {
            minor[ p.color ] = true;
            matVal[ p.color ] += tmatTab[ bishop ];
            bishopCount[ p.color ]++;
            loneKing[ p.color ] = 0;
            colorBishops[ p.color ][ sqrColor[i] ]++;
            bishopPair[ p.color ]++;
            if ( stage == 0 /*&& turn == p.color*/ )
            {
                if ( p.color == white )
                {
                    wmat += bishopOpeningBonusWhite[i];
                    // penalize bishop blocking d/e pawn
                    i32 y = i >> 3;
                    if ( y < 7 )
                    {
                        i32 x = i & 7;
                        if ( x == 3 || x == 4 )
                        {
                            const Piece &bp = piece[ y + 1 ][ x ];
                            if ( bp.type == pawn && bp.color == p.color )
                            {
                                wmat -= 100;
                            }
                        }
                    }
                }
                else
                {
                    bmat += bishopOpeningBonusBlack[i];
                    // penalize bishop blocking d/e pawn
                    i32 x = i & 7;
                    i32 y = i >> 3;
                    if ( y > 0 )
                    {
                        i32 x = i & 7;
                        if ( x == 3 || x == 4 )
                        {
                            const Piece &bp = piece[ y - 1 ][ x ];
                            if ( bp.type == pawn && bp.color == p.color )
                            {
                                bmat -= 100;
                            }
                        }
                    }
                }
            }
            else
            {
                if ( p.color == white )
                    wmat += bishopBonus[i];
                else
                    bmat += bishopBonus[i];
            }
        }
        break;
        case knight:
        {
            minor[ p.color ] = true;
            matVal[ p.color ] += tmatTab[ knight ];
            loneKing[ p.color ] = 0;
            knightCount[ p.color ]++;
            if ( stage == 0 /*&& turn == p.color*/ )
            {
                if ( p.color == white )
                    wmat += knightOpeningBonusWhite[i];
                else
                    bmat += knightOpeningBonusBlack[i];
            }
            else
            {
                if ( p.color == white )
                    wmat += knightBonus[i];
                else
                    bmat += knightBonus[i];
            }
        }
        break;
        case king:
        {
#if 1
            if ( stage <= 1 && !(flags.castled & ( 1 << p.color )) )
            {
                i32 mpawn = matTab[ pawn ] >> (stage + stage);
                ////				// new: only penalize if (castled) king hasn't moved yet
                ////				const PiecePosition &pp = flags.kingPos[ p.color ^ 1 ];
                if ( 1/*(pp.x == 2 || pp.x == 6) && pp.y == ( p.color == white ? 7 : 0 )*/ )
                {
                    if ( p.color == white )
                        wmat -= mpawn * 1/4;
                    else
                        bmat -= mpawn * 1/4;
                    /*					if ( p.moved )
                    {
                    // penalize even more (castling not possible )
                    if ( p.color == white )
                    wmat -= mpawn * 1/7;
                    else
                    bmat -= mpawn * 1/7;
                    }*/
                }
            }
#endif
            // penalize if king has moved
            if ( stage <= 1 && p.moved )
            {
                i32 mpawn = matTab[ pawn ] >> (stage + stage);
                if ( p.color == white )
                {
                    wmat -= mpawn / 6;
                }
                else
                {
                    bmat -= mpawn / 6;
                }
            }
#ifdef OLD_SAFETY
            if ( stage < 2)
            {
                i32 x, y;
                x = i & 7;
                y = i >> 3;
                // king safety (only if file < d || file > e AND rank is 1,2 or 7,8!)
                if ( (x < 4 || x > 5) && (y <= 1 || y >= 6) )
                {
                    i32 fronty;
                    i32 *pmat;
                    if ( p.color == white )
                    {
                        pmat = &wmat;
                        fronty = y - 1;
                    }
                    else
                    {
                        pmat = &bmat;
                        fronty = y + 1;
                    }
                    if ( !(fronty & ~7) )
                    {
                        i32 safety = 0;
                        const Piece &fp = piece[ fronty ][ x ];
                        if ( fp.color == p.color && fp.type == pawn )
                        {
                            safety++;
                        }
                        if ( x > 0 )
                        {
                            const Piece &fp = piece[ fronty ][ x - 1 ];
                            if ( fp.color == p.color && fp.type == pawn )
                            {
                                safety++;
                            }
                        }
                        if ( x < 7 )
                        {
                            const Piece &fp = piece[ fronty ][ x + 1 ];
                            if ( fp.color == p.color && fp.type == pawn )
                            {
                                safety++;
                            }
                        }
                        bool sr = ( y == 0 || y == 7 );
                        if ( safety == 1 )
                        {
                            *pmat += sr ? 100 : 50;
                        }
                        else if ( safety > 1 )
                        {
                            *pmat += sr ? 400 : 200;
                            if ( safety > 2 )
                                *pmat += sr ? 50 : 25;
                        }
                    }
                }
            }
#endif
            if ( stage == 0 || (stage == 1 && !p.moved) )
            {
                /*				if ( p.color == white )
                wmat += kingOpeningBonus[i];
                else
                bmat += kingOpeningBonus[i];*/
            }
            else if ( stage == 2 )
            {
                if ( p.color == white )
                    wmat += kingEndingBonus[i];
                else
                    bmat += kingEndingBonus[i];
            }
        }
        break;
        case rook:
        {
            major[ p.color ] = true;
            matVal[ p.color ] += tmatTab[ rook ];
            rookCount[ p.color ]++;
            matingMat[ p.color ] = true;
            loneKing[ p.color ] = 0;
            filerooks[ p.color ] |= 1 << (i & 7);
            i32 y = (i >> 3) & 7;
            if ( p.color == white )
            {
                if ( y == 1 )
                    rookOn7th[ white ]++;
            }
            else
            {
                if ( y == 6 )
                    rookOn7th[ black ]++;
            }
            rookPair[ p.color ]++;
            if ( stage == 0 && !(flags.castled & ( 1 << p.color )) )
            {
                if ( p.color == white )
                    wmat += rookOpeningBonus[i];
                else
                    bmat += rookOpeningBonus[i];
            }
        }
        break;
        case queen:
        {
            major[ p.color ] = true;
            matVal[ p.color ] += tmatTab[ queen ];
            matingMat[ p.color ] = true;
            loneKing[ p.color ] = 0;
            if ( stage == 0 && !(flags.castled & ( 1 << p.color )) )
            {
                if ( p.color == white )
                    wmat += (p.moved ? queenOpeningBonusMoved : queenOpeningBonus)[i];
                else
                    bmat += (p.moved ? queenOpeningBonusMoved : queenOpeningBonus)[i];
            }
        }
        break;
        }
    }

    if ( matVal[ white ] > matVal[ black ] )
    {
        wmat += 1000;			// white has more non-pawn material!
    }
    else
        if ( matVal[ white ] < matVal[ black ] )
        {
            bmat += 1000;			// black has more non-pawn material!
        }

    if ( major[white] && !major[black] && !minor[black] )
    {
        wmat += 1500;			// white has major piece(s) against pawns
    }

    if ( major[black] && !major[white] && !minor[white] )
    {
        bmat += 1500;			// black has major piece(s) against pawns
    }

    if ( !hasPawns[white] && hasPawns[black] )
        wmat -= 100;			// pawnless white
    else
        if ( !hasPawns[black] && hasPawns[white] )
            bmat -= 100;			// pawnless black

    if ( tmat == 0 )
    {
        // pawn ending!
        wmat += hasPawns[white] * 200;
        wmat -= tdblpawns[white] * 100;
        bmat += hasPawns[black] * 200;
        bmat -= tdblpawns[black] * 100;
    }

    i32 closedmsk = filepawns[ white ] | filepawns[ black ];
    if ( closedmsk )
    {
        // handle rook on (semi)open files
        u8 wo = filerooks[ white ] & ~closedmsk;
        if ( wo )
        {
            // bitcount!
            i32 bc = 1;	//bitCount16[ wo ];
            wmat += (bc * 100) >> stage;
        }
        u8 bo = filerooks[ black ] & ~closedmsk;
        if ( bo )
        {
            // bitcount!
            i32 bc = 1;	//bitCount16[ bo ];
            bmat += (bc * 100) >> stage;
        }
        // semiopen
        u8 t = filerooks[ white ] & ~filepawns[ white ] & ~wo;
        if ( t )
        {
            // bitcount!
            i32 bc = 1;	//bitCount16[ t ];
            wmat += (bc * 50) >> stage;
        }
        // semiopen
        t = filerooks[ black ] & ~filepawns[ black ] & ~bo;
        if ( t )
        {
            // bitcount!
            i32 bc = 1;	//bitCount16[ t ];
            bmat += (bc * 50) >> stage;
        }
    }

    /*	if ( stage == 2 )
    	{
    		// bonus for each endgame pawn (+50)
    		u64 b;
    		b = bpiece[ white ][ pawn ];
    		if ( b )
    		{
    			wmat += bitCount( b ) * 50;
    		}
    		b = bpiece[ black ][ pawn ];
    		if ( b )
    		{
    			bmat += bitCount( b ) * 50;
    		}
    	}*/

    i32 t;
    if ( (t = dblpawns[ white ]) != 0 )
    {
        // doubled pawn detected! (penalize)
        dblpawns[ white ] -= tdblpawns[ white ];
        wmat -= t*( stage <= 1 ? 100 : 50 );
        wmat -= tdblpawns[ white ]*( stage <= 1 ? 250 : 125 );
    }

    if ( (t = dblpawns[ black ]) != 0 )
    {
        // doubled pawn detected! (penalize)
        dblpawns[ black ] -= tdblpawns[ black ];
        bmat -= t*( stage <= 1 ? 100 : 50 );
        bmat -= tdblpawns[ black ]*( stage <= 1 ? 250 : 125 );
    }

    if ( bpiece[ white ][ bishop ] )
    {
        if ( bishopPair[ white ] > 1 )
            wmat += 500;
        else
        {
            i32 good;
            // good vs bad bishop
            if ( colorBishops[ white ][ white ] )
            {
                // white has a bishop on white squares
                good = colorPawns[ white ][ black ] - colorPawns[ white ][ white ];
            }
            else
            {
                // white has a bishop on black squares
                good = colorPawns[ white ][ white ] - colorPawns[ white ][ black ];
            }
            wmat += good * 13;
            if ( good < 0 && bishopPair[ black ] > 1 )
            {
                bmat -= good * 63;
            }
        }
    }
    if ( bpiece[ black ][ bishop ] )
    {
        if ( bishopPair[ black ] > 1 )
            bmat += 500;
        else
        {
            i32 good;
            // good vs bad bishop
            if ( colorBishops[ black ][ white ] )
            {
                // black has a bishop on white squares
                good = colorPawns[ black ][ black ] - colorPawns[ black ][ white ];
            }
            else
            {
                // black has a bishop on black squares
                good = colorPawns[ black ][ white ] - colorPawns[ black ][ black ];
            }
            bmat += good * 13;
            if ( good < 0 && bishopPair[ white ] > 1 )
            {
                wmat -= good * 63;
            }
        }
    }

    // new: my assumption that a pair of rooks is dangerous
    if ( rookPair[ white ] > 1 )
        wmat += 250;
    if ( rookPair[ black ] > 1 )
        bmat += 250;

    // todo: finetune (should be work more depending on stage)
    if ( hasPawns[black] && rookOn7th[ white ] > 0 && (bpiece[ black ][ pawn ] & 0xff00) )
        wmat += rookOn7th[ white ] * (300 >> stage);
    if ( hasPawns[white] && rookOn7th[ black ] > 0 && (bpiece[ white ][ pawn ] & U64C( 0xff000000000000 )) )
        bmat += rookOn7th[ black ] * (300 >> stage);

    u64 pinMsk[2] = {
        potPins( flags.kingPos[ white ] ),
        potPins( flags.kingPos[ black ] )
    };

    // build cmasks
    i32 j;
    for (j=0; j<2; j++)
    {
        for ( i=pawn; i <= king; i++ )
        {
            cmask[ j ] |= bpiece[ j ][ i ];
        }
    }

    bool potEnd = false;		// potential end of game
    i32 potVal = 0;				// potential end of game value

    i32 twinRooks[ 2 ] = { 0, 0 };		// vert twin rook flags
    i32 htwinRooks[ 2 ] = { 0, 0 };		// horiz twin rook flags
    i32 twinRQueen[ 2 ] = { 0, 0 };		// twin rook-queen flags
    i32 twinQBishop[ 2 ] = { 0, 0 };	// twin queen-bishop flags
    i32 rookPass[ 2 ] = { 0, 0 };		// rook behind passed pawn (count)
    {
        // build fast attack/mobility info

        i8 att[ 2 ][ 64 ];		// attacker counts
        u8 attPiece[ 2 ][ 64 ];	// attacker piece types (= 1<<type, except for king)
////		i8 def[ 2 ][ 64 ];	// defender counts

        memset( att, 0, sizeof(att) );
        memset( attPiece, 0, sizeof(attPiece) );
////		memset( def, 0, sizeof(def) );

        u64 b, t;

        // pawn structure..
        {
            u64 cm[ 2 ];	// cover_mask
            u64 nm[ 2 ];	// neighb_mask
            u64 bkm[ 2 ];	// back_prot mask
            u64 blm[ 2 ];	// passed_pawn_blockmask
            u64 p[ 2 ];		// pawn bits

            if ( hasPawns[ white ] )
            {
                p[ white ] = bpiece[ white ][ pawn ];

                b = p[ white ];
                // generate cover_mask
                cm[ white ] = ( ( b >> 9 ) & R1MASK ) | ( ( b >> 7 ) & L1MASK );
                // generate back_mask
                bkm[ white ] = (b << 8) | ( ( b << 9 ) & L1MASK ) | ( ( b << 7 ) & R1MASK );
                // generate neighb_mask
                nm[ white ] = ( ( b << 1 ) & L1MASK ) | ( ( b >> 1 ) & R1MASK );
                blm[ white ] = b | nm[ white ];
            }
            if ( hasPawns[ black ] )
            {
                p[ black ] = bpiece[ black ][ pawn ];

                b = p[ black ];
                // generate cover_mask
                cm[ black ] = ( ( b << 9 ) & L1MASK ) | ( ( b << 7 ) & R1MASK );
                // generate back_mask
                bkm[ black ] = (b >> 8) | ( ( b >> 9 ) & R1MASK ) | ( ( b >> 7 ) & L1MASK );
                // generate neighb_mask
                nm[ black ] = ( ( b << 1 ) & L1MASK ) | ( ( b >> 1 ) & R1MASK );
                blm[ black ] = b | nm[ black ];
            }
            for ( i=0; i<2; i++)
            {
                if ( !hasPawns[i] )
                    continue;
                b = p[ i ];

                // one_front mask
                u64 frm = ( i == white ) ? (b >> 8) : (b << 8);

                // get protected pawn mask
                u64 prot = b & cm[i];

                // get backward pawn mask ( not protected but has neighbors )
                u64 bwd = (b & ~cm[i]) & (nm[i] | bkm[i] | frm);

                // get isolated/passed pawn mask
                u64 iso = b & ~cm[i] & ~nm[i] & ~bkm[i] & ~frm;

                u64 pass = 0;		// passed pawns mask
                {
                    // now find passed pawns!
                    u64 bl = blm[ i ^ 1 ];

                    if ( i == white )
                        t = b >> 8;
                    else
                        t = b << 8;
////					i64 fpass = 0;			// passed file mask
                    for ( j=0; t && j<7; j++ )
                    {
                        t &= ~bl;
                        if ( i == white )
                        {
                            if ( t & 0x00000000000000ff )
                            {
////								fpass |= t & 0xff;
                                pass |= (t & 0xff) << (8*j + 8);
                            }
                            t = t >> 8;
                        }
                        else
                        {
                            if ( t & U64C( 0xff00000000000000 ) )
                            {
////								fpass |= t >> (64-8);
                                pass |= (t  & U64C( 0xff00000000000000 )) >> (8*j + 8);
                            }
                            t = t << 8;
                        }
                    }
                    /*					// duplicate filemask
                    					if ( fpass )
                    					{
                    						fpass |= fpass << 8;
                    						fpass |= fpass << 16;
                    						fpass |= fpass << 32;
                    					}*/

////					pass = b & fpass;				// passed pawns
                    iso &= ~pass;					// passed not isolated!
                    bwd &= ~pass;					// backward not isolated!
                }
                bpass[ i ] = pass;

                // passed bonus
                {
                    i32 *pmat = (i == white) ? &wmat : &bmat;
                    *pmat += 175 * bitCount( pass ) + 20 * bitCount( pass & prot )
                             - 15 * bitCount( iso ) - 5 * bitCount( bwd ) + 4 * bitCount( prot )
                             ;
                }

                /*
                				printf("evalPawnStruct[ %d ]:\n", i);
                				printf("\tnum_pawns: %d\n", bitCount( b ) );
                				printf("\tnum_bwd  : %d\n", bitCount( bwd ) );
                				printf("\tnum_iso  : %d\n", bitCount( iso ) );
                				printf("\tnum_prot : %d\n", bitCount( prot ) );
                				printf("\tnum_pass : %d\n", bitCount( pass ) );
                				printf("\tnum_ppass: %d\n", bitCount( pass & prot ) );
                */
            }
        }


        // pawn mobility...
        u64 m = ~mask;
        u64 tm, tm2;
        i32 j;
        u64 attm[ 2 ] = { 0, 0 };			// attack mask for each color
        u64 knatt[ 2 ] = { 0, 0 };			// knight attack mask for each color
        u64 patt[ 2 ] = { 0, 0 };			// pawn attack mask for each color
        u64 batt[ 2 ] = { 0, 0 };			// bishop attack mask for each color

        tm = ~cmask[ white ];
        tm2 = ~cmask[ black ];

        b = bpiece[ white ][ pawn ] & ~pinMsk[ white ];

#define ATTDEF_old( pce, who, t )	\
		if ( t ) {	\
				i32 ii;	\
				u64 tmp = (t) & (cmask[ white ] | cmask[ black ]) ;	\
				for ( ii=0; tmp && ii<64; ii++, tmp >>= 1 )	\
				{	\
					u8 skp = skipTab[ (u16)tmp ];	\
					if ( skp )	\
					{	\
						if ( --skp )	\
						{	\
							ii += skp;	\
							tmp >>= skp;	\
						}	\
						continue;	\
					}	\
					if ( (u8)tmp & 1 )	\
					{	\
						attPiece[ (who) ][ ii ] |= 1 << (pce);	\
						att[ (who) ][ ii ]++;	\
					}	\
				}	\
		}

#define ATTDEF( pce, who, t )	\
		if ( t ) {	\
				i32 ii, ipsn;	\
				u8 psn[ 64 ];	\
				u64 tmp = (t) & (cmask[ white ] | cmask[ black ]) ;	\
				i32 npsn = getPos( tmp, psn );	\
				for ( ipsn = 0; ipsn < npsn; ipsn++ )	\
				{	\
					ii = psn[ ipsn ];	\
					/*if ( (u8)(tmp>>ii) & 1 )*/ /*BUGFIX!*/	\
					{	\
						attPiece[ (who) ][ ii ] |= 1 << (pce);	\
						att[ (who) ][ ii ]++;	\
					}	\
				}	\
		}

        if ( b )
        {
            t = (b >> 8) & m;
            bmob[ white ][ pawn ] |= t;
            t = b;
            t &= U64C( 0x00ff000000000000 );
            t = (t >> 8) & m;
            t = (t >> 8) & m;
            bmob[ white ][ pawn ] |= t;
            t = b;
            t = ( t >> 9 ) & R1MASK;
            attm[ white ] |= t;
            patt[ white ] |= t;
            t &= tm;
            u64 atd = t;			// new: don't call attdef too many times!
////			ATTDEF( pawn, white, t );
            t &= ~tm2;
            bmob[ white ][ pawn ] |= t;
            t = b;
            t = ( t >> 7 ) & L1MASK;
            attm[ white ] |= t;
            patt[ white ] |= t;
            t &= tm;
            atd |= t;
            ATTDEF( pawn, white, atd );
            t &= ~tm2;
            bmob[ white ][ pawn ] |= t;
        }

        b = bpiece[ black ][ pawn ] & ~pinMsk[ black ];
        if ( b )
        {
            t = (b << 8) & m;
            bmob[ black ][ pawn ] |= t;
            t = b;
            t &= 0x000000000000ff00;
            t = (t << 8) & m;
            t = (t << 8) & m;
            bmob[ black ][ pawn ] |= t;
            t = b;
            t = ( t << 9 ) & L1MASK;
            attm[ black ] |= t;
            patt[ black ] |= t;
            t &= tm2;
            u64 atd = t;			// new: don't call attdef too many times!
////			ATTDEF( pawn, black, t );
            t &= ~tm;
            bmob[ black ][ pawn ] |= t;
            t = b;
            t = ( t << 7 ) & R1MASK;
            attm[ black ] |= t;
            patt[ black ] |= t;
            t &= tm2;
            atd |= t;
            ATTDEF( pawn, black, atd );
            t &= ~tm;
            bmob[ black ][ pawn ] |= t;
        }

        // knight mobility (incl. attacks)
        for ( i=0; i<2; i++ )
        {
            b = bpiece[ i ][ knight ] & ~pinMsk[ i ];
            if ( !b )
                continue;			// no knights
            tm = ~cmask[ i ];
            t = (b << 17) & L1MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            u64 atd = t;			// new: don't call attdef too many times!
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;
            t = (b << 15) & R1MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;
            t = (b >> 17) & R1MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;
            t = (b >> 15) & L1MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;

            t = (b << 10) & L2MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;
            t = (b << 6) & R2MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;
            t = (b >> 10) & R2MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
////			ATTDEF( knight, i, t );
            t &= tm;
            bmob[ i ][ knight ] |= t;
            t = (b >> 6) & L2MASK;
            knatt[ i ] |= t;
            attm[ i ] |= t;
            atd |= t;
            ATTDEF( knight, i, atd );
            t &= tm;
            bmob[ i ][ knight ] |= t;
        }

        // bishop mobility (incl. attacks)
        for ( i=0; i<2; i++ )
        {
            b = bpiece[ i ][ bishop ] & ~pinMsk[ i ];
            if ( !b )
                continue;			// no bishops
            tm = ~cmask[ i ];
            tm2 = cmask[ i ^ 1 ];

            u64 atd = 0;

            // diagonal0
            for (t = b; t;)
            {
                t = ( t << 9 ) & L1MASK;
                atd |= t;
////				ATTDEF( bishop, i, t );
                attm[ i ] |= t;
                batt[ i ] |= t;
                t &= tm;
                bmob[ i ][ bishop ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal1
            for (t = b; t;)
            {
                t = ( t << 7 ) & R1MASK;
                atd |= t;
////				ATTDEF( bishop, i, t );
                attm[ i ] |= t;
                batt[ i ] |= t;
                t &= tm;
                bmob[ i ][ bishop ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal2
            for (t = b; t;)
            {
                t = ( t >> 9 ) & R1MASK;
                atd |= t;
////				ATTDEF( bishop, i, t );
                attm[ i ] |= t;
                batt[ i ] |= t;
                t &= tm;
                bmob[ i ][ bishop ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal3
            for (t = b; t;)
            {
                t = ( t >> 7 ) & L1MASK;
                atd |= t;
////				ATTDEF( bishop, i, t );
                attm[ i ] |= t;
                batt[ i ] |= t;
                t &= tm;
                bmob[ i ][ bishop ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }

            ATTDEF( bishop, i, atd );
        }

        // rook mobility (incl. attacks)
        for ( i=0; i<2; i++ )
        {
            b = bpiece[ i ][ rook ] & ~pinMsk[ i ];
            if ( !b )
                continue;			// no rooks
            tm = ~cmask[ i ];
            tm2 = cmask[ i ^ 1 ];

            u64 atd = 0;

            // horz0
            for (t = b; t;)
            {
                t = ( t << 1 ) & L1MASK;
                atd |= t;
////				ATTDEF( rook, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ rook ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // horz1
            for (t = b; t;)
            {
                t = ( t >> 1 ) & R1MASK;
                if ( bpiece[ i ][ rook ] & t )
                {
                    htwinRooks[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( rook, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ rook ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // vert0
            for (t = b; t;)
            {
                t <<= 8;
                if ( bpass[ black ] & t )
                {
                    // rook behind black passed pawn
                    rookPass[ i ]++;
                }
                if ( bpiece[ i ][ queen ] & t )
                {
                    twinRQueen[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( rook, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ rook ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // vert1
            for (t = b; t;)
            {
                t >>= 8;
                if ( bpass[ white ] & t )
                {
                    // rook behind white passed pawn
                    rookPass[ i ]++;
                }
                if ( bpiece[ i ][ rook ] & t )
                {
                    twinRooks[ i ] = 1;
                }
                if ( bpiece[ i ][ queen ] & t )
                {
                    twinRQueen[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( rook, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ rook ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            ATTDEF( rook, i, atd );
        }

        // queen mobility (incl. attacks)
        for ( i=0; i<2; i++ )
        {
            b = bpiece[ i ][ queen ] & ~pinMsk[ i ];
            if ( !b )
                continue;			// no queens
            tm = ~cmask[ i ];
            tm2 = cmask[ i ^ 1 ];

            u64 atd = 0;

            // diagonal0
            for (t = b; t;)
            {
                t = ( t << 9 ) & L1MASK;
                if ( bpiece[ i ][ bishop ] & t )
                {
                    twinQBishop[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal1
            for (t = b; t;)
            {
                t = ( t << 7 ) & R1MASK;
                if ( bpiece[ i ][ bishop ] & t )
                {
                    twinQBishop[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal2
            for (t = b; t;)
            {
                t = ( t >> 9 ) & R1MASK;
                if ( bpiece[ i ][ bishop ] & t )
                {
                    twinQBishop[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal3
            for (t = b; t;)
            {
                t = ( t >> 7 ) & L1MASK;
                if ( bpiece[ i ][ bishop ] & t )
                {
                    twinQBishop[ i ] = 1;
                }
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // horz0
            for (t = b; t;)
            {
                t = ( t << 1 ) & L1MASK;
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // horz1
            for (t = b; t;)
            {
                t = ( t >> 1 ) & R1MASK;
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // vert0
            for (t = b; t;)
            {
                t = ( t << 8 );
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // vert1
            for (t = b; t;)
            {
                t = ( t >> 8 );
                atd |= t;
////				ATTDEF( queen, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ queen ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            ATTDEF( queen, i, atd );
        }

        // king mobility (incl. attacks)
        for ( i=0; i<2; i++ )
        {
            tm = ~cmask[ i ];
            tm2 = cmask[ i ^ 1 ];
            b = bpiece[ i ][ king ];

            u64 atd = 0;
            u64 msk;
            /*			const PiecePosition &ekp = flags.kingPos[ i ^ 1 ];
            			u64 atm = ~attm[ i ^ 1 ] & ~kingMov[ (ekp.y << 3)|ekp.x ];*/
            const u64 atm = (u64)0 - 1;

            // diagonal0
            msk = L1MASK & atm;
            for (t = b, j=0; j<1; j++)
            {
                t = ( t << 9 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal1
            msk = R1MASK & atm;
            for (t = b, j=0; j<1; j++)
            {
                t = ( t << 7 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal2
            for (t = b, j=0; j<1; j++)
            {
                t = ( t >> 9 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // diagonal3
            msk = L1MASK & atm;
            for (t = b, j=0; j<1; j++)
            {
                t = ( t >> 7 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // horz0
            for (t = b, j=0; j<1; j++)
            {
                t = ( t << 1 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // horz1
            msk = R1MASK & atm;
            for (t = b, j=0; j<1; j++)
            {
                t = ( t >> 1 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // vert0
            msk = atm;
            for (t = b, j=0; j<1; j++)
            {
                t = ( t << 8 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            // vert1
            for (t = b, j=0; j<1; j++)
            {
                t = ( t >> 8 ) & msk;
                atd |= t;
////				ATTDEF( king, i, t );
                attm[ i ] |= t;
                t &= tm;
                bmob[ i ][ king ] |= t;
                if ( t & tm2 )
                {
                    break;
                }
            }
            ATTDEF( king, i, atd );
        }

        u64 tmob[ 2 ] = { 0, 0 };

        b = (bpiece[ black ][ rook ] | bpiece[ black ][ queen ]) & knatt[ white ];
        if ( b && bitCount( b ) > 1 )
        {
            // knight fork!
            wmat += (turn == white) ? 100 : 50;
        }

        b = (bpiece[ black ][ bishop ] | bpiece[ black ][ knight ] |
             bpiece[ black ][ rook ] | bpiece[ black ][ queen ]) & patt[ white ];
        if ( b && bitCount( b ) > 1 )
        {
            // pawn fork!
            wmat += (turn == white) ? 50 : 25;
        }

        b = (bpiece[ black ][ rook ] | bpiece[ black ][ queen ]) & batt[ white ];
        if ( b && bitCount( b ) > 1 )
        {
            // bishop fork!
            wmat += (turn == white) ? 50 : 25;
        }

        b = (bpiece[ white ][ rook ] | bpiece[ white ][ queen ]) & knatt[ black ];
        if ( b && bitCount( b ) > 1 )
        {
            // knight fork!
            bmat += (turn == black) ? 100 : 50;
        }

        b = (bpiece[ white ][ bishop ] | bpiece[ white ][ knight ] |
             bpiece[ white ][ rook ] | bpiece[ white ][ queen ]) & patt[ black ];
        if ( b && bitCount( b ) > 1 )
        {
            // pawn fork!
            bmat += (turn == black) ? 50 : 25;
        }

        b = (bpiece[ white ][ rook ] | bpiece[ white ][ queen ]) & batt[ black ];
        if ( b && bitCount( b ) > 1 )
        {
            // bishop fork!
            bmat += (turn == black) ? 50 : 25;
        }

        if ( bpiece[ white ][ knight ] & knatt[ white ] )
        {
            // self-guarding knights
            wmat += 10;
        }

        if ( bpiece[ black ][ knight ] & knatt[ black ] )
        {
            // self-guarding knights
            bmat += 10;
        }

        if ( bpiece[ white ][ bishop ] & patt[ white ] )
        {
            // self-guarding bishop-pawn
            wmat += 10;
        }

        if ( bpiece[ black ][ bishop ] & patt[ black ] )
        {
            // self-guarding bishop-pawn
            bmat += 10;
        }

#if 1
        if ( turn == black && (knatt[ white ] & bpiece[ black ][ king ]) )
        {
            // black king checked by white knight
            if ( knatt[ white ] & bpiece[ black ][ queen ] )
            {
                // dangerous - lose queen in next move
                bmat -= (matTab[ queen ] - matTab[ knight ])/1;
            }
            if ( knatt[ white ] & bpiece[ black ][ rook ] )
            {
                // dangerous - lose rook in next move
                bmat -= (matTab[ rook ] - matTab[ knight ])/1;
            }
            /*			if ( knatt[ white ] & (bpiece[ black ][ knight ] | bpiece[ black ][ bishop ] ) )
            			{
            				// dangerous - maybe lose knight/bishop in next move
            				bmat -= matTab[ bishop ]/4;
            			}*/
        }

        if ( turn == white && (knatt[ black ] & bpiece[ white ][ king ]) )
        {
            // black king checked by white knight
            if ( knatt[ black ] & bpiece[ white ][ queen ] )
            {
                // dangerous - lose queen in next move
                wmat -= (matTab[ queen ] - matTab[ knight ])/1;
            }
            if ( knatt[ black ] & bpiece[ white ][ rook ] )
            {
                // dangerous - lose rook in next move
                wmat -= (matTab[ rook ] - matTab[ knight ])/1;
            }
            /*			if ( knatt[ black ] & (bpiece[ white ][ knight ] | bpiece[ white ][ bishop ] ) )
            			{
            				// dangerous - maybe lose knight/bishop in next move
            				wmat -= matTab[ bishop ]/4;
            			}*/
        }
#endif

        u64 hangMask[2] = {0, 0};
        u64 attSafe[2][8];
        memset( attSafe, 0, sizeof(attSafe) );

//		for (i=0; i<2; i++)
        {
            for (j=0; j<64; j++)
            {
                u64 m = oneShlTab[ j ];

                i32 ii;
                for (ii=0; ii<2; ii++)
                {
                    attSafe[ ii ][ pawn ] |= m;
                    attSafe[ ii ][ king ] |= m;		// fixme: OK?!

                    if ( !(attPiece[ ii ^ 1 ][ j ] & ( 1 << pawn )) )
                    {
                        attSafe[ ii ][ knight ] |= m;
                        attSafe[ ii ][ bishop ] |= m;
                    }
                    if ( !(attPiece[ ii ^ 1 ][ j ] & (( 1 << pawn ) | ( 1 << knight ) | ( 1 << bishop ) ) ) )
                    {
                        attSafe[ ii ][ rook ] |= m;
                    }
                    if ( !(attPiece[ ii ^ 1 ][ j ] & (( 1 << pawn ) | ( 1 << knight ) | ( 1 << bishop ) | ( 1 << rook ) ) ) )
                    {
                        attSafe[ ii ][ queen ] |= m;
                    }
                }

                const Piece &p = pieces[j];
                if ( p.type == none || p.type == king )
                    continue;
                i32 c = p.color;
                i32 *pmat = (c == white) ? &wmat : &bmat;
                i32 *opmat = (c == white) ? &bmat : &wmat;

                static i32 hangTab[]=
                {
                    0, 1*10, 1*33, 1*33, 1*50, 1*98, 0, 0
                };
                static i32 phangTab[]=
                {
                    0, 10/5, 33/5, 33/5, 50/5, 98/5, 0, 0
                };
                i32 d = att[ c ][ j ];
                i32 ea = att[ c ^ 1 ][ j ];
                bool hanging = (attPiece[ c ^ 1 ][ j ] & ((1 << p.type ) - 1)) != 0;
                {
                    if ( hanging || ea > d || ea )
                    {
                        // penalize hanging piece
                        *pmat -= hangTab[ p.type ] >> (turn == c ? 1 : 0);
                        hangMask[ c ] |= oneShlTab[ j ];
                    }
                    else if ( (ea && ea == d) )
                    {
                        // penalize potentially hanging piece
                        *pmat -= phangTab[ p.type ] >> (turn == c ? 1 : 0);
                    }
                }
                if ( d )
                {
                    *pmat += 1 * d;
                }
                if ( ea )
                {
                    *opmat += 2 * ea;
                }
            }
        }
        if ( hangMsk )
        {
            *hangMsk |= hangMask[ white ] | hangMask[ black ];
        }

        for (i=0; i<2; i++)
        {
            i32 *pmat = (i == white) ? &wmat : &bmat;
            u64 unsafe = (bmob[ i ][ king ] & attm[ i ^ 1 ]);
            i32 uc = bitCount( unsafe );
            if ( uc )
            {
                // penalize (king not safe!)
                // (not in endgame)
                if ( stage == 0 )
                {
                    *pmat -= uc * 90;
                }
                else if ( stage == 1 )
                {
                    *pmat -= uc * 75;
                }
                else
                {
                    *pmat -= uc * 10;
                }
            }
            bmob[ i ][ king ] &= ~attm[ i ^ 1 ];
            u64 protp = cmask[ i ^ 1 ] & attm[ i ^ 1 ];
            bmob[ i ][ king ] &= ~protp;
            if ( i == turn && !bmob[ i ][ king ] )
            {
                // mate/stalemate threat
                mateThreat = true;
            }
            bool check = (bpiece[ i ][ king ] & attm[ i ^ 1 ]) != 0;
            if ( check && i == turn )
                inCheck = true;
            i32 cmob = 0;
            for (j=pawn; j<=king; j++)
            {
                bmob[ i ][ j ] &= attSafe[ i ][ j ];
                tmob[ i ] |= bmob[ i ][ j ];
                i32 bc = bitCount( bmob[ i ][ j ] );
                i32 rbc = bitCount( bmob[ i ][ j ] & attm[ i ^ 1 ] );
                if ( bc )
                {
                    if ( i == black )
                        bc = -bc;
                    cmob += bc * mobTab[ j ];
                    // reduce mobility if moving onto attacked square!
                    if ( rbc )
                    {
                        cmob -= bc/2 * mobTab[ j ];
                    }
                }
                else
                {
                    // zero piece mobility -- very dangerous for rooks in the opening
                    if ( j == rook )
                        *pmat -= 500;
                }
            }
            if ( mateThreat && i == turn && !inCheck && tmob[i] )
            {
                mateThreat = 0;
            }
            if ( check )
            {
                if ( bpiece[ i ][ king ] & knatt[ i ^ 1 ] )
                    cmob = cmob * 1/8;	// hint: when in check by knight, restrict mobility to 12.5%
                else
                    cmob = cmob * 1/4;	// hint: when in check, restrict mobility to 25%
////				printf("!");
            }//// else printf(".");
            mob += cmob;
        }
        if ( !tmob[ turn ] )
        {
            // potential stalemate or checkmate
            potVal = ( bpiece[ turn ][ king ] & attm[ turn ^ 1 ] ) ? mate/2 : 0;
            if ( turn == white )
                potVal = -potVal;
            potEnd = true;
            /*			if ( potVal )
            			{
            				printf(" === POTEND_HIT: %d === \n", potVal );
            			}*/
        }

#ifndef OLD_SAFETY
        if ( stage < 2 )
        {
            enum
            {
                scale = 1
            };
            enum
            {
                leftRight = 100*scale,			// left/right pawn
                front = 150*scale,				// front pawn
                front2 = 60*scale,				// front2 pawn
                emptyFront = 100*scale,			// empty file in front of king
                emptyClose = 50*scale,			// empty file close to king
                forceWindow = 75*scale			// force window
            };
            // new king safety
            i32 bc;

            i32 sfty;
            u8 kp = flags.kingPos[ white ];
            u64 pawnMask = bpiece[ white ][ pawn ] & ~hangMask[ white ];

            b = oneShlTab[ kp ];
            t = (b << 1) & L1MASK;
            t |= (b >> 1) & R1MASK;
            t &= pawnMask;
            sfty = bitCount( t ) * leftRight;			// left/right pawn
            b >>= 8;
            t = b | ((b << 1) & L1MASK);
            t |= (b >> 1) & R1MASK;
            t &= pawnMask;
            bc = bitCount( t );
            if ( !sfty && bc == 3 )
            {
                // penalize (force to make a 'window')
                sfty -= forceWindow;
            }
            sfty += bc * front;			// front pawns
            b >>= 8;
            t = b | ((b << 1) & L1MASK);
            t |= (b >> 1) & R1MASK;
            t &= pawnMask;
            sfty += bitCount( t ) * front2;				// front2 pawns

            i8 kpx = kp & 7;
            if ( !fpawns[ white ][ kp & 7 ] /*|| fpawns[ white ][ kp.x ] > 1*/ )
            {
                // empty file in front of king -- penalize!
                sfty -= emptyFront;
            }
            if ( kpx > 0 && !fpawns[ white ][ kpx - 1 ] /*|| fpawns[ white ][ kp.x - 1 ] > 1*/ )
            {
                // empty file close to king -- penalize!
                sfty -= emptyClose;
            }
            if ( kpx < 7 && !fpawns[ white ][ kpx + 1 ] /*|| fpawns[ white ][ kp.x + 1 ] > 1*/ )
            {
                // empty file close to king -- penalize!
                sfty -= emptyClose;
            }
            /*			if ( kp.x == 0 || kp.x == 7 )
            				sfty -= 100;					// king in corner -- penalize!*/

            wmat += 1*sfty;
            //		printf("w_sfty:%d\n", sfty);

            pawnMask = bpiece[ black ][ pawn ] & ~hangMask[ black ];

            kp = flags.kingPos[ black ];
            kpx = kp & 7;
            b = oneShlTab[ kp ];
            t = (b << 1) & L1MASK;
            t |= (b >> 1) & R1MASK;
            t &= pawnMask;
            sfty = bitCount( t ) * leftRight;			// left/right pawn
            b <<= 8;
            t = b | ((b << 1) & L1MASK);
            t |= (b >> 1) & R1MASK;
            t &= pawnMask;
            bc = bitCount( t );
            if ( !sfty && bc == 3 )
            {
                // penalize (force to make a 'window')
                sfty -= forceWindow;
            }
            sfty += bc * front;			// front pawns
            b <<= 8;
            t = b | ((b << 1) & L1MASK);
            t |= (b >> 1) & R1MASK;
            t &= pawnMask;
            sfty += bitCount( t ) * front2;				// front2 pawns

            if ( !fpawns[ black ][ kpx ] /*|| fpawns[ black ][ kp.x ] > 1*/ )
            {
                // empty file in front of king -- penalize!
                sfty -= emptyFront;
            }
            if ( kpx > 0 && !fpawns[ black ][ kpx - 1 ] /*|| fpawns[ black ][ kp.x - 1 ] > 1*/ )
            {
                // empty file close to king -- penalize!
                sfty -= emptyClose;
            }
            if ( kpx < 7 && !fpawns[ black ][ kpx + 1 ] /*|| fpawns[ black ][ kp.x + 1 ] > 1*/ )
            {
                // empty file close to king -- penalize!
                sfty -= emptyClose;
            }
            /*			if ( kp.x == 0 || kp.x == 7 )
            				sfty -= 100;					// king in corner -- penalize!*/

            bmat += 1*sfty;
            //		printf("b_sfty:%d\n", sfty);
        }
#endif

        /*		wmat += 10 * attc[ white ];
        		bmat += 10 * attc[ black ];*/

        // extra passed pawn on blank file bonus

        const i32 ppbonusScale = 2;			// 50%

        static i32 ppbonus[] = {
            800, 400, 200, 100, 50, 25, 13, 7, 0
        };

        b = bpass[ white ];
        t = 0;
        for (i=0; b && i<8; i++)
        {
            b = (b >> 8) & ~mask;
            t |= b & 0x00000000000000ff;
            if ( (u8)b )
            {
                // passed pawn distance to end bonus
                wmat += ppbonus[ i ] / ppbonusScale;
            }
        }
        enum {
            passPawnBase = 150
        };
        wmat += bitCount( t ) * passPawnBase;
        b = bpass[ black ];
        t = 0;
        for (i=0; b && i<8; i++)
        {
            b = (b << 8) & ~mask;
            t |= b & U64C( 0xff00000000000000 );
            if ( (u8)(b >> (64-8)) )
            {
                // passed pawn distance to end bonus
                bmat += ppbonus[ i ] / ppbonusScale;
            }
        }
        bmat += bitCount( t ) * passPawnBase;

        if ( stage == 2 )
        {
            // passed pawn distance bonus
            static i32 passTab[] = {
                0, 35/4, 30/4, 25/4, 20/4, 15/4, 10/4, 5/4, 0
            };
            // passed pawn penalty
            static i32 passPenTab[] = {
                0, 13, 25, 38, 50, 63, 75, 88, 100
            };
            // extra bonus/penalty (king distance from passed pawns, endgame only!)
            {
                i32 i;
                i32 *pmat[2] = { &wmat, &bmat };
                for (i=0; i<2; i++)
                {
                    // i = white/black
                    if ( (b = bpass[ i ]) != 0 )
                    {
                        // extract...
                        i32 j;
                        // i = white/black
                        for ( j=0; b && j<64; j++, b >>= 1 )
                        {
                            if ( !((u8)b) )
                            {
                                b >>= 7;
                                j += 7;
                                continue;
                            }
                            if ( b & 1 )
                            {
                                // bonus (distance to own passed pawn)
                                i32 d = distTab[ kpos[ i ] ][ j ];
                                if ( turn != i )
                                    d++;
                                (*pmat[i]) += passTab[ d ] << stage;
                                // penalty (distance to enemy passed pawn)
                                d = distTab[ kpos[ i ^ 1 ] ][ j ];
                                if ( turn == i )
                                    d++;
                                if ( stage >= 2 )
                                {
                                    // see if can_intercept (endgame only!)
                                    // black pawns reach bottom
                                    i32 rank = j >> 3;
                                    i32 pd = 0;				// promotion_distance
                                    if ( i == black )
                                    {
                                        pd = 7 - rank;
                                    }
                                    else
                                    {
                                        pd = rank;
                                    }
                                    if ( d >= pd )
                                    {
                                        // can't intercept!
                                        (*pmat[i ^ 1]) -= 600;
                                        if ( !minor[ i ^ 1 ] && !major[ i ^ 1 ] )
                                        {
                                            // no minor/major piece to prevent passed pawn from promoting
                                            (*pmat[i ^ 1]) -= matTab[ queen ] - matTab[ pawn ];
                                        }
                                    }
                                }
                                (*pmat[i ^ 1]) -= passPenTab[ d ] << stage;
                            }
                        }
                    }
                }
            }
        }
    }

    wmat += rookPass[ white ] * 250;
    bmat += rookPass[ black ] * 250;

    if ( twinRooks[ white ] )
    {
        wmat += 25;
    }
    if ( twinRooks[ black ] )
    {
        bmat += 25;
    }

    if ( htwinRooks[ white ] )
    {
        wmat += 10;
    }
    if ( htwinRooks[ black ] )
    {
        bmat += 10;
    }

    if ( twinRQueen[ white ] )
    {
        wmat += 5;
    }
    if ( twinRQueen[ black ] )
    {
        bmat += 5;
    }

    if ( twinQBishop[ white ] )
    {
        wmat += 10;
    }
    if ( twinQBishop[ black ] )
    {
        bmat += 10;
    }

    mat = wmat - bmat + mob;	//(4*(wmat - bmat + mob) + 2*(orgmat + 0*mob))/6;
    if ( potEnd )
    {
        mat = (mat + potVal) / 2;
    }

    if ( dmat != 0 )
    {
        // tempo/capture balance help...
        if ( turn == white && dmat > 0 )
        {
            // penalize white
            mat -= 5;
            mat -= flags.fiftyCounter*8;
        }
        else if ( turn == black && dmat < 0 )
        {
            // penalize black
            mat += 5;
            mat += flags.fiftyCounter*8;
        }
    }
    /*	if ( inCheck )
    	{
    		if ( turn == white )
    		{
    			// penalize white
    			mat -= 5;
    		}
    		else
    		{
    			// penalize black
    			mat += 5;
    		}
    	}*/

    bool ismate = false;
    bool isdraw = false;
    if ( mateThreat )
    {
        Move m[ maxMoves ];
        i32 nm = moves( m, turn, useMoveHash | noSort );
        if ( !nm )
        {
            sf |= 4;			// spec flag: done_quiesce!
            if ( inCheck )
            {
                ismate = true;
                // mate
                if ( turn == white )
                {
                    mat = -mate / 2;	//(mat - mate) / 2;
                }
                else
                {
                    mat = +mate / 2;	//(mat + mate) / 2;
                }
            }
            else
            {
                // stalemate
                isdraw = true;
////				mat /= 8;				// new: to avoid easy draws
                mat = 0;
            }
        }
    }

    // attractiveness
    i32 attr = this->stage()/8;

    if ( !ismate && !isdraw )
    {
        if ( turn == white )
            mat += attr;
        else
            mat -= attr;

        if ( stage >= 2 )
        {
            bool lw = loneKing[ white ];
            bool lb = loneKing[ black ];
            if ( /*specFlags &&*/ (lb || lw) )
            {
                if ( specFlags )
                    *specFlags |= 2;				// lone king(s) => no qs!
                sf |= 2;
            }
            if ( !lw || !lb )
            {
                if ( dmat < 0 && lw )
                {
                    // bonus for king_proximity to enemy king
                    mat -= kingMateTab[ distTab[ kpos[white] ][ kpos[black] ] ];
                    mat += kingCenterTab[ kpos[white] ] * 100;
                    if ( matingMat[black] && !(cmask[black] & (
                                                   kingMov[ kpos[white] ] & ~kingMov[ kpos[black] ]) ) )
                    {
                        // this is a position we can win
                        mat -= mate/4;
                    }
                }
                if ( dmat > 0 && lb )
                {
                    // bonus for king_proximity to enemy king
                    mat += kingMateTab[ distTab[ kpos[white] ][ kpos[black] ] ];
                    mat -= kingCenterTab[ kpos[black] ] * 100;
                    if ( matingMat[white] && !(cmask[white] & (
                                                   kingMov[ kpos[black] ] &~ kingMov[ kpos[white] ]) ) )
                    {
                        // this is a position we can win
                        mat += mate/4;
                    }
                }
            }
            if ( (lw || lb) && (tmat <= tmatTab[ knight ] + tmatTab[ bishop ]) )
            {
                // eval knbk endgame
                if ( 1/*specFlags*/ )
                {
                    sf |= 1;
                    if ( specFlags )
                        *specFlags |= 1;
                }
                if ( lb && bishopCount[ white ] == 1 && knightCount[ white ] == 1 )
                {
                    // black is a lone king
                    // must deliver mate in the corner of the same color as white's bishop
                    bool blackCorner = !colorBishops[ white ][ white ];
                    mat += knbTable[ kpos[black] ] * (blackCorner ? -2 : +2);
                }
                else
                    if ( lw && bishopCount[ black ] == 1 && knightCount[ black ] == 1 )
                    {
                        // white is a lone king
                        // must deliver mate in the corner of the same color as black's bishop
                        bool blackCorner = !colorBishops[ black ][ white ];
                        mat -= knbTable[ kpos[white] ] * (blackCorner ? -2 : +2);
                    }
            }
        }

        // endgame => final eval better for me?
        // lone king, king + bishop, king + knight
        // note: see stamma's mate (wiki)
        if ( !ismate && stage >= 2 && ( tmat <= tmatTab[ bishop ] || tmat <= tmatTab[ knight ] ) )
        {
            if ( !hasPawns[white] && mat > 0 )
            {
                // white has no pawns and because of low material conditions can't win!
                mat = -100;
            }
            if ( !hasPawns[black] && mat < 0 )
            {
                // black has no pawns and because of low material conditions can't win!
                mat = +100;
            }
        }

//		mat += (-2 + ((rand() & 255) % 5));

    }

    if ( turn == black )
        mat = -mat;

    if ( !ismate && tmat == (tmatTab[rook]<<1) && !hasPawns[white] && !hasPawns[black] && rookCount[white] == 1 && rookCount[black] == 1 )
    {
        mat = 0;			// a trick to force to end KRKR endgame
    }

#ifdef CHENG_MT
    evalMutex.lock();
#endif
    evalHash[ hi ] = h;
    evalVal[ hi ] = mat;
    evalSpec[ hi ] = sf | (turn == black ? 0x80 : 0);
#ifdef CHENG_MT
    evalMutex.unlock();
#endif

    return mat; //wmat - bmat;	//mat;
}

void Board::dump( String *st ) const
{
    i32 i, j;
    const char *bp = " pnbrqk";
    const char *wp = " PNBRQK";

#define printf logFmt

    for (i=0; i<8; i++)
    {
        printf("   ");
        if ( st )
            *st += "   ";
        for (j=0; j<8; j++)
        {
            printf( ((i+j) & 1) ? "       " : "#######" );
            if ( st )
            {
                *st += ((i+j) & 1) ? "       " : "#######";
            }
        }
        printf("\n");
        if ( st )
            *st += "\n";
        printf(" %d ", 1 + (i ^ 7));
        if ( st )
        {
            String tmp;
            tmp.format(" %d ", 1 + (i ^ 7));
            *st += tmp;
        }
        for (j=0; j<8; j++)
        {
            if ( piece[i][j].type == none )
            {
                printf( ((i+j) & 1) ? "       " : "#######" );
                if ( st )
                {
                    *st += ((i+j) & 1) ? "       " : "#######";
                }
            }
            else
            {
                printf( ((i+j) & 1) ? "  " : "##" );
                if ( st )
                {
                    *st += ((i+j) & 1) ? "  " : "##";
                }
                printf( " %c ", (piece[i][j].color == white ? wp : bp)[ piece[i][j].type ] );
                if ( st )
                {
                    String tmp;
                    tmp.format( " %c ", (piece[i][j].color == white ? wp : bp)[ piece[i][j].type ] );
                    *st += tmp;
                }
                printf( ((i+j) & 1) ? "  " : "##" );
                if ( st )
                {
                    *st += ((i+j) & 1) ? "  " : "##";
                }
            }
        }
        printf("\n");
        if ( st )
            *st += "\n";
        printf("   ");
        if ( st )
            *st += "   ";
        for (j=0; j<8; j++)
        {
            printf( ((i+j) & 1) ? "       " : "#######" );
            if ( st )
            {
                *st += ((i+j) & 1) ? "       " : "#######";
            }
        }
        printf("\n");
        if ( st )
            *st += "\n";
    }
    printf("\n   ");
    if ( st )
        *st += "\n   ";
    for (j=0; j<8; j++)
    {
        printf( "   %c   ", 'a' + j );
        if ( st )
        {
            String tmp;
            tmp.format( "   %c   ", 'a' + j );
            *st += tmp;
        }
    }
    printf("\n\n");
    if ( st )
        *st += "\n\n";

#undef printf

}

// returns castling rights for a player
// 0 = no castling rights
// otherwise:
// lo-nybble: long castling rights (1 = A .. 8 = 'H')
// hi-nybble: short castling rights (1 = A .. 8 = 'H')
i32 Board::castlingRights( i32 who ) const
{
    if ( (u32)who > 1 )
        return 0;
    if ( flags.castled & (1<<who) )
        return 0;
    u8 kp = flags.kingPos[ who ];
    if ( pieces[ kp ].moved )
        return 0;				// king moved => no rights!
    const Piece &rl = piece[ (who == white) ? 7 : 0 ][ 0 ];
    const Piece &rs = piece[ (who == white) ? 7 : 0 ][ 7 ];
    i32 res = 0;
    if ( rl.type == rook && rl.color == who && !rl.moved )
    {
        res |= 0x1;
    }
    if ( rs.type == rook && rs.color == who && !rs.moved )
    {
        res |= 0x80;
    }
    return res;
}

// get FEN for current position
void Board::getFEN( char *fen, i32 turn ) const
{
    const char *bp = " pnbrqk";
    const char *wp = " PNBRQK";
    i32 i;
    i32 skip = 0;
    i32 x, y;
    for (y=0; y<8; y++)
    {
        i32 skp = 0;			// skipfile_count
        for (x=0; x<8; x++)
        {
            const Piece &p = piece[y][x];
            if ( p.type == none )
            {
                skp++;
            }
            else
            {
                if ( skp )
                {
                    *fen++ = '0' + skp;
                    skp = 0;
                }
                if ( p.color == white )
                {
                    *fen++ = wp[ p.type ];
                }
                else
                {
                    *fen++ = bp[ p.type ];
                }
            }
        }
        if ( skp )
        {
            *fen++ = '0' + skp;
        }
        if ( y != 7 )
            *fen++ = '/';
    }
    *fen++ = 32;
    *fen++ = (turn == white) ? 'w' : 'b';
    *fen++ = 32;
    // here come castling rights:
    // KQkq = default!
    i32 cw, cb;
    cw = castlingRights( white );
    cb = castlingRights( black );

    if ( cw || cb )
    {
        if ( cw )
        {
            i32 ln = cw & 15;
            i32 sh = cw >> 4;
            if ( sh == 8 )
                *fen++ = 'K';
            else if ( sh )
                *fen++ = 'A' + sh - 1;
            if ( ln == 1 )
                *fen++ = 'Q';
            else if ( ln )
                *fen++ = 'A' + ln - 1;
        }
        if ( cb )
        {
            i32 ln = cb & 15;
            i32 sh = cb >> 4;
            if ( sh == 8 )
                *fen++ = 'k';
            else if ( sh )
                *fen++ = 'a' + sh - 1;
            if ( ln == 1 )
                *fen++ = 'q';
            else if ( ln )
                *fen++ = 'a' + ln - 1;
        }
    }
    else
        *fen++ = '-';
    *fen++ = 32;
    // enpassant square
    if ( flags.ep )
    {
        u8 fep = flags.ep;
        if ( fep < 32 )
        {
            fep -= 8;
        }
        else
        {
            fep += 8;
        }
        *fen++ = 'a' + (fep & 7);
        *fen++ = '8' - (fep >> 3);
    }
    else
        *fen++ = '-';
    *fen++ = 32;
    *fen = 0;
    // 50 rule counter
    char buf[32];
    sprintf(buf, "%d", flags.fiftyCounter );
    strcat( fen, buf );
    // next should be move number...
}

// parse FEN position
// out: true on success
bool Board::parseFEN( const char *fen, i32 *turn, i32 *movnum )
{
    if ( turn )
        *turn = white;
    if ( movnum )
        *movnum = 1;
    if ( !fen )
        return false;
    i32 trn = white;
    memset( pieces, 0, sizeof(pieces) );
    flags.packed = 0;
    flags.lastMoved = 255;
    i32 x = 0, y = 0;
    Piece tr;
    Board backup( *this );
    while ( fen && *fen )
    {
        if ( *fen <= 32 )
        {
            fen++;
            continue;				// skip blanks
        }
        Piece &p = (!(y & ~7) && !(x & ~7)) ? piece[ y ][ x ] : tr;
        switch ( *fen )
        {
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
            x += *fen - '0';
            break;
        case 'p':
            p.type = pawn;
            p.color = black;
            x++;
            break;
        case 'n':
            p.type = knight;
            p.color = black;
            x++;
            break;
        case 'b':
            p.type = bishop;
            p.color = black;
            x++;
            break;
        case 'r':
            p.type = rook;
            p.color = black;
            p.moved = ( (x != 0 && x != 7) || y != 0 );
            x++;
            break;
        case 'q':
            p.type = queen;
            p.color = black;
            x++;
            break;
        case 'k':
            p.type = king;
            p.color = black;
            p.moved = ( x != 4 || y != 0 );
            // auto black-castled flags (hack)
            flags.castled |= (y == 0 && (x == 2 || x == 6) ) ? 1 << black : 0;
            x++;
            break;
        case 'P':
            p.type = pawn;
            p.color = white;
            x++;
            break;
        case 'N':
            p.type = knight;
            p.color = white;
            x++;
            break;
        case 'B':
            p.type = bishop;
            p.color = white;
            x++;
            break;
        case 'R':
            p.type = rook;
            p.color = white;
            p.moved = ( (x != 0 && x != 7) || y != 7 );
            x++;
            break;
        case 'Q':
            p.type = queen;
            p.color = white;
            x++;
            break;
        case 'K':
            p.type = king;
            p.color = white;
            p.moved = ( x != 4 || y != 7 );
            // auto white-castled flags (hack)
            flags.castled |= (y == 7 && (x == 2 || x == 6) ) ? 1 << white : 0;
            x++;
            break;
        case '/':
            x = 0;
            y++;
            break;
        default:
            *this = backup;
            return false;
        }
        fen++;
        if ( y >= 8 || (y == 7 && x >= 8) )
            break;
    }
    while ( *fen && (*fen == '/' || *fen <= 32) )
        fen++;
    // ready to parse turn
    trn = (*fen | 32) == 'b' ? black : white;
    if ( *fen )
        fen++;

    if ( turn )
        *turn = trn;

    while ( *fen && *fen <= 32 )
        fen++;

    // ready to parse castling rights...
    // (hack: determined automatically -- just skip!)
	i32 cr[2] = {0, 0};
    while ( *fen && (*fen|32) >= 'a' && (*fen|32) <= 'q' )
	{
		if ( *fen == 'K' || *fen == 'H' )
		{
			cr[white] |= 1;
		}
		else if ( *fen == 'Q' || *fen == 'A' )
		{
			cr[white] |= 2;
		}
		else if ( *fen == 'k' || *fen == 'h' )
		{
			cr[black] |= 1;
		}
		else if ( *fen == 'q' || *fen == 'a' )
		{
			cr[black] |= 2;
		}
        fen++;
	}
    if ( *fen == '-' )
	{
		cr[0] = cr[1] = 0;
        fen++;
	}

	if ( !(cr[white] & 1) )
	{
		// white can't castle short
		pieces[ H1 ].moved = 1;
	}
	if ( !(cr[white] & 2) )
	{
		// white can't castle long
		pieces[ A1 ].moved = 1;
	}

	if ( !(cr[black] & 1) )
	{
		// black can't castle short
		pieces[ H8 ].moved = 1;
	}
	if ( !(cr[white] & 2) )
	{
		// black can't castle long
		pieces[ A8 ].moved = 1;
	}

	// new: update castled flags
	if ( !cr[white] )
	{
		flags.castled |= 1;
	}
	if ( !cr[black] )
	{
		flags.castled |= 2;
	}

    while ( *fen && *fen <= 32 )
        fen++;

    if ( *fen == '-' )
    {
        // no ep
        flags.ep = 0;
        fen++;
    }
    else
    {
        if ( (*fen|32) >= 'a' && (*fen|32) <= 'h' )
        {
            flags.ep |= (*fen|32) - 'a';
            fen++;
            if ( *fen < '1' || *fen > '8' )
                flags.ep = 0;
            else
            {
                flags.ep |= ('8' - *fen) << 3;
                // fix fen ep square
                if ( flags.ep < 32 )
                {
                    flags.ep += 8;
                }
                else
                {
                    flags.ep -= 8;
                }
                // TODO: validate ep square!
                fen++;
            }
        }
    }

    while ( *fen && *fen <= 32 )
        fen++;
    // parse fifty move rule counter!
    if ( *fen >= '0' && *fen <= '9' )
    {
        char *endp = (char *)fen;
        flags.fiftyCounter = strtol( fen, &endp, 10 );
        fen = endp;
    }

    if ( movnum )
    {
        // parse move number
        while ( *fen && *fen <= 32 )
            fen++;

        // parse fifty move rule counter!
        if ( *fen >= '0' && *fen <= '9' )
        {
            char *endp = (char *)fen;
            *movnum = strtol( fen, &endp, 10 );
            fen = endp;
        }
    }

    update( trn );

    curHash ^= (posHash)zobristEP[ flags.ep ];
    return true;
}

// validate board
// returns 0 if ok, otherwise ValidError condition
// optionally returns position index of where error was detected (-1 => nowhere/unknown)
i32 Board::validate( i32 turn, i32 *pos ) const
{
    turn &= 1;
    i32 i;
    if ( pos )
        *pos = -1;
    i32 kings[2] = {0, 0};
    i32 pawnc[2] = {0, 0};
    for (i=0; i<64; i++)
    {
        const Piece &p = pieces[ i ];
        if ( p.type == pawn )
        {
            if ( (i>>3) == 0 || (i>>3) == 7 )
            {
                if ( pos )
                    *pos = i;
                return vePawn;
            }
            if ( ++pawnc[ p.color ] > 8 )
            {
                if ( pos )
                    *pos = i;
                return veManyP;
            }
        }
        if ( p.type == king )
        {
            if ( ++kings[ p.color ] > 1 )
            {
                if ( pos )
                    *pos = i;
                return veManyK;
            }
        }
    }
    if ( !kings[ white ] || !kings[ black ] )
    {
        return veKMiss;
    }
    u8 pp = flags.kingPos[ turn ^ 1 ];
    if ( check( pp ) )
    {
        if ( pos )
        {
            *pos = pp;
            return veOppCheck;
        }
    }
    if ( flags.ep )
    {
        i32 y = flags.ep >> 3;
        if ( y < 3 || y > 4 )
        {
            if ( pos )
                *pos = flags.ep;
            return veBadEP;
        }
        const Piece &p = pieces[ flags.ep ];
        if ( p.type != pawn || p.color == turn )
        {
            if ( pos )
                *pos = flags.ep;
            return veBadEP;
        }
    }
    // TODO: badcast
    if ( flags.fiftyCounter > fiftyCounterMax )
    {
        return veBadFifty;
    }
    return veOk;
}

// parse SAN move
// returns true if ok
const char *Board::parseSAN( const char *c, i32 turn, Move &outm, String *desc, i32 *NAG ) const
{
    if ( desc )
    {
        desc->clear();
    }
    if ( NAG )
        *NAG = 0;
    if ( !c || !*c )
        return 0;
    i32 piece = pawn;		// piece to move
    i32 fx = -1;
    i32 fy = -1;
    i32 tx = -1;
    i32 ty = -1;
    i32 promo = none;		// promote to
    i32 index = 0;			// 0 = parsing fx, 1 = parsing fy, 2 = parsing tx, 3 = parsing ty
    switch ( *c )
    {
    case 'N':
        piece = knight;
        c++;
        break;
    case 'B':
        piece = bishop;
        c++;
        break;
    case 'R':
        piece = rook;
        c++;
        break;
    case 'Q':
        piece = queen;
        c++;
        break;
    case 'K':
        piece = king;
        c++;
        break;
    }
    if ( !*c )
        return 0;

    Move m[ maxMoves ];
    i32 vm = moves( m, turn );
    if ( !vm )
        return 0;					// no legal moves

    bool castling = false;
    bool longc = false;
    if ( c[0] == '0' && c[1] == '-' && c[2] == '0' )
    {
        // stupid illegal castling -- but accept
        castling = true;
        c += 3;
        if ( c[0] == '-' && c[1] == '0' )
        {
            c += 2;
            longc = true;
        }
    }
    if ( *c == 'O' )
    {
        // castling move
        castling = true;
        c++;
        if ( *c != '-' )
            return 0;
        c++;
        if ( *c != 'O' )
            return 0;
        c++;
        if ( *c == '-' )
        {
            longc = true;
            c++;
            if ( *c != 'O' )
                return 0;
            c++;
        }
    }
    else
    {
        // non-castling move
        if ( *c >= '1' && *c <= '8' )
        {
            // fy
            fy = '8' - *c;
            index = 2;
            c++;
        }
        else if ( *c >= 'a' && *c <= 'h' )
        {
            // fx
            fx = *c - 'a';
            index = 1;
            c++;
            if ( *c >= '1' && *c <= '8' )
            {
                fy = '8' - *c;
                index = 2;
                c++;
            }
        }
        if ( *c == 'x' || *c == '-' )
        {
            c++;				// ignore (capture)
        }
        if ( *c >= '1' && *c <= '8' )
        {
            // ty
            ty = '8' - *c;
            index = 4;
            c++;
        }
        else if ( *c >= 'a' && *c <= 'h' )
        {
            // tx
            tx = *c - 'a';
            index = 3;
            c++;
            if ( *c >= '1' && *c <= '8' )
            {
                ty = '8' - *c;
                index = 4;
                c++;
            }
        }
        if ( piece == pawn && (*c == '=' || *c == 'N' || *c == 'B' || *c == 'R' || *c == 'Q') )
        {
            if ( *c == '=' )
            {
                c++;
            }
            switch ( *c )
            {
            case 'N':
                promo = knight;
                c++;
                break;
            case 'B':
                promo = bishop;
                c++;
                break;
            case 'R':
                promo = rook;
                c++;
                break;
            case 'Q':
                promo = queen;
                c++;
                break;
            default:
                return 0;
            }
        }
        if ( *c == '+' )
        {
            c++;
            if (*c == '+')
            {
                // FIXME: doublecheck unsupported by pgn?!
                c++;
            }
        }
        if ( *c == '#' )
        {
            c++;
        }
    }
    // now parse move description
    const char *nc = c;
    if ( *c == 32 || *c == 9 )
    {
        nc = c+1;
    }
    i32 nag = 0;
    if ( *nc == '!' || *nc == '?' )
    {
        if ( nc[1] == '!' || nc[1] == '?' )
        {
            if ( desc )
                *desc = String( nc, nc+2 );
            c = nc + 2;
            if ( nc[0] == '!' && nc[1] == '!' )
                nag = 3;
            else if ( nc[0] == '!' && nc[1] == '?' )
                nag = 4;
            else if ( nc[0] == '?' && nc[1] == '!' )
                nag = 5;
            else if ( nc[0] == '?' && nc[1] == '?' )
                nag = 6;
        }
        else
        {
            nag = *nc == '!' ? 1 : 2;
            if ( desc )
                *desc = String( nc, nc+1 );
            c = nc + 1;
        }
    }
    else if ( *nc == '$' )
    {
        // parse NAG...
        char *cc = 0;
        nag = strtol( nc + 1, &cc, 10 );
        if ( cc && cc > nc + 1 )
        {
            if ( desc )
            {
                switch ( nag )
                {
                case 1:
                    *desc = "!";
                    break;
                case 2:
                    *desc = "?";
                    break;
                case 3:
                    *desc = "!!";
                    break;
                case 4:
                    *desc = "!?";
                    break;
                case 5:
                    *desc = "?!";
                    break;
                case 6:
                    *desc = "?!";
                    break;
                }
            }
        } else nag = 0;
    }
    if ( NAG )
        *NAG = nag;
    // fix fx, fy, tx, ty
    if ( tx < 0 && ty < 0 )
    {
        tx = fx;
        ty = fy;
        fx = fy = -1;
    }
    // now try to find a move!
    i32 i;
    i32 mcount = 0;
    i32 found = -1;
    if ( castling )
        piece = king;
    for (i=0; i<vm; i++)
    {
        if ( m[i].promo != promo )
            continue;
        const Piece &p = this->pieces[ m[i].from ];
        if ( p.color != turn || p.type != piece )
            continue;
        if ( castling )
        {
            char buf[32];
            m[i].describeSAN( turn, *this, buf );
            if ( strcmp( buf, "O-O-O" ) == 0 && longc )
            {
                found = i;
                mcount++;
                continue;
            }
            else if ( strcmp( buf, "O-O" ) == 0 && !longc )
            {
                found = i;
                mcount++;
                continue;
            }
            else continue;
        }
        i32 mfx = m[i].from & 7;
        i32 mfy = m[i].from >> 3;
        i32 mtx = m[i].to & 7;
        i32 mty = m[i].to >> 3;
        if ( fx >= 0 && mfx != fx )
            continue;
        if ( fy >= 0 && mfy != fy )
            continue;
        if ( tx >= 0 && mtx != tx )
            continue;
        if ( ty >= 0 && mty != ty )
            continue;
        found = i;
        mcount++;
    }
    if ( found < 0 )
        return 0;
    if ( mcount > 1 )			// ambiguous move found!
        return 0;
    outm = m[ found ];
    return c;
}
