/* AUTOTURN.C
 * This contains the functions for the program to decide what to do
 */

#include	<proto/all.h>
#include	<stdlib.h>
#include	<math.h>
#include	<intuition/intuition.h>
#include	<string.h>
#include	"defns.h"
#include	"externs.h"


VOID	autoturn(cp,np,pp)
struct	playerinfo	*cp,*np,*pp;
{
	register	USHORT	i,n,p,takefup,cc=cp->nchld,gtemp;
	register	struct	condset	*s;
	char	buf[3];
	/* deselect fup,fdp*/
	gtemp = RemoveGList(wind,&stdgad[FUPG],2);
	RefreshGList(&stdgad[FUPG],wind,NULL,2);
	stdgad[FUPG].Flags &= ~SELECTED;
	stdgad[FDPG].Flags &= ~SELECTED;
	TXTOUT(180,21,1,0,cp->name);
	TXTOUT(MESX,MESY,7,0,"... is thinking ...");
	cp->hand[0] = fup[fuptc]; /*test how well next card from fup integrates with other cards*/
	buildsets(cp);
	for(i=0;i<=ncs;i++) score(&cs[i],cp); /*initial scores for sets*/
	n=mutexcl(cp); /*generate non-mutually exclusive combination of submissible sets*/
	if(!n) /*fup card not very useful, and not submissible*/
	{ /*try fdp card instead*/
		TXTOUT(MESX,MESY,7,0,"Takes the facedown card.   ");
		takefup=0;
		maintainmem(cp,pp,np,fup[fuptc],GONEPAST);
		maintainmem(cp,pp,np,fup[fuptc],CURRPLRDROPD);
		cp->hand[0]=NEXTFDP;
		cp->mem[CARD2(cp->hand[0])] |= CURRPLRHOLDS;
		buildsets(cp);
		for(i=0;i<=ncs;i++) score(&cs[i],cp);
		mutexcl(cp);
	}
	else
	{
		takefup=1;
		TXTOUT(MESX,MESY,7,0,"Takes the faceup card.   ");
		NEXTFUP;	/*fup card will be used so remove it from pack*/
		maintainmem(cp,pp,np,cp->hand[0],CURRPLRHOLDS);
	}
	for(p=3;p;p--)
	{
		for(n=0;n<=ncs;n++) if(selcs[n].ns) /*some cards in set*/
		{
			s=&selcs[n];
			if((p==3 && s->ns==3 || p==2) && s->type&SUB) autosubset(s,cp); /*submit set*/
			else if(p==1 && s->type&ADD) autoaddset(s,cp); /*add to set*/
		}
	}
	/*now drop card with lowest score*/
	for(i=0,p=60000,n=500;i<=cp->nchld;i++) 
		if(!ISNUL(cp->hand[i]) && (cdsc[i]<p || n==500) && (i!=0 || !takefup)) p=cdsc[n=i]; /*new lowest*/
	PUTFUP(cp->hand[n]);
	DRAWFUP;
	maintainmem(cp,pp,np,cp->hand[n],CURRPLRDROPD);
	cp->hand[n]=nulcard;
	if(!ISNUL(cp->hand[0]))
	{ /*copy selected card to body of hand*/
		for(i=1;i<=cp->nchld && !ISNUL(cp->hand[i]);i++);
		cp->hand[i]=cp->hand[0];
	}
	compacthand(cp->hand,&cp->nchld);
	REMHAND(cp);
	if(cp->viewcard) disphand(cp); else hidecards(cp);
	cc-=cp->nchld;
	if(cc)
	{
		sortdispset();
		stci_d(buf,cc);
		TXTOUT(MESX+25*8,MESY,7,0,buf);
		TXTOUT(MESX+27*8,MESY,7,0,"submitted - Hit continue");
	}
	else
	{
		TXTOUT(MESX+25*8,MESY,7,0,"None submitted - Hit continue");
	}
	if(!contplay) WAITCONT;
	AddGList(wind,&stdgad[FUPG],gtemp,2,NULL);
	TXTOUT(MESX,MESY,7,0,"                                                           ");
	TXTOUT(180,21,1,0,"                   ");
}

