예제 #1
0
        public bool CanCast(ManaPool floating)
        {
            if (UnpayableManaCost)
            {
                return(false);
            }

            ManaPool subtractedPool = new ManaPool(floating);
            bool     coloredOk      = true;

            foreach (ColorsEnum color in Enum.GetValues(typeof(ColorsEnum)))
            {
                coloredOk &= (subtractedPool[color] -= this[color]) >= 0;
            }

            if (!coloredOk)
            {
                return(false);
            }

            subtractedPool.Generic += subtractedPool.W
                                      + subtractedPool.U
                                      + subtractedPool.B
                                      + subtractedPool.R
                                      + subtractedPool.G;

            return(subtractedPool.Generic >= GenericMana);
        }
예제 #2
0
 /// <summary>
 /// Copy constructor for the <see cref="mlaSharp.ManaPool"/> class.
 /// </summary>
 /// <param name='mp'>
 /// ManaPool to copy
 /// </param>
 public ManaPool(ManaPool mp)
 {
     this.W = mp.W;
     this.U = mp.U;
     this.B = mp.B;
     this.R = mp.R;
     this.G = mp.G;
     this.Generic = mp.Generic;
 }
예제 #3
0
 public bool Equals(ManaPool other)
 {
     return((W == other.W) &&
            (U == other.U) &&
            (B == other.B) &&
            (R == other.R) &&
            (G == other.G) &&
            (Generic == other.Generic));
 }
예제 #4
0
 /// <summary>
 /// Copy constructor for the <see cref="mlaSharp.ManaPool"/> class.
 /// </summary>
 /// <param name='mp'>
 /// ManaPool to copy
 /// </param>
 public ManaPool(ManaPool mp)
 {
     this.W       = mp.W;
     this.U       = mp.U;
     this.B       = mp.B;
     this.R       = mp.R;
     this.G       = mp.G;
     this.Generic = mp.Generic;
 }
예제 #5
0
        private void RemoveManaFromPool(ManaPool pool, ManaCost cost)
        {
            // pay colored costs
            var ColorsEnumValues = Enum.GetValues(typeof(ColorsEnum));

            foreach (ColorsEnum color in ColorsEnumValues)
            {
                pool[color] -= cost[color];
            }

            // pay generic costs
            int genericCost = cost.GenericMana;

            // if can pay with only generic mana, do so
            if (pool.Generic - genericCost >= 0)
            {
                pool.Generic -= genericCost;
                return;
            }

            // otherwise, pay for generic costs by looking through each color
            genericCost -= pool.Generic;
            pool.Generic = 0;
            foreach (ColorsEnum color in ColorsEnumValues)
            {
                // if there's enough mana in the current color to pay the rest of the cost,
                // pay it and break
                // otherwise, subtract what we can
                if (pool[color] - genericCost >= 0)
                {
                    pool[color] -= genericCost;
                    genericCost  = 0;
                    break;
                }

                genericCost -= pool[color];
                pool[color] -= pool[color];
            }
        }
예제 #6
0
        /// <summary>
        /// Copy constructor for the <see cref="mlaSharp.State"/> class.
        /// </summary>
        /// <param name='s'>
        /// State s to copy.
        /// </param>
        public State(State s)
        {
            CurrStep           = s.CurrStep;
            PlayersTurn        = s.PlayersTurn;
            PlayerWithPriority = s.PlayerWithPriority;
            Battlefield        = new List <Card>(s.Battlefield);
            Stack      = new List <StackObject>(s.Stack);
            ManaPools  = new Dictionary <Player, ManaPool>();
            LifeTotals = new Dictionary <Player, int>();
            Hands      = new Dictionary <Player, List <Card> >();
            Graveyards = new Dictionary <Player, List <Card> >();

            HostGameEngine = s.HostGameEngine;

            // deep copy dictionary entries
            foreach (Player p in s.HostGameEngine.Players)
            {
                ManaPools[p]  = new ManaPool(s.ManaPools[p]);
                LifeTotals[p] = s.LifeTotals[p];
                Hands[p]      = new List <Card>(s.Hands[p]);
                Graveyards[p] = new List <Card>(s.Graveyards[p]);
            }
        }
예제 #7
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);
        }
예제 #8
0
 public bool Equals(ManaPool other)
 {
     return (W == other.W)
         && (U == other.U)
         && (B == other.B)
         && (R == other.R)
         && (G == other.G)
         && (Generic == other.Generic);
 }