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
...
 
@@ -394,572 +394,573 @@ namespace DesertPaintLab
 
                            case "MinReagents":
 
                                MinReagents = uint.Parse(value);
 
                                MaxReagents = (uint)Math.Max(this.MinReagents, this.MaxReagents);
 
                                break;
 
                            case "MaxReagents":
 
                                MaxReagents = uint.Parse(value);
 
                                MinReagents = (uint)Math.Min(this.MinReagents, this.MaxReagents);
 
                                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.MinReagents = MinReagents;
 
                                    node.MaxReagents = MaxReagents;
 
                                    node.MaxConcentration = MaxConcentration;
 
                                    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.MaxConcentration + 1;
 
                        do {
 
                            --targetQuantity;
 
                            node.InitForQuantity(targetQuantity);
 
                        } while (targetQuantity > MinConcentration && (node.CurrentTargetQuantity != node.UsedQuantity));
 
    
 
                        while ((ok = IterateDepthFirst(node)) && !requestCancel)
 
                        {
 
                            if (Progress != null)
 
                            {
 
                                Progress(this, null);
 
                            }
 
                        }
 
                    }
 
                    else
 
                    {
 
                        // breadth-first search
 
                        uint targetQuantity = MinConcentration - 1;
 
                        uint quantityLimit = (node.ReagentCount <= FullQuantityDepth) 
 
                            ? (FullQuantity * (uint)node.ReagentCount) 
 
                            : node.MaxConcentration;
 
                        do {
 
                            ++targetQuantity;
 
                            node.InitForQuantity(targetQuantity);
 
                        } while ((targetQuantity < quantityLimit) && (node.CurrentTargetQuantity != node.UsedQuantity));
 
    
 
                        while ((ok = IterateBreadthFirst(node)) && !requestCancel)
 
                        {
 
                            Progress?.Invoke(this, null);
 
                        }
 
                    }
 
                    if (ok)
 
                    {
 
                        // 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);
 
                        }
 
                    }
 
                    if (node.ReagentCount == node.MinReagents)
 
                    {
 
                        // done
 
                        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);
 
            //    for (int i = 0; i < node.Reagents.Count; ++i)
 
            //    {
 
            //        Console.WriteLine("   > {0} {1}", node.Reagent(i).Name, node.CurrentWeights[i]);
 
            //    }
 
            //}
 
            
 
            return (weightToConsume == 0);
 
        }
 

	
 
        private bool NextRecipeSize(RecipeSearchNode node)
 
        {
 
            uint newQuantity = node.CurrentTargetQuantity - 1;
 
            if (newQuantity < (node.MinConcentration + node.CatalystCount))
 
            {
 
                return false;
 
            }
 

	
 
            node.InitForQuantity(newQuantity);
 
            if (node.CurrentTargetQuantity > node.UsedQuantity)
 
            {
 
                return false;
 
            }
 

	
 
            return true;
 
        }
 

	
 
        public void Wait()
 
        {
 
            if (generatorThreads.Count > 0)
 
            {
 
                foreach (Thread thr in generatorThreads)
 
                {
 
                    thr.Join();
 
                }
 
                generatorThreads.Clear();
 
            }
 
        }
 

	
 
        public void Stop()
 
        {
 
            this.requestCancel = true;
 
        }
 

	
 
        public void Reset()
 
        {
 
            foreach (PaintRecipe recipe in recipes.Values)
 
            {
 
                recipe.Clear();
 
            }
 
            foreach (string key in recipeCosts.Keys)
 
            {
 
                recipeCosts[key] = uint.MaxValue;
 
            }
 
        }
 
    }
 
}
 

	
UI/RecipeGeneratorView.cs
Show inline comments
...
 