VOID	autosubset(struct condset *s,struct playerinfo *p)
{
	register	struct	card	*t=s->set;
	register	USHORT	i,d,n,samesuit=1,samenum=1;
	for(i=0,n=0;i<=p->nchld;i++) if(!ISNUL(p->hand[i])) n++;
	d=s->ns;
	if(d==n+1)
	{/* ensure at least one card will be left when finished*/
		for(i=0;i<=d;i++) if(CDEQU(p->hand[i],t[d])) s->uc&=~(1<<i);
		d--;
	}
	for(i=2;i<=d;i++)
	{ /*final check on validity of set*/
		if(t[i-1].n!=t[i].n) samenum=0;
		if(t[i-1].s!=t[i].s) samesuit=0;
	}
	if(d<3 || !(samesuit || samenum)) return; /*rejected submission attempt*/
	if(samesuit)
	{ /*check set is continuous*/
		for(i=2;i<=d;i++)
			if((t[i-1].n!=t[i].n-1) && !(t[i-1].n==KING && t[i].n==ACE)) return; /*rejected*/
	}
	n=s->uc<<1;
	for(i=0;i<=p->nchld;i++) if((n>>=1)&1) p->hand[i]=nulcard; /*this card about to be submitted*/
	for(i=0;i<=5 && fupset[i]->ncard;i++); /*find empty set*/
	fupset[i]->ncard=d;
	if(samesuit) for(n=1;n<=d;n++)
	{
		fupset[i]->cards[n+t[1].n-1]=t[n];
		maintainmem(NULL,NULL,NULL,t[n],INFUPSET);
	}
	else for(n=1;n<=d;n++)
	{
		fupset[i]->cards[n]=t[n];
		maintainmem(NULL,NULL,NULL,t[n],INFUPSET);
	}
	s->ns=s->nd=0; /*remove all cards from set*/
}

VOID	autoaddset(struct condset *s,struct playerinfo *p)
{
	register	struct card	*fsc,*phc;
	register	USHORT	*fsn,phn,i,j,f,l,used;
	fsc=fupset[s->addtoset]->cards;
	fsn=&fupset[s->addtoset]->ncard;
	phc=p->hand;
	phn=p->nchld;
	for(f=1;ISNUL(fsc[f]);f++); /*find first card of set*/
	l=f+(*fsn)-1;
	if(fsc[f].s==fsc[f+1].s && s->type&SUITSET) /*same suit*/
	{
		if(s->type&TOSTART)
		{
			if(s->set[1].n==fsc[f].n-1 && s->set[1].s==fsc[f].s)
			{
				for(i=0,used=1;i<=phn;i++) if(CDEQU(s->set[1],phc[i]) && !ISNUL(s->set[1]))
				{
					phc[i]=nulcard;
					used=0; /*card has not already been used*/
				}
				if(!used)
				{
					fsc[--f]=s->set[1];
					maintainmem(NULL,NULL,NULL,s->set[1],INFUPSET);
					(*fsn)++;
					for(j=1;j<=s->nd;j++) if(s->dep[j].n==fsc[f].n-1 && s->dep[j].s==fsc[f].s)
					{
						for(i=0,used=1;i<=phn;i++) if(CDEQU(s->dep[j],phc[i]) && !ISNUL(s->dep[j]))
						{
							phc[i]=nulcard;
							used=0; /*card not already used*/
						}
						if(!used)
						{
							fsc[--f]=s->dep[j];
							maintainmem(NULL,NULL,NULL,s->dep[j],INFUPSET);
							(*fsn)++;
						}
						else
						{/*exit function - no more additions possible*/
							s->nd=s->ns=0;
							return;
						}
					}
				}
				else
				{/*exit function - no more additions possible*/
					s->nd=s->ns=0;
					return;
				}
			}
		}
		else if(s->type&TOEND)
		{
			if(((s->set[1].n==fsc[l].n+1 && s->set[1].n!=2) || (fsc[l].n==KING && s->set[1].n==ACE))
				&& s->set[1].s==fsc[l].s)
			{
				for(i=0,used=1;i<=phn;i++) if(CDEQU(s->set[1],phc[i]) && !ISNUL(s->set[1]))
				{
					phc[i]=nulcard;
					used=0; /*card not already used*/
				}
				if(!used)
				{
					fsc[++l]=s->set[1];
					maintainmem(NULL,NULL,NULL,s->set[1],INFUPSET);
					(*fsn)++;
					for(j=1;j<=s->nd;j++) if(((s->dep[j].n==fsc[l].n+1 && s->dep[j].n!=2) || (fsc[l].n==KING && s->dep[j].n==ACE))
						&& s->dep[j].s==fsc[l].s)
					{
						for(i=0,used=1;i<=phn;i++) if(CDEQU(s->dep[j],phc[i]) && !ISNUL(s->dep[j]))
						{
							phc[i]=nulcard;
							used=0; /*card not already used*/
						}
						if(!used)
						{
							fsc[++l]=s->dep[j];
							maintainmem(NULL,NULL,NULL,s->dep[j],INFUPSET);
							(*fsn)++;
						}
						else
						{/*exit function - no more additions possible*/
							s->nd=s->ns=0;
							return;
						}
					}
				}
				else
				{/*exit function - no more additions possible*/
					s->nd=s->ns=0;
					return;
				}
			}
		}
	}
	else if(fsc[f].n==fsc[f+1].n && s->set[1].n==fsc[f].n && s->type&NUMSET && *fsn==3)
	{
		for(i=0,used=1;i<=phn && used;i++) if(CDEQU(s->set[1],phc[i]) && !ISNUL(s->set[1]))
		{
			phc[i]=nulcard;
			used=0; /*card not already used*/
			fsc[++l]=s->set[1];
			maintainmem(NULL,NULL,NULL,s->dep[j],INFUPSET);
			(*fsn)++;
		}
	}
	s->ns=s->nd=0; /*remove all cards from set*/
}

			
		
