Beispiel #1
0
        /// <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
        }
Beispiel #2
0
 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);
     }
 }
Beispiel #3
0
        public void DealAssignedDamage()
        {
            foreach (var attacker in _attackers)
            {
                attacker.DealAssignedDamage();
            }

            foreach (var blocker in _blockers)
            {
                blocker.DealAssignedDamage();
            }

            DefendingPlayer.DealAssignedDamage();
        }
Beispiel #4
0
        /// <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);
        }