/**********************************************************
* PROGRAM: Spades                                         *
* AUTHOR: Gregory M. Stelmack                             *
* COMPILER: Lattice C V5.04                               *
*      Copyright (C) 1988 Lattice, 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.                              *
* Tab stops set to 2,4,6,8,... 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 <stdio.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>

/* 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

/************** 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   */
};

/*********** NewWindow Structure **********/
struct NewWindow NewWdw =
{
	0,0,            /* Left Edge, Top Edge          */
	320,200,        /* Width, Height                */
	BLKP,WHTP,      /* Block Pen, Detail Pen        */
	MOUSEBUTTONS | CLOSEWINDOW,      /* IDCMP Flags */
	SMART_REFRESH | ACTIVATE | GIMMEZEROZERO | RMBTRAP | WINDOWCLOSE,
	NULL,           /* Pointer to First Gadget      */
	NULL,           /* Pointer to Check Mark image  */
	"Spades, by Greg Stelmack",            /* Title */
	NULL,           /* Pointer to Screen structure  */
	NULL,           /* Pointer to custom Bit Map    */
	0,0,            /* 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};
  
/********************************************
* SuitPoints: how many points are added to  *
*   the strength of a hand depending on the *
*   number of cards in each suit in the     *
*   hand. The first row is for non-spades,  *
*   the second for spades.                  *
********************************************/
int SuitPoints[2][14] = { {6,3,1,0,0,0,-2,-4,-6,-8,-10,-12,-20,-50},
                          {-4,-2,-1,0,0,+1,+2,+5,+8,+10,+12,+20,+50} };

/********************************************
* 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;
char *CardData;

SHORT Mx=0, My=0;        /* Mouse Coordinates */

/******************** Function Declarations *******************/
void Spades(), SetUpScreen(), DealCards(), ShowHand(), DrawCard();
int CalcBid(),GetPlayerBid(), TakeTrick(), GetPlayerCard();
int GetCompCard(),FindDealer();
BOOL ValidCard();
void itoa(), PrintBids(), PrintScore(), PrintTricks(), GetBids();
void ReadMouse(), PlayHand(), InitVars(), CalcLead(), CalcFollow();
void FinishRoutine(), WrapUp(), SuggestCard(), CountCards();
void Title();

/********************* Program Begins Here ********************/

/**********************************************************
* Function: main                                          *
* Purpose: Open everything, play the game, close          *
*          everything.                                    *
**********************************************************/
main()
{
  int fh;
  unsigned int count=0;
  unsigned int length=0;
    
	/* Open the Intuition and Graphics libraries. */
  
	IntuitionBase = (struct IntuitionBase *)
	     OpenLibrary("intuition.library",LIBRARY_VERSION);
	if (IntuitionBase == NULL) 
  {
    printf("Can't open Intuition Library");
    WrapUp();
  }    
	
	GfxBase = (struct GfxBase *)
	     OpenLibrary("graphics.library",LIBRARY_VERSION);
	if (GfxBase == NULL)
  {
    printf("Can't open Graphics Library");
    WrapUp();
  }
  
  /* Open the screen and window */
	
	if ((NewWdw.Screen = CustScr =
	  (struct Screen *)OpenScreen(&NewCustScr)) == NULL)
  {
    printf("Can't open new screen");
    WrapUp();
  }
  	  
	if (( Wdw = (struct Window *)OpenWindow(&NewWdw)) == NULL)
  {
    printf("Can't open new window");
    WrapUp();
  }
  	
	/* Find the viewport and load color map */
	
	WVP = (struct ViewPort *)ViewPortAddress(Wdw);
	LoadRGB4(WVP,&colormap,PENS);
	
  /* Seed random number generator with TIMER */
  
  srand(time(NULL));
  
  /* Load Graphic Images */
  
  length=52*126*2*3;
  CardData = (char *)AllocMem(length,MEMF_CHIP);
  if (!CardData)
  {
    printf("Could not allocate image memory!");
    WrapUp();
  }
  fh = open("Spades.images",O_RDONLY,0);
  if (fh==-1)
  {
    printf("Can't open images file!");
    WrapUp();
  }
  count = read(fh,CardData,length);
  if (count==-1)
  {
    printf("Error reading images file!");
    WrapUp();
  }
    
  /* Start Game */
	
  Spades();
		
  /* Close Everything */
  
  WrapUp();
}

/**********************************************************
* Function: WrapUp                                        *
* Parameters: none                                        *
* Return Values: none                                     *
* Purpose: close everything and exit                      *
**********************************************************/
void WrapUp()
{
  if (CardData)      FreeMem((char*)CardData,52*126*2);
	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);
  