@@ -52,384 +52,394 @@ namespace DesertPaintLab
 

	
 
        public EventHandler SetStatus;
 
        public EventHandler Started;
 
        public EventHandler Stopped;
 

	
 
        static public Gtk.ListStore RecipeModel
 
        {
 
            get
 
            {
 
                return colorStore;   
 
            }
 
        }
 

	
 
        Gtk.ThreadNotify notifyFinished;
 
        Gtk.ThreadNotify notifyProgress;
 
        Gtk.ThreadNotify notifyNewRecipe;
 

	
 
        ConcurrentQueue<PaintRecipe> pendingNewRecipes = new ConcurrentQueue<PaintRecipe>();
 

	
 
        Gtk.ListStore reagentListStore;
 
        // end reagent view
 

	
 
        public RecipeGeneratorView(PlayerProfile profile) : base()
 
        {
 
            this.profile = profile;
 
            this.Build();
 
            minIngredientsSpinButton.Value = 1; // TODO: read/save profile info
 
            maxIngredientsSpinButton.Value = 5; // TODO: read/save profile info
 
            maxRecipeSpinButton.Value = 20; // TODO: read/save profile info
 
            fullQuantitySpinButton.Value = 20; // TODO: read/save profile info
 
            fullQuantityDepthSpinButton.Value = 4; // TODO: read/save profile info
 

	
 
            fullQuantityDepthSpinButton.SetRange(0, ReagentManager.Names.Count);
 
            maxIngredientsSpinButton.SetRange(1, ReagentManager.Names.Count);
 
            minIngredientsSpinButton.SetRange(1, ReagentManager.Names.Count);
 

	
 

	
 
            Gtk.TreeViewColumn recipeColorColumn = new Gtk.TreeViewColumn();
 
            Gtk.CellRendererText recipeColumnCell = new Gtk.CellRendererText();
 
            recipeColorColumn.PackStart(recipeColumnCell, true);       
 
            recipeColorColumn.Title = "Color";
 

	
 
            recipeList.AppendColumn(recipeColorColumn);
 
            recipeColorColumn.AddAttribute(recipeColumnCell, "text", 0);
 

	
 
            colorStore.Clear();
 

	
 
            colorStore.SetSortColumnId(0, Gtk.SortType.Ascending);
 
            recipeList.Model = RecipeModel;
 

	
 
            recipeList.Selection.Changed += OnColorSelected;
 

	
 
            recipeIngredientsView.AppendColumn("Quantity", new Gtk.CellRendererText(), "text", 0);
 
            recipeIngredientsView.AppendColumn("Ingredient", new Gtk.CellRendererText(), "text", 1);
 
            recipeIngredientsView.Model = new Gtk.ListStore(typeof(string), typeof(string));
 

	
 
            profile.LoadRecipes();
 

	
 
            canceling = false;
 
            running = false;
 
            pauseForCheckpoint = false;
 

	
 
            generator = new RecipeGenerator(profile.Reactions);
 
            int threads;
 
            DesertPaintLab.AppSettings.Get("GeneratorThreads", out threads);
 
            if (threads <= 0) { threads = 15; }
 
            generator.MaxThreads = (uint)threads;
 
            generator.InitRecipes(profile.Recipes);
 

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

	
 
            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;
 
                    checkButtonRibbon.Active = false;
 
                }
 
            }
 
            //generator.Log = System.IO.Path.Combine(profile.Directory, "dp_log.txt");
 
            countLabel.Text = String.Format("{0} / {1}", profile.RecipeCount, Palette.Count);
 

	
 
            Destroyed += OnDestroyed;
 

	
 
            notifyFinished = new Gtk.ThreadNotify(new Gtk.ReadyEvent(HandleFinished));
 
            notifyProgress = new Gtk.ThreadNotify(new Gtk.ReadyEvent(HandleProgress));
 
            notifyNewRecipe = new Gtk.ThreadNotify(new Gtk.ReadyEvent(HandleNewRecipe));
 

	
 
            // initialize reagent list
 
 
 
            // Add the columns to the TreeView
 
            Gtk.TreeViewColumn reagentEnabledColumn = new Gtk.TreeViewColumn ();
 
            reagentEnabledColumn.Title = "Enabled";
 
            Gtk.CellRendererToggle reagentEnabledCell = new Gtk.CellRendererToggle ();
 
            reagentEnabledCell.Activatable = true;
 
            reagentEnabledCell.Sensitive = true;
 
            reagentEnabledCell.Mode = Gtk.CellRendererMode.Activatable;
 
            reagentEnabledCell.Visible = true;
 
            reagentEnabledCell.Toggled += new Gtk.ToggledHandler(OnReagentEnableToggled);
 
            reagentEnabledColumn.PackStart (reagentEnabledCell, true);
 
            //reagentEnabledColumn.AddAttribute(reagentEnabledCell, "active", 0);
 

	
 
            Gtk.TreeViewColumn reagentNameColumn = new Gtk.TreeViewColumn ();
 
            reagentNameColumn.Title = "Ingredient";
 
            Gtk.CellRendererText reagentNameCell = new Gtk.CellRendererText ();
 
            reagentNameCell.Mode = Gtk.CellRendererMode.Inert;
 
            reagentNameColumn.PackStart (reagentNameCell, true);
 
            reagentNameColumn.AddAttribute(reagentNameCell, "text", 1);
 
            reagentNameColumn.Expand = true;
 

	
 
            Gtk.TreeViewColumn reagentCostColumn = new Gtk.TreeViewColumn ();
 
            reagentCostColumn.Title = "Cost";
 
            Gtk.CellRendererText reagentCostCell = new Gtk.CellRendererText ();
 
            reagentCostCell.Edited += OnReagentCostChanged;
 
            reagentCostCell.Editable = true;
 
            reagentCostCell.Sensitive = true;
 
            reagentCostCell.Mode = Gtk.CellRendererMode.Editable;
 
            reagentCostColumn.PackStart (reagentCostCell, true);
 
            //reagentCostColumn.AddAttribute(reagentCostCell, "text", 0);
 

	
 
            Gtk.TreeViewColumn reagentMaxColumn = new Gtk.TreeViewColumn ();
 
            reagentMaxColumn.Title = "Max";
 
            Gtk.CellRendererText reagentMaxCell = new Gtk.CellRendererText ();
 
            reagentMaxCell.Edited += OnReagentQuantityChanged;
 
            reagentMaxCell.Editable = true;
 
            reagentMaxCell.Sensitive = true;
 
            reagentCostCell.Mode = Gtk.CellRendererMode.Editable;
 
            reagentMaxColumn.PackStart (reagentMaxCell, true);
 
            //reagentMaxColumn.AddAttribute(reagentMaxCell, "text", 0);
 

	
 
            reagentListStore = new Gtk.ListStore(typeof(Reagent), typeof(string), typeof(Reagent), typeof(Reagent));
 
            foreach (string reagentName in ReagentManager.Names)
 
            {
 
                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;
 
            fullQuantitySpinButton.Adjustment.Upper = 100;
 
            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;
 
            }
 
            generator.InitRecipes(profile.RibbonRecipes);
 

	
 
            string stateFile = System.IO.Path.Combine(profile.Directory, RIBBON_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, PaintRecipe.RIBBON_RECIPE_MIN_CONCENTRATION); //
 
                    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 = PaintRecipe.RIBBON_RECIPE_MIN_CONCENTRATION
 
