protected async Task replaceRandomAmountParallel(ActionQueue q, QueueActionEventArgs e) { // Choosing a random value (damage amount in this case) // Clone and start processing for every possibility #if _TREE_DEBUG DebugLog.WriteLine(""); DebugLog.WriteLine("--> Depth: " + e.Game.Depth); #endif double perItemWeight = 1.0 / ((e.Args[RandomAmount.MAX] - e.Args[RandomAmount.MIN]) + 1); await Task.WhenAll( Enumerable.Range(e.Args[RandomAmount.MIN], (e.Args[RandomAmount.MAX] - e.Args[RandomAmount.MIN]) + 1).Select(i => Task.Run(async() => { // When cloning occurs, RandomAmount has been pulled from the action queue, // so we can just insert a fixed number at the start of the queue and restart the queue // to effectively replace it var clonedNode = ((ProbabilisticGameNode)e.UserData).Branch(perItemWeight); NodeCount++; clonedNode.Game.ActionQueue.StackPush(i); await clonedNode.Game.ActionQueue.ProcessAllAsync(clonedNode); searcher.Visitor(clonedNode, this, e); }) ) ); #if _TREE_DEBUG DebugLog.WriteLine("<-- Depth: " + e.Game.Depth); DebugLog.WriteLine(""); #endif }
protected async Task replaceRandomChoiceParallel(ActionQueue q, QueueActionEventArgs e) { // Choosing a random entity (minion in this case) // Clone and start processing for every possibility #if _TREE_DEBUG DebugLog.WriteLine(""); DebugLog.WriteLine("--> Depth: " + e.Game.Depth); #endif double perItemWeight = 1.0 / e.Args[RandomChoice.ENTITIES].Count(); await Task.WhenAll( e.Args[RandomChoice.ENTITIES].Select(entity => Task.Run(async() => { // When cloning occurs, RandomChoice has been pulled from the action queue, // so we can just insert a fixed item at the start of the queue and restart the queue // to effectively replace it var clonedNode = ((ProbabilisticGameNode)e.UserData).Branch(perItemWeight); NodeCount++; clonedNode.Game.ActionQueue.StackPush((Entity)clonedNode.Game.Entities[entity.Id]); await clonedNode.Game.ActionQueue.ProcessAllAsync(clonedNode); searcher.Visitor(clonedNode, this, e); }) ) ); #if _TREE_DEBUG DebugLog.WriteLine("<-- Depth: " + e.Game.Depth); DebugLog.WriteLine(""); #endif }
protected Task replaceRandomAmount(ActionQueue q, QueueActionEventArgs e) { // Choosing a random value (damage amount in this case) // Clone and start processing for every possibility #if _TREE_DEBUG DebugLog.WriteLine(""); DebugLog.WriteLine("--> Depth: " + e.Game.Depth); #endif double perItemWeight = 1.0 / ((e.Args[RandomAmount.MAX] - e.Args[RandomAmount.MIN]) + 1); for (int i = e.Args[RandomAmount.MIN]; i <= e.Args[RandomAmount.MAX]; i++) { // When cloning occurs, RandomAmount has been pulled from the action queue, // so we can just insert a fixed number at the start of the queue and restart the queue // to effectively replace it var clonedNode = ((ProbabilisticGameNode)e.UserData).Branch(perItemWeight); NodeCount++; clonedNode.Game.ActionQueue.StackPush(i); clonedNode.Game.ActionQueue.ProcessAll(clonedNode); searcher.Visitor(clonedNode, this, e); } #if _TREE_DEBUG DebugLog.WriteLine("<-- Depth: " + e.Game.Depth); DebugLog.WriteLine(""); #endif return(Task.FromResult(0)); }
// When an in-game action completes, check if the game state has changed // Some actions (like selectors) won't cause the game state to change, // so we continue running these until a game state change occurs public override void PostAction(ActionQueue q, GameTree <GameNode> t, QueueActionEventArgs e) { // This game will be on the same thread as the calling task in parallel mode if it hasn't been cloned // If it has been cloned, it may be on a different thread if (e.Game.Changed) { e.Game.Changed = false; // If the action queue is empty, we have reached a leaf node game state // so compare it for equality with other final game states if (e.Game.ActionQueue.IsEmpty) { t.LeafNodeCount++; // This will cause the game to be discarded if its fuzzy hash matches any other final game state if (!tlsUniqueGames.Value.ContainsKey(e.Game)) { tlsUniqueGames.Value.Add(e.Game, e.UserData as ProbabilisticGameNode); #if _TREE_DEBUG DebugLog.WriteLine("UNIQUE GAME FOUND ({0}) - Hash: {1:x8}", uniqueGames.Count + tlsUniqueGames.Value.Count, e.Game.FuzzyGameHash); DebugLog.WriteLine("{0:S}", e.Game); #endif } else { tlsUniqueGames.Value[e.Game].Probability += ((ProbabilisticGameNode)e.UserData).Probability; #if _TREE_DEBUG DebugLog.WriteLine("DUPLICATE GAME FOUND - Hash: {0:x8}", e.Game.FuzzyGameHash); #endif } } else { // The game state has changed but there are more actions to do // (which may or may not involve further cloning) so add it to the search queue #if _TREE_DEBUG DebugLog.WriteLine("QUEUEING GAME " + e.Game.GameId + " FOR NEXT SEARCH"); #endif if (!tlsSearchQueue.Value.ContainsKey(e.Game)) { tlsSearchQueue.Value.Add(e.Game, e.UserData as ProbabilisticGameNode); } else { tlsSearchQueue.Value[e.Game].Probability += ((ProbabilisticGameNode)e.UserData).Probability; } } #if _TREE_DEBUG DebugLog.WriteLine(""); #endif e.Cancel = true; } }
public override void Visitor(ProbabilisticGameNode cloned, GameTree <GameNode> tree, QueueActionEventArgs e) { // If the action queue is empty, we have reached a leaf node game state // so compare it for equality with other final game states if (cloned.Game.ActionQueue.IsEmpty) { if (!cloned.Game.EquivalentTo(e.Game)) { tree.LeafNodeCount++; // This will cause the game to be discarded if its fuzzy hash matches any other final game state // TODO: Optimize to use TLS and avoid spinlocks lock (uniqueGames) { if (!uniqueGames.ContainsKey(cloned.Game)) { uniqueGames.Add(cloned.Game, cloned.Probability); #if _TREE_DEBUG DebugLog.WriteLine("UNIQUE GAME FOUND ({0}) - Hash: {1:x8}", uniqueGames.Count, cloned.Game.FuzzyGameHash); DebugLog.WriteLine("{0:S}", cloned.Game); #endif } else { uniqueGames[cloned.Game] += cloned.Probability; #if _TREE_DEBUG DebugLog.WriteLine("DUPLICATE GAME FOUND - Hash: {0:x8}", cloned.Game.FuzzyGameHash); #endif } } } } }
public override void Visitor(ProbabilisticGameNode cloned, GameTree <GameNode> tree, QueueActionEventArgs e) { // If the action queue is empty, we have reached a leaf node game state // TODO: Optimize to use TLS and avoid spinlocks if (cloned.Game.ActionQueue.IsEmpty) { tree.LeafNodeCount++; lock (leafNodeGames) { leafNodeGames.Add(cloned); } } }
public virtual void Visitor(ProbabilisticGameNode cloned, GameTree <GameNode> tree, QueueActionEventArgs e) { }
public virtual void PostAction(ActionQueue q, GameTree <GameNode> tree, QueueActionEventArgs e) { }