[net.sources.games] sources for a draughts playing program

aeb@mcvax.UUCP (Andries Brouwer) (07/26/85)

This is a dam/continental checkers/jeu de dames/draughts playing
program. It adheres accurately to the international rules
(so don't think the program is wrong in case your friends taught
you the game differently).
I wrote this in the times that int was the only data type
and converted it today to something that passes the C compiler
and leaves lint reasonably happy.
People with more spare time could make the program more useful
and more portable by
1. Rewriting prbord.c to use TERMCAP ; right now it only works
   on a hp2640a or on a hardcopy terminal.
2. Rewriting pmesg() to use sprintf() to avoid varargs problems.
Good luck!
#------------------- cut here -----------------------------------

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile damdefs.h dam.c prbord.c prinfo.c readmove.c timeac.c prinfo.x

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
OBJ=dam.o prbord.o prinfo.o readmove.o timeac.o

dam:	$(OBJ)
	cc -o dam $(OBJ)

$(OBJ):	damdefs.h
//E*O*F Makefile//

echo x - damdefs.h
cat > "damdefs.h" << '//E*O*F damdefs.h//'
#define	VERSION	"3.0"
#define	DATE	"770531"

#define	EMPTY	0
#define	WHITE	01000
#define	BLACK	02000
#define	EDGE	03000
#define	DAM	04000
#define	CAPT	0400
#define	FLAG	CAPT
#define	FLAG2	010000
#define	FLAGS	FLAG+FLAG2
#define	COL	WHITE+BLACK
#define	VAL	COL+DAM
#define	MASK	CAPT+COL

/* the values for USER and PDP are related to answer() */
#define	USER	1
#define	PDP	2
#define	UNDEF	-1

#define	DRAWWT	25
#define	PVAL	100
#define	DVAL	350
#define	DMVAL	10

#define	TPVAL	10

#define	GIANT	10000
#define	MVMAX	50
//E*O*F damdefs.h//

echo x - dam.c
cat > "dam.c" << '//E*O*F dam.c//'
/* Copyright (C) A.E. Brouwer, Amsterdam, 1977,1985 */
/* Interactive draugths playing program */
/* Version 1.0, AEB, Easter monday 1977 */
/* Version 2.0, AEB&CLP, 770514 */
/* Version 3.0, AEB&CLP, 771203 */
/* Linted and converted to BSD4.2, AEB, 850725 */

#include	<setjmp.h>
#include	<signal.h>
#include	"damdefs.h"

#define	MSIZE	3000
#define	GSIZE	1000

/*
   Numbering of the board:

	  00  01  02  03  04  05
	05  06  07  08  09  10
	  11  12  13  14  15  16
	16  17  18  19  20  21
	  22  23  24  25  26  27
	27  28  29  30  31  32
	  33  34  35  36  37  38
	38  39  40  41  42  43
	  44  45  46  47  48  49
	49  50  51  52  53  54
	  55  56  57  58  59  60
	60  61  62  63  64  65

   where the fields 0-5, 16, 27, 38, 49, 60-65
   constitute the edge of the board.
   Note: the fields with numbers divisible by 5
   form the main diagonal (see 'value(c)').
   Conversion tables:
 */

int conv[51] = { 0,
	 6, 7, 8, 9,10,
	11,12,13,14,15,
	17,18,19,20,21,
	22,23,24,25,26,
	28,29,30,31,32,
	33,34,35,36,37,
	39,40,41,42,43,
	44,45,46,47,48,
	50,51,52,53,54,
	55,56,57,58,59
};
int reconv[66] = {
	 0,  0,  0,  0,  0,  0,
	   1,  2,  3,  4,  5,
	 6,  7,  8,  9, 10,  0,
	  11, 12, 13, 14, 15,
	16, 17, 18, 19, 20,  0,
	  21, 22, 23, 24, 25,
	26, 27, 28, 29, 30,  0,
	  31, 32, 33, 34, 35,
	36, 37, 38, 39, 40,  0,
	  41, 42, 43, 44, 45,
	46, 47, 48, 49, 50,  0,
	   0,  0,  0,  0,  0
};

int bord[66];
int game[GSIZE];
int *gp  = &game[0];
int moves[MSIZE];
int playb,playw;
int mvnr = 1;		/* odd means black's move */
int endgame = 0;	/* endgame strategy */
int fouragnst1 = 0;	/* four kings against one */
int lasthap = 1;	/* draw if nothing happens for too long */
int drawdel = DRAWWT;	/* 3 or 10 or DRAWWT */
int me   = WHITE;	/* player to move */
int optp = 1;		/* frequency of prbord() */
int optg = 0;		/* nonzero if game */
int optu = 0;	/* print evaluation */
			/* bit0: static evaluation of position
			   bit1: static evaluation of each move
			   bit2: evaluation of position
			   bit3: evaluation of each move
			 */
int optf = -1;	/* file descriptor of ofile */
int userand = 1;
int rdif = 100;		/* cannot be much larger */
int rdifmin = 100;	/* min used during this game */
int rdmin = 1;		/* minimum recursion depth */
int rdmax = 10;		/* maximum recursion depth */
int fplevel = 1;	/* level of search for free paths */
int captct,captmax, *mpf,*mp0,*mp,possct;
extern int *findmove(), *readmove();
extern long lseek();
int debug,crownmv,altermv;
int rescct;
int wpct,bpct,wkct,bkct;	/* global */
int timeb,timew;	/* time used by b/w (in seconds) */
int timemax  = 10;	/* nr of seconds allowed per move */

extern int value2(),value(),evalue();
int (*avalue)() = value;
int (*stratw)(),(*stratb)();
int (*(strategies[]))() = { value, value2, evalue };

extern int pb,disp,dirt,prall,prbwait;
extern char line[];
extern int rdbord[];

setstrat(w,b) int w,b; {
register int i;
	i = sizeof(strategies)/2;
	if(w >= i || b >= i){
		pmesg("no such strategies\n");
		return;
	}
	stratw = strategies[w];
	stratb = strategies[b];
	pmesg("strategies %d(w),%d(b)\n",w,b);
}

intrup(){
	(void) signal(SIGINT,SIG_IGN);		/* no more interrupts */
	if(disp) delay();
	pmesg("?");
	rdlin();
	rdcomd(line);
	(void) signal(SIGINT,intrup);
	if(!mpf){
		pmesg("you cannot change the board during an interrupt\n");
		reset();
	}
}

int intflg = 0;	/* interrupt while non-interruptable */

intrdp(){
	intflg++;
	(void) signal(SIGINT,intrdp);
}

jmp_buf env;

main(argc,argv)
int argc;
char **argv;
{
	int p;
	char *ac;

	initterm();
	printf("dam %s version %s\n",VERSION,DATE);

	optf = open(".outdam",1);	/* use it if it exists */
	if(optf >= 0) (void) lseek(optf, 0L, 2);	/* seek eof */

	setstrat(0,1);

	if(argc == 1){
		printf("do you want info? (y/n)\n");
		if(answer()) prinfo();
	}
	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, intrup);
	if(setjmp(env)) ; /* Program was reset */
	if(mvnr > 1){
		prbord();
		rdcomd("t");	/* report time used */
		if(optf > 0) outgame();
		printf("another game?\n");
		if(!answer()) exit(0);
	}
	sleep(2);
	init();
	/* 2nd time here argc will be one */
	while(--argc) {
		ac = *++argv;
		if(*ac == '-') ac++;
		switch(*ac){
		case 'p':
			optp = atoi(++ac);
			break;
		case 'g':
			optg++;
			break;
		case ':':
			debug++;
			break;
		case '\0':
			/* do not use random generator */
			userand = 0;
			printf("no random generator used\n");
			break;
		default:
			rdcomd(ac);
		}
	}
	argc++;
	init2();
	while(1){
		mvnr++;
		if(optu&3) prsteval();
		timebeg();

		p = ((me == WHITE) ? playw : playb);
		if(p == USER) domove(readmove(me));
		else domove(findmove(me));

		if(me == WHITE) timew += timedif();
		else timeb += timedif();

		if(optp) if(mvnr%optp == 0) prbord();

		me = COL-me;
	}
}

reset(){
	longjmp(env,1);
}

