/********************************************************************/
/*                                                                  */
/*  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                                            */
/*                                                                  */
/*  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          */
/*                                                                  */
/********************************************************************/
#include <libraries/dos.h>

extern int AvgPos;
extern int Moves;

int MeInc,YouInc=0;
int MeD,MeM,YouD,YouM=0;
int MeBar,YouBar=0;
int Me2,You2=0;

#define StackSize 500
  static int BoardPos[StackSize][26]; /* stack of the previous positions */
  static int DicePos[StackSize][4];   /* stack of the dice rolls */

  static int StackPos=0;     /* how many moves back are we at? */

  static int MoveList[4];       /* stack of this turns moves */
  static int CurMove=0;        /* place in the list */
  
int Uside;    /* user side */
int Cside;    /* computer side */

int Turn;     /* whose turn is it? */

 int board[26];    /* describes the board position..0 is the bar */
 int Dice[4];      /* describes the 2->4 dice availible */

main() /* main procedure */
  {

  int i,j,play;

   Gsetup();
   Turn=setup(board,Dice);

   for (;;)         /* go until game is over */
     {
      if (Turn==Uside) DoMenuStrip("Your Turn");
                 else  DoMenuStrip("My Turn");

      ShowDice(Dice,Turn);
      if (Turn==Uside)
      {
       /* store this board position */
           StoreMove();
       /* user stats */
           if (Dice[2]>0) You2++;
           YouD+=Dice[0]+Dice[1]+Dice[2]+Dice[3];
           YouM++;
           if (board[25]<0) YouBar++;


       /* do the Move */
          do
            if (UserMove(board,Dice,Turn)==0) GameOver(); /* escape */
            while (  (Turn==Uside)&&
                     (Won(board,Turn)==0)&&
                     (Dice[0]+Dice[1]+Dice[2]+Dice[3]!=0) );
       }
       else /* turn==Cside */
       {  
           /* computer stats */
              if (Dice[2]>0) Me2++;
              MeD+=Dice[0]+Dice[1]+Dice[2]+Dice[3];
              MeM++;
              if (board[0]>0) MeBar++;

           /* do move */
              GenerateMoves(board,Dice);
              DoMove(board);
           }

      Turn=Turn*-1;
      Roll(Dice);

       /* signal to restart the game */
          if (Turn==0) Turn=setup(board,Dice);
          
       /* did user win yet? */
          if (Won(board,Uside))
             {
             stats();
             if (requestor("I lose Play Again?","No","Yes")) GameOver();
                                                else Turn=setup(board,Dice);
             }

       /* did computer win yet? */
          if (Won(board,Cside))
             {
              stats();
              if (requestor("Heehee. I Win. Play Again?","No","Yes")) GameOver();
                                         else Turn=setup(board,Dice);
             }
      ShowDice(Dice,Turn);

    } /* end do forever */

 } /* end of main */

Restart()
{
 Turn=0;
}

StoreMove()
{ 
 int i;
 
 if (StackPos<StackSize+1) /* can only store that many */
  {
   for (i=0;i<26;i++)
       BoardPos[StackPos][i]=board[i];       
   for (i=0;i<4;i++)
       DicePos[StackPos][i]=Dice[i];
   StackPos++;
   }
/* printf("STORE: stack is at %d \n",StackPos); */
}

RecallMove(how)
int how;
{
  int i;

  StackPos=StackPos-1+how;
  if (StackPos<0) StackPos=0;

  for (i=0;i<26;i++)
      if (board[i]!=BoardPos[StackPos][i])
         { /* only refresh stuff changed */
          board[i]=BoardPos[StackPos][i];       
          PutSpike(i,board[i]);
        }

  for (i=0;i<4;i++)
      Dice[i]=DicePos[StackPos][i];

  ShowDice(Dice,Uside);
 StackPos++;
/* printf("RECALL: stack is at %d \n",StackPos);  */
}

Won(board,side)
  int board[26],side;
      {
       int i;

       if (side==1) for(i=0;((board[i]<=0)&&(i<=25));i++);
               else for(i=0;((board[i]>=0)&&(i<=25));i++);

       if (i==26) return(1);
             else return(0);
       }

UserMove(board,Dice,side)
 int board[26],Dice[4],side;
 {
 char Action;
 int  Pick,Valid;  /* flags */
 int i,x,y;

 Valid=0;
 Pick =0;
 while (Valid==0)
 {
   Action=Whats_up(&x);
   UnDoMenuStrip();
   switch (Action)
      {
       /* check for abortion */
       case 'D':Dice[0]=0;
                Dice[1]=0;
                Dice[2]=0;
                Dice[3]=0;
                Valid=1;
                YouInc++;
                break;


       case 'Q':
                Valid=-1;
                break;

       case 'P':if (Pick==0)
                   {
                    if (board[x]*side<=0)
                         DoMenuStrip("No peice there!!");
                         else
                             {
                              Pick=1;
                              y=x;
                              }
                          break;
                     }

                     else if (Pick==1) /* only if peice marked already */
                       {
                         Pick =0;
                         for (i=0;(Valid==0)&&(i<4);i++)
                           {
                             if ((valid(board,y,x,Dice[i]))&&(Dice[i]!=0))
                                 {
                                  BlinkPeice(board,y);
                                  update(board,y,x,side);
                                  PutSpike(y ,board[ y]);
                                  BlinkPeice(board,x);
                                  PutSpike(0 ,board[ 0]);
                                  PutSpike(25,board[25]);
                                  Valid=1;
                                  Dice[i]=0;
                                  }
                             } /* end for */

                        if (Valid==0) DoMenuStrip("Not Valid move");

                          break;
                         } /* end if...case 'M' */

            case 'C':Valid=DoMenu(x);
                     break;
                    
            default :break;

            } /* end of case */

      } /* end while Valid==0 */

   if (Valid==-1) return(0);
   return(1);
   } /* end UserMove */


