[comp.sources.amiga] hearts--card game of the same name. 1 of 1.

ahh@j.cc.purdue.edu (Brent L. Woods) (03/13/88)

Program Name:  hearts
Submitted By:  griggs%cory.Berkeley.EDU@ucbvax.berkeley.edu (Ted Griggs)
Summary:  A program that allows you to play the card game "hearts" with
          three computer opponents.
Poster Boy:  Brent Woods  (ahh@j.cc.purdue.edu)
Tested.  Shar archive.  Part 1 of 1.

NOTES:  This program came to us, for the most part, without documentation.
        However, I believe that the only skills one needs to play the
        game is a knowledge of how to play hearts.  The program appears
        to run just fine, but I can't attest to how closely it plays
        since I don't know how to play hearts.  I'm from Indiana; I
        learned eucre instead.  :-)  The following shar file contains
        some comments from the author, a makefile for Manx, and the
        source code itself.  It compiled under Manx 3.4a without any
        problems.



Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga

USENET:  ...!j.cc.purdue.edu!ahh     ARPANET:  ahh@j.cc.purdue.edu
BITNET:  PODUM@PURCCVM               PHONE:  +1 (317) 743-8421
USNAIL:  320 Brown St., #406  /  West Lafayette, IN  47906

================================================================

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	author
#	Makefile
#	hearts.c
# This archive created: Sat Mar 12 20:56:03 1988
# By:	Brent L. Woods (Co-Moderators Unlimited.)
cat << \SHAR_EOF > author
Here is a small program I wrote one Saturday afternoon a while back.
For those of you familiar with the card game "Hearts," this program
lets you play against 3 computer players (who also compete among
themselves).

Alas there are no comments since all I really wanted was to quickly
write the code.  Likewise, the user interface is not wonderful either -
all commands must be typed in uppercase, and when it asks for a letter
it really wants a letter, not just a return.

Otherwise, the logic gives the program a bit above the average skill level.
It plays an interesting game, and, I think, that will provide some
entertainment for Hearts enthusiasists.

Also, in order to really "win" you should beat all 3 computer players.


To compile under MANX:
cc hearts.c
ln hearts.o -lc -lm

Simple.  It also compiles under Lattice.

SHAR_EOF
cat << \SHAR_EOF > Makefile
CFLAGS = +pCD -Damiga

SOURCES=hearts.c
OBJECTS=hearts.o

all: $(OBJECTS)
	ln -o hearts $(OBJECTS) -lcl32 -lml32

.c.o: $(SOURCES)
	cc $(CFLAGS) $*.c
SHAR_EOF
cat << \SHAR_EOF > hearts.c


#include "stdio.h"
#include "exec/types.h"
#include "intuition/intuition.h"

#define INTSIZ 4
#define LEAD 0
#define FOLLOW 1
#define INTUITION_REV 29
#define HUMAN 3
#define SPADES 2
#define SPQUEEN 36
#define SPKING 37
#define SPACE 38
#define HEARTS 3
#define RUN 26
#define CLUBS 0
#define DIAMONDS 1
#define CLS (char)(0x0c)
#define CSI (char)(0x9b)

struct IntuitionBase *IntuitionBase, *OpenLibrary();

int ok=TRUE;
int start,first,taker,deck[52],out[52],trick[4],score[4],hscr[4];
int hand[4][13];
int outc[4][4];
int dump[4][4];
int pass[4][3];
int outi,outt,outp,outh,seed,turn;
ULONG seconds,micros;

int wt[53] =
{
  0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,13,0,0,
  1,1,1,1,1,1,1,1,1,1,1,1,1,
};

char cardname[13]=
{
  '2','3','4','5','6','7','8','9','T','J','Q','K','A',
};

char suitname[4]=
{
  'C','D','S','H',
};

int who[4][4]=
{
  {3,1,2,0},
  {0,2,3,0},
  {1,3,0,0},
  {2,0,1,0},
};

scrnx[4]={10,35,60,35,};
scrny[4]={9,3,9,15,};

char dirstr[4][8]=
{
  {"left"},
  {"right"},
  {"across"},
  {"hold"},
};

