/* 
   Copyright 2001 Tristan Cazenave
   gtpclient connects a Go program understanding
   the Go Text Protocol to the Internet Go Server
   Version 1.2

   Todo :
   - Replace the telnet call by a socket
   - Test disconnection from the server so as to reconnect automatically
   - Improve saving of files
   - Graphical interface to display games
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>

#define BufSize  1024
#define MaxMoves 1024
#define NbLevels 6
#define MinByoYomi 10
#define MaxStoredGames 1000
#define MaxSizeName 500

// server, name, port
#define NbServers 2
char *Servers[30] =
{ "nngs","nngs.cosmic.org","9696",
  "igs","igs.joyjoy.net","6969" };

// Base path to the log directory. Game records will be saved in a
// subdirectory to this directory named after the server. The log file
// will go there as well.
//char *SavePath = "/home/cazenave/golois/games/";
char *SavePath = "/disks/users/cazenave/golois/games/";
char *ProgramName = "Golois-1.0";
char SgfFileName[256];
int SgfMoveNumber=1;
char ServerGameName[256];

char Home[1024];

typedef struct {
  char color;
  int x,y;
} Move;

Move MoveArray[MaxMoves];

int NbStoredGames=0;
char OpponentNameStoredGame[MaxStoredGames][MaxSizeName];
char GameNameStoredGame[MaxStoredGames][MaxSizeName];
char ColorNameStoredGame[MaxStoredGames][MaxSizeName];
char ServerGameNameStoredGame[MaxStoredGames][MaxSizeName];

// People we don't want to play against.
#define NumberOfBlackListed 1
char *BlackList [NumberOfBlackListed] = {""};

char *Server,*ProgramPath,*MyName,*MyPassword,*ServerName,*ServerPort;
time_t TimeLastSendServer=0;

int PidServer,ServerPipeSend[2],ServerPipeGet[2];
int PidGo,GoPipeSend[2],GoPipeGet[2];
char BufServer[BufSize],BufSend[BufSize];
char BufGetGo[BufSize],BufSendGo[BufSize];
char BufKeyBoard[BufSize];
int nread=0;
int IndiceKeyBoard=0;

char Color[30],OpponentName[30],Useless[30],ChatName[30],*White,*Black;
int BoardSize,InitialTime,ByoYomi;
int Handicap;
float Komi=5.5;
int Level=4;
int TimeLeft=60;
int NumMove,NumGame;
int PrisonnersWhite,SecLeftWhite,MovesByoYomiWhite;
int PrisonnersBlack,SecLeftBlack,MovesByoYomiBlack;
int TimeToMove[NbLevels];

int NbSendGo=0;

void LoadStoredGames();
char *StoredGame(char *Name);
void SendServer(char *s);
void ReadAndProcess ();
void ProcessLine(char *s);
void RemoveStones();
char *Date();
char *LogDate();
void LogEvent(char *s);
void StartSgf();
void AddMoveSgf(char color,char *move);
void EndSgf();
void LoadSgf();

int main (int argc, char **argv) {
  int i;
  
  if (argc<5) {
    fprintf(stderr,"Usage: %s Server ProgramPath Name Password\n",argv[0]);
    /* only send the pwd, not the real directory of the program */
    /*
      getwd(Home);
      fprintf(stderr,"Home: %s\n",Home);
    */
    return 1;
  }
  
  Server = argv[1];
  ProgramPath = argv[2];
  MyName = argv[3];
  MyPassword = argv[4];
  sprintf(SgfFileName,"%s%s.sgf",SavePath,ProgramName);
  sprintf(OpponentName,"toto");

  LoadStoredGames();
  
  for (i=0; i<NbServers; i++)
    if (!strcmp(Server,Servers[3*i])) {
      ServerName=Servers[3*i+1];
      ServerPort=Servers[3*i+2];
    }
  
  pipe(ServerPipeSend);
  pipe(ServerPipeGet);
  PidServer=fork();
  if (PidServer<0) { /* error */
    fprintf(stderr,"error: cannot fork ()\n");
    exit(1);
  }
  else if (PidServer>0) { /* father returns pid of son */
    pipe(GoPipeSend);
    pipe(GoPipeGet);
    PidGo=fork();
    if (PidGo<0) { /* error */
      fprintf(stderr,"error: cannot fork ()\n");
      exit(1);
    }
    else if (PidGo>0) { /* father returns pid of son */
      ReadAndProcess();
    }
    else { /* second son : launches the Go program to read gtp commands */
      dup2(GoPipeSend[0],0);
      dup2(GoPipeGet[1],1);
      close(GoPipeSend[0]);
      close(GoPipeGet[1]);
      execlp(ProgramPath,ProgramPath,"-gtp","--quiet","-NoPattern",NULL);
    }
  }
  else { /* first son : telnet to server */
    dup2(ServerPipeSend[0],0);
    dup2(ServerPipeGet[1],1);
    close(ServerPipeSend[0]);
    close(ServerPipeGet[1]);
    execlp("telnet","telnet",ServerName,ServerPort,NULL);
  }
}

