Example #1
0
        /// <summary>Initializes a new instance of the <see cref="Game"/> class.</summary>
        /// <param name="gameConfig">The game configuration.</param>
        /// <param name="setupHeroes"></param>
        public Game(GameConfig gameConfig, bool setupHeroes = true)
            : base(null, Card.CardGame, new Dictionary <GameTag, int>
        {
            [GameTag.ENTITY_ID] = GAME_ENTITYID,
            [GameTag.ZONE]      = (int)Enums.Zone.PLAY,
            [GameTag.CARDTYPE]  = (int)CardType.GAME
        })
        {
            _gameConfig       = gameConfig;
            Game              = this;
            GamesEventManager = new GameEventManager(this);

            _players[0] = new Controller(this, gameConfig.Player1Name, 1, 2);
            _players[1] = new Controller(this, gameConfig.Player2Name, 2, 3);

            // add power history create game
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.CreateGame(this, _players));
            }

            if (setupHeroes)
            {
                _players[0].AddHeroAndPower(Cards.HeroCard(gameConfig.Player1HeroClass));
                _players[1].AddHeroAndPower(Cards.HeroCard(gameConfig.Player2HeroClass));
            }

            TaskQueue = new TaskQueue(this);
            TaskStack = new TaskStack(this);
        }
Example #2
0
 public static void Clear(this PowerHistory p)
 {
     p.Full.Clear();
     //p.Full.Capacity = 0;
     p.Last.Clear();
     //p.Last.Capacity = 0;
 }
Example #3
0
        internal static PowerHistory CopyPowerHistory(this HearthNode h)
        {
            var copy = new PowerHistory();

            //copy.AddRange(h.PowerHistory);
            return(copy);
        }
Example #4
0
 public static void ForEach(this PowerHistory h, Action <IPowerHistoryEntry> a)
 {
     for (int i = 0; i < h.Full.Count; ++i)
     {
         a(h.Full[i]);
     }
 }
Example #5
0
 public static void AddRange(this PowerHistory dest, PowerHistory src)
 {
     for (int i = 0; i < src.Full.Count; ++i)
     {
         dest.Add(src.Full[i]);
     }
 }
Example #6
0
 // Perform a fuzzy equivalence between two game states
 public bool EquivalentTo(Game game)
 {
     if (Settings.UseGameHashForEquality)
     {
         return(FuzzyGameHash == game.FuzzyGameHash);
     }
     return(PowerHistory.EquivalentTo(game.PowerHistory));
 }
Example #7
0
        /// <summary>Process the specified task.
        /// The game will execute the desired task and all effects coupled either
        /// directly or indirectly in synchronous manner.
        ///
        /// Call <see cref="Controller.Options(bool)"/> on the <see cref="CurrentPlayer"/>
        /// instance for tasks which are accepted as arguments.
        /// After this method returns, check <see cref="Controller.Options(bool)"/>
        /// again until only <see cref="EndTurnTask"/> remains, which will
        /// start the turn of <see cref="CurrentOpponent"/>.
        /// </summary>
        /// <param name="gameTask">The game task to execute.</param>
        public void Process(PlayerTask gameTask)
        {
            // start with no splits ...
            Splits = new List <Game>();

            Log(LogLevel.INFO, BlockType.PLAY, "Game", gameTask.FullPrint());

            // clear last power history
            PowerHistory.Last.Clear();

            // make sure that we only use task for this game ...
            gameTask.Game = this;
            gameTask.Process();

            // add enchantment and buff tag changes
            if (History)
            {
                Enchants.ForEach(p =>
                                 p.Effects.Keys.ToList().ForEach(t =>
                                                                 IdEntityDic.Values.ToList().ForEach(o =>
                                                                                                     PowerHistory.Add(PowerHistoryBuilder.TagChange(o.Id, t, o[t])))));

                foreach (var controller in _players)
                {
                    controller.Hero.Enchants.ForEach(p =>
                                                     p.Effects.Keys.ToList().ForEach(t =>
                                                                                     PowerHistory.Add(PowerHistoryBuilder.TagChange(Game.CurrentPlayer.Hero.Id, t, Game.CurrentPlayer.Hero[t]))));

                    //CurrentPlayer.Hero.Weapon?.Enchants.ForEach(p => p.IsEnabled());
                    //CurrentPlayer.Hero.Weapon?.Triggers.ForEach(p => p.IsEnabled());
                    //CurrentOpponent.Hero.Weapon?.Enchants.ForEach(p => p.IsEnabled());
                    //CurrentOpponent.Hero.Weapon?.Triggers.ForEach(p => p.IsEnabled());

                    controller.ControlledZones.Where(z => z != null).ToList().ForEach(z =>
                                                                                      z.Enchants.ForEach(p =>
                                                                                                         p.Effects.Keys.ToList().ForEach(t =>
                                                                                                                                         z.GetAll.ForEach(o =>
                                                                                                                                                          PowerHistory.Add(PowerHistoryBuilder.TagChange(o.Id, t, o[t]))))));
                }

                Characters.ForEach(c =>
                                   c.Enchants.ForEach(p =>
                                                      p.Effects.Keys.ToList().ForEach(t =>
                                                                                      PowerHistory.Add(PowerHistoryBuilder.TagChange(c.Id, t, c[t])))));
            }

            if (Splitting)
            {
                var finalSplits = SplitNode.GetSolutions(this, 10, 10000);
                Dump("Split", $"found {finalSplits.Count} final splits of {finalSplits.Sum(p => p.SameState + 1)}!");
                finalSplits.GroupBy(p => p.SameState)
                .Select(i => new { Word = i.Key, Count = i.Count() })
                .ToList().ForEach(p => Dump("Split", $" {p.Count},  with {p.Word} same states"));
                FinalSplits = finalSplits;
            }
        }
