
/* Gammon IV, by David C. Oshel, Ames, Iowa */

/*--------------------------------------------------------------------*/
/* GAMEPLAN.C -- separately compiled module, contains the tactics and */
/*               strategy for making the computer's move in Gammon IV */
/*               BACKGMMN.REL calls MyMove(), MyMove() calls topstone */
/*               and movestone and a few others.  Uses the globals in */
/*               GAMEPLAN.HDR quite heavily.  Bgversion declared here */
/*--------------------------------------------------------------------*/

#include "gameplan.hdr" /* contains external, global declarations */

/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Bgversion is an external reference in the main module.  Use this string */
/* to take credit for a decent gameplaying algorithm if you find one!      */
/*                                                                         */
char *bgversion = ".20  -April 1, 1986- by David C. Oshel";
                 /*....v....1....v....2....v....3....v...*/
                 /* just 38 characters for version info! */
/*                                                                         */
/*-------------------------------------------------------------------------*/


/*=============================================*/
/*     M Y   M O V E                           */
/*=============================================*/


naked() {  /* am I leaving too many blots? */
static int i, clink;
	i = 24;  clink = 0;
	while (i) {
		if (point[i].stones == 1 && point[i].owner == ME) clink++;
		i--;
	}
	return (clink > 2);
} /* end: naked */


yourfolly() { /* look for lotsa blots in your inner table */
static int i, clink;
	i = 18; clink = 0;
	while (i < 25) {
		if (point[i].owner == YU && point[i].stones == 1) 
			clink++; 
		i++;
	}
	return (clink >= 3);
} /* end: yourfolly */


goodboard() { /* look for four made points near my inner table */
static int i, clank, clink;
	i = 9; clank = 0;
	while (i > 3) {
		if (point[i].owner == ME && point[i].stones > 1) clank++;
		i--;
	}
	if (clank > 4) return (TRUE);  /* bar is nearly blocked */

	i = 6; clank = clink = 0;
	while (i) {
		if (point[i].owner != ME) 
			;
		else if (point[i].stones == 1) clink++; else clank++;
		i--;
	}
	return (clank > 3 && clink < 2);
} /* end: goodboard */


bearoff() {
	return (topstone(ME) < 7);
} /* end: bearoff */


scanahead( from ) int from; {
static int count;

	count = 0;
	while (--from > 0) {
		++count;
		if ( point[ from ].owner == YU ) return (count);
	}
	return (7);

} /* end: scanahead */


endgame() { /* Is no strategy required from here on in? */
	return ( (25 - topstone(YU)) > topstone(ME) );
} /* end: endgame */


/*------------------------------------------------------------*/
/* MATCHUP                                                    */
/*                                                            */
/* 2-stone functions that force the choice of the next move.  */
/* These are the HEART and SOUL of this backgammon algorithm! */
/*------------------------------------------------------------*/

setpend( from, to ) int from, to; {
	pending.fr   = from;
	pending.to   = to;
	pending.flag = TRUE;
} /* end: setpend */


clrpend() {
	pending.flag = FALSE;
} /* end: clrpend */


natural(f1,t1,f2,t2) int f1,t1,f2,t2; {
	clrpend();
	if (point[t2].stones == 1 && t1 == f2) setpend(f2,t2);
	return (pending.flag);
} /* end: natural */


matchup( test4 ) int (* test4)(); {
static int i, j, ti, tj;

	if ( pending.flag ) return (FALSE); /* this is probably redundant */

	for (i = 1; i < 26; i++) {
		ti = list[0][i];
		if ( ti == ERROR ) goto zoo;
		for (j = 1; j < 26; j++) {
			tj = list[1][j];
			if ( tj == ERROR ) goto voo;
			if ( (* test4)( i, ti, j, tj ) ) {
				lurch( i, ti, 0);
				return (TRUE);
			}
		   voo: ;
		}
	   zoo: ;
	}
	return (FALSE);

} /* end: matchup */


