windows-nt/Source/XPSP1/NT/shell/osshell/games/mshearts/comp2.cpp

432 lines
12 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/***************************************************************************/
/** 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;
}
}
}
}