/* Work out the equities of a triple of hands, or of a file containing triples of hands */
/* A.P.Selby Oct 1999 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define IN "out1"
static int sc[91],h0[91],h1[91],(*cd)[6],(*st)[6],*sp;
static char *cc="23456789TJQKA",*su="cdhs",*(td[9])={"High card ","Pair      ","Two pair  ","Trips     ","Straight  ","Flush     ","Full House","Quadzilla ","Str8 Flush"};
static void prsh(int j){int i;for(i=0;i<6;i++){printf("%c%c",cc[cd[j][i^1]],su[st[j][i^1]]);if(i&1)printf(" ");}}
static void prub(int *b){int i;for(i=0;i<5;i++)printf("%c",cc[b[i]]);}
static int cmp(const void*x,const void*y){return sc[*(int*)x]-sc[*(int*)y];}
static int cmp1(const void*x,const void*y){return sp[*(int*)x]-sp[*(int*)y];}
static void desc(int lx){int i;if(lx<0){printf("----------------");return;}
 printf("%s ",td[lx>>20]);for(i=4;i>=0;i--)printf("%c",cc[(lx>>4*i)&15]);}
static void desh(int i){printf("%c%c",cc[h1[i]],cc[h0[i]]);}
static int c2n(char c){return strchr(cc,c)-cc;}
static int s2n(char c){return strchr(su,c)-su;}
main(int ac,char **av){
 int c,h,i,j,k,n,t,tt,i0,ii,nn,nc,nw,u0,u1,mw,mf,ms,hf,hs,lx,ty,
   p0,p1,bs,id,bm,bm0,nf,v,cs,bsf,pr,nub,fs,nd,
   b[5],str[1<<13],lxf[1<<13],ml0[13],ml[13],fr[16][5],wfr[16],bin[6][6],
   db[5],mb[5],hb[13],bbm[16],ok[5],nok[5],fok[5],(*cn)[3],*tp,
   sh[3],usc[3],nws[3],lbm[3],usr[91],pp[91];
 double z,tti,nxt,pin;
 char l[1000],*l0,l1[3][50],*bb,*hh,*inf;
 FILE *fp;
 clock_t ti0;

 pr=0;pin=10;
 bb=0;hh=0;inf=IN;
 while((c=getopt(ac,av,"b:h:p:i:?"))!=EOF){
  switch(c){
  case 'b':bb=optarg;break;
  case 'h':hh=optarg;break;
  case 'p':pr=atoi(optarg);break;
  case 'i':inf=optarg;break;
  case '?':
   printf("Usage: mk2 [-b ranks] [-d] [-h hands] [-p print level] [-i input file]\n");
   printf(" -b<ranks> only considers particular ranks of board cards (for debugging)\n");
   printf(" -h<triple of hands> evaluates a particular hand triple\n");
   printf(" -i<file> evaluates all the hand triples in the given file\n");
   printf(" -p<num> sets print level (for debugging)\n");
   printf("e.g., mk2 -h'AcKc AdKd Td4c'\n      mk2 -itestfile\n      mk2 -b2355J -p1\n");
   exit(0);
  }
 }
 printf("Input file = %s",inf);
 if(hh)printf(" (disregarded)\nHands: %s\n",hh); else printf("\n");
 if(bb)printf("Board: %s\n",bb);
 if(pr)printf("Print level %d\n",pr);

 for(i=0;i<=5;i++){bin[i][0]=1;for(j=1;j<=5;j++){bin[i][j]=(bin[i][j-1]*(i-j+1))/j;}}
 for(i=0;i<(1<<13);i++){
  bm=(i<<1)|(i>>12);
  for(v=9;v>=0 && (bm&(31<<v))!=(31<<v);v--);
  if(v==0)v=0x3210c; else if(v>0)v=(v-1)|v<<4|(v+1)<<8|(v+2)<<12|(v+3)<<16;
  str[i]=v;
  if(v<0){
   for(j=12,n=0,v=0;j>=0&&n<5;j--)if(i&(1<<j))v|=j<<4*(4-(n++));
   if(n==5)lxf[i]=5<<20|v; else lxf[i]=-1;
  }else lxf[i]=8<<20|v;
 }
 for(i=0;i<13;i++)for(j=i;j<13;j++){id=i+(j*(j+1))/2;h0[id]=i;h1[id]=j;}
 for(i=0,ii=0;i<32;i++){
  for(j=0,k=0;j<n;j++)k+=i>>j&1;
  if(k>=3){for(j=0;j<n;j++)fr[ii][j]=i>>j&1;wfr[ii]=k;ii++;}
 }
 for(i=0;i<91;i++)usr[i]=0;

 if(hh==0){strcpy(l,"wc ");strcpy(l+3,inf);fp=popen(l,"r");fscanf(fp,"%d",&nn);pclose(fp);}
  else nn=1;
 tp=(int*)malloc(nn*sizeof(int));
 sp=(int*)malloc(nn*sizeof(int));
 cn=(int(*)[3])malloc(nn*3*sizeof(int));
 cd=(int(*)[6])malloc(nn*6*sizeof(int));
 st=(int(*)[6])malloc(nn*6*sizeof(int));
 if(tp==0||sp==0||cn==0||cd==0||st==0)exit(printf("Couldn't malloc\n"));
 if(hh==0)fp=fopen(inf,"r");
 for(i=0;hh?i<1:(fgets(l,100,fp)!=NULL);i++){
  if(hh)l0=hh; else l0=l;
  if(sscanf(l0,"%s %s %s",l1[0],l1[1],l1[2])!=3)exit(fprintf(stderr,"Bad hand description\n"));
  for(j=0;j<3;j++){
   int cd0,cd1,st0,st1;
   cd0=c2n(l1[j][0]);st0=s2n(l1[j][1]);cd1=c2n(l1[j][2]);st1=s2n(l1[j][3]);
   if(cd0>cd1||(cd0==cd1&&st0>st1)){t=cd0;cd0=cd1;cd1=t;t=st0;st0=st1;st1=t;}
   cd[i][j*2]=cd0;st[i][j*2]=st0;
   cd[i][j*2+1]=cd1;st[i][j*2+1]=st1;
   usr[cd0+(cd1*(cd1+1))/2]=1;
   cn[i][j]=0;
  }
 }
 if(hh==0&&i!=nn)exit(fprintf(stderr,"Bad\n"));
 if(hh==0)fclose(fp);
 printf("Read %d set%s of hands\n\n",nn,(nn==1?"":"s"));

 nxt=0;tti=0;nub=0;
 /* Loop over unsuited boards */
 for(b[0]=0;b[0]<13;b[0]++)
 for(b[1]=b[0];b[1]<13;b[1]++)
 for(b[2]=b[1];b[2]<13;b[2]++)
 for(b[3]=b[2];b[3]<13;b[3]++)
 for(b[4]=b[3];b[4]<13;b[4]++){
  ti0=clock();
  if(bb)for(i=0;i<5;i++)b[i]=c2n(bb[i]);
  if(b[0]==b[4])continue;
  if(tti>=nxt||pr>=1){prub(b);printf(" %4d %5.1f%% %9.2fs\n",nub,nub/6175.*100,tti);fflush(stdout);nxt=tti+pin;}
  nub++;

  bm=1<<b[0]|1<<b[1]|1<<b[2]|1<<b[3]|1<<b[4];
  for(i=0;i<13;i++)ml0[i]=0;bm0=0;
  for(i=0;i<5;i++){ml0[b[i]]++;bm0|=1<<b[i];}
  for(i=0;i<91;i++)sc[i]=-1;
  /* Work out the value of each possible hand, disregarding flushes */
  for(i0=0;i0<91;i0++){
   if(usr[i0]==0)continue;
   p0=h0[i0];p1=h1[i0];
   memcpy(ml,ml0,13*sizeof(int));
   ml[p0]++;ml[p1]++;bm=bm0|1<<p0|1<<p1;
   mf=0;ms=0;
   for(h=12;h>=0;h--){
    if(ml[h]>mf){hs=hf;ms=mf; hf=h;mf=ml[h]; continue;}
    if(ml[h]>ms){hs=h;ms=ml[h];}
   }
   if(mf>4)continue;
   lx=0;
   for(i=0;i<mf;i++)lx+=hf<<4*(4-i);
   if(mf<4)for(;i<mf+ms&&i<5;i++)lx+=hs<<4*(4-i); else hs=-1;
   for(j=12;i<5;j--)if(j!=hf&&j!=hs&&ml[j]>0)lx+=j<<4*(4-(i++));
   if(mf==4)ty=7;
   if(mf==3&&ms>=2)ty=6;
   if(mf==3&&ms==1)ty=3;
   if(mf==2&&ms==2)ty=2;
   if(mf==2&&ms==1)ty=1;
   if(mf==1)ty=0;
   if(ty<4&&str[bm]>=0){lx=str[bm];ty=4;}
   lx+=ty<<20;
   sc[i0]=lx;
  }
  if(pr>=3){
   for(i=0;i<91;i++)pp[i]=i;
   qsort(pp,91,sizeof(int),cmp);
   for(i=0;i<91;i++){k=pp[i];
    prub(b);printf(" ");desh(k);if(sc[k]<0){printf("  *** Impossible ***\n");continue;}
    lx=sc[k];printf("  %06x  ",lx);desc(lx);printf("\n");
   }
  }
  
  for(i=0;i<5;i++)mb[i]=(i==0);
  for(i=1,nd=1,db[0]=b[0];i<5;i++){if(b[i]!=b[i-1])db[nd++]=b[i]; mb[nd-1]++;}
  for(i=0;i<13;i++)hb[i]=-1;
  for(i=0;i<nd;i++)hb[db[i]]=i;
  nf=bin[nd][3]+bin[nd][4]+bin[nd][5];
  for(i=0;i<nf;i++)for(j=0,bbm[i]=0;j<nd;j++)if(fr[i][j])bbm[i]|=1<<db[j];

  /* Loop over hand triples */
  for(tt=0;tt<nn;tt++){
   if(pr>=1){
    for(i=0;i<5;i++)printf("%c",cc[b[i]]);printf("  %4d) ",tt);
    prsh(tt);
    printf("\n");
   }
   for(i=0,bs=-1;i<3;i++){j=cd[tt][i*2+1];sh[i]=sc[cd[tt][i*2]+(j*(j+1))/2];if(sh[i]>bs)bs=sh[i];}
   for(i=0,nw=0;i<3;i++)nw+=(sh[i]==bs);
   for(i=0;i<3;i++)usc[i]=(sh[i]==bs?6/nw:0);
   for(i=0;i<nd;i++)ok[i]=15;
   for(i=0;i<6;i++){j=hb[cd[tt][i]];if(j>=0)ok[j]&=~(1<<st[tt][i]);}
   for(i=0;i<nd;i++){j=ok[i];nok[i]=(j&1)+(j>>1&1)+(j>>2&1)+(j>>3);}
   /* Calculate contribution of this board to equity, disregarding flushes */
   for(i=0,nc=1;i<nd;i++)nc*=bin[nok[i]][mb[i]];
   for(i=0;i<3;i++)cn[tt][i]+=nc*usc[i];

   /* Loop over ways to make the board cause a flush, adding in the difference between the 
      flush equity and the non-flush equity each time */
   if(nd>=3)for(fs=0;fs<4;fs++){
    for(i=0;i<nd;i++)fok[i]=ok[i]>>fs&1;
    for(i=0,mw=0;i<3;i++){
     lbm[i]=0;j=0;
     if(st[tt][i*2]==fs){lbm[i]=1<<cd[tt][i*2];j=1;}
     if(st[tt][i*2+1]==fs){lbm[i]|=1<<cd[tt][i*2+1];j++;}
     if(j>mw)mw=j;
    }
    for(ii=0;ii<nf;ii++){
     if(wfr[ii]+mw<5)continue;
     if(pr>=3){for(i=0;i<nd;i++)if(fr[ii][i])printf("%c",su[fs]);else printf(".");printf("\n");}
     for(i=0,nc=1;i<nd&&nc;i++){
      if(fr[ii][i]){
       if(!fok[i])goto nlii;
       nc*=bin[nok[i]-1][mb[i]-1];
      }else nc*=bin[nok[i]-fok[i]][mb[i]];
     }
     bm=bbm[ii];
     bsf=-1;
     for(i=0;i<3;i++){
      cs=lxf[bm|lbm[i]];
      nws[i]=cs;
      if(cs>bsf){bsf=cs;nw=1;} else if(cs==bsf)nw++;
     }
     if(bsf>bs){
      j=6/nw;
      for(i=0;i<3;i++)cn[tt][i]+=nc*(nws[i]==bsf?j-usc[i]:-usc[i]);
      if(pr>=2){
       printf("  ");prub(b);printf(" ");prsh(tt);printf("  ");
       for(i=0;i<3;i++){desc(nws[i]);if(i<2)printf(", ");}
       printf("  (+");for(i=0;i<3;i++)printf("%d",nws[i]==bsf?j:0);
       printf("-");for(i=0;i<3;i++)printf("%d",usc[i]);
       printf(")*%d\n",nc);
      }
     }
    nlii:
    }/* ii */
   }/* fs */
  }/* tt */
  
  tti+=(clock()-ti0)/(double)CLOCKS_PER_SEC;
  if(bb)goto el0;
 }/* b[] */
 el0:
 printf("Total time %9.2fs\n",tti);

 for(i=0;i<nn;i++){
  u0=cn[i][0];u1=cn[i][0];
  if(cn[i][1]>u0)u0=cn[i][1];
  if(cn[i][1]<u1)u1=cn[i][1];
  if(cn[i][2]>u0)u0=cn[i][2];
  if(cn[i][2]<u1)u1=cn[i][2];
  sp[i]=u0-u1;
 }
 for(i=0;i<nn;i++)tp[i]=i;
 qsort(tp,nn,sizeof(int),cmp1);
 printf("----Hands-----   ---------Equities(%%)---------   Best-Worst(%%, #boards)\n");
 for(i=0;i<nn;i++){
  j=tp[i];
  prsh(j);
  t=cn[j][0]+cn[j][1]+cn[j][2];z=t;
  k=sp[j];
  printf("  %9.6f %9.6f %9.6f   %9.6f %7d+%d/2\n",
	  cn[j][0]/z*100,cn[j][1]/z*100,cn[j][2]/z*100,k/z*100,k/6,(k/3)%2);
  if(t%6||k%3)exit(printf("Error t=%d k=%d\n",t,k));
 }
}