matchhi( test4 ) int (* test4)(); {
static int i, j, ti, tj;

	if ( pending.flag ) return (FALSE); /* this is probably redundant */

	for (i = 1; i < 26; i++) {
		ti = list[1][i];
		if ( ti == ERROR ) goto zoo;
		for (j = 1; j < 26; j++) {
			tj = list[0][j];
			if ( tj == ERROR ) goto voo;
			if ( (* test4)( i, ti, j, tj ) ) {
				lurch( i, ti, 1);
				return (TRUE);
			}
		   voo: ;
		}
	   zoo: ;
	}
	return (FALSE);

} /* end: matchhi */



/*--------------------------------------------------------*/
/* CLOCKWISE and COUNTERCLOCK                             */
/*                                                        */
/* the rest of these are single-stone decisions based on  */
/* rules of thumb, board-scanning functions               */
/*--------------------------------------------------------*/

plainstupid( from ) int from; { /* don't break a safe point */
	return (from < 13 && (point[from].stones == 2 && scanahead(from) < 7));
} /* end: plainstupid */


unwise( innertablept ) int innertablept; {
	/* if it's a hit, just for god's sake don't put him on the bar!! */
	if ( innertablept < 7 ) {
		if (point[ innertablept ].owner == YU ||
		    point[ YRBAR ].stones > 0) 
			return (TRUE);
	}
	return(FALSE);
} /* end: unwise */



covermine( from, to ) int from, to; {
	if ( from < 8 ) return(FALSE);
	return ( (point[ to ].stones == 1) && (point[ to ].owner == ME) );
} /* end: covermine */


idareyou( from, to ) int from, to; {
	if (unwise( to )) return (FALSE);
	if ( (point[ from ].stones != 2)
		&& (point[ to ].stones < 2)
		&& (scanahead( to ) > 6) ) return ( TRUE );
	else return (FALSE);
} /* end: idareyou */


hitandrun( from, to ) int from, to; {
	return ( point[ to ].owner == YU );
} /* end: hitandrun */


dbuild( from, to ) int from, to; {
static int diceleft;
	diceleft = (myturns? 2 + movesleft: movesleft);
	if (diceleft > 1) {
		/* can't possibly be only one stone on from point */
		/* or kamikaze would have covered it on last move */
		return ( point[to].stones == 0 );
	}
	return (FALSE);
} /* end: dbuild */


kamikaze( from, to ) int from, to; { 
/* cover my distant blot, or razzle-dazzle 'em with the long doubles hit */
static int j, k, diceleft;
	
	k = from; 
	j = from - to;
	diceleft = myturns * movesleft;  /* NB: 2*2 == 2+2, "fourtunately" */
	while ( diceleft-- ) {   /* predicting where doubles land is easy! */
		k -= j;
		if (k < 1) return (FALSE); /* out of bounds */
		if ( point[ k ].stones == 0 ) continue;        /* simplify */
		if ( point[ k ].stones == 1 )   /* found my blot or yours? */
			return (TRUE);
		else if ( point[k].owner == YU )   /* found your blockade? */
			return (FALSE); 
		else continue;        /* found my safe point, so ignore it */
	}
	return (FALSE);

} /* end: kamikaze */


hittite( from, to ) int from, to; {
	return (hitandrun(from,to) && to > 9);
} /* end: hittite */


safehit( from, to ) int from, to; {
	return (hittite(from,to) && idareyou(from,to));
} /* end: safehit */


foolsdemise( from, to ) int from, to; { 
        /* annihilate orphaned blots in enemy's inner, my outer table */
	return (to > 17 && point[to].owner == YU);
} /* end: foolsdemise */


landonme( from, to ) int from, to; {
	if ( plainstupid(from) ) return (FALSE);
	if ( loneranger(from,to) ) {
		if (from < 19 && to > 6) return(TRUE);
	}
	else return ( point[ to ].owner == ME && point[to].stones < 4);
} /* end: landonme */



/* these evaluations have meaning only in the endgame */


nobackgammon( from, to ) int from, to; { /* endgame */
	return (from > 19);
} /* end: nobackgammon */


crosstable( from, to ) int from, to; { 
	/* always move a table ahead if possible, in the endgame */
	if (from < 7) return (FALSE);
	if (from > 18 && to <= 18) return (TRUE);
	if (from > 12 && to <= 12) return (TRUE);
	if (from >  6 && to <=  6) return (TRUE);
	return (FALSE);
} /* end: crosstable */


