コード例 #1
0
        public void EnemyMove(FightAction enemyAction = null)
        {
            if (enemyAction == null)
            {
                enemyAction = _Enemies[0].GetAction(TurnNumber);
            }
            var history = new List <string>();

            StartEnemyTurn(history);

            if (enemyAction == null)
            {
                throw new Exception("No enemy action?");
            }
            if (enemyAction.CardInstance != null)
            {
                _Attack(enemyAction.CardInstance, history);
                enemyAction.AddHistory(history);
                AssignLastAction(enemyAction);
            }
            else
            {
                throw new Exception("Noop");
            }
            EnemyDone = true;
        }
コード例 #2
0
        public MonteCarlo(Deck deck, Enemy enemy, Player player, int turnNumber = 0, List <string> firstHand = null)
        {
            _Deck       = deck ?? throw new ArgumentNullException(nameof(deck));
            _Enemy      = enemy ?? throw new ArgumentNullException();
            _Player     = player ?? throw new ArgumentNullException();
            _TurnNumber = turnNumber;

            if (firstHand == null)
            {
                //by default shuffle the deck at fight start.
                _Deck.Reshuffle(new EffectSet(), new List <string>());
            }
            else
            {
                var cards       = _Deck.FindSetOfCards(_Deck.GetDrawPile, firstHand);
                var firstAction = new FightAction(FightActionEnum.StartTurn, cardTargets: cards);
                _FirstAction = firstAction;
            }

            var fight = new Fight(_Deck, _Player, _Enemy);

            fight.TurnNumber = _TurnNumber;

            var root = new FightNode(fight);

            fight.FightNode = root;
            root.StartFight();
            Root = root;
            if (_FirstAction != null)
            {
                root.ApplyAction(_FirstAction);
            }
        }
コード例 #3
0
        internal FightNode EnemyMove(FightAction action)
        {
            var c = GetNode();

            c.Fight.EnemyMove(action);
            AddChild(c);
            return(c);
        }
コード例 #4
0
        /// <summary>
        /// Call various high level actions on nodes and they'll eventually return ienumerable<FightNode>
        /// </summary>
        internal FightNode PlayCard(FightAction action)
        {
            var c = GetNode();

            c.Fight.PlayCard(action);
            AddChild(c);
            return(c);
        }
コード例 #5
0
        internal FightNode DrinkPotion(FightAction action)
        {
            var c   = GetNode();
            var rnd = c.Fight.DrinkPotion(action.Potion, (Enemy)action.Target);

            AddChild(c);
            return(c);
        }
コード例 #6
0
 private void GenerateKeyIfNecessary(FightAction action)
 {
     if (action.Keys != null)
     {
         action.Key = Rnd.Next(action.Keys.Count);
         //note down which randomness we chose for this so that we can identify duplicates later.
     }
 }
コード例 #7
0
        internal FightNode StartTurn(FightAction action = null)
        {
            var c = GetNode();

            c.Fight.StartTurn(action);
            AddChild(c);
            return(c);
        }
コード例 #8
0
 public void AssignLastAction(FightAction a)
 {
     //unless a fight is part of a fightnode, don't assign history.  e.g. tests.
     if (FightAction != null && FightNode != null)
     {
         throw new Exception("protection");
     }
     FightAction = a;
 }
コード例 #9
0
        public bool StartTurn(FightAction action = null)
        {
            if (PlayerTurn)
            {
                throw new Exception("Turn Already Started");
            }
            if (!EnemyDone)
            {
                throw new Exception("Enemy not done");
            }
            PlayerTurn = true;
            EnemyDone  = false;
            var history = new List <string>();

            TurnNumber++;
            var ef = new EffectSet();

            var drawCount = _Player.GetDrawAmount();
            List <CardInstance> initialHand = action?.CardTargets.ToList();

            if (action?.CardTargets == null)
            {
                initialHand = _Deck.DrawCards(drawCount, ef, history);
            }
            else
            {
                _Deck.ForceDrawCards(_Player, initialHand, ef, history);
            }

            history.Add($"Drew: {Helpers.SJ(' ', initialHand.OrderBy(el => el.ToString()))}");

            _Player.Energy = _Player.MaxEnergy();
            _Player.Block  = 0;

            foreach (var status in _Player.StatusInstances)
            {
                status.StartTurn(_Player, ef.PlayerEffect, ef.EnemyEffect);
            }

            foreach (var relic in _Player.Relics)
            {
                relic.StartTurn(_Player, _Enemies[0], ef);
            }

            ApplyEffectSet(ef, _Player, _Enemies[0], history);

            if (action == null)
            {
                action = new FightAction(FightActionEnum.StartTurn, cardTargets: initialHand, history: history);
            }
            else
            {
                action.History = history;
            }
            AssignLastAction(action);
            return(true);
        }
