Changeset - 3e36d49355f5
[Not reviewed]
default
0 1 0
Jason Maltzen (jmaltzen) - 9 years ago 2015-12-27 09:42:27
jason.maltzen@unsanctioned.net
Re-enable multiple worker threads.
1 file changed with 1 insertions and 1 deletions:
0 comments (0 inline, 0 general)
RecipeGenerator.cs
Show inline comments
...
 
@@ -74,385 +74,385 @@ namespace DesertPaintLab
 

	
 
        int runningThreads = 0;
 
        
 
        SortedDictionary<string, uint> recipeCosts = new SortedDictionary<string, uint>();
 
        SortedDictionary<string, PaintRecipe> recipes = new SortedDictionary<string, PaintRecipe>();
 

	
 
        uint totalReagents;
 

	
 
        List<Reagent> costSortedReagents = new List<Reagent>();
 

	
 
        ConcurrentQueue<RecipeSearchNode> searchQueue = new ConcurrentQueue<RecipeSearchNode>();
 

	
 
        ulong recipeCount = 0;
 

	
 
        List<Thread> generatorThreads = new List<Thread>();
 
        Object workerLock = new Object();
 

	
 
        bool requestCancel = false;
 

	
 
        StreamWriter log;
 

	
 
        // events
 
        public event EventHandler Finished;
 
        public event EventHandler Progress;
 
        public event EventHandler<NewRecipeEventArgs> NewRecipe;
 

	
 
        public RecipeGenerator(ReactionSet reactions)
 
        {
 
            Mode = SearchType.BREADTH_FIRST;
 
            this.reactions = reactions;
 
            foreach (PaintColor color in Palette.Colors)
 
            {
 
                recipes.Add(color.Name, new PaintRecipe());
 
                recipeCosts.Add(color.Name, uint.MaxValue);
 
            }
 
        }
 

	
 
        public SortedDictionary<string, PaintRecipe> Recipes
 
        {
 
            get
 
            {
 
                return recipes;
 
            }
 
        }
 

	
 
        public ulong RecipeCount
 
        {
 
            get
 
            {
 
                return recipeCount;
 
            }
 
        }
 

	
 
        public bool CanResume
 
        {
 
            get
 
            {
 
                return (!running && (searchQueue.Count > 0));
 
            }
 
        }
 

	
 
        public string Log
 
        {
 
            set
 
            {
 
                if (value != null)
 
                {
 
                    log = new StreamWriter(value);
 
                }
 
                else
 
                {
 
                    log = null;
 
                }
 
            }
 
        }
 

	
 
        private class ReagentCostSort : IComparer<Reagent>
 
        {
 
            public int Compare(Reagent reagent1, Reagent reagent2)
 
            {
 
                return (int)reagent1.Cost - (int)reagent2.Cost;
 
            }
 
        }
 

	
 
        public void InitRecipes(SortedDictionary<string, PaintRecipe> initialRecipes)
 
        {
 
            if (running)
 
            {
 
                return;
 
            }
 
            foreach (PaintRecipe recipe in initialRecipes.Values)
 
            {
 
                //PaintRecipe recipeCopy = new PaintRecipe(recipe);
 
                AddCheapestRecipe(recipe);
 
            }
 
        }
 

	
 
        private void InitSortedReagents()
 
        {
 
            costSortedReagents.Clear();
 
            foreach (string name in ReagentManager.Names)
 
            {
 
                Reagent reagent = ReagentManager.GetReagent(name);
 
                costSortedReagents.Add(reagent);
 
            }
 
            costSortedReagents.Sort(new ReagentCostSort());
 
        }
 

	
 
        public void BeginRecipeGeneration(uint maxQuantity, uint maxReagents, uint fullQuantityDepth, uint fullQuantity)
 
        {
 
            if (running)
 
            {
 
                // Already running - don't start again
 
                return;
 
            }
 

	
 
            this.MaxQuantity = maxQuantity;
 
            this.MaxReagents = maxReagents;
 
            this.FullQuantity = fullQuantity;
 
            this.FullQuantityDepth = fullQuantityDepth;
 

	
 
            // first, sort reagents by cost.
 
            InitSortedReagents();
 

	
 
            totalReagents = (uint)costSortedReagents.Count;
 

	
 
            // Pre-populate recipes list with:
 
            // 1) 1-ingredient recipes @ 10db for all enabled ingredients with a count >= 10
 
            // 2) any previously-generated recipes
 
            int enabledReagentCount = 0;
 
            PaintRecipe recipe = new PaintRecipe();
 
            recipe.Reactions = reactions;
 
            foreach (Reagent reagent in costSortedReagents)
 
            {
 
                if (reagent.Enabled)
 
                {
 
                    if (!reagent.IsCatalyst && ((reagent.RecipeMax >= 10) || ((FullQuantityDepth > 0) && (FullQuantity >= 10))))
 
                    {
 
                        recipe.Clear();
 
                        recipe.AddReagent(reagent.Name, 10);
 
                        AddCheapestRecipe(recipe);
 
                    }
 
                    ++enabledReagentCount;
 
                }
 
            }
 
            this.MaxReagents = (uint)Math.Min(enabledReagentCount, this.MaxReagents);
 

	
 
            while (!searchQueue.IsEmpty)
 
            {
 
                RecipeSearchNode node;
 
                searchQueue.TryDequeue(out node);
 
            }
 
            for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
 
            {
 
                if (costSortedReagents[(int)reagentIdx].Enabled)
 
                {
 
                    RecipeSearchNode initialNode = new RecipeSearchNode(costSortedReagents, reagentIdx);
 
                    initialNode.FullQuantity = FullQuantity;
 
                    initialNode.FullQuantityDepth = FullQuantityDepth;
 
                    initialNode.MaxQuantity = maxQuantity;
 
                    initialNode.MaxReagents = maxReagents;
 
                    searchQueue.Enqueue(initialNode);
 
                }
 
            }
 

	
 
            recipeCount = 0;
 

	
 
            if (log != null)
 
            {
 
                log.WriteLine("Begin recipe generation: MaxQuantity={0} MaxReagents={1} FullQuantity={2} FullQuantityDepth={3}", MaxQuantity, MaxReagents, FullQuantity, FullQuantityDepth);
 
            }
 
            
 
            // start worker threads to do the actual work
 
            ResumeRecipeGeneration();
 
        }
 

	
 
        public void ResumeRecipeGeneration()
 
        {
 
            if (running)
 
            {
 
                // Already running - don't start again
 
                return;
 
            }
 
            running = true;
 
            requestCancel = false;
 

	
 
            if (log != null)
 
            {
 
                log.WriteLine("Resuming recipe generation: pre-threads={0} reagent count={1} search queue={2}", runningThreads, costSortedReagents.Count, searchQueue.Count);
 
            }
 
            runningThreads = 0; // presumably!
 

	
 
            int threadCount = 1; // Math.Min(costSortedReagents.Count, searchQueue.Count);
 
            int threadCount = Math.Min(costSortedReagents.Count, searchQueue.Count);
 
            if (threadCount == 0)
 
            {
 
                if (Finished != null)
 
                {
 
                    Finished(this, null);
 
                }
 
            }
 
            generatorThreads.Clear();
 
            for (int i = 0; i < threadCount; ++i)
 
            {
 
                Thread thr = new Thread(new ThreadStart(this.Generate));
 
                generatorThreads.Add(thr);
 
            }
 
            foreach (Thread thr in generatorThreads)
 
            {
 
                thr.Start();
 
            }
 
        }
 

	
 
        public bool SaveState(string file)
 
        {
 
            if (running)
 
            {
 
                // can't save state while running
 
                return false;
 
            }
 

	
 
            using (StreamWriter writer = new StreamWriter(file, false))
 
            {
 
                writer.WriteLine("MaxReagents: {0}", MaxReagents);
 
                writer.WriteLine("FullQuantityDepth: {0}", FullQuantityDepth);
 
                writer.WriteLine("FullQuantity: {0}", FullQuantity);
 
                writer.WriteLine("TotalReagents: {0}", totalReagents);
 
                writer.WriteLine("RecipeCount: {0}", recipeCount);
 
                writer.WriteLine("SearchType: {0}", Mode.ToString());
 
                foreach (KeyValuePair<string, PaintRecipe> pair in recipes)
 
                {
 
                    PaintRecipe recipe = pair.Value;
 
                    string colorName = Palette.FindNearest(recipe.ReactedColor);
 
                    writer.WriteLine("BeginRecipe: {0}", colorName);
 
                    foreach (PaintRecipe.RecipeIngredient ingredient in recipe.Ingredients)
 
                    {
 
                        writer.WriteLine("Ingredient: {0}={1}", ingredient.name, ingredient.quantity);
 
                    }
 
                    writer.WriteLine("EndRecipe: {0}", colorName);
 
                }
 
                writer.WriteLine("SearchNodes: {0}", searchQueue.Count);
 
                foreach (RecipeSearchNode node in searchQueue)
 
                {
 
                    node.SaveState(writer);
 
               }
 
            }
 
            return true;
 
        }
 

	
 
        static Regex keyValueRegex = new Regex(@"(?<key>\w+)\:\s*(?<value>.+)\s*");
 
        static Regex ingredientRegex = new Regex(@"(?<ingredient>(\w+\s)*\w+)\s*=\s*(?<quantity>\d)\s*");
 
        public bool LoadState(string file)
 
        {
 
            // cannot be running, and reactions must be set
 
            if (running)
 
            {
 
                return false;
 
            }
 

	
 
            InitSortedReagents();
 
            bool success = true;
 

	
 
            PaintRecipe currentRecipe = null;
 
            Match match;
 
            string line;
 
            using (StreamReader reader = new StreamReader(file, false))
 
            {
 
                while (success && ((line = reader.ReadLine()) != null))
 
                {
 
                    match = keyValueRegex.Match(line);
 
                    if (match.Success)
 
                    {
 
                        string value = match.Groups["value"].Value;
 
                        switch(match.Groups["key"].Value)
 
                        {
 
                            case "MaxReagents":
 
                                MaxReagents = uint.Parse(value);
 
                                break;
 
                            case "FullQuantityDepth":
 
                                FullQuantityDepth = uint.Parse(value);
 
                                break;
 
                            case "FullQuantity":
 
                                FullQuantity = uint.Parse(value);
 
                                break;
 
                            case "TotalReagents":
 
                                totalReagents = uint.Parse(value);
 
                                break;
 
                            case "RecipeCount":
 
                                if (!ulong.TryParse(value, out recipeCount))
 
                                {
 
                                    // must have rolled to negative - try as an int and convert
 
                                    int recipeCountInt = int.Parse(value);
 
                                    recipeCount = (ulong)((long)recipeCountInt & 0x00000000ffffffffL);
 
                                }
 
                                break;
 
                            case "BeginRecipe":
 
                                currentRecipe = new PaintRecipe();
 
                                currentRecipe.Reactions = reactions;
 
                                break;
 
                            case "EndRecipe":
 
                                if (currentRecipe != null)
 
                                {
 
                                    PaintColor color = currentRecipe.ReactedColor;
 
                                    uint cost = currentRecipe.Cost;
 
                                    recipes[color.Name] = currentRecipe; // replace
 
                                    recipeCosts[color.Name] = cost;
 
                                    currentRecipe = null;
 
                                }
 
                                break;
 
                            case "Ingredient":
 
                                if (currentRecipe != null)
 
                                {
 
                                    Match ingredientMatch = ingredientRegex.Match(match.Groups["value"].Value);
 
                                    if (ingredientMatch.Success)
 
                                    {
 
                                        uint quantity = uint.Parse(ingredientMatch.Groups["quantity"].Value);
 
                                        currentRecipe.AddReagent(ingredientMatch.Groups["ingredient"].Value, quantity);
 
                                    }
 
                                    else
 
                                    {
 
                                        success = false;
 
                                    }
 
                                }
 
                                break;
 
                            case "SearchNodes":
 
                                int nodeCount = int.Parse(match.Groups["value"].Value);
 
                                for (int i = 0; i < nodeCount; ++i)
 
                                {
 
                                    RecipeSearchNode node = new RecipeSearchNode(costSortedReagents);
 
                                    node.FullQuantity = FullQuantity;
 
                                    node.FullQuantityDepth = FullQuantityDepth;
 
                                    node.MaxReagents = MaxReagents;
 
                                    node.MaxQuantity = MaxQuantity;
 
                                    success = success && node.LoadState(reader);
 
                                    if (success)
 
                                    {
 
                                        searchQueue.Enqueue(node);
 
                                    }
 
                                }
 
                                break;
 
                            case "SearchType":
 
                                Mode = (SearchType)Enum.Parse(typeof(SearchType), match.Groups["value"].Value);
 
                                break;
 
                            default:
 
                                success = false;
 
                                break;
 
                        }
 
                    }
 
                    else
 
                    {
 
                        success = false;
 
                        break;
 
                    }
 
                }
 
                return success;
 
            }
 
        }
 

	
 
        private void Generate()
 
        {
 
            RecipeSearchNode node;
 

	
 
            lock(workerLock)
 
            {
 
                ++runningThreads;
 
            }
 

	
 
            bool ok = true;
 
            do
 
            {
 
                lock (workerLock)
 
                {
 
                    ok = searchQueue.TryDequeue(out node);
 
                }
 
                if (ok)
 
                {
 
                    if (Mode == SearchType.DEPTH_FIRST)
 
                    {
 
                        uint targetQuantity = (node.ReagentCount <= FullQuantityDepth) ? ((uint)node.ReagentCount * FullQuantity) : node.MaxQuantity + 1;
 
                        do {
 
                            --targetQuantity;
 
                            node.InitForQuantity(targetQuantity);
 
                        } while (targetQuantity > 10 && (node.CurrentTargetQuantity != node.UsedQuantity));
 
    
 
                        while ((ok = IterateDepthFirst(node)) && !requestCancel)
 
                        {
0 comments (0 inline, 0 general)