/// <summary> /// this is the logic that happens when a defender loses, its pretty ugly /// </summary> private void DefenderLoses() { Deck cardsToDraw = new Deck(); //make a deck to draw all the attacks from. THIS NEEDS TO BE FIXED LATER its dumb Console.WriteLine("{} cannot defend, they get the cards and lose their attack.", DefendingPlayer.Name); foreach (AttackPair pair in Attacks) { cardsToDraw.Add(pair.Attack); cardsToDraw.Add(pair.Defend); } while (cardsToDraw.Count > 0) { DefendingPlayer.DrawCard(cardsToDraw); } if ((Attacker.Key + 2) >= myPlayers.Count) { SetNewAttacker(Attacker.Key + 2 - myPlayers.Count); } else { SetNewAttacker(Attacker.Key + 2); } Attacks = new ArrayList(); // refresh the attacks arraylist SetDefender(); //throw new NotImplementedException("This is not done"); // more to be done here }
private void Defend() { if (!DefendingPlayer.CanDefend(LastAttack, trump)) { throw new CannotDefendException("The defending player cannot defend the attack"); } else { (Attacks[Attacks.Count - 1] as AttackPair).Defend = DefendingPlayer.Defend(LastAttack, trump); } }
public void DealAssignedDamage() { foreach (var attacker in _attackers) { attacker.DealAssignedDamage(); } foreach (var blocker in _blockers) { blocker.DealAssignedDamage(); } DefendingPlayer.DealAssignedDamage(); }
/// <summary> /// Enumerate all possible actions from CurrState for a given player. /// </summary> /// <returns> /// The list of actions. /// </returns> /// <param name='p'> /// The player for whom to enumerate the actions. /// </param> public List <ActionDescriptionTuple> EnumActions(Player p) { var actions = new List <ActionDescriptionTuple>(); // if the stack is empty, and that player has priority if (p == CurrState.PlayersTurn && p == CurrState.PlayerWithPriority && CurrState.Stack.Count == 0) { #region Attacks // declareAtk if (p == CurrState.PlayersTurn && CurrState.CurrStep == Steps.declareAtk && !CurrState.AttackersDeclared) { var possibleAttackers = (from c in CurrState.Battlefield where c.Controller == CurrState.PlayersTurn where c.Type.Contains("Creature") where c.ControlTimestamp < CurrState.TurnNumber // TODO: represent summoning sickness correctly where (c.Status & Card.StatusEnum.Tapped) == 0 // i.e. untapped select c as CreatureCard) .ToList(); if (possibleAttackers.Count() > 0) { AttackersToBlockersDictionary = new Dictionary <CreatureCard, IList <CreatureCard> >(); DefendingPlayer = Players[(currentPlayerIndex + 1) % Players.Count]; // TODO: make choosing attackers actually consistent, fix this hack // present the list of actions as comprising only of choosing attackers // warning - number of actions is exponential in number of attackers if (true || p is MctsPlayer) { var allPossibleAttacks = possibleAttackers.PowerSet(); StringBuilder descSb = new StringBuilder(); foreach (var atkSubset in allPossibleAttacks) { var atkSubsetClosureVariable = atkSubset; // needed so the closures point to distinct subsets instead of just the last descSb.Clear(); descSb.Append("Attack with "); foreach (CreatureCard c in atkSubset) { descSb.Append(c.Name); descSb.Append(", "); } if (atkSubset.Count == 0) { descSb.Append("nothing"); // complete description for attacking with nothing } else { descSb.Remove(descSb.Length - 2, 2); // remove trailing ", " } var possibleAttack = new ActionDescriptionTuple() { GameAction = (Player player, State s) => { foreach (CreatureCard c in atkSubsetClosureVariable) { AttackersToBlockersDictionary[c] = null; c.Status |= Card.StatusEnum.Tapped; // TODO: allow for vigilance } // TODO: allow "fast" effects to be played, but for now, skip priority pass // Give "priority" to defending player so they can assign blocks CurrState.PlayerWithPriority = DefendingPlayer; s.MoveToNextStep(); }, ActionDescription = descSb.ToString() }; actions.Add(possibleAttack); } CurrState.AttackersDeclared = true; return(actions); } // TODO: change implementation to allow attacking planeswalkers, multiplayer, etc var chosenAttackers = p.ChooseAttackers(possibleAttackers); // create attackers to blockers dictionary that will be passed to defending player string attackersMsg = "Player " + p.Name + " attacks with "; foreach (var creature in chosenAttackers) { AttackersToBlockersDictionary[creature] = new List <CreatureCard>(); attackersMsg += creature.Name + ", "; } if (chosenAttackers.Count > 0) { logger.Debug(attackersMsg); } } } #endregion // move to the next step var moveToNextStep = new ActionDescriptionTuple() { GameAction = (Player player, State s) => { s.MoveToNextStep(); }, ActionDescription = "Move to the next step." }; actions.Add(moveToNextStep); // for convience, an end turn action is available var endTurn = new ActionDescriptionTuple() { GameAction = (Player player, State s) => { do { s.MoveToNextStep(); }while(s.CurrStep != Steps.main1); }, ActionDescription = "Move to the next turn" }; actions.Add(endTurn); // if it's a main phase, sorcery speed effects can be played if ((CurrState.CurrStep == Steps.main1 || CurrState.CurrStep == Steps.main2)) { // AP can play lands if (CurrState.LandsLeftToPlayThisTurn > 0) { var lands = from c in CurrState.Hands[CurrState.PlayerWithPriority] where c.Type.Contains("Land") select c; foreach (Card land in lands) { var actionDescription = new ActionDescriptionTuple() { GameAction = (Player player, State s) => { s.Battlefield.Add(land); s.Hands[player].Remove(land); land.ControlTimestamp = s.TurnNumber; land.Controller = player; s.LandsLeftToPlayThisTurn--; }, ActionDescription = "Play a " + land.Name + " (land)" }; actions.Add(actionDescription); } } // Cast sorceries and creatures // TODO: make this work for sorceries ManaPool floating = CurrState.ManaPools[CurrState.PlayerWithPriority]; var sorcerySpeedSpells = from c in CurrState.Hands[CurrState.PlayerWithPriority] where c.Type.Contains("Creature") where c.Cost.CanCast(floating) select c; foreach (Card spell in sorcerySpeedSpells) { var spellClosureVar = spell; // needed so the closure refers to this spell, not just the last one. // this doesn't work for sorceries StackObject so = new StackObject(StackObject.StackObjectType.Card, spell.Type, spell.Text, spell.Owner, spell.Owner, spell.Colors, (Player Player, State s) => { s.Battlefield.Add(spellClosureVar); spellClosureVar.ControlTimestamp = s.TurnNumber; spellClosureVar.Controller = Player; }); var actionDescription = new ActionDescriptionTuple() { GameAction = (Player player, State s) => { s.Stack.Add(so); s.Hands[player].Remove(spellClosureVar); RemoveManaFromPool(s.ManaPools[p], spellClosureVar.Cost); }, ActionDescription = "Cast " + spellClosureVar.Name + " (creature)" }; actions.Add(actionDescription); } } } #region Blocks if (CurrState.AttackersDeclared && p == DefendingPlayer && CurrState.CurrStep == Steps.declareBlk && !CurrState.BlockersDeclared) { var possibleBlockers = (from c in CurrState.Battlefield where c.Controller == DefendingPlayer where c.Type.Contains("Creature") where (c.Status & Card.StatusEnum.Tapped) == 0 select c as CreatureCard) .ToList(); if (AttackersToBlockersDictionary == null) { throw new NullReferenceException("AttackersToBlockersDictionary was unexpectedly null"); } if (possibleBlockers.Count() == 0) // give "priority" back to active player { actions.Add(new ActionDescriptionTuple() { GameAction = (Player player, State s) => { CurrState.BlockersDeclared = true; CurrState.PlayerWithPriority = CurrState.PlayersTurn; // TODO: while there are no "fast" effects, skip priority pass CurrState.MoveToNextStep(); }, ActionDescription = "No blocks" }); } else { // TODO: make choosing blockers actually consistent, fix this hack // present the list of actions as comprising only of choosing blocks // warning - number of actions is exponential in the number of attackers AND blockers if (true || p is MctsPlayer) { var attackers = AttackersToBlockersDictionary.Keys.ToList(); var possibleBlocks = GetPossibleBlocks(attackers, possibleBlockers); StringBuilder descSb = new StringBuilder(); foreach (var block in possibleBlocks) { var blockClosureVariable = block; // needed so the closures point to distinct subsets instead of just the last descSb.Clear(); GameActionDelegate ga = (Player player, State s) => { foreach (var t in blockClosureVariable) { AttackersToBlockersDictionary[t.Attacker] = t.Blockers; } // TODO: allow "fast" effects to be played, but for now, skip priority pass // give priority back to active player CurrState.PlayerWithPriority = CurrState.PlayersTurn; s.MoveToNextStep(); }; foreach (var atbTuple in block) { AttackersToBlockersDictionary[atbTuple.Attacker] = atbTuple.Blockers; descSb.Append("Block "); descSb.Append(atbTuple.Attacker.ToString()); descSb.Append(" with "); if (atbTuple.Blockers.Count == 0) { descSb.Append("nothing; "); } else { foreach (var blocker in atbTuple.Blockers) { descSb.Append(blocker.ToString()); descSb.Append(" & "); } descSb.Remove(descSb.Length - 3, 3); // remove trailing " & " descSb.Append("; "); } } //descSb.Remove(descSb.Length-2,2); // remove trailing "; " var possibleBlockDescription = new ActionDescriptionTuple() { GameAction = ga, ActionDescription = descSb.ToString() }; actions.Add(possibleBlockDescription); } CurrState.BlockersDeclared = true; return(actions); } List <CreatureCard> blockersCopy; IDictionary <CreatureCard, IList <CreatureCard> > atbCopy; // query player for legal blocks do { // copy the data structures in case defending player generates invalid blocks blockersCopy = possibleBlockers.ToList(); atbCopy = AttackersToBlockersDictionary.DeepCopy(); // query player for blocks DefendingPlayer.ChooseBlockers(atbCopy, blockersCopy); } while (!LegalBlocks(atbCopy)); AttackersToBlockersDictionary = atbCopy; p.OrderBlockers(AttackersToBlockersDictionary); // display blocks string blockersMsg = "Player " + DefendingPlayer.Name + " blocks:\n"; foreach (var attacker in AttackersToBlockersDictionary.Keys) { // only display attackers that are blocked if (AttackersToBlockersDictionary[attacker].Count > 0) { blockersMsg += attacker.Name + " is blocked by "; foreach (var blockers in AttackersToBlockersDictionary[attacker]) { blockersMsg += blockers.Name + ", "; } blockersMsg += "\n"; } } logger.Debug(blockersMsg); } } #endregion // "instant" speed effects, such as activated abilities, instants if (p == CurrState.PlayerWithPriority) { // check for activated abilities // note, this where clause assumes that only the controllers of cards can activate their abilities // not true for Oona's Prowler, Mindslaver, etc var abilitiesAvailable = from card in CurrState.Battlefield where card.Controller == CurrState.PlayerWithPriority from ab in card.ActivatedAbilities where ab.AbilityAvailable(p, CurrState) select Tuple.Create(card, ab); foreach (var tuple in abilitiesAvailable) { var abilityAction = new ActionDescriptionTuple() { GameAction = tuple.Item2.AbilityAction, ActionDescription = tuple.Item1.Name + "'s activated ability" }; actions.Add(abilityAction); } // TODO: allow casting instants } return(actions); }