Changeset - 66a2d6c441ac
[Not reviewed]
default
0 2 0
Jason Maltzen (jmaltzen) - 7 years ago 2018-05-21 20:05:00
jason.maltzen@unsanctioned.net
Add a debug log to paint recipe generation
2 files changed with 35 insertions and 24 deletions:
0 comments (0 inline, 0 general)
RecipeGenerator.cs
Show inline comments
...
 
@@ -538,130 +538,131 @@ namespace DesertPaintLab
 
                        // stopped because cancel was requested - requeue the node in its current state for resume
 
                        searchQueue.Enqueue(node);
 
                    }
 
                }
 
            } while (!requestCancel && ok);
 

	
 
            bool done = false;
 
            lock(workerLock)
 
            {
 
                --runningThreads;
 
                //generatorThreads.Remove(Thread.CurrentThread);
 

	
 
                done = (runningThreads == 0);
 
            }
 
            if (done)
 
            {
 
                running = false;
 
                requestCancel = false;
 
                if (Finished != null)
 
                {
 
                    Finished(this, null);
 
                }
 
            }
 
        }
 

	
 
        // Add the cheapest recipe to the recipe list
 
        // returns the discarded recipe from the pair (or null if no original recipe to replace)
 
        private void AddCheapestRecipe(PaintRecipe recipe)
 
        {
 
            if (recipe.IsValidForConcentration(MinConcentration))
 
            {
 
                recipe.Reactions = reactions;
 

	
 
                string colorName = Palette.FindNearest(recipe.ReactedColor);
 
                //System.Console.WriteLine("Recipe: {0} {1}:", colorName, recipe.Cost);
 
                //foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
 
                //{
 
                //    System.Console.WriteLine("    -> {0} {1}", ingr.quantity, ingr.name);
 
                //}
 
                uint cost;
 
                lock (workerLock)
 
                {
 
                    if (recipeCosts.TryGetValue(colorName, out cost))
 
                    {
 
                        if (cost > recipe.Cost)
 
                        {
 
                            recipes[colorName].CopyFrom(recipe);
 
                            recipeCosts[colorName] = recipe.Cost;
 
                            log?.WriteLine("New recipe (cost {0}): {1}", recipe.Cost, recipe);
 
                            if (NewRecipe != null)
 
                            {
 
                                NewRecipeEventArgs args = new NewRecipeEventArgs(colorName, recipe);
 
                                NewRecipe(this, args);
 
                            }
 
                        }
 
                        else
 
                        {
 
                            Console.WriteLine("Ignoring recipe - existing cost is cheaper.");
 
                        }
 
                        //else
 
                        //{
 
                        //    Console.WriteLine("Ignoring recipe - existing cost is cheaper.");
 
                        //}
 
                    }
 
                    else
 
                    {
 
                        // This would be an error!
 
                        recipeCosts.Add(colorName, recipe.Cost);
 
                        recipes.Add(colorName, new PaintRecipe(recipe));
 
                        if (NewRecipe != null)
 
                        {
 
                            NewRecipeEventArgs args = new NewRecipeEventArgs(colorName, recipe);
 
                            NewRecipe(this, args);
 
                        }
 
                    }
 
                }
 
            }
 
            else
 
            {
 
                string msg = String.Format("Recipe is invalid ({0} ingredients)\n", recipe.Ingredients.Count);
 
                foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
 
                {
 
                    msg += String.Format("    -> {0} {1}", ingr.quantity, ingr.name);
 
                }
 
                lock (workerLock) {
 
                    Console.WriteLine(msg);
 
                }
 
                //string msg = String.Format("Recipe is invalid ({0} ingredients)\n", recipe.Ingredients.Count);
 
                //foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
 
                //{
 
                //    msg += String.Format("    -> {0} {1}", ingr.quantity, ingr.name);
 
                //}
 
                //lock (workerLock) {
 
                //    Console.WriteLine(msg);
 
                //}
 
            }
 
        }
 

	
 
        private bool IterateDepthFirst(RecipeSearchNode node)
 
        {
 
            TestCurrentRecipe(node);
 

	
 
            // pick recipe quantities at current recipe ingredients/size
 
            if (NextRecipe(node))
 
            {
 
                lock(workerLock)
 
                {
 
                    ++recipeCount;
 
                }
 
                //System.Console.WriteLine("Found next recipe at size {0} qty {1}", node.Reagents.Count, node.CurrentTargetQuantity);
 
                return true;
 
            }
 

	
 
            if (NextRecipeSize(node))
 
            {
 
                //System.Console.WriteLine("Found next recipe size {0}", node.CurrentTargetQuantity);
 
                return true;
 
            }
 

	
 
            // Search for next ingredient combo - all quantity combos for previous were searched
 
            //System.Console.WriteLine("Finding next ingredient combo");
 
            do
 
            {
 
                if (!node.AddNextReagent())
 
                {
 
                    while ((node.ReagentCount > node.MinReagents) && (node.LastReagent == (totalReagents-1)))
 
                    {
 
                        node.RemoveLastReagent();
 
                    }
 
                    if (node.ReagentCount == node.MinReagents)
 
                    {
 
                        // done
 
                        return false;
 
                    }
 
                    uint nextReagent = node.NextFreeReagent(node.LastReagent);
 
                    while ((node.ReagentCount > node.MinReagents) && (nextReagent >= totalReagents))
 
                    {
 
                        // No more reagents to try at this level
 
                        node.RemoveLastReagent();
 
                        if (node.ReagentCount > node.MinReagents)
 
                        {
 
                            nextReagent = node.NextFreeReagent(node.LastReagent);
 
                        }
...
 
@@ -672,237 +673,237 @@ namespace DesertPaintLab
 
                        return false;
 
                    }
 
                    node.ReplaceLastReagent(nextReagent);
 
                }
 
            } while (node.MaxConcentration < (node.MinConcentration + node.CatalystCount));
 
            node.InitForQuantity(node.MaxConcentration);
 

	
 
            //string outStr = "{0} : {1} : ";
 
            //for (int i = 0; i < currentReagents.Count; ++i)
 
            //{
 
            //    Reagent reagent = costSortedReagents[(int)currentReagents[i]];
 
            //    if (i > 0)
 
            //    {
 
            //        outStr += ", ";
 
            //    }
 
            //    outStr += reagent.Name + " (" + reagent.Cost + ")";
 
            //}
 
            //Console.WriteLine(outStr, currentReagents.Count, recipeCount);
 
            return true;
 
        }
 

	
 
        private bool IterateBreadthFirst(RecipeSearchNode node)
 
        {
 
            // pick recipe quantities at current recipe ingredients/size
 
            TestCurrentRecipe(node);
 
            lock(workerLock)
 
            {
 
                ++recipeCount;
 
            }
 

	
 
            // search all quantities of current recipe
 
            if (NextRecipe(node))
 
            {
 
                //System.Console.WriteLine("Found next recipe at size {0} qty {1}", node.ReagentCount, node.CurrentTargetQuantity);
 
                return true;
 
            }
 

	
 
            // Try next quantity
 
            uint newQuantity;
 
            uint quantityLimit = ((uint)node.ReagentCount <= FullQuantityDepth) ? ((uint)node.ReagentCount * FullQuantity) : node.MaxConcentration;
 
            do {
 
                newQuantity = node.CurrentTargetQuantity + 1;
 
                //Console.WriteLine("Try quantity {0}", newQuantity);
 
                if (newQuantity <= quantityLimit)
 
                {
 
                    node.InitForQuantity(newQuantity);
 
                    if (node.CurrentTargetQuantity <= node.UsedQuantity)
 
                    {
 
                        if (log != null) { lock(log) { log.WriteLine("Update quantity to {0}", node.CurrentTargetQuantity); } }
 
                        //if (log != null) { lock(log) { log.WriteLine("Update quantity to {0}", node.CurrentTargetQuantity); } }
 
                        return true;
 
                    }
 
                }
 
            } while (newQuantity < quantityLimit);
 

	
 
            bool ok = NextReagentSetBreadthFirst(node, node.MinReagents, node.MaxReagents);
 
            return ok;
 
        }
 

	
 
        private bool NextReagentSetBreadthFirst(RecipeSearchNode node, uint minReagents, uint maxReagents)
 
        {
 
            // search all variants at this depth of recipe
 
            // increase recipe depth
 

	
 
            // next reagent in last position
 
            // if at end, pop reagent
 
            //Console.WriteLine("Finding new recipe after quantity {0}/{1} used {2}", newQuantity, node.MaxConcentration, node.UsedQuantity);
 
            node.InitForQuantity(node.MinConcentration+node.CatalystCount); // reset quantity
 
            int currentDepth = node.ReagentCount;
 
            bool recipeFound = false;
 
            uint nextReagent;
 
            do {
 
                //Console.WriteLine("Current depth: {0}/{1}", currentDepth, node.MaxReagents);
 
                do {
 
                    recipeFound = false;
 
                    // back out until we find a node that can be incremented
 
                    if (currentDepth > minReagents)
 
                    {
 
                        while (node.ReagentCount > minReagents)
 
                        {
 
                            if (node.LastReagent < (totalReagents - 1))
 
                            {
 
                                nextReagent = node.NextFreeReagent(node.LastReagent);
 
                                if (nextReagent < totalReagents)
 
                                {
 
                                    //Console.WriteLine("Replace last reagent with {0}", nextReagent);
 
                                    node.ReplaceLastReagent(nextReagent);
 
                                    break;
 
                                }
 
                                else
 
                                {
 
                                    // shouldn't happen
 
                                    //Console.WriteLine("No available reagents at depth {0}!", node.ReagentCount);
 
                                    node.RemoveLastReagent();
 
                                    if (node.ReagentCount == minReagents)
 
                                    {
 
                                        // just popped the last reagent at the top level
 
                                        ++currentDepth;
 
                                        if (log != null) { lock(log) { log.WriteLine("Increased depth to {0}/{1}", currentDepth, node.MaxReagents); } }
 
                                        //if (log != null) { lock(log) { log.WriteLine("Increased depth to {0}/{1}", currentDepth, node.MaxReagents); } }
 
                                    }
 
                                }
 
                            }
 
                            else
 
                            {
 
                                //Console.WriteLine("Pop last reagent");
 
                                node.RemoveLastReagent();
 
                                if (node.ReagentCount == minReagents)
 
                                {
 
                                    // just popped the last reagent at the top level
 
                                    ++currentDepth;
 
                                    if (log != null) { lock(log) { log.WriteLine("Increased depth to {0}/{1} [pop last reagent at top level]", currentDepth, node.MaxReagents); } }
 
                                    //if (log != null) { lock(log) { log.WriteLine("Increased depth to {0}/{1} [pop last reagent at top level]", currentDepth, node.MaxReagents); } }
 
                                }
 
                            }
 
                        }
 
                        // fill in the nodes up to the current depth
 
                        if (node.ReagentCount >= minReagents && currentDepth <= maxReagents)
 
                        {
 
                            recipeFound = true;
 
                            while (node.ReagentCount < currentDepth)
 
                            {
 
                                if (! node.AddNextReagent())
 
                                {
 
                                    if (log != null) { lock(log) { log.WriteLine("Failed to reagent {0}/{1}", node.ReagentCount+1, currentDepth); } }
 
                                    //if (log != null) { lock(log) { log.WriteLine("Failed to add reagent {0}/{1}", node.ReagentCount+1, currentDepth); } }
 
                                    recipeFound = false;
 
                                }
 
                            }
 
                        }
 
                    }
 
                    //Console.WriteLine("Catalysts: {0} Reagents: {1} Min: {2}", node.CatalystCount, node.ReagentCount, node.MinReagents);
 
                } while ((node.CatalystCount >= node.ReagentCount) && (node.ReagentCount >= minReagents)); // make sure to skip all-catalyst combinations
 
                if (recipeFound)
 
                {
 
                    break;
 
                }
 
                else
 
                {
 
                    ++currentDepth;
 
                    if (log != null) { lock(log) { log.WriteLine("Increased depth to {0}/{1} [no recipe]", currentDepth, node.MaxReagents); } }
 
                    //if (log != null) { lock(log) { log.WriteLine("Increased depth to {0}/{1} [no recipe]", currentDepth, node.MaxReagents); } }
 
                }
 
            } while (currentDepth <= maxReagents);
 
            if (recipeFound)
 
            {
 
                node.InitForQuantity(node.MinConcentration+node.CatalystCount); // minimum quantity for this recipe
 
                if (node.TestRecipe == null)
 
                {
 
                    node.TestRecipe = new PaintRecipe();
 
                    node.TestRecipe.Reactions = reactions;
 
                }
 
                node.TestRecipe.Clear();
 
                for (int i = 0; i < node.ReagentCount; ++i)
 
                {
 
                    node.TestRecipe.AddReagent(node.Reagent(i).Name, node.CurrentWeights[i]);
 
                }
 
                if (log != null) { 
 
                    string combo = "";
 
                    foreach (PaintRecipe.RecipeIngredient ingr in node.TestRecipe.Ingredients)
 
                    {
 
                        combo += " " + ingr.name;
 
                    }
 
                    lock(log) { log.WriteLine("New ingredients: " + combo); }
 
                    //string combo = "";
 
                    //foreach (PaintRecipe.RecipeIngredient ingr in node.TestRecipe.Ingredients)
 
                    //{
 
                    //    combo += " " + ingr.name;
 
                    //}
 
                    //lock(log) { log.WriteLine("New ingredients: " + combo); }
 
                }
 
            }
 

	
 
            return recipeFound;
 
        }
 

	
 
        private void TestCurrentRecipe(RecipeSearchNode node)
 
        {
 
            if (node.TestRecipe == null)
 
            {
 
                node.TestRecipe = new PaintRecipe();
 
                node.TestRecipe.Reactions = reactions;
 
            }
 
            node.TestRecipe.Clear();
 
            for (int i = 0; i < node.ReagentCount; ++i)
 
            {
 
                node.TestRecipe.AddReagent(node.Reagent(i).Name, node.CurrentWeights[i]);
 
            }
 
            AddCheapestRecipe(node.TestRecipe);
 
            //if (log != null) { lock(log) { log.WriteLine("Tested recipe: {0}", node.TestRecipe); } }
 
        }
 

	
 
        private bool NextRecipe(RecipeSearchNode node)
 
        {
 
            // check for the next recipe
 
            uint remainingWeight = node.CurrentTargetQuantity - node.CatalystCount;
 
            if (remainingWeight < MinConcentration)
 
            {
 
                // not possible to make a valid recipe
 
                Console.WriteLine("Insufficient remaining weight");
 
                //Console.WriteLine("Insufficient remaining weight");
 
                return false;
 
            }
 
            //uint remainingReagents = (uint)node.Reagents.Count - node.CatalystCount;
 

	
 
            uint depth = (uint)node.ReagentCount;
 
            uint weightToConsume = 0;
 
            uint spaceBelow = 0;
 
            int reagentsBelow = 0;
 
            for (int i = (int)depth-1 ; i >= 0; --i)
 
            {
 
                uint currentWeight = node.CurrentWeights[i];
 

	
 
                if ((spaceBelow >= (weightToConsume+1)) && (currentWeight > 1))
 
                {
 
                    // reduce this node by 1, allocate remaining weight to reagents below it
 
                    node.SetWeight(i, currentWeight-1);
 
                    weightToConsume += 1;
 
                    for (int j = i+1; j < depth; ++j)
 
                    {
 
                        --reagentsBelow;
 
                        Reagent reagent = node.Reagent(j);
 
                        uint allocated = (uint)Math.Min(reagent.IsCatalyst ? 1 : (depth <= FullQuantityDepth ? FullQuantity : reagent.RecipeMax), weightToConsume - reagentsBelow);
 
                        if (allocated > 100)
 
                        {
 
                            Console.WriteLine("ACK: allocated = {0}", allocated);
 
                        }
 
                        node.SetWeight(j, allocated);
 
                        weightToConsume -= allocated;
 
                    }
 
                    break;
 
                }
 
                else
 
                {
 
                    Reagent reagent = node.Reagent(i);
 
                    spaceBelow += (reagent.IsCatalyst ? 1 : (depth <= FullQuantityDepth ? FullQuantity : reagent.RecipeMax));
 
                    weightToConsume += currentWeight;
 
                    ++reagentsBelow;
 
                }
 
            }
 

	
 
            //int recipeWeight = 0;
 
            //foreach (int weight in node.CurrentWeights)
 
            //{
 
            //    recipeWeight += weight;
 
            //}
 
            //if ((weightToConsume != 0) || (recipeWeight != node.CurrentTargetQuantity))
 
            //{
 
            //    Console.WriteLine("Failed recipe with leftover weight {0} ({1}/{2}):", weightToConsume, recipeWeight, node.CurrentTargetQuantity);
UI/RecipeGeneratorView.cs
Show inline comments
...
 
@@ -196,96 +196,106 @@ namespace DesertPaintLab
 
                Reagent reagent = ReagentManager.GetReagent(reagentName);
 
                reagentListStore.AppendValues(reagent, reagentName); // , reagent, reagent);
 
            }
 

	
 
            reagentEnabledColumn.SetCellDataFunc (reagentEnabledCell, new Gtk.TreeCellDataFunc (RenderReagentToggle));
 
            reagentCostColumn.SetCellDataFunc (reagentCostCell, new Gtk.TreeCellDataFunc (RenderReagentCost));
 
            reagentMaxColumn.SetCellDataFunc (reagentMaxCell, new Gtk.TreeCellDataFunc (RenderReagentQuantity));
 

	
 
            // Assign the model to the TreeView
 
            reagentListView.Model = reagentListStore;
 
            reagentListView.Sensitive = true;
 

	
 
            reagentListView.AppendColumn(reagentEnabledColumn);
 
            reagentListView.AppendColumn(reagentNameColumn);
 
            reagentListView.AppendColumn(reagentCostColumn);
 
            reagentListView.AppendColumn(reagentMaxColumn);
 

	
 
            bool ribbons = false;
 
            profile.ProfileSettings.Get("Generator.Ribbons", out ribbons);
 
            if (ribbons)
 
            {
 
                checkButtonRibbon.Active = true;
 
                InitStateForRibbons();
 
            }
 
            else
 
            {
 
                checkButtonRibbon.Active = false;
 
                InitStateForPaint();
 
            }
 
            
 
            ShowAll();
 
        }
 

	
 
        private void InitStateForPaint()
 
        {
 
            maxRecipeSpinButton.Adjustment.Lower = PaintRecipe.PAINT_RECIPE_MIN_CONCENTRATION;
 
            fullQuantitySpinButton.Adjustment.Upper = 30;
 
            if (generator == null)
 
            {
 
                generator = new RecipeGenerator(profile.Reactions);
 
                int threads;
 
                DesertPaintLab.AppSettings.Get("GeneratorThreads", out threads);
 
                if (threads <= 0) { threads = 15; }
 
                generator.MaxThreads = (uint)threads;
 

	
 
                generator.Progress += OnProgress;
 
                generator.Finished += OnFinished;
 
                generator.NewRecipe += OnNewRecipe;
 

	
 
                bool enableDebugMenu = false;
 
                DesertPaintLab.AppSettings.Get("EnableDebugMenu", out enableDebugMenu);
 
                bool logGenerator = false;
 
                DesertPaintLab.AppSettings.Get("GeneratorLog", out logGenerator);
 
                if (enableDebugMenu && logGenerator)
 
                {
 
                    string logDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
                    generator.Log = System.IO.Path.Combine(logDir, "dpl_generator.txt");
 
                }
 
            }
 
            generator.InitRecipes(profile.Recipes);
 

	
 
            string stateFile = System.IO.Path.Combine(profile.Directory, PAINT_STATE_FILE);
 
            if (System.IO.File.Exists(stateFile))
 
            {
 
                generator.LoadState(stateFile);
 
                if (generator.CanResume)
 
                {
 
                    beginButton.Label = "Restart";
 
                    stopResumeButton.Label = "Resume";
 
                    stopResumeButton.Sensitive = true;
 

	
 
                    maxRecipeSpinButton.Value = Math.Max(generator.MaxConcentration, 14); //
 
                    fullQuantitySpinButton.Value = generator.FullQuantity; // TODO: read/save profile info
 
                    fullQuantityDepthSpinButton.Value = generator.FullQuantityDepth; // TODO: read/save profile info
 
                    maxIngredientsSpinButton.Value = generator.MaxReagents;
 
                }
 
            }
 
            else
 
            {
 
                beginButton.Label = "Start";
 
                stopResumeButton.Label = "Stop";
 
                stopResumeButton.Sensitive = false;
 

	
 
                maxRecipeSpinButton.Value = 14;
 
                //fullQuantitySpinButton.Value = generator.FullQuantity; // TODO: read/save profile info
 
                //fullQuantityDepthSpinButton.Value = generator.FullQuantityDepth; // TODO: read/save profile info
 
                //maxIngredientsSpinButton.Value = generator.MaxReagents;
 
            }
 
            //generator.Log = System.IO.Path.Combine(profile.Directory, "dp_log.txt");
 
            countLabel.Text = String.Format("{0} / {1}", profile.RecipeCount, Palette.Count);
 

	
 
            colorStore.Clear();
 
            foreach (KeyValuePair<string, PaintRecipe> pair in profile.Recipes)
 
            {
 
                if (pair.Value.IsValidForConcentration(PaintRecipe.PAINT_RECIPE_MIN_CONCENTRATION))
 
                {
 
                    string colorName = pair.Key;
 
                    colorStore.AppendValues(colorName);
 
                }
 
            }
 
            recipeList.Show();
 
        }
 

	
 
        private void InitStateForRibbons()
 
        {
 
            maxRecipeSpinButton.Adjustment.Lower = PaintRecipe.RIBBON_RECIPE_MIN_CONCENTRATION;
0 comments (0 inline, 0 general)