init(){
register int i;
	/* check correctness of conv and reconv */
	for(i=1; i<=50; i++)
		if(reconv[conv[i]] != i)
			error("conv error");
	/* fill edge of the board */
	for(i=0; i<66; i++)
		if(reconv[i] == 0) bord[i] = EDGE;
	/* initial board position */
	for(i=1; i<=20; i++)
		bord[conv[i]] = BLACK;
	for(i=21; i<=30; i++)
		bord[conv[i]] = EMPTY;
	for(i=31; i<=50; i++)
		bord[conv[i]] = WHITE;
	me = WHITE;	/* white to move */
	rdbord[0] = 0;	/* not from file */
	timew = timeb = 0;
	lasthap = mvnr =1;
	endgame = fouragnst1 = 0;
	crownmv = altermv = 0;
	mpf = &moves[0];
	gp = &game[0];
	prall++;
}

init2(){
	if(!playb && !playw){
		printf("do you want me to play black? (y/n)\n");
		playb = answer()+1;
		printf("do you want me to play white? (y/n)\n");
		playw = answer()+1;
		dirt++;
	} else {
		if(!playb) playb = USER;
		if(!playw) playw = USER;
	}

	if(!mpf) mpf = moves; else
	if(optp)prbord();
}

/* fatal error routine */
error(s) char *s; {
	printf("%s\n",s);
	exit(0);
}

/* create list of all possible moves */

move(c) int c; {
register int i;
int c0;

	possct = 0;
	captmax = captct = 0;
	mp = mp0 = mpf;
	for(i=6; i<60; i++) if(((c0 = bord[i]) & COL) == c) {
		bord[i] = EMPTY;
		mp += 3;
		*mp0 = i+c0;
		capt(c0,i);
		mp -= 3;
		bord[i] = c0;
	}
	if(!captmax) {
		if(c == BLACK) {
		for(i=6; i<60; i++) if(((c0 = bord[i]) & COL) == c) {
			if(c0 == BLACK) {
				hmove(c0,i,5);
				hmove(c0,i,6);
			} else {
				dmove(c0,i,-6);
				dmove(c0,i,-5);
				dmove(c0,i,5);
				dmove(c0,i,6);
			}
		}} else
		for(i=59; i>5; i--) if(((c0 = bord[i]) & COL) == c) {
			if(c0 == WHITE) {
				hmove(c0,i,-5);
				hmove(c0,i,-6);
			} else {
				dmove(c0,i,-6);
				dmove(c0,i,-5);
				dmove(c0,i,5);
				dmove(c0,i,6);
			}
		}
	}
	if(mp > &moves[MSIZE]) error("overflow moves array");
}

/* king move in specified direction */
dmove(c,pos,dir) int c,pos,dir; {
register int i;
	for(i = pos+dir; bord[i] == EMPTY; i += dir){
		*mp++ = pos+c;
		*mp++ = i+c;
		*mp++ = 0;
		possct++;
	}
}

/* piece move */
hmove(c,pos,dir) int c,pos,dir; {
register int i;
	if(bord[i = pos+dir] == EMPTY){
		*mp++ = pos + c;
		*mp++ = prom(c,i);
		*mp++ = 0;
		possct++;
	}
}

/* partial capture in specified direction */
hcapt(c,pos,dir) int c,pos,dir; {
register int i,j;
int c1;
	if(c&DAM){
		for(i=pos+dir; (c1=bord[i]) == EMPTY; i += dir);
		if((c1 & MASK) != DAM+COL-c) return;
		j = i;
		bord[i] |= CAPT;
		captct++;
		*mp++ = i+c1;
		while(bord[j += dir] == EMPTY)
			capt(c,j);
		bord[i] = c1;
		captct--;
		mp--;
	} else {
		c1 = bord[i = pos+dir];
		if((c1 & MASK) != COL-c) return;
		if(bord[j=i+dir] != EMPTY) return;
		bord[i] |= CAPT;
		captct++;
		*mp++ = i+c1;
		capt(c,j);
		bord[i] = c1;
		captct--;
		mp--;
	}
}

capt(c,pos) int c,pos; {
register int *mp1;
	hcapt(c,pos,-6);
	hcapt(c,pos,-5);
	hcapt(c,pos,5);
	hcapt(c,pos,6);
	if(captct == captmax){
		if(!captct) return;
		possct++;
		*++mp0 = prom(c,pos);
		*++mp0 = captct;
		mp0 -= 2;
		mp1 = mp;
		do { *mp++ = *mp0++; } while (mp0 != mp1);
		return;
	} else if(captct > captmax){
		captmax = captct;
		possct = 1;
		mp1 = mpf;
		*mp1++ = *mp0;
		*mp1++ = prom(c,pos);
		*mp1++ = captct;
		mp0 += 3;
		do { *mp1++ = *mp0++; } while(mp0 != mp);
		mp0 = mpf;
		mp = mp1;
		do { *mp1++ = *mp0++; } while(mp0 != mp);
		mp = mp1;
		return;
	}
}

prom(c,pos) int c,pos; {
	if(c&WHITE) {
		if(pos < 11) c |= DAM;
	} else	if(pos > 54) c |= DAM;
	return(pos+c);
}

/* execute actual move */
domove(mvpt) int *mvpt; {
register int i;
register int *mp1;
int ct,ctd,cte;
int difflist[66];
	/* store move in array game */
	mp1 = mvpt;
	*gp++ = *mp1++;
	*gp++ = *mp1++;
	*gp++ = ct = *mp1++;
	while(ct--) *gp++ = *mp1++;
	if(gp+4 > &game[GSIZE]) error("overflow game array");

	/* change board position */
	mp1 = mvpt;
	bord[((*mp1++) & 0377)] = EMPTY;
	i = *mp1 & VAL;
	bord[((*mp1++) & 0377)] = i;
	ct = *mp1++;
	if(ct || !(*mvpt & DAM)) lasthap = mvnr;
	while(ct--) bord[((*mp1++) & 0377)] = EMPTY;

	/* check for repetition of moves */
	ct = cte = 0;
	ctd = mvnr-lasthap;
	if(ctd > 3){
		for(i=0; i<66; i++) difflist[i]=0;
		mp1 = gp;
		while(ctd--){	/* go back one move */
			if(*--mp1) error("capture after lasthap");
			i = *--mp1;
			if(i & (difflist[i & 0377] ^= (i&COL)))
				ct++;
			else	ct--;
			i = *--mp1;
			if(i & (difflist[i & 0377] ^= (i&COL)))
				ct++;
			else	ct--;
			if(difflist[0] ^= 1) ct++;
			else ct--;
			if(!ct) cte++;
		}
		if(cte == 2){
			pmesg("\nDraw by repetition of moves\n");
			reset();
		} else if(cte == 1) {
			pmesg("\nThis is the 2nd occurrence\
 of this position\n");
			pb = 1;
		}
	}
	/* check for three kings against one */
	pdistr();
	if((!wpct) && (!bpct) && (wkct+bkct == 4) && ((wkct&bkct) == 1)){
		drawdel = 10;
		ct = (wkct==1 ? WHITE : BLACK);
		/* single king on main diagonal? */
		for(i=10; i<60; i += 5)
			if(bord[i] & ct){
				drawdel = 3;
				break;
			}
	}
	if(mvnr-lasthap > drawdel*2){
		pmesg("\nDraw since nothing happened for %d moves\n",
			drawdel);
		reset();
	}
	if(wkct+bkct && wpct+bpct<12) {
		if(!endgame) setstrat(2,2);		/* %% */
		endgame++;
	}
	if(endgame && ((wkct > 3) && (bkct < 2) ||
			(bkct > 3) && (wkct < 2)))
		fouragnst1++;
}

/* he asks for remise, shall we accept it? */
/* very stupid algorithm %% */
remise(){
	if(mvnr-lasthap > drawdel*2){
		pmesg("I accept\n");
		reset();
	} else {
		pmesg("I reject\n");
	}
}

pdistr(){	/* find distribution of white and black pieces */
register int i;
	wpct = wkct = bpct = bkct = 0;
	for(i=6; i<60; i++) switch(bord[i] & VAL){
	case WHITE:
		wpct++;
		break;
	case BLACK:
		bpct++;
		break;
	case WHITE|DAM:
		wkct++;
		break;
	case BLACK|DAM:
		bkct++;
		break;
	default:
		break;
	}
}

