#include "bittables.h"
#include "bitsearch.h"
#include <deque>
#include "bitkpk.h"
#include "bitmovegen.h"
#include "gtbwrapper.h"
#include "book.h"
#include "bitpersonality.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

BitKPK tkpk;

Book book;
bool gPonder = 1;			// global pondering flag
bool gOwnBook = 1;			// own book flag
String gGTBPath = "/gtb/";	// gtb path
int gGTBCache = 8;			// gtb cache size in megs
int gTBUsage = 2;			// tb usage

//#define COPY_MOVE

// NEW BUG: engine sometimes doesn't report newmove on UCI stop!

// FIXME!!! there's a bug where the engine starts thinking and returns nothing. maybe blunder check?!?! don't know. it SUCKS!
// OR maybe an infinite loop? couldn't replicate it. or maybe I/O or Arena bug?
// NO! my bug. fixed pawn_promo_movegen_bug (bad else statement)

//extern void test();

void (*searchCallback)( i32 depth, i32 bestScore, i32 progress, const Move &bestmove, const Move &curmove, PVMoves *pv, i32 nodes, void *param ) = 0;
void *searchCallbackParam = 0;

//#define VERIFY_SEE

class ThinkThread : public Thread
{
public:
    Mutex mutex;
    BitSearch bs;
    std::deque<String> cmds;
    std::deque<BitSearchMode> modes;
    Event evcmd;
    Event searchFinished;
    Event threadFinished;
    volatile bool searching;
    i32 work( void *param );
    int work() {
        return work(0);
    }
    void search( BitSearchMode sm );
    void send( const char *str, BitSearchMode sm = BitSearchMode() );
    // stop thinking. wait for (all) threads to stop computing current position
    void stop();

    ThinkThread( void *param = 0 );
    ~ThinkThread();
};

ThinkThread *tt = 0;

void realMove( BitSearch &bs, const BitMove &bm )
{
    Move tm;
    tm.packed = 0;
    tm.from = bm.from;
    tm.to = bm.to;
    tm.promo = bm.promo;
    i32 turn = bs.rturn;
    Move m[ maxMoves ];
    i32 vm = bs.rboard.moves( m, turn );
    i32 i;
    for (i=0; i<vm; i++)
    {
        if ( tm.equal( m[i] ) )
            break;
    }
    if ( i < vm )
    {
#ifdef VERIFY_SEE
        if ( bm.flags & bcapture )
        {
            i32 cq = bs.rboard.captureQuality( m[i] );
            i32 se = bs.board.see( bm );
            if ( cq != se )
            {
////				bs.rboard.dump();
                logFmt("see_bug: old=%d, new=%d\n", cq, se);
                char buf[32];
                m[i].describeSAN( bs.board.flags().turn, bs.rboard, buf );
                logFmt("move: %s (f:%d t:%d)\n", buf, m[i].from, m[i].to);
                String brd;
                bs.rboard.dump( &brd );
                logFmt("board:\n%s\n", brd.ansi());
                char fen[256];
                bs.rboard.getFEN( fen, bs.board.flags().turn );
                logFmt("FEN: %s\n", fen);
                i32 se0 = bs.board.see( bm );
                i32 cq0 = bs.rboard.captureQuality( m[i] );
            }
        }
#endif
        /*		i32 newevl = bs.eval( bs.board );
        		i32 oldevl = (bs.rboard.eval( bs.board.flags().turn ) + 5) / 10;
        		printf("#old_eval: %d #new_eval: %d\n", oldevl, newevl);*/

        bs.rboard.move( m[i] );
		bs.rturn ^= 1;
    }
    else
    {
        logFmt("movegen_BUG_FOUND!\n");
    }
}

ThinkThread::ThinkThread( void *param )
        : searching(0), evcmd(1), searchFinished(1), threadFinished(1), Thread( param )
{
}

ThinkThread::~ThinkThread()
{
    stop();
    send("");
    threadFinished.wait();
}

// stop thinking. wait for (all) threads to stop computing current position
void ThinkThread::stop()
{
    if ( searching )
    {
        bs.stopSearch = 1;
        searchFinished.wait();
        searchFinished.reset();
    }
}

void ThinkThread::search( BitSearchMode sm )
{
    if ( searching )
    {
        stop();
    }
    send( "search", sm );
}

i32 ThinkThread::work( void *param )
{
    for (;;)
    {
        evcmd.wait();
        String cmd;
        BitSearchMode sm;
        {
            MutexLock _( mutex );
            while ( !cmds.empty() )
            {
                cmd = cmds.front();
                cmds.pop_front();
                sm = modes.front();
                modes.pop_front();
            }
        }
        if ( cmd.empty() )
            break;				// quit_flag!
        searching = 1;
        String st;
        /*		bs.rboard.dump( &st );
        		printf("%s\n", st.ansi());*/
        bs.search( sm );
#if 0
        realMove( bs, bs.stack[0].pv[0] );
        bs.board.move( bs.stack[0].pv[0] );
        if ( bs.board.flags().fiftyCounter == 0 )
            bs.rh->clear();
        bs.rh->push( bs.board.hash(), bs.board.flags().fiftyCounter == 0 );
#endif
        bs.advance();
        bs.advance();
        searching = 0;
        searchFinished.signal();
    }
    threadFinished.signal();
    return 0;
}

void ThinkThread::send( const char *str, BitSearchMode sm )
{
    if ( !this )
        return;
    MutexLock _( mutex );
    cmds.push_back( str );
    modes.push_back( sm );   
    evcmd.signal();
}

const char *execFirst = 0;

u64 perftChecks = 0;

BitBoard perftBoards[ bmaxPly+1 ];

// my movegen + makemove roughly 2.8x slower than crafty's (56.267 sec vs 19.92 sec for depth 6)
// well... I use now a cheat to not make moves on leaf nodes => 2x faster
u64 oldperft( BitSearch &bs, i32 depth, i32 ply = 0 )
{
    u64 res = 0;
    if ( depth <= 0 )
    {
        return 1;
    }
    if ( ply == 0 )
    {
        char buf[256];
        bs.board.getFEN(buf);
        printf("fen = %s\n", buf);
        bs.clearSlots();
        bs.clearHistory();
    }
    /*	if ( ply == 1 )
    	{
    		char buf[256];
    		bs.board.getFEN(buf);
    		printf("childfen = %s\n", buf);
    	}*/

    BitMove nm, mv;
    nm.packed = 0;
    if ( bs.board.flags().check )
    {
        perftChecks++;
    }
    BitMoveGen mg( bs.board, ply, bs, nm, bs.board.flags().check ? mgtEvasions : mgtCaptures );

    if ( depth == 1 )
    {
        // cheat - count only
        while ( mg.next( mv ) )
            res++;
        return res;
    }

    while ( mg.next( mv ) )
    {
        u64 undoBuf[ bundomax ];
        i32 im = bs.board.move( mv, undoBuf );
        res += oldperft( bs, depth-1, ply+1 );
        bs.board.undo( im, undoBuf );
    }
    return res;
}

// my movegen + makemove roughly 2.8x slower than crafty's (56.267 sec vs 19.92 sec for depth 6)
u64 perft( BitSearch &bs, i32 depth, i32 ply = 0 )
{
    u64 res = 0;
    if ( depth <= 0 )
    {
        return 1;
    }
    if ( ply == 0 )
    {
        bs.clearSlots();
        bs.clearHistory();
    }
    const BitBoard &board = perftBoards[ ply ];
    BitMove nm, mv;
    nm.packed = 0;
    if ( board.flags().check )
    {
        perftChecks++;
    }
    BitMoveGen mg( board, ply, bs, nm, board.flags().check ? mgtEvasions : mgtCaptures );

    if ( depth == 1 )
    {
        // cheat - count only
        while ( mg.next( mv ) )
            res++;
        return res;
    }

    while ( mg.next( mv ) )
    {
//		u64 undoBuf[ bundomax ];
        /*i32 im = */
        perftBoards[ ply+1 ] = board;
        perftBoards[ ply+1 ].move( mv );
        res += perft( bs, depth-1, ply+1 );
//		bs.board.undo( im, undoBuf );
    }
    return res;
}

