#include "bitmovegen.h"
#include "bitconfig.h"
#include "bitmagic.h"
#include "bitpsq.h"

//#define USE_COUNTER

// must match same def in bitboard.cpp!
//#define RANDOMIZE_SCORE

// always generate ALL moves!
//#define DEBUG_MOVEGEN

//#define VALIDATE_MOVECOUNT

//#define USE_FULL_QS

// if true, even for evasions, all moves are generated first
//#define USE_SINGLEREPLY

//#define USE_SORTING

// new: only sort using MVV/LVA, delay SEE (should be faster)
#define USE_DELAYED_SEE

// new: only sort using MVV/LVA, delay SEE (qsearch only; should be faster)
#define USE_DELAYED_SEE_QS

#define VALIDATE_HASHMV

// make_first_move
static void makeFirst( const BitMove &bm, i32 bval, i32 vm, BitMove *m, i32 *mval, i32 killer = 0 )
{
    if ( bm.packed )
    {
        i32 i;
        i32 dest = 0;
        for (i=0; i<vm; i++)
        {
#if 0
            if ( killer && (m[i].promo/* == queen*/ || (m[i].flags & (bcastling|bgoodcap))) )
            {
                dest = i+1;
            }
#endif
            if ( m[i].packed == bm.packed )
            {
                /*				i32 j;
                				for (j=i; j>dest; j--)
                				{
                					m[j].packed = m[j-1].packed;
                					mval[j] = mval[j-1];
                				}
                				m[dest].packed = bm.packed;
                				mval[dest] = bval;*/
                mval[i] = bval;
                return;
            }
        }
    }
}

#ifdef VALIDATE_MOVECOUNT
static void boardFromBitBoard( Board &b, const BitBoard &bb )
{
    i32 turn = bb.flags().turn;
    i32 i;
    b.clear(turn);
    for (i=white; i<=black; i++)
    {
        i32 j;
        for (j=pawn; j<king; j++)
        {
            const u64 &m = bb.bits[ INDEX(i, j) ];
            u8 pos[64];
            i32 npos = getPos( m, pos );
            i32 k;
            for (k=0; k<npos; k++)
            {
                Piece &p = b.pieces[ pos[k] ];
                p.color = i;
                p.type = j;
                p.moved = (bb.bits[ rmoved ] & ONESHL( pos[k] )) ? 1 : 0;
            }
        }
        u8 pos = bb.flags().kingPos[ i ];
        Piece &p = b.pieces[ pos ];
        p.color = i;
        p.type = king;
        u8 cr = bb.flags().castRights[ i ];
        if ( !cr )
        {
            b.flags.castled |= (1 << i);
        }
        p.moved = !cr ? 1 : 0;
        b.flags.kingPos[i] = bb.flags().kingPos[ i ];
    }
    b.flags.lastMoved = 255;		// dunno
    b.flags.ep = bb.flags().ep;
    b.flags.fiftyCounter = bb.flags().fiftyCounter;
    b.update( turn );
}
#endif

// BitMoveGen

BitMoveGen::BitMoveGen( const BitBoard &b, i32 ply_,
                        const BitSearch &search_, const BitMove &hashmv_, BitMoveGenType type )
        :	ply(ply_), search(search_), stack(search_.stack), board(b), hashmv(hashmv_), phase( type ), moveCount(0),
        badCapCount(0), finishedCount(0), returnCount(3), id(0), specPhase(0)
{
    const BitFlags &bf = board.flags();
    u8 ekp = bf.kingPos[ bf.turn ^ 1 ];
    discovered = board.discoveredCheckers();

#ifdef DEBUG_MOVEGEN
    phase = mgtFull;
#endif

    if ( phase == mgtEvasions || (phase != mgtQEvasions && board.flags().check) )
    {
        // in check => force full legal movegen!
        phase = mgtFull;
    }
#ifdef USE_SINGLEREPLY
    if ( phase == mgtFull )
    {
        returnCount = moveCount = b.moves( moves, mval, 0, &search );
#ifdef VALIDATE_MOVECOUNT
        Board tb;
        boardFromBitBoard( tb, b );
        Move tm[ 256 ];
        i32 tvm = tb.moves( tm, b.flags().turn );
        if ( returnCount != tvm )
        {
            logFmt("MOVGEN_BUG_FOUND!\n");
        }
#endif
//		search.sortHistory( moveCount, moves );
        // now process make_first_stuff!
        i32 i;
        for (i=bkillers-1; i>=0; i--)
        {
            makeFirst( stack[ply].killers[i],
#ifdef RANDOMIZE_SCORE
                       23 << 24
#else
                       (23 << 16) - i
#endif
                       , moveCount, moves, mval, 1 );
        }
        makeFirst( stack[ply].mateKiller, 0x7ffffffe, moveCount, moves, mval);
        makeFirst( hashmv, 0x7fffffff, moveCount, moves, mval );
////		makeFirst( ply == 0 ? stack[ply].pv[ply] : stack[ply-1].pv[ply], moveCount, moves );
    }
    else
#endif
    {
        pins = board.pinned();
        moveCount = -1;
    }
#if defined(DEBUG) || defined(_DEBUG)
    if ( hashmv.packed && !board.isLegal( hashmv, &pins ) )
    {
        printf("ILLEGAL_HASHMOVE!\n");
        char buf[64];
        hashmv.describeSAN( board, buf );
        printf("move: %s\n", buf );
        BitBoard bb(board);
        bb.dump();
        board.isLegal( hashmv, &pins );
    }
#endif
}

// return number of moves (only valid when generating full/evasions, in all other cases returns 3)
i32 BitMoveGen::count() const
{
    return returnCount;
}

static u32 pickBest( BitMove *moves, i32 *mval, i32 &id, i32 &count )
{
#ifdef USE_SORTING
	if ( id == 0 )
	{
		i32 i, j;
		for (i=0; i<count; i++)
		{
			for (j=i+1; j<count; j++)
			{
				if ( mval[i] < mval[j] )
				{
					std::swap( mval[i], mval[j] );
					std::swap( moves[i].packed, moves[j].packed );
				}
			}
		}
	}
	return moves[id++].packed;
#else
    i32 i;
    i32 besti = id;
    i32 bval = mval[ id ];
    for ( i = id + 1; i < count; i++)
    {
        if ( mval[ i ] > bval )
        {
            bval = mval[i];
            besti = i;
        }
    }
    // FIXME: how probable is THIS?!
    u32 res = moves[ besti ].packed;
    if ( besti == count-1 )
    {
        count--;
        return res;
    }
    // now swap id <-> besti
    if ( id != besti )
    {
        moves[besti] = moves[id];
        mval[besti] = mval[id];			// no need to swap!
    }
    id++;
    return res;
#endif
}

