Ejemplo n.º 1
0
 public StackObject(StackObjectType objectType, string typeString, string text,
                    Player owner, Player controller, ColorsEnum colors,
                    GameActionDelegate resolutionAction)
 {
     ObjectType       = objectType;
     TypeString       = typeString;
     Text             = text;
     Owner            = owner;
     Controller       = controller;
     Colors           = colors;
     ResolutionAction = resolutionAction;
 }
Ejemplo n.º 2
0
        public StackObject(StackObjectType objectType, string typeString, string text, 
		                   Player owner, Player controller, ColorsEnum colors, 
		                   GameActionDelegate resolutionAction)
        {
            ObjectType = objectType;
            TypeString = typeString;
            Text = text;
            Owner = owner;
            Controller = controller;
            Colors = colors;
            ResolutionAction = resolutionAction;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Expands the node.  Creates a child node using the first untried action and adds it to the tree.
        /// </summary>
        /// <returns>
        /// The new child node.
        /// </returns>
        /// <param name='node'>
        /// Node to expand.
        /// </param>
        /// <param name='actionsFromState'>
        /// List of actions from the state.
        /// </param>
        private MctsNode ExpandNode(MctsNode node, IList <GameActionDelegate> actionsFromState)
        {
            // choose an untried action from node
            GameActionDelegate    untriedAction    = null;
            LinkedList <MctsNode> childrenToVerify = new LinkedList <MctsNode>(node.Children);

            foreach (var a in actionsFromState)
            {
                bool taken = false;
                var  child = childrenToVerify.First;

                while (child.Next != null)
                {
                    if (child.Value.ActionTaken == a)
                    {
                        taken = true;
                        childrenToVerify.Remove(child);
                        break;
                    }
                    child = child.Next;
                }
                if (!taken)
                {
                    untriedAction = a;
                    break;
                }
            }

            if (untriedAction == null)
            {
                throw new ArgumentNullException("Parameter node must not be fully expanded");
            }

            // create and add the child
            stateMachine.CurrState = node.GameState;
            stateMachine.PerformAction(untriedAction);
            MctsNode newChild = new MctsNode()
            {
                ActionTaken = untriedAction,
                GameState   = stateMachine.CurrState,
            };

            node.Children.Add(newChild);
            return(newChild);
        }
Ejemplo n.º 4
0
 public Ability(AbilityAvailablePredicate pred, GameActionDelegate a)
 {
     this.AbilityAvailable = pred;
     this.AbilityAction    = a;
 }
Ejemplo n.º 5
0
 public Ability(AbilityAvailablePredicate pred, GameActionDelegate a)
 {
     this.AbilityAvailable = pred;
     this.AbilityAction = a;
 }
Ejemplo n.º 6
0
 public void PerformAction(GameActionDelegate action)
 {
     action(CurrState.PlayerWithPriority, CurrState);
 }
Ejemplo n.º 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);
        }