bool debugCommand( const String &cmd )
{
	if ( cmd == "swap" )
	{
		tt->bs.board.swap();
		return 1;
	}
    if ( cmd == "genkpk" )
    {
        tt->stop();
        tkpk.generate();
        tkpk.saveBin("kpk.bit");
        return 1;
    }
    /*	if ( cmd == "test" )
    	{
    		tt->stop();
    		test();
    		return 1;
    	}*/
    if ( cmd == "dump" )
    {
        tt->stop();
        String st;
        tt->bs.rboard.dump( &st );
        tt->bs.board.dump();
        printf("%s\n", st.ansi());
        i32 evl = tt->bs.eval( tt->bs.board );
        printf("eval: %d\n", evl);
        logFmt("eval: %d\n", evl);

#if 1
        {
            i32 i;
            i32 res = 0;
            for (i=0; i<2; i++)
            {
                i32 j;
                for (j=pawn; j < king; j++)
                {
                    const u64 &b = tt->bs.board.bits[ INDEX( i, j ) ];
                    res += bitCount(b) * (j==pawn ? 100 : tmatTab[j]) * (i==0 ? 10 : -10);
                }
            }
            res /= tt->bs.board.flags().turn == white ? 10 : -10;
            printf("material: %d\n", res);
        }
#endif

        return 1;
    }
    return 0;
}

static void rgstat( i64 games )
{
	if ( games <= 0 )
		return;
	srand( getMillisec() );
    i32 ms = getMillisec();

	i64 wmates = 0, bmates = 0;
	i64 insuff = 0;
	i64 stales = 0;
	i64 reps = 0;
	i64 fifty = 0;
	i64 ogames = games;
	i32 minPlies = 0x7fffffff;
	i32 maxPlies = 0;
	i64 totPlies = 0;
	while ( games > 0 )
	{
		if ( !(games & 32767) )
		{
			printf("%0.1lf%% ", 100.0 - (double)games * 100.0 / ogames);
		}
		i32 plies = 0;
		BitBoard bb;
		std::vector< u64 > history;
		bb.reset();
		history.push_back( bb.hash() );
		for (;;)
		{
			BitMove m[ bmaxMoves ];
			if ( bb.isDraw() )
			{
			    if ( bb.flags().fiftyCounter >= fiftyCounterMax )
				{
					fifty++;
				}
				else
				{
					insuff++;
				}
				break;
			}
			i32 vm = bb.moves( m );
			if ( vm == 0 )
			{
				if ( bb.flags().check )
				{
					if ( bb.flags().turn == white )
					{
						bmates++;
					}
					else
					{
						wmates++;
					}
				}
				else stales++;
				break;
			}
			i32 mi = rand() % vm;
			if ( mi < 0 )
				mi += vm;
			bb.move( m[mi] );
			plies++;
			if ( bb.flags().fiftyCounter == 0 )
				history.clear();
			history.push_back( bb.hash() );
			i32 i;
			i32 hreps = 0;
			for (i=(i32)history.size()-1; i>=0; i-=2)
			{
				if ( bb.hash() == history[i] )
				{
					hreps++;
					if ( hreps >= 3 )
					{
						reps++;
						break;
					}
				}
			}
			if ( i < history.size() )
				break;
		}
		if ( plies < minPlies )
			minPlies = plies;
		if ( plies > maxPlies )
			maxPlies = plies;
		totPlies += plies;
		games--;
	}
	printf("\n");
    ms = getMillisec() - ms;
	printf("took %d ms\n", ms);
	printf("%d min plies\n", minPlies);
	printf("%d max plies\n", maxPlies);
	printf("%lld total plies\n", totPlies);
	printf("%0.6lf avg plies\n", (double)totPlies / ogames );
	printf("%lld total games\n", ogames);
	printf("%lld checkmates (%0.6lf%%)\n", wmates+bmates, (double)(wmates+bmates)/ogames );
	printf("%lld whitewins (%0.6lf%%)\n", wmates, (double)(wmates)/ogames );
	printf("%lld blackwins (%0.6lf%%)\n", bmates, (double)(bmates)/ogames );
	printf("%lld fiftyrule (%0.6lf%%)\n", fifty, (double)fifty/ogames );
	printf("%lld insuffmaterial (%0.6lf%%)\n", insuff, (double)insuff/ogames );
	printf("%lld repetition (%0.6lf%%)\n", reps, (double)reps/ogames );
	printf("%lld stalemate (%0.6lf%%)\n", stales, (double)stales/ogames );
	printf("%0.6lf Hz (%0.6lf s)\n", (double)ogames / ((double)ms / 1000.0), 1.0 / ((double)ogames / ((double)ms / 1000.0)));
}

i32 gMultiPV = 1;
i32 oldTC = 0;

