/********************************************************************/
/*                                                                  */
/*  Hoser BackGammon version 1.0                                    */
/*                                                                  */
/*      Robert Pfister                                              */
/*                                                                  */
/*      Rfd#3 Box 2340                home:(207)-873-3520           */
/*      Waterville, Maine 04901                                     */
/*                                                                  */
/*      Pfister_rob%dneast@dec.decwrl                               */
/*                                                                  */
/*                                                                  */
/*  Copyright  June,1987 all rights reserved.                       */
/*                                                                  */
/*  This program will play a game of backgammon at the novice level */
/*                                                                  */
/*  The code is in 4 parts...                                       */
/*                                                                  */
/*   /   1) back.c     - main driver                                */
/* \/    2) eval.c     - evaluation of moves                        */
/*       3) backscn.c  - screen stuff..                             */
/*       4) backmenu.c - menu stuff, help text, and ``decoder''     */
/*                                                                  */
/* this was compiled under Manx 3.20a, using long integers          */
/*                                                                  */
/********************************************************************/

/****************************************************************/
/* These routines  do all the thinking for the computer side    */
/*                                                              */
/* (read: the magic lives here)                                 */
/****************************************************************/

#define Running  1
#define Blocking 2
#define Pegging  3
#define BackGame 4

extern int MeInc;

int Moves =0;
int AvgPos=0;
int LastVun=0;

int list[4][2];
int count=0;
int max=0;
int GameStat=0;
int MaxStat=0;

int block[26]={-1,1,2,3,4,5,6,  8,9,10,11,12,13   ,14,15,17,21,25,27,
                 25,21,19,8,3,1,0};

int run[26]  ={-1,1,2,3,4,5,6,  15,16,17,18,19,20,  30,31,32,33,34,35,
               50,51,52,53,54,55};

int peg[26]  ={-20,0,0,0,0,0,0 ,1,2,3,4,5,6, 30,31,32,33,34,35,
                50,49,48,47,46,45,0};

int back[26]={20,40,30,20,10,5,0 ,1,2,3,4,5,6, 30,31,32,30,40,50,
                50,49,48,30,20,10,0};

Eval(Board)
int Board[26];
{
  register int i;
  int num,value,me,you;
  int Msum,Ysum;


  num=0;
  value=0;

  /* see who is ahead */
  Msum=0;
  for(i=0;i<=24;i++)
     if (Board[i]>0)
        Msum+=(25-i)*Board[i];

  Ysum=0;
  for(i=1;i<=25;i++)
     if (Board[i]<0)
        Ysum-=i*Board[i];

  for (i= 0;(i<=24)&&(Board[i]<=0);i++);    /* first occurance of me  */
  me=i;
  for (i=25;(i>=1)&&(Board[i]>=0);i--);/* first occurance of you */
  you=1;
                    GameStat=BackGame; /* default case */
  if (Ysum>Msum+10) GameStat=Running;  /* if closer to winning than them */
  if (you>18)       GameStat=Blocking; /* if a opponent is within the bounds */
  if (me>you)       GameStat=Pegging;  /* if complete separation */

  for(i=0;i<=24;i++)
   {
    if (Board[i]>0)
       {
        num=num+Board[i];
        switch(GameStat)
           {
            case BackGame:
                 value+=back[i]*Board[i];
                 if (i<18) value+=3*vunerable(Board,i);
                 break;

            case Pegging:
                 value+=peg[i]*Board[i];
                 break;

            case Blocking:
                 value+=block[i]*Board[i];
                 value-=4*vunerable(Board,i);
                 value-=6*doom(Board);
                 break;

            case Running:
                 value+=run[i]*Board[i];
                 value-=6*vunerable(Board,i);
                 value-=6*doom(Board);
                 break;
            } 
        }
     }

  /* add points for taking men off...more if no chance of being taken */

  if (GameStat==Pegging)  value+=(15-num)*300;
                     else value+=(15-num)*75;


  /* if blocking game, check the block length...add points for more */

  switch(GameStat)
    {
     case BackGame:
          num=0;
          for(i=0;i<=6;i++) if ((Board[i]>0)&&(you>i)) num++;
          value+=100*num;
          num=0;
          for(i=15;i<=21;i++) if (Board[i]>0) num+=Board[i];
          value+=30*num;
          value-=100*Board[25];
          num=0;
          for(i=17;i<=23;i++) if (Board[i]>=2) num++;
          value+=(num*300);
          break;

     case Blocking:
          num=0;
          for(i=14;i<=22;i++)
             {
              if (Board[i]>=2) num++;
              if ((you>i)&&(Board[i]>=2)) num++;
              }
          value+=(num*400);
          num=0;
          for(i=0;i<=17;i++) if (Board[i]>0) num+=(Board[i]*(18-i)*(18-i));
          value-=num/1.5;
          break;

     case Pegging:

     case Running:
          num=0;
          for(i=0;i<=17;i++) if (Board[i]>0) num++;
          value-=10*num;
          num=0;
          for(i=0;i<=17;i++) if (Board[i]>0) num+=(Board[i]*(26-i)*(26-i));
          value-=num;
          break;
      }

  return(value);
}



