#include "core/core.h"
#include "bittables.h"
#include "bitboard.h"
#include "board.h"
#include "zobrist.h"
#include "piece.h"
#include "bitsearch.h"
#include "bitmagic.h"

#include "bitconfig.h"
#include <string.h>
#include <stdio.h>

// FIXME: forgot that a rook capture can also lose castling rights!

//#define DEBUG_ISCHECK					// debug ischeck routine
//#define DEBUG_VALID_MOVE
//#define DEBUG_MOVES
//#define DEBUG_IHASH					// debug incremental board_hashing (CRUCIAL in order to work properly!)
//#define DEBUG_CHECK						// debug (=validate) that the current check flag matches
//#define DEBUG_VALID_BOARD				// validate board (pieces vs indices)

//#define OLD_ISCHECK						// old_ischeck (new seems buggy?!)

// use static exchange evaluator
#define USE_SEE
// use randomized ordering of unsorted moves before sorting
//#define RANDOMIZE_SCORE

#if defined(DEBUG) || defined(_DEBUG)
//#define DEBUG_ALL
#endif

const i32 tmatTab[] = {
    0, 0, 325, 325, 500, 975, 0, 0
};

i32 gmatTab[] = {
    0, 75/*100*/, 325, 325, 500, 975, 0, 0
};

// BitMove

void BitMove::describeUCI( char *buf ) const
{
    if ( !buf )
        return;
    buf[0] = 'a' + (from & 7);
    buf[1] = '8' - (from >> 3);
    buf[2] = 'a' + (to & 7);
    buf[3] = '8' - (to >> 3);
    buf[4] = 0;
    if ( promo )
    {
        const char *promot = "  nbrq   ";
        buf[4] = promot[ promo ];
        buf[5] = 0;
    }
}

void BitMove::describeSAN( const BitBoard &b, char *buf, bool checks ) const
{
    BitMove m = *this;
    i32 turn = b.flags().turn;

    if ( m.flags & bcastling )
    {
        if ( m.flags & blongcastling )
        {
            strcpy( buf, "O-O-O");
        }
        else
        {
            strcpy( buf, "O-O");
        }
        return;
    }

    BitMove mv[ bmaxMoves ];
    i32 nm = b.moves( mv );
    i32 i;
    for ( i = pawn; i < king; i++ )
    {
        if ( b.bits[ INDEX( turn, i ) ] & ONESHL( m.from ) )
            break;
    }

    i32 ptype = i;
    const char *prefix = "  NBRQK ";
    char pr = prefix[ ptype ];
    char *ch = buf;
    if ( pr != 32 )
        *ch++ = pr;

    i32 startx = 0, starty = 0;
    for (i=0; i<nm; i++)
    {
        if ( m.equal( mv[i] ) )
        {
            if ( m.promo != mv[i].promo )
                continue;
            m = mv[i];
            continue;
        }
        i32 type = b.pieceType( mv[i].from );
        if ( type != ptype )
            continue;
        if ( mv[i].to != m.to )
            continue;
        if ( (mv[i].from & 7) != (m.from & 7) )
            startx++;
        else if ( (mv[i].from >> 3) != (m.from >> 3) )		// TODO/FIXME: is this else ok?!
            starty++;
    }
    if ( !startx && !starty && pr == 32 && (m.flags & bcapture) )
        startx = 1;			// because of pawns
    if ( startx )
    {
        *ch++ = 'a' + (m.from & 7);
    }
    if ( starty )
        *ch++ = '8' - (m.from >> 3);
    if ( m.flags & bcapture )
        *ch++ = 'x';
    *ch++ = 'a' + (m.to & 7);
    *ch++ = '8' - (m.to >> 3);

    if ( ptype == pawn && m.promo )
    {
        *ch++ = '=';
        *ch++ = prefix[ m.promo ];
    }

    if ( checks )
    {
        BitBoard tb( b );
        tb.move( m );
        u8 kp = tb.flags().kingPos[ turn ^ 1 ];
        if ( tb.attacked( kp, turn ^ 1 ) )
        {
            nm = tb.moves( mv );
            *ch++ = nm ? '+' : '#';
        }
    }

    *ch = 0;
}

// BitBoard

u64 swapRows( const u64 &val )
{
    u64 res = 0;
    i32 i;
    for (i=0; i<8; i++)
    {
        u8 tmp = (val >> (i<<3) );
        /*		// byteswap-val!
        		i32 j;
        		u8 tmp2 = 0;
        		for (j=0; j<8; j++)
        		{
        			tmp2 |= ((tmp >> j) & 1) << (7-j);
        		}*/
        u8 tmp2 = tmp;
        res += (u64)tmp2 << ((i ^ 7) << 3);
    }
    return res;
}

void BitBoard::swap()
{
    i32 i;
    for (i=pawn; i<=king; i++)
    {
        u64 w, b;
        w = bits[ INDEX(white, i ) ];
        b = bits[ INDEX(black, i ) ];
        bits[ INDEX(white, i ) ] = swapRows( b );
        bits[ INDEX(black, i ) ] = swapRows( w );
    }
    // update flags!
    BitFlags &f = flags();
    if ( f.ep )
    {
        f.ep ^= 0x38;
    }
    std::swap( f.kingPos[ white ], f.kingPos[ black ] );
    f.kingPos[ white ] ^= 0x38;
    f.kingPos[ black ] ^= 0x38;
    f.turn ^= 1;
    std::swap( f.castRights[white], f.castRights[ black ] );
    BitMat &m = mat();
    m.dmat *= -1;
    std::swap( m.pair[0], m.pair[1] );
    // rehash and so on
    update();
}

// returns true if it's ok to do null move for this position
bool BitBoard::nullMoveOk() const
{
    const BitFlags &f = flags();
    if ( f.check )
        return 0;				// don't do nullmove
    i32 tmat = mat().tmat;
    if ( !tmat )
        return 0;				// don't do nullmove
    i32 turn = f.turn;
    // ok if side to move has any minor/major piece(s)
    return bits[ wknight + turn ] || bits[ wbishop + turn ] || bits[ wrook + turn ] || bits[ wqueen + turn ];
}

// optionally return piece color
// return piece type at pos
i32 BitBoard::pieceType( u8 pos, i32 *c ) const
{
    i32 res = pieces[ pos ];
    if ( c )
    {
        *c = (res & 127) ? res >> 7 : -1;
    }
    return res & 127;
}

BitBoard::BitBoard() : matTab( gmatTab )
{
}

static i32 castlingRightsFromBoard( const Board &ob, const BitBoard &b, i32 turn )
{
    i32 res = 0;
    if ( turn == white )
    {
        if ( !ob.pieces[ H1 ].moved && (b.bits[ wrook ] & U64C(0x8000000000000000 )) )
        {
            // short castling possible
            res |= 0x08;
        }
        if ( !ob.pieces[ A1 ].moved && (b.bits[ wrook ] & U64C(0x0100000000000000 )) )
        {
            // long castling possible
            res |= 0x10;
        }
    }
    else
    {
        if ( !ob.pieces[ H8 ].moved && (b.bits[ brook ] & 0x80) )
        {
            // short castling possible
            res |= 0x08;
        }
        if ( !ob.pieces[ A8 ].moved && (b.bits[ brook ] & 0x01) )
        {
            // long castling possible
            res |= 0x10;
        }
    }
    return res;
}

BitBoard::BitBoard( const Board &b, i32 turn ) : matTab( gmatTab )
{
    reset();
    memset( bits, 0, sizeof(bits) );
    i32 i;
    for (i=0; i<64; i++)
    {
        const Piece &p = b.pieces[i];
        u8 &lp = pieces[i];
        lp = p.type;
        if ( lp )
        {
            lp |= p.color ? 0x80 : 0;
        }
        if ( p.type == none )
            continue;
        if ( p.type == king )
        {
            flags().kingPos[ p.color ] = i;
        }
        else
        {
            i32 ind = INDEX( p.color, p.type );
            bits[ ind ] |= ONESHL( i );
        }
    }
    // handle castling rights!!
    update();
    flags().castRights[ white ] = (b.flags.castled & 1) ? 0 :
                                  b.pieces[ b.flags.kingPos[white] ].moved ? 0 : castlingRightsFromBoard( b, *this, white );
    flags().castRights[ black ] = (b.flags.castled & 2) ? 0 :
                                  b.pieces[ b.flags.kingPos[black] ].moved ? 0 : castlingRightsFromBoard( b, *this, black );

    flags().check = b.flags.check;

    flags().ep = b.flags.ep;
    flags().fiftyCounter = b.flags.fiftyCounter;
    flags().turn = turn;
    update();
}

// compare to other bitboard. returns true if same
bool BitBoard::compare( const BitBoard &o ) const
{
    i32 i;
    bool res = true;
    for (i=0; i<bmax; i++)
    {
        if ( bits[i] != o.bits[i] )
        {
#ifdef DEBUG_ALL
            logFmt("compare failed at index %d\n", i );
            res = false;
#else
            return false;
#endif
        }
    }
    // compare pieces
    if ( memcmp( pieces, o.pieces, sizeof(pieces) ) )
    {
        return false;
    }
    return res;
}

i32 BitBoard::stage() const
{
    i32 tmat = mat().tmat;
    if ( tmat > 4585 )			// 3500?
    {
        return 0;
    }
    if ( tmat >= 2200 )		// 1700?
        return 1;
    return tmat ? 2 : 3;
}

void BitBoard::reset()
{
    memset( bits, 0, sizeof(bits) );

    bits[ wpawn ] = U64C( 0x00ff000000000000 );
    bits[ bpawn ] = U64C( 0x000000000000ff00 );
    bits[ wknight ] = U64C( 0x4200000000000000 );
    bits[ bknight ] = U64C( 0x0000000000000042 );
    bits[ wbishop ] = U64C( 0x2400000000000000 );
    bits[ bbishop ] = U64C( 0x0000000000000024 );
    bits[ wrook ] = U64C( 0x8100000000000000 );
    bits[ brook ] = U64C( 0x0000000000000081 );
    bits[ wqueen ] = U64C( 0x0800000000000000 );
    bits[ bqueen ] = U64C( 0x0000000000000008 );
    bits[ wall ] = U64C( 0xffff000000000000 );
    bits[ ball ] = U64C( 0x000000000000ffff );

    mat().dmat = 0;
    mat().tmat = tmatTab[ knight ]*4 + tmatTab[ bishop ]*4 + tmatTab[ rook ]*4 + tmatTab[ queen ]*2;
    mat().wtmat = mat().tmat/2;
    mat().pair[ white ] = bpair | rpair;
    mat().pair[ black ] = bpair | rpair;

    flags().turn = white;
    flags().fiftyCounter = 0;
    flags().kingPos[ white ] = 60;
    flags().kingPos[ black ] = 4;
    flags().ep = 0;
    flags().castRights[0] = 0x18;			// ah
    flags().castRights[1] = 0x18;			// AH

    update();
}

// hash one piece (for incremental hashing)
inline u64 BitBoard::hashPiece( u8 who, u8 type, u8 pos ) const
{
    // now hash using val...
    return zobristKeys[ type | (who << 3) | ((i32)pos << 8) ];
}

void BitBoard::update()
{
    BitFlags &bf = flags();

    // calculate hashes...
    bits[bhash] = zobristTurn[ bf.turn ] ^
                  zobristCast[ white ][ bf.castRights[ white ] ] ^
                  zobristCast[ black ][ bf.castRights[ black ] ] ^
                  zobristEP[ bf.ep ]
                  ;
	bits[bmathash] = 0;
    pawnHash() = 0;
    // now: rehash!
    i32 i, c;
    BitMat &bm = mat();
    bm.dmat = 0;
    bm.tmat = 0;
    bm.wtmat = 0;

    bits[ wall ] = bits[ ball ] = 0;

    memset( pieces, 0, sizeof(pieces) );

    // don't forget to hash both kings!
    bits[ bhash ] ^=
        hashPiece( white, king, bf.kingPos[ white ] )
        ^
        hashPiece( black, king, bf.kingPos[ black ] )
        ;

    bits[ wall ] |= ONESHL( bf.kingPos[ white ] );
    bits[ ball ] |= ONESHL( bf.kingPos[ black ] );

    pieces[ bf.kingPos[ white ] ] = king;
    pieces[ bf.kingPos[ black ] ] = king | 0x80;

    for ( c = white; c <= black; c++ )
    {
        bm.pair[ c ] = 0;
        for ( i = pawn; i < king; i++ )
        {
            i32 ind = INDEX( c, i );
            u64 b = bits[ind];
            if ( !b )
                continue;
            i32 bc = bitCount(b);
            i32 tmpval = bc * tmatTab[ i ];
            bm.tmat += tmpval;
            if ( c == white )
            {
                bm.wtmat += tmpval;
            }
            if ( i == bishop && bc > 1 )
            {
                mat().pair[ c ] |= bpair;
            }
            else
                if ( i == rook && bc > 1 )
                {
                    mat().pair[ c ] |= rpair;
                }
            bits[ INDEX( c, king ) ] |= b;		// hint: update all piece mask
            u8 pos[64];
            i32 npos = getPos( b, pos );
            i32 ip;
			ASSERT( npos > 0 );
			matHash() += (u64)npos << MATSHIFT( c, i );	// update material hash
            for (ip=0; ip<npos; ip++)
            {
                u8 p = pos[ip];
                pieces[ p ] = i | (c << 7);
                u8 val = i | (c << 3);

                // now hash using val...
                const u64 &zk = zobristKeys[ val | ((i32)p << 8) ];
                bits[ bhash ] ^= zk;
                if ( i == pawn )
                {
                    pawnHash() ^= zk;
                }
            }
        }
    }
    for ( c = white; c <= black; c++ )
    {
        // pass 2: dmat!
        for ( i = pawn; i < king; i++ )
        {
            i32 ind = INDEX( c, i );
            u64 b = bits[ind];
            if ( !b )
                continue;
            i32 bc = bitCount(b);
            if ( c == white )
            {
                mat().dmat += bc * matTab[ i ];
            }
            else
            {
                mat().dmat -= bc * matTab[ i ];
            }
        }
    }
}

#define SAVE_UNDO( index )	{ undobuf[ index ] = bits[ index ]; res |= 1 <<( index ); }
#define SAVE_UNDO_PIECE( pos ) { *undopos++ = pos+1; *undopos++ = pieces[pos]; }

#ifdef DEBUG_ISCHECK
bool reent_lock = 0;
#endif

i32 BitBoard::nullMove( u64 *undobuf )
{
    ASSERT( undobuf != 0 );
    {
        // special case: null move!
        BitFlags &f = flags();
        {
            undobuf[ bhash ] = bits[ bhash ];
            bits[ bhash ] ^= zobristTurn[1];
            undobuf[ bflags ] = bits[ bflags ];
            f.turn ^= 1;
            // update ep flag
            if ( f.ep )
            {
                // keep hash up to date
                bits[ bhash ] ^= zobristEP[ f.ep ];
                f.ep = 0;
            }
#if 0
            f.fiftyCounter = 0;
#else
            f.fiftyCounter++;
#endif

#ifdef DEBUG_IHASH
            {
                u64 h = bits[ bhash ];
                u64 ph = bits[ bpawnhash ];
                update();
                if ( h != bits[ bhash ] )
                {
                    logFmt("INCREMENTAL_HASH_BUG_FOUND!\n");
                    abort();
                }
                if ( ph != bits[ bpawnhash ] )
                {
                    logFmt("INCREMENTAL_PAWN_HASH_BUG_FOUND!\n");
                    abort();
                }
            }
#endif
        }
    }
    return (1 << bhash) | (1 << bflags);
}