// returns next move (in mv_ref)
// returns true if valid, otherwise no more moves available
bool BitMoveGen::next( BitMove &mv )
{
    switch ( phase )
    {
	case mgtQEvasions:
#ifndef USE_SINGLEREPLY_QS
        if ( specPhase++ == 0 && hashmv.packed
#ifdef VALIDATE_HASHMV
                && board.isLegal( hashmv, &pins )
#endif
           )
        {
            mv = hashmv;
            return true;
        }
        if ( moveCount < 0 )
        {
            returnCount = moveCount = board.moves( moves, mval, 0, &search );
#ifdef VALIDATE_MOVECOUNT
            Board tb;
            boardFromBitBoard( tb, b );
            Move tm[ 256 ];
            i32 tvm = tb.moves( tm, b.flags().turn );
            if ( returnCount != tvm )
            {
                logFmt("MOVGEN_BUG_FOUND!\n");
            }
#endif
            //		search.sortHistory( moveCount, moves );
            // now process make_first_stuff!
            i32 i;
            for (i=bkillers-1; i>=0; i--)
            {
                makeFirst( stack[ply].killers[i],
#ifdef RANDOMIZE_SCORE
                           23 << 24
#else
                           (23 << 16) - i
#endif
                           , moveCount, moves, mval, 1 );
            }
            makeFirst( stack[ply].mateKiller, 0x7ffffffe, moveCount, moves, mval);
            makeFirst( hashmv, 0x7fffffff, moveCount, moves, mval );
            ////		makeFirst( ply == 0 ? stack[ply].pv[ply] : stack[ply-1].pv[ply], moveCount, moves );
        }
#endif
#ifndef USE_SINGLEREPLY_QS
        do {
#endif
            if ( id >= moveCount )
            {
                mv.packed = 0;
                phase = mgtFinished;
                return false;
            }
            // pick_best_move
            mv.packed = pickBest( moves, mval, id, moveCount );
#ifndef USE_SINGLEREPLY_QS
        } while ( mv.packed == hashmv.packed );
#endif
////		mv.packed = moves[ id++ ].packed;
        return true;
    case mgtFull:
    case mgtEvasions:
#ifndef USE_SINGLEREPLY
        if ( specPhase++ == 0 && hashmv.packed
#ifdef VALIDATE_HASHMV
                && board.isLegal( hashmv, &pins )
#endif
           )
        {
            mv = hashmv;
            return true;
        }
        if ( moveCount < 0 )
        {
            returnCount = moveCount = board.moves( moves, mval, 0, &search );
#ifdef VALIDATE_MOVECOUNT
            Board tb;
            boardFromBitBoard( tb, b );
            Move tm[ 256 ];
            i32 tvm = tb.moves( tm, b.flags().turn );
            if ( returnCount != tvm )
            {
                logFmt("MOVGEN_BUG_FOUND!\n");
            }
#endif
            //		search.sortHistory( moveCount, moves );
            // now process make_first_stuff!
            i32 i;
            for (i=bkillers-1; i>=0; i--)
            {
                makeFirst( stack[ply].killers[i],
#ifdef RANDOMIZE_SCORE
                           23 << 24
#else
                           (23 << 16) - i
#endif
                           , moveCount, moves, mval, 1 );
            }
            makeFirst( stack[ply].mateKiller, 0x7ffffffe, moveCount, moves, mval);
            makeFirst( hashmv, 0x7fffffff, moveCount, moves, mval );
            ////		makeFirst( ply == 0 ? stack[ply].pv[ply] : stack[ply-1].pv[ply], moveCount, moves );
        }
#endif
#ifndef USE_SINGLEREPLY
        do {
#endif
            if ( id >= moveCount )
            {
                mv.packed = 0;
                phase = mgtFinished;
                return false;
            }
            // pick_best_move
            mv.packed = pickBest( moves, mval, id, moveCount );
#ifndef USE_SINGLEREPLY
        } while ( mv.packed == hashmv.packed );
#endif
////		mv.packed = moves[ id++ ].packed;
        return true;
    case mgtBadCaptures:
    {
        i32 i;
        do
        {
            if ( id >= badCapCount )
            {
                mv.packed = 0;
                phase = mgtFinished;
                return false;
            }
            mv.packed = badCaps[ id++ ].packed;//pickBest( badCaps, badmval, id, badCapCount );
            for (i=0; i<finishedCount; i++)
            {
                if ( finishedMoves[i].packed == mv.packed )
                {
                    // already processed!
                    // new: REMOVE from list!!!
                    if ( --finishedCount > 0 )
                    {
                        finishedMoves[ i ].packed = finishedMoves[ finishedCount ].packed;
                    }
                    i = -1;
                    break;
                }
            }
            if ( i >= finishedCount && !board.pseudoIsLegal( mv, pins ) )
            {
                i = -1;			// skip illegal moves!
            }
        } while ( i < finishedCount );
		ASSERT( board.isLegal( mv, &pins ) );
    }
    return true;
    case mgtQChecks:
    {
        if ( moveCount < 0 )
        {
            moveCount = genQChecks( moves );
            scoreQChecks( moveCount, moves, mval );
        }
        i32 i;
        do
        {
            if ( id >= moveCount )
            {
                mv.packed = 0;
#ifdef USE_FULL_QS
                phase = mgtBadCaptures;
#else
                phase = mgtFinished;
#endif
                id = 0;
                moveCount = -1;
                return false;
            }
            mv.packed = pickBest( moves, mval, id, moveCount );
            for (i=0; i<finishedCount; i++)
            {
                if ( finishedMoves[i].packed == mv.packed )
                {
                    // already processed!
                    // new: REMOVE from list!!!
                    if ( --finishedCount > 0 )
                    {
                        finishedMoves[ i ].packed = finishedMoves[ finishedCount ].packed;
                    }
                    i = -1;
                    break;
                }
            }
            if ( i >= finishedCount && (!board.pseudoIsLegal( mv, pins ) || board.see( mv, 1 ) < 0) )
            {
                i = -1;			// skip illegal moves!
            }
        } while ( i < finishedCount );
		ASSERT( board.isLegal( mv, &pins ) );
    }
    return true;
    case mgtQCaptures:
    case mgtQCapturesWithChecks:
    {
        // generate_now!
        i32 i;
        if ( specPhase < 2 )
        {
            const BitMove *m;
            BitMove null;
            null.packed = 0;
            for ( ; specPhase < 2; specPhase++ )
            {
                switch ( specPhase )
                {
                case 0:
                    m = &hashmv;
                    break;
                case 1:
                    m = &stack[ply].mateKiller;
                    break;
                }
                if ( !m->packed )
                {
                    continue;
                }
                for (i=0; i<finishedCount; i++)
                {
                    if ( finishedMoves[ i ].packed == m->packed )
                        break;				// already_processed!
                }

                // new: skip legality check for hash move
                if ( i >= finishedCount && (
#ifndef VALIDATE_HASHMV
                            specPhase == 0 ||
#endif
                            board.isLegal( *m, &pins )) )
                {
                    break;
                }
            }
            if ( specPhase < 2 )
            {
                finishedMoves[ finishedCount++ ].packed = mv.packed = m->packed;
                specPhase++;
                return true;
            }
        }
        if ( moveCount < 0 )
        {
            // NOW: generate_captures!
            moveCount = genCaps( moves, 0 );
            ASSERT( moveCount < bmaxMoves );
            scoreQCaps( moveCount, moves, mval );

            // assume id == 0
            ASSERT( id == 0 );
        }
        do
        {
            if ( id >= moveCount )
            {
                // switch to next phase!
                phase = 
#ifdef USE_FULL_QS
                         (phase == mgtQCapturesWithChecks ? mgtQChecks : mgtBadCaptures)
#else
                         (phase == mgtQCapturesWithChecks ? mgtQChecks : mgtFinished )
#endif
				;
                id = 0;
                moveCount = -1;
                return next( mv );			// a little hack...
            }
            mv.packed = pickBest( moves, mval, id, moveCount );
#ifdef USE_DELAYED_SEE_QS
			if ( mv.flags & bcapture )
			{
				i32 see = board.see( mv, 1 );
				if ( see >= 0 )
				{
					mv.flags |= bgoodcap;
				}
				else
				{
                    badCaps[ badCapCount++ ].packed = mv.packed;
					i = -1;
					continue;
				}
			}
#endif
            for (i=0; i<finishedCount; i++)
            {
                if ( finishedMoves[i].packed == mv.packed )
                {
                    // new: REMOVE from list!!!
                    if ( --finishedCount > 0 )
                    {
                        finishedMoves[ i ].packed = finishedMoves[ finishedCount ].packed;
                    }
                    i = -1;
                    break;					// already processed!
                }
            }
            if ( i >= finishedCount && !board.pseudoIsLegal( mv, pins ) )
            {
                i = -1;			// skip illegal moves!
            }
        } while ( i < finishedCount );
		ASSERT( board.isLegal( mv, &pins ) );
    }
    return true;
    case mgtCaptures:
    {
        // generate_now!
        i32 i;
        if ( specPhase < 2 )
        {
            const BitMove *m;
            BitMove null;
            null.packed = 0;
            for ( ; specPhase < 2; specPhase++ )
            {
                switch ( specPhase )
                {
                case 0:
                    m = &hashmv;
                    break;
                case 1:
                    m = &stack[ply].mateKiller;
                    break;
                }
                if ( !m->packed )
                {
                    continue;
                }
                for (i=0; i<finishedCount; i++)
                {
                    if ( finishedMoves[ i ].packed == m->packed )
                        break;				// already_processed!
                }

                // new: skip legality check for hash move
                if ( i >= finishedCount && (
#ifndef VALIDATE_HASHMV
                            specPhase == 0 ||
#endif
                            board.isLegal( *m, &pins )) )
                {
                    break;
                }
            }
            if ( specPhase < 2 )
            {
                finishedMoves[ finishedCount++ ].packed = mv.packed = m->packed;
                specPhase++;
                return true;
            }
        }
        if ( moveCount < 0 )
        {
            // NOW: generate_captures!
            moveCount = genCaps( moves, 1 );
            ASSERT( moveCount < bmaxMoves );
            scoreCaps( moveCount, moves, mval, badCaps, badCapCount );

            // assume id == 0
            ASSERT( id == 0 );
        }
        do
        {
            if ( id >= moveCount )
            {
                // switch to next phase!
                phase = mgtNormal;
                id = 0;
                moveCount = -1;
                return next( mv );			// a little hack...
            }
            mv.packed = pickBest( moves, mval, id, moveCount );
#ifdef USE_DELAYED_SEE
			if ( mv.flags & bcapture )
			{
				i32 see = board.see( mv, 1 );
				if ( see >= 0 )
				{
					mv.flags |= bgoodcap;
				}
				else
				{
                    badCaps[ badCapCount++ ].packed = mv.packed;
					i = -1;
					continue;
				}
			}
#endif
            for (i=0; i<finishedCount; i++)
            {
                if ( finishedMoves[i].packed == mv.packed )
                {
                    // new: REMOVE from list!!!
                    if ( --finishedCount > 0 )
                    {
                        finishedMoves[ i ].packed = finishedMoves[ finishedCount ].packed;
                    }
                    i = -1;
                    break;					// already processed!
                }
            }
            if ( i >= finishedCount && !board.pseudoIsLegal( mv, pins ) )
            {
                i = -1;			// skip illegal moves!
            }
        } while ( i < finishedCount );
		ASSERT( board.isLegal( mv, &pins ) );
    }
    return true;
    case mgtNormal:
    {
        i32 i;
        if ( specPhase < 2 )
        {
            specPhase = 2;
        }
        if ( specPhase < 2 + bkillers )
        {
            const BitMove *m;
            for ( ; specPhase < 2+bkillers; specPhase++ )
            {
                m = &stack[ply].killers[ specPhase-2 ];
                if ( !m->packed )
                {
                    continue;
                }
                for (i=0; i<finishedCount; i++)
                {
                    if ( finishedMoves[i].packed == m->packed )
                    {
                        break;
                    }
                }
                if ( i >= finishedCount && board.isLegal( *m, &pins ) )
                {
                    break;					// ok!
                }
            }
            if ( specPhase < 2 + bkillers )
            {
                finishedMoves[ finishedCount++ ].packed = mv.packed = m->packed;
                specPhase++;
                return true;
            }
        }
        if ( moveCount < 0 )
        {
            // NOW: generate_normal_moves!
            moveCount = genQuiet( moves );//board.moves( moves, mval, 2, &search );
            ASSERT( moveCount < bmaxMoves );
            scoreQuiet( moveCount, moves, mval );
//				search.sortHistory( moveCount, moves );
            id = 0;
        }
        do
        {
            if ( id >= moveCount )
            {
                // switch to next phase!
                phase = mgtBadCaptures;
                id = 0;
                return next( mv );			// a little hack...
            }
            mv.packed = pickBest( moves, mval, id, moveCount );
            for (i=0; i<finishedCount; i++)
            {
                if ( finishedMoves[i].packed == mv.packed )
                {
                    // new: REMOVE from list!!!
                    if ( --finishedCount > 0 )
                    {
                        finishedMoves[ i ].packed = finishedMoves[ finishedCount ].packed;
                    }
                    i = -1;
                    break;					// already processed!
                }
            }
            if ( i >= finishedCount && !board.pseudoIsLegal( mv, pins ) )
            {
                i = -1;			// skip illegal moves!
            }
        } while ( i < finishedCount );
		ASSERT( board.isLegal( mv, &pins ) );
    }
    return true;
    case mgtFinished:
        return false;
    }
    return false;			// keep compiler happy
}