crown(c) int c; {
register int i,v;
int pct,kct,kseen,save_i[2],sk;
	pct = kct = kseen = 0;
	for(i=1; i<=50; i++){
		v = bord[conv[i]] & VAL;
		if(v == EMPTY) continue;
		if(v & c){
			if(kseen++ || (!(v & DAM))) goto err;
			else sk = i;
		} else {
			if(v & DAM) kct++;
			else if(pct == 2) goto err;
			else save_i[pct++] = i;
		}
	}
	if((kct + pct == 3) && pct){
		drawdel = ((sk/5)*5 == sk ? 3 : 10);
		lasthap = crownmv = mvnr;
		pmesg("%d pieces crowned to king\n",pct);
		pmesg("it will be a draw in %d moves\n",drawdel);
		while(pct--){
			bord[conv[save_i[pct]]] |= DAM;
		}
	} else {
	err:
		pmesg("crowning my pieces to a king is allowed only\n");
		pmesg("if I have 3 pieces among which 1 or 2 kings\n");
	}
}

int rdepth;
int res0,res1,res2;

/* allowed difference with optimal move */
#define	Q	30

int*
findmove(c) int c; {
int	r,res;
struct mvv {
	int *mvp,val0,val1;
} mvvals[MVMAX], *mvg;
register struct mvv *mvvp;
register int *mp1;

	rescct = 0;
	move(c);
	if(!possct){
		pmesg("I lost\n");
		reset();
	}
	if(possct == 1){
		prmove(mpf);
		return(mpf);
	}
	avalue = (me == WHITE ? stratw : stratb);
	rdepth = 0;
	res2 = -1;
	/* first look to a short depth */
	rdif -= 40;
	rdmin--;
	res = -GIANT;
	mvvp = mvvals;
	for(mp1 = mpf; mp1<mp; ){
		r = result(mp1,res-Q,GIANT);
		if(r < -GIANT) r = -GIANT;
		if(r > res) res = r;
		mvvp->mvp = mp1;
		mvvp++->val0 = r;
		if(mvvp >= mvvals+MVMAX-1){
			pmesg("error in findmove: MVMAX too small\n");
			break;
		}
		mp1 += 2;
		mp1 += (*mp1++);
	}
	/* end of list */
	mvvp->mvp = 0;

	/* sort mvvals */
	for(mvvp=mvvals; mp1=(mvvp+1)->mvp; mvvp++){
		res1 = (mvvp+1)->val0;
	test:
		if(res1 > (res0 = mvvp->val0)){
			/* switch */
			(mvvp+1)->mvp = mvvp->mvp;
			(mvvp+1)->val0 = res0;
			mvvp -> mvp = mp1;
			mvvp -> val0 = res1;
			if(mvvp > mvvals){
				mvvp--;
				goto test;
			}
		}
	}
	/* short term result */
	res0 = res;
	/* now look to full depth */
	mvvp = mvvals;
	rdif += 40;
	rdmin++;
	res = -GIANT-1;		/* force assignment to mvg */
	while(mp1 = mvvp->mvp){
		r = result(mp1,res-Q,GIANT);
		if(r < -GIANT) r = -GIANT;
		if(r > res){
			res = r;
			mvg = mvvp;
		/*	gdct = 1;	*/
		}
	/*	else if(r == res)if(random(++gdct)==0) mvg=mvvp; %% */
		mvvp++ -> val1 = r;
	}
	/* long term result */
	res1 = res;

	/* final result good? */
	if(res1 >= res0-Q) goto ret;

	/* or short term result not worse than for the others? */
	if(mvg->val0 >= res0-30) goto ret;

	/* this seems a bad move at first sight;
	   see whether it is really necessary */
	rdif += (mp-mpf);
	res2 = result(mvg->mvp, res1-PVAL, res1+PVAL);
	rdif -= (mp-mpf);
	if(res2 >= res1-Q) goto ret;

	/* a bad move after all, take the one that seems best */
	r = -GIANT-1;
	mvvp = mvvals;
	while(mvvp->val0 >= res0-Q){
		if(mvvp->val1 > r){
			r = mvvp->val1;
			mvg = mvvp;
		}
		mvvp++;
	}
ret:
	if(optu&8){
		disp = 0;
		/* print value of each move */
		mvvp = mvvals;
		while(mp1 = mvvp->mvp){
			prmove(mp1);
			printf(mvvp == mvg ? "####" : "****");
			printf(" %d %d\n",mvvp->val0,mvvp->val1);
			mvvp++;
		}
		putchar('\n');
	}
	prmove(mvg->mvp);
	if(optu&4) preval(mvg->val1);
	return(mvg->mvp);
}

preval(r) int r; {
	pmesg("value: %d\n",r);
	pmesg("res[i]: %d %d %d\n",res0,res1,res2);
	pmesg("#calls: %d\n",rescct);
}

/* deliver either evaluation or answer '<a' or '>b' */
result(mvpt,a,b) int *mvpt,a,b; {
register int i;
register int *mp1;
int c,ct0,ct,r,res,*mpf0,fp;
	rescct++;
	mp1 = mvpt;
	c = *mp1 & COL;
	bord[((*mp1++) & 0377)] = EMPTY;
	i = *mp1 & VAL;
	bord[((*mp1++) & 0377)] = i;
	ct0 = ct = *mp1++;
	while(ct--) bord[((*mp1++) & 0377)] = EMPTY;

	if(!ct0) rdepth++;
	fp = (rdepth/2 == fplevel ? freepath(c) : 0);
	a += fp;
	b += fp;

	if(ct0 || (rdepth<rdmin)) goto go_on;
	if((mp-moves > rdif) || (rdepth >= rdmax)) goto eval;
	goto go_on;

eval:
	res = (*avalue)(c);
	goto unmove;

go_on:
	mpf0 = mpf;
	mpf = mp;
	move(COL-c);
	res = b+1;
	for(mp1=mpf; mp1<mp; ){
		r = -result(mp1,-res,-a);
		if(r < res){
			res = r;
			if(res < a) break;
		}
		mp1 += 2;
		mp1 += (*mp1++);
	}
	mp = mpf;
	mpf = mpf0;

unmove:
	mp1 = mvpt;
	i = *mp1++;		/* be careful; perhaps start and
				   final position are the same! */
	bord[((*mp1++) & 0377)] = EMPTY;
	bord[i & 0377] = i & VAL;
	ct = *mp1++;
	while(ct--){
		i = *mp1 & VAL;
		bord[((*mp1++) & 0377)] = i;
	}
	
	if(!ct0) rdepth--;
	return(res-fp);
}

/* evaluation of position after recursion */
int info[66] = {
	 0,  0,  0,  0,  0,  0,
	   1,  1,  1,  1,  1,
	 2,  2,  2,  2,  2,  0,
	   3,  3,  3,  3,  3,
	 4,  4,  4,  4,  4,  0,
	   5,  5,  5,  5,  5,
	 6,  6,  6,  6,  6,  0,
	   7,  7,  7,  7,  7,
	 8,  8,  8,  8,  8,  0,
	   9,  9,  9,  9,  9,
	10, 10, 10, 10, 10,  0,
	   0,  0,  0,  0,  0
};

/* valuations for the columns: index is fldnr%11 */
/* weight center */
int valar1[11] = {
	0, 2, 4, 3, 1, UNDEF, 1, 3, 4, 2, 0
};

/* weight distribution */
int valar2[11] = {
	-5, -3, -1, 2, 4, UNDEF, -4, -2, 1, 3, 5
};

value(c){
int sum,v,oc;
register int d,*bp,*ip;
	oc = COL - c;
	sum = 0;
	ip = &info[6];
	for(bp = &bord[6]; bp < &bord[60]; ){
		d = *bp++ & VAL;
		v = *ip++;
		if(d == EMPTY) continue;
		if(d == EDGE) continue;
		if(d&DAM){
			v = DVAL;
			/* increase value if on main diagonal */
			if((bp-bord)%5 == 1) v += DMVAL;
		} else {
			if(d == WHITE) v = 11-v;
			v += PVAL;
		}
		if(d & oc) v = -v;
		sum += v;
	}
	return(sum);
}