VOID	buildsets(struct playerinfo *p)
{
	register	USHORT	i,n,nchld=p->nchld,s;
	register	struct	card	*h=p->hand;
	USHORT	nconc,setfound;	/*next condensed set, number of consecutive cards*/
	for(i=0;i<=3;i++)
	{ /*initialise arrays*/
		for(n=0;n<=15;n++) ss[i].c[n] = nulcard;
		ss[i].nc=0;
	}
	for(i=0;i<=32;i++)
	{
		cs[i].type = NOTSET;
		cs[i].ns = cs[i].nd = cs[i].score = cs[i].ig = cs[i].addtoset = cs[i].uc= 0;
		for(n=0;n<=8;n++) cs[i].set[n]=cs[i].dep[n]=nulcard;
	}
	for(i=0;i<=13;i++) ns[i].nc=0;

	for(i=0;i<=nchld;i++)
	{ /*separate hand into sets*/
		ns[h[i].n].c[++ns[h[i].n].nc]=h[i];	/*add card to numset*/
		ss[h[i].s].c[h[i].n]=h[i]; /*add card to suit set*/
		if(h[i].n==ACE) ss[h[i].s].c[14]=h[i];
		ss[h[i].s].nc++;
	}
	/*card ss[s].c[n] has suit s and number n.*/
	ncs=0;
	for(i=0;i<=3;i++)
		if(ss[i].nc)
		{ /*convert suit set to condensed form*/
			nconc=setfound=0;
			for(n=1;n<=15;n++)
			{ /*look for submissible sets*/
				if(!ISNUL(ss[i].c[n])) nconc++;
				else
				{
					if(nconc>=3)
					{ /*submissible set*/
						if(setfound) ncs++; /*current set already has submissible subset*/
						while(nconc) cs[ncs].set[++cs[ncs].ns]=ss[i].c[n-nconc--];
						setfound=1; /*current record now has a submissible set*/
						cs[ncs].type = SUITSET | SUB;
					}
					else
					{
						if((cs[ncs].nd || cs[ncs].ns) && 
							(n-nconc)-max(cs[ncs].dep[cs[ncs].nd].n,cs[ncs].set[cs[ncs].ns].n)>=(p->nchld<4?p->nchld:4))
						{
							ncs++;
							setfound=0; /*no set in current record*/
						}
					while(nconc) cs[ncs].dep[++cs[ncs].nd]=ss[i].c[n-nconc--];
						cs[ncs].type = SUITSET | SUB;
					}
				}
			}
			if(cs[ncs].ns || cs[ncs].nd) cs[ncs++].type = SUITSET | SUB;
			else cs[ncs].ns=cs[ncs].nd=0;
		}
	cs[ncs].type = NOTSET;

	/*detect cards which can be added to existing suitsets*/
	for(i=0;i<=3;i++)
	{
		if(ss[i].nc)	/*check some cards in set*/
		{
			for(n=1;n<=14;n++)
			{
				if(!ISNUL(ss[i].c[n]))
				{
					setfound=0;
					for(s=0;s<=5 && !setfound && fupset[s]->ncard;s++)
					{
						if(ISNUL(fupset[s]->cards[n]))
						{
							if(fupset[s]->cards[n-1].n==n-1 && !ISNUL(fupset[s]->cards[n-1])
								&& fupset[s]->cards[n-1].s==i && fupset[s]->cards[n-2].s==i)
							{ /*can add after set s*/
								if(cs[ncs].type!=NOTSET) ncs++; /*current set already used*/
								else cs[ncs].ns=cs[ncs].nd=0; /*current set no good - throw it away*/
								cs[ncs].set[++cs[ncs].ns]=ss[i].c[n];
								cs[ncs].addtoset=s;
								cs[ncs].type = ADD | SUITSET | TOEND;
								setfound=1;
							}
							else if(fupset[s]->cards[n+1].n==n+1 && fupset[s]->cards[n+1].s==i && fupset[s]->cards[n+2].s==i)
							{ /*can add before set s*/
								if(cs[ncs].type!=NOTSET || (cs[ncs].nd && cs[ncs].dep[1].s!=ss[i].c[n].s)) ncs++;
								cs[ncs].set[++cs[ncs].ns]=ss[i].c[n];
								cs[ncs].addtoset=s;
								cs[ncs].type= ADD | SUITSET | TOSTART;
								setfound=1;
								ncs++; /*move to new set*/
							}
						}
					}
					if(!setfound)
					{ /*add to dependants*/
						if((cs[ncs].ns || cs[ncs].nd)
							&& ((n-max(cs[ncs].set[cs[ncs].ns].n,cs[ncs].dep[cs[ncs].nd].n))>=4)
							|| (ss[i].c[n].s!=(cs[ncs].ns==0?cs[ncs].dep[1].s:cs[ncs].set[1].s)))
						{ /*start new set*/
							if(cs[ncs].type!=NOTSET) ncs++;
							else cs[ncs].ns=cs[ncs].nd=0;
						}
						cs[ncs].dep[++cs[ncs].nd]=ss[i].c[n];
					}
				}
			}
		}
	}
	if(cs[ncs].ns) ncs++;
	else cs[ncs].nd=0;

	/*copy numsets to condensed sets*/
	for(i=1;i<=13;i++)
	{
		if(ns[i].nc==1)
		{ /*can card be added to existing set*/
			setfound=0;
			for(s=0;s<=5 && !setfound && fupset[s]->ncard;s++)
				if(fupset[s]->cards[1].n==ns[i].c[1].n && fupset[s]->cards[2].n==ns[i].c[1].n)
				{ /*can add card this set*/
					cs[ncs].addtoset=s;
					cs[ncs].set[++cs[ncs].ns]=ns[i].c[1];
					cs[ncs++].type= ADD | NUMSET;
					setfound=1;
				}
			if(!setfound)
			{
				cs[ncs].dep[++cs[ncs].nd]=ns[i].c[1];
				cs[ncs++].type = SUB | NUMSET;
			}
		}
		else if(ns[i].nc==2)
		{
			cs[ncs].dep[1]=ns[i].c[1];
			cs[ncs].dep[2]=ns[i].c[2];
			cs[ncs].type = NUMSET | SUB;
			cs[ncs++].nd=2;
		}
		else if(ns[i].nc>=3)
		{
			for(n=1;n<=ns[i].nc;n++) cs[ncs].set[n]=ns[i].c[n];
			cs[ncs].type = NUMSET | SUB;
			cs[ncs++].ns=ns[i].nc;
		}
	}
	for(i=0;i<=ncs;i++)
		for(n=1;n<=cs[i].ns;n++)
			for(s=setfound=0;s<=nchld && !setfound;s++) if(setfound=CDEQU(cs[i].set[n],h[s])) cs[i].uc|=(1<<s);
}

