private Dictionary <Game, double> _search(Game game, ITreeActionWalker searcher, TestAction testAction) { return(RandomOutcomeSearch.Find( Game: game, SearchMode: searcher, Action: () => { // This is the action that shall be taken to build the tree switch (testAction) { case TestAction.BoomBot: game.CurrentPlayer.Board.First(t => t.Card.Id == "GVG_110t").Hit(1); break; case TestAction.ArcaneMissiles: game.CurrentPlayer.Hand.First(t => t.Card.Name == "Arcane Missiles").Play(); break; } } )); }
public void TestPartialArgumentSyntax() { // Arrange var ActionGraphs = new List <ActionGraph> { // The default Damage(RandomOpponentHealthyMinion, RandomAmount(1, 4)), // Swapped named arguments Damage(Amount: RandomAmount(1, 4), Targets: RandomOpponentHealthyMinion), // No arguments to Damage RandomOpponentHealthyMinion.Then(1).Then(4).Then(RandomAmount()).Then(Damage()), RandomOpponentHealthyMinion.Then(RandomAmount(1, 4)).Then(Damage()), // No Targets argument to Damage RandomOpponentHealthyMinion.Then(Damage(Amount: RandomAmount(1, 4))), RandomOpponentHealthyMinion.Then(Damage(Amount: ((ActionGraph)1).Then(4).Then(RandomAmount()))), // No Amount argument to damage RandomAmount(1, 4).Then(Damage(Targets: RandomOpponentHealthyMinion)), ((ActionGraph)1).Then(4).Then(RandomAmount()).Then(Damage(Targets: RandomOpponentHealthyMinion)) }; Cards.FromName("Arcane Missiles").Behaviour.Battlecry = Damage(RandomOpponentHealthyCharacter, 1) * 1; foreach (var graph in ActionGraphs) { // Arrange var game = _setupGame(7, 2, "Bloodfen Raptor"); // This converts the ActionGraph to a List<QueueAction> Cards.FromId("GVG_110t").Behaviour.Deathrattle = graph; // Act var count = RandomOutcomeSearch.Find(game, () => game.CurrentPlayer.Hand.First(t => t.Card.Name == "Arcane Missiles").Play()).Count; // Assert // 1 missile produces 30 game states if Boom Bot deathrattle works correctly Assert.AreEqual(30, count); } }
public void TestTreeUsage([Values(true, false)] bool ParallelClone) { Settings.ParallelClone = ParallelClone; var game = _setupGame(7, 2, "Bloodfen Raptor"); game.MaxMinionsOnBoard = 10; // Make a new tree with the game as the root var tree = new GameTree <ProbabilisticGameNode>(new ProbabilisticGameNode(game)); // Add arbitrary number of children int children = 3; var depth1Nodes = tree.RootNode.Branch(children).ToList(); // Check the correct number of games were cloned Assert.AreEqual(children, depth1Nodes.Count); Assert.AreEqual(children, tree.RootNode.Children.Count); // Assert all children have correct parent in both GameNode and Game foreach (var n in depth1Nodes) { Assert.AreSame(game, n.Parent.Game); Assert.AreSame(tree.RootNode, n.Parent); } // Check all child games are unique but exact clones var childGameIds = new List <int>(); foreach (var n in depth1Nodes) { Assert.False(childGameIds.Contains(n.Game.GameId)); Assert.True(game.EquivalentTo(n.Game)); childGameIds.Add(n.Game.GameId); } // Get all games from nodes var depth1Games = depth1Nodes.Select(n => n.Game).ToList(); // Do something different on each child depth1Games[0].CurrentPlayer.Give("Flame Juggler").Play(); depth1Games[1].CurrentPlayer.Give("Arcane Missiles").Play(); depth1Games[2].CurrentPlayer.Give("Whirlwind").Play(); // Check every game is different var childHashes = new List <int>(); foreach (var g in depth1Games) { Assert.False(childHashes.Contains(g.FuzzyGameHash)); childHashes.Add(g.FuzzyGameHash); } // Do a random action on the first game in depth 1 and add all possible outcomes as children Minion FirstBoomBot = depth1Games[0].CurrentPlayer.Board.First(x => x.Card.Id == "GVG_110t"); var boomBotResults = RandomOutcomeSearch.Find(depth1Games[0], () => FirstBoomBot.Hit(1)); var depth2Nodes = depth1Nodes[0].AddChildren(boomBotResults).ToList(); // Check the correct number of games were cloned Assert.AreEqual(boomBotResults.Count, depth2Nodes.Count); Assert.AreEqual(boomBotResults.Count, depth1Nodes[0].Children.Count); // Assert all children have correct parent in both GameNode and Game foreach (var n in depth2Nodes) { Assert.AreSame(depth1Games[0], n.Parent.Game); Assert.AreSame(depth1Nodes[0], n.Parent); } }
static async Task DepthFirstAsync() { // Create initial game state Console.WriteLine("Initializing game state..."); var game = new Game(HeroClass.Druid, HeroClass.Druid, PowerHistory: true); game.Start(FirstPlayer: 1, SkipMulligan: true); for (int i = 0; i < MaxMinions - NumBoomBots; i++) { game.CurrentPlayer.Give(FillMinion).Play(); } for (int i = 0; i < NumBoomBots; i++) { game.CurrentPlayer.Give("GVG_110t").Play(); } game.EndTurn(); for (int i = 0; i < MaxMinions - NumBoomBots; i++) { game.CurrentPlayer.Give(FillMinion).Play(); } for (int i = 0; i < NumBoomBots; i++) { game.CurrentPlayer.Give("GVG_110t").Play(); } var ArcaneMissiles = game.CurrentPlayer.Give("Arcane Missiles"); Cards.FromName("Arcane Missiles").Behaviour.Battlecry = Damage(RandomOpponentHealthyCharacter, 1) * 2; Cards.FromId("GVG_110t").Behaviour.Deathrattle = Damage(RandomOpponentHealthyMinion, RandomAmount(1, 4)); // Start timing var sw = new Stopwatch(); sw.Start(); // Start the search going on another thread and retrieve its task handle var treeTask = RandomOutcomeSearch.BuildAsync( Game: game, SearchMode: new DepthFirstActionWalker(), Action: () => { if (BoomBotTest) { var BoomBot = game.CurrentPlayer.Board.First(t => t.Card.Id == "GVG_110t"); BoomBot.Hit(1); } if (ArcaneMissilesTest) { ArcaneMissiles.Play(); } } ); // Now we can do other stuff until our tree search results are ready! while (!treeTask.IsCompleted) { Console.Write("."); await Task.Delay(100); } // Get the results var tree = treeTask.Result; Console.WriteLine(""); // Print benchmark results Console.WriteLine("{0} branches in {1}ms", tree.NodeCount, sw.ElapsedMilliseconds); Console.WriteLine("{0} intermediate clones pruned ({1} unique branches kept)", tree.NodeCount - tree.LeafNodeCount, tree.LeafNodeCount); var uniqueGames = tree.GetUniqueGames(); sw.Stop(); foreach (var kv in uniqueGames) { Console.WriteLine(Math.Round(kv.Value * 100, 2) + "%: "); Console.WriteLine("{0:s}", kv.Key); } Console.WriteLine("{0} unique games found in {1}ms", uniqueGames.Count, sw.ElapsedMilliseconds); }
static void Main(string[] args) { for (int missiles = 1; missiles <= 6; missiles++) { // Create initial game state Console.WriteLine("Initializing game state for {0} missiles test...", missiles); var game = new Game(HeroClass.Druid, HeroClass.Druid, PowerHistory: true); game.Start(FirstPlayer: 1, SkipMulligan: true); for (int i = 0; i < MaxMinions - NumBoomBots; i++) { game.CurrentPlayer.Give(FillMinion).Play(); } for (int i = 0; i < NumBoomBots; i++) { game.CurrentPlayer.Give("GVG_110t").Play(); } game.EndTurn(); for (int i = 0; i < MaxMinions - NumBoomBots; i++) { game.CurrentPlayer.Give(FillMinion).Play(); } for (int i = 0; i < NumBoomBots; i++) { game.CurrentPlayer.Give("GVG_110t").Play(); } var ArcaneMissiles = game.CurrentPlayer.Give("Arcane Missiles"); Cards.FromName("Arcane Missiles").Behaviour.Battlecry = Damage(RandomOpponentHealthyCharacter, 1) * missiles; Cards.FromId("GVG_110t").Behaviour.Deathrattle = Damage(RandomOpponentHealthyMinion, RandomAmount(1, 4)); // Start timing var sw = new Stopwatch(); sw.Start(); // Create first layer of nodes underneath the root node // and add them to the search queue, then do breadth-first search var tree = RandomOutcomeSearch.Build( Game: game, SearchMode: new BreadthFirstActionWalker(), Action: () => { if (BoomBotTest) { var BoomBot = game.CurrentPlayer.Board.First(t => t.Card.Id == "GVG_110t"); BoomBot.Hit(1); } if (ArcaneMissilesTest) { ArcaneMissiles.Play(); } } ); // Print benchmark results Console.WriteLine("{0} branches in {1}ms", tree.NodeCount, sw.ElapsedMilliseconds); Console.WriteLine("{0} intermediate clones pruned ({1} unique branches kept)", tree.NodeCount - tree.LeafNodeCount, tree.LeafNodeCount); var uniqueGames = tree.GetUniqueGames(); sw.Stop(); Console.WriteLine("{0} unique games found in {1}ms", uniqueGames.Count, sw.ElapsedMilliseconds); Console.WriteLine(""); } }
static void Main(string[] args) { // Create initial game state Console.WriteLine("Initializing game state..."); var game = new Game(HeroClass.Druid, HeroClass.Druid, PowerHistory: true); game.Player1.Deck.Fill(); game.Player2.Deck.Fill(); game.Start(FirstPlayer: 1, SkipMulligan: true); for (int i = 0; i < MaxMinions - NumBoomBots; i++) { game.CurrentPlayer.Give(FillMinion).Play(); } for (int i = 0; i < NumBoomBots; i++) { game.CurrentPlayer.Give("GVG_110t").Play(); } game.EndTurn(); for (int i = 0; i < MaxMinions - NumBoomBots; i++) { game.CurrentPlayer.Give(FillMinion).Play(); } for (int i = 0; i < NumBoomBots; i++) { game.CurrentPlayer.Give("GVG_110t").Play(); } Cards.FromName("Arcane Missiles").Behaviour.Battlecry = Damage(RandomOpponentHealthyCharacter, 1) * 2; Cards.FromId("GVG_110t").Behaviour.Deathrattle = Damage(RandomOpponentHealthyMinion, RandomAmount(1, 4)); // Start timing var sw = new Stopwatch(); sw.Start(); // Build search tree Console.WriteLine("Building search tree..."); var tree = RandomOutcomeSearch.Build( Game: game, SearchMode: new NaiveActionWalker(), Action: () => { if (BoomBotTest) { var BoomBot = game.CurrentPlayer.Board.First(t => t.Card.Id == "GVG_110t"); BoomBot.Hit(1); } if (ArcaneMissilesTest) { game.CurrentPlayer.Give("Arcane Missiles").Play(); } } ); // Print intermediate results Console.WriteLine("{0} branches in {1}ms", tree.NodeCount, sw.ElapsedMilliseconds); Console.WriteLine("{0} leaf node games kept", tree.LeafNodeCount); Console.WriteLine(""); // Find unique games Console.WriteLine("Finding unique game states..."); var uniqueGames = tree.GetUniqueGames(); sw.Stop(); foreach (var kv in uniqueGames) { Console.WriteLine(Math.Round(kv.Value * 100, 2) + "%: "); Console.WriteLine("{0:s}", kv.Key); } Console.WriteLine("{0} unique games found in {1}ms", uniqueGames.Count, sw.ElapsedMilliseconds); }