;
 
                //fullQuantitySpinButton.Value = generator.FullQuantity; // TODO: read/save profile info
 
                //fullQuantityDepthSpinButton.Value = generator.FullQuantityDepth; // TODO: read/save profile info
 
                //maxIngredientsSpinButton.Value = generator.MaxReagents;
 
            }
 

	
 
            countLabel.Text = String.Format("{0} / {1}", profile.RibbonCount, Palette.Count);
 

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

	
 
        public PlayerProfile Profile {
 
            set {
 
                if (profile != value) {
 
                    // TODO: ensure not running
 
                    profile = value;
 
                    profile.LoadRecipes();
 

	
 
                    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;
 

	
 
                    if (checkButtonRibbon.Active)
 
                    {
 
                        InitStateForRibbons();
 
                    }
 
                    else
 
                    {
 
                        InitStateForPaint();
 
                    }
 
                }
 
            }
 
        }
 

	
 
        protected void OnMinIngredientsChanged(object sender, EventArgs e)
 
        {
 
            Gtk.SpinButton button = (Gtk.SpinButton) sender;
 
            if (button.ValueAsInt > maxIngredientsSpinButton.ValueAsInt)
 
            {
 
                maxIngredientsSpinButton.Value = button.ValueAsInt;
 
            }
 
            maxIngredientsSpinButton.SetRange(button.ValueAsInt, maxIngredientsSpinButton.Adjustment.Upper);
 
        }
 

	
 
        protected void OnMaxIngredientsChanged(object sender, EventArgs e)
 
        {
 
            Gtk.SpinButton button = (Gtk.SpinButton) sender;
 
            if (button.ValueAsInt < minIngredientsSpinButton.ValueAsInt)
 
            {
 
                minIngredientsSpinButton.Value = button.ValueAsInt;
 
            }
 
            minIngredientsSpinButton.SetRange(1, button.ValueAsInt);
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnMaxRecipeChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnFullQuantityDepthChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnFullQuantityChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
 
        }
 

	
 
        protected void OnBegin(object sender, EventArgs e)
 
        {
 
            minIngredientsSpinButton.Sensitive = false;
 
            maxIngredientsSpinButton.Sensitive = false;
 
            //TODO ExportToWikiAction.Sensitive = false;
 
            maxRecipeSpinButton.Sensitive = false;
 
            beginButton.Sensitive = false; // TODO: change to "pause"?
 
            stopResumeButton.Sensitive = true;
 
            fullQuantitySpinButton.Sensitive = false;
 
            fullQuantityDepthSpinButton.Sensitive = false;
 
            reagentListView.Sensitive = false;
 
            checkButtonRibbon.Sensitive = false;
 

	
 
            countLabel.Text = String.Format("{0} / {1}", checkButtonRibbon.Active ? profile.RibbonCount : profile.RecipeCount, Palette.Count);
 

	
 
            // TODO: hook up event notifications
 
            // - progress
0 comments (0 inline, 0 general)