private bool IsSuddenDeathRestart(TriadDeckInstanceScreen deck) { // sudden death: all red cards visible, not matching npc deck at all int numMismatchedCards = 0; int numVisibleCards = 0; int hiddenCardId = TriadCardDB.Get().hiddenCard.Id; for (int Idx = 0; Idx < deck.cards.Length; Idx++) { int npcCardIdx = deck.deck.GetCardIndex(deck.cards[Idx]); if (npcCardIdx < 0) { numMismatchedCards++; } if (deck.cards[Idx] != null && deck.cards[Idx].Id != hiddenCardId) { numVisibleCards++; } } bool bHasOpenAndMismatched = (numVisibleCards >= 4 && numMismatchedCards > 1); bool bHasOpenAndShouldnt = (numVisibleCards >= 4 && !bHasOpenRule); Logger.WriteLine("IsSuddenDeathRestart? numMismatchedCards:" + numMismatchedCards + ", numVisibleCards:" + numVisibleCards); return(bHasOpenAndMismatched || bHasOpenAndShouldnt); }
public TriadGameScreenMemory() { gameSession = new TriadGameSession(); gameState = new TriadGameData(); deckBlue = new TriadDeckInstanceScreen(); deckRed = new TriadDeckInstanceScreen(); blueDeckHistory = new List <TriadCard[]>(); bHasSwapRule = false; swappedBlueCardIdx = -1; lastScanNpc = null; }
private void UpdateRedDeckDetails(TriadDeckInstanceScreen deck) { string NumPlacedPrefix = "All placed: "; string VarPlacedPrefix = "Var placed: "; if (deck == null || deck.deck == null) { for (int Idx = 0; Idx < redDeckKnownCards.Length; Idx++) { redDeckKnownCards[Idx].Visible = false; redDeckUnknownCards[Idx].Visible = false; deckCtrlRed.SetTransparent(Idx, false); } labelNumPlaced.Text = NumPlacedPrefix + "0"; labelUnknownPlaced.Text = VarPlacedPrefix + "0"; } else { int firstKnownIdx = deck.cards.Length; int firstUnknownIdx = deck.cards.Length + deck.deck.knownCards.Count; for (int Idx = 0; Idx < redDeckKnownCards.Length; Idx++) { bool bIsValidKnownCard = Idx < deck.deck.knownCards.Count; redDeckKnownCards[Idx].Visible = bIsValidKnownCard; if (bIsValidKnownCard) { bool bIsUsed = deck.IsPlaced(firstKnownIdx + Idx); redDeckKnownCards[Idx].bIsTransparent = bIsUsed; TriadCard showCard = (deck.swappedCardIdx == (firstKnownIdx + Idx)) ? deck.swappedCard : deck.deck.knownCards[Idx]; redDeckKnownCards[Idx].SetCard(new TriadCardInstance(showCard, ETriadCardOwner.Red)); } bool bIsValidUnknownCard = Idx < deck.deck.unknownCardPool.Count; redDeckUnknownCards[Idx].Visible = bIsValidUnknownCard; if (bIsValidUnknownCard) { bool bIsUsed = deck.IsPlaced(firstUnknownIdx + Idx); redDeckUnknownCards[Idx].bIsTransparent = bIsUsed; TriadCard showCard = (deck.swappedCardIdx == (firstUnknownIdx + Idx)) ? deck.swappedCard : deck.deck.unknownCardPool[Idx]; redDeckUnknownCards[Idx].SetCard(new TriadCardInstance(showCard, ETriadCardOwner.Red)); } } for (int Idx = 0; Idx < deck.cards.Length; Idx++) { bool bIsUsed = deck.IsPlaced(Idx); deckCtrlRed.SetTransparent(Idx, bIsUsed); } labelNumPlaced.Text = NumPlacedPrefix + deck.numPlaced; labelUnknownPlaced.Text = VarPlacedPrefix + deck.numUnknownPlaced; } }
private bool IsDeckMatching(TriadDeckInstanceScreen deckInstance, TriadCard[] cards) { bool bIsMatching = false; if ((deckInstance != null) && (cards != null) && (deckInstance.cards.Length >= cards.Length)) { bIsMatching = true; for (int Idx = 0; Idx < cards.Length; Idx++) { bIsMatching = bIsMatching && (cards[Idx] == deckInstance.cards[Idx]); } } return(bIsMatching); }
public TriadDeckInstanceScreen(TriadDeckInstanceScreen copyFrom) { cards = new TriadCard[copyFrom.cards.Length]; for (int Idx = 0; Idx < copyFrom.cards.Length; Idx++) { cards[Idx] = copyFrom.cards[Idx]; } deck = copyFrom.deck; numUnknownPlaced = copyFrom.numUnknownPlaced; numPlaced = copyFrom.numPlaced; availableCardMask = copyFrom.availableCardMask; unknownPoolMask = copyFrom.unknownPoolMask; swappedCardIdx = copyFrom.swappedCardIdx; swappedCard = copyFrom.swappedCard; }
private bool FindSwappedCardVisible(TriadCard[] screenCards, TriadCardInstance[] board, TriadDeckInstanceScreen otherDeck, out int swappedCardIdx, out int swappedOtherIdx, out TriadCard swappedCard) { swappedCardIdx = -1; swappedOtherIdx = -1; swappedCard = null; int numDiffs = 0; int numOnHand = 0; int hiddenCardId = TriadCardDB.Get().hiddenCard.Id; for (int Idx = 0; Idx < otherDeck.cards.Length; Idx++) { if (otherDeck.cards[Idx] != null && otherDeck.cards[Idx].Id != hiddenCardId) { // find in source deck, not in instance int cardIdx = otherDeck.deck.GetCardIndex(otherDeck.cards[Idx]); if (cardIdx < 0) { swappedOtherIdx = Idx; swappedCard = otherDeck.cards[Idx]; for (int ScreenIdx = 0; ScreenIdx < screenCards.Length; ScreenIdx++) { cardIdx = otherDeck.deck.GetCardIndex(screenCards[ScreenIdx]); if (cardIdx >= 0) { swappedCardIdx = ScreenIdx; numDiffs++; } } } } numOnHand += (otherDeck.cards[Idx] != null) ? 1 : 0; } bool bBoardMode = false; if (numOnHand < screenCards.Length) { for (int Idx = 0; Idx < board.Length; Idx++) { if (board[Idx] != null && board[Idx].owner == ETriadCardOwner.Red) { // find in source deck, not in instance int cardIdx = otherDeck.deck.GetCardIndex(board[Idx].card); if (cardIdx < 0) { swappedCard = board[Idx].card; swappedOtherIdx = 100; // something way outside, it's not going to be used directly as card was already placed for (int ScreenIdx = 0; ScreenIdx < screenCards.Length; ScreenIdx++) { cardIdx = otherDeck.deck.GetCardIndex(screenCards[ScreenIdx]); if (cardIdx >= 0) { swappedCardIdx = ScreenIdx; bBoardMode = true; numDiffs++; } } } } } } bool bHasSwapped = (numDiffs == 1); Logger.WriteLine("FindSwappedCardVisible: blue[" + swappedCardIdx + "]:" + (swappedCardIdx >= 0 ? screenCards[swappedCardIdx].Name : "??") + " <=> red[" + swappedOtherIdx + "]:" + (swappedCard != null ? swappedCard.Name : "??") + ", boardMode:" + bBoardMode + ", diffs:" + numDiffs + " => " + (bHasSwapped ? "SWAP" : "ignore")); return(bHasSwapped); }
private bool FindSwappedCard(TriadCard[] screenCards, TriadCard[] expectedCards, TriadDeckInstanceScreen otherDeck, out int swappedCardIdx, out int swappedOtherIdx, out TriadCard swappedCard) { swappedCardIdx = -1; swappedOtherIdx = -1; swappedCard = null; TriadCard swappedBlueCard = null; int numDiffs = 0; int numPotentialSwaps = 0; for (int Idx = 0; Idx < screenCards.Length; Idx++) { if ((screenCards[Idx] != expectedCards[Idx]) && (screenCards[Idx] != null)) { numDiffs++; swappedCardIdx = Idx; swappedOtherIdx = otherDeck.GetCardIndex(screenCards[Idx]); swappedBlueCard = screenCards[Idx]; swappedCard = expectedCards[Idx]; Logger.WriteLine("FindSwappedCard[" + Idx + "]: screen:" + screenCards[Idx].Name + ", expected:" + expectedCards[Idx].Name + ", redIdxScreen:" + swappedOtherIdx); if (swappedOtherIdx >= 0) { numPotentialSwaps++; } } } bool bHasSwapped = (numDiffs == 1) && (numPotentialSwaps == 1); Logger.WriteLine("FindSwappedCard: blue[" + swappedCardIdx + "]:" + (swappedBlueCard != null ? swappedBlueCard.Name : "??") + " <=> red[" + swappedOtherIdx + "]:" + (swappedCard != null ? swappedCard.Name : "??") + ", diffs:" + numDiffs + ", potentialSwaps:" + numPotentialSwaps + " => " + (bHasSwapped ? "SWAP" : "ignore")); return(bHasSwapped); }
private void UpdateAvailableRedCards(TriadDeckInstanceScreen redDeck, TriadCard[] screenCardsRed, TriadCard[] screenCardsBlue, TriadCard[] screenBoard, TriadCard[] prevCardsBlue, TriadCardInstance[] prevBoard, bool bContinuePrevState) { bool bDebugMode = false; int hiddenCardId = TriadCardDB.Get().hiddenCard.Id; int numVisibleCards = deckRed.cards.Length; redDeck.numPlaced = 0; if (!bContinuePrevState) { redDeck.numUnknownPlaced = 0; } int maxUnknownToUse = redDeck.cards.Length - redDeck.deck.knownCards.Count; int firstUnknownPoolIdx = redDeck.cards.Length + redDeck.deck.knownCards.Count; if (redDeck.deck.unknownCardPool.Count > 0) { redDeck.unknownPoolMask = ((1 << redDeck.deck.unknownCardPool.Count) - 1) << firstUnknownPoolIdx; for (int Idx = 0; Idx < screenCardsRed.Length; Idx++) { if ((screenCardsRed[Idx] != null) && (screenCardsRed[Idx].Id != hiddenCardId) && redDeck.deck.unknownCardPool.Contains(screenCardsRed[Idx])) { redDeck.unknownPoolMask |= (1 << Idx); } } } int allDeckAvailableMask = ((1 << (redDeck.deck.knownCards.Count + redDeck.deck.unknownCardPool.Count)) - 1) << numVisibleCards; bool bCanCompareWithPrevData = (screenCardsRed.Length == redDeck.cards.Length) && (screenCardsBlue.Length == prevCardsBlue.Length) && (screenBoard.Length == prevBoard.Length); if (bCanCompareWithPrevData && !bContinuePrevState) { // special case: 1st turn int numCardsOnBoard = 0; for (int Idx = 0; Idx < screenBoard.Length; Idx++) { if (screenBoard[Idx] != null) { numCardsOnBoard++; } } if (numCardsOnBoard <= 1) { bCanCompareWithPrevData = true; prevBoard = new TriadCardInstance[screenBoard.Length]; prevCardsBlue = new TriadCard[numVisibleCards]; deckRed.cards = new TriadCard[numVisibleCards]; deckRed.availableCardMask = allDeckAvailableMask; deckRed.numPlaced = 0; deckRed.numUnknownPlaced = 0; } else { bCanCompareWithPrevData = false; } } if (bDebugMode) { Logger.WriteLine("Red deck update, diff mode check... " + "bContinuePrevState:" + bContinuePrevState + ", cards(screen:" + screenCardsRed.Length + ", prev:" + deckRed.cards.Length + ")=" + ((screenCardsRed.Length == deckRed.cards.Length) ? "ok" : "nope") + ", other(screen:" + screenCardsBlue.Length + ", prev:" + prevCardsBlue.Length + ")=" + ((screenCardsBlue.Length == prevCardsBlue.Length) ? "ok" : "nope") + ", board(screen:" + screenBoard.Length + ", prev:" + prevBoard.Length + ")=" + ((screenBoard.Length == prevBoard.Length) ? "ok" : "nope")); } if (bCanCompareWithPrevData) { // create diffs, hopefully prev state comes from last turn and is just 2 cards away List <int> usedCardsIndices = new List <int>(); List <TriadCard> usedCardsOther = new List <TriadCard>(); int numKnownOnHand = 0; int numUnknownOnHand = 0; int numHidden = 0; int numOnHand = 0; for (int Idx = 0; Idx < deckRed.cards.Length; Idx++) { if (screenCardsRed[Idx] == null) { TriadCard prevCard = deckRed.cards[Idx]; if ((prevCard != null) && (prevCard.Id != hiddenCardId)) { if (bDebugMode) { Logger.WriteLine(" card[" + Idx + "]:" + prevCard.Name + " => mark as used, disappeared from prev state"); } usedCardsIndices.Add(Idx); } deckRed.availableCardMask &= ~(1 << Idx); deckRed.numPlaced++; } else { if (screenCardsRed[Idx].Id != hiddenCardId) { bool bIsUnknown = (deckRed.unknownPoolMask & (1 << Idx)) != 0; numUnknownOnHand += bIsUnknown ? 1 : 0; numKnownOnHand += bIsUnknown ? 0 : 1; numOnHand++; deckRed.availableCardMask |= (1 << Idx); int knownCardIdx = deckRed.deck.knownCards.IndexOf(screenCardsRed[Idx]); int unknownCardIdx = deckRed.deck.unknownCardPool.IndexOf(screenCardsRed[Idx]); if (knownCardIdx >= 0) { deckRed.availableCardMask &= ~(1 << (knownCardIdx + deckRed.cards.Length)); } else if (unknownCardIdx >= 0) { deckRed.availableCardMask &= ~(1 << (unknownCardIdx + deckRed.cards.Length + deckRed.deck.knownCards.Count)); } if (bDebugMode) { TriadCard cardOb = screenCardsRed[Idx]; Logger.WriteLine(" card[" + Idx + "]:" + (cardOb != null ? cardOb.Name : "??") + " => numUnknown:" + numUnknownOnHand + ", numKnown:" + numKnownOnHand + ", numHidden:" + numHidden); } } else { numHidden++; } } } for (int Idx = 0; Idx < prevCardsBlue.Length; Idx++) { if ((prevCardsBlue[Idx] != null) && (screenCardsBlue[Idx] == null)) { usedCardsOther.Add(prevCardsBlue[Idx]); if (bDebugMode) { Logger.WriteLine(" blue[" + Idx + "]:" + prevCardsBlue[Idx].Name + " => mark as used"); } } } for (int Idx = 0; Idx < prevBoard.Length; Idx++) { TriadCard testCard = screenBoard[Idx]; if ((prevBoard[Idx] == null || prevBoard[Idx].card == null) && (testCard != null)) { int testCardIdx = deckRed.GetCardIndex(testCard); if (!usedCardsOther.Contains(testCard) && (testCardIdx >= 0)) { usedCardsIndices.Add(testCardIdx); if (bDebugMode) { Logger.WriteLine(" card[" + testCardIdx + "]:" + testCard.Name + " => mark as used, appeared on board[" + Idx + "], not used by blue"); } } } } Array.Copy(screenCardsRed, deckRed.cards, 5); for (int Idx = 0; Idx < usedCardsIndices.Count; Idx++) { int cardMask = 1 << usedCardsIndices[Idx]; deckRed.availableCardMask &= ~cardMask; bool bIsUnknownPool = (deckRed.unknownPoolMask & cardMask) != 0; if (bIsUnknownPool) { deckRed.numUnknownPlaced++; } if (bDebugMode) { TriadCard cardOb = deckRed.GetCard(usedCardsIndices[Idx]); Logger.WriteLine(" card[" + usedCardsIndices[Idx] + "]:" + (cardOb != null ? cardOb.Name : "??") + " => used"); } } if ((numHidden == 0) && ((numOnHand + deckRed.numPlaced) == numVisibleCards)) { deckRed.availableCardMask &= (1 << numVisibleCards) - 1; if (bDebugMode) { Logger.WriteLine(" all cards are on hand and visible"); } } else if ((deckRed.numUnknownPlaced + numUnknownOnHand) >= maxUnknownToUse || ((numKnownOnHand >= (numVisibleCards - maxUnknownToUse)) && (numHidden == 0))) { deckRed.availableCardMask &= (1 << (numVisibleCards + deckRed.deck.knownCards.Count)) - 1; if (bDebugMode) { Logger.WriteLine(" removing all unknown cards, numUnknownPlaced:" + deckRed.numUnknownPlaced + ", numUnknownOnHand:" + numUnknownOnHand + ", numKnownOnHand:" + numKnownOnHand + ", numHidden:" + numHidden + ", maxUnknownToUse:" + maxUnknownToUse); } } } else { // TriadDeckInstanceScreen is mostly stateless (created from scratch on screen capture) // this makes guessing which cards were placed hard, especially when there's no good // history data to compare with. // Ignore board data here, cards could be placed by blue and are still available for red deck deckRed.UpdateAvailableCards(screenCardsRed); deckRed.availableCardMask = allDeckAvailableMask; } if (bDebugMode) { redDeck.LogAvailableCards("Red deck"); } }
public override TriadDeckInstance CreateCopy() { TriadDeckInstanceScreen deckCopy = new TriadDeckInstanceScreen(this); return(deckCopy); }