/* reads the keyboard if something has been typed */

void ReadKeyBoard() {
  char c;
  do {
    //fprintf(stderr,"reading keyboard...");
    nread=read(0,&c,1);
    if (nread>0) {
      //fprintf(stderr," %c",c);
      BufKeyBoard[IndiceKeyBoard]=c;
      IndiceKeyBoard++;
    }
  } while (nread>0);
  if (IndiceKeyBoard>0)
    if (BufKeyBoard[IndiceKeyBoard-1]=='\n') {
      /* one line has been typed and memorized */
      BufKeyBoard[IndiceKeyBoard-1]='\0';
      SendServer(BufKeyBoard);
      if (!strncmp(BufKeyBoard,"resign",6))
	EndSgf();
      IndiceKeyBoard=0;
    }
}

void ReadServer() {
  int i=0;
  char c='\0';
  
  while ( (c!='\n') && (i<(BufSize-1)) && ((nread>0)||(i==0)) ) {
    nread=read(ServerPipeGet[0],&c,1);
    if (nread>0) {
      BufServer[i]=c;
      i++;
    }
  }
  BufServer[i]='\0';
  if (strncmp(BufServer,"21 ",3) && strncmp(BufServer,"1 ",2) && (strlen(BufServer)>1)) {
    fprintf(stderr,"Server %s",BufServer);
    fflush(stderr);
  }
}

void SendServer(char *s) {
  /* delay the sent instructions otherwise, they can be lost */
  while (time(NULL)-TimeLastSendServer<1)
    ;
  if (strncmp(s,"ayt",3))
    fprintf(stderr,"SendServer %s\n",s);
  write(ServerPipeSend[1],s,strlen(s));
  write(ServerPipeSend[1],"\n",1);
  TimeLastSendServer=time(NULL);
}

void ReadGo() {
  int i=0;
  char c='\0';
  
  while ( (c!='\n') && (i<(BufSize-1))) {
    nread=read(GoPipeGet[0],&c,1);
    if (nread>0) {
      BufGetGo[i]=c;
      i++;
    }
  }
  BufGetGo[i]='\0';
  fprintf(stderr,"ReadGo %s",BufGetGo);
}

void SendGo(char *s) {
  sprintf(BufSendGo,"%d %s",NbSendGo,s);
  fprintf(stderr,"SendGo %s\n",BufSendGo);
  write(GoPipeSend[1],BufSendGo,strlen(BufSendGo));
  write(GoPipeSend[1],"\n",1);
  NbSendGo++;
}

void ReadAndProcess () {
  int state;
  /* Initialisations */
  SendGo("boardsize 19");
  SendGo("komi 5.5");
  /* logging to the server*/
  SendServer(MyName);
  /* non blocking reading of keyboard */
  state=fcntl(STDIN_FILENO,F_GETFL,0);
  fcntl(STDIN_FILENO,F_SETFL,state^O_NONBLOCK);
  while (1) {
    /* reads a line from server */
    ReadServer();
    ProcessLine(BufServer);
    if (time(NULL)-TimeLastSendServer>30) {
      SendServer("ayt");
    }
    ReadKeyBoard();
  }
}

