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

int npat=500;

char tic[NPAT];
int uuu=0;
#define MAXv(a,b) (a)>(b)?a:b;
struct _pattree {
  char field;
  char question;
  int yes;
  int no;
  short int pat[10];
} pattree[PATTREE];
int npattree=1;
int biggestp;

int pat_info(int p,int *best)
{
  int i,vali,score;
  int count[128];
  int total=0,x;
  int w[8]={white|man,white|crown,black|man,black|crown,empty};

  for(i=0;i<128;i++) count[i]=0;
  for(i=0;i<npat;i++) if (tic[i]==true) {
    count[tpat[i].board[p]]++; total++;
  }

  if (count[OPTION]==total) return(INF);
  score=INF; *best=-1;
  for(i=0;i<5;i++) {
    x=w[i];
    vali=MAXv(count[OPTION]+count[x],total-count[x]);
    if (vali<score) { score=vali; *best=x; }
  }
  if (score>=total) return(INF);
  if (total<50) {
    total-=count[OPTION]; count[OPTION]*=3.5; total+=count[OPTION];
  }
  else {
    total-=count[OPTION]; count[OPTION]*=2.0; total+=count[OPTION];
  }
  score=INF;
  for(i=0;i<5;i++) {
    x=w[i];
    vali=MAXv(count[OPTION]+count[x],total-count[x]);
    if (vali<score) { score=vali; *best=x; }
  }
  return(score);
}

void rec_tree(int level,int oldn,int my_node)
{
  int info,i,j,best,bestp,bestc,n=0,p,bestx;
  int mytic[NPAT];

  uuu++;
  for(j=0;j<npat;j++) if (tic[j]==true) n++;
#ifdef notdef
  for(i=0;i<level;i++) printf("  ");
  if (oldn==n) {printf("no progress:%i\n",n); return;}
  printf("%i\n",n);
#endif
  /* select optimal choice: bestp,bestc*/
  best=INF;
  for(i=0;i<50;i++) {
    info=pat_info(i,&bestx);
    if (info<best) {
      best=info;
      bestp=i;
      bestc=bestx;
    }
  }

  if (best==INF) {
    int p=0;
    pattree[my_node].field=127;
    pattree[my_node].question=127;
    for(j=0;j<npat;j++) if (tic[j]==true) {
      pattree[my_node].pat[p++]=j;
      if (p>biggestp) biggestp=p;
    }
    if (p>10) {printf("fatal: too many patterns in node\n"); exit(1);}
    pattree[my_node].yes=p;
    return;
  }
  /* save tics */
  for(j=0;j<npat;j++) mytic[j]=tic[j];
  pattree[my_node].field=bestp;
  pattree[my_node].question=bestc;
  
  /* choose bestp,bestc */
  for(j=0;j<npat;j++) if (tic[j]==true) {
    tic[j]=false;
    if (tpat[j].board[bestp]==bestc || tpat[j].board[bestp]==OPTION) tic[j]=true;
  }
  pattree[my_node].yes=npattree;
  rec_tree(level+1,n,npattree++);

  /* choose not bestp,bestc */
  for(j=0;j<npat;j++) tic[j]=mytic[j];
  for(j=0;j<npat;j++) if (tic[j]==true) {
    tic[j]=false;
    if (tpat[j].board[bestp]!=bestc) tic[j]=true;
  }
  pattree[my_node].no=npattree;
  rec_tree(level+1,n,npattree++);

  /* reset tics */
  for(j=0;j<npat;j++) tic[j]=mytic[j];
}

void make_pat_tree(void)
{
  int i;
  if (npat<1) return;
  npattree=1;
  biggestp=0;
  for(i=0;i<npat;i++) tic[i]=true;
  rec_tree(0,-1,0);
  printf("%i recursive calls to rec_tree\n",uuu);
  printf("%i tree nodes\n",npattree);
  printf("biggest p:%i\n",biggestp);
}

void load_pat_tree(void)
{
  npattree=bin_load("pattern_tree",sizeof(pattree[0]),pattree);
}

void save_pat_tree(void)
{
  bin_save("pattern_tree",npattree,sizeof(pattree[0]),pattree);
}

void init_tpat(void)
{
  int pat,ok,ip,p,i;
  char filename[256];
  BTYPE temp[93];

  copy_board(temp,board);
  pat=0;
  for(p=0;p<npat;p++) {
    sprintf(filename,"patterns/%.3i",p);
    printf("opening pattern file:%s\r",filename);
    ok=load_board(filename);
    if (ok==false) {
      printf("pattern not found\n");
      continue;
    }
    tpat[pat].use_succes=tpat[pat].use_fail=0;
    for(ip=0;ip<50;ip++) tpat[pat].board[ip]=board[map[ip]];
    tpat[pat].nmoves=PV[1][0][0];
    for(i=0;i<PV[1][0][0];i++) movecopy(tpat[pat].movelist[i],PV[0][i]);
    pat++;
    /*print_move(tpat[pat].movelist[0]); printf("\n");*/
  }
  printf("\n%i patterns loaded\n",pat);
  npat=pat;
  load_pat_tree(); /* we are not sure the tree corresponds with
		      the patterns */
  copy_board(board,temp);
}