i32 BitBoard::move( const BitMove &m, u64 *undobuf, i8 checking )
{
    ASSERT( undobuf != 0 );
    u8 *undopos = 0;
    {
        undopos = (u8 *)(undobuf + bmax);
////		*undopos = 0;							// terminator
        undobuf[ bmax ] = 0;						// init_terminators
    }
#ifdef DEBUG_VALID_MOVE
    if ( m.from != m.to )
    {
        static i32 rec = 0;
        if ( !rec )
        {
            BitBoard old( *this );
            BitMove tm[ bmaxMoves ];
            i32 vm = old.moves( tm );
            i32 i;
            for (i=0; i<vm; i++)
            {
                if ( m.mask == tm[i].mask && m.promo == tm[i].promo &&
                        (m.flags & ~bgoodcap) == (tm[i].flags & ~bgoodcap) )
//				if ( m.packed == tm[i].packed )
                    break;
            }
            if ( i >= vm )
            {
                logFmt("BAD_MOVE_into BitBoard::move()!\n");
                u64 pmsk = pinned();
                bool res = pseudoIsLegal( m, pmsk );
                dump();
                char buf[64];
                rec++;
                m.describeSAN( *this, buf );
                rec--;
                logFmt("move: %s\n", buf );
                ASSERT(0);
            }
        }
    }
#endif

#if defined(DEBUG_ALL) || defined(DEBUG_VALID_BOARD)
    BitBoard old( *this );
#endif

#if defined(DEBUG_IHASH) || defined(DEBUG_CHECK)
    BitBoard oboard( *this );
#endif

#ifdef DEBUG_ISCHECK
    if ( !reent_lock )
    {
        reent_lock = 1;
        bool ischk = isCheck( m, discoveredCheckers() );
        reent_lock = 0;
    }
#endif

////	bool fix_dmat = false;				// need to recalculate dmat if stage changes!

    i32 res = 0;
    BitFlags &f = flags();
    BitMat &bm = mat();

//	i32 stage = this->stage();			// current game stage
    const u64 &fm = ONESHL( m.from );
    u64 tm = ONESHL( m.to );

    i32 i;

    i32 type;							// moving piece type
    i32 captype = none;				// captured piece type
    /*	if ( f.kingPos[ f.turn ] == m.from )
    	{
    		type = king;
    	}
    	else*/
    {
        // first: find piece to move
        /*		for ( i = pawn; i < king; i++)
        		{
        			if ( bits[ INDEX( f.turn, i ) ] & fm )
        				break;			// found!
        		}
        		// TODO: ASSERT!
        		type = i;*/
        type = pieces[ m.from ] & 127;
        ASSERT( type && ((pieces[ m.from ] >> 7) == f.turn) );
    }

    u8 cap = m.flags & bcapture;
    u8 matChange = m.promo | cap;

	if ( matChange )
	{
		SAVE_UNDO( bmathash );
	}

    // hash and flags: always
    {
        SAVE_UNDO( bhash );
        SAVE_UNDO( bflags );

        if ( type == pawn )
        {
            SAVE_UNDO( bpawnhash );
        }

        /*	if ( matChange )
        {*/
        // new: always (because of positional bonus tables!)
        SAVE_UNDO( bmat );
        /*	}*/
    }
    u8 enp = m.flags & benpassant;
    if ( cap | enp )
    {
        u8 cp = m.to;
        u64 otm = tm;
        if ( enp )
        {
            captype = pawn;
            cp = (m.from & 0xf8) | (m.to & 7);
            // cp = pawn_position
            tm = ONESHL( cp );
            {
                SAVE_UNDO_PIECE( cp );
            }
            pieces[ cp ] = 0;
			// update mathash: opponent loses a pawn
			bits[ bmathash ] -= U64C(1) << MATSHIFT( f.turn^1, pawn );
        }
        else
        {
            // find piece to be captured
            /*			for ( i = pawn; i < king; i++)
            			{
            				if ( bits[ INDEX( (f.turn^1), i ) ] & tm )
            					break;			// found!
            			}
            			// TODO: ASSERT!
            			captype = i;*/
            captype = pieces[ m.to ] & 127;
			// opponent loses a piece/pawn
			bits[ bmathash ] -= U64C(1) << MATSHIFT( f.turn^1, captype );
            if ( captype == rook && f.castRights[ f.turn ^ 1 ] && ((m.to & 0xf0) == (f.kingPos[ f.turn ^ 1 ] & 0xf0)) )
            {
                // NEW: (bugfix): can lose castling rights!!
                u8 rpx = (m.to & 7) + 1;
                if ( (f.castRights[ f.turn ^ 1 ] & 15) == rpx )
                {
                    // losing short (kingside) castling rights
                    // keep hash up to date
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                    f.castRights[ f.turn^1 ] &= 0xf0;			// update castling rights
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                }
                else if ( (f.castRights[ f.turn ^ 1 ] >> 4) == rpx )
                {
                    // losing long (queenside) castling rights
                    // keep hash up to date
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                    f.castRights[ f.turn^1 ] &= 0x0f;			// update castling rights
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                }
            }
            ASSERT( captype && (pieces[ m.to ] >> 7) != f.turn );
        }

        i32 ind = INDEX( f.turn ^ 1, captype );

        {
            if ( captype == pawn && !( res & (1 << bpawnhash)) )
            {
                SAVE_UNDO( bpawnhash );
            }

            SAVE_UNDO( ind );
            SAVE_UNDO( ball - f.turn );
        }
        bits[ ball - f.turn ] &= ~tm;	// clear capture piece bit (all)
        bits[ ind ] &= ~tm;				// clear captured piece bit

        {
            // keep hash up to date
            u64 zk = hashPiece( f.turn ^ 1, captype, cp );
            bits[ bhash ] ^= zk;
            if ( captype == pawn )
            {
                pawnHash() ^= zk;
            }

            // update_dmat!
            if ( f.turn == white )
            {
                // undo positional bonus
//				i32 capbonus = (*bonusTable)[ stage ][ f.turn ^ 1 ][ captype ][ cp ];
                bm.dmat += matTab[ captype ];// + capbonus;
            }
            else
            {
                // undo positional bonus
//				i32 capbonus = (*bonusTable)[ stage ][ f.turn ^ 1 ][ captype ][ cp ];
                bm.dmat -= matTab[ captype ];// + capbonus;
            }

            if ( captype > pawn )
            {
                // update_tmat!
                i32 tmpval = tmatTab[ captype ];
                bm.tmat -= tmpval;
                if ( f.turn == black )
                {
                    bm.wtmat -= tmpval;
                }
                if ( captype == bishop || captype == rook )
                {
                    u8 bc = bitCount( bits[ ind ] );
                    if ( bc < 2 )
                    {
                        // clear pair flags
                        bm.pair[ f.turn ^ 1 ] &= ~( captype == bishop ? bpair : rpair );
                    }
                }
            }
        }

        tm = otm;
    }

    // trivially save from -> to pieces (always valid!)
    // special cases to handle: enpassant, castling
    {
        SAVE_UNDO_PIECE( m.from );
        SAVE_UNDO_PIECE( m.to );
        SAVE_UNDO( wall + f.turn );
    }
    pieces[ m.to ] = m.promo ? (m.promo | (f.turn << 7)) : pieces[ m.from ];
    pieces[ m.from ] = 0;

    if ( type != king )
    {
        i32 ind = INDEX( f.turn, type );
        {
            SAVE_UNDO( ind );
        }

        {
            /*			// undo positional bonus
            			if ( f.turn == white )
            			{
            				bm.dmat -= (*bonusTable)[ stage ][ f.turn ][ type ][ m.from ];
            			}
            			else
            			{
            				bm.dmat += (*bonusTable)[ stage ][ f.turn ][ type ][ m.from ];
            			}*/

            // keep hash up to date
            u64 zk = hashPiece( f.turn, type, m.from );
            bits[ bhash ] ^= zk;
            if ( type == pawn )
            {
                pawnHash() ^= zk;
            }
        }

        if ( m.promo )
        {
            bits[ wall + f.turn ] &= ~fm;	// move piece! (all)
            bits[ wall + f.turn ] |= tm;	// move piece! (all)
            bits[ ind ] &= ~fm;				// move piece!
            ind = INDEX( f.turn, m.promo );
            {
                SAVE_UNDO( ind );
            }
            bits[ ind ] |= tm;				// promote!

			// promote!
			bits[ bmathash ] -= U64C(1) << MATSHIFT( f.turn, pawn );
			bits[ bmathash ] += U64C(1) << MATSHIFT( f.turn, m.promo );

            if ( (m.promo == bishop || m.promo == rook) )
            {
                u8 bc = bitCount( bits[ ind ] );
                if ( bc >= 2 )
                {
                    // set pair flags
                    bm.pair[ f.turn ] |= ( m.promo == bishop ? bpair : rpair );
                }
            }

            {
                // update_dmat!
                if ( f.turn == white )
                {
                    bm.dmat -= matTab[ pawn ];
                    bm.wtmat += tmatTab[ m.promo ];
                }
                else
                {
                    bm.dmat += matTab[ pawn ];
                }
                // update_tmat!
                bm.tmat += tmatTab[ m.promo ];

                // tmat should be up to date at this point...
                /*				i32 nstage = this->stage();
                				if ( nstage != stage )
                					fix_dmat = true;*/
                // update positional bonus
                if ( f.turn == white )
                {
                    bm.dmat += matTab[ m.promo ];// + (*bonusTable)[ nstage ][ f.turn ][ m.promo ][ m.to ];
                }
                else
                {
                    bm.dmat -= matTab[ m.promo ];// + (*bonusTable)[ nstage ][ f.turn ][ m.promo ][ m.to ];
                }

                // keep hash up to date
                bits[ bhash ] ^= hashPiece( f.turn, m.promo, m.to );
            }
        }
        else
        {
            u64 ftm = fm | tm;
            bits[ wall + f.turn ] ^= ftm;	// move piece! (all)
            bits[ ind ] ^= ftm;				// move piece!

            {
                if ( type == rook )
                {
                    if ( f.castRights[ f.turn ] )
                    {
                        i32 x1 = (m.from & 7) + 1;
                        if ( (f.castRights[ f.turn ] & 15) == x1 )
                        {
                            // losing short (kingside) castling rights
                            // keep hash up to date
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                            f.castRights[ f.turn ] &= 0xf0;			// update castling rights
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                        }
                        else if ( (f.castRights[ f.turn ] >> 4) == x1 )
                        {
                            // losing long (queenside) castling rights
                            // keep hash up to date
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                            f.castRights[ f.turn ] &= 0x0f;			// update castling rights
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                        }
                    }
                }

                // tmat should be up to date at this point...
                /*				i32 nstage = this->stage();
                				if ( nstage != stage )
                					fix_dmat = true;
                				// update positional bonus
                				if ( f.turn == white )
                				{
                					bm.dmat += (*bonusTable)[ nstage ][ f.turn ][ type ][ m.to ];
                				}
                				else
                				{
                					bm.dmat -= (*bonusTable)[ nstage ][ f.turn ][ type ][ m.to ];
                				}*/

                // keep hash up to date
                u64 zk = hashPiece( f.turn, type, m.to );
                bits[ bhash ] ^= zk;
                if ( type == pawn )
                    pawnHash() ^= zk;
            }
        }
    }
    else
    {
        // king move!

        i32 nstage;
        {
            /*			// undo positional bonus
            			if ( f.turn == white )
            			{
            				bm.dmat -= (*bonusTable)[ stage ][ f.turn ][ king ][ m.from ];
            			}
            			else
            			{
            				bm.dmat += (*bonusTable)[ stage ][ f.turn ][ king ][ m.from ];
            			}*/

            // keep hash up to date
            bits[ bhash ] ^=
                hashPiece( f.turn, king, m.from )
                ^
                hashPiece( f.turn, king, m.to )
                ;

            // tmat should be up to date at this point...
            /*			nstage = this->stage();
            			if ( nstage != stage )
            				fix_dmat = true;
            			// update positional bonus
            			if ( f.turn == white )
            			{
            				bm.dmat += (*bonusTable)[ nstage ][ f.turn ][ king ][ m.to ];
            			}
            			else
            			{
            				bm.dmat -= (*bonusTable)[ nstage ][ f.turn ][ king ][ m.to ];
            			}*/
        }

        // update king position
        f.kingPos[ f.turn ] = m.to;
        // update all
        bits[ wall + f.turn ] ^= ONESHL( m.from ) ^ ONESHL( m.to );

        u8 cr = f.castRights[ f.turn ];
        if ( cr )
        {
            // keep hash up to date
            bits[ bhash ] ^= zobristCast[ f.turn ][ cr ];
            f.castRights[ f.turn ] = 0;			// clear castling rights
        }
        if ( m.flags & bcastling )
        {
            // handle castling!!!
            i32 ind = INDEX( f.turn, rook );
            {
                SAVE_UNDO( ind );
            }

            // TODO: support Chess 960!

            // rook pos
            u8 rp;				// rook_pos (from)
            u8 rpt;				// rook_pos (to)
            if ( f.turn == white )
            {
                // white castling
                if ( (m.to & 7) == 6 )
                {
                    // short
                    rp = (7 << 3) | (( cr & 15 ) - 1);
                    rpt = (7 << 3) | 5;
                }
                else
                {
                    // long
                    rp = (7 << 3) | (( cr >> 4 ) - 1);
                    rpt = (7 << 3) | 3;
                }
            }
            else
            {
                // black castling
                if ( (m.to & 7) == 6 )
                {
                    // short
                    rp = (( cr & 15 ) - 1);
                    rpt = 5;
                }
                else
                {
                    // long
                    rp = (( cr >> 4 ) - 1);
                    rpt = 3;
                }
            }

            {
                SAVE_UNDO_PIECE( rp );
                SAVE_UNDO_PIECE( rpt );
            }
            pieces[ rpt ] = pieces[ rp ];
            pieces[ rp ] = 0;

            // TODO: assert!
            u64 rfm = ONESHL( rp );
            u64 rtm = ONESHL( rpt );

            u64 rftm = rfm | rtm;

            {
                // keep hash up to date!
                // note: this must be done before rmoved is modified
                bits[ bhash ] ^= hashPiece( f.turn, rook, rp );
            }

            bits[ wall + f.turn ] ^= rftm;
            bits[ ind ] ^= rftm;

            {
                /*				// undo positional bonus
                				if ( f.turn == white )
                				{
                					bm.dmat -= (*bonusTable)[ stage ][ f.turn ][ rook ][ rp ];
                					bm.dmat += (*bonusTable)[ nstage ][ f.turn ][ rook ][ rpt ];
                				}
                				else
                				{
                					bm.dmat += (*bonusTable)[ stage ][ f.turn ][ rook ][ rp ];
                					bm.dmat -= (*bonusTable)[ nstage ][ f.turn ][ rook ][ rpt ];
                				}*/

                // keep hash up to date!
                bits[ bhash ] ^= hashPiece( f.turn, rook, rpt );
            }
        }
//		f.castRights[ f.turn ] = 0;			// king move => losing castling rights!
    }

    {
        // update ep flag
        if ( f.ep )
        {
            // keep hash up to date
            bits[ bhash ] ^= zobristEP[ f.ep ];
            f.ep = 0;
        }

        if ( type == pawn )
        {
            i32 dy = ((i32)m.to >> 3) - ((i32)m.from >> 3);
            if ( abs(dy) > 1 )
            {
                f.ep = m.to;
                // keep hash up to date
                bits[ bhash ] ^= zobristEP[ f.ep ];
            }
        }

        // update 50 move rule counter
        if ( type == pawn || (m.flags & bcapture) )
        {
            f.fiftyCounter = 0;
        }
        else
        {
            f.fiftyCounter++;
        }

#if 0
        if ( fix_dmat )
        {
            // update_dmat!
            i32 stage = this->stage();
            bm.dmat = 0;
            i32 i, j;
            for (i=white; i <= black; i++)
            {
                for (j=pawn; j<king; j++)
                {
                    i32 ind = INDEX( i, j );
                    if ( !bits[ ind ] )
                        continue;
                    u8 p[64];
                    i32 np = getPos( bits[ ind ], p );
                    i32 k;
                    for (k=0; k<np; k++)
                    {
                        if ( i == white )
                        {
                            bm.dmat += matTab[ j ];
////							bm.dmat += (*bonusTable)[ stage ][ i ][ j ][ p[ k] ];
                        }
                        else
                        {
                            bm.dmat -= matTab[ j ];
////							bm.dmat -= (*bonusTable)[ stage ][ i ][ j ][ p[ k] ];
                        }
                    }
                }
            }
            /*			bm.dmat += (*bonusTable)[ stage ][ white ][ king ][ f.kingPos[ white ] ];
            			bm.dmat -= (*bonusTable)[ stage ][ black ][ king ][ f.kingPos[ black ] ];*/
        }
#endif
    }

    // and finally: update turn and turn_hash!
    f.turn ^= 1;
    // update current check state!
    f.check = checking < 0 ? attacked( f.kingPos[ f.turn ], f.turn ) : checking;

    {
        bits[ bhash ] ^= zobristTurn[1];

#ifdef DEBUG_CHECK
        bool wcheck = attacked( flags().kingPos[ white ], white );
        bool bcheck = attacked( flags().kingPos[ black ], black );
        if ( f.turn == white && bcheck || f.turn == black && wcheck )
        {
            logFmt("FATAL ERROR: check flag invalid after move()!\n");
            oboard.dump();
            char buf[32];
            m.describeSAN( oboard, buf, 0 );	// FIXME: 0 to avoid recursion!
            logFmt("move: %s\n", buf);
            bool legal = oboard.isLegal( m );
            abort();
        }
#endif

#ifdef DEBUG_ALL
        {
            {
                // validate_undo!
                BitBoard tmp( *this );
                tmp.undo( res, undobuf );
                if ( !tmp.compare( old ) )
                {
                    logFmt("undo error!\n");
                }
            }
            bool wcheck = attacked( flags().kingPos[ white ], white );
            bool bcheck = attacked( flags().kingPos[ black ], black );
            if ( !old.validate() )
            {
                logFmt("?old_movgen error!\n");
            }
            if ( !validate() || (f.turn == white && bcheck) || (f.turn == black && wcheck) )
            {
                BitMove mv[ bmaxMoves ];
                i32 nmv = old.moves( mv );
                logFmt("?movgen error!\n");
                logFmt("old:\n");
                old.dump();
                logFmt("new:\n");
                dump();
                char buf[32];
                m.describeSAN( old, buf );
                logFmt("move: %s\n", buf);
                BitBoard bo( old );
                bo.move( m );
            }
            BitBoard tmp2( *this );
            tmp2.update();
            if ( !tmp2.compare( *this ) )
            {
                logFmt("move error!\n");
                logFmt("old:\n");
                old.dump();
                logFmt("new:\n");
                dump();
                char buf[32];
                m.describeSAN( old, buf );
                logFmt("move: %s\n", buf);
                tmp2.compare( *this );
                BitBoard bo( old );
                bo.move( m );
            }
        }
#endif
    }
#ifdef DEBUG_IHASH
    {
        u64 h = bits[ bhash ];
        update();
        if ( h != bits[ bhash ] )
        {
            logFmt("INCREMENTAL_HASH_BUG_FOUND!\n");
            oboard.dump();
            char buf[32];
            m.describeSAN( oboard, buf );
            logFmt("move: %s\n", buf);
            abort();
        }
    }
#endif
#ifdef DEBUG_VALID_BOARD
    {
        i32 i;
        i32 j;
        for (j=white; j<=black; j++)
        {
            for (i=pawn; i<king; i++)
            {
                u64 b = bits[ INDEX(j, i) ];
                while ( b )
                {
                    i32 ind = popLSB( b );
                    if ( (pieces[ ind ] & 127) != i )
                    {
                        logFmt("invalid_board!\n");
                        logFmt("old:\n");
                        old.dump();
                        logFmt("new:\n");
                        dump();
                        *this = old;
                        move( m, undobuf );
//						abort();
                    }
                }
            }
        }
    }
#endif
    return res;
}