void uciLoop()
{
    char buf[16384];
    while ( execFirst || fgets( buf, sizeof(buf), stdin ) != 0)
    {
		if ( execFirst )
        {
            strcpy( buf, execFirst );
            execFirst = 0;
        }
        for (;;)
        {
            size_t sz = strlen(buf);
            if ( sz > 0 && buf[sz-1] < 32 )
            {
                buf[sz-1] = 0;
            }
            else break;
        }
        String cmd( buf );
        if ( cmd.empty() )
            continue;

#ifdef DEBUG
        logFmt("got_cmd: %s\n", cmd.ansi());
#endif

//		logFmt("got_cmd: %s\n", cmd.ansi());
        if ( cmd == "quit" )
        {
            break;
        }
        if ( cmd == "isready" )
        {
            printf("readyok\n");
            continue;
        }
        if ( cmd == "$debug" )
        {
            continue;
        }
        if ( cmd == "uci" )
        {
            printf("id name cheng3 1.07\n");
            printf("id author Martin Sedlak\n");
            printf("option name Hash type spin min 1 max 1024 default 4\n");
            printf("option name Clear Hash type button\n");
            printf("option name Ponder type check default true\n");
            printf("option name Skill type spin min 0 max 100 default 100\n");
            printf("option name MultiPV type spin min 1 max 256 default 1\n");
			printf("option name GaviotaTbPath type string default /gtb/\n");
			printf("option name GaviotaTbCache type spin default 8 min 1 max 1024\n");
			printf("option name TBUsage type combo default always var never var root var always\n");
			printf("option name OwnBook type check default true\n");
			printf("option name PersonalityFile type string default\n");
			// enable this to be able to edit all the personality parameters on the fly
			if ( 0 )
			{
				std::vector< BitParameter * > params;
				BitPersonality::get()->getParameters( params );
				size_t i;
				for (i=0; i<params.size(); i++)
				{
					BitParameter *p = params[i];
					printf("option name %s type spin min %d max %d default %d\n",
						p->getName().ansi(), p->getMinValue(), p->getMaxValue(), p->getDefaultValue() );
				}
			}
            printf("uciok\n");
            continue;
        }
        if ( cmd == "ucinewgame" )
        {
            tt->stop();
            tt->bs.board.reset();
            tt->bs.rboard.reset();
			tt->bs.rturn = white;
            tt->bs.rh->clear();
            tt->bs.clearSlots();
            tt->bs.trans.clear();		// new: important! clear trans_table!
            printf("info string Starting new game, hash table cleared\n");
            continue;
        }
        std::vector< String > tokens;
        char buf[4096];
        strcpy( buf, cmd.ansi() );
        char *tok = strtok( buf, " \t\r\n\v" );
        do
        {
//			logFmt("token: %s\n", tok);
            tokens.push_back( tok );
        } while ( tok = strtok( 0, " \t\r\n\v" ) );
        if ( tokens.empty() )
            continue;
        if ( tokens[0] == "setoption" )
        {
            if ( 1 < tokens.size() )
            {
                if ( tokens[1] != "name" )
                    continue;
            }
            if ( 2 < tokens.size() )
            {
                if ( tokens[2] == "PersonalityFile" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
                                String fnm = tokens[4];
                                // load pesonality!
                                tt->stop();
                                tt->bs.trans.clear();
                                printf("info string Hash table cleared\n");
								if ( fnm.empty() )
									BitPersonality::get()->reset();
								else
								{
									bool res = BitPersonality::get()->load( fnm );
									printf("info string %s %s\n",
										res ? "personality file loaded" : "personality file not found",
										fnm.ansi()
									);
								}
                            }
                        }
                    }
                }
                else if ( tokens[2] == "Skill" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
                                i32 skill = strtol( tokens[4].ansi(), 0, 10 );
                                if ( skill < 0 )
                                    skill = 0;
                                else if ( skill > 100 )
                                    skill = 100;
                                // set skill!
                                tt->stop();
                                if ( tt->bs.evaluator )
                                {
                                    tt->bs.evaluator->skill = skill;
                                    printf("info string Skill set to %d (note: affects analysis as well)\n", skill);
                                    tt->bs.trans.clear();
                                    printf("info string Hash table cleared\n");
                                }
                            }
                        }
                    }
                }
                else if ( tokens[2] == "Hash" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
                                i32 hsize = strtol( tokens[4].ansi(), 0, 10 );
                                if ( hsize < 1 )
                                    hsize = 1;
                                else if ( hsize > 1024 )
                                    hsize = 1024;
                                // set hash size!
                                tt->stop();
                                tt->bs.trans.resize( hsize );
                                printf("info string Hash table size set to %d MB\n", hsize);
                            }
                        }
                    }
                }
                else if ( tokens[2] == "GaviotaTbCache" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
                                i32 csize = strtol( tokens[4].ansi(), 0, 10 );
                                if ( csize < 1 )
                                    csize = 1;
                                else if ( csize > 1024 )
                                    csize = 1024;
                                // set gtb cache!
                                tt->stop();
                                gGTBCache = csize;
                                printf("info string GTB cache size set to %d MB\n", csize);
								delete tt->bs.tb;
								tt->bs.tb = new GTBWrapper;
								tt->bs.tb->init( gGTBPath.ansi(), gGTBCache * 1024*1024 );
								if ( !(tt->bs.tbMen = tt->bs.tb->numMen()) )
								{
									delete tt->bs.tb;
									tt->bs.tb = 0;
									printf("info string no GTB found\n");
								}
                            }
                        }
                    }
                }
				else if ( tokens[2] == "GaviotaTbPath" )
				{
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
							gGTBPath = String();
                            if ( 4 < tokens.size() )
                            {
								size_t i;
								for (i=4; i<tokens.size(); i++)
								{
									gGTBPath += tokens[i];
									if ( i+1 < tokens.size() )
									{
										gGTBPath += " ";
									}
								}
							}
                            tt->stop();
							delete tt->bs.tb;
							tt->bs.tb = new GTBWrapper;
							tt->bs.tb->init( gGTBPath.ansi(), gGTBCache * 1024*1024 );
							if ( !(tt->bs.tbMen = tt->bs.tb->numMen()) )
							{
								delete tt->bs.tb;
								tt->bs.tb = 0;
								printf("info string no GTB found\n");
							}
						}
					}
				}
				else if ( tokens[2] == "TBUsage" )
				{
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
							gTBUsage = 2;
                            if ( 4 < tokens.size() )
                            {
								if ( tokens[4] == "never" )
									gTBUsage = 0;
								else if ( tokens[4] == "root" )
									gTBUsage = 1;
							}
                            tt->stop();
							tt->bs.tbUsage = gTBUsage;
							static const char *tbu[] = { "never", "root", "always" };
							printf("info string TB usage set to %s\n", tbu[ gTBUsage ] );
						}
					}
				}
                else if ( tokens[2] == "MultiPV" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
								gMultiPV = strtol( tokens[4].ansi(), 0, 10 );
								if ( gMultiPV < 1 )
									gMultiPV = 1;
								else if ( gMultiPV > 256 )
									gMultiPV = 256;
                                tt->stop();
                                printf("info string MultiPV %d\n", gMultiPV );
                            }
                        }
                    }
                }
                else if ( tokens[2] == "Ponder" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
                                bool ponder = tokens[4] == "true";
                                gPonder = ponder;
                                tt->stop();
                                printf("info string Pondering %s\n", ponder ? "enabled" : "disabled");
                            }
                        }
                    }
                }
                else if ( tokens[2] == "OwnBook" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "value" )
                        {
                            if ( 4 < tokens.size() )
                            {
                                bool obk = tokens[4] == "true";
                                gOwnBook = obk;
                                printf("info string Own book %s\n", obk ? "enabled" : "disabled");
                            }
                        }
                    }
                }
                else if ( tokens[2] == "Clear" )
                {
                    if ( 3 < tokens.size() )
                    {
                        if ( tokens[3] == "Hash" )
                        {
                            // clear hash!
                            tt->stop();
                            tt->bs.trans.clear();
                            printf("info string Hash table cleared\n");
                        }
                    }
                }
				else
				{
					BitParameter *par = BitPersonality::get()->find( tokens[2] );
					if ( par )
					{
						if ( 3 < tokens.size() )
						{
							if ( tokens[3] == "value" )
							{
								if ( 4 < tokens.size() )
								{
									i32 pval = strtol( tokens[4].ansi(), 0, 10 );
									par->setValue(pval);
									printf("info string %s set to %d\n", tokens[2].ansi(), par->getValue() );
								}
							}
						}

					}
				}
            }
            continue;
        }
		else if ( tokens[0] == "loadpers" )
		{
            if ( tokens.size() > 1 )
            {
				if ( !BitPersonality::get()->load(tokens[1]) )
				{
					printf("error loading personality <= `%s'\n", tokens[1].ansi());
				}
			}
		}
		else if ( tokens[0] == "savepers" )
		{
            if ( tokens.size() > 1 )
            {
				if ( !BitPersonality::get()->save(tokens[1]) )
				{
					printf("error saving personality => `%s'\n", tokens[1].ansi());
				}
			}
		}
        else if ( tokens[0] == "perft" )
        {
            if ( tokens.size() > 1 )
            {
                long d = strtol( tokens[1].ansi(), 0, 10 );
                tt->stop();
                perftChecks = 0;
                i32 ms = getMillisec();
                perftBoards[0] = tt->bs.board;
#ifdef COPY_MOVE
                u64 nodes = perft( tt->bs, d );
#else
                u64 nodes = oldperft( tt->bs, d );
#endif
                ms = getMillisec() - ms;
                printf("%lld nodes, %lld checks, %0.3lf s\n", nodes, perftChecks, (double)ms / 1000.0 );
            }
        }
        else if ( tokens[0] == "rgstat" )
        {
            if ( tokens.size() > 1 )
            {
                long d = strtol( tokens[1].ansi(), 0, 10 );
				rgstat( d );
            }
        }
        else if ( debugCommand( tokens[0] ) )
        {
            continue;
        }
        else
            if ( tokens[0] == "ponderhit" )
            {
                tt->bs.ponderHit();
//			tt->stop();
                continue;
            }
            else
                if ( tokens[0] == "stop" )
                {
#ifdef DEBUG
                    logFmt("stop command!\n");
#endif
                    tt->stop();
                    continue;
                }
                else
                    if ( tokens[0] == "go" )
                    {
                        BitSearchMode sm;
                        i32 i;
                        i32 wtime = 0;
                        i32 btime = 0;
                        i32 winc = 0;
                        i32 binc = 0;
                        i32 movestogo = 0;
                        bool movemode = 0;
                        bool ponder = 0;
						sm.multiPV = gMultiPV;
                        for (i=1; i<tokens.size(); i++)
                        {
                            if ( tokens[i] == "ponder" )
                            {
                                ponder = 1;
                            }
                            else
                                if ( tokens[i] == "movestogo" )
                                {
                                    if ( i+1 < tokens.size() )
                                    {
                                        movestogo = strtol( tokens[i+1].ansi(), 0, 10 );
                                        i++;
                                    }
                                }
                                else
                                    if ( tokens[i] == "wtime" )
                                    {
                                        if ( i+1 < tokens.size() )
                                        {
                                            wtime = strtol( tokens[i+1].ansi(), 0, 10 );
                                            i++;
                                        }
                                    }
                                    else
                                        if ( tokens[i] == "btime" )
                                        {
                                            if ( i+1 < tokens.size() )
                                            {
                                                btime = strtol( tokens[i+1].ansi(), 0, 10 );
                                                i++;
                                            }
                                        }
                                        else
                                            if ( tokens[i] == "winc" )
                                            {
                                                if ( i+1 < tokens.size() )
                                                {
                                                    winc = strtol( tokens[i+1].ansi(), 0, 10 );
                                                    i++;
                                                }
                                            }
                                            else
                                                if ( tokens[i] == "binc" )
                                                {
                                                    if ( i+1 < tokens.size() )
                                                    {
                                                        binc = strtol( tokens[i+1].ansi(), 0, 10 );
                                                        i++;
                                                    }
                                                }
                                                else
                                                    if ( tokens[i] == "mate" )
                                                    {
                                                        if ( i+1 < tokens.size() )
                                                        {
                                                            sm.mateSearch = strtol( tokens[i+1].ansi(), 0, 10 );
                                                            i++;
                                                        }
                                                    }
                                                    else
                                                        if ( tokens[i] == "nodes" )
                                                        {
                                                            if ( i+1 < tokens.size() )
                                                            {
                                                                sm.maxNodes = strtol( tokens[i+1].ansi(), 0, 10 );
                                                                i++;
                                                            }
                                                        }
                                                        else
                                                            if ( tokens[i] == "depth" )
                                                            {
                                                                if ( i+1 < tokens.size() )
                                                                {
                                                                    sm.maxDepth = strtol( tokens[i+1].ansi(), 0, 10 );
                                                                    i++;
                                                                }
                                                            }
                                                            else
                                                                if ( tokens[i] == "infinite" )
                                                                {
                                                                    sm.maxTime = 0;
                                                                    wtime = btime = winc = binc = 0;
                                                                }
                                                                else if ( tokens[i] == "movetime" )
                                                                {
                                                                    if ( i+1 < tokens.size() )
                                                                    {
                                                                        sm.maxTime = strtol( tokens[i+1].ansi(), 0, 10 );
                                                                        wtime = btime = winc = binc = 0;
                                                                        i++;
                                                                    }
                                                                }
                                                                else if ( tokens[i] == "searchmoves" )
                                                                {
                                                                    movemode = 1;
                                                                }
                                                                else if ( movemode )
                                                                {
                                                                    BitMove m[ bmaxMoves ];
                                                                    i32 vm = tt->bs.board.moves( m );

                                                                    i32 rturn = tt->bs.rturn;
                                                                    Move rm[ maxMoves ];
                                                                    i32 rvm = tt->bs.rboard.moves( rm, rturn );
                                                                    if ( vm != rvm )
                                                                    {
                                                                        logFmt("FATAL ERROR: new_movegen_bug!\n");
                                                                    }

                                                                    BitMove bm;
                                                                    bm.packed = 0;
                                                                    // parse_uci_move
                                                                    const char *mv = tokens[i].ansi();
                                                                    if ( tokens[i].length() >= 4 )
                                                                    {
                                                                        i32 fx = (*mv++ | 32) - 'a';
                                                                        i32 fy = '8' - (*mv++);
                                                                        i32 tx = (*mv++ | 32) - 'a';
                                                                        i32 ty = '8' - (*mv++);
                                                                        bm.from = (fy << 3) + fx;
                                                                        bm.to = (ty << 3) + tx;
                                                                        if ( *mv )
                                                                        {
                                                                            switch ( *mv | 32 )
                                                                            {
                                                                            case 'b':
                                                                                bm.promo = bishop;
                                                                                break;
                                                                            case 'n':
                                                                                bm.promo = knight;
                                                                                break;
                                                                            case 'r':
                                                                                bm.promo = rook;
                                                                                break;
                                                                            case 'q':
                                                                                bm.promo = queen;
                                                                                break;
                                                                            }
                                                                        }
                                                                        i32 i;
                                                                        for (i=0; i<vm; i++)
                                                                        {
                                                                            if ( bm.equal( m[i] ) )
                                                                            {
                                                                                break;
                                                                            }
                                                                        }
                                                                        if ( i < vm )
                                                                        {
                                                                            sm.searchMoves.push_back( m[i] );
                                                                        }
                                                                        else
                                                                        {
                                                                            char buf[32];
                                                                            bm.describeUCI( buf );
                                                                            logFmt("FATAL ERROR: invalid move passed by gui: %s\n", buf );
                                                                        }
                                                                    }
                                                                }
                        }
                       
                        if ( !ponder || gPonder )
                        {
                            sm.ponder = ponder;
                            if ( wtime || btime )
                            {
                                // tournament time controls!
                                tt->stop();
                                i32 turn = tt->bs.board.flags().turn;

                                i32 mvl;
                                i32 otc = oldTC;
                                if ( !otc && winc == 0 && binc == 0 && movestogo == 0 )
                                {
                                    // NEW: revert to old time control if mytime <= 1 minute sudden death!
                                    i32 mytime;

                                    if ( turn == white )
                                    {
                                        mytime = wtime;
                                    }
                                    else
                                    {
                                        mytime = btime;
                                    }
                                    if ( mytime <= 60000 )
                                        otc = 1;
                                }
                                if ( !otc )
                                {
                                    i32 mytime, myinc;
                                    if ( turn == white )
                                    {
                                        mytime = wtime;
                                        myinc = winc;
                                    }
                                    else
                                    {
                                        mytime = btime;
                                        myinc = binc;
                                    }

                                    if ( movestogo == 0 )
                                        movestogo = 25;
                                    mvl = (mytime + movestogo * myinc) / movestogo;
									if ( movestogo == 1 )
									{
										mvl -= 100;
										if ( mvl < 0 )
											mvl = 0;
										sm.absLimit = 1;
									}
                                }
                                else
                                {
                                    i32 rt = turn == white ? wtime : btime;
                                    i32 ort = turn == white ? btime : wtime;
                                    mvl = 1000;

                                    i32 dt = rt - ort;
                                    if ( rt < 1*500 )
                                        mvl = 1;
                                    if ( rt < 1*1000 )
                                        mvl = 8;
                                    if ( rt < 2*1000 )
                                        mvl = 16;
                                    if ( rt < 4*1000 )
                                        mvl = 32;
                                    if ( rt < 8*1000 )
                                        mvl = 63;				// less than 8 secs -> instant!
                                    else
                                        if ( rt <= 15*1000 )
                                            mvl = 125;				// less than 15 secs -> 0.125 sec/move
                                        else
                                            if ( rt <= 30*1000 )
                                                mvl = 250;				// less than 30 secs => 0.25 sec/move
                                            else
                                                if ( rt <= 2*60*1000 )
                                                    mvl = 500;				// less than 2 minutes => 0.5 sec/move
                                                else if ( rt <= 5*60*1000 )
                                                    mvl = 1000;				// less than 5 minutes => 1 sec/move
                                                else if ( rt <= 10*60*1000 )
                                                    mvl = 2000;				// less than 10 minutes => 2 sec/move
                                                else if ( rt <= 15*60*1000 )
                                                    mvl = 4000;				// less than 15 minutes => 4 sec/move
                                                else
                                                    mvl = 6000;				// more than 15 minutes => 6 sec/move
                                    // much more than opponent -- think longer!
                                    if ( dt + 1000 >= 20000 && rt > 1*60*1000)
                                        mvl = 20000;
                                    else
                                        if ( dt + 1000 >= 15000 && rt > 1*60*1000)
                                            mvl = 15000;
                                        else
                                            if ( dt + 1000 >= 10000 && rt > 1*60*1000)
                                                mvl = 10000;
                                            else
                                                if ( dt + 1000 >= 5000 && rt > 1*30*1000)
                                                    mvl = 3000;
                                }
                                sm.maxTime = mvl;
                            }
							bool bkm = 0;
							if ( sm.maxTime && !sm.ponder && gOwnBook )
							{
								Move m[ maxMoves ];
								i32 vm = tt->bs.rboard.moves( m, tt->bs.rturn );
//								printf("rand() = %d\n", rand());
								i32 res = book.check( tt->bs.rturn, tt->bs.rboard, tt->bs.rboard.curHash, vm, m );
								if ( res >= 0 )
								{
									char buf[32];
									m[res].describeUCI( buf );
									printf("bestmove %s\n", buf);
									bkm = 1;
								}
							}						
							if ( !bkm )
								tt->search( sm );
                        }
                    }
                    else if ( tokens[0] == "position" )
                    {
                        tt->stop();
                        i32 i;
                        i32 movemode = 0;
                        for (i=1; i<tokens.size(); i++)
                        {
                            if ( tokens[i] == "fen" )
                            {
                                tt->bs.board.reset();
                                tt->bs.rboard.reset();
								tt->bs.rturn = white;
                                tt->bs.rh->clear();
                                i32 j;
                                String fen;
                                for (j=i+1; j<tokens.size(); j++)
                                {
                                    if ( tokens[j] != "moves" && tokens[j] != "fen" && tokens[j] != "startpos" )
                                    {
                                        fen += tokens[j] + " ";
                                    } else break;
                                }
                                i32 turn = white;
                                i32 movnum = 0;
                                if ( !tt->bs.rboard.parseFEN( fen.ansi(), &turn, &movnum ) )
                                {
                                    logFmt( "FATAL ERROR: invalid fen coming from gui: %s\n", fen.ansi() );
                                }
								tt->bs.rturn = turn;
                                tt->bs.board = BitBoard( tt->bs.rboard, turn );
                                if ( tt->bs.rh )
                                    tt->bs.rh->push( tt->bs.board.hash(), tt->bs.board.flags().fiftyCounter == 0 );
                                i = j-1;
                            }
                            else
                                if ( tokens[i] == "startpos" )
                                {
                                    tt->bs.board.reset();
                                    tt->bs.rboard.reset();
									tt->bs.rturn = white;
                                    tt->bs.rh->clear();
                                    if ( tt->bs.rh )
                                        tt->bs.rh->push( tt->bs.board.hash(), tt->bs.board.flags().fiftyCounter == 0 );
                                }
                                else if ( tokens[i] == "moves" )
                                {
                                    movemode = 1;
                                }
                                else if ( movemode )
                                {
                                    BitMove m[ bmaxMoves ];
                                    i32 vm = tt->bs.board.moves( m );

                                    i32 rturn = tt->bs.rturn;
									ASSERT( tt->bs.board.flags().turn == tt->bs.rturn );
                                    Move rm[ maxMoves ];
                                    i32 rvm = tt->bs.rboard.moves( rm, rturn );
                                    if ( vm != rvm )
                                    {
                                        logFmt("FATAL ERROR: new_movegen_bug!\n");
                                        vm = tt->bs.board.moves( m );
                                        char fen[4096];
                                        tt->bs.rboard.getFEN( fen, rturn );
                                        logFmt("\tfen: %s\n", fen);
                                        i32 i;
                                        for (i=0; i<vm; i++)
                                        {
                                            Move tm;
                                            tm.packed = 0;
                                            tm.from = m[i].from;
                                            tm.to = m[i].to;
                                            tm.promo = m[i].promo;
                                            i32 j;
                                            for (j=0; j<rvm; j++)
                                            {
                                                if ( tm.equal( rm[j] ) )
                                                    break;
                                            }
                                            if ( j >= rvm )
                                            {
                                                char buf[32];
                                                m[i].describeSAN( tt->bs.board, buf );
                                                logFmt("illegal move found: %s\n", buf);
                                            }
                                        }

                                        for (i=0; i<rvm; i++)
                                        {
                                            i32 j;
                                            for (j=0; j<vm; j++)
                                            {
                                                Move tm;
                                                tm.packed = 0;
                                                tm.from = m[j].from;
                                                tm.to = m[j].to;
                                                tm.promo = m[j].promo;
                                                if ( tm.equal( rm[i] ) )
                                                    break;
                                            }
                                            if ( j >= vm )
                                            {
                                                char buf[32];
                                                rm[i].describeSAN( rturn, tt->bs.rboard, buf );
                                                logFmt("legal move not found: %s\n", buf);
//									i32 vm = tt->bs.board.moves( m );
                                            }
                                        }

                                        logFmt("new board dump:\n");
                                        for (i=0; i<bmax; i++)
                                        {
                                            logFmt("bits[%d] = %llx\n", i, tt->bs.board.bits[i]);
                                        }
                                        logFmt("castrights_white = %02x\n", tt->bs.board.flags().castRights[ white ] );
                                        logFmt("castrights_black = %02x\n", tt->bs.board.flags().castRights[ black ] );

                                        logFmt("old(real): ");
                                        for (i=0; i<rvm; i++)
                                        {
                                            char buf[32];
                                            rm[ i ].describeSAN( rturn, tt->bs.rboard, buf );
                                            logFmt("%s ", buf);
                                        }
                                        logFmt("\n");
                                        logFmt("new(bad): ");
                                        for (i=0; i<vm; i++)
                                        {
                                            char buf[32];
                                            m[ i ].describeSAN( tt->bs.board, buf );
                                            logFmt("%s ", buf);
                                        }
                                        logFmt("\n");
                                    }

                                    BitMove bm;
                                    bm.packed = 0;
                                    // parse_uci_move
                                    const char *mv = tokens[i].ansi();
                                    if ( tokens[i].length() >= 4 )
                                    {
                                        i32 fx = (*mv++ | 32) - 'a';
                                        i32 fy = '8' - (*mv++);
                                        i32 tx = (*mv++ | 32) - 'a';
                                        i32 ty = '8' - (*mv++);
                                        bm.from = (fy << 3) + fx;
                                        bm.to = (ty << 3) + tx;
                                        if ( *mv )
                                        {
                                            switch ( *mv | 32 )
                                            {
                                            case 'b':
                                                bm.promo = bishop;
                                                break;
                                            case 'n':
                                                bm.promo = knight;
                                                break;
                                            case 'r':
                                                bm.promo = rook;
                                                break;
                                            case 'q':
                                                bm.promo = queen;
                                                break;
                                            }
                                        }
                                        i32 i;
                                        for (i=0; i<vm; i++)
                                        {
                                            if ( bm.equal( m[i] ) )
                                            {
                                                break;
                                            }
                                        }
                                        if ( i < vm )
                                        {
                                            realMove( tt->bs, m[i] );
                                            tt->bs.board.move( m[i] );
                                            if ( tt->bs.board.flags().fiftyCounter == 0 )
                                                tt->bs.rh->clear();
                                            if ( tt->bs.rh )
                                                tt->bs.rh->push( tt->bs.board.hash(), tt->bs.board.flags().fiftyCounter == 0 );

                                        }
                                        else
                                        {
                                            char buf[32];
                                            bm.describeUCI( buf );
                                            logFmt("FATAL ERROR: invalid move passed by gui: %s\n", buf );
                                        }
                                    }
                                }
                        }
                    }
    }