// NEW movegens!

// new: pseudo-legal move generators:
// generates captures PLUS all promotions (including underpromotions if desired)
i32 BitMoveGen::genCaps( BitMove *m, bool underpromo )
{
    BitMove *om = m;
    const BitFlags &f = board.flags();
    i32 c = f.turn;

    u64 src = board.bits[ wall + c ];
    u64 opp = board.bits[ ball - c ];
    u64 all = src | opp;
    opp &= NONESHL( f.kingPos[ c ^ 1 ] );

#if 1
	// pawns
	src = board.bits[ wpawn + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
		u64 targ = (c == white ? bpAttm[ pos ] : wpAttm[ pos ]) & opp;
		while ( targ )
		{
			m->from = pos;
			m->to = popLSB( targ );
			m->promo = ((m->to & 0x38) == 0x38 || !(m->to & 0x38)) ? queen : 0;
			m->flags = bcapture;
			m++;
			if ( underpromo && m[-1].promo )
			{
				m->packed = m[-1].packed;
				m->promo = rook;
				m++;
				m->packed = m[-1].packed;
				m->promo = knight;
				m++;
				m->packed = m[-1].packed;
				m->promo = bishop;
				m++;
			}
		}
		// enpassant capture
		if ( !((pos ^ f.ep) & 0x38) )
		{
			// rank ok
			if ( abs( (pos & 7) - (f.ep & 7) ) == 1 )
			{
				// file ok
				m->from = pos;
				m->to = ( c == white ) ? f.ep - 8 : f.ep + 8;
				m->promo = 0;
				m->flags = bcapture | benpassant;
				m++;
			}
		}
		// promotions
		if ( c == white && (pos & 0x38) == 0x08 && !(board.pieces[ pos - 8 ] & 127) )
		{
			m->from = pos;
			m->to = pos - 8;
			m->promo = queen;
			m->flags = 0;
			m++;
			if ( underpromo )
			{
				m->packed = m[-1].packed;
				m->promo = rook;
				m++;
				m->packed = m[-1].packed;
				m->promo = knight;
				m++;
				m->packed = m[-1].packed;
				m->promo = bishop;
				m++;
			}
		}
		else if ( c == black && (pos & 0x38) == 0x30 && !(board.pieces[ pos + 8 ] & 127) )
		{
			m->from = pos;
			m->to = pos + 8;
			m->promo = queen;
			m->flags = 0;
			m++;
			if ( underpromo )
			{
				m->packed = m[-1].packed;
				m->promo = rook;
				m++;
				m->packed = m[-1].packed;
				m->promo = knight;
				m++;
				m->packed = m[-1].packed;
				m->promo = bishop;
				m++;
			}
		}
	}

	// knights
	src = board.bits[ wknight + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = knightMov[ pos ] & opp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = bcapture;
            m->promo = 0;
            m++;
        }
	}

	// bishops
	src = board.bits[ wbishop + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = bishopAttm( pos, all ) & opp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = bcapture;
            m->promo = 0;
            m++;
        }
	}

	// rooks
	src = board.bits[ wrook + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = rookAttm( pos, all ) & opp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = bcapture;
            m->promo = 0;
            m++;
        }
	}

	// queens
	src = board.bits[ wqueen + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = queenAttm( pos, all ) & opp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = bcapture;
            m->promo = 0;
            m++;
        }
	}

	// king
	u8 pos = f.kingPos[c];
	u64 targ = kingMov[ pos ] & opp;
    while ( targ )
    {
		m->from = pos;
        m->to = popLSB( targ );
        m->flags = bcapture;
        m->promo = 0;
        m++;
    }