/* evaluation of position after recursion */
/* value of tripods : the value of the field they point to */
int info2[66] = {
	 0,  0,  0,  0,  0,  0,
	   1,  1,  1,  1,  1,
	 1,  1,  1,  1,  1,  0,
	   5,  7,  8,  6,  2,
	 2,  7,  9,  8,  6,  0,
	   7,  9, 10,  8,  2,
	 2,  9, 11, 10,  8,  0,
	   2,  2,  2,  2,  2,
	 2,  2,  2,  2,  2,  0,
	   2,  2,  2,  2,  2,
	 2,  2,  2,  2,  2,  0,
	   0,  0,  0,  0,  0
};

int vcentrw,vcentrb,vequidw,vequidb,vtempw,vtempb;

value2(c){
int sum,v,oc;
register int d,*bp,i;
	oc = COL - c;
	sum = 0;
	vcentrw = vcentrb = vequidw = vequidb = vtempw = vtempb = 0;
	for(bp = bord; bp < &bord[6]; bp++){
		sum += trip(5,BLACK,bp);
		sum += trip(6,BLACK,bp);
	}
	if(c == WHITE) sum = -sum;
	for(i=6; i<60; (i++,bp++)){
		d = *bp & VAL;
		if(d == EMPTY) continue;
		if(d == EDGE) {
			v = trip(-5,WHITE,bp);
			v += trip(-6,WHITE,bp);
			v -= trip(5,BLACK,bp);
			v -= trip(6,BLACK,bp);
			sum += (c == WHITE ? v : -v);
			continue;
		}
		if(d&DAM){
			v = DVAL;
			/* increase value if on main diagonal */
			if((i/5)*5 == i) v += DMVAL;
		} else {
			v = PVAL;
			if(d == WHITE){
				vtempw += info[65-i];
				v += trip(-5,WHITE,bp);
				v += trip(-6,WHITE,bp);
				vequidw += valar2[i%11];
				vcentrw += valar1[i%11];
			} else {
				vtempb += info[i];
				v += trip(5,BLACK,bp);
				v += trip(6,BLACK,bp);
				vequidb += valar2[i%11];
				vcentrb += valar1[i%11];
			}
		}
		if(d & oc) v = -v;
		sum += v;
	}
	for( ;bp < &bord[66]; bp++){
		v = trip(-5,WHITE,bp);
		v += trip(-6,WHITE,bp);
		sum += (c == WHITE ? v : -v);
	}
	if(vequidw < 0) vequidw = -vequidw;
	if(vequidb < 0) vequidb = -vequidb;

	v = vcentrw-vcentrb-vequidw+vequidb+vtempw-vtempb;
	sum += (c == WHITE ? v : -v);
	return(sum);
}

evalue(c){
int sum,v,oc,i;
register int d,*bp,*ip;
	oc = COL-c;
	sum = 0;
	ip = &info[6];
	for(bp = &bord[6]; bp < &bord[60]; ){
		d = *bp++ & VAL;
		v = *ip++;
		if(d == EMPTY) continue;
		if(d == EDGE) continue;
		/* if opponent has a king then traps are valuable */
		if((d==WHITE && bkct) || (d==BLACK && wkct))
			v += trap(-5,d&COL,bp)
			   + trap(-6,d&COL,bp)
			   + trap( 5,d&COL,bp)
			   + trap( 6,d&COL,bp);
		if(d&DAM) {
			v = DVAL;
			if((bp-bord)%5 == 1) v += DMVAL;
			if(fouragnst1){
				i = (bp-bord)%6;
				if(i <= 1) v += DMVAL;
				switch(bp-bord){
				case 6+1:
				case 11+1:
				case 54+1:
				case 59+1:
					v += DMVAL/2;
				}
			}
		} else {
			if(d == WHITE) v = 11-v;
			v += PVAL;
		}
		if(d & oc) v = -v;
		sum += v;
	}
	return(sum);
}

prsteval(){
int rdmax0,rdmin0;
register int *mp1;
	if(optu&1) printf("static eval: %d\n",value2(me));
	if(optu&2){
		rdmax0 = rdmax;
		rdmin0 = rdmin;
		rdmax = rdmin = 1;
		rdepth = 0;
		disp = 0;
		avalue = value2;
		move(me);
		for(mp1=mpf; mp1<mp; ){
			printf("%4d  ", result(mp1,-GIANT,GIANT));
			printf("(%2d %2d %2d / %2d %2d %2d)	",
				vtempw-vtempb,vcentrw,vequidw,
				vtempb-vtempw,vcentrb,vequidb	);
			prmove(mp1);
			if((mvnr&1)==0) putchar('\n');	/* %% */
			mp1 += 2;
			mp1 += (*mp1++);
		}
		printf("\n");
		rdmax = rdmax0;
		rdmin = rdmin0;
	}
}

/* test for X XY where Y=X or Y=Edge */
trap(dir,col,nbp) int dir,col,*nbp; {
register int *bp,w,v;
	bp = nbp+dir;
	if(*bp != EMPTY) return(0);
	bp += dir;
	if(((w = *bp)&COL) != col) return(0);
	bp += dir;
	if(!(*bp & col)) return(0);
	/* value is number of empty fields covered by trap */
	v = 0;
	bp = nbp-dir;
	while(*bp == EMPTY){
		v += TPVAL;
		bp -= dir;
	}
	/* extra bonus if base of trap is a king */
	if(w & DAM) v += (v/2);
	return(v);
}

trip(dir,col,nbp) int dir,col,*nbp; {
register int *bp;
int tv1,tv2;
	tv2 = 0;
	bp = nbp+dir;
	if(*bp != col) return(0);
	bp += dir;
	if(*bp != col) return(0);
	tv1 = (col == WHITE ? info2[65-(bp+dir-bord)] : info2[bp+dir-bord]);
	if(bp[dir] == col) tv1 >>= 1;
	if(dir < 0) dir = -dir;
	dir = 11-dir;		/* interchange 5 and 6 */
	if(bp[dir] == col && ((bp[dir+dir] & col) == 0)) tv2++; else
	if(bp[-dir]== col && ((bp[-dir-dir]& col) == 0)) tv2++;
	return(tv2 ? 2 : tv1);
}

/* check for free path (for oc) to a king */
int freect,frlngt,flth;	/* 'real' free path */
int freect2,frlngt2,fbcct;	/* free paths discarding backward captures */
int fpval[11] = {
	0,		/* 0 cannot occur */
	PVAL,		/* 1 is already seen as a king in recursion %% */
	PVAL,		/* 2 */
	PVAL-10,	/* 3 */
	PVAL-20,	/* 4 */
	PVAL/2,		/* 5 */
	30,30,		/* 6,7 */
	15,15,		/* 8,9 */
	0		/* 10, i.e. no free paths */
};

int fpval2[11] = {
	0, 0,		/* 0,1 cannot occur */
	PVAL/2,		/* 2 */
	PVAL/3,		/* 3 */
	10,10,		/* 4,5 */
	5,5,		/* 6,7 */
	2,2,		/* 8,9 */
	0		/* 10 */
};

freepath(c) int c; {
register int *bp,*nbp,i;
int nbord[66],oc;

	oc = COL-c;

	/* copy */
	bp = bord;
	nbp = nbord;
	while(bp < &bord[66]) *nbp++ = *bp++;

	/* swell */
	for(nbp = &nbord[6]; nbp < &nbord[60]; nbp++){
		if((*nbp & COL) == c){
			if(*nbp & DAM) {
				dmark(nbp, -6);
				dmark(nbp, -5);
				dmark(nbp, 5);
				dmark(nbp, 6);
			} else {
				if(c == WHITE){
					nbp[-6] |= FLAGS;
					nbp[-5] |= FLAGS;
					nbp[5] |= FLAG;
					nbp[6] |= FLAG;
				} else {
					nbp[-6] |= FLAG;
					nbp[-5] |= FLAG;
					nbp[5] |= FLAGS;
					nbp[6] |= FLAGS;
				}
			}
			*nbp = EDGE;
		}
	}

	/* but note: points on the border are always safe */
	nbp = nbord;
	for(i = 6; i<10; i++) nbp[i] &= VAL;
	for( ; i<65; i += 10){
		nbp[i++] &= VAL;
		nbp[i] &= VAL;
	}
	for(i = 56; i<60; i++) nbp[i] &= VAL;

	/* determine strong component of last line */
	freect = flth = freect2 = fbcct = 0;
	frlngt = frlngt2 = 10;	/* shortest freepath: +inf */
	if(c == WHITE){
		for(nbp = &nbord[55]; nbp < &nbord[60]; nbp++)
			reach(oc,nbp,-6);
	} else	for(nbp = &nbord[6]; nbp < &nbord[11]; nbp++)
			reach(oc,nbp,5);

	if(debug){		/* %% */
		pmesg("fct: %d, fct2: %d, fl: %d, fl2: %d,\
flth: %d=0, fbcct: %d=0\n",
			freect,freect2,frlngt,frlngt2,flth,fbcct);
	}
	if(freect) return(fpval[frlngt]);
	return(fpval2[frlngt2]);
}