fiftytworule( from, to ) int from, to; { /* endgame */
static int p;
	if (from < 7) return (FALSE);   /* not in inner table! */
	p = from % 6;
	if (p == 0) return (TRUE);  		/* improve the six */
	if (p != 5) return ( (to % 6) < 3 );    /* best improve under five */
} /*  end: fiftytworule */




/* these evaluations are universally applicable, last resort moves */


gohome( from, to ) int from, to; { /* always go home if you can */
	return (to == MYHOME);
} /* end: gohome */


scatter( from, to ) int from, to; {  /* scatter, esp. in the endgame */
	if (plainstupid(from) || unwise(to)) return (FALSE);
	return ( point[ from ].stones > 1 && point[ to ].stones == 0 );
} /* end: scatter */


runnerup( from, to ) int from, to; {
	if (from < 10 || from > 18) return (FALSE);
	return (TRUE);
} /* end: runnerup */


loneranger( from, to ) int from, to; {
	return( point[ from ].stones == 1 );
} /* end: loneranger */


run( dummy1, dummy2 ) int dummy1, dummy2; { /* MUST move something! */
	return (TRUE);
} /* end: run */





/* clockwise and counterclock make a 1-stone choice on rules of thumb */


counterclock( test ) int (* test)(); {
static int i,j;

	for (i = 0; i < 2; i++) {
		for (j = 1; j < 25; j++) {
			if ( list[i][j] == ERROR ) continue;
			if ( (* test)( j, list[i][j] ) ) {
				lurch( j, list[i][j], i);
				return ( TRUE );
	}	}	}
	return (FALSE);

} /* end: counterclock */


clockwise( test ) int (* test)(); {
static int i,j;

	for (i = 0; i < 2; i++) {
		for (j = 25; j > 0; j--) {
			if ( list[i][j] == ERROR ) continue;
			if ( (* test)( j, list[i][j] ) ) {
				lurch( j, list[i][j], i);
				return ( TRUE );
	}	}	}
	return (FALSE);

} /* end: clockwise */





/*-------------------------------------------*/
/*  Make Prime                               */
/*-------------------------------------------*/

static int prmchk;


buildprime( f1,t1,f2,t2 ) int f1,t1,f2,t2; {
	clrpend();
	/* check for the doubles bug */
	if ((dice[0] == dice[1]) && (point[f1].stones < 2)) return(FALSE);

	/* look for the combination */
	if ( t1 == prmchk && t2 == prmchk) setpend(f2,t2);

	/* stick like glue to a made point, but doubles may move forward */
	if (dice[0] != dice[1]) { 
		if ((f2 < 8) && (point[f2].stones == 2)) clrpend();
		if ((f1 < 8) && (point[f1].stones == 2)) clrpend();
	}

	return(pending.flag);

} /* end: buildprime */


makeprime() {
static int i, tab[] = { ERROR,1,2,3,20,22,24,9,4,6,8,5,7 };
	i = 12;
	while (i) {
		prmchk = tab[i]; 
		i--;
		if ( point[ prmchk ].stones > 1 ) continue;
		else if ( matchup( buildprime ) ) return(TRUE);
	}
	return(FALSE);
} /* end: makeprime */


coverprime( from, to ) int from, to; {
	return (((to == prmchk) && 
		(point[prmchk].owner == ME)) &&
		(point[from].stones != 2));
} /* coverprime */


cleanup() {
static int i, tab[] = { ERROR,1,2,3,20,22,24,9,4,6,8,5,7 };
	i = 12;
	while (i) {
		prmchk = tab[i]; 
		i--;
		if ( point[ prmchk ].stones != 1 ) continue;
		else if ( counterclock( coverprime ) ) return(TRUE);
	}
	return(FALSE);
} /* end: cleanup */


/*-------------------------------------*/
/* Walking Prime                       */
/*-------------------------------------*/

swivelhips( from, to ) int from, to; {
	return ( from > prmchk );
} /* end: swivelhips */


slink( from, to ) int from, to; {
	return( (from > prmchk) && (point[to].stones == 1) );
} /* end: slink */


weasel() {
	if ( clockwise( slink ) )
		return(TRUE);
	if ( counterclock( swivelhips ) ) 
		return(TRUE);
	if ( clockwise( run ) )
		return(TRUE);
} /* end: weasel */