void GetHandicapAndKomi(char *s) {
  int i, len=strlen(s);

  for (i=0; i<len-strlen("handicap"); i++)
    if (!strncmp(s+i,"handicap",8))
      sscanf(s+i,"handicap %d",&Handicap);
  for (i=0; i<len-strlen("komi"); i++)
    if (!strncmp(s+i,"komi",4))
      sscanf(s+i,"komi %f",&Komi);
}

void LowerCase(char *s) {
  int i, len=strlen(s);
  
  for (i=0; i<len; i++)
    if ((s[i]>'A') && (s[i]<'Z'))
      s[i]=s[i]-'A'+'a';
}

int CopyUntil(char *target,char *source,char until) {
  int i=0, len=strlen(source);
  
  while ((i<len) && (source[i]!=until)) {
    target[i]=source[i];
    i++;
  }
  target[i]='\0';
  return i;
}

int Contains(char *s,char *pattern) {
  int i, len=strlen(s);

  fprintf (stderr,"s=%s",s);
  fflush(stderr);
  fprintf (stderr,"%d",len);
  fflush(stderr);
  fprintf (stderr,"pat=%s,%d\n",pattern,strlen(pattern));
  fflush(stderr);
  if (len-strlen(pattern)>0)
    for (i=0; i<len-strlen(pattern); i++)
      if (!strncmp(s+i,pattern,strlen(pattern)))
	return 1;
  return 0;
}

void LoadStoredGames() {
  FILE *StoredGameFile;
  char s[1000];
  int i;

  sprintf(s,"%sStoredGames%s.txt",SavePath,Server);
  StoredGameFile=fopen(s,"r");
  if (StoredGameFile!=NULL) {
    fscanf(StoredGameFile,"%d",&NbStoredGames);
    for (i=0; i<NbStoredGames; i++) {
      fscanf(StoredGameFile,"%s",OpponentNameStoredGame[i]);
      fscanf(StoredGameFile,"%s",GameNameStoredGame[i]);
      fscanf(StoredGameFile,"%s",ColorNameStoredGame[i]);
      fscanf(StoredGameFile,"%s",ServerGameNameStoredGame[i]);
    }
    fclose(StoredGameFile);
  }
}

void StoreStoredGame() {
  FILE *StoredGameFile;
  char s[1000];
  int i;

  sprintf(s,"%sStoredGames%s.txt",SavePath,Server);
  StoredGameFile=fopen(s,"w");
  if (StoredGameFile!=NULL) {
    fprintf(StoredGameFile,"%d\n",NbStoredGames);
    for (i=0; i<NbStoredGames; i++) {
      fprintf(StoredGameFile,"%s %s %s %s\n",OpponentNameStoredGame[i],GameNameStoredGame[i],\
	      ColorNameStoredGame[i],ServerGameNameStoredGame[i]);
    }
    fclose(StoredGameFile);
  }
} 
 
void RemoveStoredGame() {
  FILE *StoredGameFile;
  char s[1000];
  int i,j;
  
  EndSgf();
  if ((strlen(OpponentName)<MaxSizeName) && (strlen(SgfFileName)<MaxSizeName)) {
    if (StoredGame(OpponentName)!=NULL) {
      j=0;
      for (i=0; i+j<NbStoredGames; i++) {
	if (!strcmp(OpponentName,OpponentNameStoredGame[i]))
	  j++;
	else if (j>0) {
	  sprintf(OpponentNameStoredGame[i],OpponentNameStoredGame[i+j]);
	  sprintf(GameNameStoredGame[i],GameNameStoredGame[i+j]);
	  sprintf(ColorNameStoredGame[i],ColorNameStoredGame[i+j]);
	  sprintf(ServerGameNameStoredGame[i],ServerGameNameStoredGame[i+j]);
	}
      }
      NbStoredGames-=j;
    }
  }
  StoreStoredGame();
}

