/***************************************************************************/
/*                               Gothoglot,                                */
/* An adapter to connect the www.gothic-chess.com server to WinBoard,      */
/* by H.G. Muller                                                          */
/***************************************************************************/
char *Version = "1.1";
/* 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>

#ifdef WIN32
#include <winsock.h>
#include <io.h>
#else
#define closesocket close
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif

#define TBUF_LEN 1024*16

int IOCheck;
FILE *logfile;

int board[8][10];
int fpos[40], rpos[40];
char engine[80];

char *gameEndMsg[] = {
       "White has been checkmated.",
       "Black has been checkmated.",
       "Black has been stalemated.",
       "White has been stalemated.",
       "Server terminated game for unknown reason",
       "Server terminated game for unknown reason",
       "Server terminated game for unknown reason",
       "Server terminated game for unknown reason",
       "Server terminated game for unknown reason",
       "Server terminated game for unknown reason",
       "White resigns.",
       "Black resigns.",
       "White forfeits on time.",
       "Black forfeits on time.",
       "Game drawn by mutual agreement.",
       "Game drawn by repetition.",
       "Game drawn by the 50-move rule.",
       "Game drawn because neither player has mating material.",
       "Draw. White is out of time and black has no material to mate.",
       "Draw. Black is out of time and white has no material to mate."
};

char *gameResult[] = {
	"0-1",
	"1-0",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"0-1",
	"1-0",
	"0-1",
	"1-0",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2",
	"1/2-1/2"
};

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;
    }
}

/* routines for communication with server */  

void printpacket(char *s, char *p, int n)
{
        int i, j=0, k; char c;

        fprintf(logfile, "%s\n", s);
        for(i=0; i<n; i++) {
            fprintf(logfile, " %02x", *p++ & 255);
            if((++j & 15) == 0) fprintf(logfile, "\n");
        }
        if((j&15)!=0) fprintf(logfile, "\n");
}

int prepareMessage(char *buf, int header, int messType, int user,
                     char c1, char c2, char c3, char c4, char c5, char c6,
                     char *mess)
{
      int i, len = 0;

      for(i=0; i<21; i++) buf[i] = 0;

      if(mess != NULL) while(mess[len])len++; 
      buf[20] = len & 255;
      buf[19] = len >> 8;
      buf[9] = messType & 255;
      buf[8] = messType >> 8;
      strcpy(buf + 21, mess);
      len += 21 - 8;
      buf[7] = len & 255;
      buf[6] = len >> 8;
      buf[3] = header & 255;
      buf[2] = header >> 8;
      buf[13] = user & 255;
      buf[12] = user >> 8;
      buf[14] = c1;
      buf[15] = c2;
      buf[16] = c3;
      buf[17] = c4;
      buf[18] = c5;

      return len + 8;
}

int chatMessage(char *buf, int header, int messType, int user, int table, 
                     char *mess)
{
      int i, len = 0;

      for(i=0; i<25; i++) buf[i] = 0;

      if(mess != NULL) while(mess[len])len++; 
      buf[24] = len & 255;
      buf[23] = len >> 8;
      buf[9] = messType & 255;
      buf[8] = messType >> 8;
      strcpy(buf + 25, mess);
      len += 25 - 8;
      buf[7] = len & 255;
      buf[6] = len >> 8;
      buf[3] = header & 255;
      buf[2] = header >> 8;
      buf[13] = user & 255;
      buf[12] = user >> 8;
      buf[15] = 1;
      buf[16] = 1;
      buf[18] = 1;
      buf[22] = 1;

      if(table) { /* direct at specific table */
          buf[14] = buf[21] = 0x27;
          buf[15] = buf[22] = table;
      }

      return len + 8;
}


