/// <summary> /// /// </summary> /// <param name="pt"></param> /// <returns></returns> public override CardCollection GetCardsToDrag( Point pt ) { // Loop backwards through all cards in this collection // and when we find a card that fits the bill, we return // that card and all cards on top of that card. // Also note that we don't allow dragging cards which are // face down. int i = base.Count - 1; for ( ; i >= 0; i-- ) { Card c = base.m_cards[ i ]; if ( c.Contains( pt ) && c.FaceUp ) { break; } } if ( i != -1 ) { CardCollection cards = new CardCollection( ); cards.AddRange( base.m_cards.GetRange( i, base.Count - i ) ); return cards; } return null; }
public void Received(IEnumerable <Card> cards) { if (!this.IsTurnFinished) { _CardsReceived.AddRange(cards); } else { _CardsReceivedAfter.AddRange(cards); } }
public PileChangedEventArgs(Player player, Operation operation, IEnumerable <Card> cardsChanged) : this(player, operation) { switch (operation) { case PileChangedEventArgs.Operation.Added: _AddedCards.AddRange(cardsChanged); break; case PileChangedEventArgs.Operation.Removed: _RemovedCards.AddRange(cardsChanged); break; } }
void Produce(TextWriter report, Board board, Seat seat, Suit suit) { report.WriteLine("<td class='suit' colspan='2'>"); switch (suit) { case Suit.Clubs: report.WriteLine("<span class='bsuit'>♣</span> "); break; case Suit.Spades: report.WriteLine("<span class='bsuit'>♠</span> "); break; case Suit.Diamonds: report.WriteLine("<span class='rsuit'>♦</span> "); break; case Suit.Hearts: report.WriteLine("<span class='rsuit'>♥</span> "); break; } var honours = new StringBuilder(); var rags = new StringBuilder(); var cards = new CardCollection(); cards.AddRange(board.Hands[seat].Cards); cards.Sort((a, b) => (int)b.Rank - (int)a.Rank); foreach (Card card in cards) { if (card.Suit == suit) { if (card.IsBiddingHonour) { honours.Append(card.ToString("P")[1]); } else { rags.Append(card.ToString("P")[1]); } } } if (honours.Length > 0) { report.Write(honours.ToString()); } if (rags.Length > 0) { report.Write("<span class='rags'>{0}</span>", rags.ToString()); } report.WriteLine("</td>"); }
protected override ChoiceResult Decide_Forge(Choice choice) { switch (choice.ChoiceType) { case ChoiceType.Cards: // Only trash Curses & Ruins CardCollection forgeToTrash = new CardCollection(); // Always choose to trash all Curses forgeToTrash.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Curse)); // Always choose to trash all Ruins forgeToTrash.AddRange(choice.Cards.Where(c => (c.Category & Category.Ruins) == Category.Ruins)); return new ChoiceResult(forgeToTrash); case ChoiceType.Supplies: return new ChoiceResult(FindBestCardForCost(choice.Supplies.Values, null, false)); default: return base.Decide_Forge(choice); } }
protected override ChoiceResult Decide_DameAnna(Choice choice) { if (choice.Text == "Choose up to 2 cards to trash") { CardCollection dameAnnaToTrash = new CardCollection(); // Always choose to trash all Curses dameAnnaToTrash.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Curse).Take(2)); // Always choose to trash all Ruins dameAnnaToTrash.AddRange(choice.Cards.Where(c => (c.Category & Category.Ruins) == Category.Ruins).Take(2 - dameAnnaToTrash.Count)); return new ChoiceResult(dameAnnaToTrash); } else if (choice.Text == "Choose a card to trash") return new ChoiceResult(new CardCollection(FindBestCardsToTrash(choice.Cards, 1))); else return base.Decide_Rogue(choice); }
protected override ChoiceResult Decide_Chapel(Choice choice) { CardCollection chapelToTrash = new CardCollection(); // Always choose to trash all Curses chapelToTrash.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Curse).Take(4)); // Always choose to trash all Ruins chapelToTrash.AddRange(choice.Cards.Where(c => (c.Category & Category.Ruins) == Category.Ruins).Take(4 - chapelToTrash.Count)); return new ChoiceResult(chapelToTrash); }
protected override ChoiceResult Decide_Cellar(Choice choice) { CardCollection cellarCards = new CardCollection(); // TODO -- the AI makes slightly bad decisions when it comes to Cellar -- Fix me! cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Base.TypeClass.Cellar)); cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Curse)); cellarCards.AddRange(choice.Cards.Where(c => (c.Category & Category.Ruins) == Category.Ruins)); cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Hinterlands.TypeClass.Tunnel)); cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.DarkAges.TypeClass.Hovel)); cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.DarkAges.TypeClass.OvergrownEstate)); cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.DarkAges.TypeClass.Rats)); cellarCards.AddRange(choice.Cards.Where(c => (c.Category == Category.Victory && c.CardType != Cards.Universal.TypeClass.Estate && c.CardType != Cards.Universal.TypeClass.Province))); if (choice.Cards.Count(c => c.CardType == Cards.Universal.TypeClass.Estate) > 0) cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Estate).Take(choice.Cards.Count(c => c.CardType == Cards.Universal.TypeClass.Estate) - choice.Cards.Count(c => c.CardType == Cards.Intrigue.TypeClass.Baron))); if (choice.Cards.Count(c => c.CardType == Cards.Universal.TypeClass.Province) > 0) cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Province).Take(choice.Cards.Count(c => c.CardType == Cards.Universal.TypeClass.Province) - choice.Cards.Count(c => c.CardType == Cards.Cornucopia.TypeClass.Tournament))); if (this.GameProgress < 0.63) cellarCards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Universal.TypeClass.Copper || c.CardType == Cards.Guilds.TypeClass.Masterpiece)); return new ChoiceResult(cellarCards); }
protected override IEnumerable<Card> FindBestCards(IEnumerable<Card> cards, int count) { // Choose the most expensive cards CardCollection cardsToReturn = new CardCollection(); cardsToReturn.AddRange(cards.OrderByDescending(c => c.BaseCost).ThenBy(c => c.Name).Take(count)); return cardsToReturn; }
protected override IEnumerable<Card> FindBestCardsToTrash(IEnumerable<Card> cards, int count) { // choose the worse card in hand in this order // 1) curse // 2) cheapest card left CardCollection cardsToTrash = new CardCollection(); cardsToTrash.AddRange(cards.Where(c => c.Category == Category.Curse).Take(count)); if (cardsToTrash.Count >= count) return cardsToTrash; cardsToTrash.AddRange(cards.OrderBy(c => c.BaseCost).ThenBy(c => c.Name).Where(c => !cardsToTrash.Contains(c)).Take(count - cardsToTrash.Count)); return cardsToTrash; }
private void PerformCleanup() { // Sets up card movements to indicate where each card should go. CardMovementCollection cardsToMove = new CardMovementCollection(this.SetAside, c => c.CanCleanUp, DeckLocation.SetAside, DeckLocation.Discard); cardsToMove.AddRange(this.Hand, DeckLocation.Hand, DeckLocation.Discard); cardsToMove.AddRange(this.InPlay, DeckLocation.InPlay, DeckLocation.Discard); foreach (Card durationCard in this.InPlay.Where(c => !c.CanCleanUp)) cardsToMove[durationCard].Destination = DeckLocation.SetAside; IEnumerable<CardMovement> inPlayCards = cardsToMove.Where(cm => cm.CurrentLocation == DeckLocation.InPlay); ParallelQuery<CardMovement> pqCardsToMove = cardsToMove.AsParallel().Where(cm => cm.CurrentLocation == DeckLocation.InPlay && cm.Destination == DeckLocation.SetAside && cm.Card.ModifiedBy != null && cardsToMove.Contains(cm.Card.ModifiedBy.PhysicalCard) && cardsToMove[cm.Card.ModifiedBy.PhysicalCard].Destination == DeckLocation.Discard); pqCardsToMove.ForAll(cm => cardsToMove[cm.Card.ModifiedBy.PhysicalCard].Destination = DeckLocation.SetAside); int drawSize = 5; if (CleaningUp != null) { Boolean cancelled = false; // Possibly changing events that can happen in the game CleaningUpEventArgs cuea = null; do { cuea = new CleaningUpEventArgs(this, 5, ref cardsToMove); cuea.Cancelled |= cancelled; CleaningUp(this, cuea); OptionCollection options = new OptionCollection(); IEnumerable<Type> cardTypes = cuea.Actions.Keys; foreach (Type key in cardTypes) options.Add(new Option(cuea.Actions[key].Text, false)); if (options.Count > 0) { options.Sort(); Choice choice = new Choice("Performing Clean-up", options, this, cuea); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) cuea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cuea); else break; } else break; cancelled |= cuea.Cancelled; } while (CleaningUp != null); if (cuea != null) cancelled |= cuea.Cancelled; if (cancelled) return; if (cuea.NextPlayer != null) _CurrentTurn.NextPlayer = cuea.NextPlayer; _CurrentTurn.NextGrantedBy = cuea.NextGrantedBy; drawSize = cuea.DrawSize; } // Discard any Revealed cards (should be none?) this.DiscardRevealed(); CardsDiscardAction cdaHand = null; if (cardsToMove.Count(c => c.CurrentLocation == DeckLocation.Hand) > 0) cdaHand = new CardsDiscardAction(this, null, "Discard hand", player_DiscardHand, true) { Data = cardsToMove }; // Discard non-Duration (or Duration-modifying) cards in In Play & Set Aside at the same time this.Discard(DeckLocation.InPlayAndSetAside, cardsToMove.Where(cm => (cm.CurrentLocation == DeckLocation.InPlay || cm.CurrentLocation == DeckLocation.SetAside) && cm.Destination == DeckLocation.Discard).Select<CardMovement, Card>(cm => cm.Card), cdaHand); // Discard Hand this.AddCardsInto(DeckLocation.Discard, this.RetrieveCardsFrom(DeckLocation.Hand, c => cardsToMove[c].CurrentLocation == DeckLocation.Hand && cardsToMove[c].Destination == DeckLocation.Discard)); // Move Duration (and Duration-modifying) cards from In Play into Set Aside this.MoveInPlayToSetAside(c => cardsToMove.Contains(c) && cardsToMove[c].CurrentLocation == DeckLocation.InPlay && cardsToMove[c].Destination == DeckLocation.SetAside); // Move any cards that have had their Destination changed to their appropriate locations IEnumerable<Card> replaceCards = cardsToMove.Where(cm => cm.Destination == DeckLocation.Deck).Select(cm => cm.Card); if (replaceCards.Count() > 0) { Choice replaceChoice = new Choice("Choose order of cards to put back on your deck", null, replaceCards, this, true, replaceCards.Count(), replaceCards.Count()); ChoiceResult replaceResult = this.MakeChoice(replaceChoice); this.RetrieveCardsFrom(DeckLocation.InPlay, c => cardsToMove[c].CurrentLocation == DeckLocation.InPlay && replaceResult.Cards.Contains(c)); this.RetrieveCardsFrom(DeckLocation.SetAside, c => cardsToMove[c].CurrentLocation == DeckLocation.SetAside && replaceResult.Cards.Contains(c)); this.AddCardsToDeck(replaceResult.Cards, DeckPosition.Top); } #if DEBUG if (this.InPlay.Count > 0) throw new Exception("Something happened -- there are cards left in the player's In Play area!"); #endif if (CurrentTurn != null) CurrentTurn.Finished(); _Actions = _Buys = 0; _Currency.Coin.Value = 0; _Currency.Potion.Value = 0; _ActionsPlayed = 0; #if DEBUG // Check to see that there are no duplicate cards anywhere CardCollection allCards = new CardCollection(); allCards.AddRange(this.Hand); allCards.AddRange(this.Revealed); allCards.AddRange(this.Private); allCards.AddRange(this.InPlay); allCards.AddRange(this.SetAside); allCards.AddRange(this.DrawPile.LookThrough(c => true)); allCards.AddRange(this.DiscardPile.LookThrough(c => true)); foreach (CardMat mat in this.PlayerMats.Values) allCards.AddRange(mat); ParallelQuery<Card> duplicateCards = allCards.AsParallel().Where(c => allCards.Count(ct => ct == c) > 1); //IEnumerable<Card> duplicateCards = allCards.FindAll(c => allCards.Count(ct => ct == c) > 1); if (duplicateCards.Count() > 0) { // Ruh Roh throw new Exception("Duplicate cards found! Something went wrong!"); } #endif DrawHand(drawSize); if (CleanedUp != null) { CleanedUp(this, new CleanedUpEventArgs(this, drawSize)); } if (TurnEnded != null) { TurnEnded(this, new TurnEndedEventArgs(this)); } }
protected override ChoiceResult Decide_Stash(Choice choice) { // For now, always put Stash on top of the draw pile // This is very bad for instances involving Thief or Saboteur, but for now, it'll function CardCollection cards = new CardCollection(); cards.AddRange(choice.Cards.Where(c => c.CardType == Cards.Promotional.TypeClass.Stash)); cards.AddRange(choice.Cards.Where(c => c.CardType != Cards.Promotional.TypeClass.Stash)); return new ChoiceResult(cards); }
protected override ChoiceResult Decide_Herald(Choice choice) { switch (choice.ChoiceType) { case ChoiceType.Options: for (int index = choice.Options.Count - 1; index >= 0; index--) { // Overpay by up to the amount of cards we have in our discard pile, // excluding Coppers, Curses, Ruins, Victory cards, & Shelters Currency overpayAmount = new DominionBase.Currency(choice.Options[index].Text); if (overpayAmount.Potion.Value > 0) continue; if (this.DiscardPile.LookThrough(c => c.CardType != Cards.Universal.TypeClass.Copper && c.CardType != Cards.Universal.TypeClass.Curse && (c.Category & Category.Ruins) != Category.Ruins && (c.Category & Category.Shelter) != Category.Shelter && c.Category != Category.Victory && c.CardType != Cards.Hinterlands.TypeClass.Tunnel).Count >= overpayAmount.Coin.Value) return new ChoiceResult(new List<String>() { choice.Options[index].Text }); } return base.Decide_Herald(choice); case ChoiceType.Cards: // Find highest in-deck value cards to put on top // While ignoring Victory cards, Curses, & Shelters CardCollection heraldCards = new CardCollection(choice.Cards .Where(c => c.Category != Category.Victory && c.CardType != Cards.Universal.TypeClass.Curse && c.CardType != Cards.Hinterlands.TypeClass.Tunnel && (c.Category & Category.Shelter) != Category.Shelter) .OrderByDescending(c => ComputeValueInDeck(c)) .Take(choice.Minimum)); // If this doesn't lead to enough cards, then include Shelters if (heraldCards.Count < choice.Minimum) heraldCards.AddRange(choice.Cards .Where(c => (c.Category & Category.Shelter) == Category.Shelter) .Take(choice.Minimum - heraldCards.Count)); // If this *still* doesn't include enough cards, include Victory cards if (heraldCards.Count < choice.Minimum) heraldCards.AddRange(choice.Cards .Where(c => c.Category == Category.Victory || c.CardType == Cards.Hinterlands.TypeClass.Tunnel) .OrderByDescending(c => ComputeValueInDeck(c)) .Take(choice.Minimum - heraldCards.Count)); // Uh... our deck is absolutely shit. Just take the first however many cards if (heraldCards.Count < choice.Minimum) heraldCards = new CardCollection(choice.Cards .OrderByDescending(c => ComputeValueInDeck(c)) .Take(choice.Minimum)); return new ChoiceResult(heraldCards); default: return base.Decide_Herald(choice); } }
/// <summary> /// Removes the first count cards and returns them. /// </summary> /// <param name="count"></param> /// <returns></returns> public CardCollection RemoveCards( int count ) { CardCollection cards = new CardCollection( ); cards.AddRange( m_cards.GetRange( 0, count ) ); m_cards.RemoveRange( 0, count ); return cards; }
public override CardCollection CardStack() { CardCollection cc = new CardCollection() { this }; if (this.ClonedCard != null) cc.AddRange(this.ClonedCard.CardStack()); return cc; }
internal void player_GainMandarin(Player player, ref Players.CardGainEventArgs e) { CardCollection cardsInPlay = new CardCollection(player.InPlay[Cards.Category.Treasure]); cardsInPlay.AddRange(player.SetAside[Cards.Category.Treasure]); Choice replaceChoice = new Choice("Choose order of Treasure cards to put back on your deck", this, cardsInPlay, player, true, cardsInPlay.Count, cardsInPlay.Count); ChoiceResult replaceResult = player.MakeChoice(replaceChoice); player.RetrieveCardsFrom(DeckLocation.InPlay, c => replaceResult.Cards.Contains(c)); player.RetrieveCardsFrom(DeckLocation.SetAside, c => replaceResult.Cards.Contains(c)); player.AddCardsToDeck(replaceResult.Cards, DeckPosition.Top); e.HandledBy.Add(TypeClass.Mandarin); // Clear out the Event Triggers -- this only happens when its Gained, so we don't care any more foreach (Player playerLoop in _CardGainedHandlers.Keys) playerLoop.CardGained -= _CardGainedHandlers[playerLoop]; _CardGainedHandlers.Clear(); }
private CardCollection GetSelectableCards(CleaningUpEventArgs e) { CardCollection allInPlayCards = new CardCollection(); allInPlayCards.AddRange(e.CardsMovements.Where(cm => ((cm.Card.Category & Cards.Category.Action) == Cards.Category.Action) && cm.CurrentLocation == DeckLocation.InPlay && (cm.Destination == DeckLocation.SetAside || cm.Destination == DeckLocation.Discard)).Select(cm => cm.Card)); // This is used to separate the In Play from the Set Aside for the Choice.MakeChoice call allInPlayCards.Add(new Universal.Dummy()); allInPlayCards.AddRange(e.CardsMovements.Where(cm => ((cm.Card.Category & Cards.Category.Action) == Cards.Category.Action) && cm.CurrentLocation == DeckLocation.SetAside && cm.Destination == DeckLocation.Discard).Select(cm => cm.Card)); if (allInPlayCards.FirstOrDefault() is Universal.Dummy) allInPlayCards.RemoveAt(0); if (allInPlayCards.LastOrDefault() is Universal.Dummy) allInPlayCards.RemoveAt(allInPlayCards.Count - 1); return allInPlayCards; }
public void Bought(IEnumerable <Card> cards) { _CardsBought.AddRange(cards); }
protected override ChoiceResult Decide_SecretChamber(Choice choice) { if (choice.Text == "Choose order of cards to put back on your deck") { // Order all the cards IEnumerable<Card> cardsToReturn = this.FindBestCardsToDiscard(choice.Cards, choice.Cards.Count()); // Try to save 1 Curse if we can if (choice.CardTriggers[0].CardType == Cards.Prosperity.TypeClass.Mountebank) { cardsToReturn = cardsToReturn.Take(3); if (cardsToReturn.ElementAt(0).CardType == Cards.Universal.TypeClass.Curse) return new ChoiceResult(new CardCollection(cardsToReturn.Skip(1))); if (cardsToReturn.ElementAt(1).CardType == Cards.Universal.TypeClass.Curse) return new ChoiceResult(new CardCollection() { cardsToReturn.ElementAt(0), cardsToReturn.ElementAt(2)}); } // Try to not put Treasure cards onto our deck, even if that means putting Action cards there else if (choice.CardTriggers[0].CardType == Cards.Seaside.TypeClass.PirateShip) { CardCollection pirateShipCards = new CardCollection(cardsToReturn.Where(c => (c.Category & Category.Treasure) != Category.Treasure)); if (pirateShipCards.Count < 2) pirateShipCards.AddRange(cardsToReturn.Where(c => (c.Category & Category.Treasure) != Category.Treasure).Take(2 - pirateShipCards.Count)); return new ChoiceResult(pirateShipCards); } return new ChoiceResult(new CardCollection(cardsToReturn.Take(2))); } else { CardCollection scCards = new CardCollection(); foreach (Card card in choice.Cards) { if (card.Category == Category.Curse || ((card.Category & Category.Victory) == Category.Victory && (card.Category & Category.Treasure) != Category.Treasure) || (card.CardType == Cards.Universal.TypeClass.Copper && this.RealThis.InPlay[Cards.Intrigue.TypeClass.Coppersmith].Count == 0) || (this.RealThis.Actions == 0 && (card.Category & Category.Treasure) != Category.Treasure)) scCards.Add(card); } return new ChoiceResult(scCards); } }
public bool CheckHighestPairConfiguration(out Hand hand) { checkCardList(); // make a copy //List<Card> cardList = new List<Card>(_cardList); CardCollection cardList = new CardCollection(_cardList); cardList.Sort(); // this is a list of cards that match the key value Dictionary<int, CardCollection> matches = new Dictionary<int, CardCollection>(); int numPairs = 0; int numThrees = 0; int numFours = 0; // Construct the dictionary foreach (Card card in cardList) { if (matches.ContainsKey(card.Value)) matches[card.Value].Add(card); else matches.Add(card.Value, new CardCollection(new Card[] { card })); //matches.Add(card.Value, new List<Card>(new Card[] { card })); } // Determine the number of pairs, threes, and fours //foreach (List<Card> cardPairs in matches.Values) foreach (CardCollection cardPairs in matches.Values) { if (cardPairs.Count == 2) numPairs++; if (cardPairs.Count == 3) numThrees++; if (cardPairs.Count == 4) numFours++; } //List<Card> resultCards = new List<Card>(); CardCollection resultCards = new CardCollection(); // Okay, here comes the real logic // One Pair and One Pair Only if (numPairs == 1 && numThrees == 0 && numFours == 0) { // Continue with the assumption that we'll only find one pair foreach (int cardValue in matches.Keys) { if (matches[cardValue].Count == 2) // this is our pair { resultCards.AddRange(matches[cardValue]); break; } } // Also add the next highest card for (int highCard = cardList.Count - 1; highCard >= 0; highCard--) { if (!resultCards.Contains(cardList[highCard])) { // Add the next highest card from the sorted list and break resultCards.Add(cardList[highCard]); break; } } // Construct the Best Hand for one pair and return true //hand = new BestHand(resultCards, HandType.OnePair); hand = new Hand(resultCards, Combination.OnePair); return true; } // Two Pair and two pair only else if (numPairs >= 2 && numThrees == 0 && numFours == 0) { int numPairsFound = 0; // Continue with the assumption that we'll only find two pairs foreach (int cardValue in new Stack<int>(matches.Keys)) { if (matches[cardValue].Count == 2) // this is our pair { resultCards.AddRange(matches[cardValue]); numPairs++; if (numPairsFound >= 2) break; } } // Also add the next highest card for (int highCard = cardList.Count - 1; highCard >= 0; highCard--) { if (!resultCards.Contains(cardList[highCard])) { // Add the next highest card from the sorted list and break resultCards.Add(cardList[highCard]); break; } } // Construct the Best Hand for one pair and return true //hand = new BestHand(resultCards, HandType.TwoPair); hand = new Hand(resultCards, Combination.TwoPair); return true; } // Three of a kind only else if (numPairs == 0 && numThrees >= 1 && numFours == 0) { foreach (int cardValue in new Stack<int>(matches.Keys)) { if (matches[cardValue].Count == 3) // this is our highest 3 { resultCards.AddRange(matches[cardValue]); break; } } // Also add the next highest card for (int highCard = cardList.Count - 1; highCard >= 0; highCard--) { if (!resultCards.Contains(cardList[highCard])) { // Add the next highest card from the sorted list and break resultCards.Add(cardList[highCard]); break; } } // Construct the Best Hand for one pair and return true //hand = new BestHand(resultCards, HandType.ThreeOfAKind); hand = new Hand(resultCards, Combination.ThreeOfAKind); return true; } // four of a kind only else if (numPairs <= 1 && numThrees <= 1 && numFours == 1) { foreach (int cardValue in new Stack<int>(matches.Keys)) { if (matches[cardValue].Count == 4) // this is the 4 { resultCards.AddRange(matches[cardValue]); break; } } // Also add the next highest card for (int highCard = cardList.Count - 1; highCard >= 0; highCard--) { if (!resultCards.Contains(cardList[highCard])) { resultCards.Add(cardList[highCard]); break; } } // Construct the Best Hand for one pair and return true //hand = new BestHand(resultCards, HandType.FourOfAKind); hand = new Hand(resultCards, Combination.FourOfAKind); return true; } // Full house only else if (numPairs >= 1 && numThrees == 1 && numFours == 0) { foreach (int cardValue in new Stack<int>(matches.Keys)) { if (matches[cardValue].Count == 3) // this is the 3 in the full house resultCards.AddRange(matches[cardValue]); if (matches[cardValue].Count == 2) // this is the 2 resultCards.AddRange(matches[cardValue]); if (resultCards.Count >= 5) break; } // Construct the Best Hand for one pair and return true //hand = new BestHand(resultCards, HandType.FullHouse); hand = new Hand(resultCards, Combination.FullHouse); return true; } else { // Just return the high card //List<Card> highCard = new List<Card>(); CardCollection highCard = new CardCollection(); highCard.Add(cardList[cardList.Count - 1]); //hand = new BestHand(highCard, HandType.HighCard); hand = new Hand(highCard, Combination.HighCard); return false; } }
protected override ChoiceResult Decide_Vault(Choice choice) { switch (choice.ChoiceType) { case ChoiceType.Options: // If there are at least 2 non-Action & non-Treasure cards, discard 2 of them IEnumerable<Card> vaultDiscardableCards = this.RealThis.Hand[c => (c.Category & Category.Treasure) != Category.Treasure && (c.Category & Category.Action) != Category.Action]; if (vaultDiscardableCards.Count() >= 2) return new ChoiceResult(new List<String>() { choice.Options[0].Text }); else return new ChoiceResult(new List<String>() { choice.Options[1].Text }); case ChoiceType.Cards: if (choice.Text.StartsWith("Discard any number of cards")) { /// TODO -- this needs to be a bit smarter. It shouldn't discard Action cards if we have 1+ Actions left /// It can also be improved to chain with Tactician (and so can Secret Chamber, for that matter) // Discard all non-Treasure cards return new ChoiceResult(new CardCollection(choice.Cards.Where(c => (c.Category & Category.Treasure) != Category.Treasure))); } else // "Choose 2 cards to discard" { CardCollection vDiscards = new CardCollection(); vDiscards.AddRange(choice.Cards.Where(c => (c.Category & Category.Curse) == Category.Curse)); if (vDiscards.Count < 2) vDiscards.AddRange(choice.Cards.Where(card => (card.Category & Category.Ruins) == Category.Ruins)); if (vDiscards.Count < 2) vDiscards.AddRange(choice.Cards.Where(card => card.Category == Category.Victory)); if (vDiscards.Count > 2) vDiscards.RemoveRange(2, vDiscards.Count - 2); else vDiscards.AddRange(this.FindBestCardsToDiscard(choice.Cards, 2 - vDiscards.Count)); return new ChoiceResult(vDiscards); } default: return base.Decide_Vault(choice); } }
protected IEnumerable<Card> FindBestCardsToTrash(IEnumerable<Card> cards, int count, Boolean onlyReturnTrashables) { // choose the worse card in hand in this order // 1) curse // 2) any ruins // 3) Sea Hag if there are no curses left // 4) Loan if we have fewer than 3 Coppers left // 5) Copper if we've got a lot of better Treasure // 6) Fortress // (If onlyReturnTrashables is false): // 7) lowest value from ComputeValueInDeck CardCollection cardsToTrash = new CardCollection(); cardsToTrash.AddRange(cards.Where(c => c.Category == Category.Curse).Take(count)); if (cardsToTrash.Count >= count) return cardsToTrash; cardsToTrash.AddRange(cards.Where(c => (c.Category & Category.Ruins) == Category.Ruins).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; if (this.RealThis._Game.Table.Curse.Count <= 1) { cardsToTrash.AddRange(cards.Where(c => c.CardType == Cards.Seaside.TypeClass.SeaHag).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; } cardsToTrash.AddRange(cards.Where(c => c.CardType == Cards.DarkAges.TypeClass.OvergrownEstate).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; cardsToTrash.AddRange(cards.Where(c => c.CardType == Cards.DarkAges.TypeClass.Hovel).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; if (this.RealThis.CountAll(this.RealThis, c => (c.Category & Category.Treasure) == Category.Treasure && (c.Benefit.Currency.Coin > 1 || c.CardType == Cards.Prosperity.TypeClass.Bank)) >= this.RealThis.CountAll(this.RealThis, c => c.CardType == Cards.Universal.TypeClass.Copper)) { cardsToTrash.AddRange(cards.Where(c => c.CardType == Cards.Universal.TypeClass.Copper).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; } if (this.RealThis.CountAll(this.RealThis, c => c.CardType == Cards.Universal.TypeClass.Copper) < 3) { cardsToTrash.AddRange(cards.Where(c => c.CardType == Cards.Prosperity.TypeClass.Loan).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; } cardsToTrash.AddRange(cards.Where(c => c.CardType == Cards.DarkAges.TypeClass.Fortress).Take(count - cardsToTrash.Count)); if (cardsToTrash.Count >= count) return cardsToTrash; if (!onlyReturnTrashables) cardsToTrash.AddRange(cards.OrderBy(c => ComputeValueInDeck(c)).ThenBy(c => c.Name).Where(c => !cardsToTrash.Contains(c)).Take(count - cardsToTrash.Count)); return cardsToTrash; }
public CardCollection DrawFrom(DeckPosition deckPosition, int number, Object destination) { CardCollection cards = new CardCollection(); if (number <= 0) return cards; CardCollection cardsFirst = _DrawPile.Retrieve(this, deckPosition, c => true, number); cards.AddRange(cardsFirst); cards.RemovedFrom(DeckLocation.Deck, this); if (_AsynchronousDrawing) { if (_AsynchronousCardsDrawnEventArgs == null) _AsynchronousCardsDrawnEventArgs = new CardsDrawnEventArgs(cardsFirst, deckPosition, number); else _AsynchronousCardsDrawnEventArgs.Cards.AddRange(cardsFirst); } else if (CardsDrawn != null) { CardsDrawnEventArgs cdea = new CardsDrawnEventArgs(cardsFirst, deckPosition, number); CardsDrawn(this, cdea); } if (destination is Type) this.AddCardsInto((Type)destination, cardsFirst); else if (destination is DeckLocation) this.AddCardsInto((DeckLocation)destination, cardsFirst); else throw new Exception(String.Format("Destination of {0} ({1}) is not supported", destination, destination.GetType())); if (cardsFirst.Count < number && _DrawPile.Count == 0 && _DiscardPile.Count > 0) { this.ShuffleForDrawing(); CardCollection cardsSecond = _DrawPile.Retrieve(this, deckPosition, c => true, number < 0 ? number : number - cards.Count); cards.AddRange(cardsSecond); cardsSecond.RemovedFrom(DeckLocation.Deck, this); if (_AsynchronousDrawing) { if (_AsynchronousCardsDrawnEventArgs == null) _AsynchronousCardsDrawnEventArgs = new CardsDrawnEventArgs(cardsSecond, deckPosition, number); else _AsynchronousCardsDrawnEventArgs.Cards.AddRange(cardsSecond); } else if (CardsDrawn != null) { CardsDrawnEventArgs cdea = new CardsDrawnEventArgs(cardsSecond, deckPosition, number); CardsDrawn(this, cdea); } if (destination is Type) this.AddCardsInto((Type)destination, cardsSecond); else if (destination is DeckLocation) this.AddCardsInto((DeckLocation)destination, cardsSecond); else throw new Exception(String.Format("Destination of {0} ({1}) is not supported", destination, destination.GetType())); } return cards; }
protected override IEnumerable<Card> FindBestCardsToDiscard(IEnumerable<Card> cards, int count) { // choose the worse card in hand in this order // 1) positive victory points // 2) curse // 3) cheapest card left CardCollection cardsToDiscard = new CardCollection(); CardCollection cardsLeftOver = new CardCollection(); foreach (Card card in cards) { if (card.Category == Category.Victory) cardsToDiscard.Add(card); else cardsLeftOver.Add(card); if (cardsToDiscard.Count >= count) break; } if (cardsToDiscard.Count >= count) return cardsToDiscard; cardsToDiscard.AddRange(FindBestCardsToTrash(cardsLeftOver, count - cardsToDiscard.Count)); return cardsToDiscard; }