void StoreGame() {
  EndSgf();
  if ((strlen(OpponentName)<MaxSizeName) && (strlen(SgfFileName)<MaxSizeName)) {
    if (StoredGame(OpponentName)==NULL) {
      sprintf(OpponentNameStoredGame[NbStoredGames],OpponentName);
      sprintf(GameNameStoredGame[NbStoredGames],SgfFileName);
      sprintf(ColorNameStoredGame[NbStoredGames],Color);
      sprintf(ServerGameNameStoredGame[NbStoredGames],ServerGameName);
      NbStoredGames++;
    }
    StoreStoredGame();
  }
}

int LoadGame(char *Name) {
  char GtpCommand[1000];
  int Number=0,nscanned=0;

  strcpy(OpponentName,Name);
  if (StoredGame(OpponentName)!=NULL) {
    sprintf(SgfFileName,StoredGame(OpponentName));
    LoadSgf();
    sprintf(GtpCommand,"loadsgf %s\n",SgfFileName);
    SendGo(GtpCommand);
    /* we have to wait the answer otherwise bad things can happen such as :
       SendGo 276 loadsgf toto.sgf
       Server 15 159(W): J19 J18
       SendGo 277 white J19
       gtp: 276 loadsgf toto.sgf
       gtp: 277 white J19
       SendGo 278 genmove_black
       ReadGo =276 white
       ReadGo ?277 illegal move
    */
/*     do { */
/*       ReadGo(); */
/*       nscanned=sscanf(BufGetGo,"=%d",&Number); */
/*     } while ((Number<NbSendGo-1) || (nscanned<1)); */
    //sleep(5);
    /* we succeed in loading the game */
    return 1;
  }
  /* we failed to load the game */
  return 0;
}

char *StoredGame(char *Name) {
  int i;

  for (i=0; i<NbStoredGames; i++)
    if (!strcmp(Name,OpponentNameStoredGame[i]))
      return GameNameStoredGame[i];
  return NULL;
}

char *ServerStoredGame(char *Name) {
  int i;

  for (i=0; i<NbStoredGames; i++)
    if (!strcmp(Name,OpponentNameStoredGame[i]))
      return ServerGameNameStoredGame[i];
  return NULL;
}

char *ColorStoredGame(char *Name) {
  int i;

  for (i=0; i<NbStoredGames; i++)
    if (!strcmp(Name,OpponentNameStoredGame[i]))
      return ColorNameStoredGame[i];
  return NULL;
}

int BlackListed(char *Name) {
  int i;

  for (i=0; i<NumberOfBlackListed; i++)
    if (!strcmp(Name,BlackList[i]))
      return 1;
  return 0;
}

void PlayMove(char color, char *move) {
  char s[BufSize];
  if (color == 'W')
    sprintf(s,"white %s",move);
  else
    sprintf(s,"black %s",move);
  SendGo(s);
  AddMoveSgf(color,move);
}