#endif
#if 0
	i32 nm = m - om;
	m = om;
	src = board.bits[ wall + c ];
#if 1
    while ( src )
    {
        u64 targ;
        u8 pos = popLSB( src );
        u8 p = board.pieces[ pos ] & 127;
        switch ( p )
        {
        case pawn:
            targ = (c == white ? bpAttm[ pos ] : wpAttm[ pos ]) & opp;
            while ( targ )
            {
                m->from = pos;
                m->to = popLSB( targ );
                m->promo = ((m->to & 0x38) == 0x38 || !(m->to & 0x38)) ? queen : 0;
                m->flags = bcapture;
                m++;
                if ( underpromo && m[-1].promo )
                {
                    m->packed = m[-1].packed;
                    m->promo = rook;
                    m++;
                    m->packed = m[-1].packed;
                    m->promo = knight;
                    m++;
                    m->packed = m[-1].packed;
                    m->promo = bishop;
                    m++;
                }
            }
            // enpassant capture
            if ( !((pos ^ f.ep) & 0x38) )
            {
                // rank ok
                if ( abs( (pos & 7) - (f.ep & 7) ) == 1 )
                {
                    // file ok
                    m->from = pos;
                    m->to = ( c == white ) ? f.ep - 8 : f.ep + 8;
                    m->promo = 0;
                    m->flags = bcapture | benpassant;
                    m++;
                }
            }
            // promotions
            if ( c == white && (pos & 0x38) == 0x08 && !(board.pieces[ pos - 8 ] & 127) )
            {
                m->from = pos;
                m->to = pos - 8;
                m->promo = queen;
                m->flags = 0;
                m++;
                if ( underpromo )
                {
                    m->packed = m[-1].packed;
                    m->promo = rook;
                    m++;
                    m->packed = m[-1].packed;
                    m->promo = knight;
                    m++;
                    m->packed = m[-1].packed;
                    m->promo = bishop;
                    m++;
                }
            }
            else if ( c == black && (pos & 0x38) == 0x30 && !(board.pieces[ pos + 8 ] & 127) )
            {
                m->from = pos;
                m->to = pos + 8;
                m->promo = queen;
                m->flags = 0;
                m++;
                if ( underpromo )
                {
                    m->packed = m[-1].packed;
                    m->promo = rook;
                    m++;
                    m->packed = m[-1].packed;
                    m->promo = knight;
                    m++;
                    m->packed = m[-1].packed;
                    m->promo = bishop;
                    m++;
                }
            }
            continue;
            break;
        case knight:
            targ = knightMov[ pos ] & opp;
            break;
        case bishop:
            targ = bishopAttm( pos, all ) & opp;
            break;
        case rook:
            targ = rookAttm( pos, all ) & opp;
            break;
        case queen:
            targ = queenAttm( pos, all ) & opp;
            break;
        case king:
            targ = kingMov[ pos ] & opp;
            break;
        default:
            targ = 0;
        }
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = bcapture;
            m->promo = 0;
            m++;
        }
    }