vunerable(Board,pos)
int Board[26],pos;
{
 register int i,j,k;
 
 int value;
 int HitTable[7][7];

 value=0;

 if ( (Board[pos]<0) || (pos==0) || (Board[pos]>=2) ) return (value);


/*  intitialize table */
 for(i=1;i<=6;i++)
    for(j=1;j<=6;j++)
       HitTable[i][j]=0;

/* look at all dice within 12 places */
   for(i=1;i<=12;i++)
      {
      if (pos-i>=0)  /* within the board */
       if (Board[pos-i]<0) /* there peice exists at that place */
         if (valid(Board,pos,pos-i,i))  /* can capture legally */
            {
             /*--------------------------------------------------*/
             /* find hits for one die */
              if (i<=6)
                 for(j=1;j<=6;j++)
                   {
                    HitTable[i][j]=1;
                    HitTable[j][i]=1;
                    }

              /* figure out combinations */
                 for (j=1;j<i;j++)
                     {
                       /* first die is j, second is k */
                        k=i-j;

                        /* check legality of the intermediate place */

                        if (valid (Board,pos,pos-j,j))
                           {
                            HitTable[k][j]=1;
                            HitTable[j][k]=1;
                            }

                       if (valid (Board,pos,pos-k,k))
                          {
                           HitTable[k][j]=1;
                           HitTable[j][k]=1;
                           }
                      }
             /*---------------------------------------------------*/
             }   /* end of valid (board,position) */

       } /* end of for i */

/* find how many hits in that table */
  for(i=1;i<=6;i++)
     for(j=1;j<=6;j++)
        if (HitTable[i][j]==1) value++;

 LastVun=value;

 return(value);

}  /* end of vunerable */


doom(Board)
int Board[26];
{
register int i,value;

   if (LastVun==0) return(0);

/* find the "doom" factor..the possibilty of getting outa being captured */
  for(i=1;i<=10;i++) if (Board[i]<-1) value+=5;

 return(value);
}

/* copy b1 to b2 */
CopyBoard(b1,b2)
int b1[26],b2[26];
{
  register int i;
  for (i=0;i<=25;i++)
     {
       b2[i]=b1[i];
      } /* end for */
 } /* end of Copy */

DoMove(Board)
int Board[26];
{
 int j,sum;

 AvgPos+=count;

 /* dont do anything if nothing to be done  */
 if (count==0) 
    {
     DoMenuStrip("Cant move from this position!!");
     return(0);
     }

 PutMoveNumber(count);

  /* show move */
 for(j=0;j<=3;j++)
    if (list[j][0]!=-1)
       {
        BlinkPeice(Board,list[j][0]);
        update(Board,list[j][0],      list[j][1] ,1);
        PutSpike(    list[j][0],Board[list[j][0]]  );
        BlinkPeice(Board,list[j][1]);
        }

 /* touch up the bar's */
 PutSpike(0 ,Board[ 0]);
 PutSpike(25,Board[25]);

 }

FindMove(Board,Dice,i1,i2,i3,i4)
int Board[26],Dice[4],i1,i2,i3,i4;
{
 int j;

 if ((count<50)||(10*(count/10)==count))
     PutMoveNumber(count);
 j=Eval(Board);
 if ((count==1)||(j>max))
    {
     max=j;
     MaxStat=GameStat;
     list[0][0]=i1; list[0][1]=i1+Dice[0];
     list[1][0]=i2; list[1][1]=i2+Dice[1];
     list[2][0]=i3; list[2][1]=i3+Dice[2];
     list[3][0]=i4; list[3][1]=i4+Dice[3];
     }
 }

GenerateMoves(Board,Dice)
int Board[26],Dice[4];
{

register int i;

int Dice2[4];
count=0;

PutMoveNumber(count);

/* check for doubles rolled */
if (Dice[2]!=0)
           {
            Find4(Board,Dice);
                if (count!=0) return(0);
                MeInc++;
            Find3(Board,Dice);
                if (count!=0) return(0);
            Find2(Board,Dice);
                if (count!=0) return(0);
            }
      else
          {
           Find2(Board,Dice);

           Dice2[0]=Dice[1];
           Dice2[1]=Dice[0];
           Dice2[2]=Dice[2];
           Dice2[3]=Dice[3];

           Find2(Board,Dice2);
             if (count!=0) return(0);
           MeInc++;
           }

 Find1(Board,Dice);
 return(0);

 } /* end of generate */