VOID	score(struct condset *c,struct playerinfo *p)
{
	register	USHORT	i,n,j,last,m,s;
	USHORT	gap,next;
	struct	card	t[5];
	c->score = 0;
	if(c->type&NUMSET)
	{
		for(n=1;n<=c->ns;n++) c->score+=(c->set[n].n*8+(n>1?1100:0))+300;
		for(n=1;n<=c->nd;n++) c->score+=((15-c->dep[n].n)*4+(n>1?1300:0));
		for(n=1;n<=4;n++) t[n]=nulcard;
		if(c->nd)
		{
			s=c->dep[1].n;
			for(n=1;n<=c->nd;n++) t[c->dep[n].s]=c->dep[n];
		}
		else
		{
			s=c->set[1].n;
			for(n=1;n<=c->ns;n++) t[c->set[n].s]=c->set[n];
		}
		for(n=1;n<=4;n++) if(ISNUL(t[n]))
		{
			m=p->mem[CARD(s,n)];
			if(!(m&INFUPSET))
			{
				c->score+=100+15-s;
				if(!(m&NEXTPLRHOLDS)) c->score+=70;
				if(!(m&PREVPLRHOLDS)) c->score+=40;
				if(!(m&GONEPAST)) c->score+=100;
				if(m&(NEXTPLRDROPD | PREVPLRDROPD)) c->score+=100;
			}
		}
		else c->score+=(16-t[n].n)*4;
	}
	else /*score suitset*/
	{
		if(ISNUL(c->set[1])) last=c->dep[1].n;
		else if(ISNUL(c->dep[1])) last=c->set[1].n;
		else last=min(c->dep[1].n,c->set[1].n);
		if(c->type&SUB)
		{
			c->score+=c->ns*500+c->nd*100;
			for(i=n=1;(i<=c->ns || n<=c->nd) && !exgm && !quit;)
			{
				if(c->set[i].n<=c->dep[n].n && i<=c->ns || n>c->nd)
				{
					if(c->set[i].n==ACE && i>1) c->score+=(last==KING?1200:(7-(13-last))*130)+15*9+100;
					else if(c->set[i].n!=last)
						c->score+=(c->set[i].n-last==1?1200:(6-(c->set[i].n-last-1))*100)+c->set[i].n*9+100;
					else c->score+=(15-last)*9+100;
					last=c->set[i++].n;
				}
				else
				{
					if(c->dep[n].n==ACE && n>1) c->score+=(last==KING?1200:(6-(13-last))*110)+15*5+80;
					else if(c->dep[n].n!=last)
						c->score+=(c->dep[n].n-last==1?1200:(6-(c->dep[n].n-last-1))*90)+(15-c->dep[n].n)*5+80;
					else c->score+=(15-last)*5+80;
					last=c->dep[n++].n;
				}
			}
		}
		else if(c->type&ADD)
		{
			for(i=n=1;(i<=c->ns || n<=c->nd) && !exgm && !quit;)
			{
				if(c->set[i].n<=c->dep[n].n && i<=c->ns || n>c->nd)
				{
					if(c->set[i].n==ACE && i>1) next = 14;
					else next=c->set[i].n;
					if(last!=c->set[i].n)
						c->score+=(next-last==1?1200:(7-(next-last-1))*100)+c->set[i].n*9+100;
					else c->score+=(15-last)*9+100;
					last=c->set[i++].n;
				}
				else
				{
					if(c->dep[n].n==ACE && i>1) next = 14;
					else next=c->dep[n].n;
					if(next-last==1) c->score+=1200+c->dep[n].n*8+80;
					else if(last!=next) c->score+=(6-(next-last-1))*90+c->dep[n].n*5+80;
					else c->score+=(15-last)*5+80;
					last=c->dep[n++].n;
				}
			}
		}
		
		if(!ISNUL(c->set[1])) s=c->set[1].s;
		else s=c->dep[1].s;
		if(ISNUL(c->set[1])) last=c->dep[1].n;
		else if(ISNUL(c->dep[1])) last=c->set[1].n;
		else last=min(c->dep[1].n,c->set[1].n);
		for(i=n=1;(i<=c->ns || n<=c->nd) && !exgm && !quit;)
		{
			if(c->set[i].n<=c->dep[n].n && i<=c->ns || n>c->nd)
			{
				gap=c->set[i].n-last-1;
				next=c->set[i++].n;
			}
			else
			{
				gap=c->dep[n].n-last-1;
				next=c->dep[n++].n;
			}
			for(j=last+1;j<next;j++)
			{
				m=p->mem[CARD(s,n)];
				if(!(m&INFUPSET))
				{
					c->score+=90/gap+15-s;
					if(!(m&NEXTPLRHOLDS)) c->score+=70/gap;
					if(!(m&PREVPLRHOLDS)) c->score+=70/gap;
					if(!(m&GONEPAST)) c->score+=90/gap;
					if(m&(NEXTPLRDROPD | PREVPLRDROPD)) c->score+=90/gap;
				}
				last=next;
			}
		}
	}
	if(c->type&SUB && p->nchld<=2) c->score=0; /*less than useless*/
	if(c->type&ADD && p->nchld<=2) c->score+=15000; /*ensure high score for addable sets when submission is impossible*/
	if(c->type&NUMSET && c->nd==2) c->score+=500;
	c->score+=c->ns*600;
}