#endif
	if ( m - om != nm )
	{
		printf("capgen_error!\n");
		genCaps( om, underpromo );
	}
#endif
    return m - om;
}

i32 BitMoveGen::genQChecks( BitMove *m )
{
    // NOTE: all captures and (queen) promotions have already been generated
    // (this means that we WON'T be generating ANY underpromotions)
    // 3 distinct possibilites:
    // direct check
    // discovered check
    // castling into check
    BitMove *om = m;
    const BitFlags &f = board.flags();
    ASSERT( !f.check );
    i32 c = f.turn;

    u64 src = board.bits[ wall + c ];
    u64 all = src | board.bits[ ball - c ];
    u64 quiet = ~all;
    u8 ekp = f.kingPos[ c ^ 1 ];
    u64 ekdiag = bishopAttm( ekp, all ) & quiet;
    u64 ekortho = rookAttm( ekp, all ) & quiet;
    u64 ekknight = knightMov[ ekp ] & quiet;

#if 1
	// pawns
	src = board.bits[ wpawn + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
		u64 targ = 0;
		if ( c == white )
		{
			// white pawn checks
			u64 direct = wpAttm[ ekp ];		// direct checks mask
			u64 t = ONESHL( pos );
			// pawn push into check
			u8 p = pos - 8;
			if ( p >= 16 )					// ignore promotions!
			{
				u64 np = ONESHL( p ) & quiet;	// exclude (self) captures
				if ( np )
				{
					// also add pawn push if it gives a discovered check!
					// FIXME: a pawn push might not be a discovered check in a special case
					// (if it's a rook/queen dc) but we simply ignore it here (extra branch in some cases)
					if ( (t & discovered) && ((pos^ekp) & 7) )
					{
						direct = U64C(0)-1;
					}
					if ( np & direct )
					{
						targ |= np;
					}
					// try pawn push by 2
					if ( (pos & 0x38) == 0x30 )
					{
						np = ONESHL( pos - 16 ) & ~all;	// exclude (self) captures
						if ( np && (np & direct) )
						{
							targ |= np;
						}
					}
				}
			}
		}
		else
		{
			// black pawn checks
			u64 direct = bpAttm[ ekp ];		// direct checks mask
			u64 t = ONESHL( pos );
			// pawn push into check
			u8 p = pos + 8;
			if ( p < 48 )					// ignore promotions!
			{
				u64 np = ONESHL( p ) & quiet;	// exclude (self) captures
				if ( np )
				{
					// also add pawn push if it gives a discovered check!
					// FIXME: a pawn push might not be a discovered check in a special case
					// (if it's a rook/queen dc) but we simply ignore it here (extra branch in some cases)
					if ( (t & discovered) && ((pos^ekp) & 7) )
					{
						direct = U64C(0)-1;
					}
					if ( np & direct )
					{
						targ |= np;
					}
					// try pawn push by 2
					if ( (pos & 0x38) == 0x08 )
					{
						np = ONESHL( pos + 16 ) & ~all;	// exclude (self) captures
						if ( np && (np & direct) )
						{
							targ |= np;
						}
					}
				}
			}
		}
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
	}

	// knights
	src = board.bits[ wknight + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
		// direct checks:
		u64 targ = knightMov[ pos ] & ekknight;
		if ( ONESHL( pos ) & discovered )
		{
			// discovered checks (always)
			targ |= knightMov[ pos ] & quiet;
		}
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
	}

	// bishops
	src = board.bits[ wbishop + c ];
	while ( src )
	{	
		u8 pos = popLSB( src );
		// direct checks:
		u64 tmp = bishopAttm( pos, all );
		u64 targ = tmp & ekdiag;
		if ( ONESHL( pos ) & discovered )
		{
			// discovered checks
			targ |= tmp & quiet;
		}
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
	}

	// rooks
	src = board.bits[ wrook + c ];
	while ( src )
	{	
		u8 pos = popLSB( src );
		// direct checks:
		u64 tmp = rookAttm( pos, all );
		u64 targ = tmp & ekortho;
		if ( ONESHL( pos ) & discovered )
		{
			// discovered checks
			targ |= tmp & quiet;
		}
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
	}

	// queens
	src = board.bits[ wqueen + c ];
	while ( src )
	{	
		u8 pos = popLSB( src );
		// direct checks only
        u64 targ = queenAttm( pos, all ) & (ekortho | ekdiag);
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
	}

	u8 kpos = f.kingPos[ c ];
	// discovered checks only!
	if ( ONESHL( kpos ) & discovered )
	{
		u64 targ = (kingMov[ kpos ] & quiet) & ~(diagAttMask[ ekp ] | orthoAttMask[ ekp ]);
        while ( targ )
        {
            m->from = kpos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
	}
#endif
#if 0
	i32 nm = m - om;
	m = om;
	src = board.bits[ wall + c ];
#endif

#if 0
	while ( src )
    {
        u64 targ, tmp;
        u8 pos = popLSB( src );
        u8 p = board.pieces[ pos ] & 127;
        switch ( p )
        {
        case pawn:
            targ = 0;
            if ( c == white )
            {
                // white pawn checks
                u64 direct = wpAttm[ ekp ];		// direct checks mask
                u64 t = ONESHL( pos );
                // pawn push into check
                u8 p = pos - 8;
                if ( p >= 16 )					// ignore promotions!
                {
                    u64 np = ONESHL( p ) & quiet;	// exclude (self) captures
                    if ( np )
                    {
                        // also add pawn push if it gives a discovered check!
                        // FIXME: a pawn push might not be a discovered check in a special case
                        // (if it's a rook/queen dc) but we simply ignore it here (extra branch in some cases)
                        if ( (t & discovered) && ((pos^ekp) & 7) )
                        {
                            direct = U64C(0)-1;
                        }
                        if ( np & direct )
                        {
                            targ |= np;
                        }
                        // try pawn push by 2
                        if ( (pos & 0x38) == 0x30 )
                        {
                            np = ONESHL( pos - 16 ) & ~all;	// exclude (self) captures
                            if ( np && (np & direct) )
                            {
                                targ |= np;
                            }
                        }
                    }
                }
            }
            else
            {
                // black pawn checks
                u64 direct = bpAttm[ ekp ];		// direct checks mask
                u64 t = ONESHL( pos );
                // pawn push into check
                u8 p = pos + 8;
                if ( p < 48 )					// ignore promotions!
                {
                    u64 np = ONESHL( p ) & quiet;	// exclude (self) captures
                    if ( np )
                    {
                        // also add pawn push if it gives a discovered check!
                        // FIXME: a pawn push might not be a discovered check in a special case
                        // (if it's a rook/queen dc) but we simply ignore it here (extra branch in some cases)
                        if ( (t & discovered) && ((pos^ekp) & 7) )
                        {
                            direct = U64C(0)-1;
                        }
                        if ( np & direct )
                        {
                            targ |= np;
                        }
                        // try pawn push by 2
                        if ( (pos & 0x38) == 0x08 )
                        {
                            np = ONESHL( pos + 16 ) & ~all;	// exclude (self) captures
                            if ( np && (np & direct) )
                            {
                                targ |= np;
                            }
                        }
                    }
                }
            }
            break;
        case knight:
            // direct checks:
            targ = knightMov[ pos ] & ekknight;
            if ( ONESHL( pos ) & discovered )
            {
                // discovered checks (always)
                targ |= knightMov[ pos ] & quiet;
            }
            break;
        case bishop:
            // direct checks:
            tmp = bishopAttm( pos, all );
            targ = tmp & ekdiag;
            if ( ONESHL( pos ) & discovered )
            {
                // discovered checks
                targ |= tmp & quiet;
            }
            break;
        case rook:
            // direct checks:
            tmp = rookAttm( pos, all );
            targ = tmp & ekortho;
            if ( ONESHL( pos ) & discovered )
            {
                // discovered checks
                targ |= tmp & quiet;
            }
            break;
        case queen:
            // direct checks only
            targ = queenAttm( pos, all ) & (ekortho | ekdiag);
            break;
        case king:
            // discovered checks only!
            if ( ONESHL( pos ) & discovered )
            {
                targ = (kingMov[ pos ] & quiet) & ~(diagAttMask[ ekp ] | orthoAttMask[ ekp ]);
            } else continue;
            break;
        }
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->promo = 0;
            m->flags = 0;
            m++;
        }
    }
#endif

#if 0
	if ( m - om != nm )
	{
		printf("qchecks_err!\n");
	}
#endif

    u8 pos = f.kingPos[ c ];
    // handle castling!
    u8 cr = f.castRights[ c ];
    if ( cr && !board.flags().check )
    {
        // FIXME: slow?!
        // proper castling!
        if ( c == white )
        {
            if ( all & U64C(0x6000000000000000) )
                cr &= 0xf0;
            if ( all & U64C(0x0e00000000000000) )
                cr &= 0x0f;
            if ( cr )
            {
                u64 eatt = board.attackMask( c ^ 1 );
                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)) )
                    {
                        if ( rookAttm( pos+1, all ^ ONESHL( pos ) ^ ONESHL( pos + 2 ) ) & ONESHL( ekp ) )
                        {
                            // castling possible!
                            m->from = pos;
                            m->to = pos + 2;
                            m->flags = bcastling;
                            m->promo = 0;
                            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!
                        if ( rookAttm( pos-1, all ^ ONESHL( pos ) ^ ONESHL( pos - 2 ) ) & ONESHL( ekp ) )
                        {
                            m->from = pos;
                            m->to = pos - 2;
                            m->flags = bcastling | blongcastling;
                            m->promo = 0;
                            m++;
                        }
                    }
                }
            }
        }
        else
        {
            if ( all & 0x60 )
                cr &= 0xf0;
            if ( all & 0x0e )
                cr &= 0x0f;
            if ( cr )
            {
                u64 eatt = board.attackMask( c ^ 1 );
                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!
                        if ( rookAttm( pos+1, all ^ ONESHL( pos ) ^ ONESHL( pos + 2 ) ) & ONESHL( ekp ) )
                        {
                            m->from = pos;
                            m->to = pos + 2;
                            m->flags = bcastling;			// FIXME: dangerous?
                            m->promo = 0;
                            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!
                        if ( rookAttm( pos-1, all ^ ONESHL( pos ) ^ ONESHL( pos - 2 ) ) & ONESHL( ekp ) )
                        {
                            m->from = pos;
                            m->to = pos - 2;
                            m->flags = bcastling | blongcastling;
                            m->promo = 0;
                            m++;
                        }
                    }
                }
            }
        }
    }