int read_response(int sockfd, unsigned char *buf, int buflen)
{
        int totresplen, resplen, i;
	char tbuf[TBUF_LEN], *tptr;
        static int readahead=0, backlog=0;

	struct timeval timeout;
	fd_set readfds;

	/*
	 * Parameter check
	 */
	if (sockfd < 0 || buf == NULL || buflen < 1)
		return(-1);

	/*
	 * Setup a timeout for read
	 */
	timeout.tv_sec = 8;
	timeout.tv_usec = 0;

	FD_ZERO(&readfds);
	FD_SET(sockfd, &readfds);

	/*
	 * Initialize an empty response
	 */
        for(i=0; i+backlog < readahead; i++) buf[i] = buf[i+backlog];

	/*
	 * Read the response
	 */
        totresplen = readahead - backlog;
	tbuf[totresplen] = '\0';
        while( (totresplen < 8 || totresplen < buf[7] + 256*buf[6] + 8)
                && select(32, &readfds, NULL, NULL, &timeout) == 1)
        {
              resplen = recv(sockfd, buf+totresplen, (TBUF_LEN-1)-totresplen, 0);
              totresplen += resplen;

		/* Terminate as a string */
                buf[totresplen] = '\0';
              if(resplen == 0) {
//                  fprintf(logfile, "EOF\n");
                  ;
              }
        }
        if(totresplen >= 8) {
            backlog = buf[7] + 256*buf[6] + 8;
            readahead = totresplen;
            if(backlog > totresplen) {
                fprintf(logfile, "Incomplete packet, %d in stead of %d\n", totresplen, backlog);
                backlog = totresplen;
            }
        } else backlog = readahead = totresplen;
        return(backlog);
}

int parseTables(unsigned char *mess, char list[][6])
{
    int i, j, k, m; char *p, c;

    if(mess[9] != 0x6c) return -1;
    k = mess[24]+256*mess[23];
    if(mess[25] != '1' || mess[26] != '|') {
        printf("bad tables: %s\n", mess+25);
        return -1;
    }
    p = mess + 27; j = -1;
    for(i=0; i<12; i++) {
        m = p[4]-'0' + 10*(p[3]-'0');
        list[i][0] = m;
        while(*p && *p++ != '|');
        while(*p && *p++ != '|');
        list[i][1] = *p++;
        while(*p && *p++ != '|');
        list[i][2] = *p++;
        while(*p && *p++ != '|');
        list[i][3] = *p++;
        while(*p && *p++ != '|');
        list[i][4] = *p++;
        while(*p && *p++ != '|');
        list[i][5] = *p++;
        while(*p && *p++ != '|');
    }
    // search empty table
    for(i=11; i>=0; i--)
        if(list[i][1] == '#' && list[i][2] == '#') j = i;
    return j;
}

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.          */

InitGame()
{
 int i,j;
 for(i=0; i<10; i++) {
     board[0][i] = i;    rpos[i   ] = 0; fpos[i   ] = i;
     board[1][i] = i+10; rpos[i+10] = 1; fpos[i+10] = i;
     board[6][i] = i+30; rpos[i+30] = 6; fpos[i+30] = i;
     board[7][i] = i+20; rpos[i+20] = 7; fpos[i+20] = i;
     for(j=2; j<6; j++) board[j][i] = -1;
 }
 Side = WHITE;
}