コード例 #10
0
        public void EnemyMove(int amount, int count)
        {
            if (PlayerTurn)
            {
                throw new Exception("Not your turn");
            }
            var ea = new FightAction(FightActionEnum.EnemyMove, card: new CardInstance(new EnemyCard(targetType: TargetType.Player, amount, count), 0), hadRandomEffects: true);

            EnemyMove(ea);
        }
コード例 #11
0
 public override FightAction GetAction(int turn)
 {
     if (turn == 1)
     {
         var res = new FightAction(FightActionEnum.EnemyMove, hadRandomEffects: true, card: new CardInstance(new EnemyCard(targetType: TargetType.Enemy, buffs: new List <StatusInstance>()
         {
             new StatusInstance(new Feather(), 3)
         }), 0), key: 1);
         return(res);
     }
     else
     {
         var res = new FightAction(FightActionEnum.EnemyMove, hadRandomEffects: true, card: new CardInstance(new EnemyCard(targetType: TargetType.Player, 6, 1), 0), key: 1);
         return(res);
     }
 }
コード例 #12
0
        public FightNode ApplyAction(FightAction action)
        {
            //check if a child already has this action; if so just return that one.
            //would be a lot better to just MC the child directly rather than MCing the parent and then redoing this traversal.

            GenerateKeyIfNecessary(action);
            if (action.Random && action.Key == null)
            {
                throw new Exception("Guard");
            }

            var dup = FindDuplicate(action);

            if (dup != null)
            {
                dup.Weight++;
                return(dup);
            }

            switch (action.FightActionType)
            {
            case FightActionEnum.PlayCard:
                var child = PlayCard(action);     //non-rnd
                return(child);

            case FightActionEnum.Potion:
                var child2 = DrinkPotion(action);     //non-rnd
                return(child2);

            case FightActionEnum.EndTurn:
                var child3 = EndTurn();     //non-rnd
                return(child3);

            case FightActionEnum.EnemyMove:
                var child4 = EnemyMove(action);     //rnd
                return(child4);

            case FightActionEnum.StartTurn:
                var child5 = StartTurn(action);
                return(child5);

            default:
                throw new Exception("Invalid action");
            }
        }
コード例 #13
0
        /// <summary>
        /// All playable cards, potions, or endturn.
        /// Complication: Some cards generate multiple actions (when they have random effects.)
        /// </summary>
        public IList <FightAction> GetNormalActions(bool includeUnplayable)
        {
            var res               = new List <FightAction>();
            var hand              = _Deck.GetHand;
            var consideredCis     = new List <string>();
            var en                = _Player.Energy;
            var includeDuplicates = includeUnplayable ? true : false;

            foreach (var ci in hand)
            {
                if (!includeUnplayable) //
                {
                    if (consideredCis.Contains(ci.ToString()))
                    {
                        continue;
                    }
                }

                var playable = ci.EnergyCost() <= en && ci.Playable(hand);
                if (includeUnplayable || playable)
                {
                    var action = new FightAction(fightActionType: FightActionEnum.PlayCard, card: ci, playable: playable);
                    res.Add(action);
                    consideredCis.Add(ci.ToString());
                }
            }

            var seenPots = new HashSet <string>();

            foreach (var pot in _Player.Potions)
            {
                var key = pot.ToString();
                if (seenPots.Contains(key))
                {
                    continue;
                }
                seenPots.Add(key);
                var sa = new FightAction(FightActionEnum.Potion, potion: pot, target: _Enemies[0]);
                res.Add(sa);
            }

            res.Add(new FightAction(FightActionEnum.EndTurn));

            return(res);
        }
コード例 #14
0
        /// <summary>
        /// When we generate an action, we check to see if it's already a duplicate (in randoms or choices).  This is how we compare them.
        /// </summary>
        public bool IsEqual(FightAction other)
        {
            if (FightActionType != other.FightActionType)
            {
                return(false);
            }
            if (CardInstance?.ToString() != other.CardInstance?.ToString())
            {
                return(false);
            }
            if (Potion?.Name != other.Potion?.Name)
            {
                return(false);
            }
            if (Target?.Name != other.Target?.Name)
            {
                return(false);
            }
            if (CardTargets != null && other.CardTargets == null)
            {
                return(false);
            }
            if (CardTargets == null && other.CardTargets != null)
            {
                return(false);
            }
            if (!Helpers.CompareHands(CardTargets, other.CardTargets, out var msg2))
            {
                return(false);
            }
            //if (Key != other.Key) return false;
            //we do not compare key here because this is at the "choice" stage.
            if (Random != other.Random)
            {
                return(false);
            }

            return(true);
        }