double rnd()
{
  double num;

  seed=((25173*seed)+13849) % 20000;
  if (seed<0) seed=-seed;
  num=(double)((double)(seed)/20000.0);
  return(num);
}

Shuffle(deck)
int deck[52];
{
  int i1,i2,i3,i;

  for(i=0;i<2000;i++)
  {
    i1=(int)(rnd()*52.0);
	i2=(int)(rnd()*52.0);
	i3=deck[i1];
	deck[i1]=deck[i2];
	deck[i2]=i3;
  }
}

SortHand(pl)
int pl;
{
  int i,j,k;

  for(i=0;i<13;i++)
    for(j=i;j<13;j++)
	  if(hand[pl][j]<hand[pl][i])
	  {
		k=hand[pl][i];
	    hand[pl][i]=hand[pl][j];
		hand[pl][j]=k;
	  }
}

Deal()
{
  int i,j;

  for(i=0;i<13;i++)
    for(j=0;j<4;j++)
	  hand[j][i]=deck[j+i*4];
  for(i=0;i<4;i++) SortHand(i);
}

TwoClubs()
{
  int i,j,st;

  for(i=0;i<13;i++)
    for(j=0;j<4;j++)
	  if (hand[j][i]==0) st=j;
  return(st);
}

PrintCard(card)
int card;
{
  int suit;

  suit=card/13;
  card-=13*suit;
  printf("%c%c ",cardname[card],suitname[suit]);
}

DisplayHand(pl)
int pl;
{
  int i,ii;

  for(ii=0;ii<4;ii++)
  {
    printf("%c%d%c%d%c",CSI,scrny[pl]+ii+1,';'
	       ,scrnx[pl]-5,'H');
    for(i=0;i<13;i++)
      if((hand[pl][i]!=-1)&&(hand[pl][i]/13==ii))PrintCard(hand[pl][i]);
	printf("    ");
  }
}

ParseCard(str)
char *str;
{
  int c=-1,i,good=FALSE;

  for(i=0;i<4;i++)
    if(str[1]==suitname[i])
	  c=i*13;
  if(c==-1)return(-1);
  for(i=0;i<13;i++)
    if(str[0]==cardname[i])
	{
	  c+=i;
	  good=TRUE;
	}
  if(good==FALSE)return(-1);
  if(HaveCard(HUMAN,c))
  {
	if(outt==0)
	{
	  if((c/13==HEARTS)&&(outh==FALSE))
	    if((SuitHand(HUMAN,CLUBS)==0)&&(SuitHand(HUMAN,DIAMONDS)==0)
		     &&(SuitHand(HUMAN,SPADES)==0))
		  return(c);
		else
		  return(-1);
	  return(c);
	}
    if(c/13==trick[0]/13) return(c);
    if(SuitHand(HUMAN,trick[0]/13)==0) return(c);
  }
  return(-1);
}

SuitPlayed(suit)
int suit;
{
  int c,i,ct=0;

  c=13*suit;
  for(i=0;i<outi;i++)
    if((out[i]>=c)&&(out[i]<c+13))ct++;
  return(ct);
}

SuitHand(pl,suit)
int pl,suit;
{
  int c,i,ct=0;

  c=13*suit;
  for(i=0;i<13;i++)
    if((hand[pl][i]>=c)&&(hand[pl][i]<c+13))ct++;
  return(ct);
}

HaveCard(pl,card)
int pl,card;
{
  int i;

  for(i=0;i<13;i++)
    if(hand[pl][i]==card)return(TRUE);
  return(FALSE);
}

CardOut(card)
int card;
{
  int i;

  for(i=0;i<outi;i++)
    if(out[i]==card)return(TRUE);
  return(FALSE);
}

PlayerSuit(suit)
int suit;
{
  int i,n=4;

  for(i=0;i<4;i++)
    if(outc[i][suit])n--;
  return(n);
}