void tpat_stats(void)
{
  int i,n,succes=0,fail=0;
  float succes_rate;
  for(i=0;i<npat;i++) {
    succes+=tpat[i].use_succes;
    fail+=tpat[i].use_fail;
    if (tpat[i].use_succes!=0 || tpat[i].use_fail!=0) printf("pattern: %3i succes:%i  fail:%i\n",i,tpat[i].use_succes,tpat[i].use_fail);
  }
  printf("bad patterns:\n");
  for(i=0;i<npat;i++) {
    n=tpat[i].use_succes+tpat[i].use_fail;
    if (n>50) {
      succes_rate=(float)tpat[i].use_succes/n;
      if (succes_rate<.3) printf("pattern: %3i succes:%i  fail:%i\n",i,tpat[i].use_succes,tpat[i].use_fail);
    }
  }
  printf("\n");
  printf("very bad patterns:\n");
  for(i=0;i<npat;i++) {
    n=tpat[i].use_succes+tpat[i].use_fail;
    if (n>50) {
      succes_rate=(float)tpat[i].use_succes/n;
      if (succes_rate<.08) printf("pattern: %3i succes:%i  fail:%i\n",i,tpat[i].use_succes,tpat[i].use_fail);
    }
  }
  printf("\n");
  if (succes>0 || fail>0) printf("total succes:%i  total fail:%i, rating:%f\n",succes,fail,(float) succes/(succes+fail));
}

void save_tpat(void)
{
  int pat,ok,ip,p,i,j;
  char filename[256];
  FILE *out;
  for(pat=0;pat<npat;pat++) {
    sprintf(filename,"patterns/%.3i",pat);
    for(i=0;i<50;i++) board[map[i]]=tpat[pat].board[i];
    save_board(2,filename);
    out=fopen(filename,"a");
    if (out==NULL) {printf("append error %i\n",pat); continue;}
    fprintf(out,"@ %c",(char) (48+tpat[pat].nmoves));
    for(i=0;i<tpat[pat].nmoves;i++) {
      for(j=tpat[pat].movelist[i][0]+1;j<32;j++) tpat[pat].movelist[i][j]=0;
      for(j=0;j<32;j++) fprintf(out,"%c",(char) (32+tpat[pat].movelist[i][j]));
    }
    fclose(out);
  }
}

int tpat_ispattern(int pat,int color)
{
  int match,ip;

  match=true;

  if (color==white) {
    for(ip=0;ip<50;ip++) if (tpat[pat].board[ip]!=OPTION && tpat[pat].board[ip]!=board[map[ip]]) {
      match=false;
      return(false);
    }
  } else {
    for(ip=0;ip<50;ip++) if (tpat[pat].board[ip]!=OPTION && tpat[pat].board[ip]!=reverse_color[board[map[49-ip]]]) {
      match=false;
      return(false);
    }
  }
  if (match==true) return(true);
  return(false); /* never get here */
}

int tpat_reconize_search(int color)
{
  int pat;

  pat=-1;
  for(pat=0;pat<npat;pat++) {
    if (tpat_ispattern(pat,color)==true) return(pat);
  }
  return(-1);
}

int tpat_reconize(int color)
{
  int pat,search,tree=-1,i,node;
  
  /*return(tpat_reconize_search(color));*/
  
  node=0;
  do {
    if (pattree[node].field==127) {
      for(i=0;i<(pat=pattree[node].yes);i++) {
	if (tpat_ispattern(pattree[node].pat[i],color)==true) {tree=pattree[node].pat[i]; break;}
      }
      break;
    }
    if (color==white) {
      if (board[map[pattree[node].field]]==pattree[node].question) node=pattree[node].yes;
      else node=pattree[node].no;
    }
    else if (color==black) {
     if (board[map[49-pattree[node].field]]==reverse_color[pattree[node].question]) node=pattree[node].yes;
      else node=pattree[node].no;
    } 
  } while(1==1);
/*  if (search!=-1 || tree!=-1) printf("full search: %i tree search: %i   %i\n",search,tree,pat);*/
  return(tree);  
}

int palfabeta(int alfa,int beta,int color,int cdepth,int depth)
{
  int nmoves,nr,score,best,bestnr,q,nextdepth;
  char nullmove[32]={4,0,0,0,0};

  bestnr=-1; best=-INF;
  if (depth<=0 || (color==black && quiet(color)==true)) {
    storemove(cdepth,nullmove);
    return(evalboard(color,-INF,INF));
  }
  nmoves=move_list(cdepth,color);
  if (nmoves==0) {
    storemove(cdepth,nullmove);
    return(LOSE);
  }
  for(nr=0;nr<nmoves;nr++) {
    do_move(movelist[cdepth][nr]);
    nextdepth=depth-100;
    if (nextdepth<=0) { /* last move ? : check quiescence */
      q=quiet(color^1);
      if (q==false) nextdepth=1;
    }
    if (color==white && nextdepth>0) if (move_list(cdepth+1,color^1)!=1) {
       undo_move(movelist[cdepth][nr]);
       continue;
    }
    score=-palfabeta(-beta,-alfa,color ^1,cdepth+1,nextdepth);
    undo_move(movelist[cdepth][nr]);
    if (score>=beta) {
      storemove(cdepth,movelist[cdepth][nr]);
      return(score);
    }
    if (score>best) {bestnr=nr; best=score;storemove(cdepth,movelist[cdepth][bestnr]);}
    if (score>alfa) alfa=score;
  }
  return(alfa);
}

