public static void AddRange(this PowerHistory dest, PowerHistory src) { for (int i = 0; i < src.Full.Count; ++i) { dest.Add(src.Full[i]); } }
/// <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); }
/// <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; } }
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()); }
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); }
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); }
/// <summary> /// Part of the state machine. /// Runs when STATE = RUNNING && 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; }
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); }
//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); }
/// <summary> /// Part of the state machine. /// Runs when STATE = RUNNING && 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; }
/// <summary> /// Part of the state machine. /// Runs when STATE = RUNNING && 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; }
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(); } }
/// <summary> /// Part of the state machine. /// Runs when STATE = RUNNING && 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; }
/// <summary> /// Part of the state machine. /// Runs when STATE = RUNNING && 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()); } }
/// <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; }
// 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(); }