int main(int argc, char **argv)
{
        const char *p; 
        int Computer, MaxTime=600, MaxMoves, TimeInc=12, sec, i, j, cnt;
        char line[256], command[256], c, cc, *FEN;
        int m, nr, firstGame, lastColor, firstMove, owner, seated = 0;
        FILE *f;

        /* communication declarations */
        int sockfd;
	struct sockaddr_in mailhost_addr;
        int mail_serv_tcp_port = 1088;
        unsigned long inaddr = 0;
	char messagestring[TBUF_LEN];
        int userID, dataLength, stringLength, isComputer=0;
        char tableList[12][6], table;

	struct hostent *hp;

	struct timeval timeout;
	fd_set writefds;

        unsigned char tbuf[TBUF_LEN];
        unsigned char mailhost[TBUF_LEN], response_message[TBUF_LEN];
	char domain[TBUF_LEN], to[TBUF_LEN], from[TBUF_LEN];
	int totresplen = 0, resplen = 0;
	int reponsecode;
        int k;
	
#ifdef WIN32
	WSADATA wsaData;

	/*
	 * Setup Winsock 1.1
	 */
	WSAStartup(0x0101, &wsaData);

	/* Confirm that the WinSock DLL supports 1.1.        */
	/* Note that if the DLL supports versions greater    */
	/* than 1.1 in addition to 1.1, it will still return */
	/* 1.1 in wVersion since that is the version we      */
	/* requested.                                        */ 
	if (LOBYTE(wsaData.wVersion) != 1 ||
        HIBYTE(wsaData.wVersion) != 1 ) {
		/* Couldn't find an acceptable WinSock DLL       */
		WSACleanup();

                fprintf(stderr, "%s: failed to find Winsock version 1.1 or better\n", 
				argv[0]);

		exit(1);
	}

	/* The WinSock DLL is acceptable. Proceed...         */
        fprintf(stderr, "%s (version %s) found Winsock version 1.1 or better\n", 
				argv[0], Version);
#else
        fprintf(stderr, "%s (version %s) using sockets\n", 
				argv[0], Version);
#endif

        if (argc < 3) {
                fprintf(stderr, "Usage: %s username password\n", argv[0]);
		exit(1);
        }
        logfile = fopen("client.log", "w");
	signal(SIGINT, SIG_IGN);

        printf("tellics say     Gothoglot %s\n", Version);
        printf("tellics say     by H.G. Muller\n");

        InitGame();

        Computer = EMPTY;
        MaxTime  = 10000;  /* 10 sec */
        MaxDepth = 30;     /* maximum depth of your search */
        firstGame = 1;

        /* login on Gothic server */
        strncpy(mailhost, "www.gothic-chess.com", sizeof(mailhost));

	/*
	 * Setup basics about server address
	 */
	memset((char *) &mailhost_addr, 0, sizeof(mailhost_addr));
	mailhost_addr.sin_family      = AF_INET;
	mailhost_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	mailhost_addr.sin_port        = htons((u_short) mail_serv_tcp_port);

	/*
	 * Find out and fill in more about server address
	 */
        if ( (inaddr = inet_addr(mailhost)) != INADDR_NONE) {
            /* it's dotted-decimal */
            memcpy((char *) &mailhost_addr.sin_addr, (char *) &inaddr, sizeof(inaddr));
        } else {
            if ( (hp = gethostbyname(mailhost)) == NULL) {
                    fprintf(logfile, "%s: unknown host name: %s\n",
                            argv[0], mailhost);
                    exit(1);
            }
            memcpy((char *) &mailhost_addr.sin_addr, hp->h_addr, hp->h_length);
        }

	/*
	 * Open a TCP socket (an Internet stream socket).
	 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
          fprintf(logfile, "%s: can't open stream socket\n", argv[0]);
	  exit(1);
	}
	
        /*
	 * Connect to the server.
	 */
	if (connect(sockfd, (struct sockaddr *) &mailhost_addr, sizeof(mailhost_addr)) < 0) {
                fprintf(logfile, "%s: can't connect to server\n", argv[0]);
		exit(1);
	}

	/*
	 * Setup a timeout for read and write
	 */
	timeout.tv_sec = 8;
	timeout.tv_usec = 0;


	FD_ZERO(&writefds);		
	FD_SET(sockfd, &writefds);

	/* 
         * Read Gothic server login prompt
	 */
        if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                fprintf(logfile, "%s: failed to read response\n", argv[0]);
		exit(1);
	}
        printpacket("login prompt", response_message, i);
        if(response_message[9] != 0x65) {
                fprintf(logfile, "%s: no login prompt\n", argv[0]);
		exit(1);
        }
        userID = response_message[13] + 256*response_message[12];
        if(userID != response_message[22])
            printf("UserID unclear: %d or %d\n", userID, response_message[22]);
        /*
         * Login with username and password from arguments
         */
        sprintf(messagestring, "%s|%s", argv[1], argv[2]);
        i = prepareMessage(tbuf, 0, 0x66, userID, 0, 0, 0, 0, 0, 0, messagestring);
        printpacket(messagestring, tbuf, i);
	if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                send(sockfd, tbuf, i, 0);
        if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                fprintf(logfile, "%s: failed to read response\n", argv[0]);
		exit(1);
	}
        printpacket("login acknowledgement", response_message, i);
        /*
         * Request list of rooms
         */
        i = prepareMessage(tbuf, 0, 0x69, userID, 0, 0, 0, 0, 0, 0, "");
        printpacket("request rooms", tbuf, i);
	if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                send(sockfd, tbuf, i, 0);
    readrooms:
        if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                fprintf(logfile, "%s: failed to read response\n", argv[0]);
		exit(1);
	}
        printpacket("room list", response_message, i);
        if(response_message[9] != 0x6a) {
            fprintf(logfile, "no room list %d %x\n", i, response_message[9]);
            goto readrooms;
        }
        if(response_message[25] != '1' || response_message[26] != '|') {
                fprintf(logfile, "%s: no Gothic Room\n", argv[0]);
		exit(1);
        }

        /*
         * Print arrival message in chat window
         */
        sprintf(messagestring, "[Gothoglot] New computer logged in as '%s'", argv[1]);
        i = chatMessage(tbuf, 0, 0x12d, userID, 0, messagestring);
        printpacket("chat", tbuf, i);
	if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                send(sockfd, tbuf, i, 0);
