/********************************************************************/
/*                                                                  */
/*  Hoser BackGammon version 1.0                                    */
/*                                                                  */
/*      Robert Pfister                                              */
/*  Copyright  June,1987 all rights reserved.                       */
/********************************************************************/

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

/* This file contains:
	Eval		vulnerable	doom		[CopyBoard]
	DoMove		FindMove	GenerateMoves	Find4
	Find3		Find2		Find1
*/
#include "back.h"
#include <string.h>

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

extern you_me_rec	me;

int	Moves  = 0,	AvgPos = 0,
	LastVun = 0,	list[4][2],
	count = 0,	max = 0,
	GameStat = 0,	MaxStat = 0;
const BYTE
    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},
    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},
    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},
    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};

int doom(BYTE Board[26])
{
  int	i, value = 0;

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

int vulnerable(BYTE Board[26], int pos)
{
  int	i,j,k;
  int	value = 0;
  int	HitTable[7][7];

  if ( (Board[pos]<0) || (pos == 0) || (Board[pos] >= 2) )
    return 0;
  memset(&HitTable,0,sizeof HitTable);		/*  intitialize table */

  for(i = 1; i <= 12; i++)	/* look at all dice within 12 places	*/
    if (pos-i >= 0)		/* within the board			*/
      if (Board[pos-i] < 0)	/* there exists a piece at that place	*/
	if (valid(Board,pos,pos-i,i)) {	/* can capture legally		*/
	  if (i <= 6)		/* find hits for one die		*/
	    for(j = 1; j <= 6; j++) {
	      HitTable[i][j] = 1;
	      HitTable[j][i] = 1;
	    }
	  for (j = 1;j < i; j++) { /* figure out combinations		*/
	    k = i-j;		/* first die is j, second is k		*/
				/* 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 if valid (board,position) */
  for(i = 1;i <= 6; i++)	/* find how many hits in that table	*/
    for(j = 1;j <= 6; j++)
      if (HitTable[i][j] == 1)
	value++;
  LastVun = value;
  return(value);
}  /* end of vulnerable */

int Eval(BYTE Board[26])
{
  int	i;
  int	num = 0, value = 0, me,you;
  int	Msum = 0, Ysum = 0;

  /* see who is ahead */
  for(i = 0; i <=24; i++)
    if (Board[i] > 0)
      Msum += (25 - i) * Board[i];
  for(i = 1; i <= 25; i++)
    if (Board[i] < 0)
      Ysum -= i * Board[i];
  for (i = 0; (i <= 24) && (Board[i] <= 0); i++); /* first occurence of me	*/
    me = i;
  for (i = 25; (i >= 1) && (Board[i] >= 0); i--); /* first occurence of you	*/
    you = i;
 		GameStat = BackGame;/* default case				*/
  if (Ysum > Msum+10) GameStat = Running;/* if closer to winning than them	*/
  if (you > 18)	GameStat = Blocking;/* if an opponent is within the bounds	*/
  if (me > you)	GameStat = Pegging;	/* if complete separation		*/
  for(i = 0; i <= 24; i++) {
    if (Board[i] > 0) {
      num += Board[i];
      switch(GameStat) {
	case BackGame:
	  value += back[i] * Board[i];
	  if (i < 18)
	    value += 3 * vulnerable(Board,i);
	  break;
	case Pegging:
	  value += peg[i] * Board[i];
	  break;
	case Blocking:
	  value += block[i] * Board[i];
	  value -= 4 * vulnerable(Board,i);
	  value -= 6 * doom(Board);
	  break;
	case Running:
	  value += run[i] * Board[i];
	  value -= 6 * vulnerable(Board,i);
	  value -= 6 * doom(Board);
	  break;
	}	/* end cases	*/
      }		/* end if	*/
    }		/* end for	*/

    /* 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;
    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;
      /* if blocking game, check the block length...add points for more */
      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 * 2/3;
	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);
}

/* copy board 1 to board 2 */
#define CopyBoard(b1, b2)	memcpy(b2,b1,sizeof_board)

void DoMove(BYTE Board[26], BYTE Dice[4])
{
  int	j;

  AvgPos += count;
  /* don't do anything if nothing to be done  */
  if (count == 0) {
    DoMenuStrip("Can't move from this position!!");
    me.wasted += Dice[0] + Dice[1] + Dice[2] + Dice[3];
  }
  else {
    PutMoveNumber(count);
    /* show move */
    for(j = 0; j <= 3; j++)
      if (list[j][0] != -1) {
	BlinkPiece(Board,list[j][0]);
	update(Board,list[j][0],      list[j][1] ,1);
	PutSpike(    list[j][0],Board[list[j][0]]  );
	if ((list[j][1] > 0) && (list[j][1] < 25))	/******/
	  BlinkPiece(Board,list[j][1]);
	if (list[j][0] == 0)
	  me.wasted += list[j][1];
      }
      else
	me.wasted += Dice[j];
    /* touch up the bars */
    PutSpike(0 ,Board[ 0]);
    PutSpike(25,Board[25]);
  }
}

void FindMove(BYTE Board[26],BYTE Dice[4], int i1, int i2, int i3,int 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];
  }
}

void Find1(BYTE Board[26], BYTE Dice[4])
{
  int	i1,i2;
  BYTE	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);
	}
  }
} /* end of find1 */

void Find2(BYTE Board[26], BYTE Dice[4])
{
  int	i1, i2;
  BYTE	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 */

void Find3(BYTE Board[26], BYTE Dice[4])
{
  int	i1,i2,i3;
  BYTE	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		*/
} /* end of Find3 */

void Find4(BYTE Board[26], BYTE Dice[4])
{
  int	i1, i2, i3, i4;
  BYTE	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		*/
} /* end of Find4 */

void GenerateMoves(BYTE Board[26], BYTE Dice[4])
{
  BYTE Dice2[4];

  count = 0;
  PutMoveNumber(count);
  /* check for doubles rolled */
  if (Dice[2] != 0) {
    Find4(Board,Dice);
    if (count != 0)
      return;
    me.incomplete++;
    Find3(Board,Dice);
    if (count != 0)
      return;
    Find2(Board,Dice);
    if (count != 0)
      return;
  }
  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;
    me.incomplete++;
  }
  Find1(Board,Dice);
} /* end of generate */