i32 BitBoard::move( const BitMove &m )
{
#ifdef DEBUG_VALID_MOVE
    if ( m.from != m.to )
    {
        static i32 rec = 0;
        if ( !rec )
        {
            BitBoard old( *this );
            BitMove tm[ bmaxMoves ];
            i32 vm = old.moves( tm );
            i32 i;
            for (i=0; i<vm; i++)
            {
                if ( m.mask == tm[i].mask && m.promo == tm[i].promo &&
                        (m.flags & ~bgoodcap) == (tm[i].flags & ~bgoodcap) )
//				if ( m.packed == tm[i].packed )
                    break;
            }
            if ( i >= vm )
            {
                logFmt("BAD_MOVE_into BitBoard::move()!\n");
                u64 pmsk = pinned();
                bool res = pseudoIsLegal( m, pmsk );
                dump();
                char buf[64];
                rec++;
                m.describeSAN( *this, buf );
                rec--;
                logFmt("move: %s\n", buf );
                ASSERT(0);
            }
        }
    }
#endif

#if defined(DEBUG_ALL) || defined(DEBUG_VALID_BOARD)
    BitBoard old( *this );
#endif

#if defined(DEBUG_IHASH) || defined(DEBUG_CHECK)
    BitBoard oboard( *this );
#endif

#ifdef DEBUG_ISCHECK
    if ( !reent_lock )
    {
        reent_lock = 1;
        bool ischk = isCheck( m, discoveredCheckers() );
        reent_lock = 0;
    }
#endif

////	bool fix_dmat = false;				// need to recalculate dmat if stage changes!

    i32 res = 0;
    BitFlags &f = flags();
    BitMat &bm = mat();

//	i32 stage = this->stage();			// current game stage
    const u64 &fm = ONESHL( m.from );
    u64 tm = ONESHL( m.to );

    i32 i;

    i32 type;							// moving piece type
    i32 captype = none;				// captured piece type
    /*	if ( f.kingPos[ f.turn ] == m.from )
    	{
    		type = king;
    	}
    	else*/
    {
        // first: find piece to move
        /*		for ( i = pawn; i < king; i++)
        		{
        			if ( bits[ INDEX( f.turn, i ) ] & fm )
        				break;			// found!
        		}
        		// TODO: ASSERT!
        		type = i;*/
        type = pieces[ m.from ] & 127;
        ASSERT( type && ((pieces[ m.from ] >> 7) == f.turn) );
    }

    u8 cap = m.flags & bcapture;
    u8 matChange = m.promo | cap;

    // hash and flags: always
    u8 enp = m.flags & benpassant;
    if ( cap | enp )
    {
        u8 cp = m.to;
        u64 otm = tm;
        if ( enp )
        {
            captype = pawn;
            cp = (m.from & 0xf8) | (m.to & 7);
            // cp = pawn_position
            tm = ONESHL( cp );
            pieces[ cp ] = 0;
			// update mathash: opponent loses a pawn
			bits[ bmathash ] -= U64C(1) << MATSHIFT( f.turn^1, pawn );
        }
        else
        {
            // find piece to be captured
            /*			for ( i = pawn; i < king; i++)
            			{
            				if ( bits[ INDEX( (f.turn^1), i ) ] & tm )
            					break;			// found!
            			}
            			// TODO: ASSERT!
            			captype = i;*/
            captype = pieces[ m.to ] & 127;
			// opponent loses a piece/pawn
			bits[ bmathash ] -= U64C(1) << MATSHIFT( f.turn^1, captype );
            if ( captype == rook && f.castRights[ f.turn ^ 1 ] && ((m.to & 0xf0) == (f.kingPos[ f.turn ^ 1 ] & 0xf0)) )
            {
                // NEW: (bugfix): can lose castling rights!!
                u8 rpx = (m.to & 7) + 1;
                if ( (f.castRights[ f.turn ^ 1 ] & 15) == rpx )
                {
                    // losing short (kingside) castling rights
                    // keep hash up to date
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                    f.castRights[ f.turn^1 ] &= 0xf0;			// update castling rights
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                }
                else if ( (f.castRights[ f.turn ^ 1 ] >> 4) == rpx )
                {
                    // losing long (queenside) castling rights
                    // keep hash up to date
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                    f.castRights[ f.turn^1 ] &= 0x0f;			// update castling rights
                    bits[ bhash ] ^= zobristCast[ f.turn^1 ][ f.castRights[ f.turn^1 ] ];
                }
            }
            ASSERT( captype && (pieces[ m.to ] >> 7) != f.turn );
        }

        i32 ind = INDEX( f.turn ^ 1, captype );

        bits[ ball - f.turn ] &= ~tm;	// clear capture piece bit (all)
        bits[ ind ] &= ~tm;				// clear captured piece bit

        {
            // keep hash up to date
            u64 zk = hashPiece( f.turn ^ 1, captype, cp );
            bits[ bhash ] ^= zk;
            if ( captype == pawn )
            {
                pawnHash() ^= zk;
            }

            // update_dmat!
            if ( f.turn == white )
            {
                // undo positional bonus
//				i32 capbonus = (*bonusTable)[ stage ][ f.turn ^ 1 ][ captype ][ cp ];
                bm.dmat += matTab[ captype ];// + capbonus;
            }
            else
            {
                // undo positional bonus
//				i32 capbonus = (*bonusTable)[ stage ][ f.turn ^ 1 ][ captype ][ cp ];
                bm.dmat -= matTab[ captype ];// + capbonus;
            }

            if ( captype > pawn )
            {
                // update_tmat!
                i32 tmpval = tmatTab[ captype ];
                bm.tmat -= tmpval;
                if ( f.turn == black )
                {
                    bm.wtmat -= tmpval;
                }
                if ( captype == bishop || captype == rook )
                {
                    u8 bc = bitCount( bits[ ind ] );
                    if ( bc < 2 )
                    {
                        // clear pair flags
                        bm.pair[ f.turn ^ 1 ] &= ~( captype == bishop ? bpair : rpair );
                    }
                }
            }
        }

        tm = otm;
    }

    // trivially save from -> to pieces (always valid!)
    // special cases to handle: enpassant, castling
    pieces[ m.to ] = m.promo ? (m.promo | (f.turn << 7)) : pieces[ m.from ];
    pieces[ m.from ] = 0;

    if ( type != king )
    {
        i32 ind = INDEX( f.turn, type );
        {
            /*			// undo positional bonus
            			if ( f.turn == white )
            			{
            				bm.dmat -= (*bonusTable)[ stage ][ f.turn ][ type ][ m.from ];
            			}
            			else
            			{
            				bm.dmat += (*bonusTable)[ stage ][ f.turn ][ type ][ m.from ];
            			}*/

            // keep hash up to date
            u64 zk = hashPiece( f.turn, type, m.from );
            bits[ bhash ] ^= zk;
            if ( type == pawn )
            {
                pawnHash() ^= zk;
            }
        }

        if ( m.promo )
        {
            bits[ wall + f.turn ] &= ~fm;	// move piece! (all)
            bits[ wall + f.turn ] |= tm;	// move piece! (all)
            bits[ ind ] &= ~fm;				// move piece!
            ind = INDEX( f.turn, m.promo );
            bits[ ind ] |= tm;				// promote!

			// promote!
			bits[ bmathash ] -= U64C(1) << MATSHIFT( f.turn, pawn );
			bits[ bmathash ] += U64C(1) << MATSHIFT( f.turn, m.promo );

            if ( (m.promo == bishop || m.promo == rook) )
            {
                u8 bc = bitCount( bits[ ind ] );
                if ( bc >= 2 )
                {
                    // set pair flags
                    bm.pair[ f.turn ] |= ( m.promo == bishop ? bpair : rpair );
                }
            }

            {
                // update_dmat!
                if ( f.turn == white )
                {
                    bm.dmat -= matTab[ pawn ];
                    bm.wtmat += tmatTab[ m.promo ];
                }
                else
                {
                    bm.dmat += matTab[ pawn ];
                }
                // update_tmat!
                bm.tmat += tmatTab[ m.promo ];

                // tmat should be up to date at this point...
                /*				i32 nstage = this->stage();
                				if ( nstage != stage )
                					fix_dmat = true;*/
                // update positional bonus
                if ( f.turn == white )
                {
                    bm.dmat += matTab[ m.promo ];// + (*bonusTable)[ nstage ][ f.turn ][ m.promo ][ m.to ];
                }
                else
                {
                    bm.dmat -= matTab[ m.promo ];// + (*bonusTable)[ nstage ][ f.turn ][ m.promo ][ m.to ];
                }

                // keep hash up to date
                bits[ bhash ] ^= hashPiece( f.turn, m.promo, m.to );
            }
        }
        else
        {
            u64 ftm = fm | tm;
            bits[ wall + f.turn ] ^= ftm;	// move piece! (all)
            bits[ ind ] ^= ftm;				// move piece!

            {
                if ( type == rook )
                {
                    if ( f.castRights[ f.turn ] )
                    {
                        i32 x1 = (m.from & 7) + 1;
                        if ( (f.castRights[ f.turn ] & 15) == x1 )
                        {
                            // losing short (kingside) castling rights
                            // keep hash up to date
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                            f.castRights[ f.turn ] &= 0xf0;			// update castling rights
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                        }
                        else if ( (f.castRights[ f.turn ] >> 4) == x1 )
                        {
                            // losing long (queenside) castling rights
                            // keep hash up to date
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                            f.castRights[ f.turn ] &= 0x0f;			// update castling rights
                            bits[ bhash ] ^= zobristCast[ f.turn ][ f.castRights[ f.turn ] ];
                        }
                    }
                }

                // tmat should be up to date at this point...
                /*				i32 nstage = this->stage();
                				if ( nstage != stage )
                					fix_dmat = true;
                				// update positional bonus
                				if ( f.turn == white )
                				{
                					bm.dmat += (*bonusTable)[ nstage ][ f.turn ][ type ][ m.to ];
                				}
                				else
                				{
                					bm.dmat -= (*bonusTable)[ nstage ][ f.turn ][ type ][ m.to ];
                				}*/

                // keep hash up to date
                u64 zk = hashPiece( f.turn, type, m.to );
                bits[ bhash ] ^= zk;
                if ( type == pawn )
                    pawnHash() ^= zk;
            }
        }
    }
    else
    {
        // king move!

        i32 nstage;
        {
            /*			// undo positional bonus
            			if ( f.turn == white )
            			{
            				bm.dmat -= (*bonusTable)[ stage ][ f.turn ][ king ][ m.from ];
            			}
            			else
            			{
            				bm.dmat += (*bonusTable)[ stage ][ f.turn ][ king ][ m.from ];
            			}*/

            // keep hash up to date
            bits[ bhash ] ^=
                hashPiece( f.turn, king, m.from )
                ^
                hashPiece( f.turn, king, m.to )
                ;

            // tmat should be up to date at this point...
            /*			nstage = this->stage();
            			if ( nstage != stage )
            				fix_dmat = true;
            			// update positional bonus
            			if ( f.turn == white )
            			{
            				bm.dmat += (*bonusTable)[ nstage ][ f.turn ][ king ][ m.to ];
            			}
            			else
            			{
            				bm.dmat -= (*bonusTable)[ nstage ][ f.turn ][ king ][ m.to ];
            			}*/
        }

        // update king position
        f.kingPos[ f.turn ] = m.to;
        // update all
        bits[ wall + f.turn ] ^= ONESHL( m.from ) ^ ONESHL( m.to );

        u8 cr = f.castRights[ f.turn ];
        if ( cr )
        {
            // keep hash up to date
            bits[ bhash ] ^= zobristCast[ f.turn ][ cr ];
            f.castRights[ f.turn ] = 0;			// clear castling rights
        }
        if ( m.flags & bcastling )
        {
            // handle castling!!!
            i32 ind = INDEX( f.turn, rook );

            // TODO: support Chess 960!

            // rook pos
            u8 rp;				// rook_pos (from)
            u8 rpt;				// rook_pos (to)
            if ( f.turn == white )
            {
                // white castling
                if ( (m.to & 7) == 6 )
                {
                    // short
                    rp = (7 << 3) | (( cr & 15 ) - 1);
                    rpt = (7 << 3) | 5;
                }
                else
                {
                    // long
                    rp = (7 << 3) | (( cr >> 4 ) - 1);
                    rpt = (7 << 3) | 3;
                }
            }
            else
            {
                // black castling
                if ( (m.to & 7) == 6 )
                {
                    // short
                    rp = (( cr & 15 ) - 1);
                    rpt = 5;
                }
                else
                {
                    // long
                    rp = (( cr >> 4 ) - 1);
                    rpt = 3;
                }
            }

            pieces[ rpt ] = pieces[ rp ];
            pieces[ rp ] = 0;

            // TODO: assert!
            u64 rfm = ONESHL( rp );
            u64 rtm = ONESHL( rpt );

            u64 rftm = rfm | rtm;

            {
                // keep hash up to date!
                // note: this must be done before rmoved is modified
                bits[ bhash ] ^= hashPiece( f.turn, rook, rp );
            }

            bits[ wall + f.turn ] ^= rftm;
            bits[ ind ] ^= rftm;

            {
                /*				// undo positional bonus
                				if ( f.turn == white )
                				{
                					bm.dmat -= (*bonusTable)[ stage ][ f.turn ][ rook ][ rp ];
                					bm.dmat += (*bonusTable)[ nstage ][ f.turn ][ rook ][ rpt ];
                				}
                				else
                				{
                					bm.dmat += (*bonusTable)[ stage ][ f.turn ][ rook ][ rp ];
                					bm.dmat -= (*bonusTable)[ nstage ][ f.turn ][ rook ][ rpt ];
                				}*/

                // keep hash up to date!
                bits[ bhash ] ^= hashPiece( f.turn, rook, rpt );
            }
        }
//		f.castRights[ f.turn ] = 0;			// king move => losing castling rights!
    }

    {
        // update ep flag
        if ( f.ep )
        {
            // keep hash up to date
            bits[ bhash ] ^= zobristEP[ f.ep ];
            f.ep = 0;
        }

        if ( type == pawn )
        {
            i32 dy = ((i32)m.to >> 3) - ((i32)m.from >> 3);
            if ( abs(dy) > 1 )
            {
                f.ep = m.to;
                // keep hash up to date
                bits[ bhash ] ^= zobristEP[ f.ep ];
            }
        }

        // update 50 move rule counter
        if ( type == pawn || (m.flags & bcapture) )
        {
            f.fiftyCounter = 0;
        }
        else
        {
            f.fiftyCounter++;
        }

#if 0
        if ( fix_dmat )
        {
            // update_dmat!
            i32 stage = this->stage();
            bm.dmat = 0;
            i32 i, j;
            for (i=white; i <= black; i++)
            {
                for (j=pawn; j<king; j++)
                {
                    i32 ind = INDEX( i, j );
                    if ( !bits[ ind ] )
                        continue;
                    u8 p[64];
                    i32 np = getPos( bits[ ind ], p );
                    i32 k;
                    for (k=0; k<np; k++)
                    {
                        if ( i == white )
                        {
                            bm.dmat += matTab[ j ];
////							bm.dmat += (*bonusTable)[ stage ][ i ][ j ][ p[ k] ];
                        }
                        else
                        {
                            bm.dmat -= matTab[ j ];
////							bm.dmat -= (*bonusTable)[ stage ][ i ][ j ][ p[ k] ];
                        }
                    }
                }
            }
            /*			bm.dmat += (*bonusTable)[ stage ][ white ][ king ][ f.kingPos[ white ] ];
            			bm.dmat -= (*bonusTable)[ stage ][ black ][ king ][ f.kingPos[ black ] ];*/
        }
#endif
    }

    // and finally: update turn and turn_hash!
    f.turn ^= 1;
    // update current check state!
    f.check = attacked( f.kingPos[ f.turn ], f.turn );

    {
        bits[ bhash ] ^= zobristTurn[1];

#ifdef DEBUG_CHECK
        bool wcheck = attacked( flags().kingPos[ white ], white );
        bool bcheck = attacked( flags().kingPos[ black ], black );
        if ( f.turn == white && bcheck || f.turn == black && wcheck )
        {
            logFmt("FATAL ERROR: check flag invalid after move()!\n");
            oboard.dump();
            char buf[32];
            m.describeSAN( oboard, buf, 0 );	// FIXME: 0 to avoid recursion!
            logFmt("move: %s\n", buf);
            bool legal = oboard.isLegal( m );
            abort();
        }
#endif

#ifdef DEBUG_ALL
        {
            {
                // validate_undo!
                BitBoard tmp( *this );
                tmp.undo( res, undobuf );
                if ( !tmp.compare( old ) )
                {
                    logFmt("undo error!\n");
                }
            }
            bool wcheck = attacked( flags().kingPos[ white ], white );
            bool bcheck = attacked( flags().kingPos[ black ], black );
            if ( !old.validate() )
            {
                logFmt("?old_movgen error!\n");
            }
            if ( !validate() || (f.turn == white && bcheck) || (f.turn == black && wcheck) )
            {
                BitMove mv[ bmaxMoves ];
                i32 nmv = old.moves( mv );
                logFmt("?movgen error!\n");
                logFmt("old:\n");
                old.dump();
                logFmt("new:\n");
                dump();
                char buf[32];
                m.describeSAN( old, buf );
                logFmt("move: %s\n", buf);
                BitBoard bo( old );
                bo.move( m );
            }
            BitBoard tmp2( *this );
            tmp2.update();
            if ( !tmp2.compare( *this ) )
            {
                logFmt("move error!\n");
                logFmt("old:\n");
                old.dump();
                logFmt("new:\n");
                dump();
                char buf[32];
                m.describeSAN( old, buf );
                logFmt("move: %s\n", buf);
                tmp2.compare( *this );
                BitBoard bo( old );
                bo.move( m );
            }
        }
#endif
    }
#ifdef DEBUG_IHASH
    {
        u64 h = bits[ bhash ];
        update();
        if ( h != bits[ bhash ] )
        {
            logFmt("INCREMENTAL_HASH_BUG_FOUND!\n");
            oboard.dump();
            char buf[32];
            m.describeSAN( oboard, buf );
            logFmt("move: %s\n", buf);
            abort();
        }
    }
#endif
#ifdef DEBUG_VALID_BOARD
    {
        i32 i;
        i32 j;
        for (j=white; j<=black; j++)
        {
            for (i=pawn; i<king; i++)
            {
                u64 b = bits[ INDEX(j, i) ];
                while ( b )
                {
                    i32 ind = popLSB( b );
                    if ( (pieces[ ind ] & 127) != i )
                    {
                        logFmt("invalid_board!\n");
                        logFmt("old:\n");
                        old.dump();
                        logFmt("new:\n");
                        dump();
                        *this = old;
                        move( m, undobuf );
//						abort();
                    }
                }
            }
        }
    }
#endif
    return res;
}