Example #8
0
        public static int Count(this PowerHistory p)
        {
            int count = 0;

            for (int i = 0; i < p.Full.Count; ++i)
            {
                ++count;
            }

            return(count);
        }
Example #9
0
        public Task <ActionResult> RunActionBlockAsync(BlockType Type, IEntity Source, List <QueueAction> Actions, IEntity Target = null, int Index = -2)
        {
#if _GAME_DEBUG
            DebugLog.WriteLine("Game " + GameId + ": Running " + Type + " for " + Source.ShortDescription + " => " + (Target?.ShortDescription ?? "no target"));
#endif
            int index = Index != -2 ? Index : (Type == BlockType.POWER || Type == BlockType.ATTACK ? -1 : 0);
            var block = new BlockStart(Type, Source, Target, index);
            PowerHistory?.Add(block);
            ActionQueue.StartBlock(Source, Actions, block);
            return(ActionQueue.ProcessBlockAsync());
        }
Example #10
0
        public static bool Remove(this PowerHistory powerHistory, Func <IPowerHistoryEntry, bool> p)
        {
            int count = powerHistory.Full.Count;

            var copy = new PowerHistory();

            powerHistory.Where(e => !p(e)).ForEach(e => copy.Add(e));

            powerHistory.Clear();
            powerHistory.AddRange(copy);
            return(count > powerHistory.Full.Count);
        }
Example #11
0
        public static PowerHistory Where(this PowerHistory ph, Func <IPowerHistoryEntry, bool> p)
        {
            var where = new PowerHistory();
            for (int i = 0; i < ph.Full.Count; ++i)
            {
                if (p(ph.Full[i]))
                {
                    where.Add(ph.Full[i]);
                }
            }

            return(where);
        }
Example #12
0
        /// <summary>
        /// Writes this PowerHistory to the specified file
        /// </summary>
        /// <param name="powerHistory"></param>
        /// <param name="fileName"></param>
        /// <param name="root"></param>
        /// <param name="end"></param>
        public static void Dump(this PowerHistory powerHistory, string fileName, bool root = false, bool end = false)
        {
            var str = new StringBuilder();
            var b   = new BinaryWriter(File.Open(fileName, mode: root ? FileMode.Create : FileMode.Append), Encoding.UTF8);

            powerHistory.Full.ForEach(e => str.Append(e.Print(true)));
            if (end)
            {
                str.AppendLine("  </Game>");
                str.AppendLine("</HSReplay>");
            }
            b.Write(str.ToString());
            b.Close();
        }
Example #13
0
        public static void AddRangeFirst(this PowerHistory dest, PowerHistory src)
        {
            var tmp = new PowerHistory();

            tmp.AddRange(src);

            for (int i = 0; i < dest.Full.Count; ++i)
            {
                tmp.Add(dest.Full[i]);
            }

            dest.Clear();
            dest.AddRange(tmp);
        }