コード例 #15
0
 /// <summary>
 /// Also start the first turn.
 /// </summary>
 internal void StartFight()
 {
     FightAction = new FightAction(FightActionEnum.StartFight, hadRandomEffects: true);
 }
コード例 #16
0
        /// <summary>
        /// Add node to the proper place and return it for further MCing
        /// </summary>
        public FightNode AddChild(FightNode child)
        {
            //interesting, although enemymoves are also randoms, there is no point in having an intermediate copy node.
            //i.e. Cendturn => RenemyMove => [Rvarious enemy moves] is kind of of pointless
            //unlike CstartTurn => Rwildstrike => [Rvarious wildstrikes]
            //                  => Rother random card => [ROther random card random outputs]
            child.FightAction = child.Fight.FightAction;

            if (child.FightAction.FightActionType == FightActionEnum.EnemyMove)
            {
                child.Parent      = this;
                child.FightAction = child.Fight.FightAction;
                Randoms.Add(child);
            }

            else if (child.FightAction.Random)
            {
                //two cases:
                //the intermediate node is already created:
                FightNode intermediateNode = null;
                foreach (var c in Choices)
                {
                    if (c.FightAction.IsEqual(child.FightAction))
                    {
                        intermediateNode = c;
                        break;
                    }
                }

                if (intermediateNode == null)
                {
                    //if we have a random, then consider it a c with this as a random child.
                    intermediateNode        = new FightNode(child.Fight.Copy());
                    intermediateNode.Parent = this;
                    var src = child.Fight.FightAction;

                    //we convert the specced out action into a generic one again.
                    var nonspecifiecAction = new FightAction(fightActionType: src.FightActionType,
                                                             card: src.CardInstance, cardTargets: src.CardTargets, potion: src.Potion, target: src.Target,
                                                             hadRandomEffects: src.Random);
                    intermediateNode.FightAction = nonspecifiecAction;

                    //zero these out since at this stage they represent the action pre-randomization.
                    if (intermediateNode.Fight.FightNode != null)
                    {
                        intermediateNode.Fight.FightNode.FightAction.Key = null;
                    }
                    intermediateNode.FightAction.Key = null;
                    Choices.Add(intermediateNode);
                    //there is not a random with this key already since this is the first time we played this card with random children.
                }

                //we also have to check for identity with the child nodes.
                foreach (var other in intermediateNode.Randoms)
                {
                    if (other.FightAction.IsEqual(child.Fight.FightAction) && other.FightAction.Key == child.Fight.FightAction.Key)
                    {
                        //I should just compare the order of the draw pile actually.
                        other.Weight++;
                        return(other);
                    }
                    else
                    {
                        var ae = 4;
                    }
                }
                child.Parent      = intermediateNode;
                child.FightAction = child.Fight.FightAction;
                //child.fightaction still has its key
                intermediateNode.Randoms.Add(child);
            }
            else
            {
                child.Parent      = this;
                child.FightAction = child.Fight.FightAction;
                Choices.Add(child);
            }

            //Fight is over. Calc values.
            if (child.Fight.Status != FightStatus.Ongoing)
            {
                child.CalcValue();
            }

            return(child);
        }