/*
        We don't receive this broadcast yet, as we didn't enter the room

        if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                fprintf(logfile, "%s: failed to read response\n", argv[0]);
		exit(1);
	}
        printpacket("chat echo", response_message, i);
        if(response_message[9] != 0x2e) {
            fprintf(logfile, "no chat echo %d %x\n", i, response_message[9]);
        }
*/

        for (;;) {
                fflush(stdout); fflush(logfile);
                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;
                        int piecenr, file, rank, square, color, victimnr; 

                        if(firstGame) {
                            /* first game is dummy that we abort immediately */
                            printf("resign\n");
                            Computer = EMPTY;
                            lastColor = Side; /* remember color we play */
                            continue;
                        }
                        /* wait for move from server, and relay it */

                     readmove:
                        /*
                         * Ping
                         */
                        i = prepareMessage(tbuf, 0, 0x4b1, userID, 0, 0, 0, 0, 0, 0, "");
                        printpacket("ping", tbuf, i);
                        if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                send(sockfd, tbuf, i, 0);

                        if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                exit(1);
                        }
                        printpacket("server move", response_message, i);
                        if(response_message[9] == 0x91 && response_message[8] == 0x01 ||
                           response_message[9] == 0x76 &&
                               response_message[29]-'0' + 10*(response_message[28]-'0') == tableList[table][0] ) {
                            if(response_message[9] == 0x91) { int code = 8;
                                sscanf(response_message+25, "%d", &code); /* read result code */
				if(code >= 20) code = 8;
                                printf("%s { %s }\n", gameResult[code], gameEndMsg[code]);
                            } else
                                printf("1/2-1/2 { opponent left table }\n");
                            sprintf(messagestring, "101%02d|%s|false", tableList[table][0],
                                                    lastColor == WHITE ? "black" : "white");
                            i = chatMessage(tbuf, 0, 0x70, userID, 0, messagestring);
                            printpacket("leave table", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readstandup:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("stand up", response_message, i);
                            if(response_message[9] != 0x76) {
                                fprintf(logfile, "no stand-up %d %x\n", i, response_message[9]);
                                goto readstandup;
                            }
                            /*
                             * Kill board display
                             */
                            i = prepareMessage(tbuf, 0, 0x6e, userID, 0x27, owner, 0, 0, 0, 0, "");
                            printpacket("close window", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                            Computer = EMPTY;
                            seated = 0;
                            continue;
                        }
                        if(response_message[9] == 0xf7) { int dummy;
                            if(sscanf(response_message+25, "%d|%d|%d|%d|%d|%d|%[^|]",
                              &piecenr, &file, &rank, &TimeLeft, &color, &dummy, messagestring) != 7)
                                fprintf(logfile, "bad promotion message\n");
                            goto promodone;
                        }
                        if(response_message[9] == 0xcd) {
                            int rookfile, rookrank, rooknr, dummy;

                            if(sscanf(response_message+25, "%d|%d|%d|%d|%d|nomove|%d|%d|%d|%d|%d|%s",
                              &piecenr, &file, &rank, &TimeLeft, &color,
                              &rooknr, &rookfile, &rookrank, &dummy, &dummy, messagestring) != 11)
                                fprintf(logfile, "bad castling message\n");
                            if(color != (Side==WHITE)) goto readmove;
                            if(messagestring[0] == 'n') goto readmove;
                            /* move the Rook */
                            board[rpos[rooknr]][fpos[rooknr]] = -1;
                            fpos[rooknr] = rookfile; rpos[rooknr] = rookrank;
                            board[rookrank][rookfile] = rooknr;
                            goto promodone;
                        }
                        if(response_message[9] != 0xca) {
                            fprintf(logfile, "no move %d %x\n", i, response_message[9]);
                            goto readmove;
                        }
                        if(sscanf(response_message+25, "%d|%d|%d|%d|%d|%s",
                          &piecenr, &file, &rank, &TimeLeft, &color, messagestring) != 6)
                            fprintf(logfile, "bad move message\n");
                     promodone:
                        if(color != (Side==WHITE)) goto readmove;
                        if(messagestring[0] == 'n') goto readmove;
                        if(messagestring[0] >= 'a' && (rank==2 || rank==5)) {
                            /* work-around for Pawn moves:
                             * print long algebraic if to 3rd/6th rank,
                             * because server gives faulty SAN
                             * on e.p. captures
                             */
                            printf("move %c%c%c%c\n",
                                  fpos[piecenr]+'a', rpos[piecenr]+'1',
                                  file+'a', rank+'1');
                        } else printf("move %s\n", messagestring);
                        board[rpos[piecenr]][fpos[piecenr]] = -1;
                        fpos[piecenr] = file; rpos[piecenr] = rank;
                        victimnr = board[rank][file];
                        board[rank][file] = piecenr;
                        lastColor = Side; /* remember color we play */
                        Side ^= BLACK^WHITE;

                        continue;
		}

		if (!fgets(line, 256, stdin))
			return;
                fprintf(logfile, "received command: %s", line); fflush(logfile);

		if (line[0] == '\n')
			continue;
		sscanf(line, "%s", command);
                if (!strcmp(command, "ping")) {
                   int pingNr, displayUp=0, oppoSit=0;

                   if(sscanf(line, "ping %d", &pingNr) == 1) {
                        /* note that first game is a dummy, to learn color */
                        if(firstGame || seated) {
                            /* first game is dummy that does not connect
                             * (to find out game settings for next game in advance)
                             */
                            printf("pong %d\n", pingNr);
                        } else {
                            /*
                             * Request an overview of tables
                             */
                            messagestring[0] = '1';
                            messagestring[1] = 0;
                            i = prepareMessage(tbuf, 0, 0x6b, userID, 0, 0, 0, 0, 0, 0, messagestring);
                            printpacket("select room", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readtables:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            if(response_message[9] == 0x6d) {
                                printpacket("arrival broadcast", response_message, i);
                                goto readtables;
                            }
                            printpacket("room arrival", response_message, i);
                            if(response_message[9] != 0x6c) {
                                fprintf(logfile, "no tables %d %x\n", i, response_message[9]);
                                goto readtables;
                            }
                            /*
                             * Find an empty table and sit down
                             */
                            table = parseTables(response_message, tableList);
                            sprintf(messagestring, "101%02d|%s|true", tableList[table][0],
                                                    lastColor != WHITE ? "black" : "white");
                            i = chatMessage(tbuf, 0, 0x70, userID, 0, messagestring);
                            printpacket("request table", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readsitdown:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("sit down", response_message, i);
                            if(response_message[9] != 0x71) {
                                fprintf(logfile, "no ready button %d %x\n", i, response_message[9]);
                                goto readsitdown;
                            }
                            /*
                             * Press ready to confirm sitdown
                             */
                            sprintf(messagestring, "101%02d", tableList[table][0]);
                            i = chatMessage(tbuf, 0, 0x73, userID, 0, messagestring);
                            printpacket("ready press", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readthumbup:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("thumbs up", response_message, i);
                            if(response_message[9] != 0x74) {
                                fprintf(logfile, "no thumb up %d %x\n", i, response_message[9]);
                                goto readthumbup;
                            }
                            /*
                             * Wait until an opponent arrives
                             */
                             owner = -1;
                          readopposit:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            if(response_message[9] == 0x59) {
                                printpacket("display up", response_message, i);
                                displayUp = 1;
                            }
                            if(response_message[9] == 0x74) {
                                printpacket("opponent sit", response_message, i);
                                oppoSit = 1;
                            }
                            if(response_message[9] == 0x21) {
                                printpacket("earned table ownership", response_message, i);
                                owner = response_message[15];
                            }
                            if(!oppoSit || !displayUp || owner < 0) {
                                fprintf(logfile, "no ready opponent %d %x\n", i, response_message[9]);
                                i = prepareMessage(tbuf, 0, 0x4b1, userID, 0, 0, 0, 0, 0, 0, "");
                                printpacket("ping", tbuf, i);
                                if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                                goto readopposit;
                            }
                            /*
                             * Set time and wait for it to be acknowledged
                             */
                            sprintf(messagestring, "%d:%d:false", (MaxTime+59)/60, TimeInc);
                            i = prepareMessage(tbuf, 0, 0x385, userID, 0x27, owner, 0, 0, 0, 0, messagestring);
                            printpacket("set time", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readtime:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("time set", response_message, i);
                            if(response_message[9] != 0x86) {
                                fprintf(logfile, "no time set %d %x\n", i, response_message[9]);
                                goto readtime;
                            }
                            /*
                             * Print welcome message
                             */
                            if(isComputer)
                                sprintf(messagestring, "[%s] You will be playing the %s Gothic-Chess program through Gothoglot", argv[1], engine);
                            else
                                sprintf(messagestring, "[%s] You will be playing %s through Gothoglot and WinBoard_F", argv[1], engine);
                            i = chatMessage(tbuf, 0, 0x12d, userID, owner, messagestring);
                            printpacket("table chat", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                            /*
                             * Press start
                             */
                            i = prepareMessage(tbuf, 0, 0x2bd, userID, 0x27, owner, 0, 0, 0, 0, "");
                            printpacket("select room", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                            /*
                             * Wait til game start
                             */
                          readgamestart:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("game start", response_message, i);
                            if(response_message[9] == 0x76 &&
                               response_message[29]-'0' + 10*(response_message[28]-'0') == tableList[table][0] ) {
                                /* user stands up before pressing 'start' */
                                fprintf(logfile, "opponent left!\n");
                                displayUp = 0;
                                goto readopposit;
                            }
                            if(response_message[9] != 0xbe) {
                                fprintf(logfile, "no game start %d %x\n", i, response_message[9]);
                                goto readgamestart;
                            }
                            /*
                             * After all this we are ready to play
                             * and wake up WinBoard by answering the ping
                             */
                            seated = 1;
                            printf("pong %d\n", pingNr);
                        }
                    }
                    continue;
		}
                if (!strcmp(command, "result")) {
                            /* game over according to WinBoard; stand up */
                            if(firstGame || !seated) {
                                firstGame = 0;
                                continue;
                            }

                            sprintf(messagestring, "101%02d|%s|false", tableList[table][0],
                                                    lastColor == WHITE ? "black" : "white");
                            i = chatMessage(tbuf, 0, 0x70, userID, 0, messagestring);
                            printpacket("leave table", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readstandup2:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("stand up 2", response_message, i);
                            if(response_message[9] != 0x76) {
                                fprintf(logfile, "no stand-up %d %x\n", i, response_message[9]);
                                goto readstandup2;
                            }
                            /*
                             * Kill board display
                             */
                            i = prepareMessage(tbuf, 0, 0x6e, userID, 0x27, owner, 0, 0, 0, 0, "");
                            printpacket("select room", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                            Computer = EMPTY;
                            seated = 0;
			continue;
		}
		if (!strcmp(command, "post")) {
			continue;
		}
		if (!strcmp(command, "nopost")) {
			continue;
		}
		if (!strcmp(command, "random")) {
			continue;
		}
		if (!strcmp(command, "?")) {
			continue;
		}
                if (!strcmp(command, "p")) {
			continue;
                }
		if (!strcmp(command, "time")) {
                        /* set time left on clock */
                        int t;
                        sscanf(line, "time %d", &t);
			continue;
		}
		if (!strcmp(command, "otim")) {
                        /*
                         * opponent's time. Note that the opponent of Gothoglot is the
                         * engine that is playing against the server, and that it is
                         * this time we have to send with every move to tell the server
                         * how much time we still have left
                         */
                        int t; 
                        sscanf(line, "otim %d", &t);
                        TimeLeft = t/100; /* centi-sec to sec */
			continue;
		}
                if (!strcmp(command, "name")) { int i;
                        strcpy(engine, line+5);
                        for(i=0; i<80; i++)
                            if(engine[i] == '\n') engine[i] = 0;
			continue;
		}
		if (!strcmp(command, "hard")) {
			continue;
		}
		if (!strcmp(command, "easy")) {
			continue;
		}
                if (!strcmp(command, "xboard"))
			continue;
                if (!strcmp(command, "draw"))
                        /* relay draw offer to server */
			continue;
		if (!strcmp(command, "protover")) {
                        printf("feature variants=\"gothic,fischerandom\"\n");
                        printf("feature myname=\"Gothic Server\"\n");
                        printf("feature name=1\n");
                        printf("feature setboard=0 colors=0 ping=1 done=1\n");
			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 = 60*MaxTime + sec;
                        continue;
                }
		if (!strcmp(command, "rejected")) {
			continue;
		}
		if (!strcmp(command, "accepted")) {
			continue;
		}
		if (!strcmp(command, "computer")) {
                        isComputer = 1;
			continue;
		}

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

                        if(sscanf(line+8, "gothic%c", &c)==1 && c=='\n');
			continue;
		}
		if (!strcmp(command, "go")) {
                        /* set computer to play current side to move */
                        Computer = Side;
			continue;
		}
		if (!strcmp(command, "new")) {
                        /* start new game */
                        InitGame();
                        Computer  = BLACK;
                        fprintf(logfile, "lastColor = %d\n", lastColor);
			continue;
		}
		if (!strcmp(command, "quit")) {
                        /* exit engine */
                            if(firstGame || !seated) {
                                return;
                            }

                            sprintf(messagestring, "101%02d|%s|false", tableList[table][0],
                                                    lastColor == WHITE ? "black" : "white");
                            i = chatMessage(tbuf, 0, 0x70, userID, 0, messagestring);
                            printpacket("leave table", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readstandup5:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("stand up 5", response_message, i);
                            if(response_message[9] != 0x76) {
                                fprintf(logfile, "no stand-up %d %x\n", i, response_message[9]);
                                goto readstandup5;
                            }
                            /*
                             * Kill board display
                             */
                            i = prepareMessage(tbuf, 0, 0x6e, userID, 0x27, owner, 0, 0, 0, 0, "");
                            printpacket("select room", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
			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; int file, rank, piecenr, i, j;

                        if(line[4] <= ' ') line[4] = 0;
                        else { line[5] = 0;
                               if(line[4]>='a') line[4] += 'A'-'a';
                             }
                        if(firstGame || !seated) {
                            /* first game is dummy, prevent move from being sent */
                            Side ^= BLACK^WHITE;
                            continue;
                        }

                        for(i=0; i<8; i++) {
                            for(j=0; j<10; j++)
                                fprintf(logfile, " %02d", board[i][j]);
                            fprintf(logfile, "\n");
                        }
                        piecenr = board[line[1]-'1'][line[0]-'a'];
                        rank = line[3] - '1';
                        file = line[2] - 'a';
                        if(piecenr == 5 || piecenr == 25) {
                            /* king move! Move Rook if castling */
                            if(file > fpos[piecenr]+1) {
                                /* short castling */
                                fpos[piecenr+4] = file-1;
                                board[rank][file-1] = piecenr + 4;
                                board[rank][9] = -1;
                            }
                            if(file < fpos[piecenr]-1) {
                                /* long castling */
                                fpos[piecenr-5] = file+1;
                                board[rank][file+1] = piecenr - 5;
                                board[rank][0] = -1;
                            }
                        }
                        board[rank][file] = piecenr;
                        board[rpos[piecenr]][fpos[piecenr]] = -1;
                        fpos[piecenr] = file; rpos[piecenr] = rank;
                        /*
                         * Ping
                         */
                        i = prepareMessage(tbuf, 0, 0x4b1, userID, 0, 0, 0, 0, 0, 0, "");
                        printpacket("ping", tbuf, i);
                        if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                send(sockfd, tbuf, i, 0);
                        /*
                         * Send move to server
                         */
                        sprintf(messagestring, "%d|%d|%d|%d", piecenr, file, rank, TimeLeft);
                        i = prepareMessage(tbuf, 0, 0xc9, userID, 0x27, owner, 0, 0, 0, 0, messagestring);
                        printpacket("send move", tbuf, i);
                        if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                send(sockfd, tbuf, i, 0);
                      readecho:
                        if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                exit(1);
                        }
                        /*
                         * Test for game end
                         */
                        if(response_message[9] == 0x91 && response_message[8] == 0x01 ||
                           response_message[9] == 0x76 &&
                               response_message[29]-'0' + 10*(response_message[28]-'0') == tableList[table][0] ) {
                            if(response_message[9] == 0x91) { int code = 8;
                                sscanf(response_message+25, "%d", &code); /* read result code */
				if(code >= 20) code = 8;
                                printf("%s { %s }\n", gameResult[code], gameEndMsg[code]);
                            } else
                                printf("1/2-1/2 { opponent left table }\n");
                            sprintf(messagestring, "101%02d|%s|false", tableList[table][0],
                                                    lastColor == WHITE ? "black" : "white");
                            i = chatMessage(tbuf, 0, 0x70, userID, 0, messagestring);
                            printpacket("leave table", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                          readstandup3:
                            if ((i = read_response(sockfd, response_message, sizeof(response_message))) < 0) {
                                    fprintf(logfile, "%s: failed to read response\n", argv[0]);
                                    exit(1);
                            }
                            printpacket("stand up 3", response_message, i);
                            if(response_message[9] != 0x76) {
                                fprintf(logfile, "no stand-up %d %x\n", i, response_message[9]);
                                goto readstandup3;
                            }
                            /*
                             * Kill board display
                             */
                            i = prepareMessage(tbuf, 0, 0x6e, userID, 0x27, owner, 0, 0, 0, 0, "");
                            printpacket("close window", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                            Computer = EMPTY;
                            seated = 0;
                            continue;
                        }
                        if(response_message[9] + 256*response_message[8] == 0x1f5) {
                            /* promotion-choice popup, make choice */
                            int piecenr, rank, file, time, color, code, promo;
                            char movestring[80];

                            printpacket("promotion pop-up", response_message, i);
                            if(sscanf(response_message+25, "%d|%d|%d|%d|%d|%[^|]|%d",
                                      &piecenr, &rank, &file, &time, &color, movestring, &code) != 7) {
                                fprintf(logfile, "Bad promotion popup\n");
                            }
                            /*
                             * Figure out promotion-piece code
                             */
                            switch(line[4]) {
                                case 0:
                                case 'Q':
                                    promo = 16; break;
                                case 'C':
                                    promo = 12; break;
                                case 'A':
                                    promo = 10; break;
                                case 'R':
                                    promo = 17; break;
                                case 'B':
                                    promo = 11; break;
                                case 'N':
                                    promo = 14; break;
                            }
                            if(Side == WHITE) promo += 10;
                            /*
                             * Prepare choice message and send it
                             */
                            sprintf(messagestring, "%d|%d|%d|%d|%d|%s|%d|%d",
                                      piecenr, rank, file, color, promo, movestring, time, code);
                            i = prepareMessage(tbuf, 0, 0x1f6, userID, 0x27, owner, 0, 0, 0, 0, messagestring);
                            printpacket("send choice", tbuf, i);
                            if (select(32, NULL, &writefds, NULL, &timeout) == 1)
                                    send(sockfd, tbuf, i, 0);
                            goto readecho;
                        }
                        printpacket("move echo", response_message, i);
                        if(response_message[9] != 0xca &&
                           response_message[9] != 0xcd &&
                           response_message[9] != 0xf7   ) {
                            fprintf(logfile, "no move echo %d %x\n", i, response_message[9]);
                            goto readecho;
                        }
                        Side ^= BLACK^WHITE;
		}
	}
}