USHORT	mutexcl(struct playerinfo *p)
{
	register	USHORT	i,j,k,w,x;
	long	l;
	struct	card	c,*s;
	USHORT	bias,	/*toward 1:numset, 2:suitset 3:set with higher score*/
		pass,	/*number of passes to be made with different biases*/
		nxsts,	/*next start set*/
		psperbias,	/*number of passes to be made for each bias at different start sets*/
		best=0,pn,chng,currst,pm,sc,cdscs[53];
	if(p->level==1)
	{/*easiest level*/
		bias=1; pass=1; nxsts=0; psperbias=1;
	}
	else if(p->level==1)
	{/*best level*/
		bias=1; pass=3; nxsts=0; psperbias=ncs;
	}
	else if(p->level<=5)
	{/*fairly easy*/
		l=rand();
		bias=1;
		pass=1;
		if((nxsts=l&31)>ncs) nxsts=ncs;
		if((psperbias=(l>>5&31)+1)>ncs>>1 && ncs!=1) psperbias=ncs>>1;
	}
	else
	{/*fairly good play*/
		l=rand();
		bias=1;
		pass=3;
		if((nxsts=l&31)>ncs) nxsts=ncs;
		if((psperbias=(l>>5&31)+1)>ncs) psperbias=ncs;
	}

	while(pass--)
	{
		for(currst=nxsts,pn=psperbias,chng=1;pn && chng;pn--)
		{
			chng=0;	/*change made flag*/
			for(i=0;i<=ncs;i++) testcs[i]=cs[i];
			for(i=currst,pm=0;!pm || i!=currst;i=i>=ncs?0:i+1)
			{
				pm=1;
				for(j=(i>=ncs?0:i+1);j!=i;j=j>=ncs?0:j+1)
				{
					if(w=testcs[i].uc&testcs[j].uc)
					{/*collision between sets*/
						w<<=1;k=0;
						while(w)
						{
							while(!((w>>=1)&1) && w) k++;
							c=p->hand[k];	/*card causing collision*/
							if(w && ((bias==1 && testcs[i].type&NUMSET && testcs[j].type&SUITSET)
								|| (bias==2 && testcs[i].score >= testcs[j].score)
								|| (bias==3 && testcs[i].score < testcs[j].score)))
							{ /*reduce set i*/
								chng=1;
								for(x=1;!CDEQU(testcs[i].set[x],c) && x<=testcs[i].ns;x++);
								testcs[i].set[x]=nulcard;
								if(c.n==ACE && testcs[i].set[testcs[i].ns].n==ACE) testcs[i].set[testcs[i].ns]=nulcard;
								compacthand(testcs[i].set,&testcs[i].ns);
								checksets(&testcs[i],p);
								score(&testcs[i],p);
							}
							else if(w)
							{ /*reduce set j*/
								chng=1;
								for(x=1;!CDEQU(testcs[j].set[x],c) && x<=testcs[j].ns;x++);
								testcs[j].set[x]=nulcard;
								if(c.n==ACE && testcs[j].set[testcs[j].ns].n==ACE) testcs[j].set[testcs[j].ns]=nulcard;
								compacthand(testcs[j].set,&testcs[j].ns);
								checksets(&testcs[j],p);
								score(&testcs[j],p);
							}
						}
					}
				}
			}
			for(j=0,sc=0;j<=ncs;j++) sc+=testcs[j].score;
			if(sc>best)
			{ /*new best set*/
				best=sc;
				for(j=0;j<=ncs;j++) selcs[j]=testcs[j];
			}
			if(currst==ncs) currst=0; else currst++;
			if(bias==3) bias=1; else bias++;
		}
	}
	
	for(j=0;j<=p->nchld;j++) cdscs[CARD2(p->hand[j])]=0; /*zeroise scores of player cards*/
	/* The section below prevents the autoplayer being left with just two
	 * cards, which cannot be submitted (min 3), and meaning it must wait
	 * for cards to arrive which can be added to existing sets, which the
	 * other players will also be looking out for.
	 * NB. this will only reduce submissions by one card, and only where
	 * permissible.
	 */
	/*work out number of cards to be submitted by this combination of sets*/
	for(i=0,w=0;i<=ncs;i++) if(selcs[i].type&ADD)
	{
		w++; /*at least one card can be submitted*/
		for(j=1,x=selcs[i].set[1].n;j<=selcs[i].nd;j++) if(x+1==selcs[i].dep[j].n || x-1==selcs[i].dep[j].n)
		{
			w++; x=selcs[i].dep[j].n;
		}
	}
	else if(selcs[i].type&SUB) w+=selcs[i].ns;
	if(p->nchld-w==2) /*retain one further card to enable later submissions*/
	{
		for(i=0,w=0;i<=ncs && !w;i++) if(selcs[i].type&SUB && selcs[i].ns>=4)
		{/*prevent submission of one of the cards of the set*/
			for(j=selcs[i].nd;j>=2;j--) selcs[i].dep[j]=selcs[i].dep[j-1]; /*create space in dep list*/
			selcs[i].dep[1]=selcs[i].set[selcs[i].ns--];
			selcs[i].nd++;
			w=1; /*finished*/
			cdscs[CARD2(selcs[i].dep[1])]+=20000; /*ensure this card retained*/
		}
		else if(selcs[i].type&ADD)
		{
			if(selcs[i].type&NUMSET || (selcs[i].type&SUITSET 
				&& selcs[i].set[1].n!=selcs[i].dep[1].n-1 && selcs[i].set[1].n!=selcs[i].dep[1].n+1))
			{/* only one addable card - remove it to the dep list*/
				for(j=selcs[i].nd;j>=2;j--) selcs[i].dep[j]=selcs[i].dep[j-1]; /*create space in dep list*/
				selcs[i].dep[1]=selcs[i].set[1];
				selcs[i].set[1]=nulcard;
				selcs[i].ns--; selcs[i].nd++;
				selcs[i].type&= ~ADD;
				selcs[i].type|= SUB;
				w=1;
				cdscs[CARD2(selcs[i].dep[1])]+=20000; /*ensure this card retained*/
			}
			else
			{/* reduce number of addable cards of this set to just one*/
				for(j=selcs[i].nd;j>=2;j--) selcs[i].dep[j]=selcs[i].dep[j-1]; /*create space in dep list*/
				selcs[i].dep[1]=nulcard;
				selcs[i].nd++;
				cdscs[CARD2(selcs[i].dep[2])]+=20000; /*ensure this card retained*/
				w=1;
			}
		}
	}
				
	/*generate card scores here*/
	for(i=0;i<=ncs;i++)
	{
		if(selcs[i].type&SUITSET)
		{
			if(selcs[i].ns)
			{
				for(j=1;j<=selcs[i].ns;j++) cdscs[CARD2(selcs[i].set[j])]+=selcs[i].score;
				for(j=1;j<=selcs[i].nd;j++) 
				{
					w=selcs[i].dep[j-1].n;x=selcs[i].dep[j].n;
					k=selcs[i].dep[j+1].n;
					if(selcs[i].dep[j].n<selcs[i].set[1].n)
					cdscs[CARD2(selcs[i].dep[j])]+=(selcs[i].score*j)/22 +
						((j>1?(w<x?x-w:w-x):0) * selcs[i].score)/30 +
						((j<selcs[i].nd?(k<x?x-k:k-x):0) * selcs[i].score)/30;

					else cdscs[CARD2(selcs[i].dep[j])]+=(selcs[i].score*(9-j))/22 +
						((j>1?(w<x?x-w:w-x):0) * selcs[i].score)/30 +
						((j<selcs[i].nd?(k<x?x-k:k-x):0) * selcs[i].score)/30;
				}
			}
			else /*no submissible cards*/
			{
				for(j=1;j<=selcs[i].nd;j++)
				{
					w=selcs[i].dep[j-1].n;x=selcs[i].dep[j].n;
					k=selcs[i].dep[j+1].n;
					cdscs[CARD2(selcs[i].dep[j])]+=selcs[i].score +
						((j>1?(w<x?x-w:w-x):0) * selcs[i].score)/12 +
						((j<selcs[i].nd?(k<x?x-k:k-x):0) * selcs[i].score)/12;
				}
			}
		}
		else /*numset*/
		{
			for(j=1;j<=selcs[i].ns;j++) cdscs[CARD2(selcs[i].set[j])]+=selcs[i].score*(j==4?(1/2):1);
			for(j=1;j<=selcs[i].nd;j++) cdscs[CARD2(selcs[i].dep[j])]+=selcs[i].score*(j==4?(1/2):1);
		}
	}

	for(j=0;j<=p->nchld;j++) cdsc[j]=cdscs[CARD2(p->hand[j])];
	l=rand();
	if(p->level==1) return (USHORT)(l&1);
	else if(p->level<=9)
	{
		if(p->level<(l%11)+1) return (USHORT)(l&256);
	}
	
	/* the next loop checks to see if any cards can be added to an existing
	 * set after the acquisition of one further card, a facility which was
	 * overlooked in the original program design
	 */
	for(i=0;i<=p->nchld;i++)
	{
		for(j=0;j<5 && fupset[j]->ncard;j++)
		{
			s=fupset[j]->cards;
			for(k=1;k<=13 && ISNUL(s[k]);k++);
			w=k+fupset[j]->ncard-1; /*last card*/
			if(s[k].s==s[k+1].s && s[k].s==p->hand[i].s) /*suitset*/
			{
				x=p->hand[i].n;
				if(x==s[k].n-2 && !(p->mem[CARD(x+1,s[k].s)]&INFUPSET)) cdsc[i]+=1500;
				if(x==s[w].n+2 && !(p->mem[CARD(x-1,s[k].s)]&INFUPSET)) cdsc[i]+=1500;
				if(x==ACE && s[w].n==QUEEN && !(p->mem[CARD(KING,s[k].s)]&INFUPSET)) cdsc[i]+=1500;
			}
		}
	}
	
	if(p->nchld==1)
	{
		for(i=0;i<=ncs;i++) if(selcs[i].type&ADD && CDEQU(selcs[i].set[1],p->hand[0])) return 1;
		return 0; /* last card not addable, so recommend to ignore it*/
	}
	
	/* the section below causes the last selected card to be dropped if it
	 * only belongs to one set of two cards, and the other member is about
	 * to be dropped
	 */
	j=cdsc[k=0];
	for(i=1;i<=p->nchld;i++) if(cdsc[i]<j) j=cdsc[k=i];
	if(k!=0 && cdsc[0]>1690 /* || cdsc[0]>2200*/) return 1; /*keep last selected card*/
	else 
	{
		for(i=w=x=0;i<=ncs;i++)
		{
			for(j=1;j<=selcs[i].nd;j++) if(CDEQU(selcs[i].dep[j],p->hand[0]) && selcs[i].nd==2 && !selcs[i].ns)
			{
				w++;
				x=i;
			}
		}
		if(w==1)
		{
			if(CDEQU(selcs[x].dep[1],p->hand[0])) j=2; else j=1;
			for(i=0;i<=p->nchld;i++) if(CDEQU(p->hand[i],selcs[x].dep[j]) && i==k) return 0; /*drop card*/
		}
	}
	
	for(j=0;j<=ncs;j++) if(selcs[j].uc&1) return 1; /* last chosen card being submitted in set*/
	return 0; /*last selected card not being submitted in a set,
				and not within two of an adjacent card*/
}


