/*
 * Copyright 1996 by Michel D. Grimminck
 *
 * All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <stdio.h>
#include "var.h"
#include "const.h"
#include "functions.h"
#include <string.h>
#include <time.h>

void compress_board(unsigned char *a,BTYPE *b)
/* stores the board b in a compressed format: a=compress(b) */
{
    int i;

    for(i=0;i<25;i++) a[i]=16*b[map[2*i]]+b[map[2*i+1]];
}

void strong_compress_board(unsigned char *a,BTYPE *b)
/* stores the board b in a compressed format: a=compress(b) , 17 bytes */
{
    int i;
    
    for(i=0;i<16;i++) a[i]=b[map[3*i]]+6*b[map[3*i+1]]+36*b[map[3*i+2]];
    a[16]=b[map[3*16]]+6*b[map[3*16+1]];
}

void decompress_board(BTYPE *a,unsigned char *b)
/* decompresses the board b: a=decompress(b) */
{
    int i;

    for(i=0;i<25;i++) {
        a[map[2*i]]=b[i]/16;
        a[map[2*i+1]]=b[i]&15;
        }
}

void set_col(int fg,int bg)
{
  if (usecol==true) printf("\033[%i;%im",fg,bg);
}

void res_col(void) 
{
  if (usecol==true) printf("\033[m");
}

FILE * my_fopen(char *name,char *m)
{
  FILE *test;
  char file[256];

  test=fopen(name,m);
  if (test==NULL) {
    strcpy(file,"/usr/local/lib/dragon/");
    strcat(file,name);
    test=fopen(file,m);
  }
  return(test);
}

int bin_load(char *name,int size,void *array)
{
  FILE *in;
  int n;
  
  in=my_fopen(name,"rb");
  if (in==NULL) {
    printf("binload failed to open '%s'\n",name);
    return(false);
  }
  fscanf(in,"%i\n",&n);
  fread(array,size,n,in);
  return(true);
}
  

int bin_save(char *name,int n,int size,void *array)
{
  FILE *out;

  printf("%s %i %i %x\n",name,n,size,array);
  out=fopen(name,"wb");

  if (out==NULL) {
    printf("binsave fail to opened '%s'\n",name);
    return(0);
  }
  fprintf(out,"%i\n",n);
  fwrite(array,size,n,out);
  fclose(out);
  return(n);
}

void store_history(char *move,int eval,int nodes)
{
    if (game_history_nr>=200) return;
    compress_board(game_history[game_history_nr].board,board);
    movecopy(game_history[game_history_nr].move,move);
    sprintf(game_history[game_history_nr].comment,"score:%.3f,  eval: %i",(float)(eval/1000.0F),nodes);
    game_history_nr++;
}

void xstore_history(char *move,char *comment)
{
    if (game_history_nr>=200) return;
    compress_board(game_history[game_history_nr].board,board);
    movecopy(game_history[game_history_nr].move,move);
    strncpy(game_history[game_history_nr].comment,comment,MAXCOMMENT);
    game_history_nr++;
}

void take_back(int b)
{
    if (game_history_nr==0) {
        printf("can not take back\n");
        return;
    }
    game_history_nr-=b;
    if (game_history_nr<0) {
      printf("can not take back more\n");
      game_history_nr=0;
    }
    decompress_board(board,game_history[game_history_nr].board);
    display_board();
    printf("proposed move: ");
    print_move(game_history[game_history_nr].move);
    printf(" '%s'\n",game_history[game_history_nr].comment);
    fflush(stdout);
}

void show_history(int verbose)
{
    int i;

    for(i=0;i<game_history_nr;i++) {
        if (i%2==0) printf("%i. ",i/2+1);
        print_move(game_history[i].move);
        if (verbose>1) printf(" {%s}",game_history[i].comment);
        if (i%2==0) {
            if (verbose>0) printf("    "); else printf(" ");
        }
        else {
            if (verbose>0 || (verbose==0 && i%8==7)) printf("\n"); else printf(" ");
        }
    }
    printf("\n");
}

int xparse(char a,char b)
{
  int x,y;
  x=a-'a';
  y=b-'1';
  if (x<0 || x>9 || y<0 || y>9) return(-1);
  if ((x+y)%2==1) return(-1);
  return(lmap[x][9-y]+1);
}