Roll(d)     /* roll the dice  (4 results) */
int  d[4];
  {
  long  i,k,num1,num2;
  struct DateStamp time;

  DateStamp(&time);

  k=time.ds_Tick;
  
  for (i=1;i<=k;i++)
    {
      num1=RangeRand(6L)+1;
      num2=RangeRand(6L)+1;
     }
  d[0]=num1;
  d[1]=num2;
  if (num1==num2)   /* if rolled doubles */
         {
          d[2]=num1;
          d[3]=num2;
         }
         else
         {
          d[2]=0;
          d[3]=0;
         }
  if (d[3]==0) return(0);
          else return(1);
  }

GameOver()
{
 stats();
 finit();
 exit(0);
 }

stats()
   {
    if ((YouM!=0)&&(MeM!=0))
       {
        printf("Statistics on the Game \n");
        printf("-------------------------------------------------------\n");
        printf("number of moves %ld  avg positions evaluated %ld \n",MeM,AvgPos/MeM);
        printf("\n");
        printf("avg roll    : User %ld Computer %ld \n",YouD/YouM,MeD/MeM);
        printf("doubles     : User %ld Computer %ld \n",You2,Me2);
        printf("inc. Moves  : User %ld Computer %ld \n",YouInc,MeInc);
        printf("Turns on bar: User %ld Computer %ld \n",YouBar,MeBar);
        printf("-------------------------------------------------------\n");
        }
     MeM=0;     YouD=0; YouM=0;  MeD=0;
     AvgPos=0;  You2=0;  Me2=0;  YouInc=0; MeInc=0;
     YouBar=0; MeBar=0;
   }

setup(board,Dice)
int board[26],Dice[4];
 {
 int i;

 Uside=-1;
 Cside= 1;

 /* set the board to the initial setting */

  for (i=0; i<=25; i++)
     {
      board[i]= 0;
      }

  board[ 1]= 2;
  board[ 6]=-5;
  board[ 8]=-3;
  board[12]= 5;
  board[13]=-5;
  board[17]= 3;
  board[19]= 5;
  board[24]=-2;
  for (i=0;i<=25;i++)
      {
       PutSpike(i,board[i]);
       }

 /* see who goes first */

 while (Roll(Dice));  /* until no doubles */

 if (Dice[0]>Dice[1])
           return(Uside);
      else return(Cside);
  }

/* is the move valid for the board, and one die */
valid(board,m1,m2,p)  /* board, start, stop, dice roll */
int board[26];
int m1,m2,p;
  {
  int sign,i,All_In,bar;

     
  All_In=-1;
  
  /* what direction are we going? */
  if (board[m1]>0)
         {
          sign=Cside;
          
          if (Cside==1) bar=0;
                   else bar=25;  

          /* is there a man on the bar?? */
          if ((board[bar]!=0)&&(m1!=0)) return(0);

          /* see if all peices are in home base */
          for(i=0;(i<=24)&&(All_In==-1);i++)
              if (board[i]>0) All_In=i;

          /* check if can take it off */
          if (m2>24)
             {
             if ((All_In>18)&&
                ( (m1==(25-p)) || ((25-p)<All_In)&&(m1==All_In) )) return(1);
                                                              else return(0);
             }
          }
     else
         {
          sign=-1;
          /* is there a man on the bar? */
          if ((board[25]!=0)&&(m1!=25)) return(0);

          /* see if all peices are in home base */
          for(i=25;(i>=1)&&(All_In==-1);i--)
              if (board[i]<0) All_In=i;

          /* see if can take off */
         if (m2==m1)
            {
            if ((All_In<7)&&
               ( (m1==p)|| ((p>All_In)&&(m1==All_In) )))  return(1);
                                                    else  return(0);
            }
          }

  /* does dice roll make sense for the move picked? */
  if ((m2-m1)!=(p*sign))  return(0);

  /* if spike has more than 1 opponent */
  if (board[m2] * sign < -1)  return(0);

  /* I suppose that's all there is..*/
  return(1);
  }

/* put a given move onto the board */
update(board,m1,m2,sign)
int board[26],m1,m2,sign;
  {
   int bar;

   if (sign>0) bar=0;
          else bar=25;

   /* remove the peice from the 'source'  spike */
   
   board[m1]=board[m1]-sign;

   /* if taking a peice off, dont go any furthur */
   if ((m2==m1)||(m2>=25)) return(1);

   /* add a peice to the 'destination' spike, consider if capturing*/
   
   board[m2]=board[m2]+sign;

   if (board[m2]==0) {
                      board[m2    ]=sign;
                      board[25-bar]=board[25-bar]-sign;
                      }
   return(0);
   }