#define UNROLLED_UNDO( i )	if ( indexMask & (1 << (i)) ) bits[i] = buf[i];

void BitBoard::undo( i32 indexMask, const u64 *buf )
{
    ASSERT( buf );
    // TODO: unroll! (or: asm-optimized!)
    // FIXME: when bmax changes, it must be added here as well
    // or (even better: code-generated for all 64K types, maybe shift-optimized to reduce code size!)

    UNROLLED_UNDO( 0 );
    UNROLLED_UNDO( 1 );
    if ( !( indexMask & 0xfffffffc ) )
        return;							// fast nullmove undo!
    UNROLLED_UNDO( 2 );
    UNROLLED_UNDO( 3 );
    UNROLLED_UNDO( 4 );
    UNROLLED_UNDO( 5 );
    UNROLLED_UNDO( 6 );
    UNROLLED_UNDO( 7 );
    UNROLLED_UNDO( 8 );
    UNROLLED_UNDO( 9 );
    UNROLLED_UNDO( 10 );
    UNROLLED_UNDO( 11 );
    UNROLLED_UNDO( 12 );
    UNROLLED_UNDO( 13 );
    UNROLLED_UNDO( 14 );
    UNROLLED_UNDO( 15 );
    UNROLLED_UNDO( 16 );

    /*	i32 i;
    	for ( i=0; indexMask && i<bmax; i++, indexMask >>= 1 )
    	{
    		if ( indexMask & 1 )
    		{
    			bits[i] = buf[i];
    		}
    	}*/
    // new: must handle positional (pieces) undo!
    const u8 *undopos = (const u8 *)( buf + bmax );
    // we have to restore up to 4 pieces
    if ( *undopos )
    {
        pieces[ undopos[0]-1 ] = undopos[1];
        undopos += 2;
        if ( *undopos )
        {
            pieces[ undopos[0]-1 ] = undopos[1];
            undopos += 2;
            if ( *undopos )
            {
                pieces[ undopos[0]-1 ] = undopos[1];
                undopos += 2;
                if ( *undopos )
                {
                    pieces[ undopos[0]-1 ] = undopos[1];
                    undopos += 2;
                }
            }
        }
    }
}

void bpawnMove( i32 type, const BitBoard &b, i32 c, u8 p, BitMove *&m, const u64 &all, const u64 &targm )
{
    BitMove temp;
    temp.clear();
    temp.from = p;

    u64 targ = 0;
    u64 bt = ONESHL( p );
    // one forward
    u8 ep = b.flags().ep;
    if ( c == white )
    {
        u64 t = bt >> 8;
        if ( !t )
            return;					// FIXME: invalid move!
        if ( !( t & all ) )
        {
            // pseudo valid -> add
            if ( type != 1 || (t & 0xff) )
            {
                targ |= t;
            }
            // check for move by 2
            if ( type != 1 && (p >> 3) == 6 )
            {
                t >>= 8;
                if ( t && !( t & all ) )
                {
                    // pseudo valid -> add
                    targ |= t;
                }
            }
        }
        if ( type != 2 )
        {
            // check capture targets
            t = (bt >> 9) & R1MASK;
            if ( t & b.bits[ ball ] )
            {
                targ |= t;
            }
            t = (bt >> 7) & L1MASK;
            if ( t & b.bits[ ball ] )
            {
                targ |= t;
            }
            if ( ep )
            {
                // check enpassant capture targets
                if ( ( p & ~7 ) == (ep & ~7) && abs( (p & 7) - (ep & 7) ) == 1 )
                {
                    targ |= ONESHL( ep - 8 );
                }
            }
        }
    }
    else
    {
        u64 t = bt << 8;
        if ( !t )
            return;					// FIXME: invalid move!
        if ( !( t & all ) )
        {
            // pseudo valid -> add
            if ( type != 1 || (t & U64C(0xff00000000000000)) )
            {
                targ |= t;
            }
            // check for move by 2
            if ( type != 1 && (p >> 3) == 1 )
            {
                t <<= 8;
                if ( t && !( t & all ) )
                {
                    // pseudo valid -> add
                    targ |= t;
                }
            }
        }
        if ( type != 2 )
        {
            t = (bt << 9) & L1MASK;
            if ( t & b.bits[ wall ] )
            {
                targ |= t;
            }
            t = (bt << 7) & R1MASK;
            if ( t & b.bits[ wall ] )
            {
                targ |= t;
            }
            if ( ep )
            {
                // check enpassant capture targets
                if ( ( p & ~7 ) == (ep & ~7) && abs( (p & 7) - (ep & 7) ) == 1  )
                {
                    targ |= ONESHL( ep + 8 );
                }
            }
        }
    }

    if ( !(targ &= targm) )
        return;						// no valid targets
    // now: extract targets
//	u8 tpos[64];
//	i32 i, ntpos = getPos( targ, tpos );
    u8 ekp = b.flags().kingPos[ c ^ 1 ];
//	for (i=0; i<ntpos; i++)
    while ( targ )
    {
        u8 tp = popLSB( targ );//tpos[i];
        /*		if ( tp == ekp )
        			continue;				// illegal capture of enemy king!*/
        BitMove &mv = *m;
        mv.packed = temp.packed;
        mv.to = tp;
        u64 otp = ONESHL( tp );
        bool cap = (otp & b.bits[ ball - c ]) != 0;	// fixme: hack
        if ( cap )
            mv.flags |= bcapture;
        if ( tp < 8 || tp >= 0x38 )
        {
            // promotion!
            if ( type != 2 )
            {
                mv.promo = queen;
                // support underpromotions as well...
                if ( b.flags().check )
                {
                    // now validate move!
                    if ( !b.isEvasion( *m, pawn ) )
                        m--;
                }
                if ( type != 1 )
                {
                    m++;
                }
            }
            if ( type != 1 )
            {
                m->packed = temp.packed;
                m->to = tp;
                m->promo = rook;
                m->flags = mv.flags;
                if ( b.flags().check )
                {
                    // now validate move!
                    if ( !b.isEvasion( *m, pawn ) )
                        m--;
                }
                m++;
                m->packed = temp.packed;
                m->to = tp;
                m->promo = bishop;
                m->flags = mv.flags;
                if ( b.flags().check )
                {
                    // now validate move!
                    if ( !b.isEvasion( mv, pawn ) )
                        m--;
                }
                m++;
                m->packed = temp.packed;
                m->to = tp;
                m->promo = knight;
                m->flags = mv.flags;
                if ( b.flags().check )
                {
                    // now validate move!
                    if ( !b.isEvasion( mv, pawn ) )
                        m--;
                }
            }
        }
        else
        {
            if ( ep )
            {
                // check if enpassant capture
                if ( (tp & 7) != (p & 7) && !( otp & all ) )
                {
                    mv.flags |= bcapture | benpassant;
                    // TODO: make sure the EP capture doesn't leave our king in check!
                    // simulate move...
                    // FIXME: might be slow but the point is that EP captures shouldn't be that frequent anyway... (?)

                    u64 xorm = ONESHL( mv.from ) | ONESHL( mv.to ) | ONESHL( ep );
                    u64 all = b.bits[ wall ] | b.bits[ ball ];
                    u8 kp = b.flags().kingPos[ c ];
                    all ^= xorm;

                    u64 equeens = b.bits[ bqueen - c ];
                    u64 ediagSliders = b.bits[ bbishop - c ] | equeens;
                    u64 eorthoSliders = b.bits[ brook - c ] | equeens;

                    bool invalid = ( bishopAttm( kp, all ) & ediagSliders ) ||
                                   ( rookAttm( kp, all ) & eorthoSliders );
                    if ( invalid )
                    {
                        continue;
                    }
                }
            }
            if ( b.flags().check )
            {
                // now validate move!
                if ( !b.isEvasion( *m, pawn ) )
                    continue;
            }
        }
        m++;
    }
}

void bknightMove( i32 type, const BitBoard &b, i32 c, u8 p, BitMove *&m, const u64 &all, const u64 &targm )
{
    u64 targ = knightMov[ p ];				// get target squares
    targ &= ~(all ^ b.bits[ ball - c ] );	// hack: avoid self-captures
    if ( !(targ &= targm) )
        return;						// no valid targets
    // now: extract targets
//	u8 tpos[64];
//	i32 i, ntpos = getPos( targ, tpos );
    BitMove temp;
    temp.clear();
    temp.from = p;
    u8 ekp = b.flags().kingPos[ c ^ 1 ];
//	for (i=0; i<ntpos; i++)
    while ( targ )
    {
        u8 tp = popLSB( targ );//tpos[i];
        /*		if ( tp == ekp )
        			continue;				// illegal capture of enemy king!*/
        BitMove &mv = *m;
        mv.packed = temp.packed;
        mv.to = tp;
        bool cap = (ONESHL( tp ) & b.bits[ ball - c ]) != 0;	// fixme: hack
        if ( cap )
        {
            mv.flags |= bcapture;
            if ( type == 2 )
                continue;
        }
        else
        {
            if ( type == 1 )
                continue;
        }
        if ( b.flags().check )
        {
            // now validate move!
            if ( !b.isEvasion( mv, knight ) )
                continue;
        }
        m++;
    }
}

void bbishopMove( i32 type, const BitBoard &b, i32 c, u8 p, BitMove *&m, const u64 &all, const u64 &targm )
{
    if ( !( diagAttMask[ p ] & targm ) )
    {
        // nothing to do
        return;
    }
    u64 targ = 0;				// get target squares
    u64 bt = ONESHL( p );

    i32 j;
    u64 t;

    // for each diagonal...
    const u64 &enemy = b.bits[ ball - c];
    u64 own = all ^ enemy;

    /*u64 targmagic*/
    targ = bishopAttm( p, all ) & ~own;
#if 0
#if 1
    for (j=1; j<8; j+=2)
    {
        u64 dm = diagMask[j][p];
        u64 dma = dm & all;
        if ( !dma )
        {
            if ( type != 1 )
            {
                // add_dm!
                targ |= dm;
            }
            continue;
        }
        i8 bit = (j<4) ? getMSB(dma) : getLSB(dma);
        if ( bit < 0 )
            continue;
        const u64 &b = ONESHL( bit );
        // extract targets...
        if ( type == 1 )
        {
            if ( b & enemy )
            {
                targ |= b;
            }
        }
        else
        {
            targ |= betweenMask[ p ][ bit ];
            if ( !(b & own) )
            {
                targ |= b;
            }
        }
    }
#else
    t = bt;
    for (;;)
    {
        t = (t << 9) & L1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t << 7) & R1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 9) & R1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 7) & L1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }
#endif
#endif
    /*	if ( targ != targmagic )
    	{
    		if ( (targ & targm) != (targmagic & targm) )
    		{
    			logFmt("bit_magic bug found!\n");
    		}
    		logFmt("magic bug found!\n");
    		BitBoard bb(b);
    		bb.dump();
    		u64 targmagic2 = bishopAttm( p, all ) & ~own;
    	}*/

    if ( !(targ &= targm) )
        return;						// no valid targets
    // now: extract targets