Example #14
0
        /// <summary>
        /// Part of the state machine.
        /// Runs when STATE = RUNNING &amp;&amp; NEXTSTEP = MAIN_READY
        /// </summary>
        public void MainReady()
        {
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockStart(BlockType.TRIGGER, CurrentPlayer.Id, "", 1, 0));
            }

            Characters.ForEach(p =>
            {
                p.NumTurnsInPlay++;
                p.NumAttacksThisTurn = 0;
            });

            Heroes.ForEach(p =>
            {
                p.Controller.NumCardsDrawnThisTurn              = 0;
                p.Controller.NumCardsPlayedThisTurn             = 0;
                p.Controller.NumMinionsPlayedThisTurn           = 0;
                p.Controller.NumOptionsPlayedThisTurn           = 0;
                p.Controller.NumFriendlyMinionsThatDiedThisTurn = 0;
            });

            CurrentPlayer.Hero.IsExhausted       = false;
            CurrentPlayer.Hero.Power.IsExhausted = false;
            foreach (var e in CurrentPlayer.BoardZone)
            {
                e.IsSummoned  = false;
                e.IsExhausted = false;
            }

            // De-activate combo buff
            CurrentPlayer.IsComboActive = false;

            CurrentPlayer.NumMinionsPlayerKilledThisTurn         = 0;
            CurrentOpponent.NumMinionsPlayerKilledThisTurn       = 0;
            CurrentPlayer.NumFriendlyMinionsThatAttackedThisTurn = 0;
            NumMinionsKilledThisTurn = 0;
            CurrentPlayer.HeroPowerActivationsThisTurn = 0;

            CurrentPlayer.NumElementalsPlayedLastTurn = CurrentPlayer.NumElementalsPlayedThisTurn;
            CurrentPlayer.NumElementalsPlayedThisTurn = 0;

            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockEnd());
            }

            // set next step
            NextStep = Step.MAIN_START_TRIGGERS;
        }
Example #15
0
        /// <summary>
        /// Part of the state machine.
        /// Runs when STATE = RUNNING &amp;&amp; NEXTSTEP = MAIN_DRAW
        /// </summary>
        public void MainDraw()
        {
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockStart(BlockType.TRIGGER, CurrentPlayer.Id, "", 0, 0));                 // turn start effect
            }
            //CurrentPlayer.NumCardsToDraw = 1;
            Generic.Draw(CurrentPlayer);

            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockEnd());
            }

            // set next step
            NextStep = Step.MAIN_START;
        }
Example #16
0
        internal void OnBlockEmpty(BlockStart Block)
        {
#if _GAME_DEBUG
            DebugLog.WriteLine("Game " + GameId + ": Action block " + Block.Type + " for " + Entities[Block.Source].ShortDescription + " resolved");
#endif
            PowerHistory?.Add(new BlockEnd(Block.Type));

            if (Block.Type == BlockType.TRIGGER)
            {
                ActiveTriggers.TriggerResolved();
            }

            // Post-ATTACK or Post-final TRIGGER DEATHS block
            if (Block.Type == BlockType.ATTACK || (Block.Type == BlockType.TRIGGER && ActiveTriggers.QueuedTriggersCount == 0))
            {
                RunDeathCreationStepIfNeeded();
            }
        }
Example #17
0
        /// <summary>
        /// Part of the state machine.
        /// Runs when STATE = RUNNING &amp;&amp; NEXTSTEP = MAIN_START_TRIGGERS
        /// </summary>
        public void MainStartTriggers()
        {
            CurrentPlayer.TurnStart = true;
            DeathProcessingAndAuraUpdate();

            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockStart(BlockType.TRIGGER, CurrentPlayer.Id, "", 8, 0));
            }

            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockEnd());
            }

            // set next step
            NextStep = Step.MAIN_RESOURCE;
        }
Example #18
0
        /// <summary>
        /// Part of the state machine.
        /// Runs when STATE = RUNNING &amp;&amp; NEXTSTEP = BEGIN_DRAW
        /// </summary>
        public void BeginDraw()
        {
            Log(LogLevel.VERBOSE, BlockType.PLAY, "Game", $"Begin Draw.");

            //FirstPlayer.NumCardsToDraw = 3;
            //FirstPlayer.Opponent.NumCardsToDraw = 4;

            _players.ToList().ForEach(p =>
            {
                // quest draw if there is
                var quest = p.DeckZone.GetAll.Where(q => q is Spell && ((Spell)q).IsQuest).FirstOrDefault();
                Generic.Draw(p, quest != null ? quest : null);
                Generic.Draw(p);
                Generic.Draw(p);

                if (p != FirstPlayer)
                {
                    // 4th card for second player
                    Generic.Draw(p);

                    var coin = FromCard(FirstPlayer.Opponent, Cards.FromId("GAME_005"), new Dictionary <GameTag, int>()
                    {
                        [GameTag.ZONE]     = (int)Enums.Zone.HAND,
                        [GameTag.CARDTYPE] = (int)CardType.SPELL,
                        [GameTag.CREATOR]  = FirstPlayer.Opponent.PlayerId
                    });
                    Generic.AddHandPhase(FirstPlayer.Opponent, coin);
                }

                p.NumTurnsLeft = 1;
            });

            Player1.TimeOut = 75;
            Player2.TimeOut = 75;

            // ending mulligan draw block
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockEnd());
            }

            NextStep = _gameConfig.SkipMulligan ? Step.MAIN_BEGIN : Step.BEGIN_MULLIGAN;
        }
