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