#if defined(DEBUG) || defined(_DEBUG)
	if ( 1 )
	{
		i32 i;

		for (i=0; i<m-om; i++)
		{
			if ( board.pseudoIsLegal(om[i], pins) && !board.isCheck(om[i],discovered ) )
			{
				printf("GenQChecks error!\n");
				BitBoard tbb( board );
				tbb.dump();
				char desc[1024];
				om[i].describeUCI( desc );
				printf("move: %s\n", desc );
			}
		}
	}
#endif
    return m - om;
}

// generates all quiet moves, including castling, if possible
// note that all captures and promotions have already been generated
i32 BitMoveGen::genQuiet( BitMove *m )
{
    BitMove *om = m;
    const BitFlags &f = board.flags();
    ASSERT( !f.check );
    i32 c = f.turn;

    u64 src = board.bits[ wall + c ];
    u64 nopp = board.bits[ ball - c ];
    u64 all = src | nopp;
    nopp = ~nopp & ~src;		// avoid illegal self-captures!
#if 1
	// pawns
	src = board.bits[ wpawn + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
		if ( c == white )
		{
			// white pawn move
			if ( pos >= 16 && !(board.pieces[ pos-8 ] & 127) )	// >=16 because we don't want to generate promotions here
			{
				// advance by 1 allowed
				m->from = pos;
				m->to = pos-8;
				m->flags = 0;
				m->promo = 0;
				m++;
				if ( (pos & 0x38) == 0x30 && !(board.pieces[ pos-16 ] & 127) )
				{
					// advance by 2 allowed
					m->from = pos;
					m->to = pos-16;
					m->flags = 0;
					m->promo = 0;
					m++;
				}
			}
		}
		else
		{
			// black pawn move
			if ( pos < 48 && !(board.pieces[ pos+8 ] & 127) )	// <48 because we don't want to generate promotions here
			{
				// advance by 1 allowed
				m->from = pos;
				m->to = pos+8;
				m->flags = 0;
				m->promo = 0;
				m++;
				if ( (pos & 0x38) == 0x08 && !(board.pieces[ pos+16 ] & 127) )
				{
					// advance by 2 allowed
					m->from = pos;
					m->to = pos+16;
					m->flags = 0;
					m->promo = 0;
					m++;
				}
			}
		}
	}

	// knights
	src = board.bits[ wknight + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = knightMov[ pos ] & nopp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = 0;
            m->promo = 0;
            m++;
        }
	}

	// bishops
	src = board.bits[ wbishop + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = bishopAttm( pos, all ) & nopp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = 0;
            m->promo = 0;
            m++;
        }
	}

	// rooks
	src = board.bits[ wrook + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = rookAttm( pos, all ) & nopp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = 0;
            m->promo = 0;
            m++;
        }
	}

	// queens
	src = board.bits[ wqueen + c ];
	while ( src )
	{
		u8 pos = popLSB( src );
        u64 targ = queenAttm( pos, all ) & nopp;
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = 0;
            m->promo = 0;
            m++;
        }
	}

	// king
	u8 kpos = f.kingPos[c];
	u64 targ = kingMov[ kpos ] & nopp;
    while ( targ )
    {
		m->from = kpos;
        m->to = popLSB( targ );
        m->flags = 0;
        m->promo = 0;
        m++;
    }