Example #19
0
        /// <summary>
        /// Part of the state machine.
        /// Runs when STATE = RUNNING &amp;&amp; NEXTSTEP = BEGIN_MULLIGAN
        /// </summary>
        public void BeginMulligan()
        {
            Log(LogLevel.VERBOSE, BlockType.PLAY, "Game", $"Begin Mulligan.");

            // starting mulligan draw block
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockStart(BlockType.TRIGGER, this.Id, "", -1, 0));
            }

            Player1.MulliganState = Mulligan.INPUT;
            Player2.MulliganState = Mulligan.INPUT;

            Generic.CreateChoice.Invoke(Player1, this, ChoiceType.MULLIGAN, ChoiceAction.HAND, Player1.HandZone.Select(p => p.Id).ToList());
            Generic.CreateChoice.Invoke(Player2, this, ChoiceType.MULLIGAN, ChoiceAction.HAND, Player2.HandZone.Select(p => p.Id).ToList());

            // ending mulligan draw block
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockEnd());
            }
        }
Example #20
0
        private static void StateTransition(Game game, PowerHistory history)
        {
            foreach (IPowerHistoryEntry item in history.Last)
            {
                switch (item)
                {
                case PowerHistoryBlockEnd powerHistoryBlockEnd:
                    break;

                case PowerHistoryBlockStart powerHistoryBlockStart:
                    break;

                case PowerHistoryCreateGame powerHistoryCreateGame:
                    break;

                case PowerHistoryFullEntity powerHistoryFullEntity:
                    FullEntity(game, powerHistoryFullEntity);
                    break;

                case PowerHistoryHideEntity powerHistoryHideEntity:
                    break;

                case PowerHistoryMetaData powerHistoryMetaData:
                    break;

                case PowerHistoryShowEntity powerHistoryShowEntity:
                    break;

                case PowerHistoryTagChange powerHistoryTagChange:
                    TagChange(game, powerHistoryTagChange);
                    break;

                case PowerHistoryChangeEntity powerHistoryChangeEntity:
                    ChangeEntity(game, powerHistoryChangeEntity);
                    break;
                }
            }
        }
Example #21
0
        /// <summary>
        /// Part of the state machine.
        /// Runs when STATE = RUNNING.
        /// </summary>
        public void StartGame()
        {
            Log(LogLevel.INFO, BlockType.PLAY, "Game", "Starting new game now!");

            // setting up the decks ...
            _gameConfig.Player1Deck?.ForEach(p => Entity.FromCard(Player1, p, null, Player1.DeckZone));
            _gameConfig.Player2Deck?.ForEach(p => Entity.FromCard(Player2, p, null, Player2.DeckZone));
            if (_gameConfig.FillDecks)
            {
                Player1.DeckZone.Fill();
                Player2.DeckZone.Fill();
            }

            // set gamestats
            State = State.RUNNING;
            _players.ToList().ForEach(p => p.PlayState = PlayState.PLAYING);

            // starting mulligan draw block
            if (History)
            {
                PowerHistory.Add(PowerHistoryBuilder.BlockStart(BlockType.TRIGGER, this.Id, "", -1, 0));
            }

            // getting first player
            FirstPlayer = _gameConfig.StartPlayer < 0
                                ? _players[Util.Random.Next(0, 2)]
                                : _players[_gameConfig.StartPlayer - 1];
            CurrentPlayer = FirstPlayer;

            Log(LogLevel.INFO, BlockType.PLAY, "Game", $"Starting Player is {CurrentPlayer.Name}.");

            // first turn
            Turn = 1;

            // set next step
            NextStep = Step.BEGIN_FIRST;
        }
