Пример #1
0
        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
                        }
                    }
                }
            }
        }
Пример #2
0
 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);
         }
     }
 }
Пример #3
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;
            }
        }
Пример #4
0
        // This is the entry point after the root node has been pushed into the queue
        // and the first change to the game has occurred
        public override async Task PostProcess(GameTree <GameNode> t)
        {
            // Breadth-first processing loop
            do
            {
                // Merge the TLS lists into the main lists
                foreach (var sq in tlsSearchQueue.Values)
                {
                    foreach (var qi in sq)
                    {
                        if (!searchQueue.ContainsKey(qi.Key))
                        {
                            searchQueue.Add(qi.Key, qi.Value);
                        }
                        else
                        {
                            searchQueue[qi.Key].Probability += qi.Value.Probability;
                        }
                    }
                }
                foreach (var ug in tlsUniqueGames.Values)
                {
                    foreach (var qi in ug)
                    {
                        if (!uniqueGames.ContainsKey(qi.Key))
                        {
                            uniqueGames.Add(qi.Key, qi.Value);
                        }
                        else
                        {
                            uniqueGames[qi.Key].Probability += qi.Value.Probability;
                        }
                    }
                }
#if _TREE_DEBUG
                DebugLog.WriteLine("QUEUE SIZE: " + searchQueue.Count);
#endif
                // Wipe the TLS lists
                tlsSearchQueue = new ThreadLocal <Dictionary <Game, ProbabilisticGameNode> >(() => new Dictionary <Game, ProbabilisticGameNode>(new FuzzyGameComparer()), trackAllValues: true);
                tlsUniqueGames = new ThreadLocal <Dictionary <Game, ProbabilisticGameNode> >(() => new Dictionary <Game, ProbabilisticGameNode>(new FuzzyGameComparer()), trackAllValues: true);

                // Quit if we have processed all nodes and none of them have children (all leaf nodes)
                if (searchQueue.Count == 0)
                {
                    break;
                }

                // Copy the search queue and clear the current one; it will be refilled
                var nextQueue = new Dictionary <Game, ProbabilisticGameNode>(searchQueue);
                searchQueue.Clear();

                // Only parallelize if there are sufficient nodes to do so
                if (nextQueue.Count >= MinNodesToParallelize && ((RandomOutcomeSearch)t).Parallel && MaxDegreesOfParallelism > 1)
                {
                    // Process each game's action queue until it is interrupted by OnAction above
                    await Task.WhenAll(
                        // Split search queue into MaxDegreesOfParallelism partitions
                        from partition in Partitioner.Create(nextQueue).GetPartitions(MaxDegreesOfParallelism)
                        // Run each partition in its own task
                        select Task.Run(async delegate {
#if _TREE_DEBUG
                        var count = 0;
                        DebugLog.WriteLine("Start partition run with " + MaxDegreesOfParallelism + " degrees of parallelism");
#endif
                        using (partition)
                            while (partition.MoveNext())
                            {
                                // Process each node
                                var kv = partition.Current;
                                await kv.Key.ActionQueue.ProcessAllAsync(kv.Value);
#if _TREE_DEBUG
                                count++;
#endif
                            }
#if _TREE_DEBUG
                        DebugLog.WriteLine("End run with partition size {0}", count);
#endif
                    }));

#if _TREE_DEBUG
                    DebugLog.WriteLine("=======================");
                    DebugLog.WriteLine("CLONES SO FAR: " + t.NodeCount + " / " + t.LeafNodeCount);
                    DebugLog.WriteLine("UNIQUE GAMES SO FAR: " + uniqueGames.Count);
                    DebugLog.WriteLine("NEW QUEUE SIZE: " + searchQueue.Count + "\r\n");
#endif
                }
                else
                {
#if _TREE_DEBUG
                    DebugLog.WriteLine("Start single-threaded run");
#endif
                    // Process each node in the search queue sequentially
                    foreach (var kv in nextQueue)
                    {
                        await kv.Key.ActionQueue.ProcessAllAsync(kv.Value);
                    }
#if _TREE_DEBUG
                    DebugLog.WriteLine("End single-threaded run");
#endif
                }
            } while (true);
        }
Пример #5
0
 public virtual void Visitor(ProbabilisticGameNode cloned, GameTree <GameNode> tree, QueueActionEventArgs e)
 {
 }
Пример #6
0
 public virtual Task PostProcess(GameTree <GameNode> tree)
 {
     return(Task.FromResult(0));
 }
Пример #7
0
 public virtual void PostAction(ActionQueue q, GameTree <GameNode> tree, QueueActionEventArgs e)
 {
 }