//	u8 tpos[64];
//	i32 i, ntpos = getPos( targ, tpos );
    BitMove temp;
    temp.clear();
    temp.from = p;
    u8 ekp = b.flags().kingPos[ c ^ 1 ];
//	for (i=0; i<ntpos; i++)
    while ( targ )
    {
        u8 tp = popLSB( targ );//tpos[i];
        /*		if ( tp == ekp )
        			continue;				// illegal capture of enemy king!*/
        BitMove &mv = *m;
        mv.packed = temp.packed;
        mv.to = tp;
        bool cap = (ONESHL( tp ) & b.bits[ ball - c ]) != 0;	// fixme: hack
        if ( cap )
        {
            mv.flags |= bcapture;
            if ( type == 2 )
                continue;
        } else
        {
            if ( type == 1 )
                continue;
        }
        // does move check opponent's king?
        if ( b.flags().check )
        {
            // now validate move!
            if ( !b.isEvasion( mv, bishop ) )
                continue;
        }
        m++;
    }
}

void brookMove( i32 type, const BitBoard &b, i32 c, u8 p, BitMove *&m, const u64 &all, const u64 &targm )
{
    if ( !( orthoAttMask[ p ] & targm ) )
    {
        // nothing to do
        return;
    }
    u64 targ = 0;				// get target squares
    u64 bt = ONESHL( p );

    i32 j;
    u64 t;

    // for each diagonal...
    const u64 &enemy = b.bits[ ball - c];
    u64 own = all ^ enemy;

    /*	t = bt;
    	for (;;)
    	{
    		t = (t << 1) & L1MASK;
    		if ( !t || (t & own) )
    			break;
    		targ |= t;
    		if ( t & enemy )
    		{
    			// potential capture
    			break;
    		}
    	}

    	t = bt;
    	for (;;)
    	{
    		t = (t >> 1) & R1MASK;
    		if ( !t || (t & own) )
    			break;
    		targ |= t;
    		if ( t & enemy )
    		{
    			// potential capture
    			break;
    		}
    	}

    	t = bt;
    	for (;;)
    	{
    		t = (t >> 8);
    		if ( !t || (t & own) )
    			break;
    		targ |= t;
    		if ( t & enemy )
    		{
    			// potential capture
    			break;
    		}
    	}

    	t = bt;
    	for (;;)
    	{
    		t = (t << 8);
    		if ( !t || (t & own) )
    			break;
    		targ |= t;
    		if ( t & enemy )
    		{
    			// potential capture
    			break;
    		}
    	}*/

    targ = rookAttm( p, all ) & ~own;
    /*	u64 targmagic = rookAttm( p, all ) & ~own;
    	if ( targ != targmagic )
    	{
    		logFmt("rook_magic_bug!\n");
    		u64 targmagict = rookAttm( p, all ) & ~own;
    	}*/

#if 0
#if 1
    for (j=0; j<8; j+=2)
    {
        u64 dm = diagMask[j][p];
        u64 dma = dm & all;
        if ( !dma )
        {
            if ( type != 1 )
            {
                // add_dm!
                targ |= dm;
            }
            continue;
        }
        i8 bit = (j<4) ? getMSB(dma) : getLSB(dma);
        if ( bit < 0 )
            continue;
        const u64 &b = ONESHL( bit );
        // extract targets...
        if ( type == 1 )
        {
            if ( b & enemy )
            {
                targ |= b;
            }
        }
        else
        {
            targ |= betweenMask[ p ][ bit ];
            if ( !(b & own) )
            {
                targ |= b;
            }
        }
    }
#else
    t = bt;
    for (;;)
    {
        t = (t << 1) & L1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 1) & R1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 8);
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t << 8);
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }
#endif
#endif

    if ( !(targ &= targm) )
        return;						// no valid targets
    // now: extract targets
//	u8 tpos[64];
//	i32 i, ntpos = getPos( targ, tpos );
    BitMove temp;
    temp.clear();
    temp.from = p;
    u8 ekp = b.flags().kingPos[ c ^ 1 ];
//	for (i=0; i<ntpos; i++)
    while ( targ )
    {
        u8 tp = popLSB( targ );	//tpos[i];
        /*		if ( tp == ekp )
        			continue;				// illegal capture of enemy king!*/
        BitMove &mv = *m;
        mv.packed = temp.packed;
        mv.to = tp;
        bool cap = (ONESHL( tp ) & b.bits[ ball - c ]) != 0;	// fixme: hack
        if ( cap )
        {
            mv.flags |= bcapture;
            if ( type == 2 )
                continue;
        }
        else
        {
            if ( type == 1 )
                continue;
        }
        if ( b.flags().check )
        {
            // now validate move!
            if ( !b.isEvasion( mv, rook ) )
                continue;
        }
        m++;
    }
}

void bqueenMove( i32 type, const BitBoard &b, i32 c, u8 p, BitMove *&m, const u64 &all, const u64 &targm )
{
    if ( !( (diagAttMask[p] | orthoAttMask[ p ]) & targm ) )
    {
        // nothing to do
        return;
    }
    u64 targ = 0;				// get target squares
    u64 bt = ONESHL( p );

    i32 j;
    u64 t;

    const u64 &enemy = b.bits[ ball - c];
    u64 own = all ^ enemy;
    targ = queenAttm( p, all ) & ~own;
#if 0
#if 1
    for (j=0; j<8; j++)
    {
        u64 dm = diagMask[j][p];
        u64 dma = dm & all;
        if ( !dma )
        {
            if ( type != 1 )
            {
                // add_dm!
                targ |= dm;
            }
            continue;
        }
        i8 bit = (j<4) ? getMSB(dma) : getLSB(dma);
        if ( bit < 0 )
            continue;
        const u64 &b = ONESHL( bit );
        // extract targets...
        if ( type == 1 )
        {
            if ( b & enemy )
            {
                targ |= b;
            }
        }
        else
        {
            targ |= betweenMask[ p ][ bit ];
            if ( !(b & own) )
            {
                targ |= b;
            }
        }
    }
#else
    // for each diagonal...

    t = bt;
    for (;;)
    {
        t = (t << 9) & L1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t << 7) & R1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 9) & R1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 7) & L1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t << 1) & L1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 1) & R1MASK;
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t >> 8);
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }

    t = bt;
    for (;;)
    {
        t = (t << 8);
        if ( !t || (t & own) )
            break;
        targ |= t;
        if ( t & enemy )
        {
            // potential capture
            break;
        }
    }
#endif
#endif

    if ( !(targ &= targm) )
        return;						// no valid targets
    // now: extract targets
//	u8 tpos[64];
//	i32 i, ntpos = getPos( targ, tpos );
    BitMove temp;
    temp.clear();
    temp.from = p;
    u8 ekp = b.flags().kingPos[ c ^ 1 ];
//	for (i=0; i<ntpos; i++)
    while ( targ )
    {
        u8 tp = popLSB( targ );//tpos[i];
        /*		if ( tp == ekp )
        			continue;				// illegal capture of enemy king!*/
        BitMove &mv = *m;
        mv.packed = temp.packed;
        mv.to = tp;
        bool cap = (ONESHL( tp ) & b.bits[ ball - c ]) != 0;	// fixme: hack
        if ( cap )
        {
            mv.flags |= bcapture;
            if ( type == 2 )
                continue;
        }
        else
        {
            if ( type == 1 )
                continue;
        }
        if ( b.flags().check )
        {
            // now validate move!
            if ( !b.isEvasion( mv, queen ) )
                continue;
        }
        m++;
    }
}

