Beispiel #1
0
// 
// Perform()
//
// called to do the deed
//
PlayResult CCash::Perform(CPlayEngine& playEngine, CCombinedHoldings& combinedHand, 
				   CCardLocation& cardLocation, CGuessedHandHoldings** ppGuessedHands, 
				   CPlayerStatusDialog& status, CCard*& pPlayCard)
{
	// 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());
	CSuitHoldings& declarerSuit = playerHand.GetSuit(m_nSuit);
	CSuitHoldings& dummySuit = dummyHand.GetSuit(m_nSuit);
	CCombinedSuitHoldings& combinedSuit = combinedHand.GetSuit(m_nSuit);
	CCard* pCardLed = pDOC->GetCurrentTrickCardByOrder(0);
	int nSuitLed = pCardLed? pCardLed->GetSuit() : NONE;
	CDeclarerPlayEngine& declarerEngine = (CDeclarerPlayEngine&) playEngine;
	
	// see if a trump was played in this round
	BOOL bTrumped = FALSE;
	int nTrumpSuit = pDOC->GetTrumpSuit();
	if ((nSuitLed != nTrumpSuit) && (pDOC->WasTrumpPlayed()))
		bTrumped = TRUE;
	pPlayCard = NULL;

	// see what the top card in the round is
	CCard* pTopCard = pDOC->GetCurrentTrickHighCard();
	CCard* pDeclarerCard = pDOC->GetCurrentTrickCard(playEngine.GetPlayerPosition());
	CCard* pDummysCard = pDOC->GetCurrentTrickCard(playEngine.GetPartnerPosition());
	CCard* pPartnersCard = bPlayingInHand? pDummysCard : pDeclarerCard;
	BOOL bPartnerHigh = (pTopCard == pPartnersCard);

	//
	if (!CPlay::IsPlayUsable(combinedHand, playEngine))
	{
		m_nStatusCode = PLAY_INACTIVE;
		return PLAY_POSTPONE;
	}

	// see if one or more opponents are void in the suit AND have not shown out
	// of trumps
	if (ISSUIT(nTrumpSuit))
	{
		int numOutstandingTrumps = playEngine.GetNumOutstandingCards(nTrumpSuit);
		BOOL bSkipPlay = FALSE;
		// if leading, check LHO
		CGuessedHandHoldings* pLHOHand = ppGuessedHands[playEngine.GetLHOpponent()->GetPosition()];
		if ((nOrdinal == 0) && (numOutstandingTrumps > 0) && pLHOHand->IsSuitShownOut(m_nSuit) && !pLHOHand->IsSuitShownOut(nTrumpSuit))
		{
			bSkipPlay = TRUE;
			status << "5PLCSHZ1! The play <" & m_strName & "> is not yet safe as LHO has shown out of the suit and may ruff.\n";
		}
		// also check RHO
		CGuessedHandHoldings* pRHOHand = ppGuessedHands[playEngine.GetRHOpponent()->GetPosition()];
		if ((numOutstandingTrumps > 0) && pRHOHand->IsSuitShownOut(m_nSuit) && !pRHOHand->IsSuitShownOut(nTrumpSuit))
		{
			bSkipPlay = TRUE;
			status << "5PLCSHZ2! The play <" & m_strName & "> is not yet safe as RHO has shown out of the suit and may ruff.\n";
		}
		//
		if (bSkipPlay)
		{
			// opponents might ruff
			m_nStatusCode = PLAY_INACTIVE;
			return PLAY_POSTPONE;
		}
	}

	// else proceed
	// check our position in the play
	switch(nOrdinal)
	{
		case 0:
			// we're leading, player #0
			if (bPlayingInHand) 
			{
				// playing from our own hand (declarer) and cashing in hand
				if (m_nTargetHand == IN_HAND)
				{
					// cashing from hand -- check to be sure the other hand has losers
					// i.e., don't discard a winner on a winner UNLESS both hands 
					// have nothing but winners, OR the other hand has only one card, 
					// a winner that's lower than the top card in this hand, 
					// AND this hand has only winners
					// this is because we don't want to end up stranded in the wrong hand
					// examples: 
					// ---------
					//   from Kx/A   -- don't lead the King!
					//        Kxx/AQ -- again, don't lead the King
					//        KJ/Q   -- we _can_ lead the K (in fact, we should)
					//        KQJ/T9 -- doesn't matter
					//        Qx/AKJ -- doesn't matter
					if ((dummySuit.GetNumCards() == 1) && (combinedSuit.GetNumDummyLosers() == 0) && 
								(combinedSuit.GetNumDeclarerLosers() == 0) && (*dummySuit[0] < *m_pConsumedCard))
					{
						// this is a special case, so it's OK
					}
					else if ((dummySuit.GetNumCards() > 0) && (combinedSuit.GetNumDummyLosers() == 0) && 
								(combinedSuit.GetNumDeclarerLosers() > 0) &&
								(combinedSuit.GetNumDummyWinners() < declarerSuit.GetNumCards()) &&
								(*dummySuit[0] > *declarerSuit[0]) &&
								(declarerEngine.GetNumDeclarerEntries() == 1) )
					{
						// here, nothing but higher winners in dummy, which is shorter than hand
						status << "5PLCSH02! We could cash the " & m_pConsumedCard->GetName() &
								  " from hand, but dummy has no losers to discard in the suit and could get stranded there.\n";
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}
					// proceed 
					pPlayCard = m_pConsumedCard;
					status << "PLCSH10! Cash the " & pPlayCard->GetName() &
							  " from hand.\n";
				}
				else
				{
					// playing from hand. but cashing from dummy,
					// lead a low card from hand
					if (declarerSuit.GetNumCards() > 0)
					{
						// we have cards in the suit
//						if (combinedSuit.GetNumDeclarerLosers() > 0)
						if (declarerSuit.GetNumCardsBelow(m_pConsumedCard) > 0)
						{
							// lead a low card, but test first
							pPlayCard = declarerSuit.GetBottomCard();
							if ( combinedSuit .AreEquivalentCards(pPlayCard, m_pConsumedCard) &&
//								 (declarerSuit.GetNumCards() > 1) && (ombinedSuit.GetNumDummyLosers() > 0) )
												 (combinedSuit.GetNumDummyLosers() > 0) )
							{
								// oops, the "low" card is equivalent to the cash card!
								status << "4PLCSH15! Oops, the lowest card we can lead from hand for the cash is the " & 
										  pPlayCard->GetFaceName() & ", which is equivalent to the " &
										  m_pConsumedCard->GetFaceName() & ", so skip the cash play.\n";
								m_nStatusCode = PLAY_INACTIVE;
								return PLAY_POSTPONE;
							}
							else
							{
								status << "PLCSH20! Lead a low " & STSS(m_nSuit) & 
										  " (the " & pPlayCard->GetFaceName() &
										  ") from hand in order to cash the " & 
										  m_pConsumedCard->GetFaceName() & " in dummy.\n";
							}
						}
						else 
						{
							// oops, we have no low cards in hand to lead!
							status << "4PLCSH21! Oops, we wanted to cash a " & STSS(m_nSuit) & 
									  " in dummy, but we have no low " & STS(m_nSuit) & 
									  " in hand to lead, so we have to abandon that play.\n";
							m_nStatusCode = PLAY_INACTIVE;
							return PLAY_POSTPONE;
						}
					}
					else
					{
						// oops, no card in the suit to lead!
						status << "4PLCSH22! Oops, we wanted to cash a " & STSS(m_nSuit) & 
								  " in dummy, but we have no " & STS(m_nSuit) & 
								  " in hand to lead, so we have to abandon that play.\n";
						m_nStatusCode = PLAY_NOT_VIABLE;
						return m_nStatusCode;
					}
				}
			}
			else
			{
				// playing in dummy
				if (m_nTargetHand == IN_DUMMY)
				{
					// we're leading from dummy and also cashing in dummy 
					// also check to be sure the player's hand has losers
					if ((declarerSuit.GetNumCards() == 1) && (combinedSuit.GetNumDeclarerLosers() == 0) && 
								(combinedSuit.GetNumDummyLosers() == 0) && (*declarerSuit[0] < *m_pConsumedCard))
					{
						// this is a special case (e.g., Kx/J), so it's OK
					}
					else if ((declarerSuit.GetNumCards() > 0) && (combinedSuit.GetNumDeclarerLosers() == 0) && 
								(combinedSuit.GetNumDummyLosers() > 0) &&
								(combinedSuit.GetNumDeclarerWinners() < dummySuit.GetNumCards()) &&
								(*declarerSuit[0] > *dummySuit[0]) &&
								(declarerEngine.GetNumDummyEntries() == 1) )
					{
						// here, nothing but higher winners in hand, which is shorter than dummy
						status << "5PLCSH12! We could cash the " & m_pConsumedCard->GetName() &
								  " from dummy, but we have no losers in hand to discard in the suit and could get stranded there.\n";
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}

					// proceed
					pPlayCard = m_pConsumedCard;
					status << "PLCSH30! Cash the " & pPlayCard->GetName() & " from dummy.\n";
				}
				else
				{
					// leading from dummy, but cashing from hand
					// lead a low card of the suit from dummy
					if (dummySuit.GetNumCards() > 0)
					{
//						if (combinedSuit.GetNumDummyLosers() > 0)
						if (dummySuit.GetNumCardsBelow(m_pConsumedCard) > 0)
						{
							pPlayCard = dummySuit.GetBottomCard();
							if ( combinedSuit .AreEquivalentCards(pPlayCard, m_pConsumedCard) &&
//								 (dummySuit.GetNumCards() > 1) && (combinedSuit.GetNumDeclarerLosers() > 0) )
												(combinedSuit.GetNumDeclarerLosers() > 0) )
							{
								// oops, the "low" card is equivalent to the cash card!
								status << "4PLCSH35! Oops, the lowest card we can lead from dummy for the cash is the " & 
										  pPlayCard->GetFaceName() & ", which is equivalent to the " &
										  m_pConsumedCard->GetFaceName() & ", so skip the cash play.\n";
								m_nStatusCode = PLAY_INACTIVE;
								return PLAY_POSTPONE;
							}
							else
							{
								status << "PLCSH40! Lead a low " & STSS(m_nSuit) & 
										  " (the " & pPlayCard->GetFaceName() &
										  ") from dummy in order to cash the " & 
										  m_pConsumedCard->GetFaceName() & " in hand.\n";
							}
						}
						else
						{
							// oops, we have no low cards in dummy to lead!
							status << "4PLCSH41! Oops, we wanted to cash a " & STSS(m_nSuit) & 
									  " in hand, but we have no low " & STS(m_nSuit) & 
									  " in dummy to lead, so we have to abandon that play.\n";
							m_nStatusCode = PLAY_INACTIVE;
							return PLAY_POSTPONE;
						}
					}
					else
					{
						// oops, no card in the suit to lead!
						status << "4PLCSH42! Oops, we wanted to cash a " & STSS(m_nSuit) & 
								  " in hand, but we have no " & STS(m_nSuit) & 
								  " in dummy to lead, so we have to abandon that play.\n";
						m_nStatusCode = PLAY_NOT_VIABLE;
						return m_nStatusCode;
					}
				}
			}
			// all went OK
			m_nStatusCode = PLAY_IN_PROGRESS;
			break;

		case 1:
			// playing second
			if (nSuitLed == m_nSuit)
			{
				// see if the card led is higher than ours
				if (*pCardLed > *m_pConsumedCard)
				{
					status << "3PLCSH53! the card led (" & pCardLed->GetName() & ") " & 
							  " beats the " & m_pConsumedCard->GetName() & 
							  " we were going to play, so skip the cash for now.\n";
					m_nStatusCode = PLAY_INACTIVE;
					return PLAY_POSTPONE;
				}
				// card led is not higher than our cash card
				// see if this is the correct hand for the cash
				if ( ((bPlayingInHand) && (m_nTargetHand == IN_HAND)) ||
				     ((!bPlayingInHand) && (m_nTargetHand == IN_DUMMY)) )
				{
					// this is the right time to play the cash card
					// but do a special test here -- see if partner 
					// has a singleton which is higher than this card
					if ( (bPlayingInHand && (dummySuit.GetNumCards() == 1) &&
					     (*(dummySuit[0]) > *m_pConsumedCard)) ||
						 (!bPlayingInHand && (declarerSuit.GetNumCards() == 1) &&
					     (*(declarerSuit[0]) > *m_pConsumedCard)) )
					{
						status << "3PLCSH54! Partner has a singleton which is higher than the " & 
								  m_pConsumedCard->GetName() & " we were going to cash, so skip the cash play for now.\n";
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}
					
					// also check if partner has nothing but winners in his hand
					if ( (bPlayingInHand && (dummySuit.GetNumCards() > 0) && (combinedSuit.GetNumDummyLosers() == 0) && (combinedSuit.GetNumDeclarerLosers() > 0)) ||
						 (!bPlayingInHand && (declarerSuit.GetNumCards() > 0) && (combinedSuit.GetNumDeclarerLosers() == 0) && (combinedSuit.GetNumDummyLosers() > 0)) )
					{
/*
 * ???
 */
						status << "5PLCSH54a! We could cash the " & m_pConsumedCard->GetName() & 
							(bPlayingInHand? " in hand" : " in dummy") & ", but we have no losers in " &
							(bPlayingInHand? " in dummy" : " in hand") & " in the suit, so skip this play.\n";
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}
					pPlayCard = m_pConsumedCard;
					status << "PLCSH55! The opponents led a " & STSS(m_nSuit) & 
							  ", so cash the " & pPlayCard->GetFaceName() & " now.\n";
				}
				else
				{
					// not the right hand, so discard from this hand
					// but skip if we have no losers in this hand but do in the other
					if ( (m_nTargetHand == IN_HAND) &&
								 (dummySuit.GetNumCards() > 0) && 
								 (combinedSuit.GetNumDummyLosers() == 0) &&
								 (combinedSuit.GetNumDeclarerLosers() > 0))
					{
						// no losers in dummy to discard, but some in hand!
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}
					if ( (m_nTargetHand == IN_DUMMY) &&
								 (declarerSuit.GetNumCards() > 0) && 
								 (combinedSuit.GetNumDeclarerLosers() == 0) &&
								 (combinedSuit.GetNumDummyLosers() > 0))
					{
						// no losers in hand to discard, but some in dummy!
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}

					// else go ahead and discard
					pPlayCard = playEngine.GetDiscard();
					status << "PLCSH56! Discard a " & STSS(m_nSuit) & " from " &
							  (bPlayingInHand? "hand" : "dummy") &
							  " in anticipation of cashing the " & 
							  m_pConsumedCard->GetFaceName() & " in " &
							  (bPlayingInHand? "dummy" : "hand") & ".\n";
				}
			}
			else
			{
				// wrong suit led, so no point here
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// else we're ok
			m_nStatusCode = PLAY_IN_PROGRESS;
			break;


		case 2:
			// playing third -- this play may or may not be active
			// i.e., may have started one cash, then switched to another
			// see if the wrong suit was led 
			if (nSuitLed != m_nSuit)
			{
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// see if RHO has trumped
			if (bTrumped)
			{
				status << "2PLCSH64! RHO has trumped, so abandon the cashing play for this round.\n";
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// see if the current top card in the trick is higher than ours
			if (*pTopCard > *m_pConsumedCard)
			{
				status << "2PLCSH66! the " & m_pConsumedCard->GetName() & " can't beat the " &
						   pTopCard->GetName() & ", so skip the cash.\n";
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// else check which hand we're playing in
			if (bPlayingInHand) 
			{
				// playing from our own hand (declarer)
				// see if it's time to cash
				if (m_nTargetHand == IN_HAND)
				{
					// proceed
					pPlayCard = m_pConsumedCard;
					status << "PLCSH68! Cash the " & pPlayCard->GetName() & " from hand.\n";
				}
				else
				{
					// cash is/was in dummy
					if (bTrumped)
					{
						pPlayCard = playEngine.GetDiscard();
						status << "2PLCSH70! RHO's trump negates our cash; discard the " &
								  pPlayCard->GetName() & ".\n";
					}
					else
					{
						// see if we need to win in this hand to cash the remaining winnners
						if ((dummySuit.GetNumCards() == 0) && (declarerSuit.GetNumCards() > 1) && 
								(declarerSuit.GetNumLosers() == 0) && (*declarerSuit[0] > *pCardLed))
						{
							pPlayCard = declarerSuit.GetLowestCardAbove(pCardLed);
							status << "PLCSH71! Our cash of the " & pCardLed->GetName() & 
									  " from dummy is holding, but we need to win in the hand to cash the remaining winners, so play the " &
									  pPlayCard->GetName() & " here.\n";
						}
						else
						{
							pPlayCard = playEngine.GetDiscard();
							status << "PLCSH72! Our cash of the " & pCardLed->GetName() & 
									  " from dummy is holding, so discard the " & pPlayCard->GetName() & ".\n";
						}
					}
				}
			}
			else
			{
				// playing in dummy
				// see if it's time to cash
				if (m_nTargetHand == IN_DUMMY)
				{
					// proceed
					pPlayCard = m_pConsumedCard;
					status << "PLCSH74! Cash the " & pPlayCard->GetName() &
							  " from dummy.\n";
				}
				else
				{
					// cashed from hand, so discard
					if (bTrumped)
					{
						pPlayCard = playEngine.GetDiscard();
						status << "2PLCSH76! RHO's trump negates our cash; discard the " &
								  pPlayCard->GetName() & ".\n";
					}
					else
					{
						// see if we need to win here in dummy to cash the remaining winnners
						if ((declarerSuit.GetNumCards() == 0) && (dummySuit.GetNumCards() > 1) && 
								(dummySuit.GetNumLosers() == 0) && (*dummySuit[0] > *pCardLed))
						{
							pPlayCard = dummySuit.GetLowestCardAbove(pCardLed);
							status << "PLCSH77! Our cash of the " & pCardLed->GetName() & 
									  " from hand is holding, but we need to win in dummy to cash the remaining winners, so play the " &
									  pPlayCard->GetName() & " here.\n";
						}
						else
						{
							pPlayCard = playEngine.GetDiscard();
							status << "PLCSH78! Our cash of the " &
									  pCardLed->GetName() & " from hand is holding, so discard the " &
									  pPlayCard->GetName() & ".\n";
						}
					}
				}
			}
			// all's OK
			m_nStatusCode = PLAY_COMPLETE;
			break;


		case 3:
			// playing fourth
			// make sure the play is in progress
			if (m_nStatusCode != PLAY_IN_PROGRESS)
				return PLAY_INACTIVE;
			// since opponent led the suit, we are still trying to cash
			status << "3PLCSH80! the opponents led a " & STSS(nSuitLed) & 
					  ", so see if we can finish up the cash play here.\n";
			// see if RHO (or partner) has trumped
			if (bTrumped)
			{
				status << "2PLCSH82! The suit was trumped, so abandon the cashing play for this round.\n";
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// see if the current (opponents') top card in the trick is higher than ours
			if ((!bPartnerHigh) && (*pTopCard > *m_pConsumedCard))
			{
				status << "2PLCSH83! the " & m_pConsumedCard->GetName() & " we intended to cash can't beat the " &
						   pTopCard->GetName() & ", so skip the cash.\n";
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// else see if partner played a top card (which wasn't part of the cash)
			if ((bPartnerHigh) && (m_pConsumedCard != pPartnersCard))
			{
				status << "2PLCSH84! Partner's " & pTopCard->GetName() & " is high, so skip the cash of the " &
						  m_pConsumedCard->GetFaceName() & ".\n";
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// else check which hand we're playing in
			if (bPlayingInHand) 
			{
				// playing from our own hand (declarer)
				// see if it's time to cash
				if (m_nTargetHand == IN_HAND)
				{
					// cashing from hand, so do it
					// BUT see if we have a lower card which would win!
					if (declarerSuit.GetNumCardsAbove(pTopCard) > 1)
					{
						CCard* pCard = declarerSuit.GetLowestCardAbove(pTopCard);
						status << "3PLCSH85! We can actually play the " & pCard->GetFaceName() &
								  " to win this trick, so postpone the cash of the " &
								  m_pConsumedCard->GetFaceName() & ".\n";
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}
					pPlayCard = m_pConsumedCard;
					status << "PLCSH86! Cash the " & pPlayCard->GetName() & " from hand.\n";
				}
				else
				{
					// cash is/was in dummy, so discard
					if (bTrumped)
					{
						pPlayCard = playEngine.GetDiscard();
						status << "2PLCSH87! RHO's trump negates our cash; discard the " &
								  pPlayCard->GetName() & ".\n";
					}
					else
					{
						// see if we need to win in this hand to cash the remaining winnners
						if ((dummySuit.GetNumCards() == 0) && (declarerSuit.GetNumCards() > 1) && 
								(declarerSuit.GetNumLosers() == 0) && (*declarerSuit[0] > *pCardLed))
						{
							pPlayCard = declarerSuit.GetLowestCardAbove(pCardLed);
							status << "PLCSH88a! Our cash of the " & m_pConsumedCard->GetName() & 
									  " from dummy is holding, but we need to win in the hand to cash the remaining winners, so play the " &
									  pPlayCard->GetName() & " here.\n";
						}
						else
						{
							pPlayCard = playEngine.GetDiscard();
							status << "PLCSH88b! Our cash of the " &
									  m_pConsumedCard->GetName() & " from dummy is holding, so discard the " &
									  pPlayCard->GetName() & ".\n";
						}
					}
				}
			}
			else
			{
				// playing in dummy
				// see if it's time to cash
				if (m_nTargetHand == IN_DUMMY)
				{
					// cashing from dummy, so do it
					// BUT see if we have a lower card which would win!
					if (dummySuit.GetNumCardsAbove(pTopCard) > 1)
					{
						CCard* pCard = dummySuit.GetLowestCardAbove(pTopCard);
						status << "3PLCSH89! We can actually play the " & pCard->GetFaceName() &
								  " to win this trick, so postpone the cash of the " &
								  m_pConsumedCard->GetFaceName() & ".\n";
						m_nStatusCode = PLAY_INACTIVE;
						return PLAY_POSTPONE;
					}
					pPlayCard = m_pConsumedCard;
					status << "PLCSH90! Cash the " & pPlayCard->GetName() &
							  " from dummy.\n";
				}
				else
				{
					// cashed from hand, so discard
					if (bTrumped)
					{
						pPlayCard = playEngine.GetDiscard();
						status << "2PLCSH91! RHO's trump negates our cash; discard the " &
								  pPlayCard->GetName() & ".\n";
					}
					else
					{
						// see if we need to win here in dummy to cash the remaining winnners
						if ((declarerSuit.GetNumCards() == 0) && (dummySuit.GetNumCards() > 1) && 
								(dummySuit.GetNumLosers() == 0) && (*dummySuit[0] > *pCardLed))
						{
							pPlayCard = dummySuit.GetLowestCardAbove(pCardLed);
							status << "PLCSH92a! Our cash of the " & m_pConsumedCard->GetName() & 
									  " from hand is holding, but we need to win in dummy to cash the remaining winners, so play the " &
									  pPlayCard->GetName() & " here.\n";
						}
						else
						{
							pPlayCard = playEngine.GetDiscard();
							status << "PLCSH92b! Our cash of the " &
									  m_pConsumedCard->GetName() & " from hand is holding, so discard the " &
									  pPlayCard->GetName() & ".\n";
						}
					}
				}
			}
			// all's OK
			m_nStatusCode = PLAY_COMPLETE;
			break;
	}

	// ### TEMP ###
	if (pDOC->GetCurrentPlayerPosition() == playEngine.GetPlayerPosition())
		ASSERT(playEngine.GetPlayer()->HasCard(pPlayCard));
	else
		ASSERT(playEngine.GetPartner()->HasCard(pPlayCard));

	// done
	ASSERT(pPlayCard->IsValid());
	return m_nStatusCode;
}
//
// Perform()
//
PlayResult CTypeAFinesse::Perform(CPlayEngine& playEngine, CCombinedHoldings& combinedHand, 
						   CCardLocation& cardLocation, CGuessedHandHoldings** ppGuessedHands, 
						   CPlayerStatusDialog& status, CCard*& pPlayCard)
{
	// Type A Finesse
	// - opportunistic play of a non-top card in second position to
	//   finesse against LHO

	// 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());
	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;
	CCard* pTopCard = pDOC->GetCurrentTrickHighCard();
	pPlayCard = NULL;
	CCard* pOppCard = NULL;

	// test preconditions
	if (!CPlay::IsPlayUsable(combinedHand, playEngine))
	{
		m_nStatusCode = PLAY_INACTIVE;
		return PLAY_POSTPONE;
	}

	// check our position in the play
	switch(nOrdinal)
	{
		case 0:
			// can't use in first seat
			m_nStatusCode = PLAY_NOT_VIABLE;
			return m_nStatusCode;


		case 1:
			// playing second -- this is the key to the finesse
			// see if the wrong suit was led 
			if (nSuitLed != m_nSuit)
			{
				m_nStatusCode = PLAY_INACTIVE;
				return PLAY_POSTPONE;
			}
			// check which hand we're playing in
			if (bPlayingInHand) 
			{
				// playing second in our own hand (declarer)
				// see if it's time to finesse
				if (m_nTargetHand == IN_HAND)
				{
					// play the finesse card
					pPlayCard = m_pConsumedCard;
					status << "PLAFN20! Opportunistically finesse the " & pPlayCard->GetName() & " from hand in second position against " &
							  PositionToString(playEngine.GetLHOpponent()->GetPosition()) & ".\n";
				}
				else
				{
					// finessing in hand, but this is dummy? messed up
					status << "4PLAFN30! We intended to finesse in hand, but ended up here in dummy in third position -- so skip this play.\n";
					m_nStatusCode = PLAY_INACTIVE;
					return PLAY_POSTPONE;
				}
			}
			else
			{
				// playing third in dummy
				// see if it's time to finnesse here
				if (m_nTargetHand == IN_DUMMY)
				{
					// finesse the card from dummy 
					pPlayCard = m_pConsumedCard;
					status << "PLAFN54! Opportunistically finesse the " & pPlayCard->GetName() & " from dummy in second position against " &
							  PositionToString(playEngine.GetRHOpponent()->GetPosition()) & ".\n";
				}
				else
				{
					// messed up
					status << "4PLAFN60! We intended to finesse in hand, but ended up here in dummy in third position -- so skip this play.\n";
					m_nStatusCode = PLAY_INACTIVE;
					return PLAY_POSTPONE;
				}
			}
			// all went OK
			m_nStatusCode = PLAY_IN_PROGRESS;
			break;


		case 2:
			// can't use this play in third posiiton
			m_nStatusCode = PLAY_NOT_VIABLE;
			return m_nStatusCode;


		case 3:
			// in 4th position, simply discard 
			if (m_nStatusCode == PLAY_IN_PROGRESS)
			{
				// discard here, but see if we won
				if (pTopCard == m_pConsumedCard)
				{
					pPlayCard = playEngine.GetDiscard();
					status << "PLAFN76! The finesse worked; finish the play by discarding the " & pPlayCard->GetName() & ".\n";
					m_nStatusCode = PLAY_COMPLETE;
				}
				else
				{
					status << "PLAFN77! The finesse failed, so abandon the play\n";
					m_nStatusCode = PLAY_NOT_VIABLE;
				}
				return m_nStatusCode;
			}
			else
			{
				// play is not active
				m_nStatusCode = PLAY_INACTIVE;
				return m_nStatusCode;
			}
	}

	// done
	ASSERT(pPlayCard->IsValid());
	return m_nStatusCode;
}
Beispiel #3
0
//
// 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;
}
Beispiel #4
0
//
// Perform()
//
PlayResult CRuff::Perform(CPlayEngine& playEngine, CCombinedHoldings& combinedHand, 
				   CCardLocation& cardLocation, CGuessedHandHoldings** ppGuessedHands, 
				   CPlayerStatusDialog& status, CCard*& pPlayCard)
{
	// 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());
	CSuitHoldings& playerSuit = playerHand.GetSuit(m_nSuit);
	CSuitHoldings& dummySuit = dummyHand.GetSuit(m_nSuit);
	CCombinedSuitHoldings& combinedSuit = combinedHand.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;
	int nTrumpSuit = pDOC->GetTrumpSuit();
	if ((nSuitLed != nTrumpSuit) && (pDOC->WasTrumpPlayed()))
		bTrumped = TRUE;
	pPlayCard = NULL;
	CCard* pOppCard = NULL;
	//
	CCard* pRoundTopCard = pDOC->GetCurrentTrickHighCard();
	CCard* pDeclarerCard = pDOC->GetCurrentTrickCard(playEngine.GetPlayerPosition());
	CCard* pDummysCard = pDOC->GetCurrentTrickCard(playEngine.GetPartnerPosition());
	CCard* pPartnersCard = bPlayingInHand? pDummysCard : pDeclarerCard;
	BOOL bPartnerHigh = (pRoundTopCard == pPartnersCard);
	//
	BOOL bValid = FALSE;


	// test preconditions
	if (!CPlay::IsPlayUsable(combinedHand, playEngine))
	{
		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)?  can't do so!
				if (m_nTargetHand == IN_HAND)
				{
					// can't ruff here
//					status << "4PLRUF02! Can't ruff a card when leading.\n";
					m_nStatusCode = PLAY_POSTPONE;
					return m_nStatusCode;
				}
				else
				{
					// ruffing in dummy -- first check eligibility
					if (dummyHand.GetNumCardsInSuit(m_nSuit) > 0)
					{
						// can't use this now
						m_nStatusCode = PLAY_POSTPONE;
						return m_nStatusCode;
					}
					// now lead a low card of the suit from hand
					if (combinedSuit.GetNumDeclarerLosers() > 0)
					{
						pPlayCard = playerHand.GetSuit(m_nSuit).GetBottomCard();
						status << "PLRUF04! Lead a low " & STSS(m_nSuit) & 
								  " (the " & pPlayCard->GetFaceName() & ") from hand to ruff in dummy.\n";
					}
					else
					{
						// oops, no card in the suit to lead!
						status << "4PLRUF08! Oops, we wanted to ruff a " & STSS(m_nSuit) & 
								  " in dummy, but we have no " & STSS(m_nSuit) & 
								  " losers in hand to lead, so we have to abandon the play.\n";
						m_nStatusCode = PLAY_NOT_VIABLE;
						return m_nStatusCode;
					}
				}
			}
			else
			{
				// leading from dummy
				if (m_nTargetHand == IN_DUMMY)
				{
					// leading from dummy & ruffing in dummy? no can do
//					status << "4PLRUF12! Can't lead from dummy and ruff in dummy at the same time.\n";
					m_nStatusCode = PLAY_POSTPONE;
					return m_nStatusCode;
				}
				else 
				{
					// ruffing in hand -- first check eligibility
					if (playerHand.GetNumCardsInSuit(m_nSuit) > 0)
					{
						// can't use this now
						m_nStatusCode = PLAY_POSTPONE;
						return m_nStatusCode;
					}
					// now lead a low card of the suit from dummy
					if (combinedSuit.GetNumDummyLosers() > 0)
					{
						pPlayCard = dummyHand.GetSuit(m_nSuit).GetBottomCard();
						status << "PLRUF14! Lead a low " & STSS(m_nSuit) & 
								  " (the " & pPlayCard->GetFaceName() & ") from dummy to ruff in hand.\n";
					}
					else
					{
						// oops, no card in the suit to lead!
						status << "4PLRUF18! Oops, we wanted to ruff a " & STSS(m_nSuit) & 
								  " in hand, but we have no " & STS(m_nSuit) & 
								  " losers in dummy to lead, so we have to abandon the play.\n";
						m_nStatusCode = PLAY_NOT_VIABLE;
						return m_nStatusCode;
					}
				}
			}
			// at this point, all went OK
			m_nStatusCode = PLAY_IN_PROGRESS;
			break;

		case 1:
			// playing second -- may be able to ruff here, intended or not
			if (nSuitLed == nTrumpSuit)
			{
				// but not if a trump was led
					m_nStatusCode = PLAY_POSTPONE;
					return m_nStatusCode;
			}

			// an unintended ruff is OK only if the card led is not a trump card, 
			// _and_ we have zero cards in the suit in the appropriate hand
			if ( ((m_nTargetHand == IN_HAND) && (playerHand.GetNumCardsInSuit(nSuitLed) == 0)) ||
				 ((m_nTargetHand == IN_DUMMY) && (dummyHand.GetNumCardsInSuit(nSuitLed) == 0)) )
			{
				status << "3PLRUF20! We can ruff in the suit led by the opponent.\n";
			}
			else
			{
//				status << "3PLRUF21! A ruff here is not possible.\n";
				m_nStatusCode = PLAY_POSTPONE;
				return m_nStatusCode;
			}

			// see which hand this is
			if (bPlayingInHand) 
			{
				// playing second in hand -- see where the ruff is
				if (m_nTargetHand == IN_HAND)
				{
					// ruff here
					CSuitHoldings& playerTrumps = playerHand.GetSuit(nTrumpSuit);
					if (playerTrumps.GetNumCards() > 0)
					{
						pPlayCard = playerTrumps.GetBottomCard();
						status << "PLRUF30! Ruff in hand with the " & pPlayCard->GetName() & ".\n";
					}
					else
					{
						status << "4PLRUF32! Error - no trumps left in hand to use in a ruff.\n";
						m_nStatusCode = PLAY_ERROR;
						return m_nStatusCode;
					}
				}
				else
				{
					// we'll be ruffing in dummy later, so discard
					pPlayCard = playEngine.GetDiscard();
					status << "PLRUF34! We'll be ruffing in dummy, so discard the " &
							  pPlayCard->GetName() & " from hand.\n";
				}
			}
			else
			{
				// playing second in dummy -- see where the finese card is located
				if (m_nTargetHand == IN_DUMMY)
				{
					// ruff here
					CSuitHoldings& dummyTrumps = dummyHand.GetSuit(nTrumpSuit);
					if (dummyTrumps.GetNumCards() > 0)
					{
						pPlayCard = dummyTrumps.GetBottomCard();
						status << "PLRUF40! Ruff in dummy with the " & pPlayCard->GetName() & ".\n";
					}
					else
					{
						status << "4PLRUF42! Error - no trumps left in dummy to use in a ruff.\n";
						m_nStatusCode = PLAY_ERROR;
						return m_nStatusCode;
					}
				}
				else
				{
					// finessing in hand, so discard from dummy 
					pPlayCard = playEngine.GetDiscard();
					status << "PLRUF44! We'll be ruffing in hand, so discard the " &
							  pPlayCard->GetName() & " from dummy.\n";
				}
			}
			// done here
			m_nStatusCode = PLAY_IN_PROGRESS;
			break;


		case 2:  case 3:
			// playing third -- ruff if possible
//			if (m_nStatusCode != PLAY_IN_PROGRESS)
//				return PLAY_INACTIVE;
			// unintended ruff is OK only if the card led is not a trump card, 
			// _and_ we have zero cards in the suit
			if (bPlayingInHand)
			{
				if ( (m_nStatusCode == PLAY_IN_PROGRESS) ||
					 ((nSuitLed != nTrumpSuit) && (playerHand.GetNumCardsInSuit(nSuitLed) == 0)) )
					bValid = TRUE;
			}
			else
			{
				if ( (m_nStatusCode == PLAY_IN_PROGRESS) ||
					 ((nSuitLed != nTrumpSuit) && (dummyHand.GetNumCardsInSuit(nSuitLed) == 0)) )
					bValid = TRUE;
			}
			//
			if (bValid)
			{
				// bonanza
//				status << "3PLRUF50! We can ruff in the suit led by the opponent.\n";
//				status << "3PLRUF50! We can ruff here.\n";
			}
			else
			{
//				status << "3PLRUF51! A ruff here is not possible.\n";
				m_nStatusCode = PLAY_POSTPONE;
				return m_nStatusCode;
			}

			// at this point, we MUST be in the proper hand to ruff
			if (bPlayingInHand) 
			{
				// playing third/fourth in hand -- see where the ruff is
				if (m_nTargetHand == IN_HAND)
				{
					// ruff here
					CSuitHoldings& playerTrumps = playerHand.GetSuit(nTrumpSuit);
					if (playerTrumps.GetNumCards() > 0)
					{
						// see if RHO trumped
						if (bTrumped && (pPartnersCard != pRoundTopCard))
						{
							// see if we can beat it
							pPlayCard = playerTrumps.GetLowestCardAbove(pRoundTopCard);
							if (pPlayCard)
							{
								status << "PLRUF56! Overruff RHO's trump " & pRoundTopCard->GetFaceName() & 
										  " with the " & pPlayCard->GetFaceName() & ".\n";
							}
							else
							{
								status << "4PLRUF58! RHO ruffed, and we can't overruff.\n";
								m_nStatusCode = PLAY_POSTPONE;
								return m_nStatusCode;
							}
						}
						else if (pPartnersCard == pRoundTopCard)
						{
							status << "4PLRUF59! Partner'" & pPartnersCard->GetName() & " is high, so don't ruff it.\n";
							m_nStatusCode = PLAY_POSTPONE;
							return m_nStatusCode;
						}
						else
						{
							// go ahead and ruff
							pPlayCard = playerTrumps.GetBottomCard();
							status << "PLRUF60! Ruff in hand with the " & pPlayCard->GetName() & ".\n";
						}
					}
					else
					{
						status << "4PLRUF62! Error - no trumps left in hand to use in a ruff.\n";
						m_nStatusCode = PLAY_ERROR;
						return m_nStatusCode;
					}
				}
				else
				{
					// if we're playing 4th, we can discard (ruffed opposite)
					if (nOrdinal == 3)
					{
						if (bPartnerHigh)
						{
							pPlayCard = playEngine.GetDiscard();
							status << "PLRUF63! Complete the ruff by discarding the " & pPlayCard->GetName() & " from hand.\n";
						}
						else
						{
							// oops, opponents overruffed
							status << "3PLRUF63a! Oops, the opponents overruffed, so skip the play.\n";
							m_nStatusCode = PLAY_FAILED;
							return m_nStatusCode;
						}
					}
					else
					{
						// can't use _this_ ruff here
//						status << "4PLRUF64! Oops, we ended up in hand opposite a ruff in dummy.\n";
//						m_nStatusCode = PLAY_ERROR;
						m_nStatusCode = PLAY_POSTPONE;
						return m_nStatusCode;
					}
				}
			}
			else
			{
				// playing second in dummy -- see if we're ruffing here
				if (m_nTargetHand == IN_DUMMY)
				{
					// ruff here
					CSuitHoldings& dummyTrumps = dummyHand.GetSuit(nTrumpSuit);
					if (dummyTrumps.GetNumCards() > 0)
					{
						// see if RHO trumped
						if (bTrumped && (pPartnersCard != pRoundTopCard))
						{
							// see if we can beat it
							pPlayCard = dummyTrumps.GetLowestCardAbove(pRoundTopCard);
							if (pPlayCard)
							{
								status << "PLRUF70! Overruff RHO's trump " & pRoundTopCard->GetFaceName() & 
										  " with the " & pPlayCard->GetFaceName() & ".\n";
							}
							else
							{
								status << "3PLRUF72! RHO ruffed, and we can't overruff.\n";
								m_nStatusCode = PLAY_POSTPONE;
								return m_nStatusCode;
							}
						}
						else if (pPartnersCard == pRoundTopCard)
						{
							status << "4PLRUF73! Partner'" & pPartnersCard->GetName() & " is high, so don't ruff it.\n";
							m_nStatusCode = PLAY_POSTPONE;
							return m_nStatusCode;
						}
						else
						{
							// go ahead and ruff
							pPlayCard = dummyTrumps.GetBottomCard();
							status << "PLRUF74! Ruff in dummy with the " & pPlayCard->GetName() & ".\n";
						}
					}
					else
					{
						status << "4PLRUF76! Error - no trumps left in dummy to use in a ruff.\n";
						m_nStatusCode = PLAY_ERROR;
						return m_nStatusCode;
					}
				}
				else
				{
					// if we're playing 4th, we can discard (ruffed opposite)
					if (nOrdinal == 3)
					{
						if (bPartnerHigh)
						{
							pPlayCard = playEngine.GetDiscard();
							status << "PLRUF77! Complete the ruff by discarding the " & pPlayCard->GetName() & " from hand.\n";
						}
						else
						{
							// oops, opponents overruffed
							status << "3PLRUF77a! Oops, the opponents overruffed, so skip the play.\n";
							m_nStatusCode = PLAY_FAILED;
							return m_nStatusCode;
						}
					}
					else
					{
						// can't ruff here
//						status << "4PLRUF78! Oops, we ended up in dummy opposite a ruff in hand.\n";
//						m_nStatusCode = PLAY_ERROR;
						m_nStatusCode = PLAY_POSTPONE;
						return m_nStatusCode;
					}
				}
			}
			// else all went OK
			m_nStatusCode = PLAY_COMPLETE;
			break;
	}

	// done
	ASSERT(pPlayCard->IsValid());
	return m_nStatusCode;
}