// // PlaySecond() // // default implementation, generally should be overridden in derived classes // CCard* CPlayEngine::PlaySecond() { CPlayerStatusDialog& status = *m_pStatusDlg; status << "5PLAY2! Playing second, using default player logic.\n"; // get play info int nDummyPos = pDOC->GetDummyPosition(); CCard* pCardLed = pDOC->GetCurrentTrickCardLed(); int nSuitLed = pCardLed->GetSuit(); int nFaceValue = pCardLed->GetFaceValue(); CCard* pCurrTopCard = pDOC->GetCurrentTrickHighCard(); int nTrumpSuit = pDOC->GetTrumpSuit(); CSuitHoldings& suit = m_pHand->GetSuit(nSuitLed); // card to play CCard* pCard = NULL; // second hand low int numCardsInSuit = suit.GetNumCards(); if (numCardsInSuit > 0) { // default behavior -- just play the low card pCard = m_pHand->GetSuit(nSuitLed).GetBottomCard(); if (numCardsInSuit > 1) { if (*pCard < *pCurrTopCard) status << "PLY2C1! Play second hand low with the " & pCard->GetFaceName() & ".\n"; else status << "PLY2C2! As second hand, we play the lowest card we have in the suit, the " & pCard->GetFaceName() & ".\n"; } else { status << "PLY2C4! Play our only " & STSS(nSuitLed) & ", the " & pCard->GetFaceName() & ".\n"; } } else { // no cards in the suit led // trump here if possible if (m_pHand->GetNumTrumps() > 0) { // CSuitHoldings& trumpSuit = m_pHand->GetSuit(nTrumpSuit); // see if partner would win the suit otherwise CGuessedSuitHoldings& partnerSuit = m_pPlayer->GetGuessedHand(m_nPartnerPosition)->GetSuit(nSuitLed); CCardList outstandingCards; GetOutstandingCards(nSuitLed, outstandingCards); if (partnerSuit.AreAllCardsIdentified() && partnerSuit.HasCard(outstandingCards[0]->GetFaceValue())) { // partner may win the trick, so discard status << "PLY2K1! We could trump here, but we know partner holds the " & outstandingCards[0]->GetName() & " and can win the trick, so discard the " & pCard->GetName() & ".\n"; } else { // trump here if possible // but first see if we're playing ahead of dummy (dummy is to our left), // and dummy is also void in the suit CGuessedHandHoldings* pDummyHand = m_pPlayer->GetGuessedHand(nDummyPos); if ((m_pLHOpponent == pDOC->GetDummyPlayer()) && (pDummyHand->GetSuit(nSuitLed).GetNumRemainingCards() == 0) && (pDummyHand->GetSuit(nTrumpSuit).GetNumRemainingCards() > 0)) { // dummy is also void, so trump if we have a trump higher than dummy's CGuessedCard* pDummyTopTrump = pDummyHand->GetSuit(nTrumpSuit).GetAt(0); if (trumpSuit.GetNumCardsAbove(pDummyTopTrump->GetFaceValue()) > 0) { // go ahead and ruff pCard = trumpSuit.GetLowestCardAbove(pDummyTopTrump->GetFaceValue()); status << "PLY2M1! Trump here, making sure to thwart dummy's " & pDummyTopTrump->GetFaceName() & " of trumps by playing the " & pCard->GetFaceName() & ".\n"; } else { // dummy would overtrump pCard = GetDiscard(); status << "PLY2M2! We'd like to trump here, but Dummy may overruff, so discard the " & pCard->GetName() & ".\n"; } } else { // safe to trump -- play the lowest trump pCard = trumpSuit.GetBottomCard(); status << "PLY2P1! Trump with the " & pCard->GetFaceName() & ".\n"; } } } else { // discard pCard = GetDiscard(); status << "PLY2Y! We have no " & SuitToString(nSuitLed) & ", so discard the " & pCard->GetName() & ".\n"; } } // ASSERT(pCard->IsValid()); return pCard; }
// // PlayBestCard() // // called on the third and fourth hand plays to try to win the trick // CCard* CPlayEngine::PlayBestCard(int nPosition) { CPlayerStatusDialog& status = *m_pStatusDlg; // status << "2PLAY3! Playing best card.\n"; // get play info CCard* pCurrentCard = pDOC->GetCurrentTrickCardLed(); int nSuitLed = pCurrentCard->GetSuit(); int nTopPos; CCard* pCurrTopCard = pDOC->GetCurrentTrickHighCard(&nTopPos); CString strTopCardPos = PositionToString(nTopPos); BOOL bPartnerHigh = FALSE; int nCurrentRound = pDOC->GetPlayRound(); int nCurrentSeat = pDOC->GetNumCardsPlayedInRound() + 1; CCard* pPartnersCard = pDOC->GetCurrentTrickCard(m_pPartner->GetPosition()); if (pPartnersCard == pCurrTopCard) bPartnerHigh = TRUE; // int nTrumpSuit = pDOC->GetTrumpSuit(); int numCardsInSuitLed = m_pHand->GetNumCardsInSuit(nSuitLed); // card to play CCard* pCard = NULL; // // first see if somebody trumped in this hand // if ((pDOC->WasTrumpPlayed()) && (nTrumpSuit != nSuitLed)) { // a trump has been played // see whether it was played by partner or by an opponent if (bPartnerHigh) { // partner trumped -- leave it alone for now pCard = GetDiscard(); status << "PLAYB10! We let partner's " & pCurrTopCard->GetName() & " trump ride and discard the " & pCard->GetName() & ".\n"; } else { // it was an opponent that did the trumping // see if we can overtrump CSuitHoldings& trumpSuit = m_pHand->GetSuit(nTrumpSuit); CCard* pTopTrump = NULL; if (trumpSuit.GetNumCards() > 0) pTopTrump = trumpSuit.GetTopCard(); if ((numCardsInSuitLed == 0) && (pTopTrump) && (*pTopTrump > *pCurrTopCard)) { // get the lowest trump that wil top the current top trump int numTopCards = trumpSuit.GetNumCardsAbove(pCurrTopCard); pCard = trumpSuit[numTopCards-1]; status << "PLAYB20! We can overtrump " & strTopCardPos & "'s " & pCurrTopCard->GetName() & " with the " & pCard->GetFaceName() & ".\n"; } else { // no chance to win, so discard pCard = GetDiscard(); if ((numCardsInSuitLed == 0) && (trumpSuit.GetNumCards() > 0)) status << "PLAYB22! We can't overtrump " & strTopCardPos & "'s " & pCurrTopCard->GetFaceName() & ", so discard the " & pCard->GetName() & ".\n"; else status << "PLAYB23! We can't beat the opponent's " & pCurrTopCard->GetFaceName() & " of trumps, so discard the " & pCard->GetName() & ".\n"; } } } else { // else nobody has played a trump this round, _or_ a trump was led // see if we can play trumps if (numCardsInSuitLed > 0) { // nope, gotta follow the suit that was led, trumps or otherwise // if we can beat the current top card, do so with the cheapest card CSuitHoldings& suit = m_pHand->GetSuit(nSuitLed); if (*(suit.GetTopCard()) > *pCurrTopCard) { // but see if the top card is partner's if (bPartnerHigh) { // see if we should unblock here if (ISSUIT(nTrumpSuit) && (nCurrentRound == 0) && (suit.GetNumHonors() == 1)) { // first round in an NT contract, with one honor // in the suit -- unblock pCard = suit.GetTopCard(); if (suit.GetNumCards() > 1) status << "PLAYB30! Drop the " & pCard->GetFaceName() & " here to unblock the suit for partner.\n"; } else { // else this is not an unblocking situation if (nCurrentSeat == 4) { // playing in 4th seat, high card is partner, so discard pCard = GetDiscard(); status << "PLAYB34! Partner's " & pCurrTopCard->GetFaceName() & " is high, so discard the " & pCard->GetName() & ".\n"; } else { // playing in third position -- decide whether to // let partner's card ride // do so if if partner's card beats all outstanding cards CCard* pTopOutstandingCard = GetHighestOutstandingCard(nSuitLed); if ((pTopOutstandingCard == NULL) || (*pCurrTopCard > *pTopOutstandingCard)) { // let partner's card ride pCard = GetDiscard(); status << "PLAYB36! Partner's " & pCurrTopCard->GetFaceName() & " is higher than any outstanding card, so discard the " & pCard->GetName() & ".\n"; } else { // partner's card is not necessarily highest, so top it pCard = suit.GetTopSequence().GetBottomCard(); status << "PLAYB37! Partner's " & pCurrTopCard->GetFaceName() & " might not win the round, so top it with the " & pCard->GetFaceName() & ".\n"; } } } } else { // else high card is opponent's, so beat it w/ highest card affordable // although, if playing in 3rd pos ahead of dummy, just play // high enuff to beat dummy if ((nCurrentSeat == 3) && (m_bLHDefender)) { CSuitHoldings& dummySuit = GetDummySuit(nSuitLed); int nDummyTopCard = 0; if (dummySuit.GetNumCards() > 0) nDummyTopCard = dummySuit[0]->GetFaceValue(); int nTopVal = Max(nDummyTopCard, pCurrTopCard->GetFaceValue()); pCard = suit.GetLowestCardAbove(nTopVal); // see if we can beat the top card or dummy's top card if (pCard) { if (nTopVal == nDummyTopCard) { // dummy has the top card and we can beat it status << "PLAYB38A! Playing third ahead of dummy, need to beat dummy's " & CardValToString(nDummyTopCard) & ".\n"; } else { // the top card is declarer's status << "PLAYB38B! Play high to win with the " & pCard->GetFaceName() & ".\n"; } } else { // else we can't beat dummy's top card, but play // high anyway to force out his winner pCard = suit.GetLowestCardAbove(pCurrTopCard); status << "PLAYB38C! We top declarer's " & pCurrTopCard->GetFaceName() & " to force a winner from dummy.\n"; } } else if (nCurrentSeat == 3) { // else we're playing 3rd, so play the lowest card from the top sequence pCard = suit.GetTopSequence().GetBottomCard(); status << "PLAYB40! Play high to win with the " & pCard->GetFaceName() & ".\n"; } else { // else we're playing last (4th) // play the cheapest card that will beat the top card pCard = suit.GetLowestCardAbove(pCurrTopCard); status << "PLAYB41! Play the " & pCard->GetFaceName() & " to win the trick.\n"; } } } else { // we don't have a card to top the current high card if (bPartnerHigh) { // but partner's card is high, so we're OK pCard = GetDiscard(); status << "PLAYB47! Partner's " & pCurrTopCard->GetFaceName() & " can win the trick, so discard the " & pCard->GetName() & ".\n"; } else { // else we're screwed pCard = GetDiscard(); status << "PLAYB48! We can't beat " & strTopCardPos & "'s " & pCurrTopCard->GetFaceName() & ", so discard the " & pCard->GetName() & ".\n"; } } } else if (ISSUIT(nTrumpSuit) && (nSuitLed != nTrumpSuit) && (m_pHand->GetNumCardsInSuit(nTrumpSuit) > 0)) { // here, we can play a trump, so do so if appropriate // see who has the top card in this round if (bPartnerHigh) { // let partner's card ride pCard = GetDiscard(); status << "PLAYB52! Although we could trump this hand, partner's " & pCurrTopCard->GetName() & " is high, so discard the " & pCard->GetName() & ".\n"; } else { // opponents have the high card (non-trump) -- so slam 'em pCard = m_pHand->GetSuit(nTrumpSuit).GetBottomCard(); status << "PLAYB55! With no cards in " & SuitToString(nSuitLed) & ", trump with the " & pCard->GetName() & ".\n"; } } else { // here we have zero cards in the suit and in trumps, so we're hosed pCard = GetDiscard(); status << "PLAYB52! With no cards in the suit led and no trumps, we discard the " & pCard->GetName() & ".\n"; } } // ASSERT(pCard->IsValid()); ASSERT(m_pHand->HasCard(pCard)); // return pCard; }
// // Perform() // PlayResult CForce::Perform(CPlayEngine& playEngine, CCombinedHoldings& combinedHand, CCardLocation& cardLocation, CGuessedHandHoldings** ppGuessedHands, CPlayerStatusDialog& status, CCard*& pPlayCard) { // a "force" is a play of the lowest possible card that will force out // a key enemy card // check which hand this is int nOrdinal = pDOC->GetNumCardsPlayedInRound(); CPlayer* pPlayer = playEngine.GetPlayer(); BOOL bPlayingInHand = (pDOC->GetCurrentPlayer() == pPlayer); CHandHoldings& playerHand = *(combinedHand.GetPlayerHand()); CHandHoldings& dummyHand = *(combinedHand.GetPartnerHand()); CCombinedSuitHoldings& combinedSuit = combinedHand.GetSuit(m_nSuit); CSuitHoldings& playerSuit = playerHand.GetSuit(m_nSuit); CSuitHoldings& dummySuit = dummyHand.GetSuit(m_nSuit); CCard* pCardLed = pDOC->GetCurrentTrickCardByOrder(0); int nSuitLed = NONE; if (pCardLed) nSuitLed = pCardLed->GetSuit(); // see if a trump was played in this round BOOL bTrumped = FALSE; if ((nSuitLed != pDOC->GetTrumpSuit()) && (pDOC->WasTrumpPlayed())) bTrumped = TRUE; pPlayCard = NULL; // test preconditions if (!CPlay::IsPlayUsable(combinedHand, playEngine)) { m_nStatusCode = PLAY_INACTIVE; return PLAY_POSTPONE; } // // test to make sure that the required played cards have indeed been played // if (m_pRequiredPlayedCardsList) { // check if any of the cards that should have benen played // are still outstanding for(int i=0;i<m_pRequiredPlayedCardsList->GetNumCards();i++) { CCard* pCard = (*m_pRequiredPlayedCardsList)[i]; if (playEngine.IsCardOutstanding(pCard)) { status << "5PLFRCA! The force play of the " & m_pConsumedCard->GetFaceName() & " to force out the " & m_nTargetCardVal & " is not yet viable as the card [" & pCard->GetFaceName() & "] is still outstanding.\n"; m_nStatusCode = PLAY_INACTIVE; return PLAY_POSTPONE; } } } // // check our position in the play // switch(nOrdinal) { case 0: // we're leading, player #0 if (bPlayingInHand) { // playing from our own hand (declarer) if (m_nTargetHand == IN_HAND) { // play the card necessary to force the opponent pPlayCard = playerSuit.GetHighestCardBelow(m_nTargetCardVal); // NCR check that dummy does not have a singleton honor that's same value as our lead if(dummySuit.IsSingleton() && ((dummySuit.GetTopCard()->GetFaceValue() > pPlayCard->GetFaceValue()) // NCR-28 || (combinedHand.AreEquivalentCards(pPlayCard, dummySuit.GetTopCard())))) pPlayCard = playerSuit.GetBottomCard(); // NCR get lowest card if (pPlayCard == NULL) { status << "4PLFRC02! Error in force play -- no cards in declarer hand usable in a force play.\n"; m_nStatusCode = PLAY_ERROR; return m_nStatusCode; } status << "PLFRC04! Play the " & pPlayCard->GetName() & " from hand to force out the opponents' " & CardValToString(m_nTargetCardVal) & ".\n"; } else { // forcing from dummy, so lead a low card if (playerSuit.GetNumCards() > 0) { pPlayCard = playerSuit.GetBottomCard(); status << "PLFRC06! Lead a low " & STSS(m_nSuit) & " (" & pPlayCard->GetFaceName() & ") from hand to trigger a force play in dummy.\n"; } else { // oops, no card in the suit to lead! status << "4PLFRC08! Oops, we can't start a force play in the " & STSS(m_nSuit) & " suit from declarer, since we have no cards in the suit.\n"; m_nStatusCode = PLAY_POSTPONE; return m_nStatusCode; } } } else { // leading from dummy if (m_nTargetHand == IN_DUMMY) { // forcing from dummy, so do it pPlayCard = dummySuit.GetHighestCardBelow(m_nTargetCardVal); if (pPlayCard == NULL) { status << "4PLFRC10! Error in force play -- no cards in dummy usable in a force play.\n"; m_nStatusCode = PLAY_ERROR; return m_nStatusCode; } status << "PLFRC12! Play the " & pPlayCard->GetName() & " from dummy to force out the opponents' " & CardValToString(m_nTargetCardVal) & ".\n"; } else { // forcing from hand, so lead a low card from dummy if (dummySuit.GetNumCards() > 0) { pPlayCard = dummySuit.GetBottomCard(); status << "PLFRC14! Lead a low " & STSS(m_nSuit) & " (" & pPlayCard->GetFaceName() & ") from dummy to trigger a force play in hand.\n"; } else { // oops, no card in the suit to lead! status << "4PLFRC16! Oops, we can't start a force play in the " & STSS(m_nSuit) & " suit from dummy, since we have no cards in the suit.\n"; m_nStatusCode = PLAY_POSTPONE; return m_nStatusCode; } } } // all went OK m_nStatusCode = PLAY_IN_PROGRESS; // m_nStatusCode = PLAY_COMPLETE; break; case 1: // playing second -- see if we can still force // (i.e., the right suit was led, and the card led < target card) if ((nSuitLed == m_nSuit) && (pCardLed->GetFaceValue() < m_nTargetCardVal)) { // opponent led the suit, so we still try to force if ( ((bPlayingInHand) && (m_nTargetHand == IN_HAND)) || ((!bPlayingInHand) && (m_nTargetHand == IN_DUMMY)) ) { // this is the right time to play the forcing card if (bPlayingInHand) pPlayCard = playerSuit.GetHighestCardBelow(m_nTargetCardVal); else pPlayCard = dummySuit.GetHighestCardBelow(m_nTargetCardVal); // check for error if (pPlayCard == NULL) { status << "4PLFRC20! Error in force play -- no cards in " & (bPlayingInHand? "hand" : "dummy") & " usable in a force play.\n"; m_nStatusCode = PLAY_ERROR; return m_nStatusCode; } // got the forcing card status << "PLFRC22! The opponents led a " & STSS(m_nSuit) & ", so play the " & pPlayCard->GetName() & " from " & (bPlayingInHand? "hand" : "dummy") & " to try and force out the " & CardValToString(m_nTargetCardVal) & " now.\n"; // m_nStatusCode = PLAY_IN_PROGRESS; m_nStatusCode = PLAY_COMPLETE; } else { // not the right hand. so discard pPlayCard = playEngine.GetDiscard(); status << "4PLFRC24! We're not in the right position to play a forcing card, so discard the " & pPlayCard->GetName() & ".\n"; m_nStatusCode = PLAY_POSTPONE; } } else { // wrong suit led, so no point here m_nStatusCode = PLAY_POSTPONE; } // done return m_nStatusCode; break; case 2: // playing third // sanity check if ((m_nStatusCode != PLAY_IN_PROGRESS) || (nSuitLed != m_nSuit)) { m_nStatusCode = PLAY_ERROR; return m_nStatusCode; } // see if RHO trumped if (bTrumped) { status << "5PLFR30! RHO trumped, rendering the force play of the " & m_pConsumedCard->GetFaceName() & " ineffective, so skip the play.\n"; m_nStatusCode = PLAY_POSTPONE; return m_nStatusCode; } // see if we're in the correct hand if ( (bPlayingInHand && (m_nTargetHand == IN_HAND)) || (!bPlayingInHand && (m_nTargetHand == IN_DUMMY)) ) { // see if RHO played the target card CCard* pRHOCard = pDOC->GetCurrentTrickCardByOrder(1); if (pRHOCard->GetFaceValue() >= m_nTargetCardVal) { status << "5PLFR50! RHO played the " & CardValToString(m_nTargetCardVal) & ", so skip this force play.\n"; m_nStatusCode = PLAY_NOT_VIABLE; return m_nStatusCode; } // the target card has not been played yet, so proceed if (bPlayingInHand) pPlayCard = playerSuit.GetHighestCardBelow(m_nTargetCardVal); else pPlayCard = dummySuit.GetHighestCardBelow(m_nTargetCardVal); // NCR check if our card is less than the top card already played if(pPlayCard < pDOC->GetCurrentTrickHighCard()) { m_nStatusCode = PLAY_POSTPONE; // or NOT_VIABLE ??? return m_nStatusCode; } // see if we played a card from the other hand that's equivalent // to this card; if so, discard low CSuitHoldings testSuit; testSuit << combinedSuit; testSuit << pCardLed; // needed for valid test // if (testSuit.AreEquivalentCards(pCardLed, pPlayCard)) { // the opposite card can do the trick if (bPlayingInHand && (playerSuit.GetNumCards() > 1)) { pPlayCard = playerSuit.GetBottomCard(); status << "PLFRC55! The " & pCardLed->GetName() & " is sufficient for the force, so discard the " & pPlayCard->GetFaceName() & " from hand.\n"; } else if (!bPlayingInHand && (dummySuit.GetNumCards() > 1)) { pPlayCard = dummySuit.GetBottomCard(); status << "PLFRC56! The " & pCardLed->GetName() & " is sufficient for the force, so discard the " & pPlayCard->GetFaceName() & " from dummy.\n"; } } else { // check for error if (pPlayCard == NULL) { status << "4PLFRC61! Error in force play -- no cards in " & (bPlayingInHand? "hand" : "dummy") & " usable in a force play.\n"; m_nStatusCode = PLAY_ERROR; return m_nStatusCode; } // got the forcing card status << "PLFRC62! Play the " & pPlayCard->GetName() & " from " & (bPlayingInHand? "hand" : "dummy") & " to try and force out the " & CardValToString(m_nTargetCardVal) & " now.\n"; m_nStatusCode = PLAY_COMPLETE; } } else { // we're in the opposite (discard) hand. so discard pPlayCard = playEngine.GetDiscard(); status << "PLFRC64! We're in the opposite hand of a force play, so discard the " & pPlayCard->GetName() & ".\n"; m_nStatusCode = PLAY_COMPLETE; } break; case 3: // playing fourth -- can't use a force play here return PLAY_POSTPONE; break; } // done ASSERT(pPlayCard->IsValid()); return m_nStatusCode; }