////	return 0;
}

void initializeAll()
{
    bitInit();
	Board tb;
	tb.reset();
	Move m[maxMoves];
	i32 vm = tb.moves( m, white );
	srand( getMillisec() );
}

int main( int argc, char **argv )
{
    setbuf(stdin, 0);
    setbuf(stdout, 0);

    i32 skill = 100;

	book.parseBin("cheng.cbk");
	if ( book.book.empty() )
	{
		printf("opening book not found\n");
	}
	else
	{
		printf("opening book: %d positions\n", (int)book.book.size());
	}

//	FILE *f = fopen("cheng.log", "a+");
//	fprintf(f, "NEW_ENTRY\n");
    if ( argc > 1 )
    {
        // parse args
        i32 i;
        for (i=1; i<argc; i++)
        {
			if ( strcmp( argv[i], "-personality" ) == 0 )
			{
				if ( i+1 < argc )
				{
					bool res = BitPersonality::get()->load(argv[i+1]);
					printf( res ? "loaded personality %s\n" : "failed to load personality %s\n", argv[i+1] );
				}
			}
            else if ( strcmp( argv[i], "-skill" ) == 0 )
            {
                if ( i+1 < argc )
                {
                    skill = strtol( argv[i+1], 0, 10 );
                    if ( skill < 0 )
                        skill = 0;
                    else if ( skill > 100 )
                        skill = 100;
					printf("initial skill set to %d\n", skill);

                }
            } else if ( argv[i][0] == '-' )
			{
				BitParameter *par = BitPersonality::get()->find( argv[i]+1 );
				if ( par )
				{
					par->setValue( strtol( argv[i+1], 0, 10 ) );
					printf("initial %s set to %d\n", par->getName().ansi(), par->getValue() );
//					fprintf(f, "initial %s set to %d\n", par->getName().ansi(), par->getValue() );
				}
			}
        }
    }
//	fclose(f);

//	startLogFile( "cheng3.log" );
//	logFmt("cheng3 log file started\n");
    initializeAll();
    tt = new ThinkThread;
    if ( !tt )
        return 1;
	tt->bs.tbMen = 5;
	delete tt->bs.tb;
	tt->bs.tb = new GTBWrapper;
	tt->bs.tb->init( gGTBPath.ansi(), gGTBCache * 1024*1024 );
	if ( !(tt->bs.tbMen = tt->bs.tb->numMen()) )
	{
		delete tt->bs.tb;
		tt->bs.tb = 0;
	}

//	tt->setPriority(0);					// ?? fuckin' create_suspended bug ??
    tt->run();

    if ( tt->bs.evaluator )
    {
        tt->bs.evaluator->skill = skill;
    }

#if 0
    /*	tt->send("uci\n");
    	tt->send("position fen r3kb1r/ppp1pppp/2n5/3q1b2/8/5N2/PPPPBPPP/R1BQ1RK1 b kq - 3 moves e8c8\n");
    	tt->send("go movetime 5000\n");*/

//	tt->send("uci\n");
//	tt->send("position fen 1k1rr3/pppq4/7p/P3b3/3n1Pp1/3P4/1PP4N/R1BQ1BK1 b - f4 0\n");
//	tt->send("position startpos moves b1c3 g8f6 g1f3 b8c6 e2e4 d7d5 e4d5 f6d5 c3d5 d8d5 f1e2 c8f5 e1g1 e8c8 d2d3 c8b8 a2a4 e7e5 g2g4 f5g4 f3e5 g4h3 e5f3 d5d7 f3g5 h3f1 e2f1 h7h6 f1h3 f7f5 g5f3 g7g5 h3g2 f8g7 h2h3 h8e8 a4a5 g5g4 h3g4 f5g4 f3h2 c6d4 g2f1 g7e5 f2f4 g4f3\n");
//	tt->send("position startpos moves b1c3 g8f6 g1f3 b8c6 e2e4 d7d5 e4d5 f6d5 c3d5 d8d5 f1e2 c8f5 e1g1 e8c8 d2d3 c8b8 a2a4 e7e5 g2g4 f5g4 f3e5 g4h3 e5f3 d5d7 f3g5 h3f1 e2f1 h7h6 f1h3 f7f5 g5f3 g7g5 h3g2 f8g7 h2h3 h8e8 a4a5 g5g4 h3g4 f5g4 f3h2 c6d4 g2f1 g7e5 f2f4 g4f3 h2f3 d7g4 f1g2 e8f8 g1f2 e5g3 f2f1 d4f3 g2f3 f8f3\n");
    /*	tt->send("position startpos moves b1c3 g8f6 g1f3 b8c6 e2e4 d7d5 e4d5 f6d5 c3d5 d8d5 f1e2 c8f5 e1g1 e8c8 d2d3 c8b8 a2a4 e7e5 g2g4 f5g4 f3e5 g4h3 e5f3 d5d7 f3g5 h3f1 e2f1 h7h6 f1h3 f7f5 g5f3 g7g5 h3g2 f8g7 h2h3 h8e8 a4a5 g5g4 h3g4 f5g4 f3h2 c6d4 g2f1 g7e5 f2f4 g4f3 g1h1 e5h2 h1h2 e8e2 f1e2 f3e2 d1h1 d7d6 c1f4 d6f4 h2h3 f4f5 h3h2 f5e5 h2h3 d8g8\n");
    	tt->send("go movetime 5000\n");	*/


    execFirst = "position fen rnbK2R1/p6p/p1kNpN1r/P3B1Q1/3P1p1p/5p2/5p1b/8/ w\n";
//						   r3kb1r/ppp1pppp/2n5/3q1b2/8/5N2/PPPPBPPP/R1BQ1RK1 b kq - 3
//	execFirst = "position fen r1bqk2r/ppp2ppp/1bn5/1P2p3/4p1n1/P4N1P/2PP1PP1/R1BQKB1R w KQkq - 0 moves h3g4\n";
//	execFirst = "position fen r1bqk2r/ppp2ppp/1b6/1P2p3/3nQ1P1/P7/2PP1PP1/R1B1KB1R b KQkq - 2 moves d4c2\n";

    execFirst = "position fen 1Q1nk2r/5pp1/p6p/q3p3/3p2P1/1R2nPK1/2r1bN1P/6NR w - - 2 moves g1e2\n";

    execFirst = "position fen 3rq2r/1pk2pp1/p1n1b2p/2P1p3/2Rp4/P1nP3N/1Q2PPPP/4KBNR w K - 1 moves c4c3\n";

    // NOTE: movgen_bug HERE!!!
    execFirst = "position fen 1k1r3r/ppp2ppp/8/1P2p3/P1P5/3p1P2/3QP1PP/R3KBq1 w Q - 0 moves e2d3\n";

//	execFirst = "position fen r1bqk2r/ppp2p2/2n2n1p/4p1p1/3p3N/QP1P1N2/P1P1PPPP/R3KB1R w KQkq g5 0 moves f3g5\n";

//	execFirst = "position fen r2qk2r/2p2p2/ppn5/1Q1bp3/3p2PP/1P1Pn3/P1PNP1P1/R3KB1R w KQkq - 0 moves b5d5\n";

//	execFirst = "position fen 1k1r3r/ppp2ppp/8/1P2p3/P1P5/3p1P2/3QP1PP/R3KBq1 w Q - 0\n";

//	execFirst = "position fen 1k1r3q/1ppn3p/p4b2/4p3/8/P2N2P1/1PP1R1BP/2K1Q3 w - - moves d3e5\n";	// should be -2 (ok)

    execFirst = "position startpos\nuci\n";

//	execFirst = "position fen rnbK2R1/p6p/p1kNpN1r/P3B1Q1/3P1p1p/5p2/5p1b/8/ w\n";

    execFirst = "position fen r2qk2r/ppp2pp1/1bn1pnp1/8/NP4P1/P3p2P/2PP1P2/R1BQKB1R w KQkq - 0 13\n";	// go movetime 1500 -- avoid_blunder!

    execFirst = "position fen 2kr4/pppnqpB1/1bn1p1p1/1N1p2r1/1P3Q2/P3P2P/2PP1P2/R3KB1R b KQ - 1 16\n";	// f7f6 should be found very soon!
    // (probably eval/qsearch bug?! don't know...)

    execFirst = "position fen r1b1kb1r/ppp3pp/2n5/4pp2/6q1/2PP1N2/P3PPPP/R1BQKB1R w KQkq - 3 11\n";

    execFirst = "position fen 1rr1k2q/1ppb4/p2b1Q2/n3ppB1/8/2PP2P1/P3PP2/1R2KB2 w - - 0 30\n";

    execFirst = "position fen 1R1rkb1r/P3pppp/3p4/3P4/N4P2/5NP1/2P4P/4K2n b k - 2 27\n";

    execFirst = "position fen 2b2rk1/4q1pp/8/2p2p1Q/rn1p1B2/3PP3/2p2PPP/R3KBR1 w Q - 0 26\n";

    execFirst = "position fen 2k2r2/p3R3/1pbp2pp/6P1/P4P1K/2B4P/r7/8 b - - 0 41\n";

    execFirst = "position fen 8/1k6/p1p1p3/2Pp1rBp/RP1PK2P/8/8/8 w - d5 0 moves c5d6\n";

    execFirst = "position fen 8/3k4/8/5p2/6P1/1P1pK3/P3b3/8 w - f6 0 56\n";

    execFirst = "position fen r2br1k1/ppp4p/q4pp1/4PN2/3B1QP1/5B1b/PP5P/R2R2K1 b - - 0 27\n";

    execFirst = "position fen 2k1nb1r/p1p2ppp/4p3/3rP2q/3BN3/3Q1P2/PP3P1P/2KR3R b - - 3 18\n";

    execFirst = "position fen 8/3r3k/1PR4p/2p3p1/2K5/2P5/8/8 b - - 0 70\n";

//	execFirst = "position fen r3k2r/3p1ppp/1p6/4np2/2P5/3B4/PP3P1P/R2KR3 b kq - 3\n";
    execFirst = "position fen r3k2r/3p1ppp/1p6/4np2/2P5/3B4/PP3P1P/R2K3R w kq - 2 moves h1e1\n";

    execFirst = "position startpos moves d2d4 g8f6 b1c3 b7b6 e2e4 c8b7 e4e5 f6e4 c3e4 b7e4 d1e2 e4d5 c2c4 d5b7 e2c2 e7e6 g1f3 f8b4 c1d2 b4d2 e1d2 c7c5 d4c5 b7f3 g2f3 d8g5 d2d1 g5e5 c5b6 a7b6 c2c3 b8c6 c3e5 c6e5 f3f4 e5g6 f4f5 e6f5 f1d3 g6e5 h1e1\n";

    execFirst = "position startpos moves d2d4 g8f6 b1c3 b7b6 e2e4 c8b7 e4e5 f6e4 c3e4 b7e4 d1e2 e4d5 c2c4 d5b7 e2c2 e7e6 g1f3 f8b4 c1d2 b4d2 e1d2 c7c5 d4c5 b7f3 g2f3 d8g5 d2d1 g5e5 c5b6 a7b6 c2c3 b8c6 c3e5 c6e5 f3f4 e5g6 f4f5 e6f5 f1d3\n";

//	execFirst = "position startpos moves d2d4 g8f6 b1c3 b7b6 e2e4 c8b7 e4e5 f6e4 c3e4 b7e4 d1e2 e4d5 c2c4 d5b7 e2c2 e7e6 g1f3 f8b4 c1d2 b4d2 e1d2 c7c5 d4c5 b7f3 g2f3 d8g5 d2d1 g5e5 c5b6 a7b6 c2c3 b8c6 c3e5 c6e5 f3f4 e5g6 f4f5 e6f5 f1d3 g6e5 h1e1\n";

    execFirst = "position startpos moves b1c3 g8f6 e2e3 b8c6 g1h3 h7h6 f1b5 a7a6 b5e2 d7d5 e1g1 c8h3 g2h3 d8d7 e2g4 f6g4 h3g4 h6h5 g4g5 d7f5 f2f4 e8c8 d2d3 c8b8 d1f3 e7e6 f3g3 f5g4 g3g4 h5g4 c1d2 f8d6 c3d1 f7f6 d2c3 e6e5 b2b3 d5d4 e3d4 e5d4 c3e1 f6g5 e1g3 g5f4 g3f4 c6b4 f4d6 d8d6 f1f2 d6c6 d1b2 b7b5 a2a3 c6c2 a1f1 c2f2 f1f2 b4c6 f2e2 h8h3 g1g2 b5b4 a3b4 c6b4 e2e4 b4d3 e4g4 h3e3 b2d3 e3d3 b3b4 d3c3 g4d4 c7c5 b4c5 c3c5 g2f3 c5h5 d4d8 b8c7 d8g8 h5h2 g8g7 c7b6 g7g6 b6b5 g6g5 b5b4 g5g4 b4b3 g4g5 h2h8 g5a5 h8a8 f3e2 b3b4 a5g5\n";

    execFirst = "position startpos moves e2e3 e7e6 g1h3 g8h6 d1h5 b8c6 f1e2 g7g6 h5g5 d8g5 h3g5 f8e7 f2f4 f7f6 g5e4 h6f5 e1g1 e8g8 c2c4 f5h4 g2g3 h4f5 e2g4 c6b4 b1a3 b4d3 a3b5 e7d8 g4f5 e6f5 e4f2 d3c5 b2b3 f8e8 b3b4 c5e6 c1b2 b7b6 b5c3 g8g7 b4b5 a7a6 f1c1 e6c5 d2d4 c5e4 c3e4 f5e4 d4d5 a6b5 c4b5 h7h6 c1c4 c8b7 c4d4 a8a7 f2e4 d7d6 a2a4 g7g8 a1e1 f6f5 e4c3 d8f6 d4d3 f6c3 b2c3 a7a4 e1a1 a4a1 c3a1 e8a8 a1f6 a8a5 d3c3 h6h5 c3c7 b7d5 c7d7 a5b5 d7d6 d5e4 f6d4 b5b1 g1f2 b1h1 h2h4 b6b5 d6g6 g8h7 g6g5 h7h6 d4g7 h6h7 g7f8 h1h2 f2e1 h2g2 g5h5 h7g8 f8c5 e4d3 c5d4 g2e2 e1d1 e2g2 h5h8 g8f7 h8h7 f7e8 h7g7 d3e4 d4c5 e4d3 g7e7 e8d8 e7e5 d8c7 c5b4 c7c6 e5c5 c6b6 c5d5 d3c2 d1c1 c2e4 d5d6 b6c7 d6g6 c7d7 g6g8 d7e6 g8g7 g2c2 c1d1 c2a2 d1e1 a2h2 g7g6 e6f7 g6b6 h2h3 b6b5 h3g3 e1e2 g3g2 e2d1 g2g3 b5a5 g3e3 a5a3 e3a3 b4a3 f7g6 d1d2 g6h5 a3e7 e4f3 d2d3 h5g4 e7g5 g4h5 d3d4 f3e2 d4e5 e2d3 g5e7 d3e4 e5d4 e4f3 e7f6 f3e4 f6g5 e4b1 g5d8 b1c2 d8e7 c2b3 e7g5 b3c2 g5d8 c2b3 d8e7 b3c2 d4e3 c2e4 e7g5 e4d5 e3d4 d5e4 g5f6 e4c6 d4e5 c6d7 f6e7 d7c8 e7g5 c8d7 g5f6 d7c8 f6e7 c8d7 e7g5 h5g4 e5d6 d7c8 d6c5 g4h5 c5d5 c8b7 d5e6 b7e4 e6d6 e4d3 g5e7 d3e4 d6e5 e4c2 e5d5 c2e4 d5d6 e4f3 e7g5 f3e4 d6e6 e4c2 g5e7 c2e4 e7d8 e4d3 e6e5 d3c2 d8f6 c2d3 e5e6 d3c2 e6d6 c2e4 f6d8 e4g2 d6e5 g2e4 e5d6 e4g2\n";

    execFirst = "position startpos moves g1f3 g8f6 b1c3 g7g6 g2g3 f8h6 d2d3 h6c1 d1c1 b8c6 f1g2 d7d5 c1f4 f6h5 f4g5 d5d4 c3e4 c8f5 f3h4 h7h6 g5d2 f5e4 g2e4 d8d6 c2c3 e8c8 c3d4 c6d4 a1c1 h5f6 e4g2 g6g5 h4f3 d4c6 h2h4 d6d5 h4g5 h6g5 e1g1 h8g8 f3h2 d5e6 g2c6 b7c6 f1e1 g8h8 d2a5 e6h3\n";

    execFirst = "position fen 8/1rRp1kpp/3P1p2/p7/8/4RK2/1r4PP/8 b - - 2\n";

    execFirst = "position fen 5b1k/6pp/5p2/1Q2p2N/pqP3P1/4PK1P/8/8 b - - 1\n";

    execFirst = "position fen r3k3/1pp2pp1/8/p2p4/1n1P3q/2Q1PP2/1P1PK3/R1B5 b q - 2 30\n";

    execFirst = "position fen 7R/1p6/4p1kb/3bB2p/1p1K4/8/1P3r1P/4R3 b - - 2 46\n";

    execFirst = "position fen r1b1kb1r/pp1ppppp/n4n2/2pP4/1qP5/1PN5/P2BPPPP/R2QKBNR b\n";

    execFirst = "position fen 8/1k6/3K4/B1PP4/3N4/b2b4/4p3/8 b - - 14\n";

    execFirst = "position fen 3b4/8/8/8/8/6kp/8/6K1 w - - 0\n";

    execFirst = "position fen rnbK2R1/p6p/p1kNpN1r/P3B1Q1/3P1p1p/5p2/5p1b/8/ w\n";

    execFirst = "position fen 2kr2nr/pp1n1ppp/2p1p3/q7/1b1P1B2/P1N2Q1P/1PP1BPP1/R3K2R w KQ - 1\n";

    execFirst = "position fen k7/8/8/8/8/8/5P2/7K w - - 0\n";

    execFirst = "position fen r5k1/6p1/p3Pr1p/2b1N3/PpB1bP2/5pK1/7P/R2R4 b - - 3\n";

    execFirst = "position fen 5rk1/p2p2pp/4p3/1p2Pp2/3P4/qB2PQ2/6PP/3R2K1 w - - 0\n";

//	execFirst = "position fen b7/6pk/p3Pr1p/2b1N3/PpB2P2/6K1/5p1P/R7 w - - 0\n";

    execFirst = "position fen 2bb4/B2p2kp/p2P1p2/4r3/2p2NP1/2P5/Pr4PP/4RRK1 b - - 0\n";

//	execFirst = "position fen 8/5p2/PPnkp1p1/7p/8/8/5PPP/6K1 w - - 1 35\n";

    execFirst = "position fen rr4k1/2R2ppp/4p3/p2p1q2/4n3/1Q2P1P1/PP3P1P/2R1B1K1 b - - 1 22\n";

    execFirst = "position fen r3kb1r/1p3p2/2p1p1p1/P2pP3/2bP1PKp/2P2R1P/P5P1/RN2B3 b kq - 1 19\n";

    execFirst = "position fen r1r4k/7p/4N1p1/2b1np2/1P6/6P1/P1R1PP1P/6K1 w - - 0 36\n";

    execFirst = "position startpos moves d2d4 d7d5 c2c4 d5c4 g1f3 a7a6 e2e4 b7b5 a2a4 c8b7 a4b5 a6b5 a1a8 b7a8 b1c3 c7c6 f1e2 b8d7 d1c2 d8c7 c1d2\n";

    execFirst = "position fen 5k2/2R4n/p3q1p1/1p1p4/1P1P3Q/P7/1rB4K/8 w - - 2 63\n";

    execFirst = "position fen b3r1k1/2P4p/p2K2p1/8/p2N4/R3Pp2/8/8 w - - 0 52\n";

    // doesn't find h5! (should find at depth 11)
    execFirst = "position fen r5n1/p2q3k/4b2p/3pB3/2PP1QpP/8/PP4P1/5RK1 w - - 1\n";

    // there MUST be sort of a repetition bug!
    execFirst = "position fen r4q1k/p2bR1rp/2p2Q1N/5p2/5p2/2P5/PP3PPP/R5K1 w - - 0 1\n";

    // perft BUG
    // perft 5 should be 674624!
    execFirst = "position fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -\n";
    // movegen bug! should generate 6 legal moves!
    execFirst = "position fen 8/1K5r/3p4/1Pp5/1R3p1k/8/4P1P1/8 w - c6 0\n";

    execFirst = "position fen 8/8/8/3b1R2/8/4K1k1/8/8 b - - 43 143\n";

    execFirst = "position fen r5n1/p2q3k/4b2p/3pB3/2PP1QpP/8/PP4P1/5RK1 w - - 1\n";// moves h4h5\n";

	execFirst = "position fen 2kr3r/p4ppp/2p3b1/4p3/4P1P1/2N1P2P/PP6/R3K2R w - - 1 0 moves h3h4 h7h5 g4g5 c8b7 c3a4 a7a6 a1c1 d8d6 a4c5 b7a7\n";

	execFirst = "position fen 1r2k2r/p2p1p2/1p2p2p/2pPn1p1/2P5/P4P2/1P2PPBP/2R2RK1 b k h6 3 0\n";
#endif
//	execFirst = "position fen 2kr1b1r/ppp3p1/5nbp/4B3/2P5/3P2Nq/PP2BP2/R2Q1RK1 b - - 0\n";		// doesn't find d8d3 but old cheng finds it!

//	execFirst = "position fen r5n1/p2q3k/4b2p/3pB3/2PP1QpP/8/PP4P1/5RK1 w - - 1\n";

//	execFirst = "position fen 8/3bP3/8/2rPk3/7R/8/6P1/7K b - - 0 79\n";

//    execFirst = "position fen 3b4/8/8/8/8/6kp/8/6K1 w - - 0\n";

//	execFirst = "position fen r7/6k1/8/8/8/8/6P1/5K1R w - - 2 1\n";
//	execFirst = "position fen 4q2k/2r1r1p1/4PR1p/p1p5/P2pQ2P/1P1B1R2/6P1/6K1 b - - 0 38\n";

//	execFirst = "position fen 8/8/6p1/4K3/7b/8/8/2k5 w - - 0 76\n";
//	execFirst = "position fen 2kr1b1r/ppp3p1/5nbp/4B3/2P5/3P2Nq/PP2BP2/R2Q1RK1 b - - 0\n";		// doesn't find d8d3 but old cheng finds it!

//	execFirst = "position fen r5n1/p2q3k/4b2p/3pB3/2PP1QpP/8/PP4P1/5RK1 w - - 1\n";

//	execFirst = "position fen 6k1/1p4r1/1Q6/2pqp3/8/B1nn4/7R/K6R b - - 1 57\n";

    uciLoop();
    delete tt;
    return 0;
}
