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