

/* Press-up game...
       pressup <options>
  where <options> may include
       -f      Machine goes first
       -d n    Search depth is n moves (default 3)
		(greater search depths take longer...but
		 play better!!)
       -b	Print machine's evaluation of its moves.

	THIS PROGRAM WILL ONLY WORK ON TERMINALS HAVING
	LOWER CASE CHARACTERS!!!!!!!!!!!!!!!!!!!!!!!!!!

	This excellant program was written by:
		Prof. Steve Ward
		Director, Real-Time Systems Group
		MIT Lab for Computer Science
		Cambridge, Massachussetts, 02139

		(slightly modified by Leor Zolman)

	The game of Press-Ups is played as follows:
	The board is a n by n array of pegs, each of
	which is standing up at the start of the game. Pegs
	come in 3 colors: Red (yours), Blue (the machine's),
	and white (periods, actually; they're neutral.)

	The first player to move must"push down" a neutral
	peg. Thereafter, players take turns pushing down pegs,
	where each peg pushed must be adjacent to the last one
	pushed.

	Pegs are named by giving a letter and a number, for the
	row and column of the desired peg.

	As soon as a player gets all of his pegs down, he wins.
	When there are no more legal moves to play,
	the player with the most of his own colored pegs down
	is the winner.

	Watch out...at search depths of 6 or more, this program
	plays a mean game!!!
*/

#define SIDE		7	/* Dimension of board	*/

#define HISFIRST (SIDE*2+1)	/* His best first move	*/
#define MYFIRST (SIDE+SIDE/2-1) /* My best first move	*/
#define BELL 0x07
#define BACKSP 0x08

char toupper();

int	Depth;		/* Search depth (default = 3)	*/
int	Helpflag;
char	FFlag,	    /* -f option: machine goes first */
	BFlag;	    /* Debugging flag		    */
char	Startflag;	/* True on first move only */

char *image;
int Adj[16];

int	BestMove;		/* Returned by search	*/

#define BBOARD struct bord

struct bord
  {	char board[SIDE*SIDE];
	int  star;
	char red;
	char blue;
  };

BBOARD initb;

BBOARD master, savebd;

char string[20];

CheckWin(bp)
 BBOARD *bp;
 {	int i;
	i = search(bp,1,1,-32000,-32000);
	if (BestMove >= 0) return 0;

	if (i>0) printf("I win!\n");
	if (i<0) printf("You win!\n");
	if (i==0) printf("Tie game!\n");
	return 1;
	}

asknew()
{
	printf("\nAnother game? ");	
	if (toupper(getchar()) != 'Y') exit();
	printf("\n");
}

main(argc,argv)
 char **argv;
  {	int i,j; char *arg;
	FFlag  = BFlag = 0;
	image = ".rbXRB";
	initw(Adj,"-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1");

	Depth = 3;
	for (i=1; i<argc; i++)
	 {if (*(arg = argv[i])=='-') switch (toupper(*++arg)) {
		case 'D':	Depth = atoi(argv[++i]); continue;
		case 'F':	FFlag++; continue;
		case 'B':	BFlag++; continue;
		default:	printf("Illegal argument: '%1s'\n",argv[i]);
				exit(); }
	   else {printf("Illegal argument: '%1s'\n",arg); exit(); }}

ngame:
	Startflag = 1;
	Helpflag = 0;
	for (i=0; i<(SIDE*SIDE); i++) initb.board[i] = 0;
	 for (j=1; j<(SIDE-1); j++)
		{ initb.board[j] = 1;
		  initb.board[(SIDE*SIDE-1)-j] = 1;
		  initb.board[SIDE*j] = 2;
		  initb.board[SIDE*j + SIDE-1] = 2; };
	initb.star = -1; initb.red = 0; initb.blue = 0;

	bcopy(&initb,&master);
	pboard(&master); bcopy(&master,&savebd);

	for(;;)
	  {	if (FFlag) { FFlag = 0; goto Mine; }
		if (CheckWin(&master)) {
			asknew();
			goto ngame;
		 }
		i = getmove();
		if (i == 'Q') {	
			asknew();
			goto ngame;
		 }
		dmove(i);
		if (CheckWin(&master)) {
			asknew();
			goto ngame;
		 }
	Mine:	printf("I'm thinking...\n");
		i = search(&master,Depth,1,-32000,-32000);
		if (BFlag) printf("Eval = %d\n",i); 
		dmove(BestMove);
		if (i > 500) printf("I've got you!\n"); 
		if (i < -500) printf("You've got me!\n"); 

	  }
  }


pboard(bp)
BBOARD *bp;
  {	int i, j, n;
	char letter;
	letter = 'A';
	printf("\n\n   ");
	for (i=0; i<SIDE; i++) printf("%3d",i+1); printf("\n");
	printf("   "); for (i=0; i<(2+3*SIDE); i++) printf("-"); printf("\n");
	for (i = 0; i < SIDE; i++)
	  {	printf(" %c |",letter);
		for (j = 0; j < SIDE; j++)
		 { if ((n = i*SIDE + j) == bp -> star) printf(" * ");
		   else printf(" %c ",image[bp->board[n]]); }
		printf("| %c",letter++);

		if (i==0) printf("\tSearch Depth: %1d moves",
					Depth);
		if (i==2) printf("\tScore:\tBlue(me)  Red(you)");
		if (i==3) printf("\t\t  %1d        %1d",
				master.blue, master.red);
		if (i==5) {
			if (Helpflag)
				printf("\tYou've had help!");
			if (Startflag)
				printf(FFlag?"\tI go first"
					    :"\tYou go first");
			Startflag = 0;
		 }
		printf("\n");
	  }
	printf("   "); for (i=0; i<(2+3*SIDE); i++) printf("-"); printf("\n");
	printf("   ");
	for (i=0; i<SIDE; i++) printf("%3d",i+1); printf("\n");
	printf("\n");
  }