dmark(nbp,d) int *nbp,d; {
register int *bp;
	bp = nbp + d;
	while((*bp & COL) == EMPTY){
		*bp = FLAGS;
		bp += d;
	}
}

reach(oc,nbp,d) int oc,*nbp,d; {
register int *bp,v;
	bp = nbp;
	v = *bp & VAL;
	if(v == EDGE) return;
	if(v == oc) {
		if(fbcct == 0){
			freect++;
			if(frlngt > flth) frlngt=flth;
		} else {
			freect2++;
			if(frlngt2 > flth) frlngt2=flth;
		}
	}
	if(v != EMPTY) return;	/* %% */
	if((*bp & FLAG) == 0){
		flth++;
		reach(oc,bp+d,d);
		reach(oc,bp+d+1,d);
		flth--;
		if(fbcct == 0) *bp = EDGE;
		else *bp |= FLAG2;	/* dont look for semifree paths */
	} else if((*bp & FLAG2) == 0){
		fbcct++;
		flth++;
		reach(oc,bp+d,d);
		reach(oc,bp+d+1,d);
		flth--;
		fbcct--;
		*bp = EDGE;	/* dont return to this field */
	} else	*bp = EDGE;
}

/* construct position after move k */
backup(k) int k; {
register int i,*gp0;
int mvno,c,ct;
	/* the validity of k was checked by the caller */
	if(rdbord[0])
		for(i=6; i<60; i++){
			if(bord[i] != EDGE) bord[i] = rdbord[i];
		}
	else {
		for(i=1; i<=20; i++) bord[conv[i]] = BLACK;
		for(i=21;i<=30; i++) bord[conv[i]] = EMPTY;
		for(i=31;i<=50; i++) bord[conv[i]] = WHITE;
	}
	gp0 = game;
	mvno = mvnr;
	mvnr = 1;
	while(k--){
		mvnr++;
		c = *gp0 & COL;
		if(mvnr == crownmv) crown(c); /* yields output %% */
		bord[(*gp0++) & 0377] = EMPTY;
		i = *gp0 & VAL;
		bord[(*gp0++) & 0377] = i;
		ct = *gp0++;
		while(ct--) bord[(*gp0++) & 0377] = EMPTY;
	}
	mvnr++;
	if((mvno+mvnr) & 1) me = COL-me;
	mpf = 0;	/* the position has changed */
	gp = gp0;
	if(lasthap > mvnr) lasthap = mvnr;
	if(crownmv > mvnr) crownmv = 0;
}

/*
 * Implemented rules:
 *
 * a) If the same position occurs thrice (with the same
 *    player to move) it is a draw.
 * b) If a player has 3 pieces among which at least one
 *    king and his opponent has one king only, his opponent
 *    may crown all these pieces (which action does not
 *    count for a move). Immediately rule c) becomes applicable.
 * c) If the position is 3 kings against 1 king then after
 *	(single king on main diagonal ? 3 : 10)
 *    moves the game ends in a draw.
 *
 * In addition to these we use for PDP against PDP play the rule:
 *
 * z) If during DRAWWT moves no piece has been captured and only
 *    kings have moved it is a draw.
 *
 */
//E*O*F dam.c//

echo x - prbord.c
cat > "prbord.c" << '//E*O*F prbord.c//'
#include	"damdefs.h"

/* display routines for dam.c */
#define	DELAY	7

extern int mvnr,game[],conv[],reconv[],bord[],*gp;
extern int timew,timeb,optg,playw,playb;
int ospeed;

int pb = 1;	/* nonzero if at begin of line */
int disp = 0;	/* nonzero if HP display desired */
int dirt = 0;	/* nonzero if garbage printed below board */
int prall = 1;	/* if nonzero the display is rewritten */

char prtarr[] = ".wzWZ?";
char *dsparr[] = {
	"\033&dJ  \0xxx",
	"\033&dJO \0xxx",
	"\033&dJX \0xxx",
	"O \0xxxxxxxxxx",
	"X \0xxxxxxxxxx",
	"? \0xxxxxxxxxx"
};

char *emparr[] = {
/* 0 */	"\033&dB  ",
/* 1 */	"\033&dJ  ",
/* 2 */	"\033&dJ%2d",
/* 3 */	"\033&dJ\016%2d\017",
/* 4 */	"\033&d@\016%2d\017",
	0 };

char *fularr[] = {
/* 0 */	"w ","z ","W ","Z ",
/* 1 */	"w ","z ","WW","ZZ",
/* 2 */	"w ","b ","W ","B ",
/* 3 */	"o ","x ","O ","X ",
/* 4 */	"\016V \017","\016$ \017","\016VV\017","\016$$\017",
/* 5 */	"\016[ \017","\016_ \017","\016[[\017","\016__\017",
/* 6 */	"\033&dJO ","\033&dJX ","O ","X ",
	0 };

/* cursor home and clear screen */
home(){
	printf("\033H\033J");
	dirt = 0;
}

/* position cursor */
int lastrow;
posit(l,c) int l,c; {
	printf("\033&a%dr%dC",l,c);
	lastrow = l;
	delay();
}

/* print string inverted (i.e. against white background) */
/* make sure no greek chars are printed */
whstr(s) char *s; {
	printf("\033&dB\017%s\033&d@",s);
	delay();
}

/* revert to black */
blstr(){
	printf("\033&d@");
	delay();
}

/* delay is necessary after changes of color or position */
delay(){
register int ct;
	ct = DELAY;
	if(ospeed == 300) ct = 1;
	while(ct--) putchar('\177');
}

int	col1 = 26;
int	col2 = 56;
#define	COLW	14	/* width of column of moves */
#define	ROW0	16	/* row of conversation below the board */

prmove(mvpt) int *mvpt; {
int fin,ct,inh;
	prmvnr();
	prnum(*mvpt++);
	fin = *mvpt++;
	ct = *mvpt++;
	inh = (ct>1 ? 1 : 0);
	if(!ct){
		putchar('-');
		prnum(fin);
	} else {
		putchar('x');
		prnum(fin);
		putchar('(');
		while(--ct){
			prnum(*mvpt++);
			putchar(',');
		}
		prnum(*mvpt++);
		putchar(')');
	}
	pb = mvnr&1;
	pb |= inh+optg;
	putchar(pb ? '\n' : '\t');
}

prnum(num) int num; {
	printf("%2d", reconv[ num & 0377 ]);
}

/* define printing position before printing a move */
int rowdif;

prmvnr(){
int cl,rw,clr;
	clr = 0;
	rw = (mvnr>>1) + rowdif;
	if((mvnr&1) && pb) rw++;
	while(rw > 88) rw -= 44;
	if(rw > 66){
		rw -= 66;
		cl = col2;
		clr = 80-col2;
	} else if(rw > 44){
		rw -= 44;
		cl = col1;
		clr = col2-col1;
	} else if(rw > 22){
		rw -= 22;
		cl = col2;
	} else	cl = col1;
	if(mvnr&1){
		if(pb){
			rowdif++;
			if(clr) clear(rw,cl,clr);
			if(disp) posit(rw,cl);
			printf("%2d.  ...\t", mvnr>>1);
		}
		if(disp) posit(rw, cl+COLW);
	} else {
		if(clr) clear(rw,cl,clr);
		if(disp) posit(rw, cl);
		printf("%2d. ", mvnr>>1);
	}
}

/* clear the line below the given line, and
   the given line only if it is line 1 */