SpadeSafe(pl)
int pl;
{
  if((SuitPlayed(SPADES)+SuitHand(pl,SPADES)==13))return(FALSE);
  if(SuitHand(pl,SPADES)==0)return(FALSE);
  if((HaveCard(pl,SPQUEEN)==FALSE)&&(HaveCard(pl,SPKING))&&
       (SuitPlayed(SPADES)/PlayerSuit(SPADES)<SuitHand(pl,SPADES)))
	   return(TRUE);
  if((HaveCard(pl,SPQUEEN)==FALSE)&&(HaveCard(pl,SPACE))&&
       (SuitPlayed(SPADES)/PlayerSuit(SPADES)<SuitHand(pl,SPADES)))
	   return(TRUE);
  if(((HaveCard(pl,SPQUEEN))||(HaveCard(pl,SPKING))||
      (HaveCard(pl,SPACE)))&&(SuitHand(pl,SPADES)<5))return(FALSE);
  return(TRUE);
}

PickHigh(pl,suit)
int pl,suit;
{
  int c,i,cmax,card=-1;

  c=suit*13;
  cmax=c+13;
  for(i=0;i<13;i++)
    if((hand[pl][i]>=c)&&(hand[pl][i]<cmax)&&(hand[pl][i]>card))
	    card=hand[pl][i];
  return(card);
}

PickLow(pl,suit)
int pl,suit;
{
  int c,i,cmax,card=999;

  c=suit*13;
  cmax=c+13;
  for(i=0;i<13;i++)
    if((hand[pl][i]>=c)&&(hand[pl][i]<cmax)&&(hand[pl][i]<card))
	    card=hand[pl][i];
  return(card);
}

PickUnder(pl,suit,cmax)
int pl,suit,cmax;
{
  int c,i,card=-1;

  c=suit*13;
  for(i=0;i<13;i++)
    if((hand[pl][i]>=c)&&(hand[pl][i]<cmax)&&(hand[pl][i]>card))
	    card=hand[pl][i];
  return(card);
}

HighOut(pl,suit)
int pl,suit;
{
  int c,i,plr,card=-1;

  c=suit*13;
  for(i=0;i<13;i++)
    for(plr=0;plr<4;plr++)
      if((hand[plr][i]>=c)&&(hand[plr][i]<c+13)&&(hand[plr][i]>card)&&
	      (plr!=pl))
	  card=hand[plr][i];
  return(card);
}

CardsLeft(pl)
int pl;
{
  int i,n=0;

  for(i=0;i<13;i++)
    if(hand[pl][i]!=-1)n++;
  return(n);
}

Desperate(pl)
int pl;
{
  int i;

  for(i=0;i<13;i++)
    if((hand[pl][i]!=SPQUEEN)&&(hand[pl][i]!=-1))return(hand[pl][i]);
  return(SPQUEEN);
}

NumberVoid(pl,suit)
int pl,suit;
{
  int i,vd=0;

  for(i=0;i<4;i++)
    if((i!=pl)&&(outc[i][suit]))vd++;
  return(vd);
}

SafestSuit(pl)
int pl;
{
  int i,s=999;

  for(i=0;i<4;i++)
    if((SuitPlayed(i)<=s)&&(i!=SPADES)&&(SuitHand(pl,i)>0))s=i;
  return(s);
}

BestLead(pl)
int pl;
{
  int i;

  for(i=0;i<4;i++)
  {
	if(i!=SPADES)
	{
      if(((i==HEARTS)&&(outh))||(i!=HEARTS))
	  {
	    if(SuitHand(pl,i)!=0)
	    {
          if((SuitPlayed(i)+SuitHand(pl,i)==12)&&(PickLow(pl,i)<HighOut(pl,i)))
	             return (PickUnder(pl,i,HighOut(pl,i)));
	    }
	  }
	}
  }
  i=SafestSuit(pl);
  if(SuitPlayed(i)+SuitHand(pl,i)==13)i=999;
  if((PickLow(pl,i)>HighOut(i))&&(NumberVoid(pl,i)>=2))i=999;
  if((i!=999)&&(((i==HEARTS)&&(outh))||(i!=HEARTS)))
  {
      return(PickHigh(pl,i));
  }
  for(i=0;i<4;i++)
    if((PickLow(pl,i)<HighOut(pl,i))&&(SuitHand(pl,i)!=0)&&
	    (SuitHand(pl,i)>=13-SuitPlayed(i))&&
		(SuitHand(pl,i)+SuitPlayed(i)!=13)&&
		(((i==HEARTS)&&(outh))||(i!=HEARTS)))
	{
	  return (PickUnder(pl,i,HighOut(pl,i)));
	}
  i=VoidSuit(pl);
  if((CardsLeft(pl)>1)&&(i!=SPQUEEN)&&(i!=SPACE)&&(i!=SPKING))
    return(i);
  return(Desperate(pl));
}