void GenerateAndPlayMove() {
  int Number=-1,nscanned=0,OldLevel=Level;
  char move[256];
  time_t TimeBefore,TimeAfter;  

  if (Color[0]=='B') {
    /* we are black */
    if (MovesByoYomiBlack!=-1) {
      /* We are in ByoYomi */
      if ((TimeLeft<120)||(((TimeLeft-30)/MovesByoYomiBlack)<TimeToMove[4]))
	Level=1;
      else
	Level=4;
    }
    else if (ByoYomi==0) {
      /* We are in the InitialTime but no ByoYomi */
      if (TimeLeft<300)
	Level=1;
      else
	Level=4;
    }
    else
      /* we are in InitialTime and there is a ByoYomi */
      Level=4;
  }
  else {
    /* we are white */
    if (MovesByoYomiWhite!=-1) {
      /* We are in ByoYomi */
      if ((TimeLeft<120)||(((TimeLeft-30)/MovesByoYomiWhite)<TimeToMove[4]))
	Level=1;
      else
	Level=4;
    }
    else if (ByoYomi==0) {
      /* We are in the InitialTime but no ByoYomi */
      if (TimeLeft<300)
	Level=1;
      else
	Level=4;
    }
    else
      /* we are in InitialTime and there is a ByoYomi */
      Level=4;
  }
  TimeBefore=time(NULL);
  if (OldLevel!=Level) {
    sprintf(BufSend,"level %d",Level);
    SendGo(BufSend);
  }
  if (Color[0]=='B')
    SendGo("genmove_black");
  else
    SendGo("genmove_white");
  do {
    ReadGo();
    nscanned=sscanf(BufGetGo,"=%d %s",&Number,move);
  } while ((Number<NbSendGo-1) || (nscanned<2));
  TimeToMove[Level]=time(NULL)-TimeBefore;
  fprintf(stderr,"TimeToMove[%d] = %d\n",Level,TimeToMove[Level]);
  SendServer(move);
  AddMoveSgf(Color[0],move);
}

void Toggle(char *s) {
  char buf[BufSize];
  
  sprintf(buf,"toggle %s",s);
  SendServer(buf);
  do {
    ReadServer();
  } while (strncmp(BufServer,"9 Set",5));
}

void Tell(char *Name,char *s) {
  char buf[BufSize];
  
  sprintf(buf,"tell %s %s",Name,s);
  SendServer(buf);
  do {
    ReadServer();
  } while (strncmp(BufServer,"9 Set",5));
}

