/*
 * 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 "functions.h"

#define MAXBOOKRETRY 20

struct _table_entry {
    unsigned int hashkey;
    unsigned char board[25];
    char color;
    int evaltype;
    int eval;
    int usage;
    int effort;
} *table[NTABLE],*book_entry;
int table_size[NTABLE];
int book_entry_nr=0;
int table_change[NTABLE];

int remap_tt()
/* find the correct tabletype for board */
{
  int totalman;

  totalman=pieces[white|man]+pieces[black|man]+pieces[white|crown]+pieces[black|crown];
  if (totalman<=5) return(-1);
  if (totalman==6) return(P6);
  if (totalman==8) return(P8);
  if (totalman==10) return(P10);
  return(MISC);
}

void new_table_entry(int t,int color,int evaltype,int eval,int effort,int verbose)
{
  unsigned int key,entry;

 
  if (t==MISC) t=remap_tt();
  if (t<0) return;
  if (table_change[t]==T_LOCKED) return;

  key=hash_key(color);
  entry=key%table_size[t];
  if (table[t][entry].color!=127) {
    if (table[t][entry].hashkey==key) { /* double position */
      return;
    }
    else {
      if (verbose>=1) printf("table collision on table %i.%i key:%i\n",t,entry,key);
      return;
    }
  }
  /* is there enough effort to store this position? */
  if (evaltype==AB && effort<800) return;
  if (evaltype==PN && effort<200) return;
  if (evaltype==PNTWO && effort<20) return;
  /* we have a clean entry */
  table[t][entry].hashkey=key;
  table[t][entry].eval=eval;
  table[t][entry].color=color;
  table[t][entry].effort=effort;
  table[t][entry].evaltype=evaltype;
  compress_board(table[t][entry].board,board);
  if (verbose>=10) printf("new table entry\n");
  if (table_change[t]==T_UNCHANGED) table_change[t]=T_CHANGED;
}

void make_bin_book(void)
{
  BTYPE tempboard[93];
  int score;

  parameters[0]=-3;search_min=0;
  search_max=40;
  perc=1;
  copy_board(tempboard,board);
  strcpy(pdnfile,"pdnbook/candidate95.pdn");
  score=check_database(pdnfile,search_min,search_max);
  strcpy(pdnfile,"pdnbook/wk83.pdn");
  score=check_database(pdnfile,search_min,search_max);
  strcpy(pdnfile,"pdnbook/prof2.pdn");
  score=check_database(pdnfile,search_min,search_max);
  strcpy(pdnfile,"pdnbook/dutch96h.pdn");
  score=check_database(pdnfile,search_min,search_max);
  strcpy(pdnfile,"pdnbook/book.pdn");
  score=check_database(pdnfile,search_min,search_max);
  strcpy(pdnfile,"pdnbook/damop.pdn");
  score=check_database(pdnfile,search_min,search_max);
  strcpy(pdnfile,"pdnbook/syb-andr.pdn");
  score=check_database(pdnfile,search_min,search_max);
  copy_board(board,tempboard);
  table_change[BOOK]=T_CHANGED;
}

void init_tables(void)
{
    int t,i,score;
    FILE *in;
    char name[20];
    char tempboard[93];

    table_size[BOOK]=MAXBOOK;
    table_size[P6]=1;
    table_size[P8]=1;
    table_size[P10]=1;
    table_size[MISC]=1;
    
    for(t=0;t<NTABLE;t++) {
      table[t]=(struct _table_entry *)malloc((table_size[t])*sizeof(struct _table_entry));
      if (table[t]==NULL) {
	printf("malloc failed for table %i\n",t);
	table_size[t]=0;
	table_change[t]=T_LOCKED;
	exit(1);
      }
      for(i=0;i<table_size[t];i++) {
        table[t][i].color=127;
        table[t][i].hashkey=-1;
      }
      table_change[t]=T_UNLOADED;
      
      /* load files */
      sprintf(name,"tables/%i.bin",t);
      in=my_fopen(name,"rb");
      if (in!=NULL) {
        fread(table[t],sizeof(struct _table_entry),table_size[t],in);
	fclose(in);
        table_change[t]=T_UNCHANGED;
      }
    }
    /* specifics for BOOK table */
    book_entry=table[BOOK];
}

void save_tables(void)
{
  char name[20];
  int t;
  FILE *out;

  for(t=0;t<NTABLE;t++) {
    if (table_change[t]==T_UNCHANGED) continue;
    if (table_change[t]!=T_CHANGED) {
      printf("error: table %i has status %i\n",t,table_change[t]);
      printf("enter 'yes' to force writing\n");
      /*sscanf(name,"%s";
      if (strcmp(name,"yes")!=0) continue;*/
    }
    sprintf(name,"tables/%i.bin",t);
    out=fopen(name,"wb");
      if (out!=NULL) {
	printf("writing table %i\n",t);
        fwrite(table[t],sizeof(struct _table_entry),table_size[t],out);
        table_change[t]=T_UNCHANGED;
      } else printf("write error\n");
    fclose(out);
  }
  
}

