/***************************************************************************/
/*                               Smirfoglot,                               */
/* An adapter to run engines using Smirf protocol under WinBoard,          */
/* by H.G. Muller                                                          */
/***************************************************************************/
/* fused to generic Winboard driver */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <windows.h>

#include <conio.h>

int IOCheck;

int unbuffered_input(int init)
{
    static int pipe;
    static HANDLE inp;
    DWORD cnt;

    if(init)
    {   inp = GetStdHandle(STD_INPUT_HANDLE);
    } else
    {   if(!PeekNamedPipe(inp, NULL, 0, NULL, &cnt, NULL)) return 1;
        return cnt;
    }
}

typedef const char * (__cdecl *MYPROC)(char, char, int, char *); 
  
int StartKey;

#define EMPTY -1
#define WHITE 0
#define BLACK 16

/* Global variables visible to engine. Normally they */
/* would be replaced by the names under which these  */
/* are known to your engine, so that they can be     */
/* manipulated directly by the interface.            */

int Side;
int Move;
int PromPiece;
int Result;
int TimeLeft;
int MovesLeft;
int MaxDepth;
int Post;
int Fifty;
int Ponder;
int PonderFlag;
int UnderProm;
int pos[8]={3,0,1,2,4,5,6,7},pc[]={4,6,5,5,1,1,7,3},
    mask[]={0x18,0x81,0xAA,0x55,0xAA,0x55,0xFF,0xFF};
int GameNr;

int Ticks, tlim;

int GamePtr, HistPtr;


/* Generic main() for Winboard-compatible engine     */
/* (Inspired by TSCP)                                */
/* Author: H.G. Muller                               */

/* The engine is invoked through the following       */
/* subroutines, that can draw on the global vaiables */
/* that are maintained by the interface:             */
/* Side         side to move                         */
/* Move         move input to or output from engine  */
/* PromPiece    requested piece on promotion move    */
/* TimeLeft     ms left to next time control         */
/* MovesLeft    nr of moves to play within TimeLeft  */
/* MaxDepth     search-depth limit in ply            */
/* Post         boolean to invite engine babble      */

/* InitEngine() progran start-up initialization      */
/* InitGame()   initialization to start new game     */
/*              (sets Side, but not time control)    */
/* Think()      think up move from current position  */
/*              (leaves move in Move, can be invalid */
/*               if position is check- or stalemate) */
/* DoMove()     perform the move in Move             */
/*              (togglese Side)                      */
/* ReadMove()   convert input move to engine format  */
/* PrintMove()  print Move on standard output        */
/* Legal()      check Move for legality              */
/* ClearBoard() make board empty                     */
/* PutPiece()   put a piece on the board             */

/* define this to the codes used in your engine,     */
/* if the engine hasn't defined it already.          */

char userName[256] = "Donationware Version - donate !";
char engineKey[256] = "342f6f5e 026b3179";

char engineName[32];
char engineVersion[32];

InitEngine(MYPROC ProcAdd)
{
        const char *p; char buf[256], c, e;
        FILE *f; int i, j;

        if((f = fopen("SmirfGUI.ini", "r")) != NULL)
        {   /* there is a .ini file; get keys from it */
            j = 0;
            do {
                i=0; while(i<256 && (buf[i++] = e = fgetc(f)) != '\n' && e != EOF);
                buf[255] = '\n';
                if(sscanf(buf, "User%c", &c)==1 && c=='=')
                {   i=5; do userName[i-5] = buf[i]; while(buf[i++] != '\n');
                    userName[i-6] = 0; j |= 1;
                } else
                if(sscanf(buf, "KeyEngine%c", &c)==1 && c=='=')
                {   i=10; do engineKey[i-10] = buf[i]; while(buf[i++] != '\n');
                    engineKey[i-11] = 0; j |= 2;
                }
                if(j == 3) break;
            } while(e != EOF);
        }
        
        p = (ProcAdd) ('L', 'N', 0, userName); 
        if(p == NULL) 
        { printf("telluser Doesn't accept user name\n"); exit(0);} 
        p = (ProcAdd) ('L', 'K', 0, engineKey);
        if(p == NULL) 
        { printf("telluser Doesn't accept engine key\n"); exit(0);} 
        unbuffered_input(1);

        p = (ProcAdd) ('I', 'E', 0, 0); 
        if((p != NULL) && (strlen(p) < sizeof(engineName)))
           strcpy(engineName, p);
        else
           strcpy(engineName, "Smirfoglot");

        p = (ProcAdd) ('I', 'V', 0, 0); 
        if((p != NULL) && (strlen(p) < sizeof(engineVersion)))
           strcpy(engineVersion, p);
}