MustFollow(pl)
int pl;
{
  int suit;

  suit=trick[0]/13;
  if((SuitHand(pl,suit)==0))return(FALSE);
  return(TRUE);
}

HandWeight()
{
  int i,w=0;

  for(i=0;i<outt;i++) w+=wt[trick[i]+1];
  return(w);
}

HighTrick()
{
  int c,i,card=-1;

  c=trick[0]/13;
  c*=13;
  for(i=0;i<outt;i++)
    if((trick[i]>=c)&&(trick[i]<c+13)&&(trick[i]>card))card=trick[i];
  return(card);
}

BestFollow(pl)
int pl;
{
  int suit,i;

  suit=trick[0]/13;
  if(suit==SPADES)
  {
    if(((HighTrick()==SPACE)||(HighTrick()==SPKING))
       &&(HaveCard(pl,SPQUEEN))) return(SPQUEEN);
    if((HighTrick()==SPACE)&&(HaveCard(pl,SPKING))) return(SPKING);
	if((outt==3)&&(PickHigh(pl,SPADES)>SPQUEEN)&&
	    (HighTrick()!=SPQUEEN))return(PickHigh(pl,SPADES));
    if(PickUnder(pl,SPADES,SPQUEEN)!=-1)return(PickUnder(pl,SPADES,SPQUEEN));
  }
  if((suit==HEARTS)&&(outp==HighCarder(first))&&
      (PickHigh(pl,HEARTS)<HighTrick()))return(PickLow(pl,HEARTS));
  if((suit==HEARTS)&&(outp==HighCarder(first))&&
      (HandWeight()<5))return(PickHigh(pl,HEARTS));
  if(HandWeight()<2)
  {
	if(((suit==SPADES)&&(CardOut(SPQUEEN)))||(suit!=SPADES))
	{
	    return(PickHigh(pl,suit));
	}
  }
  if(PickUnder(pl,suit,HighTrick())!=-1)
        return (PickUnder(pl,suit,HighTrick()));
  return(PickHigh(pl,suit));
}

HighWeight(pl)
int pl;
{
  int c,i,card=-1,w=0;

  for(i=0;i<13;i++)
    if(wt[hand[pl][i]+1]>=w)
	{
	  if(wt[hand[pl][i]+1]>w)
	  {
	    card=hand[pl][i];
		w=wt[hand[pl][i]+1];
	  }
	  else
	  {
	    c=hand[pl][i] % 13;
	    if(c>=card%13) card=hand[pl][i];
	  }
	}
  return(card);
}

SafeHeart(pl)
int pl;
{
  int c;

  c=PickHigh(pl,HEARTS)%13;
  if(SuitHand(pl,HEARTS)-1>13-c)
      return(PickUnder(pl,HEARTS,PickHigh(pl,HEARTS)));
  return(-1);
}

VoidSuit(pl)
int pl;
{
  int i,suit=999;

  for(i=0;i<4;i++)
    if((SuitHand(pl,i)<suit)&&(SuitHand(pl,i)>0)&&
	    (SuitPlayed(i)+SuitHand(pl,i)!=13))
	  if((i!=SPADES)||((i==SPADES)&&(HaveCard(pl,SPQUEEN)==FALSE)))suit=i;
  if(suit!=999)return(PickHigh(pl,suit));
  for(i=3;i>-1;i--)
    if(SuitHand(pl,i)>0)return(PickLow(pl,i));
  return(-1);
}