void ProcessLine(char *s) {
  char color,move[256],NameWhite[100],NameBlack[100];
  int handi,Start;
  char *ps;

  if (sscanf(s, "15 %3d(%c): Handicap %d", &NumMove, &color, &handi) >= 3) {
    Handicap=handi;
    Komi=0.5;
    sprintf(BufSend,"komi %1.1f",Komi);
    SendGo(BufSend);
    sprintf(BufSend,"fixed_handicap %d",Handicap);
    SendGo(BufSend);
    /* if golois is white, he plays first */
    if (Color[0]=='W') {
      GenerateAndPlayMove();
    }
  }
  else if (sscanf(s, "15 %3d(%c): %s", &NumMove, &color, move) >= 1) {
    if (color!=Color[0]) {
      PlayMove(color,move);
      GenerateAndPlayMove();
    }
  }
  else if (sscanf(s, "15 Game %3d I: %s (%d %d %d) vs %s (%d %d %d)", &NumGame, NameWhite, &PrisonnersWhite, &SecLeftWhite, &MovesByoYomiWhite, NameBlack, &PrisonnersBlack, &SecLeftBlack, &MovesByoYomiBlack) >= 9) {
    if (Color[0]=='B') {
      TimeLeft=SecLeftBlack;
    }
    else {
      TimeLeft=SecLeftWhite;
    }
    fprintf(stderr,"TimeLeft=%d\n",TimeLeft);
  }
 
  else if (sscanf(s, "9 Use <match %s %s %d %d %d> or <decline %s> to respond.", OpponentName, Color, &BoardSize, &InitialTime, &ByoYomi, Useless) >= 5) {
    if ((BoardSize>19)||(BoardSize<9)) {
      Tell(OpponentName," sorry, only 9 to 19 boards");
      sprintf(BufSend,"decline %s",OpponentName);
      SendServer(BufSend);
    }	
    else if (StoredGame(OpponentName)!=NULL) {
      Tell(OpponentName," let's resume our stored game instead.");
      sprintf(BufSend,"tell %s type load %s to restore the game.",OpponentName,\
	      ServerStoredGame(OpponentName));
      SendServer(BufSend);
      sprintf(BufSend,"decline %s",OpponentName);
      SendServer(BufSend);
      /* let the opponent load otherwise pb with the opponent name */
      //sprintf(BufSend,"load %s",ServerStoredGame(OpponentName));
      //SendServer(BufSend);
      sprintf(Color,ColorStoredGame(OpponentName));
    }
    else if (BlackListed(OpponentName)) {
      sprintf(BufSend,"decline %s",OpponentName);
      SendServer(BufSend);
    }
    else {
      if ((InitialTime<30) && (ByoYomi<MinByoYomi)) {
	if (BoardSize==19) {
	  sprintf(BufSend,"tell %s at least 30/0 or 1/%d",OpponentName,MinByoYomi);
	  SendServer(BufSend);
	  ByoYomi=MinByoYomi;
	}
	else if (InitialTime+ByoYomi<4) {
	  sprintf(BufSend,"tell %s at least 1/%d",OpponentName,3);
	  SendServer(BufSend);
	  ByoYomi=3;
	}
	/* sleep before sending match, so that it is not missed by server */
	sleep(1);
	sprintf(BufSend,"match %s %s %d %d %d",OpponentName,Color,BoardSize,InitialTime,ByoYomi);
      }
      else {
	/* sleep before sending match, so that it is not missed by server */
	sleep(1);
	sprintf(BufSend,"match %s %s %d %d %d",OpponentName,Color,BoardSize,InitialTime,ByoYomi);
      }
      Handicap=0;
      Komi=5.5;
      SendServer(BufSend);
    }
  }
  else if (!strncmp(s, "9 Creating match", 16)) {
    StartSgf();
    sprintf(BufSend,"boardsize %d",BoardSize);
    SendGo(BufSend);
    sprintf(BufSend,"komi %1.1f",Komi);
    SendGo(BufSend);
    Level=4;
/*     if (InitialTime<50) { */
/*       if (ByoYomi<10) */
/* 	Level=3; */
/*       if (ByoYomi<5) */
/* 	Level=1; */
/*     } */
    sprintf(BufSend,"level %d",Level);
    SendGo(BufSend);
    if ((Color[0]=='B')&&(Handicap<2)) {
      // format of ServerGameName : WhiteName-BlackName
      sprintf(ServerGameName,"%s-%s",OpponentName,MyName);
      GenerateAndPlayMove();
    }
    else {
      sprintf(ServerGameName,"%s-%s",MyName,OpponentName);
    }
  }
  else if (!strncmp(s, "9 Board is restored to what it was when you started scoring", 26)) {
    //sprintf(BufSend,"Please, can you also remove my dead stones, thank you.");
    SendServer(BufSend);
    RemoveStones();
    sprintf(BufSend,"done");
    SendServer(BufSend);
    EndSgf();
  }
  else if (!strncmp(s, "9 You can check your score", 26)) {
    sprintf(BufSend,"Please, can you also remove my dead stones, thank you.");
    SendServer(BufSend);
    RemoveStones();
    sprintf(BufSend,"done");
    SendServer(BufSend);
    EndSgf();
  }
  else if (!strncmp(s, "9 Game has been adjourned", 25)) {
    /* add the game to stored games */
    StoreGame(OpponentName);
  }
  else if (!strncmp(s, "9 Removed game", 14)) {
    RemoveStoredGame();
  }
  /* other commands starting with 9,sscanf stops interpreting to the last % command */
  else if (sscanf(s, "9 %s", Useless)) {
    //fprintf(stderr,"Debug %s\n",s+strlen(Useless)+2);
    if (!strncmp(s+strlen(Useless)+2," has typed done.",16)) {
      sprintf(BufSend,"done");
      SendServer(BufSend);
      sprintf(BufSend,"tell %s thank you for the game.",OpponentName);
      SendServer(BufSend);
      RemoveStoredGame();
    }
    else if (!strncmp(s+strlen(Useless)+2," has resigned the game",20) ||
	     !strncmp(s+strlen(Useless)+2," has run out of time",20)) {
      sprintf(BufSend,"tell %s thank you for the game.",OpponentName);
      SendServer(BufSend);
      RemoveStoredGame();
    }
    else if (!strncmp(s+strlen(Useless)+2," has restarted your game",20)) {
      if (strcmp(Useless,MyName))
	strcpy (OpponentName,Useless);

      if (LoadGame(OpponentName)) {
	sprintf(Color,ColorStoredGame(OpponentName));
	sprintf(ServerGameName,ServerStoredGame(OpponentName));
      }
      /* if we cannot load the game, resign */
      else {
	SendServer("resign");
      }
    }
  }
  else if (!strncmp(s, "24 ",3)) {
    GetHandicapAndKomi(s);
    /* people talk to me, let's answer as if I were human, and let's pass the Turing test :) */
/*     if (!strncmp(s, "24 *", 4)) { */
/*       Start=CopyUntil(ChatName,s+4,'*'); */
/*       fprintf(stderr,"Chat with %s\n",ChatName); */
/*       fflush(stderr); */
/*       ps=s+Start+6; */
/*       LowerCase(ps); */
/*       fprintf(stderr,"ps= %s\n",ps); */
/*       fflush(stderr); */
/*       if (Contains(ps,"hi") || Contains(ps,"hello") || Contains(ps,"bonjour") || Contains(ps,"salut")) */
/* 	Tell(ChatName,"hi!"); */
/*       else  */
/* 	Tell(ChatName,"Sorry. My natural language abilities are somewhat limited."); */
/*     } */
  }
  else if (!strncmp(s,"Login:",6)) {
    fprintf(stderr,"Asked for password\n");
    fflush(stderr);
    SendServer(MyPassword);
    sleep(1);
    Toggle("client on");
    Toggle("bell off");
    Toggle("automail off");
    Toggle("open on");
    Toggle("looking on");
  }
}