float alloc_time(int color)
{
  int ml;
  float at;
  
  ml=(timecontrolply-game_history_nr)/2;
  if (ml<1) ml=1;
  at=(time_left[color]-time_reserve)/ml-operator_time;
  if (at>.4*time_left[color]) at=.5*time_left[color];
  if (at<.5) at=.5;
  printf("%i\n",ml);
  return(at);
}

int try_move(int c,int in1,int in2)
/* returns true on error */
{
    int nmoves,nr,found=0;
    char move[MOVEL];

    if (in1>50 || in2>50 || in1<1 || in2<1) {
        printf("Illegal move.\n");
	return(true);
    }
    in1=map[in1-1]; in2=map[in2-1];
    {
         nmoves=move_list(0,c);
         for(nr=0;nr<nmoves;nr++) {
             if (in1==movelist[0][nr][1] && in2==movelist[0][nr][movelist[0][nr][0]-1]) {
                 found++;
                 movecopy(move,movelist[0][nr]);
             }
         }
    }
    if (found==0) {printf("Illegal move.\n"); return(true);}
    else if (found>1) {printf("Ambigious move.\n"); return(true);}
    else {
         xstore_history(move,"user");
         do_move(move);
    }
    return(false);
}

void write_pdn(char *file)
{
    FILE *out;
    int i;

    out=fopen(file,"w");
    if (out==NULL) {
        printf("pdn_write: not possible to save '%s'\n",file);
        return;
    }
    fprintf(out,"[White \"%s\"]\n",pdn_info.whitepl);
    fprintf(out,"[Black \"%s\"]\n",pdn_info.blackpl);
    fprintf(out,"[Result \"%s\"]\n",pdn_info.result);
    fprintf(out,"[GameType \"20\"]");
    fprintf(out,"\n\n");

    for(i=0;i<game_history_nr;i++) {
        if (i%2==0) fprintf(out,"%i. ",i/2+1);
        fprint_move_pdn(out,game_history[i].move);
	if (game_history[i].comment[0]!=0) fprintf(out," {%s}",game_history[i].comment);
        if (i%8==7) fprintf(out,"\n"); else fprintf(out," ");

    }
    fprintf(out,"%s\n",pdn_info.result);
    fclose(out);
}

void beep(void)
{
  printf("\007");
}

void reverse_board(BTYPE *out,BTYPE *in)
{
    int i,q1,q2;

    for(i=0;i<93;i++) out[i]=invalid;
    for(i=0;i<50;i++) {
        q1=map[i];
        q2=map[49-i];
        out[q1]=in[q2]^1;
        if (out[q1]==1) out[q1]=0;
        }
}

void copy_board(BTYPE *out,BTYPE *in)
{
    int i;

    for(i=0;i<93;i++) out[i]=in[i];
}

void init_board(void)
{
    int i;

    for(i=0;i<93;i++) board[i]=invalid;
    for(i=0;i<20;i++) board[map[i]]=black | man;
    for(i=20;i<30;i++) board[map[i]]=empty;
    for(i=30;i<50;i++) board[map[i]]=white | man;
    board[invalid]=invalid;
    game_history_nr=0;
}

void save_board(int color,char *name)
{
  int p,x,y;
  FILE *out;
  out=fopen(name,"w");
  if (out==NULL) {printf("error opening file\n"); return;}
  fprintf(out,"# DCP 1.00\n# game: draughts\n# format: board\n# author: unset\n");
  p=0;
  for(y=0;y<10;y++) {
        for(x=0;x<10;x++) {
            if ((x+y)%2==0) fprintf(out,"  ");
            else {
                switch(board[map[p]]) {
                    case empty:        fprintf(out,".."); break;
                    case white | man: fprintf(out,"wO"); break;
                    case white | crown: fprintf(out,"wX"); break;
                    case black | man: fprintf(out,"bO"); break;
                    case black | crown: fprintf(out,"bX"); break;
                    default: fprintf(out,"??"); break;
                    }
                p++;
                }
            }
        fprintf(out,"\n");
        }
  if (color==white) fprintf(out,"W");
  else if (color==black) fprintf(out,"B");
  fclose(out);
}