#endif
#if 0
	i32 nm = m - om;
	m = om;
    src = board.bits[ wall + c ];
#endif
#if 0
    while ( src )
    {
        u64 targ;
        u8 pos = popLSB( src );
        u8 p = board.pieces[ pos ] & 127;
        switch ( p )
        {
        case pawn:
            if ( c == white )
            {
                // white pawn move
                if ( pos >= 16 && !(board.pieces[ pos-8 ] & 127) )	// >=16 because we don't want to generate promotions here
                {
                    // advance by 1 allowed
                    m->from = pos;
                    m->to = pos-8;
                    m->flags = 0;
                    m->promo = 0;
                    m++;
                    if ( (pos & 0x38) == 0x30 && !(board.pieces[ pos-16 ] & 127) )
                    {
                        // advance by 2 allowed
                        m->from = pos;
                        m->to = pos-16;
                        m->flags = 0;
                        m->promo = 0;
                        m++;
                    }
                }
            }
            else
            {
                // black pawn move
                if ( pos < 48 && !(board.pieces[ pos+8 ] & 127) )	// <48 because we don't want to generate promotions here
                {
                    // advance by 1 allowed
                    m->from = pos;
                    m->to = pos+8;
                    m->flags = 0;
                    m->promo = 0;
                    m++;
                    if ( (pos & 0x38) == 0x08 && !(board.pieces[ pos+16 ] & 127) )
                    {
                        // advance by 2 allowed
                        m->from = pos;
                        m->to = pos+16;
                        m->flags = 0;
                        m->promo = 0;
                        m++;
                    }
                }
            }
            continue;
            break;
        case knight:
            targ = knightMov[ pos ] & nopp;
            break;
        case bishop:
            targ = bishopAttm( pos, all ) & nopp;
            break;
        case rook:
            targ = rookAttm( pos, all ) & nopp;
            break;
        case queen:
            targ = queenAttm( pos, all ) & nopp;
            break;
        case king:
            targ = kingMov[ pos ] & nopp;
            break;
        default:
            targ = 0;
        }
        while ( targ )
        {
            m->from = pos;
            m->to = popLSB( targ );
            m->flags = 0;
            m->promo = 0;
            m++;
        }
    }
#endif
#if 0
	if ( m - om != nm )
	{
		printf("quiet_err\n");
	}
#endif
    u8 pos = f.kingPos[ c ];
    // handle castling!
    u8 cr = f.castRights[ c ];
    if ( cr && !board.flags().check )
    {
        // FIXME: slow?!
        // proper castling!
        if ( c == white )
        {
            if ( all & U64C(0x6000000000000000) )
                cr &= 0xf0;
            if ( all & U64C(0x0e00000000000000) )
                cr &= 0x0f;
            if ( cr )
            {
                u64 eatt = board.attackMask( c ^ 1 );
                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!
                        m->from = pos;
                        m->to = pos + 2;
                        m->flags = bcastling;
                        m->promo = 0;
                        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!
                        m->from = pos;
                        m->to = pos - 2;
                        m->flags = bcastling | blongcastling;
                        m->promo = 0;
                        m++;
                    }
                }
            }
        }
        else
        {
            if ( all & 0x60 )
                cr &= 0xf0;
            if ( all & 0x0e )
                cr &= 0x0f;
            if ( cr )
            {
                u64 eatt = board.attackMask( c ^ 1 );
                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!
                        m->from = pos;
                        m->to = pos + 2;
                        m->flags = bcastling;			// FIXME: dangerous?
                        m->promo = 0;
                        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!
                        m->from = pos;
                        m->to = pos - 2;
                        m->flags = bcastling | blongcastling;
                        m->promo = 0;
                        m++;
                    }
                }
            }
        }
    }
    return m - om;
}

// evasions: always legal!
i32 BitMoveGen::genEvasions( BitMove *m )
{
    // here we want:
    // 1st: all king moves moving out of check (castling not allowed)
    // 2nd: if not double-check, moves which block attack mask (including captures of a checking piece)

    // FIXME: ugly
    return board.moves( m, 0 );

    /*	BitMove *om = m;
    	const BitFlags &f = board.flags();
    	ASSERT( f.check );
    	i32 c = f.turn;


    	return m - om;*/
}

// SCORES

static const u8 ptTable[] = {0, 1, 3, 3, 5, 9, 10 };

// score capturing moves
// full: 1 = full, 0 = in qsearch
void BitMoveGen::scoreCaps( i32 count, BitMove *m, i32 *mval, BitMove *badm, i32 &badcnt, bool full )
{
    i32 i;
    i32 c = board.flags().turn;
    for (i=0; i<count; i++)
    {
        BitMove &bm = m[i];
        if ( bm.promo )
        {
            mval[i] = bm.promo == queen ? (49 << 16) : (0*28 << 16);
#ifndef USE_DELAYED_SEE
            if ( bm.flags & bcapture )
            {
                i32 see = board.see( bm, 1 );
                if ( see >= 0 )
                {
                    bm.flags |= bgoodcap;
                }
				else
				{
					// move to bad caps
					badm->packed = bm.packed;
					badm++;
					badcnt++;

					i32 j = count-1;
					if ( i != j )
					{
						m[i].packed = m[j].packed;
						mval[i] = mval[j];
					}
					count = j;
					i--;
//					mval[i] = 28 << 16;
					continue;
				}
            }
#endif
        }
        else
        {
            // assume capture
            ASSERT( bm.flags & bcapture );
#ifndef USE_DELAYED_SEE
            i32 see = board.see( bm, 1 );
            if ( see >= 0 )
            {
                bm.flags |= bgoodcap;
            }
			else
			{
				// move to bad caps
				badm->packed = bm.packed;
				badm++;
				badcnt++;

                i32 j = count-1;
                if ( i != j )
                {
					m[i].packed = m[j].packed;
                    mval[i] = mval[j];
				}
				count = j;
				i--;
				continue;
			}
#endif
            // MVV/LVA subsorting
            u8 f = ptTable[ board.pieces[ bm.from ] & 127 ];
            u8 t = ptTable[ board.pieces[ bm.to ] & 127 ];
            if ( bm.flags & benpassant )
                t = 1;
#ifndef USE_DELAYED_SEE
            mval[i] = ((48 + see) << 16) + (t<<4) - f;
#else
            mval[i] = (48 << 16) + (t<<4) - f;
#endif
        }
    }
	moveCount = count;
}