Example #22
0
        //public static string PrintBoard(this HearthNode state, XmlWriter x)
        //{
        //	var str = new StringBuilder();

        //	x.WriteTurn(state.Game);

        //	str.AppendLine("------------");
        //	str.AppendLine($"| Turn {state.Game.Turn} |");
        //	str.AppendLine("------------");
        //	str.AppendLine("-----------------------------------------------------------------------------------------------------");
        //	str.AppendLine(state.Game.FullPrint() + "-----------------------------------------------------------------------------------------------------");
        //	str.AppendLine($"{(state.Game.CurrentPlayer == state.Game.Player1 ? $"{state.Game.Player1.Hero.Card.Name}" : $"{state.Game.Player2.Hero.Card.Name}")} is thinking...");

        //	return str.ToString();
        //}

        /// <summary>
        /// Sorts this PowerHistory to match the HSReplay xml structure
        /// </summary>
        /// <param name="powerHistory"></param>
        public static void HSReplaySort(this PowerHistory powerHistory)
        {
            var first  = new PowerHistory();
            var second = new PowerHistory();

            foreach (IPowerHistoryEntry e in powerHistory.Full)
            {
                if (e.PowerType == PowerType.CREATE_GAME)
                {
                    first.Add(e);
                }

                else if (e.PowerType == PowerType.FULL_ENTITY)
                {
                    var fe = (PowerHistoryFullEntity)e;
                    if (fe.Entity.Tags[GameTag.CONTROLLER] == 1)
                    {
                        first.Add(e);
                    }

                    else if (fe.Entity.Tags[GameTag.CONTROLLER] == 2)
                    {
                        second.Add(e);
                    }
                }
            }

            powerHistory.Remove(e => first.Full.Contains(e) || second.Full.Contains(e));

            first.AddRange(second);
            second.Clear();
            second.AddRange(powerHistory);

            powerHistory.Clear();
            powerHistory.AddRange(first);
            powerHistory.AddRange(second);
        }
Example #23
0
        private void DispatcherHandler(Packet packet)
        {
            int le = packet.Ethernet.IpV4.Tcp.Http.Length;
            var d  = packet.Buffer.Reverse().Take(le).Reverse().ToArray();

            PegasusPacket x = new PegasusPacket();

            try
            {
                x.Decode(d, 0, d.Length);
            }
            catch
            {
            }
            finally
            {
                ConnectAPI.PacketDecoder decoder;

                if (s_packetDecoders.TryGetValue(x.Type, out decoder))
                {
                    PegasusPacket item = decoder.HandlePacket(x);

                    var bod = item.Body;
                    var typ = item.Type;

                    if (typ == 19)
                    {
                        PowerHistory             history = (PowerHistory)bod;
                        IList <PowerHistoryData> listy   = history.ListList;
                        foreach (var phd in listy)
                        {
                            if (phd.HasShowEntity)
                            {
                                try
                                {
                                    entity_dictionary.Add(phd.ShowEntity.Entity, phd.ShowEntity.Name);
                                }
                                catch
                                {
                                }
                            }
                            else if (phd.HasPowerStart)
                            {
                                if (phd.PowerStart.Type == PegasusGame.PowerHistoryStart.Types.Type.PLAY)
                                {
                                    if (entity_dictionary.TryGetValue(phd.PowerStart.Source, out cardid))
                                    {
                                        string cardname = CardList.List.Find(r => r.ID == cardid).Name;
                                        choose_row(cardname);
                                    }
                                }
                            }
                        }
                    }

                    else
                    {
                        //Console.WriteLine(x.Body.ToString());
                    }
                }
            }
        }
Example #24
0
 public static void Reverse(this PowerHistory p)
 {
     p.Full.Reverse();
 }
Example #25
0
 public static IPowerHistoryEntry Pop(this PowerHistory powerHistory, IPowerHistoryEntry e)
 {
     powerHistory.Full.Remove(e);
     powerHistory.Last.Remove(e);
     return(e);
 }