  /* Draw the Score Box */
  
  SetAPen(RP,YELP);
  SetBPen(RP,BLKP);
  Move(RP,224,6);
  Text(RP," YOU   CMP ",11);
  Move(RP,224,14);
  Text(RP,"SCORE SCORE",11);
  Move(RP,224,22);
  Text(RP,"           ",11);
  Move(RP,224,30);
  Text(RP," BID   BID ",11);
  Move(RP,224,38);
  Text(RP,"           ",11);
  Move(RP,224,46);
  Text(RP,"TRICK TRICK",11);
  Move(RP,224,54);
  Text(RP,"           ",11); 
  SetAPen(RP,WHTP);
  Move(RP,224,7);
  Draw(RP,312,7);
  Move(RP,267,0);
  Draw(RP,267,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 */
  
  ReadMouse();
  
  /* 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;
  
  points=0;
  
  /* 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 and add appropriate points */
    
    for (j=0 ; j<13 ; j++)
    {
      if (Deck[suit+j]==player+1) numsuit++;
    }
    points+=SuitPoints[0][numsuit];
    
    /* Add points for face cards */
    
    if (Deck[suit+12]==player+1) points+=4; /* Ace   */
    if (Deck[suit+11]==player+1) points+=3; /* King  */
    if (Deck[suit+10]==player+1) points+=2; /* Queen */
  }
  
  /* Calculate spades */
  
    numsuit=0;
    
    /* Count Spades and add appropriate points */
    
    for (j=39 ; j<52 ; j++)
    {
      if (Deck[j]==player+1) numsuit++;
    }
    points+=SuitPoints[1][numsuit];

    /* Add points for Face Cards */

    if (Deck[51]==player+1) points+=4;  /* Ace   */
    if (Deck[50]==player+1) points+=3;  /* King  */
    if (Deck[49]==player+1) points+=2;  /* Queen */
    if (Deck[48]==player+1) points+=1;  /* Jack  */
  
  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 */
    
    ReadMouse();
    
    /* 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 */
  
  ReadMouse();  
}

/**********************************************************
* 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]=6-(card%13);     /* Give lower cards a slight priority */
      SuitNumber[suit]++;       /* Count Number of Suit held */
    }
     else
      Value[i]=-10000;          /* 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 (opponentsout)          /* If opponents are out, don't waste */
             Value[i]-=50;            /* high cards. */
            else
             Value[i]+=50;
         }  
      }
      
      /* 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]+=300;
    }
  }
}

/**********************************************************
* 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)) 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))
          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 one, hold high cards */
        }
        
        /* If team has already one 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,(244-(4*length)),38);
  Text(RP,String,length);
  itoa(CompBid,String);
  length=strlen(String);
  Move(RP,(292-(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,(244-(4*length)),22);
  Text(RP,String,length);
  itoa(CompScore,String);
  length=strlen(String);
  Move(RP,(292-(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,(244-(4*length)),54);
  Text(RP,String,length);
  itoa(CompTricks,String);
  length=strlen(String);
  Move(RP,(292-(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   */

    /* See if Window Close Box clicked */
    
    if (class & CLOSEWINDOW) WrapUp();
    
    /* 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;
      }   
    }
    ReplyMsg(Message);
  }
} 

/**********************************************************
* 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);
}