ihaveprime( from )  int from; {
static int i, to, ez;
	ez = 0;
	for (i = 0; i < 6; i++) {
		to = from - i;
		if ((point[to].owner == ME) && (point[to].stones > 1)) ez++;
	}
	return (ez > 4);
} /* end: ihaveprime */


walkingprime() { 
/* looks for the walking prime anywhere in the front tables */
/* then tries to bring up a runner from behind the prime,   */
/* ensuring that a back stone WILL move before a front one  */
static int i;
	i = 12;
	while (i > 5) {
		if ( ihaveprime(i) ) {
			prmchk = i;
			if ( weasel() ) return (TRUE);
		}
		i--;
	}
	return(FALSE);
} /* end: walkingprime */


/*---------- Book Moves ----------*/
/* only valid if my move is first */
/*--------------------------------*/

zip(a,b,c,d) int a,b,c,d; {
	lurch(a,b,0);
	lurch(c,d,0);
	movesleft = 0; return( TRUE );
} /* end: zip */


zoom( a,b,c,d,e,f,g,h ) int a,b,c,d,e,f,g,h; {
	myturns = 0; zip(a,b,c,d); zip(e,f,g,h); return( TRUE );
} /* end: zoom */


book() {
int a,b;
	if (!firstmove) return (FALSE);
	firstmove = FALSE;
	a = min(dice[0],dice[1]);
	b = max(dice[0],dice[1]);
	switch (level) {
		case 0: { return ( book0(a,b) ); break; }
		case 1: { return ( book1(a,b) ); break; }
		case 2: { return ( book2(a,b) ); break; }
	}
}
book0( a,b ) int a, b; { 
	switch (a) {
		case 1: { switch (b) {
				case 1: return ( zoom(8,7,8,7,6,5,6,5) );
				case 2: return ( zip(24,23,13,11) );
				case 3: return ( zip(8,5,6,5) );
				case 4: return ( zip(24,23,13,9) );
				case 5: return ( zip(24,23,13,8) );
				case 6: return ( zip(13,7,8,7) );
			  }
			break; }
		case 2: { switch (b) {
				case 2: return ( zoom(6,4,6,4,13,11,13,11) );
				case 3: return ( zip(13,11,13,10) );
				case 4: return ( zip(8,4,6,4) );
				case 5: return ( zip(13,8,13,11) );
				case 6: return ( zip(24,18,13,11) );
			  }
			break; }
		case 3: { switch (b) {
				case 3: return ( zoom(13,10,13,10,10,7,10,7) );
				case 4: return ( zip(13,10,13,9) );
				case 5: return ( zip(13,10,13,8) );
				case 6: return ( zip(24,18,13,10) );
			  }
			break; }
		case 4: { switch (b) {
				case 4: return ( zoom(13,9,13,9,24,20,24,20) );
				case 5: return ( zip(13,8,13,9) );
				case 6: return ( zip(24,18,18,14) );
			  }
			break; }
		case 5: { switch (b) {
				case 5: return ( zoom(13,8,13,8,8,3,8,3) );
				case 6: return ( zip(24,18,18,13) );
			  }
			break; }
		case 6: { return ( zoom(13,7,13,7,24,18,24,18) );
			break; }
	}

} /* end: book0 */
book1( a,b ) int a, b; { /* mostly follows Becker */
	switch (a) {
		case 1: { switch (b) {
				case 1: return ( zoom(8,7,8,7,6,5,6,5) );
				case 2: return ( zip(13,11,6,5) );
				case 3: return ( zip(8,5,6,5) );
				case 4: return ( zip(13,9,6,5) );
				case 5: return ( zip(13,8,6,5) );
				case 6: return ( zip(13,7,8,7) );
			  }
			break; }
		case 2: { switch (b) {
				case 2: return ( zoom(6,4,6,4,13,11,13,11) );
				case 3: return ( zip(13,11,13,10) );
				case 4: return ( zip(8,4,6,4) );
				case 5: return ( zip(13,8,13,11) );
				case 6: return ( zip(13,7,7,5) );
			  }
			break; }
		case 3: { switch (b) {
				case 3: return ( zoom(13,10,13,10,8,5,8,5) );
				case 4: return ( zip(13,10,13,9) );
				case 5: return ( zip(13,8,8,5) );
				case 6: return ( zip(13,7,13,10) );
			  }
			break; }
		case 4: { switch (b) {
				case 4: return ( zoom(13,9,13,9,9,5,9,5) );
				case 5: return ( zip(13,8,13,9) );
				case 6: return ( zip(13,7,13,9) );
			  }
			break; }
		case 5: { switch (b) {
				case 5: return ( zoom(13,8,13,8,8,3,8,3) );
				case 6: return ( zip(13,7,13,8) );
			  }
			break; }
		case 6: { return ( zoom(13,7,13,7,24,18,24,18) );
			break; }
	}

} /* end: book1 */
book2( a,b ) int a, b; { /* mostly follows Becker */
	switch (a) {
		case 1: { switch (b) {
				case 1: return ( zoom(8,7,8,7,6,5,6,5) );
				case 2: return ( zip(13,11,24,23) );
				case 3: return ( zip(8,5,6,5) );
				case 4: return ( zip(13,9,24,23) );
				case 5: return ( zip(13,8,24,23) );
				case 6: return ( zip(13,7,8,7) );
			  }
			break; }
		case 2: { switch (b) {
				case 2: return ( zoom(6,4,6,4,24,23,24,23) );
				case 3: return ( zip(13,11,13,10) );
				case 4: return ( zip(8,4,6,4) );
				case 5: return ( zip(13,8,13,11) );
				case 6: return ( zip(13,7,13,11) );
			  }
			break; }
		case 3: { switch (b) {
				case 3: return ( zoom(13,10,13,10,10,7,10,7) );
				case 4: return ( zip(13,10,13,9) );
				case 5: return ( zip(13,8,8,5) );
				case 6: return ( zip(13,7,13,10) );
			  }
			break; }
		case 4: { switch (b) {
				case 4: return ( zoom(13,9,13,9,9,5,9,5) );
				case 5: return ( zip(13,8,13,9) );
				case 6: return ( zip(13,7,13,9) );
			  }
			break; }
		case 5: { switch (b) {
				case 5: return ( zoom(13,8,13,8,8,3,8,3) );
				case 6: return ( zip(13,7,13,8) );
			  }
			break; }
		case 6: { return ( zoom(13,7,13,7,24,18,24,18) );
			break; }
	}

} /* end: book2 */