// score quiescent capturing moves
void BitMoveGen::scoreQCaps( i32 count, BitMove *m, i32 *mval )
{
    i32 i;
    i32 c = board.flags().turn;
    for (i=0; i<count; i++)
    {
        BitMove &bm = m[i];
        if ( bm.promo )
        {
            mval[i] = bm.promo == queen ? (49 << 16) : (0*28 << 16);
#ifndef USE_DELAYED_SEE_QS
            if ( bm.flags & bcapture )
            {
                i32 see = board.see( bm, 1 );
                if ( see >= 0 )
                {
                    bm.flags |= bgoodcap;
                }
				else
				{
					// move to bad caps
					i32 j = count-1;
					if ( i != j )
					{
						m[i].packed = m[j].packed;
						mval[i] = mval[j];
					}
					count = j;
					i--;
//					mval[i] = 28 << 16;
					continue;
				}
            }
#endif
        }
        else
        {
            // assume capture
            ASSERT( bm.flags & bcapture );
#ifndef USE_DELAYED_SEE_QS
            i32 see = board.see( bm );
            if ( see >= 0 )
            {
                bm.flags |= bgoodcap;
            }
			else
			{
				// move to bad caps
                i32 j = count-1;
                if ( i != j )
                {
					m[i].packed = m[j].packed;
                    mval[i] = mval[j];
				}
				count = j;
				i--;
				continue;
			}
#endif
            // MVV/LVA subsorting
            u8 f = ptTable[ board.pieces[ bm.from ] & 127 ];
            u8 t = ptTable[ board.pieces[ bm.to ] & 127 ];
            if ( bm.flags & benpassant )
                t = 1;
#ifndef USE_DELAYED_SEE_QS
            mval[i] = ((48 + see) << 16) + (t<<4) - f;
#else
            mval[i] = (48 << 16) + (t<<4) - f;
#endif
        }
    }
	moveCount = count;
}

// score normal moves
void BitMoveGen::scoreQuiet( i32 count, BitMove *m, i32 *mval )
{
    i32 i;
    i32 c = board.flags().turn;
	i32 stage = std::min( 1, board.stage() );
#ifdef USE_COUNTER
	BitMove pm, cm;
	pm.packed = cm.packed = 0;

	if ( ply > 0 )
	{
		pm.packed = search.stack[ ply-1 ].move.packed;
		cm.packed = search.hist.counter[ c ][ pm.from ][ pm.to ].packed;
	}
#endif

    for (i=0; i<count; i++)
    {
        const BitMove &bm = m[i];
        if ( bm.flags & bcastling )
        {
            mval[i] = 32 << 16;
        }
        else
        {
#ifdef USE_COUNTER
			// look for counter move
			if ( bm.packed == cm.packed )
			{
				mval[ i ] = 31 << 16;
//				printf(".");
			} else
#endif
            mval[ i ] = ( 22 << 16 )
                        + search.hist.history[ c ][ board.pieces[ bm.from ] & 127 ][ bm.to ]
						+ bonusTablePSQ[ stage ][ c ][ board.pieces[ bm.from ] & 127 ][ bm.to ];
        }
    }
}

// score QChecks
void BitMoveGen::scoreQChecks( i32 count, BitMove *m, i32 *mval )
{
    i32 i;
    i32 c = board.flags().turn;
	i32 stage = std::min( 1, board.stage() );
    for (i=0; i<count; i++)
    {
        const BitMove &bm = m[i];
        if ( bm.flags & bcastling )
        {
            mval[i] = 32 << 16;
        }
        else
        {
            mval[ i ] = (22 << 16)
                        + search.hist.history[ c ][ board.pieces[ bm.from ] & 127 ][ bm.to ]
						+ bonusTablePSQ[ stage ][ c ][ board.pieces[ bm.from ] & 127 ][ bm.to ];
        }
    }
}

// score evasions
void BitMoveGen::scoreEvasions( i32 count, BitMove *m, i32 *mval )
{
    i32 i;
    i32 c = board.flags().turn;
	i32 stage = std::min( 1, board.stage() );
    for (i=0; i<count; i++)
    {
        BitMove &bm = m[i];
        if ( bm.flags & bcapture )
        {
            i32 see = board.see( bm, 1 );
            // MVV/LVA subsorting
            u8 f = ptTable[ board.pieces[ bm.from ] & 127 ];
            u8 t = ptTable[ board.pieces[ bm.to ] & 127 ];
            if ( bm.flags & benpassant )
                t = 1;
            if ( see >= 0 )
            {
                bm.flags |= bgoodcap;
                mval[i] = (48 << 16) + (t<<4) - f;
            }
            else
            {
                mval[i] = (16 << 16) + (t<<4) - f;
            }
            if ( bm.promo )
            {
                mval[i] += bm.promo == queen ? (13 << 16) : (12 << 16);
            }
        }
        else if ( bm.flags & bcastling )
        {
            mval[i] = 32 << 16;
        }
        else
        {
            if ( bm.promo )
            {
                mval[i] = bm.promo == queen ? (49 << 16) : (0*28 << 16);
            }
            else
            {
                mval[ i ] = (22 << 16)
                            + search.hist.history[ c ][ board.pieces[ bm.from ] & 127 ][ bm.to ]
							+ bonusTablePSQ[ stage ][ c ][ board.pieces[ bm.from ] & 127 ][ bm.to ];
            }
        }
    }
}

// BitMoveGenSync

i32 BitMoveGenSync::count() const
{
	MutexLock _( mutex );
	return returnCount;
}

bool BitMoveGenSync::next( BitMove &mv )
{
	MutexLock _( mutex );
	return BitMoveGen::next( mv );
}