void RemoveStones() {
  int i,j,Number=-1;
  char DeadStone[10];

  SendGo("final_status_list dead");
  do {
    ReadGo();
    sscanf(BufGetGo,"=%d ",&Number);
  } while (Number<NbSendGo-1);

  fprintf(stderr,"Go Program dead stones : %s\n",BufGetGo);
  for (i=0; ((BufGetGo[i]!='\0') && (BufGetGo[i]!='\n')); i++)
    if (BufGetGo[i]==' ')
      if ( ((BufGetGo[i+1]>='a') && (BufGetGo[i+1]<='z')) ||
           ((BufGetGo[i+1]>='A') && (BufGetGo[i+1]<='Z')) ) {
	for (j=0; ((BufGetGo[i+j+1]!=' ') && 
		   (BufGetGo[i+j+1]!='\0') && 
		   (BufGetGo[i+j+1]!='\n')) ; j++)
	  DeadStone[j]=BufGetGo[i+j+1];
	DeadStone[j]='\0';
	SendServer(DeadStone);
	//sleep(1);
      }
}

char *Date() {
  time_t seconds=time(NULL);
  struct tm *t = localtime(&seconds);
  static char s[100];
  
  sprintf(s,"%d%02d%02d%02d%02d",1900+t->tm_year,1+t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min);
  return s;
}