/*====== MyMove ======*/

torve() {
	if ( makeprime() ) {  /* this will use doubles, if it can */
			return;
	}
	else if ( walkingprime() ) {  /* i have six prime points, so run!!! */
			return;
	}
	else if ( dice[0] == dice[1] ) {	      /* this is too easy! */
		if ( counterclock( kamikaze ) )
			return;
		if ( counterclock( dbuild ) ) /* claim new turf */
			return;
		if ( clockwise( run ) )
			return;
	}
	else if ( cleanup() ) {  /* cover my single blot on prime points */
			return;
	}
	else if ( bearoff() ) { /* I'm ready, but you're in the back game! */
		if ( counterclock( gohome ) )
			return;
		if ( clockwise( run ) )
			return; 
	}
	else {
		if ( clockwise( hitandrun ) )
			return;
		if ( matchup( natural ) )
			return; 
		if ( clockwise( landonme ) )
			return;
		if ( counterclock( runnerup ) )
			return;
		if ( clockwise( scatter ) )
			return;
		if ( clockwise( run ) )
			return;
	}

} /* end: torve */



villiers() {
	if ( makeprime() ) {  /* this will use doubles, if it can */
			return;
	}
	else if ( walkingprime() ) {  /* i have six prime points, so run!!! */
			return;
	}
	else if ( dice[0] == dice[1] ) {	      /* this is too easy! */
		if ( counterclock( kamikaze ) )
			return;
		if ( counterclock( dbuild ) ) /* claim new turf */
			return;
		if ( clockwise( run ) )
			return;
	}
	else if ( cleanup() ) {  /* cover my single blot on prime points */
			return;
	}
	else if ( bearoff() ) { /* I'm ready, but you're in the back game! */
		if ( counterclock( gohome ) )
			return;
		if ( clockwise( run ) )
			return; 
	}
	else { 					   
		if ( clockwise( foolsdemise ) )
			return;
		if ( clockwise( idareyou ) ) 
			return;
		if ( counterclock( covermine ) )
			return;
		if ( matchup( natural ) )
			return;
		if ( clockwise( landonme ) )
			return;
		if ( clockwise( runnerup ) )
			return;
		if ( clockwise( scatter ) )
			return;
		if ( clockwise( run ) )
			return;
	}

} /* end: villiers */