// returns attack mask for color; i.e. if c == white then the result
// would be attack mask for all white pieces (including king)
u64 BitBoard::attackMask( i32 c ) const
{
    u64 enemy = bits[ ball - c];
    const u64 &own = bits[ wall + c ];

    // important: cast rays through enemy king!
    enemy &= NONESHL( flags().kingPos[ c ^ 1 ] );

    u64 res = kingMov[ flags().kingPos[ c ] ];
    if ( c == black )
    {
        // check black pawns
        u64 t = bits[ INDEX( black, pawn ) ];
        if ( t )
        {
            res |= (( t << 9 ) & L1MASK) | (( t << 7 ) & R1MASK);
        }
    }
    else
    {
        // check white pawns
        u64 t = bits[ INDEX( white, pawn ) ];
        if ( t )
        {
            res |= (( t >> 9 ) & R1MASK) | (( t >> 7 ) & L1MASK);
        }
    }
    // knights...
    u64 b = bits[ wknight + c ];
    res |= (b << 17) & L1MASK;
    res |= (b << 15) & R1MASK;
    res |= (b >> 17) & R1MASK;
    res |= (b >> 15) & L1MASK;
    res |= (b << 10) & L2MASK;
    res |= (b << 6) & R2MASK;
    res |= (b >> 10) & R2MASK;
    res |= (b >> 6) & L2MASK;

    // bishops/queens
    b = bits[ wbishop + c ] | bits[ wqueen + c ];
    i32 j;

    u64 t = b;
    while ( t )
    {
        t = ((t << 9) & L1MASK);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    t = b;
    while ( t )
    {
        t = ((t << 7) & R1MASK);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    t = b;
    while ( t )
    {
        t = ((t >> 9) & R1MASK);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    t = b;
    while ( t )
    {
        t = ((t >> 7) & L1MASK);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    // rooks/queens
    b = bits[ wrook + c ] | bits[ wqueen + c ];

    t = b;
    while ( t )
    {
        t = ((t >> 1) & R1MASK);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    t = b;
    while ( t )
    {
        t = ((t << 1) & L1MASK);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    t = b;
    while ( t )
    {
        t = (t << 8);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    t = b;
    while ( t )
    {
        t = (t >> 8);
        res |= t;
        if ( !(t &= ~own) )
            break;
        if ( !(t &= ~enemy) )
            break;
    }

    return res;	// & ~own;
}

// search/movegen helper. is pseudolegal move valid for current pos/turn?
// needs TRUE pin mask
bool BitBoard::pseudoIsLegal( const BitMove &bm, const u64 &pins ) const
{
    const BitFlags &f = flags();
    ASSERT( !f.check );
    if ( bm.flags & bcastling )
    {
        return true;				// already checked for attackers during movegen
    }
    u8 kp = f.kingPos[ f.turn ];
    if ( bm.flags & benpassant )
    {
        // enpassant capture -- a bit difficult
        // "emulate" move
        // make sure we don't leave our king in check
        u64 all = bits[ wall ] | bits[ ball ];
        all ^= ONESHL( bm.from ) ^ ONESHL( bm.to ) ^ ONESHL( f.ep );
        // check only for sliders
        u64 ediag = bits[ bbishop - f.turn ] | bits[ bqueen - f.turn ];
        u64 eortho = bits[ brook - f.turn] | bits[ bqueen - f.turn ];
        return !( ( bishopAttm( kp, all ) & ediag ) || ( rookAttm( kp, all ) & eortho ) );
    }
    if ( bm.from == kp )
    {
        // non-castling king move
        return !attacked( bm.to, f.turn );
    }
    // not pinned => legal
    if ( !(ONESHL( bm.from ) & pins ) )
    {
        return true;
    }
    // pinned => only legal if it moves along the ray
    return diagFlags[ bm.from ][ kp ] == diagFlags[ bm.to ][ kp ];
}

// search/movegen helper. is legal move for current pos/turn?
bool BitBoard::isLegal( const BitMove &bm, const u64 *ppins ) const
{
    if ( bm.from == bm.to )
    {
        return 0;					// ignore null moves
    }
    u64 pins;
    if ( ppins )
    {
        pins = *ppins;
    }
    else
    {
        pins = pinned();
    }
    const BitFlags &f = flags();
    if ( f.check )
    {
//		return 0;					// never when currently in check! (FIXME: sanity check only)
    }

    /*	const u64 &fm = ONESHL( bm.from );

    	if ( !(bits[ wall + f.turn ] & fm ) )
    	{
    		// illegal turn
    		return 0;
    	}*/
    i32 turn;
    i32 pt = pieceType( bm.from, &turn );
    if ( pt == none || turn != f.turn )
    {
        // illegal from/turn
        return 0;
    }
    if ( pt != pawn && ( (bm.flags & benpassant) || bm.promo ) )
    {
        // illegal flags!
        return 0;
    }
    const u64 &tm = ONESHL( bm.to );
    if ( bits[ wall + f.turn ] & tm )
    {
        // illegal self-capture (also handles own king capture!)
        return 0;
    }

    if ( bm.to == f.kingPos[ f.turn ^ 1 ] )
    {
        // illegal enemy king capture
        return 0;
    }
    if ( pt == king )
    {
        if ( f.kingPos[ turn ] != bm.from )
        {
            return 0;				// kingpos doesn't match
        }
        if ( (bm.flags & bcastling) )
        {
            if ( f.check )
            {
                return 0;			// can't castle when in check
            }
            // trying castling...
            u8 p = bm.from;
            u8 cr = flags().castRights[ turn ];
            if ( !cr )
            {
                return 0;			// no castling rights
            }
            u64 all = bits[ wall ] | bits[ ball ];
            u64 eatt = attackMask( turn ^ 1 );
            if ( turn == white )
            {
                if ( (cr & 15) == 8 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether f1, g1 free AND e1, f1, g1 not attacked
                    if ( !(all & U64C(0x6000000000000000) ) &&
                            !(eatt & U64C(0x7000000000000000)) )
                    {
                        // castling possible!
                        BitMove mv;
                        mv.packed = 0;
                        mv.from = p;
                        mv.to = p + 2;
                        mv.flags |= bcastling;
                        if ( bm.packed == mv.packed )
                        {
                            return 1;				// legal
                        }
                    }
                }
                if ( ((cr>>4) & 15) == 1 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether b1, c1, d1 free AND c1, d1, e1 not attacked
                    if ( !(all & U64C(0x0e00000000000000)) &&
                            !(eatt & U64C(0x1c00000000000000)) )
                    {
                        // castling possible!
                        BitMove mv;
                        mv.packed = 0;
                        mv.from = p;
                        mv.to = p - 2;
                        mv.flags |= bcastling | blongcastling;			// FIXME: dangerous?
                        if ( bm.packed == mv.packed )
                        {
                            return 1;				// legal
                        }
                    }
                }
            }
            else
            {
                if ( (cr & 15) == 8 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether f8, g8 free AND e8, f8, g8 not attacked
                    if ( !((u32)all & 0x60) &&
                            !((u32)eatt & 0x70) )
                    {
                        // castling possible!
                        BitMove mv;
                        mv.packed = 0;
                        mv.from = p;
                        mv.to = p + 2;
                        mv.flags |= bcastling;
                        if ( bm.packed == mv.packed )
                        {
                            return 1;				// legal
                        }
                    }
                }
                if ( ((cr>>4) & 15) == 1 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether b8, c8, d8 free AND c8, d8, e8 not attacked
                    if ( !((u32)all & 0x0e) &&
                            !((u32)eatt & 0x1c) )
                    {
                        // castling possible!
                        BitMove mv;
                        mv.packed = 0;
                        mv.from = p;
                        mv.to = p - 2;
                        mv.flags |= bcastling | blongcastling;			// FIXME: dangerous?
                        if ( bm.packed == mv.packed )
                        {
                            return 1;				// legal
                        }
                    }
                }
            }
            return 0;
        }
        if ( !kproxTab[ bm.from ][ bm.to ] )
        {
            return 0;				// illegal from->to
        }
        if ( bm.flags & bcapture )
        {
            if ( !(tm & bits[ ball - f.turn ]) )
            {
                // no capture
                return 0;
            }
        }
        else
        {
            if ( tm & bits[ ball - f.turn ] )
            {
                // capture but shouldn't be
                return 0;
            }
        }

        if ( attacked( bm.to, turn, 1 ) )
        {
            return 0;				// king would move into check
        }
        if ( bm.flags & bcapture )
        {
            // last thing to do: validate see_value!
            bool illegal;
            bool goodcap = see( bm ) >= 0;
            if ( bm.flags & bgoodcap )
            {
                illegal = !goodcap;
            }
            else
            {
                illegal = goodcap;
            }
            return !illegal;
        }

        return 1;
    }
    bool slider = pt == bishop || pt == rook || pt == queen;

    if ( pt != king && !(sliderValid[ bm.from ][ bm.to ] & (1 << pt) ) )
    {
        // invalid piece direction
        return 0;
    }
    if ( pt == pawn )
    {
        if ( bm.promo )
        {
            if ( turn == white )
            {
                if ( bm.to & ~7 )
                {
                    return 0;				// illegal white_promotion_dest
                }
            }
            else
            {
                if ( (bm.to & ~7) != 0x38 )
                {
                    return 0;				// illegal black_promotion_dest
                }
            }
        }
        if ( !(bm.flags & bcapture) && (bm.from & 7) != (bm.to & 7) )
        {
            // illegal "pawn" move!
            return 0;
        }
        if ( (bm.flags & bcapture) && (bm.from & 7) == (bm.to & 7) )
        {
            // illegal pawn capture move!
            return 0;
        }
        // already validated above
        /*		if ( !(sliderValid[ bm.from ][ bm.to ] & (1 << pawn) ) )
        		{
        			// invalid pawn direction
        			return 0;
        		}*/
        // validate pawn direction for black/white!
        if ( turn == black && bm.to <= bm.from )
        {
            // invalid pawn y-direction
            return 0;
        }
        if ( turn == white && bm.to >= bm.from )
        {
            // invalid pawn y-direction
            return 0;
        }
        if ( bm.flags & benpassant )
        {
            // handle enpassant capture
            if ( !f.ep || !(bm.flags & bcapture) )
            {
                return 0;				// illegal flags/no ep square
            }
            if ( !(ONESHL( f.ep ) & bits[ bpawn - turn ]) )
            {
                return 0;				// invalid ep target (not a pawn)
            }
            // validate "to"
            if ( turn == white )
            {
                if ( bm.to != f.ep - 8 )
                {
                    return 0;			// illegal dest
                }
            }
            else
            {
                if ( bm.to != f.ep + 8 )
                {
                    return 0;			// illegal dest
                }
            }
            // last thing to do: verify that we won't leave our king in check!
            if ( !f.check )
            {
                return pseudoIsLegal( bm, pins );
            }
            u64 xm = ONESHL( bm.from ) | ONESHL( bm.to );
            u64 xme = ONESHL( f.ep );
            u64 &b = bits[ wall + turn ];
            u64 &eb = bits[ ball - turn ];
            b ^= xm;
            eb ^= xme;

            bool illegal = attacked( f.kingPos[turn], turn, 0 );		// don't check pawns/knights/enemyking

            b ^= xm;
            eb ^= xme;
            return !illegal;
        }
    }

    if ( bm.flags & bcapture )
    {
        if ( !(tm & bits[ ball - f.turn ]) )
        {
            // no capture
            return 0;
        }
    }
    else
    {
        if ( tm & bits[ ball - f.turn ] )
        {
            // capture but shouldn't be
            return 0;
        }
    }

    if ( (slider || pt == pawn) && (betweenMask[ bm.from ][ bm.to ] & (bits[wall] | bits[ball]) ) )
    {
        // illegal slider/pawn move -- pieces between!
        return 0;
    }

    bool illegal = 1;		// keep compiler happy
    if ( !(bm.flags & bcapture) )
    {
        // now test if move reveals a check to own king
        if ( f.check )
        {
            u64 &b = bits[ INDEX( turn, pt ) ];
            u64 &ba = bits[ wall + turn ];

            u64 xmsk = ONESHL( bm.from ) ^ tm;
            b ^= xmsk;
            ba ^= xmsk;
            // only check knights, pawns and enemy king if king move
            illegal = attacked( f.kingPos[ turn ], turn, pt == king );

            // restore
            b ^= xmsk;
            ba ^= xmsk;
        }
        else
        {
            illegal = !pseudoIsLegal( bm, pins );
        }
    }
    else
    {
        i32 ept = pieces[ bm.to ];
        if ( !(ept & 127) || (ept >> 7) == turn )
        {
            return false;
        }
        ept &= 127;
        ASSERT( ept != king );
        // now test if move reveals a check to own king
        if ( f.check )
        {
            u64 &b = bits[ INDEX( turn, pt ) ];
            u64 &ba = bits[ wall + turn ];
            u64 &e = bits[ INDEX( turn ^ 1, ept ) ];
            u64 &ea = bits[ ball - turn ];

            u64 xmsk = ONESHL( bm.from ) ^ tm;
            b ^= xmsk;
            ba ^= xmsk;
            e ^= tm;
            ea ^= tm;
            // only check knights, pawns and enemy king if king move
            illegal = attacked( f.kingPos[ turn ], turn, pt == king );

            // restore
            b ^= xmsk;
            ba ^= xmsk;
            e ^= tm;
            ea ^= tm;
        }
        else
        {
            illegal = !pseudoIsLegal( bm, pins );
        }

        if ( !illegal )
        {
            // last thing to do: validate see_value!
            bool goodcap = see( bm ) >= 0;
            if ( bm.flags & bgoodcap )
            {
                illegal = !goodcap;
            }
            else
            {
                illegal = goodcap;
            }
        }
    }

    return !illegal;
}

// is check? requires discovered check candidates mask
bool BitBoard::isCheck( const BitMove &bm, const u64 &discovered ) const
{
    if ( bm.from == bm.to )
    {
        return 0;
    }
#ifndef OLD_ISCHECK
    i32 pt = pieceType( bm.from );
    const BitFlags &bf = flags();
    u64 all = bits[ wall ] | bits[ ball ];
    i32 turn = bf.turn;
    u8 ekp = bf.kingPos[ turn ^ 1 ];
    const u64 &frm = ONESHL( bm.from );
    bool dc = (frm & discovered) != 0;		// potentially discovered check?
    const u64 &tom = ONESHL( bm.to );
    const u64 &ekpm = ONESHL( ekp );
    bool res = 0;
    switch ( pt )
    {
    case pawn:
    {
        res = ((turn == white ? bpAttm[ bm.to ] : wpAttm[ bm.to ]) & ekpm) != 0;
        if ( dc && !res )
        {
////				res = !(tom & betweenMask[ ekp ][ bm.from ]);	// BUG!
            res = ((diagFlags[ ekp ][ bm.from ] ^ diagFlags[ ekp ][ bm.to ]) & ~4) != 0;
        }
        if ( !res && bm.promo )
        {
            // check via promotion?
            switch ( bm.promo )
            {
            case knight:
                res = (knightMov[ bm.to ] & ekpm ) != 0;
                break;
            case bishop:
                res = (bishopAttm( bm.to, all & ~frm ) & ekpm ) != 0;
                break;
            case rook:
                res = (rookAttm( bm.to, all & ~frm ) & ekpm ) != 0;
                break;
            case queen:
                res = (queenAttm( bm.to, all & ~frm ) & ekpm ) != 0;
                break;
            }
        }
        // handle special case: enpassant capture!
        if ( !res && (bm.flags & benpassant) )
        {
            u64 diagSliders = bits[ wbishop + turn ] | bits[ wqueen + turn ];
            u64 orthoSliders = bits[ wrook + turn ] | bits[ wqueen + turn ];
            u64 msk = all & ~(frm | ONESHL( flags().ep )) | tom;
            res = (bishopAttm( ekp, msk ) & diagSliders) != 0
                  || (rookAttm( ekp, msk ) & orthoSliders) != 0;
        }
    }
    break;
    case knight:
    {
        res = dc;
        if ( !res )
        {
            res = (knightMov[ bm.to ] & ONESHL( ekp )) != 0;
        }
    }
    break;
    case bishop:
    {
        res = dc;
        if ( !res )
        {
            res = (tom & bishopAttm( ekp, all )) != 0;
        }
    }
    break;
    case rook:
    {
        res = dc;
        if ( !res )
        {
            res = (tom & rookAttm( ekp, all )) != 0;
        }
    }
    break;
    case queen:
    {
        res = (tom & queenAttm( ekp, all )) != 0;
    }
    break;
    case king:
        // check two possibilites: discovered check or castling into check
        if ( dc )
        {
////			res = !(tom & betweenMask[ ekp ][ bm.from ]);	// BUG!
            res = ((diagFlags[ ekp ][ bm.from ] ^ diagFlags[ ekp ][ bm.to ]) & ~4) != 0;
        }
        if ( !res && (bm.flags & bcastling) )
        {
            // special case: check via castling

            // TODO: support Chess 960!

            u8 cr = bf.castRights[ turn ];

            // rook pos
            u8 rp;				// rook_pos (from)
            u8 rpt;				// rook_pos (to)
            if ( turn == white )
            {
                // white castling
                if ( (bm.to & 7) == 6 )
                {
                    // short
                    rp = (7 << 3) | (( cr & 15 ) - 1);
                    rpt = (7 << 3) | 5;
                }
                else
                {
                    // long
                    rp = (7 << 3) | (( cr >> 4 ) - 1);
                    rpt = (7 << 3) | 3;
                }
            }
            else
            {
                // black castling
                if ( (bm.to & 7) == 6 )
                {
                    // short
                    rp = (( cr & 15 ) - 1);
                    rpt = 5;
                }
                else
                {
                    // long
                    rp = (( cr >> 4 ) - 1);
                    rpt = 3;
                }
            }
            u64 blm = all ^ (ONESHL( rp ) | ONESHL( rpt ) | frm | tom);
            // new virtual blocker mask

            res = (rookAttm( rpt, blm ) & ekpm) != 0;
        }
        break;
    }
#if (defined(DEBUG) || defined(_DEBUG))
	BitBoard tbb(*this);
    u64 undob[ bundomax ];
    i32 uf = tbb.move(bm, undob );
    bool trueres = tbb.flags().check;

    if ( res != trueres )
    {
		BitBoard tbb(*this);
        tbb.dump();
        char buf[64];
        bm.describeSAN( *this, buf );
        logFmt("move: %s\n", buf );

		BitBoard tbb2(*this);
        u64 undob[ bundomax ];
        i32 uf = tbb2.move(bm, undob );
        bool trueres = tbb2.flags().check;
		isCheck( bm, discovered );
    }
    ASSERT( res == trueres );
#endif

    return res;
#else
    u64 undob[ bundomax ];
    i32 uf = move(bm, undob, 1 );
    bool res = flags().check;
    undo( uf, undob );
    return res;
#endif
}

bool BitBoard::isEvasion( const BitMove &bm, i32 type ) const
{
    if ( bm.from == bm.to || (bm.flags & bcastling) )
    {
        return 0;
    }
    const BitFlags &f = flags();
    if ( !(bm.flags & benpassant) )
    {
        if ( !(bm.flags & (bcapture /*| benpassant*/ /*| bcastling*/)) /*&& !bm.promo*/ )
        {
            // ordinary move => FAST check (no need to move())
            i32 ind = INDEX( f.turn, type );
            u64 &b = bits[ ind ];
            u64 &ball = bits[ wall + f.turn ];
            u64 xorm = ONESHL( bm.from ) ^ ONESHL( bm.to );

            b ^= xorm;
            ball ^= xorm;
            bool res = attacked( flags().kingPos[ f.turn ], f.turn ) ? false : true;
            b ^= xorm;
            ball ^= xorm;

            return res;
        }
        else
        {
            // simple capture move
            // => FAST check (no need to move())
            i32 ind = INDEX( f.turn, type );
            u64 &b = bits[ ind ];
            u64 &eall = bits[ ball - f.turn ];
            u64 &ball = bits[ wall + f.turn ];

            i32 pt = pieceType( bm.to );
            ASSERT( pt > none && pt < king );
            u64 &eb = bits[ INDEX( f.turn ^ 1, pt ) ];

            const u64 &xore = ONESHL( bm.to );
            u64 xorm = ONESHL( bm.from ) ^ xore;	//ONESHL( bm.to );

            eb ^= xore;
            eall ^= xore;
            b ^= xorm;
            ball ^= xorm;
            bool res = attacked( flags().kingPos[ f.turn ], f.turn ) ? false : true;
            b ^= xorm;
            ball ^= xorm;
            eb ^= xore;
            eall ^= xore;

            return res;
        }
    }
    // TODO: could be much faster if move is not castling/enpassant capture!
    BitBoard tmp( *this );
    tmp.move( bm );
    return tmp.attacked( flags().kingPos[ flags().turn ], flags().turn ) ? false : true;
}

static void dumpMask( const u64 &m )
{
    u8 pos[ 64 ];
    i32 npos = getPos( m, pos );
    i32 i;
    for (i=0; i<npos; i++)
    {
        logFmt("%c%c ", 'a' + (pos[i] & 7), '8' - (pos[i] >> 3) );
    }
}

// returns true if a square is attacked by opponent
// (c = my_color)
bool BitBoard::attacked( i32 pos, i32 c, bool checkKnightsPawnsKing ) const
{
    if ( checkKnightsPawnsKing )
    {
        // first: check knights
        if ( bits[ INDEX( (c^1), knight ) ] & knightMov[ pos ] )
        {
            return true;
        }
        const u64 &pm = ONESHL( pos );
        // second: check enemy king (this is also important!)
        if ( pm & kingMov[ flags().kingPos[ c ^ 1 ] ] )
            return true;
        // third: check pawns
        if ( c == white )
        {
            // check black pawns
            if ( bits[ INDEX( black, pawn ) ] & bpAttm[ pos ] )
            {
                return true;
            }
            /*		u64 t = bits[ INDEX( black, pawn ) ];
            if ( t )
            {
            t = (( t << 9 ) & L1MASK) | (( t << 7 ) & R1MASK);
            if ( t & pm )
            return true;
            }*/
        }
        else
        {
            // check white pawns
            if ( bits[ INDEX( white, pawn ) ] & wpAttm[ pos ] )
            {
                return true;
            }
            /*		u64 t = bits[ INDEX( white, pawn ) ];
            if ( t )
            {
            t = (( t >> 9 ) & R1MASK) | (( t >> 7 ) & L1MASK);
            if ( t & pm )
            return true;
            }*/
        }
    }
    // finally: check diagonals for bishops/rooks/queens
    // for each diagonal...
////	const u64 &enemy = bits[ ball - c];
////	const u64 &own = bits[ wall + c ];

#if 0

    // values 0-8: l, ul, u, ur, r, dr, d, dl, none
    u64 mask = bits[ wall ] | bits[ ball ];
    u64 msk = 0;
    i32 i;
    u64 enemyDiag[2] = {
        bits[ brook - c ] | bits[ bqueen - c ],
        bits[ bbishop - c ] | bits[ bqueen - c ]
    };
    i32 res = 0;
    for (i=0; i<8; i++)
    {
        u64 dm = diagMask[i][ pos ] & mask;
        if ( !dm )
            continue;
        i8 bit = (i<4) ? getMSB(dm) : getLSB(dm);
        if ( bit >= 0 )
        {
            if ( enemyDiag[ i & 1 ] & ONESHL( bit ) )
            {
                res = 1;
                break;
                //return true;
            }
        }
    }
    u64 atm = queenAttm( pos, mask );
    i32 res2 = ( atm & (bits[ bbishop - c ] | bits[ brook - c ] | bits[ bqueen - c ]) ) != 0;
    if ( res != res2 )
    {
        logFmt("result_attacked_error!\n");
        logFmt("attdef_mask = ");
        dumpMask(atm);
        logFmt("\n");
        BitBoard bb( *this );
        bb.dump();
    }

#else
    u64 mask = bits[ wall ] | bits[ ball ];
    if ( rookAttm( pos, mask ) & (bits[ brook - c ] | bits[ bqueen - c ]) )
    {
        return 1;
    }
    return (bishopAttm( pos, mask ) & (bits[ bbishop - c ] | bits[ bqueen - c ])) != 0;
#endif

#if 0
    u64 t = pm;
    i32 j;
    // check for bishops/queens
    for (j=0; j<7; j++)
    {
        t = (t << 9) & L1MASK;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ bbishop - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    t = pm;
    for (j=0; j<7; j++)
    {
        t = (t << 7) & R1MASK;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ bbishop - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    t = pm;
    for (j=0; j<7; j++)
    {
        t = (t >> 9) & R1MASK;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ bbishop - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    t = pm;
    for (j=0; j<7; j++)
    {
        t = (t >> 7) & L1MASK;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ bbishop - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    // check for rooks/queens
    t = pm;
    for (j=0; j<7; j++)
    {
        t = (t >> 1) & R1MASK;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ brook - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    t = pm;
    for (j=0; j<7; j++)
    {
        t = (t << 1) & L1MASK;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ brook - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    t = pm;
    for (j=0; j<7; j++)
    {
        t <<= 8;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ brook - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }

    t = pm;
    for (j=0; j<7; j++)
    {
        t >>= 8;
        if ( !t || (t & own) )
            break;
        if ( t & enemy )
        {
            if ( t & (bits[ brook - c ] | bits[ bqueen - c ] ) )
                return true;
            break;
        }
    }
#endif

    return false;			// not attacked
}

void bkingMove( i32 type, const BitBoard &b, i32 c, u8 p, BitMove *&m, const u64 &all )
{
    u8 ekp = b.flags().kingPos[ c ^ 1 ];
    u64 targ = kingMov[ p ];				// get target squares
    targ &= ~(all ^ b.bits[ ball - c ] );	// hack: avoid self-captures


    u64 eatt = b.attackMask( c ^ 1 );

    targ &= ~eatt;							////kingMov[ ekp ];				// can't approach enemy king!
    if ( !targ )
        return;								// no valid targets => this implies that castling is not possible anyway
    // FIXME: verify

    BitMove temp;
    temp.clear();
    temp.from = p;

    // now: check to see if we can castle...
    if ( type != 2 && !b.flags().check )
    {
        u8 cr = b.flags().castRights[ c ];
        if ( cr )
        {
            // TODO: proper castling!
            if ( c == white )
            {
                if ( (cr & 15) == 8 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether f1, g1 free AND e1, f1, g1 not attacked
                    if ( !(all & U64C(0x6000000000000000) ) &&
                            !(eatt & U64C(0x7000000000000000)) )
                    {
                        // castling possible!
                        BitMove &mv = *m;
                        mv.packed = temp.packed;
                        mv.to = p + 2;
                        mv.flags |= bcastling;			// FIXME: dangerous?
                        m++;
                    }
                }
                if ( ((cr>>4) & 15) == 1 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether b1, c1, d1 free AND c1, d1, e1 not attacked
                    if ( !(all & U64C(0x0e00000000000000)) &&
                            !(eatt & U64C(0x1c00000000000000)) )
                    {
                        // castling possible!
                        BitMove &mv = *m;
                        mv.packed = temp.packed;
                        mv.to = p - 2;
                        mv.flags |= bcastling | blongcastling;			// FIXME: dangerous?
                        m++;
                    }
                }
            }
            else
            {
                if ( (cr & 15) == 8 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether f8, g8 free AND e8, f8, g8 not attacked
                    if ( !((u32)all & 0x60) &&
                            !((u32)eatt & 0x70) )
                    {
                        // castling possible!
                        BitMove &mv = *m;
                        mv.packed = temp.packed;
                        mv.to = p + 2;
                        mv.flags |= bcastling;			// FIXME: dangerous?
                        m++;
                    }
                }
                if ( ((cr>>4) & 15) == 1 )			// FIXME: chess 960 not supported (yet)
                {
                    // check whether b8, c8, d8 free AND c8, d8, e8 not attacked
                    if ( !((u32)all & 0x0e) &&
                            !((u32)eatt & 0x1c) )
                    {
                        // castling possible!
                        BitMove &mv = *m;
                        mv.packed = temp.packed;
                        mv.to = p - 2;
                        mv.flags |= bcastling | blongcastling;			// FIXME: dangerous?
                        m++;
                    }
                }
            }
        }
    }

    // now: extract targets
//	u8 tpos[64];
//	i32 i, ntpos = getPos( targ, tpos );
//	for (i=0; i<ntpos; i++)
    while ( targ )
    {
        u8 tp = popLSB( targ );//tpos[i];
        if ( tp == ekp )
            continue;				// illegal capture of enemy king!
        BitMove &mv = *m;
        mv.packed = temp.packed;
        mv.to = tp;
        bool cap = (ONESHL( tp ) & b.bits[ ball - c ]) != 0;	// fixme: hack
        if ( type == 1 )
        {
            if ( !cap )
                continue;			// skip noncaptures
        } else if ( type == 2 )
        {
            if ( cap )
                continue;			// skip captures
        }
        if ( cap )
            mv.flags |= bcapture;
        m++;
    }
}

// check for direct diagonal pin
static bool diagPin( u8 dflg, u8 pos, /*const u64 &m, */const u64 &hm, const u64 &bm )
{
    i32 i;
    u64 b = ONESHL( pos );
    switch ( dflg )
    {
    case 0:
        for (i=0;i<7;i++)
        {
            b |= (b >> 1) & R1MASK;
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 1:
        for (i=0;i<7;i++)
        {
            b |= (b >> 9) & R1MASK;
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 2:
        for (i=0;i<7;i++)
        {
            b |= (b >> 8);
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 3:
        for (i=0;i<7;i++)
        {
            b |= (b >> 7) & L1MASK;
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 4:
        for (i=0;i<7;i++)
        {
            b |= (b << 1) & L1MASK;
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 5:
        for (i=0;i<7;i++)
        {
            b |= (b << 9) & L1MASK;
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 6:
        for (i=0;i<7;i++)
        {
            b |= (b << 8);
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    case 7:
        for (i=0;i<7;i++)
        {
            b |= (b << 7) & R1MASK;
            b &= ~bm;
            if ( !b )
                return false;
            if ( b & hm )
                return true;
        }
        return false;
    }
    return false;
    /*	for (i=0;i<7;i++)
    	{
    		switch( dflg )
    		{
    		case 0:
    			b |= (b >> 1) & R1MASK;
    			break;
    		case 1:
    			b |= (b >> 9) & R1MASK;
    			break;
    		case 2:
    			b |= (b >> 8);
    			break;
    		case 3:
    			b |= (b >> 7) & L1MASK;
    			break;
    		case 4:
    			b |= (b << 1) & L1MASK;
    			break;
    		case 5:
    			b |= (b << 9) & L1MASK;
    			break;
    		case 6:
    			b |= (b << 8);
    			break;
    		case 7:
    			b |= (b << 7) & R1MASK;
    			break;
    		}
    		b &= ~bm;
    		if ( !b )
    			return false;
    		if ( b & hm )
    			return true;
    	}
    	return false;*/
#if 0
#if 1
    // FIXME: this seems faster
    i32 i;
    u64 b = ONESHL( pos );
    for (i=0;i<7;i++)
    {
        switch ( dflg )
        {
        case 0:
            b |= (b >> 1) & R1MASK;
            break;
        case 1:
            b |= (b >> 9) & R1MASK;
            break;
        case 2:
            b |= (b >> 8);
            break;
        case 3:
            b |= (b >> 7) & L1MASK;
            break;
        case 4:
            b |= (b << 1) & L1MASK;
            break;
        case 5:
            b |= (b << 9) & L1MASK;
            break;
        case 6:
            b |= (b << 8);
            break;
        case 7:
            b |= (b << 7) & R1MASK;
            break;
        }
        b &= ~bm;
        if ( !b )
            return false;
        if ( b & hm )
            return true;
    }
    return false;
#else
    u64 m = diagMask[ dflg ][ pos ];
    if ( dflg < 4 )
    {
        i8 bit = getMSB( (hm | bm) & m );
        if ( bit < 0 )
            return 0;
        return (hm & ONESHL(bit)) != 0;
    }
    else
    {
        m &= (hm | bm);
        return ( (m & -m) & hm ) != 0;
    }
#endif
#endif
}

u64 BitBoard::potPins( u8 kp ) const
{
    i32 turn = (bits[ wall ] & ONESHL(kp)) ? white : black;
    // NEW! potPins: turn doesn't matter!
    // works but no speed gain :(
    // values 0-8: l, ul, u, ur, r, dr, d, dl, none
    u64 mask = bits[ wall ] | bits[ ball ];
    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);
            const u64 pmsk = ONESHL( bit );
            i32 piececolor = (pmsk & bits[ wall ]) ? white : black;
            if ( piececolor == turn )
            {
                dm &= ~pmsk;			// remove "our" piece and see whether there's an enemy slider hidden behind!
				if ( dm )
				{
					i8 bit2 = (i<4) ? getMSB(dm) : getLSB(dm);
                    const u64 pmsk2 = ONESHL( bit2 );
                    if ( pmsk2 & bits[ ball - turn ] )
                    {
                        if ( pmsk2 & bits[ bqueen - turn ] )
                        {
                            msk |= pmsk;
                        }
                        else if ( !(i & 1) && (pmsk2 & bits[ brook - turn ]) )
                        {
                            // hv only
                            msk |= pmsk;
                        }
                        else if ( (i & 1) && (pmsk2 & bits[ bbishop - turn ]) )
                        {
                            // diag only
                            msk |= pmsk;
                        }
                    }
                }
            }
        }
    }
    return msk;
}

class QSortMoves
{
protected:
    BitMove *m;
    i32 *mval;
    BitMove tm;

public:
    inline QSortMoves( i32 nm, BitMove *m_, i32 *mval_ ) : m(m_), mval(mval_)
    {
        sort( nm );
    }

    void sort( int h, int l = 0 )
    {
        i32 x, t;		// tmp_elems
        int i, j;	// tmp_vals
        if ( --h <= l )
        {
            return;
        }
        x = mval[ ( ( i = l ) + ( j = h ) ) >> 1 ];
        for (;;) {
            while ( mval[i] > x ) i++;
            while ( x > mval[j] ) j--;
            if ( i >= j )
            {
                break;
            }
            // swap!!
            t = mval[i];
            mval[i] = mval[j];
            mval[j] = t;
            tm.packed = m[i].packed;
            m[i].packed = m[j].packed;
            m[j].packed = tm.packed;

            i++;
            j--;
        }
        if ( i == j )
        {
            i++;
            j--;
        }
        if ( l < j ) sort( j + 1, l );
        if ( i < h ) sort( h + 1, i );
    }
};

i32 BitBoard::moves( BitMove *mv, i32 *mval_, i32 type, const BitSearch *bsref ) const
{
    i32 i;
    const BitFlags &f = flags();
    const i32 c = f.turn;
    BitMove *omv = mv;
    u64 all = bits[ wall ] | bits[ ball ];
    u64 ediagsliders = bits[ bbishop - c ] | bits[ bqueen - c ];	// enemy diagonal sliders
    u64 eorthosliders = bits[ brook - c ] | bits[ bqueen - c ];		// enemy orthogonal sliders
    u64 ediagblockmask = all ^ ediagsliders;
    u64 eorthoblockmask = all ^ eorthosliders;
    u8 kpos = f.kingPos[ c ];
    u64 validTargMask = (u64)0 - 1;
    // illegal targets: kings!
    validTargMask &= NONESHL( f.kingPos[ white ] );
    validTargMask &= NONESHL( f.kingPos[ black ] );
    if ( type == 1 )
    {
        validTargMask &= bits[ ball - c ];			// captures only
        if ( f.ep )
        {
            // add epsquare to validTargMask! IMPORTANT!!
            if ( c == white )
            {
                validTargMask |= ONESHL( f.ep ) >> 8;
            }
            else
            {
                validTargMask |= ONESHL( f.ep ) << 8;
            }
        }
        // IMPORTANT! add queen_promotions!
        if ( c == white )
        {
            validTargMask |= (bits[ wpawn ] >> 8) & 0xff;
        }
        else
        {
            validTargMask |= (bits[ bpawn ] << 8) & U64C(0xff00000000000000);
        }

    }
    else if ( type == 2 )
    {
        validTargMask &= ~bits[ ball - c ];			// non-captures only
    }

    u64 diagPins = bishopAttm( f.kingPos[c], all );
    u64 orthoPins = rookAttm( f.kingPos[c], all );;

    if ( f.check )
    {
        // well... seem to work but there doesn't seem to be any speedup at all...
        // extra handling!
        u8 kp = f.kingPos[ c ];
        u64 targm = kingMov[ kp ];
        const u64 &km = ONESHL( kp );

        u64 msk;
        // determine checking piece(s)
        if ( (msk = (bits[ bknight - c ] & knightMov[ kp ])) != 0 )
        {
            // knight check!
            // mask using LSBit!
            // note: only one knight can check at a time! (no pawn/slider validation needed)
            targm |= msk & -msk;
        }
        else
        {
            if ( c == white && (msk = (bits[ bpawn ] & bpAttm[ kp ])) != 0 )
            {
                // pawn check!
                // mask using LSBit!
                // note: only one pawn can check at a time! (no further validation needed)
                msk &= -msk;
                targm |= msk;
                if ( f.ep && (ONESHL( f.ep ) & msk)  )
                {
                    targm |= ONESHL( f.ep - 8 );
                }
            }
            else
                if ( c == black && (msk = (bits[ wpawn ] & wpAttm[ kp ])) != 0 )
                {
                    // pawn check!
                    // mask using LSBit!
                    // note: only one pawn can check at a time! (no further validation needed)
                    msk &= -msk;
                    targm |= msk;
                    if ( f.ep && (ONESHL( f.ep ) & msk)  )
                    {
                        targm |= ONESHL( f.ep + 8 );
                    }
                }
                else
                {
                    // check sliders
                    if ( (diagPins & ediagsliders) || (orthoPins & eorthosliders ) )
                    {
                        for (i=0; i<8; i++)
                        {
                            u64 dm = diagMask[ i ][ kp ];
                            if ( !(dm &= all) )
                                continue;
                            i8 bit = (i<4) ? getMSB(dm) : getLSB(dm);
                            const u64 &bm = ONESHL( bit );
                            if ( bm & ((i & 1) ? ediagsliders : eorthosliders ) )
                            {
                                // have attacking slider. stop now. no more sliders can be found!
                                targm |= bm | betweenMask[ kp ][ bit ];
                                break;
                            }
                        }
                    }
                }
        }

        validTargMask &= targm;
    }

    if ( type == 3 )	// mate/stalemate check
    {
        bkingMove( type, *this, c, kpos, mv, all );
        if ( mv > omv )
        {
            return mv - omv;		// one move will do
        }
    }

//	for ( i = pawn; i < king; i++ )
    {
//		i32 ind = INDEX( c, i );
        u64 _m = bits[ wall + c /*ind*/ ] & NONESHL( kpos );
        /*		if ( !m )
        			continue;*/
        // now: extract positions
//		u8 pos[64];
//		i32 npos = getPos( m, pos );
//		i32 j;
//		for (j=0; j<npos; j++)
        while (_m)
        {
            // now try to get all valid moves from pos[j]
            u8 p = popLSB(_m);//pos[j];
            i = pieces[ p ] & 127;
            u8 dflg = diagFlags[ p ][ kpos ];
            u64 targMask = validTargMask;	//(u64)0 - 1;
            bool pinned = false;
            if ( dflg < 8 )
            {
                // pin threat detected... examine further
                const u64 &m = diagMask[ dflg ][ kpos ];
                if ( dflg & 1 )
                {
                    // check diag sliders
                    if ( ediagsliders & m )
                    {
                        // diagonal sliders found!
                        // BUT: can only be diagonal pin if lsb/msb is p
                        u64 tmp = all & m;
						if ( tmp )
						{
							i8 bit = (dflg<4) ? getMSB(tmp) : getLSB(tmp);
							if ( bit == p )
							{
								if ( diagPin( dflg, p, /*m, */ediagsliders, ediagblockmask ) )
								{
									pinned = true;
									targMask = m & validTargMask;
								}
							}
                        }
                    }
                }
                else
                {
                    // check ortho sliders
                    if ( eorthosliders & m )
                    {
                        // orthogonal sliders found!
                        // BUT: can only be diagonal pin if lsb/msb is p
                        u64 tmp = all & m;
						if ( tmp )
						{
							i8 bit = (dflg<4) ? getMSB(tmp) : getLSB(tmp);
							if ( bit == p )
							{
								if ( diagPin( dflg, p, /*m, */eorthosliders, eorthoblockmask ) )
								{
									pinned = true;
									targMask = m & validTargMask;
								}
							}
						}
                    }
                }
            }
            switch ( i )
            {
            case pawn:
                bpawnMove( type, *this, c, p, mv, all, targMask );
                break;
            case knight:
                bknightMove( type, *this, c, p, mv, all, targMask );
                break;
            case bishop:
                bbishopMove( type, *this, c, p, mv, all, targMask );
                break;
            case rook:
                brookMove( type, *this, c, p, mv, all, targMask );
                break;
            case queen:
                bqueenMove( type, *this, c, p, mv, all, targMask );
                break;
            }
            if ( type == 3 && mv != omv )			// mate/stalemate check: we're done! (1 move will do)
            {
                return mv - omv;
            }
        }
    }
    if ( type == 3 )						// mate/stalemate check: we're done!
    {
        return mv - omv;
    }
    bkingMove( type, *this, c, kpos, mv, all );

    // time to sort moves!
    i32 mvalb[ bmaxMoves ];
    i32 *mval = mval_ ? mval_ : mvalb;
    i32 nm = mv - omv;

#ifdef DEBUG_MOVES
    static i64 totMoves = 0;
    static i64 winCaps = 0;
    static i64 goodCaps = 0;
    static i64 badCaps = 0;
    static i64 promos = 0;
    static i64 underpromos = 0;
    static i64 castlings = 0;
    static i64 moveGens = 0;
    static i32 moveGenCounter = 0;

    if ( !(++moveGenCounter & 65535 ) )
    {
        printf("#debug movgens: %I64d moves: total %I64d winCaps: %I64d goodCaps: %I64d badCaps: %I64d promos: %I64d underpromos: %I64d castlings: %I64d\n",
               moveGens, totMoves, winCaps, goodCaps, badCaps, promos, underpromos, castlings
              );
        printf("#debug percentage: winCaps: %0.2lf%% goodCaps: %0.2lf%% badCaps: %0.2lf%% promos: %0.2lf%% underpromos: %0.2lf%% castlings: %0.2lf%%\n",
               100.0 * winCaps / totMoves,
               100.0 * goodCaps / totMoves,
               100.0 * badCaps / totMoves,
               100.0 * promos / totMoves,
               100.0 * underpromos / totMoves,
               100.0 * castlings / totMoves
              );
        moveGenCounter = 0;
    }
    moveGens++;
#endif

    ASSERT( nm <= bmaxMoves );

    // assign move sorting values
    {
        i32 i;
        for (i=0; i<nm; i++)
        {
#ifdef DEBUG_MOVES
            totMoves++;
            if ( omv[i].flags & bcastling )
            {
                castlings++;
            }
            if ( omv[i].promo )
            {
                if ( omv[i].promo == queen )
                {
                    promos++;
                }
                else
                {
                    underpromos++;
                }
            }
#endif
            if ( omv[i].flags & bcastling )
                mval[ i ] = 32 << 16;
            else if ( omv[i].promo )
            {
                mval[ i ] = omv[i].promo == queen ? (49 << 16) : (28 << 16);
                if ( see( omv[i] ) < 0 )
                    mval[ i ] = 28 << 16;
            }
            else if ( omv[i].flags & bcapture )
            {
#ifdef USE_SEE
                i32 val = see( omv[i] );
#ifdef DEBUG_MOVES
                if ( val > 0 )
                {
                    winCaps++;
                }
                else if ( val == 0 )
                {
                    goodCaps++;
                }
                else
                {
                    badCaps++;
                }
#endif
                mval[ i ] = (val >= 0) ? ((48 + val) << 16) : ((16 + val) << 16);
                if ( val >= 0 )
                {
                    omv[i].flags |= bgoodcap;
                }

#if 1
                // MVV/LVA subsorting...
                const u64 &mf = ONESHL( omv[i].from );
                const u64 &mt = ONESHL( omv[i].to );
                i32 ftype = 10;	// assume king capture
                if ( mf & bits[ wpawn+c ])
                    ftype = 1;
                else if ( mf & (bits[ wknight+c ] | bits[ wbishop+c ]) )
                    ftype = 3;
                else if ( mf & (bits[ wrook+c ]) )
                    ftype = 5;
                else if ( mf & (bits[ wqueen+c ]) )
                    ftype = 9;
#if 1
                i32 ttype = 1;		// assume pawn
                /*				if ( mt & bits[ bpawn-c ])
                					ttype = 1;
                				else */
                if ( mt & (bits[ bknight-c ] | bits[ bbishop-c ]) )
                    ttype = 3;
                else if ( mt & (bits[ brook-c ]) )
                    ttype = 5;
                else if ( mt & (bits[ bqueen-c ]) )
                    ttype = 9;
#endif
                //mval[i] -= ftype;		//+= ttype - ftype;
                mval[i] += ttype - ftype;
#endif

#else
#if 1
                const u64 &mf = ONESHL( omv[i].from );
                const u64 &mt = ONESHL( omv[i].to );
                i32 ftype = 0;	// assume king capture
                if ( mf & bits[ wpawn+c ])
                    ftype = 1;
                else if ( mf & (bits[ wknight+c ] | bits[ wbishop+c ]) )
                    ftype = 3;
                else if ( mf & (bits[ wrook+c ]) )
                    ftype = 5;
                else if ( mf & (bits[ wqueen+c ]) )
                    ftype = 9;

                i32 ttype = 1;		// assume pawn
                /*				if ( mt & bits[ bpawn-c ])
                					ttype = 1;
                				else */
                if ( mt & (bits[ bknight-c ] | bits[ bbishop-c ]) )
                    ttype = 3;
                else if ( mt & (bits[ brook-c ]) )
                    ttype = 5;
                else if ( mt & (bits[ bqueen+c ]) )
                    ttype = 9;

                ttype -= ftype;

                mval[ i ] = 48 + ttype;
                if ( mval[i] < 48 )
                {
                    mval[i] = 16 + ttype;
                }
#else
                mval[ i ] = 48;
#endif
#endif
            }
            else
            {
                mval[ i ] = 22 << 16;	// + (rand() & 7);			// normalmove
                if ( bsref )
                {
                    i32 pt = pieceType( omv[i].from );
                    mval[ i ] += bsref->hist.history[ f.turn ][ pt ][ omv[i].to ];	//bsref->history[ f.turn ][ omv[i].from ][ omv[i].to ];
//					mval[ i ] -= see( omv[i] ) < 0 ? 1 << 16 : 0;		// FIXME: just testing! (well... seems to be worth 25 elo. very strange
                    // (scored 55% against prev_version in 40 games)
                }
            }
#ifdef RANDOMIZE_SCORE
            mval[i] <<= 8;
            mval[i] += randomOrderingTable[ nm ][i];
#endif
        }
#ifndef USE_INCREMENTAL_MOVESORTING
#if 0
        // use bubble sort to sort moves!
        // FIXME: qsort for low elem count not faster?

        i32 j;
        for (i=0; i<nm; i++)
        {
            for (j=0; j<i; j++)
            {
                if ( mval[i] > mval[j] )
                {
                    std::swap( mval[i], mval[j] );
                    std::swap( omv[i], omv[j] );
                }
            }
        }
#else
        // FIXME: quicksorted moves differ from bubble sorted (order). why? (stability problems?)
//		QSortMoves qsm( nm, omv, mval );
#if 0
        {
            i32 j;
            for (j=0; j<nm; j++)
            {
                i32 k;
                for (k=j+1; k<nm; k++)
                {
                    if ( mval[k] > mval[j] )
                    {
                        // QSort bug!
                        logFmt("QSORT_BUG!\n");
                        QSortMoves qsm( nm, omv, mval );
                    }
                }
            }
        }
#endif
#endif
#endif

    }

    return mv - omv;
}

static const u64 matKNk = (U64C(1) << MATSHIFT( white, knight ));

// returns true if position is a draw (either due to insufficient material or due to 50 move rule)
bool BitBoard::isDraw() const
{
    const BitFlags &f = flags();
    if ( f.fiftyCounter >= fiftyCounterMax )
        return true;
    i32 tmat = mat().tmat;
    if ( tmat > (tmatTab[ knight ]<<1) )
    {
        return false;
    }
    if ( bits[ wpawn ] || bits[ bpawn ] )
        return false;
    if ( !tmat )
        return true;					// lone kings

#if 1
    i32 wbc = bitCount( bits[ wbishop ] );
    i32 wnc = bitCount( bits[ wknight ] );
    i32 bbc = bitCount( bits[ bbishop ] );
    i32 bnc = bitCount( bits[ bknight ] );

    if ( !wbc && !wnc )
    {
        // lone king (white)
        // king + bighop/king + knight against king => draw
        if ( bbc + bnc == 1 )
            return true;
        if ( bnc == 2 )		// king + 2 knights against king => draw
        {
			if ( !(knightMov[ f.kingPos[white] ] & bits[ bknight ]) )
				return true;
        }
    }

    if ( !bbc && !bnc )
    {
        // lone king (black)
        // king + bighop/king + knight against king => draw
        if ( wbc + wnc == 1 )
            return true;
        if ( wnc == 2 )		// king + 2 knights against king => draw
        {
			if ( !(knightMov[ f.kingPos[black] ] & bits[ wknight ]) )
				return true;
        }
    }

    // king + bishop against king + bishop (with bishops on same square colors) => draw
    if ( !wnc && !bnc && wbc == 1 && bbc == 1 )
    {
        u64 bi = bits[ wbishop ] | bits[ bbishop ];
        if ( !( bi & WSQUARES ) || !( bi & BSQUARES ) )
            return true;
    }
#endif
    return false;
}

// returns -1 if position is mate, 1 if stalemate and 0 if ok
// (due to no valid moves left)
i32 BitBoard::isMateOrStalemate() const
{
    i32 vm;
    BitMove m[ bmaxMoves ];
    vm = moves( m, 0, 3 );
    return vm ? 0 : flags().check ? -1 : 1;
}

bool BitBoard::validate() const
{
    i32 i;
    if ( bits[ wall ] & bits[ ball ] )
        return false;
    i32 j;
    u64 call = 0;
    for (j=white; j<=black; j++)
    {
        for (i=pawn; i<king; i++)
        {
            if ( call & bits[ INDEX( j, i ) ] )
                return false;
            call |= bits[ INDEX( j, i ) ];
        }
    }
    return true;
}

void BitBoard::dump()
{
    Board tb;
    tb.reset();
    i32 i;
    u64 tmp;
    tb.flags.ep = flags().ep;
    for (i=0; i<64; i++)
    {
        u64 m = ONESHL( i );
        Piece &p = tb.pieces[i];
        p.packed = 0;
        p.type = none;

        i32 j, c;
        for (c=white; c<=black; c++)
        {
            for (j=pawn; j<king; j++)
            {
                i32 ind = INDEX(c, j);
                if ( bits[ ind ] & m )
                {
                    p.type = j;
                    p.color = c;
                }
                if ( pieceMoved(i) )
                {
                    p.moved = 1;
                }
            }
            if ( i == flags().kingPos[c] )
            {
                p.color = c;
                p.type = king;
                if ( flags().castRights[ c ] )
                    p.moved = 0;
                else
                    p.moved = 1;
            }
        }
    }
    tb.update( flags().turn );
    tb.dump();
    const BitMat &bm = mat();
    logFmt("turn = %s\ncheck = %d\ndmat = %d\ntmat = %d\npair[white] = %d\npair[black] = %d\n",
           flags().turn == white ? "white" : "black",
           flags().check ? 1 : 0,
           bm.dmat, bm.tmat, bm.pair[ white ], bm.pair[ black ]
          );
    logFmt("check = %s\n", flags().check ? "yes" : "no");
    logFmt("ep = %02x\n", flags().ep);
    logFmt("fiftyCounter = %d\n", flags().fiftyCounter);
    logFmt("castRights[white] = %02x castRights[black] = %02x\n",
           flags().castRights[white], flags().castRights[black] );
}

// get discovered checkers mask
u64 BitBoard::discoveredCheckers() const
{
#ifdef OLD_ISCHECK
    return 0;
#else
    // TODO: verify!
    const BitFlags &f = flags();
    u8 ekp = f.kingPos[ f.turn ^ 1 ];

    u64 res = 0;

    u64 all = bits[ wall ] | bits[ ball ];

    u64 queens = bits[ wqueen + f.turn ];
    u64 cand = bits[ wall + f.turn ] & ~queens;

    // get diag/ortho sliders
    u64 diagSliders = bits[ wbishop + f.turn ] | queens;
    u64 orthoSliders = bits[ wrook + f.turn ] | queens;

    u64 msk = diagAttMask[ ekp ];
    if ( msk & diagSliders )
    {
        // check diagonal candicates
        u64 tmp = bishopAttm( ekp, all ) & cand;
        if ( tmp )
        {
            // evaluate "true" candidates...
            u64 tmp2 = bishopAttm( ekp, all & ~tmp ) & diagSliders;	// here are discovered checking pieces
            while ( tmp2 )
            {
                res |= betweenMask[ ekp ][ popLSB(tmp2) ] & tmp;
            }
        }
    }

    msk = orthoAttMask[ ekp ];
    if ( msk & orthoSliders )
    {
        // check ortho candicates
        u64 tmp = rookAttm( ekp, all ) & cand;
        if ( tmp )
        {
            // evaluate "true" candidates...
            u64 tmp2 = rookAttm( ekp, all & ~tmp ) & orthoSliders;	// here are discovered checking pieces
            while ( tmp2 )
            {
                res |= betweenMask[ ekp ][ popLSB( tmp2 ) ] & tmp;
            }
        }
    }

    return res;
#endif
}

// get true pin mask
u64 BitBoard::pinned() const
{
    // TODO: verify!
    const BitFlags &f = flags();
    u8 kp = f.kingPos[ f.turn ];

    u64 res = 0;

    u64 all = bits[ wall ] | bits[ ball ];

    u64 cand = bits[ wall + f.turn ];

    // get diag/ortho sliders
    const u64 &queens = bits[ bqueen - f.turn ];
    u64 diagSliders = bits[ bbishop - f.turn ] | queens;
    u64 orthoSliders = bits[ brook - f.turn ] | queens;

    u64 msk = diagAttMask[ kp ];
    if ( msk & diagSliders )
    {
        // check diagonal candicates
        u64 tmp = bishopAttm( kp, all ) & cand;
        if ( tmp )
        {
            // evaluate "true" candidates...
            u64 tmp2 = bishopAttm( kp, all & ~tmp ) & diagSliders;	// here are discovered checking pieces
            while ( tmp2 )
            {
                res |= betweenMask[ kp ][ popLSB( tmp2 ) ] & tmp;
            }
        }
    }

    msk = orthoAttMask[ kp ];
    if ( msk & orthoSliders )
    {
        // check ortho candicates
        u64 tmp = rookAttm( kp, all ) & cand;
        if ( tmp )
        {
            // evaluate "true" candidates...
            u64 tmp2 = rookAttm( kp, all & ~tmp ) & orthoSliders;	// here are discovered checking pieces
            while ( tmp2 )
            {
                res |= betweenMask[ kp ][ popLSB( tmp2 ) ] & tmp;
            }
        }
    }
    return res;
}

void bitBoardInit()
{
    BitBoard bb;
    bb.reset();
}

// returns piece_moved for a specific square (only valid for rooks)
bool BitBoard::pieceMoved( u8 sq ) const
{
    i32 pt = pieces[ sq ] & 127;
    i32 c = pieces[ sq ] >> 7;
    if ( pt != king && pt != rook )
    {
        return true;
    }
    if ( pt == king )
    {
        return flags().castRights[ c ] == 0;
    }
    // ROOK
    bool moved = true;
    if ( sq == A1 && c == white && (flags().castRights[ white ] & 0xf0) )
    {
        moved = 0;
    }
    else
        if ( sq == H1 && c == white && (flags().castRights[ white ] & 0x0f) )
        {
            moved = 0;
        }
        else
            if ( sq == A8 && c == black && (flags().castRights[ black ] & 0xf0) )
            {
                moved = 0;
            }
            else
                if ( sq == H8 && c == black && (flags().castRights[ black ] & 0x0f) )
                {
                    moved = 0;
                }
    return moved;
}

// get FEN for current position
void BitBoard::getFEN( char *fen ) const
{
    const char *bp = " pnbrqk";
    const char *wp = " PNBRQK";
    i32 turn = flags().turn;
    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++)
        {
            u8 p_type = pieces[y*8+x];
            u8 p_color = (p_type & 128) >> 7;
            p_type &= 127;
            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 = flags().castRights[ white ];
    cb = flags().castRights[ black ];

    if ( cw || cb )
    {
        if ( cw )
        {
            i32 sh = cw & 15;
            i32 ln = 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 sh = cb & 15;
            i32 ln = cb >> 4;
            if ( sh == 8 )
                *fen++ = 'k';
            else if ( sh )
                *fen++ = 'a' + sh - 1;
            if ( ln == 1 )
                *fen++ = 'q';
            else if ( sh )
                *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...
}

// returns number of pieces (men) on the board
i32 BitBoard::numMen() const
{
	return bitCount( bits[wall] | bits[ball] );
}
