/** * Perform the core function for running the auto complete process. * This coroutine will rapidly move cards from the tableau to the foundation * and automatically complete the game once a valid winning state has been * determined by the system. */ private IEnumerator AutoWinCoroutine() { SetDoingAutoWin(true); int attempts = 0; int maxAttempts = 5000; // Build min priority queue based on all cards in tableau (prioritize lower value cards) PriorityQueue <Card> cardQueue = new PriorityQueue <Card>(true); // Add max attempts in case there is some corner case that occurs and causes a fail while (attempts < maxAttempts) { yield return(new WaitForEndOfFrame()); // Break out of the loop if the win state has been met if (HasWon()) { break; } SnapManager[] tableauSnaps = tableau.GetComponentsInChildren <SnapManager>(); foreach (SnapManager tableauSnap in tableauSnaps) { // Get only the top card for now Card topCard = tableauSnap.GetTopCard(); if (topCard != null) { cardQueue.Enqueue(topCard.value, topCard); } } // Dequeue cards from priority queue and move each one to the foundation while (cardQueue.Count > 0) { Card priorityCard = cardQueue.Dequeue(); Transform nextMove = GetNextAvailableMove(priorityCard); // Only process move if one existed and if it is to a foundation if (nextMove) { SnapManager currentSnapManager = priorityCard.GetComponentInParent <SnapManager>(); SnapManager nextSnapManager = nextMove.GetComponent <SnapManager>(); bool validMove = nextSnapManager.belongingSection.Equals(Sections.FOUNDATIONS); if (validMove) { // Make note of the current amount of attached cards int currentCardCount = nextSnapManager.GetCardCount(); priorityCard.MoveTo(nextMove); // Wait until the card is finished translating, is attached to the snap, and the current // card count has incremented by one yield return(new WaitUntil(() => { return priorityCard.transform.parent != null && !priorityCard.IsTranslating() && nextSnapManager.GetCardCount() >= (currentCardCount + 1); })); // Add the current snap manager's top card to the priority queue Card nextTopCard = currentSnapManager.GetTopCard(); if (nextTopCard != null) { cardQueue.Enqueue(nextTopCard.value, nextTopCard); } } } } attempts++; } SetDoingAutoWin(false); m_autoWinComplete = true; }
/** * Move this card instance and/or other cards in it's card set to the specified target snap transform * @param Transform snap the target snap transform to move this card to * @param Card[] cardSet the set of dragged cards to handle translation all at once * with respect to this card instance. Defaults to null if a card set is not provided. * If the card set has only one card in it then it's assumed that the one card is * this card instance and will be processed as such. * @param MoveTypes moveType the type of move that determines how the move should be tracked in the GameManager. */ public void MoveTo(Transform snap, Card[] cardSet = null, Move.MoveTypes moveType = Move.MoveTypes.NORMAL) { // Prepare the move object m_move = new Move(); m_moveType = moveType; // Set the target card based on value of card set // (if card set is null then create a new card set with this card as the only element) m_move.SetCards(cardSet ?? (new Card[] { this })); // We know that the card has/had a parent m_move.SetPreviousParent(m_startParent); // Need to get what the snap belongs to so that the card is placed in the correct location SnapManager snapManager = snap.GetComponent <SnapManager>(); bool faceDownTarget = snapManager.HasCard() && snapManager.GetTopCard().IsFaceDown(); GameManager.Sections targetSection = snapManager.belongingSection; // Set the next parent in the move m_move.SetNextParent(snapManager.transform); // Need to target the top card in the respective tableau pile and offset the y and z positions Transform tableauHasCardTarget = targetSection.Equals(GameManager.Sections.TABLEAU) && snapManager.HasCard() ? snapManager.GetTopCard().transform : snap; // Setting for reference to new parent snap m_targetTranslateSnap = snap; m_targetTranslatePos = m_targetTranslateSnap.position; // Defaults to target snap position // Keep track of the starting position m_startPos = transform.position; m_draggedCards = cardSet; // Tell the snap manager currently associated with the card(s) to wait until animations are complete SnapManager currentSnap = GetComponentInParent <SnapManager>(); if (currentSnap != null) { // Set the start parent here so that the current snap can be told to stop waiting later SetStartParent(currentSnap.transform); currentSnap.SetWaiting(true); } // Process a bit differently if a card set has been provided if (m_draggedCards != null && m_draggedCards.Length > 1) { // Do a first pass-through to remove parent and bring z-value of each of the dragged cards // to the z-offset dragging value for (int i = 0; i < m_draggedCards.Length; i++) { Card draggedCard = m_draggedCards[i]; draggedCard.transform.parent = null; // Keep track of each card's starting position draggedCard.SetStartPos(draggedCard.transform.position); float yOffset; // Apply y-offset when dragging multiple cards (start without y-offset if there isn't a card on the snap) // Handle case when action was an undo and the top card in the target snap is facedown // (only the first card in the the set of dragged cards should have the face down y-offset applied in this case). if (faceDownTarget && i == 0) { yOffset = GameManager.FACE_DOWN_Y_OFFSET; } else { if (faceDownTarget) { // Need to compensate for the fact that the first card applied face down y-offset yOffset = (GameManager.FOUNDATION_Y_OFFSET * i) + GameManager.FACE_DOWN_Y_OFFSET; } else { // Process normally (e.g., not undoing a flip event) yOffset = GameManager.FOUNDATION_Y_OFFSET * (snapManager.HasCard() ? i + 1 : i); } } Vector3 newTargetPos = new Vector3( tableauHasCardTarget.position.x, tableauHasCardTarget.position.y - yOffset, tableauHasCardTarget.position.z - (i + 1) ); // Set the new translate position for the dragged card draggedCard.SetTargetTranslatePosition(newTargetPos); // Set the z-value of the transform to move to be high enough to hover over all other cards draggedCard.transform.position = new Vector3( draggedCard.transform.position.x, draggedCard.transform.position.y, -(GameManager.Z_OFFSET_DRAGGING + i) ); } } else // Otherwise, process on one card { // transform position is a special case for the Tableau cards due to y-offset in addition to z-offset if (targetSection.Equals(GameManager.Sections.TABLEAU) && snapManager.HasCard()) { Vector3 newTargetPos = new Vector3( tableauHasCardTarget.position.x, tableauHasCardTarget.position.y - (faceDownTarget ? GameManager.FACE_DOWN_Y_OFFSET : GameManager.FOUNDATION_Y_OFFSET), tableauHasCardTarget.position.z - 1 ); m_targetTranslatePos = newTargetPos; } transform.parent = null; // Temporarily detatch from the parent SetStartPos(transform.position); // Keep track of this card instance start position // Set the z-value of the transform to move to be high enough to hover over all other cards transform.position = new Vector3( transform.position.x, transform.position.y, -GameManager.Z_OFFSET_DRAGGING ); } // Add the move to the game manager instance (only if normal move and not undone or redone) if (moveType == Move.MoveTypes.NORMAL) { GameManager.Instance.AddMove(m_move, moveType); } // Track the total number of moves with stats manager StatsManager.Instance.TallyMove(); // Begin the translating animation m_translating = true; }