void xprintboard(void)
{
  int p,x,y;

  p=0;
  for(y=0;y<10;y++) {
    for(x=0;x<10;x++) {
      if ((x+y)%2==0) {
	printf("_");
      }
      else {
	switch(board[map[p]]) { 
	case empty: {
	  printf(".");
	  break;
	}
	case white|man: {
	  printf("w");
	  break;
	}
	case white|crown: {
	  printf("W");
	  break;
	}
	case black|man: {
	  printf("b");
	  break;
	}
	case black|crown: {
	  printf("B");
	  break;
	}
      }
	p++;
      }
    }
  }
  printf("\n");
  fflush(stdout);
}

void display_board(void)
{
    int x,y,p=0,bg,fg;
    char t[8];
    FILE *out;

    p=0;
    for(y=0;y<10;y++) {
        for(x=0;x<10;x++) {
            if ((x+y)%2==0) {
	      bg=44;
	      set_col(46,46);
	      printf("  "); 
	      res_col();
	    }
            else {
                   switch(board[map[p]]) { 
                    case empty: {
		      set_col(34,44);
		      printf(".."); 
		      res_col();
		      break;
		      
		    }
                    case white | man: {
		      set_col(37,44);
		      printf("ww");
		      res_col();
		      break;
		    }
                    case white | crown: {
		      set_col(37,44);
		      printf("WW");
		      res_col();
		      break;
		    }
                    case black | man: {
		      set_col(31,44); set_col(1,1);
		      printf("bb");
		      res_col();
		      break;
		    }
                    case black | crown: {
		      set_col(31,44); set_col(1,1);
		      printf("BB");
		      res_col();
		      break;
		    }
		    case OPTION: {
		      set_col(33,44); set_col(1,1);
		      printf(",,");
		      res_col();
		      break;
		    }
                    default: printf("??"); break;
                    }
                p++;
                }
            }
        printf("\n");
        }
    if (usexv==true) {
      save_board(white,"/tmp/board");
      system("dtp -in /tmp/board -out /tmp/board.ppm");
      
    }
}

void read_board(char *buffer,FILE *in,int xx,int yy)
{
    int x,y,p=0,mp;
    for(y=0;y<yy;y++) {
         for(x=0;x<xx;x++) {
            if ((x+y)%2==1) {
                mp=map[p];
                board[mp]=empty;
                if (buffer[2*x]=='w' || buffer[2*x]=='W') board[mp]|=white;
                if (buffer[2*x]=='b' || buffer[2*x]=='B') board[mp]|=black;
                if (buffer[2*x+1]=='O' || buffer[2*x+1]=='w' || buffer[2*x+1]=='b') board[mp]|=man;
                if (buffer[2*x+1]=='X' || buffer[2*x+1]=='W' || buffer[2*x+1]=='B') board[mp]|=crown;
		if (buffer[2*x]==',' || buffer[2*x]=='?') board[mp]=OPTION;
                p++;
                }
            }
        fgets(buffer,4000,in);
        }
}

void read_list(char *buffer,int color)
{
    int p,n,str=2;
    int piece;

    do {
        n=sscanf(buffer+str,"%i",&p);
        do str++; while(buffer[str]!=','); str++;
        piece=man; if (buffer[str-2]=='*') piece=crown;
        if (n>0) board[map[p-1]]=piece|color;
        } while(n>0);
}

void read_solution(char *buffer)
{
  int i,j,nmoves;

  nmoves=buffer[2]-48;
  for(i=0;i<nmoves;i++) {
    for(j=0;j<32;j++) PV[0][i][j]=buffer[3+32*i+j]-32;
  }
  PV[1][0][0]=nmoves;
}

int load_board(char *filename)
{
    int p;
    FILE *in;
    char buffer[4000];

    in=my_fopen(filename,"r");
    if (in==NULL) {printf("file not found\n"); return(false);}
    strcpy(dcpfile,filename);
    for(p=0;p<50;p++) board[map[p]]=empty;
    do {
        fgets(buffer,4000,in);
        if (buffer[0]==' ') read_board(buffer,in,10,10);
        if (buffer[0]=='W' && buffer[1]==':') read_list(buffer,white);
        else if (buffer[0]=='B' && buffer[1]==':') read_list(buffer,black);
        else if (buffer[0]=='W' && buffer[1]==' ') printf("%s\n",buffer);
        else if (buffer[0]=='B' && buffer[1]==' ') printf("%s\n",buffer);
	else if (buffer[0]=='@') read_solution(buffer); 
        } while (!feof(in));
    fclose(in);
    return(true);
}