Example #26
0
        public string ToString(string format, IFormatProvider formatProvider)
        {
            if (format == null)
            {
                format = "G";
            }

            if (formatProvider != null)
            {
                ICustomFormatter formatter = formatProvider.GetFormat(this.GetType()) as ICustomFormatter;
                if (formatter != null)
                {
                    return(formatter.Format(format, this, formatProvider));
                }
            }

            string s = string.Format("Game hash: {0:x8}", FuzzyGameHash) + "\r\n";

            switch (format)
            {
            case "G":
                s += "Board state: ";
                var players = new List <Player> {
                    Player1, Player2
                };
                foreach (var player in players)
                {
                    s += "Player " + player.Card.Id + " - ";
                    s += "HAND: ";
                    foreach (var entity in player.Hand)
                    {
                        s += entity.ToString() + ", ";
                    }
                    s += "PLAY: ";
                    foreach (var entity in player.Board)
                    {
                        s += entity.ToString() + ", ";
                    }
                }
                s = s.Substring(0, s.Length - 2) + "\nPower log: ";
                foreach (var item in PowerHistory)
                {
                    s += item + "\n";
                }
                return(s);

            case "S":
                s += "Player 1 (health " + Player1.Hero.Health + "): ";
                foreach (var e in Player1.Board)
                {
                    s += "[" + e.ZonePosition + ":" + e.Card.AbbrieviatedName + "](" + e.Damage + ") ";
                }
                s += "\r\nPlayer 2 (health " + Player2.Hero.Health + "): ";
                foreach (var e in Player2.Board)
                {
                    s += "[" + e.ZonePosition + ":" + e.Card.AbbrieviatedName + "](" + e.Damage + ") ";
                }
                s += "\r\n";
                if (PowerHistory != null)
                {
                    foreach (var pa in PowerHistory.Skip(PowerHistory.Count() - 20))
                    {
                        s += pa + "\r\n";
                    }
                }
                return(s);

            case "s":
                s += "Player 1 (health " + Player1.Hero.Health + "): ";
                foreach (var e in Player1.Board)
                {
                    s += "[" + e.ZonePosition + ":" + e.Card.AbbrieviatedName + "](" + e.Damage + ") ";
                }
                s += "\r\nPlayer 2 (health " + Player2.Hero.Health + "): ";
                foreach (var e in Player2.Board)
                {
                    s += "[" + e.ZonePosition + ":" + e.Card.AbbrieviatedName + "](" + e.Damage + ") ";
                }
                s += "\r\n";
                return(s);

            default:
                return("Game (no format specified)");
            }
        }
Example #27
0
        // Death checking phase
        internal void RunDeathCreationStepIfNeeded()
        {
#if _GAME_DEBUG
            DebugLog.WriteLine("Game " + GameId + ": Checking for death creation step");
#endif
            if (_deathCheckQueue.Count == 0)
            {
                return;
            }

            // We only have to check health because ToBeDestroyed cannot be reversed without the minion leaving play
            var dyingEntities =
                _deathCheckQueue.Where(
                    id => ((ICharacter)Entities[id]).MortallyWounded && Entities[id].Zone.Type == Brimstone.Zone.PLAY)
                .Select(id => (ICharacter)Entities[id]).ToList();

            if (dyingEntities.Count > 0)
            {
#if _GAME_DEBUG
                DebugLog.WriteLine("Game " + GameId + ": Running death creation step");
#endif
                PowerHistory?.Add(new BlockStart(BlockType.DEATHS, this));
            }

            // Death Creation Step
            bool gameEnd = false;
            foreach (var e in dyingEntities)
            {
#if _ACTIONS_DEBUG
                DebugLog.WriteLine("Game {0}: {1} dies", GameId, e.ShortDescription);
#endif
                // Queue deathrattles and OnDeath triggers before moving mortally wounded minion to graveyard
                // (they will be executed after the zone move)
                // TODO: Test that each queue resolves before the next one populates. If it doesn't, we can make queue populating lazy
                if (e is Minion)
                {
                    ActiveTriggers.Queue(TriggerType.OnDeath, e);
                }

                // NumMinionsPlayerKilledThisTurn seems to be the number of minions that died this turn
                // regardless of who or what killed what
                e.Controller.NumMinionsPlayerKilledThisTurn++;
                NumMinionsKilledThisTurn++;
                e.IsExhausted = false;

                // Move dead character to graveyard
                e.Zone = e.Controller.Graveyard;

                // TODO: Reset all minion tags to default
                if (e is Minion)
                {
                    var minion = ((Minion)e);
                    minion.Damage = 0;
                }

                // Hero death
                if (e is Hero)
                {
                    e.Controller.PlayState = PlayState.LOSING;
                    gameEnd = true;
                }
            }
            if (gameEnd)
            {
                GameWon();
            }

            if (dyingEntities.Count > 0)
            {
                PowerHistory?.Add(new BlockEnd(BlockType.DEATHS));
            }
            _deathCheckQueue.Clear();
        }