BestDump(pl)
int pl;
{
  int i;

  if(outi<4)
  {
    i=VoidSuit(pl);
	if(i!=SPQUEEN)
	  return(i);
	else
	  for(i=0;i<4;i++)
	    if((SuitHand(pl,i)>0)&&(PickHigh(pl,i)!=SPQUEEN))
		  return(PickHigh(pl,i));
  }
  if(HaveCard(pl,SPQUEEN))return(SPQUEEN);
  if((outp!=HighCarder(first))&&(SuitHand(pl,HEARTS)!=0)&&
      (outp!=999)) return(HighWeight(pl));
  if((HaveCard(pl,SPKING))&&(SuitHand(pl,SPADES)<5))return(SPKING);
  if((HaveCard(pl,SPACE))&&(SuitHand(pl,SPADES)<5))return(SPACE);
  if((outp<0)&&(HighWeight(pl)!=-1)&&(SuitHand(pl,HEARTS)!=0))
         return(HighWeight(pl));
  if((SuitHand(pl,HEARTS)!=0)&&(SafeHeart(pl)!=-1))return(SafeHeart(pl));
  return(VoidSuit(pl));
}

RemoveCard(pl,card)
int pl,card;
{
  int i;
  for(i=0;i<13;i++)
    if(hand[pl][i]==card)hand[pl][i]=-1;
}

PlayCard(pl,card)
int pl,card;
{

  trick[outt]=card;
  if(card/13!=trick[0]/13)outc[pl][trick[0]/13]=TRUE;
  dump[pl][card/13]++;
  outt++;
  RemoveCard(pl,card);
}

HumanCard()
{
  int i,play;
  char str[8];

  do
  {
    printf("%c%d%c%d%c",CSI,scrny[HUMAN]+6,';',scrnx[HUMAN]-6,'H');
    printf(" Your play: ");
    scanf("%s",str);
    printf("%c%d%c%d%c",CSI,scrny[HUMAN]+6,';',scrnx[HUMAN]-6,'H');
    printf("               ");
	if(str[0]=='/')
	  for(i=0;i<4;i++)
	  {
	    DisplayHand(i);
	  }
    play=ParseCard(str);
  }
  while(play==-1);
  return(play);
}

DoPlayer(pl,type)
int pl,type;
{
  int play;

  if(pl==HUMAN)DisplayHand(pl);
  if((outi==0)&&(type==LEAD))
  {
    play=0;
  }
  else
  {
    if (pl==HUMAN)
	{
	play=HumanCard();
	}
	else
	{
	  if (type==LEAD)
	  {
	    if (SpadeSafe(pl))
		{
		  if(PickHigh(pl,SPADES)<SPQUEEN)
		    play=PickHigh(pl,SPADES);
		  else
		    play=PickUnder(pl,SPADES,SPQUEEN);
		}
		else
		{
		  play=BestLead(pl);
		}
      }
	  else
	  {
	    if (MustFollow(pl))
		{
		  play=BestFollow(pl);
		}
		else
		{
		  play=BestDump(pl);
		}
      }
	}
  }
  PlayCard(pl,play);
  printf("%c%d%c%d%c",CSI,scrny[pl],';',scrnx[pl],'H');
  printf("Player %d plays: ",pl);
  PrintCard(play);
  start++;
  if(start==4) start=0;
}

HighCarder(start)
int start;
{
  int i,h,c,card=-1;

  c=trick[0]/13;
  c*=13;
  for(i=0;i<4;i++)
    if((trick[i]>=c)&&(trick[i]<c+13)&&(trick[i]>card))
	{
	  card=trick[i];
	  h=i;
	}
  c=start;
  for(i=0;i<h;i++)
  {
    c++;
	if(c==4)c=0;
  }
  return(c);
}

TallyScore(pl)
int pl;
{
  int i;

  for(i=outi;i<outi+4;i++)
    out[i]=trick[i-outi];
  outi=outi+4;
  hscr[pl]+=HandWeight();
  if(HandWeight()>0)outh=TRUE;
  for(i=0;i<4;i++)
    if((HandWeight()>0)&&(outp!=pl))
	  if(outp==999)
	    outp=pl;
	  else
	    outp=-1;
}