void init_var(void)
{
    int i,j,x,y,p=0,d;
    int col,xx,yy,m;
    int rnd[50]={
        158348979, 271443740, 917110337, 672394140, 471741934, 108428288,
        746123302, 851148097, 668205810, 971697407, 23865000,  113593368,
        788288248, 667473021, 780119145, 885552817, 451359320, 771282457,
        162534553, 437004112, 934713875, 399087624, 929047756, 795327023,
        696804160, 229755822, 844868352, 159571484, 339467453, 516687944,
        784687804, 444304431, 752169198, 11086258,  8914330,   534983178,
        371807931, 239110026, 698648990, 313948582, 711627924, 303307709,
        584346480, 300873399, 187335317, 451607092, 945652566, 437207155,
        206363717, 465963450
        };
    /* time: may give some portability problems */
    struct tm *date;
    time_t t;

    t=time(NULL);
    date=gmtime(&t);
    srand(date->tm_sec+60*date->tm_min+3600*date->tm_hour);
    printf("random generator seed: %i\n",rand());
    game_history_nr=0;
    for(p=0;p<93;p++) if (invmap[p]>=0) map[invmap[p]]=p;
    for(p=0;p<93;p++) if (invmap[p]>=0) reverse_map[49-invmap[p]]=p;
    for(p=0;p<93;p++) blocked[p]=true;
    reverse_color[empty]=empty;
    reverse_color[white|man]=black|man;
    reverse_color[black|man]=white|man;
    reverse_color[white|crown]=black|crown;
    reverse_color[black|crown]=white|crown;

    /* directional arrays */
    for(col=0;col<2;col++) for(p=0;p<93;p++) for(d=0;d<4;d++)
        next[col][p][d]=invalid;
    p=0;
    for(y=0;y<10;y++) for(x=0;x<10;x++) {
            if ((x+y)%2==1) {lmap[x][y]=p++;}
            }
    p=0;
    for(y=0;y<10;y++) for(x=0;x<10;x++) {
            if ((x+y)%2==1) {
                xx=x-1; yy=y-1;
                m=lmap[xx][yy];
                if (xx>=0 && yy>=0 && xx<10 && yy<10) {

                    next[white][map[p]][forleft]=map[m];
                    next[black][map[p]][backright]=map[m];
                    }
                xx=x+1; yy=y-1;
                m=lmap[xx][yy];
                if (xx>=0 && yy>=0 && xx<10 && yy<10) {
                    next[white][map[p]][forright]=map[m];
                    next[black][map[p]][backleft]=map[m];
                    }
                xx=x-1; yy=y+1;
                m=lmap[xx][yy];
                if (xx>=0 && yy>=0 && xx<10 && yy<10) {
                    next[white][map[p]][backleft]=map[m];
                    next[black][map[p]][forright]=map[m];
                    }
                xx=x+1; yy=y+1;
                m=lmap[xx][yy];
                if (xx>=0 && yy>=0 && xx<10 && yy<10) {
                    next[white][map[p]][backright]=map[m];
                    next[black][map[p]][forleft]=map[m];
                    }
                p++;
                }
           }

    /* promotion */
    for(i=0;i<50;i++) {
        promote[white][map[i]]=white|man;
        promote[black][map[i]]=black|man;
        }
    for(i=0;i<5;i++) promote[white][map[i]]=white|crown;
    for(i=45;i<50;i++) promote[black][map[i]]=black|crown;
    /* hash randoms */
    for(i=0;i<50;i++) for(j=0;j<6;j++) hash_rnd[i][j]=rnd[i]*j;
    init_stats();
    test_nr=0;
}

void init_stats(void)
{
    int i,j,k;

    nsort=neval=ngen=ndat=inhash=outhash=nmat=nmovelist=nquiet=nquietfail=precount=dbfail=ineval=outeval=pat_try=pat_found=pat_succes=0;
    for(i=0;i<MAXPLY;i++) deval[i]=0;
    for(i=0;i<MPV;i++) for(j=0;j<MPV;j++) PV[i][j][0]=0;
    for(i=0;i<4096;i++) db_usage[i]=0;
    for(i=0;i<8;i++) pieces[i]=0;
    for(i=0;i<50;i++) pieces[board[map[i]]]++;

    if (kill_method==PROBKILL) for(k=0;k<20;k++) for(i=0;i<150;i++) for(j=0;j<20;j++) history[k][i][j]=20;
    else for(k=0;k<20;k++) for(i=0;i<150;i++) for(j=0;j<20;j++) history[k][i][j]=0;

}

