/********************************************************** * PROGRAM: Spades * * AUTHOR: Gregory M. Stelmack * * COMPILER: SAS/C 5.10 * * Copyright (C) 1988 Lattice, Inc. * * Copyright (C) 1990 SAS Institute, Inc. * * VESRION DATES: * * Version 1.0 -- February 5, 1990 * * Version 1.1 -- April 28, 1990 * * Images used for cards -- file "Spades.images" * * must be in current directory to run. * * Title graphics added. New routine for * * choosing dealer. * * Version 1.11 -- August 23, 1990 * * Fixed memory freeing bug. Added some prompts. * * Version 1.12 -- October 3, 1990 * * Removed LIBRARY_VERSION from OpenLibrary() * * calls, replacing it with zero. An attempt to * * let it run under pre-1.3. * * Version 1.20 -- April 4, 1991 * * Error messages now pause. Revised strategy. * * Window can be sized and dragged, and screen * * moved to back to allow other things to be * * done. * * * * Tab stops set to 8,16,24,32,40,48,... for this listing. * **********************************************************/ /* Include files */ #include <exec/types.h> #include <exec/memory.h> #include <intuition/intuition.h> #include <exec/libraries.h> #include <graphics/gfxmacros.h> #include <proto/all.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> #include <fcntl.h> #include "Spades.proto" /* Structures needed for libraries */ struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; struct Library *DiskfontBase; /* Intuition Structures */ struct Screen *CustScr; struct Window *Wdw; struct Viewport *WVP; struct IntuiMessage *Message; /************** Program Constants **************/ #define RP Wdw->RPort /* Raster Port for Graphics Routines */ #define PENS 8 /* Number of Pens */ #define DEPTH 3 /* Number of BitPlanes */ #define MLEFT 1 /* Left Mouse Button Pressed */ #define MRIGHT 2 /* Right Mouse Button Pressed */ #define DIAMONDS 0 /* Suit Definitions */ #define CLUBS 1 #define HEARTS 2 #define SPADES 3 #define WHITE 0xFFF /* Palette Data */ #define RED 0xF00 #define GREEN 0x0F0 #define BLUE 0x00F #define CYAN 0x0FF #define PURPLE 0xF0F #define YELLOW 0xFF0 #define BLACK 0x000 #define CARDMEMSIZE 52*126*2*3 /* Memory needed for card data */ /************** Color Map Data *****************/ static USHORT colormap[PENS] = { BLUE, /* Pen 0 */ BLACK, /* Pen 1 */ RED, /* Pen 2 */ GREEN, /* Pen 3 */ WHITE, /* Pen 4 */ YELLOW, /* Pen 5 */ PURPLE, /* Pen 6 */ CYAN /* Pen 7 */ }; #define BLUP 0 #define BLKP 1 #define REDP 2 #define GRNP 3 #define WHTP 4 #define YELP 5 #define PURP 6 #define CYNP 7 /************** Text Structure *****************/ struct TextAttr StdFont = { "topaz.font", TOPAZ_EIGHTY, FS_NORMAL, FPF_ROMFONT, }; /*********** NewScreen Structure **********/ struct NewScreen NewCustScr = { 0,0, /* Left Edge, Top Edge */ 320,200,DEPTH, /* Width, Height, Depth */ WHTP,BLUP, /* Detail Pen, Block Pen */ NULL, /* View Mode */ CUSTOMSCREEN, /* Screen Type */ &StdFont, /* Pointer to Font */ NULL, /* Pointer to Title Text */ NULL, /* Pointer to Screen Gadgets */ NULL, /* Pointer to CustomBitMap */ }; /**************** Gadgets ******************/ struct IntuiText IText1 = { YELP,BLKP,JAM2, /* front and back text pens, drawmode and fill byte */ -1,0, /* XY origin relative to container TopLeft */ NULL, /* font pointer or NULL for default */ "HIDE", /* pointer to text */ NULL /* next IntuiText structure */ }; struct Gadget HideGadget = { NULL, /* next gadget */ 265,1, /* origin XY of hit box relative to window TopLeft */ 32,8, /* hit box width and height */ NULL, /* gadget flags */ RELVERIFY+TOPBORDER, /* activation flags */ BOOLGADGET+GZZGADGET, /* gadget type flags */ NULL, /* gadget border or image to be rendered */ NULL, /* alternate imagery for selection */ &IText1, /* first IntuiText structure */ NULL, /* gadget mutual-exclude long word */ NULL, /* SpecialInfo structure */ NULL, /* user-definable data */ NULL /* pointer to user-definable data */ }; /*********** NewWindow Structure **********/ struct NewWindow NewWdw = { 0,0, /* Left Edge, Top Edge */ 320,200, /* Width, Height */ BLKP,WHTP, /* Block Pen, Detail Pen */ MOUSEBUTTONS | CLOSEWINDOW | GADGETUP, /* IDCMP Flags */ SMART_REFRESH | ACTIVATE | GIMMEZEROZERO | RMBTRAP | WINDOWCLOSE, &HideGadget, /* Pointer to First Gadget */ NULL, /* Pointer to Check Mark image */ "Spades 1.20, by Greg Stelmack", /* Title */ NULL, /* Pointer to Screen structure */ NULL, /* Pointer to custom Bit Map */ 320,100, /* Minimum Width, Height */ 0,0, /* Maximum Width, Height */ CUSTOMSCREEN /* Type of Screen it resides on */ }; /************************* Image Data *************************/ struct Image CardImage = { 0, 0, /* LeftEdge, TopEdge */ 42, 42, 3, /* Width, Height, Depth */ NULL, /* Image Data */ 7, 0, /* PlanePick, PlaneOnOff */ NULL /* *NextImage */ }; #include "Spades.h" /*************************** Arrays ***************************/ /*************************************** * Deck: status of cards. * * 0 = Undealt or played. * * 1 thru 4 = Player who holds card. * ***************************************/ int Deck[52] = {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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /*************************************** * Hand: cards in each player's hand. * * 0 = Played. * * 1 thru 52 = Card number. * ***************************************/ int Hand[4][13] = { {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,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0} }; /******************************************** * Bid: number of tricks bid by each player. * ********************************************/ int Bid[4] = {0,0,0,0}; /******************************************** * Mode: aggressiveness of each player. * * The number in this array is added to * * the strength of the hand to determine * * the number of tricks to bid. Higher * * number equals more tricks. * ********************************************/ int Mode[4] = {0,0,0,0}; /******************************************** * HighCardLeft: the highest unplayed card * * in each suit. * ********************************************/ int HighCardLeft[4] = {0,0,0,0}; /******************************************** * Card: the card played in a trick by each * * player. * ********************************************/ int Card[4] = {0,0,0,0}; /******************************************** * Value: the point value of each card held * * in a hand. Used by computer to determine* * which card to play. * ********************************************/ int Value[13] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; /******************************************** * SuitNumber: the number of cards in each * * suit held by a player. * ********************************************/ int SuitNumber[4] = {0,0,0,0}; /******************************************** * CardX & CardY: The X and Y positions for * * the played card for each player. * * MsgX & MsgY: The X and Y positions for * * single character messages for each * * player. * ********************************************/ int CardX[4] = {100,1,100,209}; int CardY[4] = {99,60,1,60}; int MsgX[4] = {116,45,116,195}; int MsgY[4] = {96,84,50,84}; /******************************************** * OutOfSuit: Whether or not a player is out * * of a suit. * ********************************************/ BOOL OutOfSuit[4][4] = { {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} }; /******************** Other Global Variables ******************/ char *String = " "; /* Temporary String Storage */ int PlayerScore=0, CompScore=0, PlayerTricks=0, CompTricks=0; int HandLead=0, Button=0, TrickLead=0, PlayerBid=0, CompBid=0; int ShortSuit=0, LongSuit=0, HighCard=0, Winner=0; BOOL SpadePlayed=FALSE, AllDone=FALSE, ScreenOpen=FALSE; char *CardData; SHORT Mx=0, My=0; /* Mouse Coordinates */ /********************* Program Begins Here ********************/ /********************************************************** * Function: main * * Purpose: Open everything, play the game, close * * everything. * **********************************************************/ main() { int fh; unsigned int count=0; /* Open the Intuition and Graphics libraries. */ IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",0); if (IntuitionBase == NULL) PError("Can't open Intuition Library"); GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0); if (GfxBase == NULL) PError("Can't open Graphics Library"); /* Load Graphic Images */ CardData = (char *)AllocMem(CARDMEMSIZE,MEMF_CHIP); if (!CardData) PError("Could not allocate image memory!"); fh = open("Spades.images",O_RDONLY,0); if (fh==-1) PError("Can't open images file!"); count = read(fh,CardData,CARDMEMSIZE); if (count==-1) PError("Error reading images file!"); /* Open the screen and window */ if ((NewWdw.Screen = CustScr = (struct Screen *)OpenScreen(&NewCustScr)) == NULL) PError("Can't open new screen"); ScreenOpen = TRUE; if (( Wdw = (struct Window *)OpenWindow(&NewWdw)) == NULL) PError("Can't open new window"); /* Find the viewport and load color map */ WVP = (struct ViewPort *)ViewPortAddress(Wdw); LoadRGB4(WVP,&colormap[0],PENS); /* Seed random number generator with TIMER */ srand(time(NULL)); /* Start Game */ Spades(); /* Close Everything */ WrapUp(); } /********************************************************** * Function: PError * * Parameters: s -- pointer to string to print * * Return Values: None * * Purpose: Handle a fatal error * **********************************************************/ void PError(s) char *s; { char c=' '; printf("%s\n",s); if (!ScreenOpen) { printf("Please press RETURN..."); c=getchar(); } WrapUp(); } /********************************************************** * Function: WrapUp * * Parameters: none * * Return Values: none * * Purpose: close everything and exit * **********************************************************/ void WrapUp() { if (CardData) FreeMem((char *)CardData,CARDMEMSIZE); if (Wdw) CloseWindow(Wdw); if (CustScr) CloseScreen(CustScr); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); exit(0); } /********************************************************** * Function: Spades * * Parameters: None * * Return Values: None * * Purpose: Play a game of spades. Loop until someone * * scores 500. * **********************************************************/ void Spades() { Title(); AllDone=FALSE; /* Loop until player no longer wants to play. */ while (!AllDone) { PlayerScore=CompScore=0; HandLead=FindDealer(); /* Loop until someone reaches 500 and there is no tie. */ while (((PlayerScore<500)&&(CompScore<500))||(PlayerScore==CompScore)) { SetUpScreen(); InitVars(); PrintScore(); DealCards(); GetBids(); PrintBids(); PlayHand(); } FinishRoutine(); } } /********************************************************** * Function: Title * * Parameters: none * * Return Values: none * * Purpose: draw title graphics. * **********************************************************/ void Title() { int i=0; SetRast(RP,BLUP); /* Clear screen */ SetAPen(RP,YELP); SetBPen(RP,BLUP); /* Draw card backs */ for (i=0; i<6 ; i++) { DrawImage(RP,&Image25,(i*50)+15,65); } /* Flip S */ Delay(5); DrawImage(RP,&Image24,15,65); Delay(5); DrawImage(RP,&Image23,15,65); Delay(5); DrawImage(RP,&Image22,15,65); Delay(5); DrawImage(RP,&Image21,15,65); Delay(5); DrawImage(RP,&Image17,15,65); Delay(5); DrawImage(RP,&Image18,15,65); Delay(5); DrawImage(RP,&Image19,15,65); Delay(5); DrawImage(RP,&Image20,15,65); /* Flip P */ Delay(5); DrawImage(RP,&Image24,65,65); Delay(5); DrawImage(RP,&Image23,65,65); Delay(5); DrawImage(RP,&Image22,65,65); Delay(5); DrawImage(RP,&Image21,65,65); Delay(5); DrawImage(RP,&Image13,65,65); Delay(5); DrawImage(RP,&Image14,65,65); Delay(5); DrawImage(RP,&Image15,65,65); Delay(5); DrawImage(RP,&Image16,65,65); /* Flip A */ Delay(5); DrawImage(RP,&Image24,115,65); Delay(5); DrawImage(RP,&Image23,115,65); Delay(5); DrawImage(RP,&Image22,115,65); Delay(5); DrawImage(RP,&Image21,115,65); Delay(5); DrawImage(RP,&Image9,115,65); Delay(5); DrawImage(RP,&Image10,115,65); Delay(5); DrawImage(RP,&Image11,115,65); Delay(5); DrawImage(RP,&Image12,115,65); /* Flip D */ Delay(5); DrawImage(RP,&Image24,165,65); Delay(5); DrawImage(RP,&Image23,165,65); Delay(5); DrawImage(RP,&Image22,165,65); Delay(5); DrawImage(RP,&Image21,165,65); Delay(5); DrawImage(RP,&Image5,165,65); Delay(5); DrawImage(RP,&Image6,165,65); Delay(5); DrawImage(RP,&Image7,165,65); Delay(5); DrawImage(RP,&Image8,165,65); /* Flip E */ Delay(5); DrawImage(RP,&Image24,215,65); Delay(5); DrawImage(RP,&Image23,215,65); Delay(5); DrawImage(RP,&Image22,215,65); Delay(5); DrawImage(RP,&Image21,215,65); Delay(5); DrawImage(RP,&Image1,215,65); Delay(5); DrawImage(RP,&Image2,215,65); Delay(5); DrawImage(RP,&Image3,215,65); Delay(5); DrawImage(RP,&Image4,215,65); /* Flip S */ Delay(5); DrawImage(RP,&Image24,265,65); Delay(5); DrawImage(RP,&Image23,265,65); Delay(5); DrawImage(RP,&Image22,265,65); Delay(5); DrawImage(RP,&Image21,265,65); Delay(5); DrawImage(RP,&Image17,265,65); Delay(5); DrawImage(RP,&Image18,265,65); Delay(5); DrawImage(RP,&Image19,265,65); Delay(5); DrawImage(RP,&Image20,265,65); /* Prompt for pause */ Move(RP,50,180); Text(RP,"Click any mouse button...",25); ReadMouse(); /* Pause for click */ } /********************************************************** * Function: FindDealer * * Parameters: none * * Return Values: the player determined to initially deal. * * Purpose: find out who deals first in a game. * **********************************************************/ int FindDealer() { int player=0, card=0, i; BOOL done=FALSE; SetRast(RP,BLUP); /* Clear Screen */ SetAPen(RP,YELP); SetBPen(RP,BLUP); for (i=0 ; i<52 ; i++) Deck[i]=0; /* Set deck to undealt */ Move(RP,70,70); /* Warn player of what's happening */ Text(RP,"Ace of Spades",13); Move(RP,98,78); Text(RP,"deals",5); while(!done) /* Loop until dealer found */ { while(Deck[card=rand()%52]) ; /* Find undealt card */ Deck[card]=1; /* Mark card as dealt */ DrawCard(CardX[player],CardY[player],card); /* Draw card */ Delay(10); /* Pause */ if (card==51) /* Ace of Spades */ { done=TRUE; Move(RP,MsgX[player],MsgY[player]); Text(RP,"*",1); } player=(++player)%4; /* Increment player */ } Move(RP,200,150); /* Wait for player */ Text(RP,"Click mouse",11); ReadMouse(); SetRast(RP,BLUP); return((++player)%4); /* Must return player 2 to left of */ /* dealer. Program will later */ /* decrement player to find lead */ /* who should be to left of dealer */ } /********************************************************** * Function: SetUpScreen * * Parameters: none * * Return Values: none * * Purpose: gets the screen ready for a new hand. * **********************************************************/ void SetUpScreen() { /* Clear the Screen */ SetRast(RP,BLUP); SetAPen(RP,YELP); SetBPen(RP,BLKP); /* Draw the Score Box */ Move(RP,215,6); Text(RP," YOU CMP ",11); Move(RP,215,14); Text(RP,"SCORE SCORE",11); Move(RP,215,22); Text(RP," ",11); Move(RP,215,30); Text(RP," BID BID ",11); Move(RP,215,38); Text(RP," ",11); Move(RP,215,46); Text(RP,"TRICK TRICK",11); Move(RP,215,54); Text(RP," ",11); SetAPen(RP,WHTP); Move(RP,215,7); Draw(RP,302,7); Move(RP,258,0); Draw(RP,258,55); } /********************************************************** * Function: InitVars * * Parameters: none * * Return Values: none * * Purpose: Initialize variables and arrays for a new hand.* **********************************************************/ void InitVars() { int i; /* Set Leader */ HandLead=(--HandLead+4)%4; TrickLead=HandLead; /* Reset HighCardLeft */ HighCardLeft[0]=12; HighCardLeft[1]=12; HighCardLeft[2]=12; HighCardLeft[3]=12; /* Reset OutOfSuit */ for (i=0 ; i<4 ; i++) { OutOfSuit[i][DIAMONDS]=FALSE; OutOfSuit[i][CLUBS] =FALSE; OutOfSuit[i][HEARTS] =FALSE; OutOfSuit[i][SPADES] =FALSE; } /* Determine Modes */ for (i=0 ; i<4 ; i++) { Mode[i]=0; if (i==HandLead) Mode[i]+=2; /* Leader should bid higher */ if (i==0||i==2) /* Human players -- check score */ { if ((PlayerScore>400)&&(CompScore<350)) Mode[i]-=1; if (PlayerScore<(CompScore-100)) Mode[i]+=1; if (PlayerScore<(CompScore-200)) Mode[i]+=1; if (PlayerScore>(CompScore+100)) Mode[i]-=1; } if (i==1||i==3) /* Computer players -- check score */ { if ((CompScore>400)&&(PlayerScore<350)) Mode[i]-=1; if (CompScore<(PlayerScore-100)) Mode[i]+=1; if (CompScore<(PlayerScore-200)) Mode[i]+=1; if (CompScore>(PlayerScore+100)) Mode[i]-=1; } } } /********************************************************** * Function: DealCards * * Parameters: none * * Return Values: none * * Purpose: Shuffle & deal the deck to the players. * **********************************************************/ void DealCards() { int i,j,player,cardnum[4]; for (i=0 ; i<4 ; i++) cardnum[i]=0; /* Reset cards for each player */ for (i=0 ; i<52 ; i++) Deck[i]=0; /* Set whole deck to undealt */ /* Shuffle and Deal Deck */ for (i=0 ; i<52 ; i++) { while(Deck[j=rand()%52]) ; /* Find undealt card */ Deck[j]=((i/13)+1); /* Store owning player */ } /* Transfer cards to player hands */ for (i=0 ; i<52 ; i++) { player=Deck[i]-1; /* Get owning player */ Hand[player][cardnum[player]++]=i+1; /* Store card, increment counter */ } } /********************************************************** * Function: ShowHand * * Parameters: none * * Return Values: none * * Purpose: To display the player's hand. * **********************************************************/ void ShowHand() { int i; /* Erase old hand */ SetAPen(RP,BLUP); SetOPen(RP,BLUP); RectFill(RP,21,145,183,186); /* Draw each card, overlaying part of the previous one */ for (i=0 ; i<13 ; i++) { if (Hand[0][i]) /* Only draw if card hasn't been played */ DrawCard(((i*10)+21),145,((Hand[0][i])-1)); } } /********************************************************** * Function: GetBids * * Parameters: none * * Return values: none * * Purpose: Get each player's bid. * **********************************************************/ void GetBids() { int i; for (i=0 ; i<4 ; i++) Bid[i]=0; /* Reset bids for each player */ /* Loop through each player */ i=HandLead; do { if (i) Bid[i]=CalcBid(i); /* Computer Player */ else Bid[i]=GetPlayerBid(); /* Human Player */ i=(++i)%4; } while (i!=HandLead); /* Calculate team contracts */ PlayerBid=Bid[0]+Bid[2]; CompBid=Bid[1]+Bid[3]; /* Pause for click */ SetAPen(RP,YELP); Move(RP,200,150); Text(RP,"Click mouse",11); ReadMouse(); Move(RP,200,150); Text(RP," ",11); /* Erase Bids */ SetBPen(RP,BLUP); for (i=0 ; i<4 ; i++) { Move(RP,MsgX[i],MsgY[i]); Text(RP," ",1); } } /********************************************************** * Function: GetPlayerBid * * Parameters: none * * Return Values: bid -- number of tricks bid * * Purpose: Get the human player's bid. Could use gadgets, * * but this is easier to program. * **********************************************************/ int GetPlayerBid() { int bid=1,length; BOOL havebid=FALSE; ShowHand(); /* Draw input box */ SetAPen(RP,YELP); SetBPen(RP,BLKP); Move(RP,258,142); Text(RP,"BID",3); Move(RP,250,150); Text(RP," + ",5); Move(RP,250,158); Text(RP," OK",5); Move(RP,250,166); Text(RP," - ",5); /* Loop until OK is clicked */ while(!havebid) { /* Draw Current Bid */ SetAPen(RP,GRNP); SetBPen(RP,BLKP); Move(RP,250,158); Text(RP," ",2); itoa(bid,String); length=strlen(String); Move(RP,(258-(4*length)),158); Text(RP,String,length); /* Wait for Mouse input */ ReadMouse(); if (Button==MLEFT) /* Left Button Pressed */ { if ((Mx>265)&&(Mx<274)&&(My>143)&&(My<152)) bid++; /* plus sign */ if ((Mx>273)&&(Mx<290)&&(My>151)&&(My<160)) havebid=TRUE; /* OK */ if ((Mx>265)&&(Mx<274)&&(My>159)&&(My<168)) bid--; /* minus sign */ } if (Button==MRIGHT) /* Right Button Pressed */ { bid=CalcBid(0); /* Suggest a Bid */ } /* Make sure bid is valid */ if (bid<1) bid=1; if (bid>12) bid=12; } /* Erase Input Box */ SetAPen(RP,BLUP); SetOPen(RP,BLUP); RectFill(RP,250,135,291,168); /* Display the bid */ SetAPen(RP,YELP); SetBPen(RP,BLUP); itoa(bid,String); Move(RP,MsgX[0],MsgY[0]); Text(RP,String,strlen(String)); /* Send the bid back */ return(bid); } /********************************************************** * Function: CalcBid * * Parameters: player -- number of player to calculate for * * Return Values: bid -- number of tricks * * Purpose: To calculate the number of tricks bid by a * * player. * **********************************************************/ int CalcBid(player) int player; { int i,j,numsuit,points,suit,bid; int k,numspades; points=0; /* Calculate spades */ numspades=0; /* Count Spades */ for (j=39 ; j<52 ; j++) if (Deck[j]==player+1) numspades++; numsuit = numspades; /* Add points for Spades -- if enough to cover, count it */ for (j=51 ; j>38 ; j--) { k = 12-(j%13); if ((Deck[j]==player+1)&&(numsuit>k)) { points+=4; numspades=numsuit-k; /* Used to make sure we don't count tricks twice for short-suitedness */ } } /* Add up points for the non-spades suits */ for (i=DIAMONDS ; i<SPADES ; i++) /* Loop thru suits */ { numsuit=0; suit=i*13; /* Count cards for suit */ for (j=0 ; j<13 ; j++) if (Deck[suit+j]==player+1) numsuit++; /* Short-Suitedness */ if (numspades>2) { if (numsuit==0) points+=8; else if (numsuit==1) points+=4; else if (numsuit==2) points+=2; } else if (numspades==2) { if (numsuit==0) points+=5; else if (numsuit==1) points+=2; } else if (numspades==1) { if (numsuit==0) points+=3; } /* Add points for face cards */ if (Deck[suit+12]==player+1) /* Ace */ { if (numsuit<6) points+=4; else if (numsuit<8) points+=2; else points+=1; } if (Deck[suit+11]==player+1) /* King */ { if ((numsuit<5)&&(numsuit>1)) points+=4; else if ((numsuit<6)&&(numsuit>1)) points+=2; else if ((numsuit<7)&&(numsuit>1)) points+=1; } if (Deck[suit+10]==player+1) /* Queen */ { if (numsuit==3) points+=2; } } points+=Mode[player]; /* Adjust for aggressiveness */ if (points<4) points=4; /* Validate number of points */ bid=points/4; /* Find Bid */ /* Make sure total team bid not greater than 13 */ i=(player+2)%4; /* Find partner */ if ((bid+Bid[i])>13) bid=13-Bid[i]; /* Display Bid */ itoa(bid,String); SetAPen(RP,YELP); SetBPen(RP,BLUP); Move(RP,MsgX[player],MsgY[player]); Text(RP,String,strlen(String)); /* Send bid back */ return(bid); } /********************************************************** * Function: PlayHand * * Parameters: none * * Return Values: none * * Purpose: Play out a hand until all 13 tricks are taken. * **********************************************************/ void PlayHand() { int i; /* Initialize */ PlayerTricks=CompTricks=0; SpadePlayed=FALSE; PrintTricks(); /* Loop through all 13 tricks */ for (i=0 ; i<13 ; i++) { TrickLead=TakeTrick(); /* Play a trick */ if ((TrickLead==0)||(TrickLead==2)) /* Who won? */ PlayerTricks++; else CompTricks++; PrintTricks(); /* Display new trick total */ /* Indicate who won with an '*' */ SetAPen(RP,YELP); SetBPen(RP,BLUP); Move(RP,MsgX[TrickLead],MsgY[TrickLead]); Text(RP,"*",1); /* Pause for click */ SetAPen(RP,YELP); Move(RP,200,150); Text(RP,"Click mouse",11); ReadMouse(); Move(RP,200,150); Text(RP," ",11); /* Erase winner indicator */ Move(RP,MsgX[TrickLead],MsgY[TrickLead]); Text(RP," ",1); } /* Calculate new score */ if (PlayerTricks<PlayerBid) PlayerScore-=(10*PlayerBid); if (CompTricks<CompBid) CompScore-=(10*CompBid); if (PlayerTricks>=PlayerBid) PlayerScore+=((10*PlayerBid)+(PlayerTricks-PlayerBid)); if (CompTricks>=CompBid) CompScore+=((10*CompBid)+(CompTricks-CompBid)); /* Pause for click */ SetAPen(RP,YELP); Move(RP,200,150); Text(RP,"Click mouse",11); ReadMouse(); Move(RP,200,150); Text(RP," ",11); } /********************************************************** * Function: TakeTrick * * Parameters: none * * Return Values: winner of trick * * Purpose: Each player plays a card, then determine trick * * winner. * **********************************************************/ int TakeTrick() { int i,j,leadsuit,suit,value; /* Clear previously played cards */ SetAPen(RP,BLUP); SetOPen(RP,BLUP); for (i=0 ; i<4 ; i++) RectFill(RP,CardX[i],CardY[i],(CardX[i]+41),(CardY[i]+41)); /* Get played cards */ i=TrickLead; do { if (!i) Card[i]=GetPlayerCard(); else Card[i]=GetCompCard(i); if (i==TrickLead) /* First card played wins so far */ { HighCard=Card[i]; leadsuit=Card[i]/13; Winner=i; } else { suit=Card[i]/13; /* See if this card is the new winner */ if (((suit==leadsuit)||(suit==SPADES))&&(Card[i]>HighCard)) { HighCard=Card[i]; Winner=i; } /* Was player out of the lead suit ? */ if (suit!=leadsuit) OutOfSuit[i][leadsuit]=TRUE; } i=(++i)%4; } while (i!=TrickLead); ShowHand(); /* Set highest card played in each suit */ for (i=0 ; i<4 ; i++) { for (j=0 ; j<4 ; j++) /* Need two loops to make sure we get all */ { value=Card[j]%13; suit=Card[j]/13; if (value==HighCardLeft[suit]) HighCardLeft[suit]=value-1; } } /* Send back trick winner */ return(Winner); } /********************************************************** * Function: GetPlayerCard * * Parameters: none * * Return Values: card picked to play * * Purpose: Allow player to pick card to play. * **********************************************************/ int GetPlayerCard() { int i,x,card; /* Let player know that it's his/her turn */ SetBPen(RP,BLUP); SetAPen(RP,YELP); Move(RP,200,150); Text(RP,"PLAY A CARD",11); /* Loop until we get a good card */ FOREVER { ReadMouse(); /* Wait for mouse click */ if (Button==MRIGHT) SuggestCard(); /* Player wants a suggestion */ if (Button==MLEFT) /* Did player pick a card ? */ { for (i=12 ; i>=0 ; i--) /* Check from right to left */ { x=(i*10)+21; /* Set left corner for card */ /* See if clicked inside this card and if card is still unplayed */ if ((My<187)&&(My>144)&&(Mx<(x+42))&&(Mx>=x)&&(Hand[0][i])) { if (ValidCard(i)) /* Was this a playable card ? */ { card=Hand[0][i]-1; Hand[0][i]=0; /* Mark card as played */ if ((card/13)==SPADES) SpadePlayed=TRUE; /* Spades have been broken */ /* Erase suggestion '*' */ SetAPen(RP,BLUP); SetOPen(RP,BLUP); RectFill(RP,21,136,170,144); DrawCard(CardX[0],CardY[0],card); /* Draw played card */ /* Erase prompt message */ SetBPen(RP,BLUP); Move(RP,200,150); Text(RP," ",11); /* Send back played card */ return(card); } else /* if chosen card was not valid, need a new card */ i=-1; } } } } } /********************************************************** * Function: ValidCard * * Parameters: card -- card in player's hand to check * * Return Values: was card valid or not? * * Purpose: To determine if the card chosen by the player * * was valid or not. * **********************************************************/ BOOL ValidCard(card) int card; { int i,suit,leadsuit; SuitNumber[DIAMONDS]=0; SuitNumber[CLUBS] =0; SuitNumber[HEARTS] =0; SuitNumber[SPADES] =0; /* Count number of cards player has in each suit */ for (i=0 ; i<13 ; i++) { if (Hand[0][i]) SuitNumber[(Hand[0][i]-1)/13]++; } suit=(Hand[0][card]-1)/13; /* Find suit of played card */ if (!TrickLead) /* Player is leading */ { /* If he didn't lead a spade, it was a good play */ if (suit!=SPADES) return(TRUE); /* If he only has spades, he has no choice */ if ((!SuitNumber[0])&&(!SuitNumber[1])&&(!SuitNumber[2])) return(TRUE); /* If spades have been played, he can lead anything */ if (SpadePlayed) return(TRUE); /* Must have lead a spade when it was illegal to */ return(FALSE); } /* Player doesn't lead */ leadsuit=Card[TrickLead]/13; /* Find suit that was lead */ /* If he played the suit that was lead, he is OK */ if (suit==leadsuit) return(TRUE); /* If he didn't have any, he's OK */ if (!SuitNumber[leadsuit]) return(TRUE); /* Must have had some but didn't play them */ return(FALSE); } /********************************************************** * Function: CountCards * * Parameters: none * * Return Values: none * * Purpose: Count cards in each suit for a player. * * Determine short and long suits. * **********************************************************/ void CountCards(player) int player; { int i,card,suit,maximum,minimum; /* Initialization */ SuitNumber[DIAMONDS]=0; SuitNumber[CLUBS] =0; SuitNumber[HEARTS] =0; SuitNumber[SPADES] =0; /* Loop through all cards in the player's hand */ for (i=0 ; i<13 ; i++) { if (Hand[player][i]) /* Make sure card hasn't been played */ { card=Hand[player][i]-1; suit=card/13; Value[i]=13-card; /* Give lower cards a slight priority */ SuitNumber[suit]++; /* Count Number of Suit held */ } else Value[i]=-50000; /* Don't throw previously played cards */ } /* Find short and long suits */ minimum=14; maximum=0; ShortSuit=LongSuit=3; for (i=DIAMONDS ; i<SPADES ; i++) { if ((SuitNumber[i]<minimum)&&(SuitNumber[i]>0)) { minimum=SuitNumber[i]; ShortSuit=i; } if (SuitNumber[i]>maximum) { maximum=SuitNumber[i]; LongSuit=i; } } } /********************************************************** * Function: SuggestCard * * Parameters: none * * Return Values: none * * Purpose: Suggest a card for player to play. * **********************************************************/ void SuggestCard() { int i,pick,maximum; CountCards(0); /* Go to appropriate point calculating routine */ if (!TrickLead) CalcLead(0); else CalcFollow(0); /* Find best card (the one with the most points) */ pick=0; maximum=Value[0]; for (i=1 ; i<13 ; i++) { if (Value[i]>maximum) { maximum=Value[i]; pick=i; } } /* Display an '*' over suggested card */ SetAPen(RP,YELP); SetBPen(RP,BLUP); Move(RP,((pick*10))+22,142); Text(RP,"*",1); } /********************************************************** * Function: GetCompCard * * Parameters: player -- player to play * * Return Values: card played * * Purpose: Determine which card a computer-controlled * * player will play. * **********************************************************/ int GetCompCard(player) int player; { int i,pick,card,maximum; CountCards(player); /* Go to appropriate point calculating routine */ if (player==TrickLead) CalcLead(player); else CalcFollow(player); /* Find best card (the one with the most points) */ pick=0; maximum=Value[0]; for (i=1 ; i<13 ; i++) { if (Value[i]>maximum) { maximum=Value[i]; pick=i; } } card=Hand[player][pick]-1; if ((card/13)==3) SpadePlayed=TRUE; /* Mark that spades have been broken */ Hand[player][pick]=0; /* Card has now been played */ DrawCard(CardX[player],CardY[player],card); /* Draw the played card */ return(card); /* Send the played card back */ } /********************************************************** * Function: CalcLead * * Parameters: player -- whose hand to calculate * * Return Values: none * * Purpose: To calculate the value of each card in a hand * * to determine which card should be played if * * the hand is leading. * **********************************************************/ void CalcLead(player) int player; { int i,card,suit,value; BOOL opponentsout=FALSE, partnerout=FALSE; /* Loop through all cards in hand */ for (i=0 ; i<13 ; i++) { if (Hand[player][i]) /* Make sure card hasn't been played */ { /* Find suit and face value */ card=Hand[player][i]-1; suit=card/13; value=card%13; if ((OutOfSuit[(player+1)%4][suit])||(OutOfSuit[(player+3)%4][suit])) opponentsout=TRUE; if (OutOfSuit[(player+2)%4][suit]) partnerout=TRUE; if (value==HighCardLeft[suit]) /* Card is highest left in a suit */ { if (suit==SPADES) /* Spades don't matter if someone is */ Value[i]+=50; /* out. */ else { /* If opponents are out (or likely to be), don't waste */ if ((opponentsout)||(value<11)) Value[i]-=50; /* high cards. */ else Value[i]+=500; } } /* If player holds spades, get rid of short suit */ if ((SuitNumber[SPADES])&&(suit==ShortSuit)) Value[i]+=250; /* If player doesn't hold spades, get rid of long suit */ if ((!SuitNumber[SPADES])&&(suit==LongSuit)) Value[i]+=250; /* If spades aren't broken, they can't be lead */ if ((suit==SPADES)&&(!SpadePlayed)) Value[i]-=5000; /* Lead suits your partner is out of */ if ((suit!=SPADES)&&(partnerout)&&(!opponentsout)) Value[i]+=100; } } } /********************************************************** * Function: CalcFollow * * Parameters: player -- whose hand to calculate * * Return Values: none * * Purpose: To calculate the value of each card in a hand * * to determine which card should be played if * * the hand is not leading. * **********************************************************/ void CalcFollow(player) int player; { int i,card,suit,value,leadsuit; BOOL alreadywon; leadsuit=Card[TrickLead]/13; /* Calculate the suit that was lead */ /* See if partner has already won the trick */ alreadywon=FALSE; /* See if win is guaranteed (player is last to play) */ if ((Winner==((player+2)%4))&&(TrickLead==((player+1)%4))) alreadywon=TRUE; /* See if win is probable (player is next to last to play) */ if ((Winner==TrickLead)&&(TrickLead==((player+2)%4))) { value=Card[TrickLead]%13; if ((value==HighCardLeft[leadsuit])&&(value>9)&& !OutOfSuit[(player+3)%4][leadsuit]) alreadywon=TRUE; } /* Loop through all cards in hand */ for (i=0 ; i<13 ; i++) { if (Hand[player][i]) /* Make sure card hasn't been played */ { /* Find suit and face value of card */ card=Hand[player][i]-1; suit=card/13; value=card%13; if (suit==leadsuit) /* Card is of lead suit */ { Value[i]+=5000; /* If it is the highest one left in that suit, throw it */ if ((value==HighCardLeft[suit])&&(TrickLead!=((player+1)%4))&&(card>HighCard)&&!OutOfSuit[(player+1)%4][suit]) Value[i]+=70; /* See if card will beat those previously played */ if (card>HighCard) { if (!alreadywon) /* Team hasn't won yet */ { if (TrickLead==((player+1)%4)) /* Can definitely take trick */ Value[i]+=100; else Value[i]+=10; /* Maybe take trick */ } else Value[i]-=75; /* If the team has won, hold high cards */ } /* If team has already won the trick, throw low cards */ if ((card<HighCard)&&alreadywon) Value[i]+=75; } /* If player is going to throw a spade, make sure it has a chance of winning */ if ((suit==SPADES)&&(card>HighCard)) { if (!alreadywon) Value[i]+=10; /* If team hasn't won, throw the spade */ else Value[i]-=1000; /* If team has won, hold high spades */ } if ((suit==SPADES)&&(card<HighCard)) { if (!alreadywon) Value[i]-=1000; /* If the team hasn't won, don't throw a losing spade */ else Value[i]+=10; /* If the team has one, throw a low spade */ } /* If player is out of the lead suit, don't throw important cards in other suits */ if ((suit!=leadsuit)&&(value==HighCardLeft[suit])) Value[i]-=100; /* If player is out of lead suit, let's see about trumping */ if ((!SuitNumber[leadsuit])&&(suit==SPADES)) { if (!alreadywon) Value[i]+=100; /* If team hasn't won, trump */ else Value[i]-=1000; /* If team has won, no need to trump */ } /* If player is out of lead suit and out of spades, throw the long suit left (keep as many suits as possible in hand) */ if ((!SuitNumber[leadsuit])&&(!SuitNumber[SPADES])&&(suit==LongSuit)) { Value[i]+=100; } /* Give short suit a priority */ if (suit==ShortSuit) Value[i]+=45; } } } /********************************************************** * Function: PrintBids * * Parameters: none * * Return Values: none * * Purpose: Display the number of tricks bid by each team * * in the score box. * **********************************************************/ void PrintBids() { int length=0; SetAPen(RP,GRNP); SetBPen(RP,BLKP); itoa(PlayerBid,String); length=strlen(String); Move(RP,(235-(4*length)),38); Text(RP,String,length); itoa(CompBid,String); length=strlen(String); Move(RP,(283-(4*length)),38); Text(RP,String,length); } /********************************************************** * Function: PrintScore * * Parameters: none * * Return Values: none * * Purpose: Display each team's score in the score box. * **********************************************************/ void PrintScore() { int length=0; SetAPen(RP,GRNP); SetBPen(RP,BLKP); itoa(PlayerScore,String); length=strlen(String); Move(RP,(235-(4*length)),22); Text(RP,String,length); itoa(CompScore,String); length=strlen(String); Move(RP,(283-(4*length)),22); Text(RP,String,length); } /********************************************************** * Function: PrintTricks * * Parameters: none * * Return Values: none * * Purpose: Display the number of tricks taken by each * * team in the score box. * **********************************************************/ void PrintTricks() { int length=0; SetAPen(RP,GRNP); SetBPen(RP,BLKP); itoa(PlayerTricks,String); length=strlen(String); Move(RP,(235-(4*length)),54); Text(RP,String,length); itoa(CompTricks,String); length=strlen(String); Move(RP,(283-(4*length)),54); Text(RP,String,length); } /********************************************************** * Function: ReadMouse * * Parameters: none * * Return Values: none * * Purpose: Wait for mouse input, and update the mouse * * variables accordingly. * **********************************************************/ void ReadMouse() { ULONG class; USHORT code; BOOL gotmouse=FALSE; /* Loop until we have a mouse message */ while(!gotmouse) { /* Wait for Intuition Message */ if ((Message=(struct IntuiMessage *) GetMsg(Wdw->UserPort)) == NULL) { Wait(1L<<Wdw->UserPort->mp_SigBit); continue; } /* Interpret Message */ class = Message->Class; code = Message->Code; Mx=((SHORT) Message->MouseX) - 4; /* Adjustments for */ My=((SHORT) Message->MouseY) - 12; /* GIMMEZEROZERO Window */ ReplyMsg((struct Message *)Message); /* See if Window Close Box clicked */ if (class & CLOSEWINDOW) WrapUp(); /* See if Hide Gadget clicked */ if (class & GADGETUP) { ScreenToBack(CustScr); return; } /* Otherwise, make sure Message was caused by mouse */ if (class & MOUSEBUTTONS) { switch(code) { /* Left Button Released */ case SELECTUP: Button=MLEFT; gotmouse=TRUE; break; /* Right Button Released */ case MENUUP: Button=MRIGHT; gotmouse=TRUE; break; /* Other Input */ default: gotmouse=FALSE; } } } } /********************************************************** * Function: DrawCard * * Parameters: x -- x coordinate of top left corner * * y -- y coordinate of top left corner * * card -- number of card to draw * * Return Values: none * * Purpose: Draw a card. * **********************************************************/ void DrawCard(x, y, card) int x, y, card; { CardImage.ImageData = (UWORD *)(CardData+card*126*6); DrawImage(RP, &CardImage, x, y); } /********************************************************** * Function: FinishRoutine * * Parameters: none * * Return Values: none * * Purpose: Display final score, ask to play again. * **********************************************************/ void FinishRoutine() { BOOL haveinput; SetRast(RP,BLUP); SetAPen(RP,WHTP); SetBPen(RP,BLUP); Move(RP,112,56); Text(RP,"FINAL SCORE:",12); Move(RP,80,72); Text(RP,"YOU:",4); Move(RP,184,72); Text(RP,"ME:",3); SetAPen(RP,YELP); itoa(PlayerScore,String); Move(RP,116,72); Text(RP,String,strlen(String)); itoa(CompScore,String); Move(RP,212,72); Text(RP,String,strlen(String)); Move(RP,112,96); Text(RP,"PLAY AGAIN ?",12); SetAPen(RP,BLKP); SetBPen(RP,WHTP); Move(RP,112,112); Text(RP,"YES",3); Move(RP,188,112); Text(RP,"NO",2); haveinput=FALSE; while (!haveinput) { ReadMouse(); if ((Mx>111)&&(Mx<136)&&(My>105)&&(My<114)) { haveinput=TRUE; AllDone=FALSE; } if ((Mx>187)&&(Mx<204)&&(My>105)&&(My<114)) { haveinput=TRUE; AllDone=TRUE; } } } /********************************************************** * Function: itoa * * Parameters: n -- number to convert * * s -- pointer to result string * * Return Values: none * * Purpose: Convert an integer to a string so it can be * * used by the Text function. Lattice does not * * have one. * **********************************************************/ void itoa(n,s) char *s; int n; { int i=0; BOOL sign=FALSE; if (n<0) { sign=TRUE; n=-n; } do { s[i++]=n%10+'0'; } while((n/=10)>0); if (sign) s[i++]='-'; s[i]='\0'; strrev(s); }