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,µs);
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