clear(rw,cl,ct) int rw,cl,ct; {
register int i;
	if(!disp) return;
	if(rw == 1){
		posit(rw,cl);
		for(i=ct; i--; ) putchar(' ');
	}
	posit(rw+1,cl);
	for(i=ct; i--; ) putchar(' ');
}

int prev_bord[66];	/* remember, and print differences only */
unsigned prbwait = 0;	/* delay after each prbord() */

pbundef(){	/* force printing of entire board */
register int i;
	for(i=0; i<66; i++) prev_bord[i] = -1;
}

prbord(){
register int i,*mp1;
int bdi,xi,yi;
	if(prall) {
		if(disp) home();
		pbundef();
	}
	if(!disp){
		if(!pb){
			putchar('\n');
			pb++;
		}
		putchar('\n');
	}
	if(disp){	/* write times above board */
		posit(1,5);
		printf("%s%4d \016:\017 %4d %s",
			dsparr[3],timew,timeb,dsparr[4]);
		if(prall) printf("\n\n      \0161   2   3   4   5\017\n");
	}
	for(i=1; i<=50; i++){
		bdi = bord[conv[i]];
		if(disp && (prev_bord[conv[i]] == bdi)) continue;
		if((i/10)*10+1 == i)
			if(!disp) putchar(' ');
		switch(bdi){
		case EMPTY:
			bdi = 0; break;
		case WHITE:
			bdi = 1; break;
		case BLACK:
			bdi = 2; break;
		case WHITE+DAM:
			bdi = 3; break;
		case BLACK+DAM:
			bdi = 4; break;
		default:
			bdi = 5;
		}
		if(disp) {
			/* first compute position */
			yi = (i-1)/5;
			xi = 4*(i - 5*yi) - 2;
			if(yi&1) xi--; else xi++;
			/* then display it there */
			posit(yi+4, xi+2);
			if(bdi) whstr(dsparr[bdi]);
			else {
				printf(dsparr[0],i);
				blstr();
			}
			if((i%5 == 0) && (i%10 != 0))
				printf("\016%2d\017\n \016%2d\017",i,i+1);
		} else {
			putchar(prtarr[bdi]);
			if(i%5 == 0) putchar('\n');
			else putchar(' ');
		}
	}
	if(disp && prall)
		printf("\n  \016 46  47  48  49  50\017\n");
	if(prall) {
		prall = 0;
		if(disp){
			rowdif = 0;
			i = mvnr;	/* save temporarily */
			mvnr = 1;
			for(mp1 = game; mp1<gp; ){
				mvnr++;
				prmove(mp1);
				mp1 += 2;
				mp1 += *mp1++;
			}
			mvnr = i;
		}
	} else
	for(i = ROW0+dirt-2; i >= ROW0-1; i--)
		clear(i,0,col1);
	dirt = 0;
	if(disp) posit(ROW0,0);
	else putchar('\n');
	for(i=0; i<66; i++) prev_bord[i] = bord[i];
	if(prbwait) sleep(prbwait);
}

prposit(){
	prlist("w",WHITE);
	prlist("z",BLACK);
	prlist("wd",WHITE|DAM);
	prlist("zd",BLACK|DAM);
	printf(".\n");
}

prlist(s,c) char *s; int c; {
register int i;
int seen;
	seen = 0;
	for(i=1; i<=50; i++) if(bord[conv[i]] == c){
		if(!seen++) printf("%s: %d",s,i);
		else printf(",%d",i);
	}
	if(seen) printf("\n");
}

int fieldw,linpos;
putcr(c) char c; {
	putchar(c);
	linpos++;
	if(c == '\n') linpos = 0;
	else if(linpos == col1){
		putchar('\n');
		linpos = 0;
	}
	if(linpos == 0) dirt++;
}

printo(n) int n; {
	if(n){
		fieldw--;
		printo((n>>3) & 017777);
	}
	for(fieldw--; fieldw>0; fieldw--)
		putcr(' ');
	putcr('0' + (n & 07));
}

printd(n) int n; {
int a;
	if(a = n/10){
		fieldw--;
		printd(a);
	}
	for(fieldw--; fieldw > 0; fieldw--)
		putcr(' ');
	putcr('0' + n%10);
}

/*VARARGS1*/
pmesg(fmt,args) char *fmt; int *args; {
	int x;
	register char *s,c;
	register int *ap;

	ap = &args;
	if(disp && lastrow != ROW0) {
		posit(ROW0,0);
		linpos = 0;
	}
	while(c = *fmt++){
		fieldw = -1;
		if(c != '%') putcr(c);
		else {
			c = *fmt++;
			if(c >= '0' && c <= '9'){
				fieldw = c-'0';
				c = *fmt++;
			}
			switch(c){
			case 'o':
				printo(*ap++);
				continue;
			case 'd':
				x = *ap++;
				if(x < 0){
					putcr('-');
					printd(-x);
				} else	printd(x);
				continue;
			case 's':
				s = (char *) (*ap++);
				while(c = *s++) putcr(c);
				continue;
			case 'c':
				putcr(*ap++);
				continue;
			default:	/* in particular when c=0 */
				error("bad string?");
			}
		}
	}
}

extern int optf,crownmv,altermv,rdifmin;
outgame(){
int mvnr0;
register int *mp1;
	outfil();
	mvnr0 = mvnr;
	mvnr = 1;
	printf("***** dam%s version of %s *****\n",VERSION,DATE);
	printf("rdifmin = %d\n", rdifmin);
	if(crownmv) printf("crownmv = %d\n", crownmv);
	if(altermv) printf("altermv = %d\n", altermv);
	if(playw == USER) printf("user has white\n");
	if(playb == USER) printf("user has black\n");
	for(mp1=game; mp1<gp; ){
		mvnr++;
		prmove(mp1);
		mp1 += 2;
		mp1 += *mp1++;
	}
	prbord();
	rdcomd("t");	/* %% recursive call */
	mvnr = mvnr0;
	outterm();
}

outboard(){
	outfil();
	prbord();
	outterm();
}

outposit(){
	outfil();
	prposit();
	outterm();
}

int disp0,fterm;

outfil(){
	if(optf<0) {
		pmesg("no file\n");
		return;
	}
	fterm = dup(1);
	(void) close(1);
	if(dup(optf) != 1) error("f not 1 in outfil");
	disp0 = disp;
	disp = 0;
}

outterm(){
	disp = disp0;
	(void) close(1);
	if(dup(fterm) != 1) error("f not 1 in outterm");
	(void) close(fterm);
}

initterm(){}
//E*O*F prbord.c//

echo x - prinfo.c
cat > "prinfo.c" << '//E*O*F prinfo.c//'
/* information about dam */
prinfo(){
int f;
char c;
	f = open("prinfo.x",0);
	if(f < 0) f = open("/usr/games/lib/dam/prinfo.x",0);
	if(f < 0){
		printf("sorry, cannot find it\n");
		return;
	}
	while(read(f,&c,1) == 1) putchar(c);
	(void) close(f);
}
//E*O*F prinfo.c//

echo x - readmove.c
cat > "readmove.c" << '//E*O*F readmove.c//'
#include	<stdio.h>
#include	"damdefs.h"

/* read move; the intended format is
	nn-mm
   or
	nnxmm
   or
	nnxmm(q1,...,qt)
 */

extern int	dirt,prall,possct,captmax,captct,timew,timeb,timemax;
extern long	lseek();
extern int	optp,rdif,disp,playw,playb,me,optg,optu,optf;
extern unsigned prbwait;
extern int	rdifmin,altermv,fplevel,rdmin;
extern int	*mp,*mpf,conv[],bord[],moves[],mvnr,*findmove();
extern char	*dsparr[],*emparr[],*fularr[],prtarr[];

int valstone[5] = {
	EMPTY,WHITE,BLACK,WHITE|DAM,BLACK|DAM
};

#define	LINSIZ	82
char line[LINSIZ];		/* terminal input line */
char ofile[LINSIZ];		/* output filename */
char ch, *ach;
#define	ERRCHAR	'\177'

rdlin(){
	register char *lp;
	register int c;

	lp = line;
	while((c = getchar()) != '\n'){
		if(c == EOF) error("end of input");
		if(lp == line+LINSIZ-1){
			lp--;
			c = ERRCHAR;
		}
		*lp++ = c;
	}
	*lp = 0;
	ach = line;
}