void set_pieces(void)
{
    int i;
    pieces[2]=pieces[3]=pieces[4]=pieces[5]=0;
    for(i=0;i<50;i++) pieces[board[map[i]]]++;
}

void print_db_nr(int i)
/* prints a database type in format 1101. Input is a database number */
{
    printf("%i%i%i%i",i%8,(i/8)%8,(i/64)%8,(i/512)%8);
}

void print_stats(void)
/* prints statistics of the search process */
{
    int i;
    float nper;

    if (neval!=0) nper=100.0F*ndat/neval;
    else nper=0;
    printf("    deval: ");
    for(i=0;i<MAXPLY;i++) if (deval[i]!=0) printf("%i:%i, ",i,deval[i]);
    printf("\n");
    for(i=0;i<4096;i++) if (db_usage[i]!=0) {
      printf("    ndat: ");
      break;
    }
    for(i=0;i<4096;i++) if (db_usage[i]!=0) {
        print_db_nr(i); printf(":%i, ",db_usage[i]);
        }
    for(i=0;i<4096;i++) if (db_usage[i]!=0) {
      printf("\n");
      break;
    }
    printf("    #eval:%i #mat:%i #preeval %i #ml:%i #quiet:%i (%i) #ngen:%i in:%i out %i #db:%i (%.1f%%) #ex:%i #dbfail:%i #ine:%i #oute:%i  pat:%i/%i/%i\n",neval,nmat,precount,nmovelist,nquiet,nquietfail,ngen,inhash,outhash,ndat,nper,nsort,dbfail,ineval,outeval,pat_try,pat_found,pat_succes);
    /*printf("pieces:%i:%i:%i:%i\n",pieces[2],pieces[3],pieces[4],pieces[5]);*/
}

void movecopy(char *a,char *b)
{
    memcpy(a,b,sizeof(char)*(b[0]+1));
}

int movecmp(char *a,char *b)
/* returns 0 if two moves are equal */
{
    int i;

    if (a[0]!=b[0]) return(1);

    for (i=1;i<a[0];i++) if (a[i]!=b[i]) return(1);
    return(0);
}

int reverse_movecmp(char *a,char *b)
/* returns 0 if two moves are equal */
{
    int i;

    if (a[0]!=b[0]) return(1);

    /* check from field */
    if (reverse_map[invmap[a[1]]]!=b[1]) return(1);
    if (a[0]==4) if (reverse_map[invmap[a[3]]]!=b[3]) return(1);
    else {
      for(i=4;i<=a[0];i+=3) if (reverse_color[a[i]]!=b[i]) return(1);
      for(i=3;i<=a[0];i+=3) if (reverse_map[invmap[a[i]]]!=b[i]) return(1);
    }
    for(i=5;i<a[0];i+=3) if (reverse_map[invmap[a[i]]]!=b[i]) return(1);
   /* for(j=5;j<m;j+=3) if (reverse_map[invmap[a[j]]]!=b[j]) return(1);*/
    return(0);
}

int text_to_move(char *movestring,int color,char *move)
{
   int in1,in2,nr,found=0,nmoves,i;

        /*for(i=0;i<strlen(movestring);i++) if (movestring[i]='x') movestring[i]='-'
  */
    sscanf(movestring,"%i%*c%i",&in1,&in2);
    in1=map[in1-1]; in2=map[in2-1];
    nmoves=move_list(0,color);
    for(nr=0;nr<nmoves;nr++) {
        if (in1==movelist[0][nr][1] && in2==movelist[0][nr][movelist[0][nr][0]-1]) {
            found++;
            movecopy(move,movelist[0][nr]);

            }
        }

    if (found!=1) return(false);
    return(true);
}

void reverse_on_board(void)
{
  BTYPE temp[93];
  copy_board(temp,board);
  reverse_board(board,temp);
}