コード例 #17
0
        /// <summary>
        /// when trying to add a child node, we check if there already is one for the same action.
        /// complexity: for random actions, we first add a choice then the child randoms.
        /// This searches for both the child + the child random and if not returns nothing.
        /// If the intermediate node *does* exist, but the appropriate random child isn't found,
        /// that will be detected in AddChild
        /// </summary>
        private FightNode FindDuplicate(FightAction action)
        {
            if (action.FightActionType == FightActionEnum.EnemyMove)
            {
                foreach (var r in Randoms)
                {
                    if (r.FightAction.IsEqual(action))
                    {
                        return(r);
                    }
                }
            }
            else if (Randoms.Count > 0)
            {
                throw new Exception("Should not happen");
            }
            else
            {
                if (action.FightActionType == FightActionEnum.PlayCard)
                {
                    if (Choices.Count > 0)
                    {
                        if (action.CardInstance.Card.Name == nameof(PommelStrike))
                        {
                            var ae = 4;
                        }
                    }
                }

                //there should only be one cardplay of this.
                var c = Choices.SingleOrDefault(el => el.FightAction.IsEqual(action));
                if (c == null)
                {
                    return(null);
                }
                if (c.FightAction.IsEqual(action))
                {
                    if (!action.Random) //the nonrandom case; you simply found a normal node that represents this state already.
                    {
                        return(c);
                    }
                    //e.g. we have played wildstrike before so we found it in the choices.
                    else
                    {
                        foreach (var r in c.Randoms)
                        {
                            if (r.FightAction.Key == action.Key)
                            {
                                return(r);
                            }
                            if (r.FightAction.Key == null)
                            {
                                throw new Exception("Should not happen");
                            }
                        }
                        //there is a node for playing this card, but no appropriate rchild.
                        return(null);
                    }
                }
            }

            return(null);
        }
コード例 #18
0
        /// <summary>
        /// From monster POV, player is the enemy.
        ///
        /// TODO: why not send a cardDescriptor, and have this method just find a matching card? would make external combinatorics easier.
        /// </summary>
        public void PlayCard(FightAction action, bool forceExhaust = false, bool newCard = false, IList <CardInstance> source = null)
        {
            var cardInstance = action.CardInstance;
            var cardTargets  = action.CardTargets;

            if (!PlayerTurn)
            {
                throw new Exception("Not your turn");
            }
            //get a copy since action was generated from an earlier version.
            if (source == null)
            {
                source = _Deck.GetHand;
            }
            cardInstance = Helpers.FindIdenticalCardInSource(source, cardInstance);
            var history = new List <string>();

            if (forceExhaust)
            {
                cardInstance.OverrideExhaust = true;
            }
            if (newCard)
            {
                //don't do checks; definitely playable, etc.
            }
            else
            {
                if (!cardInstance.Playable(_Deck.GetHand))
                {
                    throw new Exception("Trying to play unplayable card.");
                }
                if (cardInstance.EnergyCost() > _Player.Energy)
                {
                    throw new Exception("Trying to play too expensive card");
                }

                var ec = cardInstance.EnergyCost();
                _Player.Energy -= ec.En;
                _Deck.BeforePlayingCard(cardInstance);
            }

            IEntity target;

            switch (cardInstance.Card.TargetType)
            {
            case TargetType.Player:
                target = _Player;
                break;

            case TargetType.Enemy:
                target = _Enemies[0];
                break;

            default:
                throw new NotImplementedException();
            }

            //set the initial effect, or status.
            var ef = new EffectSet();

            cardInstance.Play(ef, _Player, _Enemies[0], cardTargets, _Deck, action.Key);

            //generate an effect containing all the changes that will happen.
            foreach (var si in _Player.StatusInstances)
            {
                var statusIsTargeted = _Player == target;
                si.Apply(cardInstance.Card, ef.PlayerEffect, ef.EnemyEffect, statusIsTargeted, true);
            }
            foreach (var si in _Enemies[0].StatusInstances)
            {
                var statusIsTargeted = _Enemies[0] == target;
                si.Apply(cardInstance.Card, ef.PlayerEffect, ef.EnemyEffect, statusIsTargeted, true);
            }

            foreach (var relic in _Player.Relics)
            {
                relic.CardPlayed(cardInstance.Card, ef, player: _Player, enemy: _Enemies[0]);
            }

            //relic effects apply first.

            //make sure to apply card effects before putting the just played card into discard, so it can't be drawn again by its own action.
            _Deck.AfterPlayingCard(cardInstance, ef, history);

            ApplyEffectSet(ef, _Player, _Enemies[0], history: history, ci: cardInstance);

            //reuse the same action.
            action.History = history;
            if (ef.HadRandomness)
            {
                action.Random = true;
                action.Key    = ef.Key;
            }
            AssignLastAction(action);
            _Deck.CardPlayCleanup();
        }
コード例 #19
0
        public void PlayCard(CardInstance ci, List <CardInstance> cardTargets = null, bool forceExhaust = false, bool newCard = false, IList <CardInstance> source = null)
        {
            var action = new FightAction(FightActionEnum.PlayCard, card: ci, cardTargets: cardTargets, hadRandomEffects: ci.Card.RandomEffects);

            PlayCard(action, forceExhaust: forceExhaust, newCard: newCard, source: source);
        }