newchar(){
	return(ch = *ach++);
}

answer(){
ask:
	rdlin();
	switch(newchar()){
	case '!':
		rdcomd(line);
		goto ask;
	case 'j':
	case 'y':
		return(1);
	case 'n':
		return(0);
	default:
		pmesg("answer 'y' or 'n'\n");
		goto ask;
	}
}

skipspaces(){
	while((ch == ' ') || (ch == '\t')) newchar();
}

rnum(){
int num;
	num = 0;
	skipspaces();
	while(digit()) {
		num = 10*num + (ch - '0');
		newchar();
	}
	return(num);
}

digit(){
	return((ch >= '0') && (ch <= '9'));
}

letter(){
register char c;
	c = ch|040;
	return((c >= 'a') && (c <= 'z'));
}

readnum(){
int	num;
	num = rnum();
	skipspaces();
	if((num < 0) || (num > 50)) num = 0;
	return(conv[num]);
}

check(cc) char cc; {
	if(ch != cc){
		putcr('\'');
		putsym(ch);
		pmesg("' where '");
		putsym(cc);
		pmesg("' expected\n");
		ch = ERRCHAR;		/* some error occurred */
	} else if(ch) newchar();
}

putsym(cc) char cc; {
	if(!cc) pmesg("\\n");
	else if(cc < ' ') pmesg("\\0%o",cc);
	else putcr(cc);
}

/*
 * a alter	* k		* u uit eval
 * b black	* l		* v value
 * c crown	* m move back	* w white
 * d define	* n no HP use	* x exit
 * e end	* o output	* y you
 * f file,fp	* p print	* z zwart
 * g (game)	* q		* = remise
 * h halt	* r read,rdepth * I I play
 * i info	* s sleep	* : (debug)
 * j		* t time	* \0 (no random)
 */

int	*youmv = 0;

rdcomd(ac) char *ac; {
register char *sp,*sp2;
register int i;
int ct;
	if(*ac == '!') ac++;
	ach = ac;	/* read from argstring instead of array line */
	switch(newchar()){
	case 'w':
		playw = PDP;
		break;
	case 'b':
	case 'z':
		playb = PDP;
		break;
	case 'I':
		newchar();
		if(ch == 'w') playw = USER;
		else if(ch == 'b' || ch == 'z') playb = USER;
		else playw = playb = USER;
		break;
	case 'y':
		youmv = findmove(me);
		break;
	case 'h':
		while(1){
			rdlin();
			if(*ach == '.') break;
			rdcomd(ach);
		}
		break;
	case 'p':
		newchar();
		if(ch == 'b'){
			pbundef();
			newchar();
		} else	prall++;
		if(digit()) optp = rnum();
		prbord();
		break;
	case 'm':
		newchar();
		if(i = readcol()){
			me = i;
			mpf = 0;
			break;
		}
		if(ch == '-'){
			newchar();
			i = mvnr-rnum()-2;
		} else	i = rnum();
		check(0);
		if(ch == ERRCHAR) goto ask;
		if(i<0 || i+2>mvnr){
			pmesg("impossible\n");
			goto ask;
		}
		backup(i);
		prbord();
		break;
	case 'n':
		if(disp) home();
		disp = 0;
		break;
	case 'a':
		/*
		 * awk<nr>: alter field <nr> into white king
		 * a<nr>: make the nonempty field <nr> empty
		 */
		newchar();
		ct = readstone();
	alp:
		i = readnum();
		if(!i){
			pmesg("bad field\n");
			goto ask;
		}
		if((!ct) && (bord[i] == EMPTY)){
			pmesg("empty already\n");
			goto ask;
		}
		bord[i] = valstone[ct];
		altermv = mvnr;
		mpf = 0;
		if(ch == ','){
			newchar();
			goto alp;
		}
		check(0);
		if(ch == ERRCHAR) goto ask;
		break;
	case 'f':
		if(newchar() == 'p'){
			if(!newchar()){
				pmesg("freepath values:\n");
				pmesg("w: %d\n",freepath(WHITE));
				pmesg("z: %d\n",freepath(BLACK));
				break;
			}
			fplevel = rnum()-1;
			break;
		}
		if(letter()){
			pmesg("What?\n");
			goto ask;
		}
		skipspaces();
		sp = ofile;
		while(*sp++ = ch){
			if(sp > ofile+LINSIZ) goto ask;
			newchar();
		}
		if(optf >= 0) (void) close(optf);
		optf = open(ofile,1);
		if(optf < 0) optf=creat(ofile,0666);
		else (void) lseek(optf, 0L, 2);
		if(optf < 0) pmesg("cannot create %s\n",ofile);
		break;
	case 'o':
		if(newchar() == 'g') outgame();
		else if(ch == 'b') outboard();
		else if(ch == 'p') outposit();
		else {
			pmesg("what?");
			goto ask;
		}
		break;
	case 'r':
		newchar();
		/*
		 * r: set rdif
		 * rm: set rdmin
		 * rf: read pos from file
		 * rt: read pos from terminal
		 */
		if(ch == 'f') readfile();
		else if(ch == 't') readpos();
		else if(ch == 'm') {
			newchar();
			rdmin = rnum();
			pmesg("rdmin = %d\n",rdmin);
		}
		else {
			rdif = rnum();
			pmesg("rdif = %d\n",rdif);
			if(mvnr == 1)rdifmin = rdif;
			else if(rdif<rdifmin)rdifmin = rdif;
		}
		break;
	case 's':
		newchar();
		prbwait = rnum();
		if(prbwait>30) prbwait = 30;
		pmesg("sleep after prbord for %d seconds\n",
			prbwait);
		break;
	case 't':
		newchar();
		if(digit()){
			timemax = rnum();
			pmesg("allowed time per move: %d sec\n",timemax);
		}
		pmesg("time used:\n");
		pmesg("  W %4d sec\n",timew);
		pmesg("  B %4d sec\n",timeb);
		break;
	case 'c':
		/* this does not count for a move */
		crown(me);
		break;
	case 'd':
		pbundef();	/* force printing new stones */
		newchar();
		skipspaces();
		if(!ch){
			disp = 1;
			break;
		}
		if((i = readstone()) || (ch == '"')){
			skipspaces();
			check('"');
			if(ch == ERRCHAR) goto ask;
			sp = dsparr[i];
			while(ch != '"'){
				if(sp - dsparr[i] < 12) *sp++ = ch;
				else {
					pmesg("string too long\n");
					goto fins;
				}
				if(!ch){
					pmesg("bad string\n");
					goto fins;
				}
				newchar();
			}
			newchar();
			check(0);
		fins:
			*sp++ = 0;
			break;
		} else if(ch == 'e'){
			newchar();
			i = rnum();
			for(ct=0; ct++!=i; ) if(!emparr[ct]){
				pmesg("we dont have such empty fields\n");
				goto ask;
			}
			sp = dsparr[0];
			sp2 = emparr[i];
			while(*sp++ = *sp2++);
		} else {
			i = 4*rnum();
			for(ct=0; ct++!=i; ) if(!fularr[ct]){
				pmesg("this set of stones is not available\n");
				goto ask;
			}
			for(ct=0; ct<4; ct++){
				sp = dsparr[ct+1];
				sp2 = fularr[i+ct];
				while(*sp++ = *sp2++);
			}
		}
		break;
	case 'u':
		if(newchar() == '\0') prsteval();
		else optu = rnum();
		break;
	case 'v':
		newchar();
		i = rnum();
		check(',');
		if(ch != ERRCHAR){
			ct = rnum();
			check(0);
		}
		if(ch == ERRCHAR) goto ask;
		setstrat(i,ct);
		break;
	case 'e':
		pmesg("I win\n");
		reset();
	case '=':
		remise();
		break;
	case 'i':
		if(disp) home();
		prinfo();
		prall++;
		break;
	case 'x':
		error("the game is over");
	case '?':
		switch(newchar()){
		case 'f':
			if(newchar() != 'p') goto deflt;
			i = fplevel;
			break;
		case 'r':
			i = rdif;
			break;
		case 's':
			i = prbwait;
			break;
		deflt:
		default:
			pmesg("unknown param\n");
			goto ask;
		}
		pmesg("%c: %d\n",ch,i);
		break;
	default:
		if(ch)pmesg("unknown command\n");
		goto ask;
	}
	return;
ask:
	return;
}

