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