Find4(Board,Dice)
int Board[26],Dice[4];
{
 register int i1,i2,i3,i4;
 
 int Board1[26],Board2[26],Board3[26],Board4[26];
 for (i1=0;i1<=24;i1++)
   {
   if (Board[i1]>0)
   if (valid(Board,i1,i1+Dice[0],Dice[0]))
      {
      CopyBoard(Board,Board1);
      update(Board1,i1,i1+Dice[0],1);
      for (i2=0;i2<=24;i2++)
         {
         if (Board1[i2]>0)
         if (valid(Board1,i2,i2+Dice[1],Dice[1]))
            {
            CopyBoard(Board1,Board2);
            update(Board2,i2,i2+Dice[1],1);
            for (i3=0;i3<=24;i3++)
                {
                 if (Board2[i3]>0)
                 if (valid(Board2,i3,i3+Dice[2],Dice[2]))
                    {
                    CopyBoard(Board2,Board3);
                    update(Board3,i3,i3+Dice[2],1);
                    for (i4=0;i4<=24;i4++)
                        {
                        if (Board3[i4]>0)
                        if (valid(Board3,i4,i4+Dice[3],Dice[3]))
                           {
                           count++;
                           CopyBoard(Board3,Board4);
                           update(Board4,i4,i4+Dice[3],1);
                           FindMove(Board4,Dice,i1,i2,i3,i4);
                           }
                        } /* end for i4 */ 
                    } /* end valid Dice[2] */
                } /* end for i3 */
            } /* end valid Dice[1] */
         } /* end for i2 */
      } /* end if Valid Dice[0] */
   } /* end for i1 */
return(0);
} /* end of Find4 */



Find3(Board,Dice)
int Board[26],Dice[4];
{
 register int i1,i2,i3;
 
 int Board1[26],Board2[26],Board3[26];

 for (i1=0;i1<=24;i1++)
   {
   if (Board[i1]>0)
   if (valid(Board,i1,i1+Dice[0],Dice[0]))
      {
      CopyBoard(Board,Board1);
      update(Board1,i1,i1+Dice[0],1);
      for (i2=0;i2<=24;i2++)
         {
         if (Board1[i2]>0)
         if (valid(Board1,i2,i2+Dice[1],Dice[1]))
            {
            CopyBoard(Board1,Board2);
            update(Board2,i2,i2+Dice[1],1);
            for (i3=0;i3<=24;i3++)
                {
                 if (Board2[i3]>0)
                 if (valid(Board2,i3,i3+Dice[2],Dice[2]))
                    {
                     count++;
                     CopyBoard(Board2,Board3);
                     update(Board3,i3,i3+Dice[2],1);
                     FindMove(Board3,Dice,i1,i2,i3,-1);
                    } /* end valid Dice[2] */
                } /* end for i3 */
            } /* end valid Dice[1] */
         } /* end for i2 */
      } /* end if Valid Dice[0] */
   } /* end for i1 */
 return(0);
} /* end of Find3 */

Find2(Board,Dice)
int Board[26],Dice[4];

{
 register int i1,i2;
 int Board1[26],Board2[26];

 for (i1=0;i1<=24;i1++)
   {
   if (Board[i1]>0)
   if (valid(Board,i1,i1+Dice[0],Dice[0]))
      {
      CopyBoard(Board,Board1);
      update(Board1,i1,i1+Dice[0],1);
      for (i2=0;i2<=24;i2++)
         {
         if (Board1[i2]>0)
         if (valid(Board1,i2,i2+Dice[1],Dice[1]))
            {
            count++;
            CopyBoard(Board1,Board2);
            update(Board2,i2,i2+Dice[1],1);
            FindMove(Board2,Dice,i1,i2,-1,-1);
            }/* end valid Dice[1] */
         }/* end for i2 */
      }/* end if Valid Dice[0] */
   }/* end for i1 */
}/* end find2 */

Find1(Board,Dice)
int Board[26],Dice[4];
{
register int i1,i2;
int Board1[26],Dice2[4];

for (i2=0;i2<=1;i2++)
   {
   Dice2[0]=Dice[i2]; Dice2[1]=0; Dice2[2]=0; Dice2[3]=0;

   for (i1=0;i1<=24;i1++)
       if (Board[i1]>0) if (valid(Board,i1,i1+Dice[i2],Dice[i2]))
          {
          count++;
          CopyBoard(Board,Board1);
          update(Board1,i1,i1+Dice[i2],1);
          FindMove(Board1,Dice2,i1,-1,-1,-1);
          }
     }
     return(0);
} /* end of find1 */