VOID	checksets(struct condset *c,struct playerinfo *p)
{ /*reconstruct reduced set*/
	register	USHORT	i,n=0,nconc,setfound;
	struct	card	t[16];
	for(i=0;i<=15;i++) t[i]=nulcard;
	if(c->type&SUITSET && c->type&SUB)
	{
		for(i=1;i<=c->ns;i++) if(c->set[i].n==ACE && i>1) t[14]=c->set[i];
			else t[c->set[i].n]=c->set[i];
		for(i=1;i<=c->nd;i++) if(c->dep[i].n==ACE && i>1) t[14]=c->dep[i];
			else t[c->dep[i].n]=c->dep[i];
		n=c->nd+c->ns;
		nconc=0;
		c->nd=c->ns=0;
		for(i=1;i<=15;i++)
		{
			if(!ISNUL(t[i])) nconc++;
			else
			{
				if(nconc>=3) while(nconc) c->set[++c->ns]=t[i-nconc--];
				else while(nconc) c->dep[++c->nd]=t[i-nconc--];
			}
		}
	}
	else if(c->type&NUMSET && c->type&SUB)
	{
		if(c->ns==2 || c->ns==1) for(c->nd=0;c->ns;)c->dep[++c->nd]=c->set[c->ns--];
	}
	if(!(c->ns || c->nd)) c->type=NOTSET;	/*no cards in this set*/
	else for(n=1,c->uc=0;n<=c->ns;n++)
		for(i=setfound=0;i<=p->nchld && !setfound;i++) if(setfound=CDEQU(c->set[n],p->hand[i])) c->uc|=(1<<i);

}