InitGame(MYPROC ProcAdd, char *FEN)
{
 int i,j;
        const char *p;
        p = (ProcAdd) ('E', 'F', 0, FEN); 
        if(p == NULL) 
        { printf("telluser Doesn't accept initial position\n"); exit(0);} 


 Side = WHITE;
}

char *(FENs[]) = {
    "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
    "rnbqckabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQCKABNR w KQkq - 0 1",
    "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR w KQkq - 0 1",
};
                                         
int main(int argc, char **argv)
{
        HINSTANCE hinstLib; 
        MYPROC ProcAdd; 
        BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 
        const char *p; 
        int Computer, MaxTime, MaxMoves, TimeInc, sec, i, j, cnt;
        char line[256], command[256], c, cc, *FEN;
        int m, nr;
        FILE *f;

	signal(SIGINT, SIG_IGN);
        hinstLib = LoadLibrary(TEXT("SmirfEngine.dll")); 
        if(hinstLib == NULL) { printf("telluser Smirf DLL not found\n"); exit(0); } 
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, TEXT("_DoCmd")); 
        if(ProcAdd == NULL) 
        {   ProcAdd = (MYPROC) GetProcAddress(hinstLib, TEXT("DoCmd")); 
            if(ProcAdd == NULL) 
            {   printf("telluser Bad entry point %d\n", GetLastError()); 
                fFreeResult = FreeLibrary(hinstLib);
                exit(0); 
            }
        } 

        while(argc > 1) {
            if(argv[1][0] == '=' || argv[1][0] == '/') {
                switch(argv[1][1]) {
                    case 'H':
                        if(sscanf(argv[1]+2, "%d", &m) != 1) break;
                        p = (ProcAdd) ('C', 'H', m, NULL); /* srt hash size */
                        if(p == NULL) 
                        { printf("telluser Hash size refused\n"); exit(0);} 
                        break;
                }
            }
            argc--; argv++;
        }

        printf("tellics say     Smirfoglot 1.1\n");
        printf("tellics say     by H.G. Muller\n");
        p = (ProcAdd) ('C', 'H', -1, NULL); /* get hash size */
        if(p == NULL) 
             printf("telluser Bad hash size\n"); 
        else printf("tellics say     hash size = '%s'\n", p);
        InitEngine(ProcAdd);
        InitGame(ProcAdd, FENs[0]);
        Computer = EMPTY;
        MaxTime  = 10000;  /* 10 sec */
        MaxDepth = 30;     /* maximum depth of your search */

        for (;;) {
		fflush(stdout);
                if (Side == Computer) {
                        /* think up & do move, measure time used  */
                        /* it is the responsibility of the engine */
                        /* to control its search time based on    */
                        /* MovesLeft, TimeLeft, MaxMoves, TimeInc */
                        /* Next 'MovesLeft' moves have to be done */
                        /* within TimeLeft+(MovesLeft-1)*TimeInc  */
                        /* If MovesLeft<0 all remaining moves of  */
                        /* the game have to be done in this time. */
                        /* If MaxMoves=1 any leftover time is lost*/
                        const char *p; int lastDepth=0;
                        Ticks = GetTickCount();
                        p = (ProcAdd) ('C', '+', 0, NULL); /* start thinking */
                        if(p == NULL) 
                        { printf("telluser Doesn't want to move\n"); continue;} 
                        do {
                            if(Post && (cnt & 15) == 1)
                            {   int d, min, x; float sec, score;
                                p = (ProcAdd) ('C', 'I', 2, NULL); /* poll PV */
                                if(p != NULL && 
                                   sscanf(p, "%d:%f (%d.%d%c) %f", &min, &sec, &d, &x, &x, &score)==6 && 
                                   d != lastDepth) 
                                {   lastDepth = d;
                                    printf("%3d  %5d %5d  100000 %s\n", 
                                         d, (int)(score*100), (int)(6000*min+100*sec), p+24);
                                    fflush(stdout);
                                } 
                            }
                            Sleep(20); cnt++;                  /* wait a bit */
                            p = (ProcAdd) ('C', 'B', 0, NULL); /* poll if ready */
//                            while(unbuffered_input(0)) {
//                                if(getchar() == '?') p = NULL;
//                            }
                        } while(p != NULL);
                        p = (ProcAdd) ('C', 'I', 1, NULL); /* poll PV */
                        if(p == NULL) 
                        { printf("telluser No PV\n"); continue;} 
                        p = (ProcAdd) ('C', 'I', 2, NULL); /* poll score */
                        if(p == NULL) 
                        { printf("telluser No Score\n"); continue;} 
                        p = (ProcAdd) ('C', 'A', 0, NULL); /* fetch move */
                        if(p == NULL) 
                        { printf("telluser No move\n"); continue;} 
                        Side ^= BLACK^WHITE;
                        printf("move %s\n", p);
                        p = (ProcAdd) ('E', 'M', 0, (char *) p); /* make move */
                        if(p == NULL) 
                        { printf("telluser Refuses own move\n"); continue;} 
                        m = GetTickCount() - Ticks;

                        /* time-control accounting */
                        TimeLeft -= m;
                        TimeLeft += TimeInc;
                        if(--MovesLeft == 0) {
                            MovesLeft = MaxMoves;
                            if(MaxMoves == 1)
                                 TimeLeft  = MaxTime;
                            else TimeLeft += MaxTime;
                        }

                        GamePtr++;
                        if(Ponder) {
                            p = (ProcAdd) ('C', 'P', 0, NULL); /* make move */
                            if(p == NULL) 
                            { printf("telluser Refuses to ponder\n"); continue; } 
                            PonderFlag = 1;
                        }
                        continue;
		}
		if (!fgets(line, 256, stdin))
			return;
		if (line[0] == '\n')
			continue;
		sscanf(line, "%s", command);
		if (!strcmp(command, "post")) {
                        Post = 1;
			continue;
		}
		if (!strcmp(command, "nopost")) {
                        Post = 0;
			continue;
		}
		if (!strcmp(command, "random")) {
			continue;
		}
		if (!strcmp(command, "?")) {
			continue;
		}
                if (!strcmp(command, "p")) {
                        const char *p;
                        p = (ProcAdd) ('F', 'S', 0, NULL); 
                        printf("%s\n", p); 
			continue;
                }
		if (!strcmp(command, "time")) {
                        /* set time left on clock */
                        const char *p; int t; char color = Side == WHITE ? 'B' : 'W';
                        sscanf(line, "time %d", &t);
                        t  *= 10; /* centi-sec to ms */
                        if(MaxMoves != 0) t = (1.5*t)/(MovesLeft+2);
                        else if(TimeInc == 0) t = t*0.99; else t = t + 20*(TimeInc - 50); 
                        p = (ProcAdd) ('T', color, t, NULL); 
                        if(p == NULL) 
                        { printf("telluser Doesn't accept time left\n"); continue;} 
			continue;
		}
		if (!strcmp(command, "otim")) {
                        /* opponent's time */
                        const char *p; int t; char color = Side == WHITE ? 'W' : 'B';
                        sscanf(line, "otim %d", &t);
                        t  *= 10; /* centi-sec to ms */
                        if(MaxMoves != 0) t = (1.5*t)/(MovesLeft+2);
                        else if(TimeInc == 0) t = t*0.99; else t = t + 20*(TimeInc - 50); 
                        p = (ProcAdd) ('T', color, t, NULL); 
                        if(p == NULL) 
                        { printf("telluser Doesn't accept opponent's time left\n"); continue;} 
			continue;
		}
		if (!strcmp(command, "hard")) {
                        Ponder = 1;
			continue;
		}
		if (!strcmp(command, "easy")) {
                        Ponder = 0;
			continue;
		}
                if (!strcmp(command, "xboard"))
			continue;
                if (!strcmp(command, "draw"))
                        /* ignore all draw offers */
			continue;
		if (!strcmp(command, "protover")) {
                        printf("feature variants=\"normal,capablanca,gothic,fischerandom,caparandom\"\n");
                        printf("feature myname=\"%s %s\"\n", engineName, engineVersion);
                        printf("feature setboard=1 colors=0 done=1\n");
			continue;
                }
		if (!strcmp(command, "st")) {
                        /* move-on-the-bell mode     */
                        /* indicated by MaxMoves = 1 */
                        sscanf(line, "st %d", &MaxTime);
                        MovesLeft = MaxMoves = 1;
                        TimeLeft  = MaxTime *= 1000;
                        TimeInc   = 0;
			continue;
		}
		if (!strcmp(command, "sd")) {
                        /* set depth limit (remains in force */
                        /* until next 'sd n' command)        */
                        sscanf(line, "sd %d", &MaxDepth);
			continue;
		}
                if (!strcmp(command, "level")) {
                        /* normal or blitz time control */
                        const char *p;
                        sec = 0;
                        if(sscanf(line, "level %d %d %d",
                                 &MaxMoves, &MaxTime, &TimeInc)!=3 &&
                           sscanf(line, "level %d %d:%d %d",
                                 &MaxMoves, &MaxTime, &sec, &TimeInc)!=4)
                             continue;
                        MovesLeft = MaxMoves;
                        TimeLeft  = MaxTime = 60000*MaxTime + 1000*sec;
                        p = (ProcAdd) ('T', '#', (MaxMoves!=0), NULL); 
                        if(p == NULL) 
                        { printf("telluser Doesn't accept nr of moves\n"); continue;} 
                        TimeInc  *= 1000;
                        continue;
                }
		if (!strcmp(command, "rejected")) {
			continue;
		}
		if (!strcmp(command, "accepted")) {
			continue;
		}
		if (!strcmp(command, "computer")) {
			continue;
		}

                /* The following commands interrupt pondering */
                if(PonderFlag) {
                    p = (ProcAdd) ('C', '-', 0, NULL); /* stop ponder */
                    if(p == NULL) 
                    { printf("tell Refuses to stop pondering\n"); } 
                    else PonderFlag = 0;
                }

		if (!strcmp(command, "variant")) {
                        const char *p; char c;

                        if(sscanf(line+8, "normal%c", &c)==1 && c=='\n') FEN = FENs[0]; else
                        if(sscanf(line+8, "gothic%c", &c)==1 && c=='\n') FEN = FENs[1]; else
                        if(sscanf(line+8, "capablanca%c", &c)==1 && c=='\n') FEN = FENs[2]; else
                        { printf("telluser variant not implemented yet\n", line+8); continue;} 
                        p = (ProcAdd) ('E', 'F', 0, FEN); 
                        if(p == NULL) 
                        { printf("telluser Doesn't accept initial position\n"); continue;} 
			continue;
		}
		if (!strcmp(command, "setboard")) {
                        const char *p;
                        p = (ProcAdd) ('E', 'F', 0, line+9); 
                        if(p == NULL) 
                        { printf("telluser Doesn't accept FEN\n"); continue;} 
                        Computer = EMPTY;
			continue;
		}
		if (!strcmp(command, "go")) {
                        /* set computer to play current side to move */
                        Computer = Side;
                        MovesLeft = -(GamePtr+(Side==WHITE)>>1);
                        while(MaxMoves>0 && MovesLeft<=0)
                            MovesLeft += MaxMoves;
			continue;
		}
		if (!strcmp(command, "new")) {
                        /* start new game */
                        InitGame(ProcAdd, FENs[0]);
                        GamePtr   = 0;
                        GameNr++;
                        HistPtr   = 0;
                        Computer  = BLACK;
                        TimeLeft  = MaxTime;
                        MovesLeft = MaxMoves;
                        Ponder = 0;
			continue;
		}
		if (!strcmp(command, "quit")) {
                        /* exit engine */
                        fFreeResult = FreeLibrary(hinstLib);
			return;
                }
		if (!strcmp(command, "force")) {
                        /* computer plays neither */
                        Computer = EMPTY;
			continue;
		}
                /* command not recognized, assume input move */
                m = line[0]<'a' | line[0]>'j' | line[1]<'1' | line[1]>'8' |
                    line[2]<'a' | line[2]>'j' | line[3]<'1' | line[3]>'8';
                if (m)
                        /* doesn't have move syntax */
			printf("Error (unknown command): %s\n", command);
                else {
                        const char *p;
                        if(line[4] <= ' ') line[4] = 0; else { line[5] = 0; if(line[4]>='a') line[4] += 'A'-'a'; }
                        p = (ProcAdd) ('E', 'M', 0, line); 
                        if(p == NULL || p[0] != 'o' || p[1] != 'k') 
                              printf("Illegal move:%s\n", line);
                        else {  /* legal move, perform it */
                              Side ^= BLACK^WHITE;
                              GamePtr++;
                        }
		}
	}
}