int *
readmove(color) int color; {
	register int *mp1;	/* essential ! */
	register int *mp2,*ump,*ump0,ct,f1,f2;
	int umove[22];

listmoves:
	mpf = moves;
	move(me);
	if(possct == 0){
		pmesg("you lose\n");
		reset();
	} else if(possct == 1 && !optg){
		pmesg(" forced:\n");
		prmove(mpf);
		/*  return(mpf);  */
	}
ask:
	if(!optg) pmesg("?");
	rdlin();
	dirt++;
	newchar();
	skipspaces();
	/* read options */
	if(ch == '!'){
		rdcomd(ach);
		if(mp1 = youmv){
			youmv = 0;
			return(mp1);
		}
		if(!mpf) goto listmoves; /* the position has changed */
		goto ask;
	}
	/* if line does not start with a digit and (optg) the ignore */
	if(!digit()) if(optg) goto ask;
	/* read actual moves */
	ump0 = ump = &umove[0];
	while(ch){
		if(ump-ump0 >= 22) ump--;
		if((*ump++ = readnum()) == 0) {
			if(optg) goto ask; /* ignore bad lines */
			pmesg("bad field\n");
			goto ask;
		}
		switch(ump - ump0){
		case 1:
			if(ch == '.') {
				do newchar();
				while(ch == '.' || ch == ' ' || ch == '\t');
				ump--;
			}
			else if(ch)
				check(captmax ? 'x' : '-');
			break;
		case 2:
			if(!captmax) check(0);
			else if(ch) check('(');
			break;
		default:
			if(ch == ',') {
				newchar();
				break;
			}
			check(')');
			if(ch == ERRCHAR) goto ask;
			check(0);
		}
		if(ch == ERRCHAR) goto ask;	/* some error occurred */
	}
	if(ump == ump0) {
		if(possct == 1) return(mpf);
		else goto ask;
	}
	captct = (ump - ump0) - 2;
	if(captct > 0){
		if(captct != captmax){
			pmesg("incorrect number of captured stones\n");
			goto ask;
		}
	} else if(captct == -1) {
		/* incomplete move, see whether it is to or from */
		if(bord[umove[0]] == EMPTY){	/* to */
			umove[1] = umove[0];
			umove[0] = -1;
		} else	umove[1] = -1;		/* from */
		captct = 0;
	}
	/* now determine which move he selected from the list
	   created by 'move(c);'
	 */
	ump = 0;
	for(mp1 = mpf; mp1 < mp; mp1 += ct){
		ump0 = &umove[0];
		mp2 = mp1;
		f1 = *mp1++;
		f2 = *mp1++;
		ct = *mp1++;
		if(((f1 & 0177) ^ *ump0++) > 0) continue;
		if(((f2 & 0177) ^ *ump0++) > 0) continue;
		if(captct) while(ct--)
			if((*mp1++ & 0177) != *ump0++) continue;
		if(ump){
			pmesg("ambiguous move\n");
			goto ask;
		} else ump = mp2;
	}
	if(!ump) {
		pmesg("illegal move\n");
		goto ask;
	}
	if(!optg) prmove(ump);
	return(ump);
}

/* read w, z, W, Z, wd, bk and return index (0 by default) */
readstone(){
int c;
	switch(ch|040){
	case 'w':
		c = 1;
		break;
	case 'b':
	case 'z':
		c = 2;
		break;
	default:
		return(0);
	}
	if(ch & 040){
		newchar();
		if(ch == 'd' || ch == 'k') goto dam;
	} else {
	dam:
		newchar();
		c += 2;
	}
	return(c);
}

readcol(){
	switch(ch){
	case 'w':
		newchar();
		return(WHITE);
	case 'z':
	case 'b':
		newchar();
		return(BLACK);
	default:
		return(0);
	}
}

/*
 * Read position; each line looks like
 * w: 31-48,49,50
 * the description ends with a dot.
 */
int rdbord[66];

readpos(){
register int i,j;
int c;
	rdbord[0] = -1;	/* remember that readpos was executed */
	for(i=1; i<66; i++) rdbord[i] = EMPTY;
	do {
		rdlin();
		if(newchar() == '.') break;
		if(!(i = readstone())){
			pmesg("bad colour, ");
			goto bad;
		}
		c = valstone[i];
		check(':');
		if(ch == ERRCHAR) goto bad;
	nxt:
		if(!(i = readnum())){
			pmesg("bad field, ");
			goto bad;
		}
		if(ch == '-'){
			newchar();
			if(!(j = readnum())){
				pmesg("bad 2nd field, ");
				goto bad;
			}
		} else j = i;
		while(i <= j) rdbord[i++] = c;
		if(ch){
			if(ch == '.') break;
			check(',');
			if(ch == ERRCHAR){
				pmesg("bad separator, ");
				goto bad;
			}
			goto nxt;
		}
	} while(1);
	for(i=6; i<60; i++)
		if(bord[i] != EDGE) bord[i] = rdbord[i];
	mpf = 0;
	prbord();
	return;
bad:
	pmesg("old board retained\n");
	return;
}

readfile(){
int f,f0;
	/* ach points to the f of 'rf' */
	newchar();
	skipspaces();
	ach--;
	f0 = dup(0);
	(void) close(0);
	f = open(ach,0);
	if(f < 0) pmesg("cannot open %s\n",ach);
	else if(f) error("f nonzero");
	else {
		readpos();
		(void) close(f);
	}
	if(f = dup(f0)) error("df nonzero");
	(void) close(f0);
}
//E*O*F readmove.c//

echo x - timeac.c
cat > "timeac.c" << '//E*O*F timeac.c//'
/* time accounting */
long int tvec0, tvec;

timebeg(){
	(void) time(&tvec0);
}

timedif(){
	(void) time(&tvec);
	return(tvec - tvec0);
}
//E*O*F timeac.c//

echo x - prinfo.x
cat > "prinfo.x" << '//E*O*F prinfo.x//'
Information about dam3.0

call:
% dam [options]

options:
-w	computer plays white
-b	computer plays black
-z	computer speelt zwart
-d	use display features of HP
-n	normal output, even to HP
-p<nr>	print board once every <nr> moves
-p	do not print the board unless explicitly asked for
	(by default the board is printed after each move)
-r<nr>	define recursion depth (default -r100)
-s<nr>	sleep for <nr> seconds after printing the board

In addition options of the form
!cmd
are allowed, and have the same effect as the same command
during play. Note that if cmd contains spaces or special
characters, the option should be given as
"!cmd"

During play the moves must be typed in the format
<from>-<to>	for a non-capture
<from>x<to>	for a capture
		if several captures with the same 'from' and 'to'
		fields are possible, all captured stones must be
		listed in order:
<from>x<to>(<p1>,...,<pn>)
		on the other hand if either the <to> or the <from>
		field defines the move uniquely, it suffices:
<from>

instead of typing a move, a command starting with '!' can be given.
commands:
!a	alter position (not allowed after an interrupt)
	format:
!a<col> <nr>	alter field <nr> into a piece of type <col>
!a <nr>		make field <nr> empty
!b	play black
!c	crown pieces of opponent to kings
!d<str>	define display image or printed symbol for the white and
	black pieces (default: " wzWZ");
!e	end this game (= resign)
!h	halt and read a series of commands ending
	with a line containing . only
!i	print information (i.e., this text)
!I	I play black and white
!Iw	I play white
!Ib	I play black
!Iz	ik speel zwart
!m<nr>	retrieve the position after the <nr>th move
!m-<nr>	take back <nr> moves
!mw	white to move
!mb	black to move
!mz	zwart aan zet
!pb	print board
!p	print board (and on HP also the list of moves)
!p<nr>	print board every <nr> moves
!r<nr>	define recursion depth
!rm<nr>	define minimal recursion depth (default 1; should be 3)
!rf<file>  read position from file
!rt	read position from terminal
	format of position:
<col>: <nr>-<nr>,<nr>
	where <col> is w,z,b,wk,wd,zk,zd,bk or bd
	the description ends with a dot.
!s<nr>	sleep for <nr> seconds after each prbord
!t	print time used
!v	define strategy
!x	exit
!w	play white
!z	speel zwart
//E*O*F prinfo.x//

exit 0