void assert_option(int *option,char *move,int mycolor)
{
  int i,p;

  if (move[0]==4) {
    option[move[1]]=false;
    option[move[3]]=false;
  }
  else {
    option[move[1]]=false; /* original position */
    for(i=3;i<move[0];i+=3) {
      option[move[i+2]]=false;  /* jumpto */
      option[move[i+0]]=false;  /* jumpover */
      p=move[i+2];
      if (board[p+12]!=invalid) option[p+6]=false;
      if (board[p+6]!=empty && board[p+6]!=(mycolor|man) && board[p+6]!=(mycolor|crown)) option[p+12]=false;

      if (board[p+14]!=invalid) option[p+7]=false;
      if (board[p+7]!=empty && board[p+7]!=(mycolor|man) && board[p+7]!=(mycolor|crown)) option[p+14]=false;

      if (board[p-12]!=invalid) option[p-6]=false;
      if (board[p-6]!=empty && board[p-6]!=(mycolor|man) && board[p-6]!=(mycolor|crown)) option[p-12]=false;

      if (board[p-14]!=invalid) option[p-7]=false;
      if (board[p-7]!=empty && board[p-7]!=(mycolor|man) && board[p-7]!=(mycolor|crown)) option[p-14]=false;
    }
    option[p+6]=option[p+7]=option[p-6]=option[p-7]=false;
  }
}

int searchpat(void)
{
  int pat,match,i;

  for(pat=0;pat<npat;pat++) {
    match=true;
    for(i=0;i<50;i++) if (tpat[pat].board[i]!=OPTION && tpat[pat].board[i]!=board[map[i]]) {match=false;break;}
    if (match==true) return(pat);
  }
  return(-1);
}

int search_tight_pat(void)
{
  int pat,match,i;

  for(pat=0;pat<npat;pat++) {
    match=true;
    for(i=0;i<50;i++) if (board[map[i]]!=OPTION && tpat[pat].board[i]!=board[map[i]]) {match=false;break;}
    if (match==true) return(pat);
  }
  return(-1);
}

void addpat(void)
{
  int ip,i;
  int mypat,tight;

  if (npat>=NPAT) {
    printf("out of space\n");
    return;
  }
  tight=search_tight_pat();
  if (tight==-1) mypat=npat; else mypat=tight;
  for(ip=0;ip<50;ip++) tpat[mypat].board[ip]=board[map[ip]];
  i=0;
  while(PV[0][i][1]!=0) {
    movecopy(tpat[mypat].movelist[i],PV[0][i]);
    i++;
  }
  tpat[mypat].nmoves=i;
  tpat[mypat].use_succes=tpat[mypat].use_fail=0;
  if (tight==-1) {
    npat++;
    printf("added pattern %i\n",mypat);
  }
  else {
    printf("overwrote pattern %i\n",mypat);
  }
}

void plearn(int color,int plusscore)
{
  int cscore,depth,score,i;
  char input[100];
  int option[93];
  BTYPE temp[93];

  if (color==black) {
    reverse_board(temp,board);
    copy_board(board,temp);
  }
  set_pieces();
  for(i=0;i<93;i++) temp[i]=board[i];
  maxpv=MPV;
  for(i=0;i<93;i++) option[i]=true;
  if (quiet(white)==false || quiet(black)==false) {
    return;
  }
  cscore=evalboard(white,-INF,INF);
  for(depth=100;depth<600;depth+=100) {
    init_stats();
    score=palfabeta(-INF,INF,white,0,depth);
    if (score>cscore+plusscore) {
      printf("last loaded file:'%s'\n",dcpfile);
      printf("win detected at depth=%i, score=%i\n",depth,score);
      print_pv(); print_stats();
      i=0;
      while(PV[0][i][1]!=0) {
	assert_option(option,PV[0][i],i%2);
	do_move(PV[0][i]);
	i++;
      }
      if (i<=1) {
	printf("illegal pattern detected\n");
	return;
      }
      for(i=0;i<93;i++) board[i]=temp[i];
      set_pieces();
      for(i=0;i<50;i++) if (option[map[i]]==true) board[map[i]]=OPTION;
      if (searchpat()>=0) {
	printf("pattern already present\n");
	break;
      }
      /*if (depth<500) break;*/
      display_board();
      addpat();
      for(i=0;i<93;i++) board[i]=temp[i];
      printf("original:\n");
      display_board();
      break;
    }
  }
}













