432 lines
12 KiB
C++
432 lines
12 KiB
C++
|
/***************************************************************************/
|
||
|
/** Microsoft Windows **/
|
||
|
/** Copyright(c) Microsoft Corp., 1991, 1992 **/
|
||
|
/***************************************************************************/
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
comp2.cpp
|
||
|
|
||
|
Aug 92, JimH
|
||
|
May 93, JimH chico port
|
||
|
|
||
|
Logic for computer player to select cards to play when not holding
|
||
|
the lead, and initializing data tables is here.
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "hearts.h"
|
||
|
|
||
|
#include "main.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
#include "debug.h" // undef _DEBUG instead to remove messages
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
computer::SelectCardToPlay
|
||
|
|
||
|
computer player chooses a card to play.
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void computer::SelectCardToPlay(handinfotype &h, BOOL bCheating)
|
||
|
{
|
||
|
TRACE1("<%d> ", id);
|
||
|
|
||
|
Setup(h); // calculate values of private vars
|
||
|
|
||
|
SLOT s;
|
||
|
if (bFirst) // am I leading?
|
||
|
s = SelectLeadCard(h);
|
||
|
else
|
||
|
s = SelectNonLeadCard(h);
|
||
|
|
||
|
ASSERT(s >= 0);
|
||
|
ASSERT(s < MAXSLOT);
|
||
|
ASSERT(cd[s].IsValid());
|
||
|
|
||
|
SetMode(WAITING);
|
||
|
cd[s].Play(); // mark card as played
|
||
|
h.cardplayed[id] = &(cd[s]); // update handinfo
|
||
|
|
||
|
// inform other players
|
||
|
|
||
|
::move.playerid = id;
|
||
|
::move.cardid = cd[s].ID();
|
||
|
::move.playerled = h.playerled;
|
||
|
::move.turn = h.turn;
|
||
|
|
||
|
ddeServer->PostAdvise(hszMove);
|
||
|
|
||
|
// inform gamemeister
|
||
|
|
||
|
::pMainWnd->PostMessage(WM_COMMAND, IDM_REF);
|
||
|
TRACE0("\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
computer::SelectNonLeadCard
|
||
|
|
||
|
This is where cards to play are selected when the computer player is
|
||
|
not leading.
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
SLOT computer::SelectNonLeadCard(handinfotype &h)
|
||
|
{
|
||
|
BOOL bFirstTrick = (cardled != NULL) && (cardled->ID() == TWOCLUBS);
|
||
|
|
||
|
// If we have at least one card of the led suit...
|
||
|
|
||
|
if (sHighCard[nSuitLed] != EMPTY)
|
||
|
{
|
||
|
TRACE0("can follow suit. ");
|
||
|
|
||
|
// If there's only one card of this suit, return it.
|
||
|
|
||
|
if (sHighCard[nSuitLed] == sLowCard[nSuitLed])
|
||
|
{
|
||
|
TRACE0("must ");
|
||
|
PLAY(sHighCard[nSuitLed]);
|
||
|
return sHighCard[nSuitLed];
|
||
|
}
|
||
|
|
||
|
// if it's the first trick, play the high club
|
||
|
|
||
|
if (bFirstTrick)
|
||
|
{
|
||
|
TRACE0("might as well ");
|
||
|
PLAY(sHighCard[nSuitLed]);
|
||
|
return sHighCard[nSuitLed];
|
||
|
}
|
||
|
|
||
|
// If I am the last player in this trick, and I've won the hand anyway,
|
||
|
// return highest legal card (unless it's the queen of spades.)
|
||
|
|
||
|
if (bLast && (nLowVal[nSuitLed] > currentval))
|
||
|
{
|
||
|
TRACE0("must win trick. ");
|
||
|
if (sHighCard[nSuitLed] != sBlackLady)
|
||
|
{
|
||
|
PLAY(sHighCard[nSuitLed]);
|
||
|
return sHighCard[nSuitLed];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE0("avoid queen. ");
|
||
|
PLAY(sLowCard[nSuitLed]);
|
||
|
return sLowCard[nSuitLed];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If I am the last player and I CAN win the trick....
|
||
|
|
||
|
if (bLast && (nHighVal[nSuitLed] > currentval))
|
||
|
{
|
||
|
TRACE0("can win. ");
|
||
|
|
||
|
// Don't grab the trick if there aren't enough low cards
|
||
|
// left in hand. The lead may be hard to lose!
|
||
|
|
||
|
if (nLowestVal < 7) // i.e., card val < 8
|
||
|
{
|
||
|
if ((nPoints == 0) && (sHighCard[nSuitLed] != sBlackLady))
|
||
|
{
|
||
|
TRACE0("go for it. ");
|
||
|
PLAY(sHighCard[nSuitLed]);
|
||
|
return sHighCard[nSuitLed];
|
||
|
}
|
||
|
|
||
|
// Take a few hearts if it means losing a high spade.
|
||
|
|
||
|
if ((!h.bQSPlayed) && nSuitLed == SPADES && nPoints < 4)
|
||
|
{
|
||
|
if (nHighVal[SPADES] > QUEEN)
|
||
|
{
|
||
|
TRACE0("sacrifice hearts to lose high spade. ");
|
||
|
PLAY(sHighCard[SPADES]);
|
||
|
return sHighCard[SPADES];
|
||
|
}
|
||
|
}
|
||
|
TRACE0("decline. ");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE0("no low cards. ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Otherwise, try to find the highest safe card to play...
|
||
|
|
||
|
SLOT safe = SafeCard(h);
|
||
|
if (safe != EMPTY)
|
||
|
{
|
||
|
// if someone other than me is potentially shooting,
|
||
|
// hold back high cards.
|
||
|
|
||
|
if (h.bShootingRisk && h.bHumanShooter && (h.nMoonShooter != id))
|
||
|
{
|
||
|
TRACE0("2nd ");
|
||
|
SLOT s2 = CardBelow(safe);
|
||
|
if (s2 != EMPTY)
|
||
|
safe = s2;
|
||
|
}
|
||
|
|
||
|
TRACE0("highest safe card. ");
|
||
|
PLAY(safe);
|
||
|
return safe;
|
||
|
}
|
||
|
|
||
|
// And if that fails, just play the lowest card.
|
||
|
|
||
|
TRACE0("no safe card, choose lowest. ");
|
||
|
if (sLowCard[nSuitLed] != sBlackLady)
|
||
|
{
|
||
|
PLAY(sLowCard[nSuitLed]);
|
||
|
return sLowCard[nSuitLed];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE0("try to avoid queen. ");
|
||
|
PLAY(sHighCard[nSuitLed]);
|
||
|
return sHighCard[nSuitLed];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE0("can't follow suit. ");
|
||
|
|
||
|
// At this point, there are no cards of the led suit. The first
|
||
|
// priority is to try to sluff off the queen of spades.
|
||
|
|
||
|
if (!bFirstTrick || !::pMainWnd->IsFirstBloodEnforced())
|
||
|
{
|
||
|
if (sBlackLady != EMPTY)
|
||
|
{
|
||
|
TRACE0("gotcha! Queen of Spades. ");
|
||
|
return sBlackLady;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The next priority is to dump high spades (if queen not yet played).
|
||
|
|
||
|
if ((!h.bQSPlayed) && (nHighVal[SPADES] > QUEEN))
|
||
|
{
|
||
|
TRACE0("lose high spade. ");
|
||
|
PLAY(sHighCard[SPADES]);
|
||
|
return sHighCard[SPADES];
|
||
|
}
|
||
|
|
||
|
// The next priority is to find the most vulnerable suit
|
||
|
|
||
|
int mvsuit = BestSuitToDump(!bFirstTrick);
|
||
|
|
||
|
// There is an unusual situation which must be checked for explicitly.
|
||
|
// It's possible BestSuitToDump may return SPADES, and the high card
|
||
|
// is the queen. This would still be illegal if it was first round.
|
||
|
|
||
|
if (bFirstTrick && ::pMainWnd->IsFirstBloodEnforced() && mvsuit == SPADES)
|
||
|
{
|
||
|
SLOT s = sHighCard[mvsuit];
|
||
|
if (cd[s].ID() == BLACKLADY)
|
||
|
{
|
||
|
if (sHighCard[DIAMONDS] != EMPTY) // we know there's no clubs
|
||
|
mvsuit = DIAMONDS;
|
||
|
else if (sLowCard[SPADES] != sHighCard[SPADES])
|
||
|
{
|
||
|
TRACE0("dump low spade. ");
|
||
|
return sLowCard[SPADES];
|
||
|
}
|
||
|
else
|
||
|
mvsuit = HEARTS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// if someone other than me is potentially shooting, hold back high cards
|
||
|
|
||
|
if (h.bShootingRisk && h.bHumanShooter && (h.nMoonShooter != id) &&
|
||
|
(sHighCard[mvsuit] != sLowCard[mvsuit]))
|
||
|
{
|
||
|
SLOT s = sHighCard[mvsuit];
|
||
|
SLOT s2 = CardBelow(s);
|
||
|
if (s2 != EMPTY)
|
||
|
s = s2;
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
TRACE1("hold high %c. ", suitid[mvsuit]);
|
||
|
#endif
|
||
|
PLAY(s);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
TRACE1("dump %c. ", suitid[mvsuit]);
|
||
|
#endif
|
||
|
PLAY(sHighCard[mvsuit]);
|
||
|
return sHighCard[mvsuit];
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
computer::SafeCard
|
||
|
|
||
|
Returns highest safe card or EMPTY if no safe card found.
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
SLOT computer::SafeCard(handinfotype &h)
|
||
|
{
|
||
|
// Special check. If Ace of Spades is current trick winner, play the
|
||
|
// Queen of Spades rather than the King, even though the King is higher.
|
||
|
|
||
|
if ((sBlackLady!=EMPTY) && (nSuitLed==SPADES) && (currentval==(KING+1)))
|
||
|
return sBlackLady;
|
||
|
|
||
|
// Look for highest card of same suit that won't win trick.
|
||
|
|
||
|
SLOT sSafe = EMPTY; // highest safe slot
|
||
|
int nSafeVal = -1; // value of highest safe card
|
||
|
|
||
|
for (SLOT s = 0; s < MAXSLOT; s++)
|
||
|
{
|
||
|
if (cd[s].IsValid())
|
||
|
{
|
||
|
if (cd[s].Suit() == nSuitLed)
|
||
|
{
|
||
|
int v = cd[s].Value2();
|
||
|
|
||
|
// If card is safe (v < currentval) and card is highest
|
||
|
// safe card found so far (v > nSaveVal)...
|
||
|
|
||
|
if ((v < currentval) && (v > nSafeVal))
|
||
|
{
|
||
|
sSafe = s;
|
||
|
nSafeVal = v;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sSafe;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
computer::Setup
|
||
|
|
||
|
Set up reference tables for high and low cards in each suit, etc.
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void computer::Setup(handinfotype &h)
|
||
|
{
|
||
|
cardled = h.cardplayed[h.playerled];
|
||
|
if (cardled)
|
||
|
{
|
||
|
nSuitLed = cardled->Suit();
|
||
|
nValueLed = cardled->Value2();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nSuitLed = EMPTY;
|
||
|
nValueLed = EMPTY;
|
||
|
}
|
||
|
|
||
|
nPoints = 0; // points in hand already
|
||
|
|
||
|
// Initialize Tables
|
||
|
|
||
|
for (int suit = 0; suit < MAXSUIT; suit++) // highs and lows by suit
|
||
|
{
|
||
|
sHighCard[suit] = EMPTY;
|
||
|
sLowCard[suit] = EMPTY;
|
||
|
nHighVal[suit] = ACE - 1; // lower than any real card
|
||
|
nLowVal[suit] = KING + 2; // higher than any real card
|
||
|
}
|
||
|
|
||
|
sHighestCard = EMPTY; // highs and lows regardless of suit
|
||
|
sLowestCard = EMPTY;
|
||
|
nHighestVal = ACE - 1;
|
||
|
nLowestVal = KING + 2;
|
||
|
|
||
|
// Determine currentval (the value of the winning card so far) and nPoints.
|
||
|
|
||
|
currentval = nValueLed;
|
||
|
for (int i = 0; i < MAXPLAYER; i++)
|
||
|
{
|
||
|
card *c = h.cardplayed[i];
|
||
|
if (c->IsValid())
|
||
|
{
|
||
|
// First, determine if there are any point cards in play.
|
||
|
|
||
|
if (c->Suit() == HEARTS)
|
||
|
nPoints++;
|
||
|
|
||
|
if (c->ID() == BLACKLADY)
|
||
|
nPoints += 13;
|
||
|
|
||
|
// Then, find the highest card (on table) of the led suit.
|
||
|
|
||
|
if (c->Suit() == nSuitLed)
|
||
|
{
|
||
|
int v = c->Value2();
|
||
|
|
||
|
if (v > currentval)
|
||
|
currentval = v;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Calculate if we're leading or completing this trick.
|
||
|
|
||
|
bFirst = (h.playerled == id);
|
||
|
bLast = (((h.playerled + (MAXPLAYER-1)) % MAXPLAYER) == id);
|
||
|
|
||
|
// Special check for the Queen of Spades
|
||
|
|
||
|
sBlackLady = EMPTY; // assume we don't have it
|
||
|
|
||
|
// Collect information on high and low cards in each suit.
|
||
|
|
||
|
for (SLOT s = 0; s < MAXSLOT; s++)
|
||
|
{
|
||
|
if (cd[s].IsValid())
|
||
|
{
|
||
|
int suit = cd[s].Suit();
|
||
|
int v = cd[s].Value2();
|
||
|
|
||
|
if (cd[s].ID() == BLACKLADY)
|
||
|
sBlackLady = s;
|
||
|
|
||
|
if (v < nLowVal[suit])
|
||
|
{
|
||
|
nLowVal[suit] = v;
|
||
|
sLowCard[suit] = s;
|
||
|
}
|
||
|
|
||
|
if (v < nLowestVal)
|
||
|
{
|
||
|
nLowestVal = v;
|
||
|
sLowestCard = s;
|
||
|
}
|
||
|
|
||
|
if (v > nHighVal[suit])
|
||
|
{
|
||
|
nHighVal[suit] = v;
|
||
|
sHighCard[suit] = s;
|
||
|
}
|
||
|
|
||
|
if (v > nHighestVal)
|
||
|
{
|
||
|
nHighestVal = v;
|
||
|
sHighestCard = s;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|