bcopy(p1,p2)
BBOARD *p1, *p2;
  {	int i;
	i = (SIDE*SIDE)-1;
	do p2->board[i] = p1->board[i];
		while(i--);
	p2->star = p1->star;
	p2->red = p1->red;
	p2->blue = p1->blue;
  }


/* display move #n */

dmove(n)
  {
	move(&master,n);
	pboard(&master);
	bcopy(&master,&savebd);
  }

move(bp,n)
BBOARD *bp;
  {	int type;
	type = bp->board[n] += 3;
	if (type == 4) bp->red++;
	else if (type == 5) bp->blue++;
	bp->star = n;
  }

search (bp,ddepth,who,alpha,beta)
BBOARD *bp;
  {	BBOARD new;
	int i,j,k;
	int myalpha,hisalpha,result,status;
	int best;
	int num;
	int bestmove, ii, jj, n;
	int SavStar;
	int SavBlue;
	int SavRed;
	int Save;
	char moves[9];
	status = -1;
	best = -32000;
	num = 0;
	SavStar = bp -> star;
	SavBlue = bp -> blue;
	SavRed	= bp -> red;
	BestMove = -1;		/* No best move yet...	*/

	if (SavStar == -1)	/* special case opening moves	*/
	 { BestMove = HISFIRST;
	   if (who > 0) BestMove = MYFIRST;
	   return 0; };

	if (!ddepth--)
		return(who * (bp->blue - bp->red));
	if (bp->blue == (SIDE*2-4) || bp->red == (SIDE*2-4))
		return(who*(bp->blue - bp->red)*1000);

		  /* alpha-beta pruning   */
	  if (who>0)   { myalpha = bp->blue; hisalpha = bp->red; }
	   else 	{ myalpha = bp->red; hisalpha = bp->blue; }
	   myalpha += ddepth;  /* Most optimistic estimate. */
	   if (myalpha > (SIDE*2-4)) myalpha = (SIDE*2-4);
	   if (myalpha == (SIDE*2-4)) myalpha = 1000*(myalpha-hisalpha);
	   else myalpha -= hisalpha;
	   if (myalpha <= alpha) return best;

	k = bp->star;
	i = k%SIDE;
	j = k/SIDE;
	for (n=0; n<8; n++)
	 {
		if ((ii = i+Adj[n+n]) < 0 || ii >= SIDE) continue;
		if ((jj = j+Adj[n+n+1]) < 0 || jj >= SIDE) continue;
		if (bp->board[moves[num] = jj*SIDE + ii] < 3) num++; }
	if (num == 0) return(who*(bp->blue - bp->red)*1000);
	bestmove = moves[0];
	for (i=0; i<num; i++)
	 { Save = bp->board[moves[i]];	move(bp,moves[i]);
	   k = -search(bp,ddepth,-who,beta,alpha);
	   bp->board[moves[i]] = Save;
	   bp->blue = SavBlue; bp->red = SavRed; bp->star = SavStar;
	   if (k > alpha) alpha = k;
	   if (k > best) { best = k; bestmove = moves[i]; }
	   if (k>100) break; }
	BestMove = bestmove;
	return best; }



Help()
 {	printf("I'm thinking for you...\n");
	Helpflag = 1;
	search(&master,Depth,-1,-32000,-32000);
	return BestMove; }

getmove()
  {	int row, col, n;
	int dc, dr;
	int star2;
	star2 = master.star;
loop:	printf("\nYour move (z for help; p for board): ");
getrow: for (;;) {
		 if ((row = toupper(getchar()) ) == 'Z') {
			printf("\n");
			return Help();
			}

		  if (row == 'Q') return row;
		  if (row == 'P') {
			pboard(&master);
			goto loop;
		   }
		  if (row == 0177 || row == '\n') goto err;
		  if (row<'A' || row> ('A'+SIDE-1))
			putchar(BACKSP);
			else break;
		}
	row -= 'A';

	for (;;) {
		 col = toupper(getchar());
		  if (col == 0177) {
			putchar(BACKSP);
			goto getrow;
		   }
		if (col == '\n' || col == '\b') goto err;
		if (col < '1' || col > ('0'+SIDE))
			putchar(BACKSP);
			else break;
		}

	col -= '1';
	n = row*SIDE + col;
	dr = abs(row - star2/SIDE);
	dc = abs(col - star2%SIDE);

	if ((star2 < 0 && master.board[n] == 0) ||
	    (dr < 2 && dc < 2 && master.board[n] < 3))
		return(n);

   err:	putchar(BELL);
	printf("  Illegal!  ");
	goto loop;
  }