void try_learn(int color)
{
  int tm,n,m;

  
  tm=pieces[white|man]+pieces[black|man]+pieces[white|crown]+pieces[black|crown];
  m=material(color);
  if (m!=0) return;

  if (tm==6) n=3500;
  else if (tm==8) n=1000;
  else return;

  display_board();
  do_pn_search(color,n,WINTHEO,2);
}

void tablestats(void)
{
  int t,i,j,etype[10];
  char name[30];
  int count=0;
  for(t=0;t<NTABLE;t++) {
    if (t==BOOK) strcpy(name,"BOOK");
    else if (t==P6) strcpy(name,"6PIECES");
    else if (t==P8) strcpy(name,"8PIECES");
    else if (t==P10) strcpy(name,"10PIECES");
    else if (t==MISC) strcpy(name,"MISC");
    else strcpy(name,"UNKNOWN");
    printf("statistics table '%s'\n",name);
    if (table_change[t]==T_UNLOADED) strcpy(name,"unloaded");
    else if (table_change[t]==T_LOCKED) strcpy(name,"locked");
    else if (table_change[t]==T_UNCHANGED) strcpy(name,"unchanged");
    else if (table_change[t]==T_CHANGED) strcpy(name,"changed");
    else strcpy(name,"UNKNOWN");
    printf("   status: %s\n",name);
    printf("   size:%i entries\n",table_size[t]);
    count=0;
    for(i=0;i<10;i++) etype[i]=0;
    for(i=0;i<table_size[t];i++) {
      if (table[t][i].color!=127) {
	count++;
	etype[table[t][i].evaltype]++;
      }
    }
    printf("   used total:%i (%.1f%%)\n",count,100.0F*(float)count/table_size[t]);
    printf("   evaltype: book:%i  ab:%i  pn:%i  pn2:%i\n",etype[EBOOK],etype[AB],etype[PN],etype[PNTWO],etype[EBOOK]);
  }
}

void add_book_entry(int color,int eval)
{
    unsigned int key,entry;
    int i,e;
    key=hash_key(color);
    entry=key%MAXBOOK;


    e=0;
    while(e<MAXBOOKRETRY) {
      if (book_entry[entry].color==127) goto store_book;
      if (book_entry[entry].color!=127) {
        if (book_entry[entry].hashkey==key) { /* double position */
	  book_entry[entry].eval--;
	  return;
        }
      }
    entry=(entry+1)%table_size[BOOK]; e++;
    }
    printf("!");
    return;
  store_book:
    book_entry[entry].hashkey=key;
    book_entry[entry].eval=eval;
    book_entry[entry].color=color;
    book_entry[entry].evaltype=EBOOK;
    compress_board(book_entry[entry].board,board);
    book_entry_nr++;

}

void book_info(void)
{
    printf("%i book entries\n",book_entry_nr);
}

int book_score(int color)
{
    int i,e;
    unsigned int entry,key;
    unsigned char temp[25];

    key=hash_key(color);
    entry=key%MAXBOOK;
    e=0; 
    while(e<MAXBOOKRETRY) {
      if (key==book_entry[entry].hashkey) goto retreive_book;
      entry=(entry+1)%table_size[BOOK]; e++;
    }
    return(UNKNOWN);
  retreive_book:
    compress_board(temp,board);
    for(i=0;i<25;i++) if (book_entry[entry].board[i]!=temp[i]) return(UNKNOWN);
    if (book_entry[entry].color!=color) return(UNKNOWN);
    return(book_entry[entry].eval);
}


int try_book(int depth,int color)
/* returns score if succesfull move is done, UNKNOWN otherwise */
{
    int nmoves,x,best=-INF,i,score,bestmove=-1;
    int iterscore[MAXNM];
    int sum=0,id,s;
    x=book_score(color);
    if (x==UNKNOWN) return(x);

    nmoves=move_list(depth,color);
    for(i=0;i<nmoves;i++) {
         do_move(movelist[depth][i]);
         score=-book_score(color^1);
         movecopy(movescore[i].move,movelist[depth][i]);
         movescore[i].value=score;
	 iterscore[i]=score;
	 if (score!=(-UNKNOWN)) sum+=score+1;
         undo_move(movelist[depth][i]);
    }
    if (sum==0) {
      printf("leaving book\n");
      return(UNKNOWN);
    }
    id=rand()%sum;
    s=0; i=0;
    do {
      if (iterscore[i]!=(-UNKNOWN)) {
	s+=iterscore[i]+1;
	if (id<s) {best=i; break;}
	
      }
      i++;
    } while(i<nmoves);
    if (i==nmoves) {printf("never get here 3\n");return(UNKNOWN);}

    xstore_history(movelist[depth][best],"book");
    storemove(0,movelist[depth][best]);
    do_move(movelist[depth][best]);
    return(best);
}