char *LogDate() {
  time_t seconds=time(NULL);
  struct tm *t = localtime(&seconds);
  static char s[100];
  
  sprintf(s,"%d%2d%2d %2d:%2d:",1900+t->tm_year,1+t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
  return s;
}

void LogEvent(char *s) {
  char LogFileName[100];
  FILE *LogFile;
  
  sprintf(LogFileName,"%s%s.log",SavePath,Server);
  LogFile=fopen(LogFileName,"a");
  if (LogFile!=NULL) {
    fprintf(LogFile,"%s\n",s);
  }
}

void StartSgf() {
  SgfMoveNumber=0;
  if (Color[0] == 'B') {
    Black=ProgramName;
    White=OpponentName;
  }
  else {
    Black=OpponentName;
    White=ProgramName;
  }
  sprintf(SgfFileName,"%s%s-%s-%s.sgf",SavePath,White,Black,Date());
}

void AddMoveSgf(char color,char *move) {
  char c;
  int i,x,y;

  if ((!strncmp(move,"PASS",4))||(!strncmp(move,"Pass",4))||(!strncmp(move,"pass",4))) {
    x=19; y=19;
  }
  else {
    sscanf(move,"%c%d",&c,&y);
    if ((c>='A')&&(c<='Z'))
      c+='a'-'A';
    if (c>'i')
      c--;
    x=c-'a';
    y=BoardSize-y;
  }
  MoveArray[SgfMoveNumber].color=color;
  MoveArray[SgfMoveNumber].x=x;
  MoveArray[SgfMoveNumber].y=y;
  SgfMoveNumber++;
  /* add the game to stored games */
  StoreGame(OpponentName);
}

void EndSgf() {
  int i;
  FILE *SgfFile;

  SgfFile=fopen(SgfFileName,"w");
  if (SgfFile!=NULL) {
    fprintf(SgfFile,"(;GM[1]FF[3]\nPW[%s]\nPB[%s]\nSZ[%d]\nKM[%1.1f]\nHA[%d]\nBY[%d]\n",White,Black,BoardSize,Komi,Handicap,ByoYomi);
    for (i=0; i<SgfMoveNumber; i++) {
      fprintf(SgfFile,";%c[%c%c]",MoveArray[i].color,'a'+MoveArray[i].x,'a'+MoveArray[i].y);
      if ((i+1)%10==0)
	fprintf(SgfFile,"\n");
    }
    fprintf(SgfFile,")\n");
    fclose(SgfFile);
  }
}

int ReadSgfBracketed (FILE *SgfFile,char *InsideBracket) {
  int i;
  char c; 
  
  if (fscanf (SgfFile,"%c",&c)==-1) return 0;
  i=0;
  while (c!=']') {
    InsideBracket[i]=c;
    i++;
    if (fscanf (SgfFile,"%c",&c)==-1) return 0;
  }
  InsideBracket[i]='\0';
  return 1;
}

int ReadSgfCommand (FILE *SgfFile,char *Command,char *InsideBracket) {
  int i;
  char c; 
  
  Command[0]='\0';
  if (fscanf (SgfFile,"%c",&c)==-1) return 0;
  // fin du probleme
  if (c==')') { Command[0]=')'; return 0; }
  i=0;
  while ((c!='[')&&(c!=')')) {
    if ( ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z')) ) {
      Command[i]=c;
      i++;
    }
    if (fscanf (SgfFile,"%c",&c)==-1) return 0;
  }
  Command[i]='\0';
  if (c==')') return 0;
  return ReadSgfBracketed(SgfFile,InsideBracket);
}

void LoadSgf() {
  int i;
  FILE *SgfFile;
  char Command[100],InsideBracket[1000];
  char x,y;

  SgfMoveNumber=0;
  SgfFile=fopen(SgfFileName,"r");
  if (SgfFile!=NULL) {
    while (ReadSgfCommand(SgfFile,Command,InsideBracket)) {
      if (!strncmp(Command,"SZ",2)) {
	if (sscanf(InsideBracket,"%d",&BoardSize)!=1)
	  BoardSize=19;
      }
      else if (!strncmp(Command,"W",1)) {
	if (sscanf(InsideBracket,"%c%c",&x,&y)==2) {
	  MoveArray[SgfMoveNumber].color='W';
	  MoveArray[SgfMoveNumber].x=x-'a';
	  MoveArray[SgfMoveNumber].y=y-'a';
	  SgfMoveNumber++;
	}
      }
      else if (!strncmp(Command,"B",1)) {
	if (sscanf(InsideBracket,"%c%c",&x,&y)==2) {
	  MoveArray[SgfMoveNumber].color='B';
	  MoveArray[SgfMoveNumber].x=x-'a';
	  MoveArray[SgfMoveNumber].y=y-'a';
	  SgfMoveNumber++;
	}
      }
    }
    fclose(SgfFile);
  }
}