louisa() {
	if ( makeprime() ) {  /* this will use doubles, if it can */
			return;
	}
	else if ( walkingprime() ) {  /* i have six prime points, so run!!! */
			return;
	}
	else if ( dice[0] == dice[1] ) {	      /* this is too easy! */
		if ( counterclock( kamikaze ) )
			return;
		if ( counterclock( dbuild ) ) /* claim new turf */
			return;
		if ( clockwise( run ) )
			return;
	}
	else if ( cleanup() ) {  /* cover my single blot on prime points */
			return;
	}
	else if ( bearoff() ) { /* I'm ready, but you're in the back game! */
		if ( counterclock( gohome ) )
			return;
		if ( clockwise( run ) )
			return; 
	}
	else if ( (!naked() && goodboard()) || yourfolly() ) { 
		if ( clockwise( hitandrun ) )
			return;
		if ( matchup( natural ) )
			return; 
		if ( clockwise( landonme ) )
			return;
		if ( counterclock( runnerup ) )
			return;
		if ( clockwise( scatter ) )
			return;
		if ( clockwise( run ) )
			return;
	}
	else { 					   
		if ( clockwise( foolsdemise ) )
			return;
		if ( clockwise( idareyou ) ) 
			return;
		if ( counterclock( covermine ) )
			return;
		if ( matchup( natural ) )
			return;
		if ( clockwise( landonme ) )
			return;
		if ( clockwise( runnerup ) )
			return;
		if ( clockwise( scatter ) )
			return;
		if ( clockwise( run ) )
			return;
	}
} /* end: louisa */


mymove() {
int i, d;

	if ( nomove() ) {
		if (lookforit && (dice[0] != dice[1])) {
			lookforit = FALSE;
			puts("\008... ");
			switch (level) {
				case 0: { puts("Blocked!");  break; }
				case 1: { puts("Well, no!"); break; }
				case 2: { puts("Thurb!");    break; }
			}
			sleep(10);
			restoreboard();
			update(); 

			/* put the high die in list zero */
			d = dice[0]; dice[0] = dice[1]; dice[1] = d;

			cantuse = ERROR; movesleft = 2; myturns = 1;
			switch (level) {
				case 0: { setchat("I move");  break; }
				case 1: { setchat("Let's try"); break; }
				case 2: { setchat("Move is"); break; }
			}
			debug(chatter);
			prmchk = 12;
			weasel();
			/* the rules say, use both dice if you can, or  */
			/* the highest if one or the other but not both */
		}
		else {
			lookforit = TRUE;
			strcat(chatter," and now I'm blocked ");
			myturns = movesleft = 0;
	}	}
	else if ( book() ) {
		return;
	}
	else if ( pending.flag ) {
		lurch( pending.fr, pending.to, 1 );
		clrpend();
	}
	else if ( endgame() ) {		      /* very solid tactics here!! */
		if ( clockwise( gohome ) )
			return;
		if ( clockwise( nobackgammon ) ) /* no excuse! */
			return;
		if ( clockwise( crosstable ) )
			return;
		if ( clockwise( fiftytworule ) )
			return;
		if ( clockwise( scatter ) )
			return;
		if ( clockwise( run ) ) 
			return;
	}
	else if ( point[ MYBAR ].stones > 0 ) {         /* I'm on the bar! */
		if ( clockwise( hitandrun ) )       /* wreak havoc, please */
			return;
		if ( clockwise( run ) )     /* note: uses low die first */
			return;
	}
	else switch (level) {
		case 0: { villiers(); break; }
		case 1: { louisa(); break; }
		case 2: { torve(); break; }
	}


} /* end: mymove */

/*------------------------------*/
/* end of the GAMEPLAN.C module */
/*------------------------------*/