ScoreHand()
{
  int i,ii;

  for(i=0;i<4;i++)
    if(hscr[i]==RUN)
	{
	  for(ii=0;ii<4;ii++)
	    if(i!=ii)score[ii]+=RUN;
	}
	else
	{
	  score[i]+=hscr[i];
	}
  for(i=0;i<4;i++)
  {
    printf("%c%d%c%d%c",CSI,scrny[i],';',scrnx[i],'H');
    printf("Player %d score: %d\n",i,score[i]);
  }
  for(i=0;i<4;i++)
    if(score[i]>99)exit(TRUE);
}

PassCard(pl)
int pl;
{
  if(SpadeSafe(pl)==FALSE)return(PickHigh(pl,SPADES));
  if((pass[pl][0]<HEARTS*13)&&(pass[pl][1]<HEARTS*13)&&
     (pass[pl][2]<HEARTS*13)&&(SafeHeart(pl)!=-1)&&
	 (SuitHand(pl,HEARTS)!=0))return(SafeHeart(pl));
  return(VoidSuit(pl));
}

AddCard(pl,card)
int pl,card;
{
  int i;

  for(i=0;i<13;i++)
    if(hand[pl][i]==-1)
	{
	  hand[pl][i]=card;
	  card=-1;
	}
}

DoPass()
{
  int i,pl,card,t;

  for(pl=0;pl<4;pl++)
    for(i=0;i<3;i++)
    {
      if(pl==3)
	  {
	    DisplayHand(pl);
        card=HumanCard();
	  }
	  else
	    card=PassCard(pl);
	pass[pl][i]=card;
	RemoveCard(pl,card);
	}
  t=turn%4;
  for(pl=0;pl<4;pl++)
    for(i=0;i<3;i++)
	  AddCard(pl,pass[who[pl][t]][i]);
  for(pl=0;pl<4;pl++) SortHand(pl);
}

main()
{
  int i,ii;
  char str[8];

  IntuitionBase = (struct IntuitionBase *)
                     OpenLibrary("intuition.library",INTUITION_REV);
  for(i=0;i<52;i++) deck[i]=i;
  turn=4;
  printf("%c",CLS);
  while (ok)
  {
	CurrentTime(&seconds,&micros);
	seed=micros;
    Shuffle(deck);
	Deal();
	outt=0;
	outi=0;
	outp=999;
	outh=TRUE;
    printf("%c%d%c%d%c",CSI,scrny[HUMAN],';',1,'H');
	printf("PASS: %s",dirstr[turn%4]);
	for(i=0;i<4;i++)
	  for(ii=0;ii<3;ii++)
	    pass[i][ii]=-1;
	if (turn%4!=3)DoPass();
    printf("%c%d%c%d%c",CSI,scrny[HUMAN],';',1,'H');
	printf("                  ");
	for(ii=0;ii<4;ii++)
	{
	  printf("%c%d%c%d%c",CSI,scrny[ii],';',scrnx[ii],'H');
	  printf("                         ");
	}
	outi=0;
	outp=999;
	outh=FALSE;
    for(i=0;i<4;i++) hscr[i]=0;
	for(i=0;i<4;i++)
	  for(ii=0;ii<4;ii++)
	  {
	    outc[i][ii]=FALSE;
		dump[i][ii]=0;
	  }
    for(i=0;i<13;i++)
	{
      for(ii=0;ii<4;ii++) trick[ii]=0;
	  if (i==0)
	    start=TwoClubs();
	  else
	    start=taker;
	  first=start;
	  outt=0;
	  DoPlayer(start,LEAD);
	  DoPlayer(start,FOLLOW);
	  DoPlayer(start,FOLLOW);
	  DoPlayer(start,FOLLOW);
	  taker=HighCarder(first);
	  TallyScore(taker);
      printf("%c%d%c%d%c",CSI,scrny[HUMAN]+6,';',scrnx[HUMAN],'H');
	  printf("Type letter: ");
      scanf("%s",str);
      printf("%c%d%c%d%c",CSI,scrny[HUMAN]+6,';',scrnx[HUMAN],'H');
	  printf("                 ");
	  for(ii=0;ii<4;ii++)
	  {
	    printf("%c%d%c%d%c",CSI,scrny[ii],';',scrnx[ii],'H');
	    printf("                         ");
	  }
	  turn++;
	}
	ScoreHand();
  }
}

SHAR_EOF
#	End of shell archive
exit 0