Changeset - 7b2e8ac4935e
[Not reviewed]
default
0 5 0
Jason Maltzen (jmaltzen) - 9 years ago 2015-12-25 03:17:40
jason.maltzen@unsanctioned.net
Save recipe generator state to allow resume across app runs. Periodically save current generated recipe list, instead of just saving on stop/pause.
5 files changed with 460 insertions and 82 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
...
 
@@ -2,7 +2,7 @@ syntax: glob
 
obj/
 
*.userprefs
 
*.orig
 
*.swp
 
*.zip
 
.DS_Store
 
mac/build/DesertPaintLab.app
 
mac/bin
RecipeGenerator.cs
Show inline comments
...
 
@@ -18,14 +18,16 @@
 
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
 THE SOFTWARE.
 
*/
 

	
 
using System;
 
using System.IO;
 
using System.Collections.Generic;
 
using System.Collections.Concurrent;
 
using System.Text.RegularExpressions;
 
using System.Threading;
 

	
 
namespace DesertPaintLab
 
{
 
    public class NewRecipeEventArgs : EventArgs
 
    {
...
 
@@ -52,16 +54,24 @@ namespace DesertPaintLab
 
            }
 
        }
 
    }
 

	
 
    public class RecipeGenerator
 
    {
 
        private class SearchNode
 
        protected class SearchNode
 
        {
 
            //int initialReagentCount;
 
            List<uint> reagents;
 
            public List<uint> Reagents
 
            {
 
                get
 
                {
 
                    return reagents;
 
                }
 
            }
 

	
 
            HashSet<uint> reagentInUse = new HashSet<uint>();
 
            List<Reagent> costSortedReagents;
 
            PaintRecipe testRecipe = null;
 
            public PaintRecipe TestRecipe
 
            {
 
                get
...
 
@@ -70,14 +80,19 @@ namespace DesertPaintLab
 
                }
 
                set
 
                {
 
                    testRecipe = value;
 
                }
 
            }
 

	
 
            public int InitialCount { get; private set; }
 
            public uint CurrentTargetQuantity { get; set; }
 
            public uint MaxQuantity { get; set; }
 
            public uint UsedQuantity { get; private set; }
 
            public uint CatalystCount { get; set; }
 

	
 
            uint maxReagents;
 
            public uint MaxReagents
 
            {
 
                get
 
                {
 
                    return maxReagents;
...
 
@@ -85,15 +100,12 @@ namespace DesertPaintLab
 
                set
 
                {
 
                    maxReagents = value;
 
                    currentWeights = new uint[maxReagents];
 
                }
 
            }
 
            public uint UsedQuantity { get; private set; }
 

	
 
            public uint CatalystCount { get; set; }
 

	
 
            uint[] currentWeights;
 
            public uint[] CurrentWeights
 
            {
 
                get
 
                {
...
 
@@ -122,19 +134,20 @@ namespace DesertPaintLab
 
                this.reagents.Add(NextFreeReagent(startReagent));
 
                InitialCount = 1; // don't iterate up beyond the start reagent
 
                MaxReagents = 1;
 
                UsedQuantity = 0;
 
            }
 

	
 
            public int InitialCount { get; private set; }
 
            public List<uint> Reagents
 
            public SearchNode(List<Reagent> costSortedReagents)
 
            {
 
                get
 
                {
 
                    return reagents;
 
                }
 
                this.costSortedReagents = costSortedReagents;
 
                this.reagents = new List<uint>();
 
                this.reagents.Add(NextFreeReagent(0));
 
                InitialCount = 0;
 
                MaxReagents = 1;
 
                UsedQuantity = 0;
 
            }
 

	
 
            public Reagent Reagent(int idx)
 
            {
 
                return costSortedReagents[(int)reagents[idx]];
 
            }
...
 
@@ -245,24 +258,151 @@ namespace DesertPaintLab
 
            public void SetWeight(int idx, uint quantity)
 
            {
 
                UsedQuantity -= currentWeights[idx];
 
                currentWeights[idx] = quantity;
 
                UsedQuantity += quantity;
 
            }
 

	
 
            public void SaveState(StreamWriter writer)
 
            {
 
                writer.WriteLine("---SearchNode---");
 
                writer.WriteLine("MaxReagents: {0}", MaxReagents);
 
                writer.WriteLine("Reagents: {0}", reagents.Count);
 
                for (int i = 0; i < reagents.Count; ++i)
 
                {
 
                    uint idx = reagents[i];
 
                    uint weight = currentWeights[i];
 
                    writer.WriteLine("Reagent: {0},{1},{2}", idx, reagentInUse.Contains(idx) ? 1 : 0, weight);
 
                }
 
                // pulled from parent: List<Reagent> costSortedReagents;
 
                // new on construct: PaintRecipe testRecipe = null;
 
                writer.WriteLine("CurrentTargetQuantity: {0}", CurrentTargetQuantity);
 
                writer.WriteLine("MaxQuantity: {0}", MaxQuantity);
 
                writer.WriteLine("UsedQuantity: {0}", UsedQuantity);
 
                writer.WriteLine("CatalystCount: {0}", CatalystCount);
 
                writer.WriteLine("InitialCount: {0}", InitialCount);
 
                writer.WriteLine("---EndNode---");
 
                
 
            }
 

	
 
            static Regex keyValueRegex = new Regex(@"(\w+)\:\s*(.*)\s*$");
 
            static Regex reagentPartsRegex = new Regex(@"(?<id>\d+),(?<inUse>\d+),(?<weight>\d+)");
 
            public bool LoadState(StreamReader reader)
 
            {
 
                string line = reader.ReadLine();
 
                if (!line.Equals("---SearchNode---"))
 
                {
 
                    return false;
 
                }
 

	
 
                bool success = true;
 
                Match match;
 
                int reagentIdx = 0;
 
                while ((line = reader.ReadLine()) != null)
 
                {
 
                    if (line.Equals("---EndNode---"))
 
                    {
 
                        break;
 
                    }
 
                    match = keyValueRegex.Match(line);
 
                    if (match.Success)
 
                    {
 
                        switch (match.Groups[1].Value)
 
                        {
 
                            case "Reagents":
 
                                {
 
                                    int reagentCount = int.Parse(match.Groups[2].Value);
 
                                    reagents = new List<uint>(reagentCount);
 
                                    reagentInUse.Clear();
 
                                    reagentIdx = 0;
 
                                }
 
                                break;
 
                            case "Reagent":
 
                                {
 
                                    Match reagentInfo = reagentPartsRegex.Match(match.Groups[2].Value);
 
                                    if (reagentInfo.Success)
 
                                    {
 
                                        uint reagentId = uint.Parse(reagentInfo.Groups["id"].Value);
 
                                        int isInUse = int.Parse(reagentInfo.Groups["inUse"].Value);
 
                                        uint weight = uint.Parse(reagentInfo.Groups["weight"].Value);
 
                                        reagents.Add(reagentId);
 
                                        currentWeights[reagentIdx] = weight;
 
                                        if (isInUse != 0)
 
                                        {
 
                                            reagentInUse.Add(reagentId);
 
                                        }
 
                                    }
 
                                    else
 
                                    {
 
                                        success = false;
 
                                    }
 
                                }
 
                                break;
 
                            case "CurrentTargetQuantity":
 
                                {
 
                                    uint value = uint.Parse(match.Groups[2].Value);
 
                                    CurrentTargetQuantity = value;
 
                                }
 
                                break;
 
                            case "MaxQuantity":
 
                                {
 
                                    uint value = uint.Parse(match.Groups[2].Value);
 
                                    MaxQuantity = value;
 
                                }
 
                                break;
 
                            case "MaxReagents":
 
                                {
 
                                    uint value = uint.Parse(match.Groups[2].Value);
 
                                    MaxReagents = value;
 
                                }
 
                                break;
 
                            case "UsedQuantity":
 
                                {
 
                                    uint value = uint.Parse(match.Groups[2].Value);
 
                                    UsedQuantity = value;
 
                                }
 
                                break;
 
                            case "CatalystCount":
 
                                {
 
                                    uint value = uint.Parse(match.Groups[2].Value);
 
                                    CatalystCount = value;
 
                                }
 
                                break;
 
                            case "InitialCount":
 
                                {
 
                                    int value = int.Parse(match.Groups[2].Value);
 
                                    InitialCount = value;
 
                                }
 
                                break;
 
                            default:
 
                                success = false;
 
                                break;
 
                        }
 
                    }
 
                    else
 
                    {
 
                        success = false;
 
                        break;
 
                    }
 
                }
 
                return success;
 
            }
 
        }
 

	
 
        const uint DEFAULT_MAX_QUANTITY = 14; // minimum recipe: 10 base + 4 catalysts
 
        const uint DEFAULT_MAX_REAGENTS = 5;
 

	
 
        //uint maxQuantity; // maximum number of total ingredients
 
        uint maxReagents; // maximum number of reagents to use in the recipe
 
        uint fullQuantityDepth; // at or equal this number of reagents, ignore ingredient settings for max quantity
 
        uint fullQuantity;  // The max number of a reagent to use at full quantity
 

	
 
        ReactionSet reactions;
 
        bool running = false;
 

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

	
 
        uint totalReagents;
 

	
...
 
@@ -279,14 +419,15 @@ namespace DesertPaintLab
 

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

	
 
        public RecipeGenerator()
 
        public RecipeGenerator(ReactionSet reactions)
 
        {
 
            this.reactions = reactions;
 
        }
 

	
 
        public SortedDictionary<string, PaintRecipe> Recipes
 
        {
 
            get
 
            {
...
 
@@ -299,60 +440,70 @@ namespace DesertPaintLab
 
            get
 
            {
 
                return recipeCount;
 
            }
 
        }
 

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

	
 
        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> recipes, ReactionSet reactions)
 
        public void InitRecipes(SortedDictionary<string, PaintRecipe> recipes)
 
        {
 
            if (running)
 
            {
 
                return;
 
            }
 
            this.reactions = reactions;
 
            foreach (PaintRecipe recipe in recipes.Values)
 
            {
 
                // TODO: copy?
 
                AddCheapestRecipe(recipe);
 
                PaintRecipe recipeCopy = new PaintRecipe(recipe);
 
                AddCheapestRecipe(recipeCopy);
 
            }
 
        }
 

	
 
        public void BeginRecipeGeneration(ReactionSet reactions, uint maxQuantity, uint maxReagents, uint fullQuantityDepth, uint fullQuantity)
 
        private void InitSortedReagents()
 
        {
 
            if (running)
 
            {
 
                // Already running - don't start again
 
                return;
 
            }
 
            this.running = true;
 

	
 
            this.reactions = reactions;
 
            //this.maxQuantity = maxQuantity;
 
            this.maxReagents = maxReagents;
 
            this.fullQuantity = fullQuantity;
 
            this.fullQuantityDepth = fullQuantityDepth;
 

	
 
            // first, sort reagents by cost.
 
            costSortedReagents.Clear();
 
            foreach (string name in ReagentManager.Names)
 
            {
 
                Reagent reagent = ReagentManager.GetReagent(name);
 
                if (reagent.Enabled)
 
                {
 
                    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();
 
            this.maxReagents = (uint)Math.Min(costSortedReagents.Count, this.maxReagents);
 

	
 
            totalReagents = (uint)costSortedReagents.Count;
 

	
 
            // Pre-populate recipes list with:
 
            // 1) 1-ingredient recipes @ 10db for all enabled ingredients with a count >= 10
...
 
@@ -365,62 +516,204 @@ namespace DesertPaintLab
 
                    recipe.Reactions = reactions;
 
                    recipe.AddReagent(reagent.Name, 10);
 
                    AddCheapestRecipe(recipe);
 
                }
 
            }
 

	
 
            while (!searchQueue.IsEmpty)
 
            {
 
                SearchNode node;
 
                searchQueue.TryDequeue(out node);
 
            }
 
            for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
 
            {
 
                SearchNode initialNode = new SearchNode(costSortedReagents, reagentIdx);
 
                initialNode.MaxQuantity = maxQuantity;
 
                initialNode.MaxReagents = maxReagents;
 
                searchQueue.Enqueue(initialNode);
 
            }
 
            
 
            // start worker threads to do the actual work
 

	
 
            requestCancel = false;
 
            running = true;
 
            // Start the workers thread
 
            for (int i = 0; i < costSortedReagents.Count; ++i)
 
            {
 
                Thread thr = new Thread(new ThreadStart(this.Generate));
 
                generatorThreads.Add(thr);
 
            }
 
            foreach (Thread thr in generatorThreads)
 
            {
 
                thr.Start();
 
            }
 
        
 
            ResumeRecipeGeneration();
 
        }
 

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

	
 
            for (int i = 0; i < costSortedReagents.Count; ++i)
 
            //System.Console.WriteLine("Resuming recipe generation: pre-threads={0} reagent count={1} search queue={2}", runningThreads, costSortedReagents.Count, searchQueue.Count);
 
            runningThreads = 0; // presumably!
 

	
 
            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);
 
                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 (SearchNode 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":
 
                                recipeCount = int.Parse(value);
 
                                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)
 
                                {
 
                                    SearchNode node = new SearchNode(costSortedReagents);
 
                                    success = success && node.LoadState(reader);
 
                                    if (success)
 
                                    {
 
                                        searchQueue.Enqueue(node);
 
                                    }
 
                                }
 
                                break;
 
                            default:
 
                                success = false;
 
                                break;
 
                        }
 
                    }
 
                    else
 
                    {
 
                        success = false;
 
                        break;
 
                    }
 
                }
 
                return success;
 
            }
 
        }
 

	
 
        private void Generate()
 
        {
 
            SearchNode node;
 

	
 
            lock(workerLock)
 
            {
 
                ++runningThreads;
 
            }
 

	
 
            bool ok = true;
 
            do
 
            {
 
                lock (workerLock)
 
                {
 
                    ok = searchQueue.TryDequeue(out node);
...
 
@@ -430,13 +723,13 @@ namespace DesertPaintLab
 
                    uint targetQuantity = node.MaxQuantity + 1;
 
                    do {
 
                        --targetQuantity;
 
                        node.InitForQuantity(targetQuantity);
 
                    } while (targetQuantity > 10 && (node.CurrentTargetQuantity != node.UsedQuantity));
 

	
 
                    while (ok = Iterate(node) && !requestCancel)
 
                    while ((ok = Iterate(node)) && !requestCancel)
 
                    {
 
                        if (Progress != null)
 
                        {
 
                            Progress(this, null);
 
                        }
 
                    }
...
 
@@ -448,23 +741,25 @@ namespace DesertPaintLab
 
                }
 
            } while (!requestCancel && ok);
 

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

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

	
 
        // Add the cheapest recipe to the recipe list
 
        // returns the discarded recipe from the pair (or null if no original recipe to replace)
 
        private PaintRecipe AddCheapestRecipe(PaintRecipe recipe)
...
 
@@ -695,18 +990,19 @@ namespace DesertPaintLab
 

	
 
            return true;
 
        }
 

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

	
 
        public void Stop()
 
        {
 
            this.requestCancel = true;
RecipeGeneratorWindow.cs
Show inline comments
...
 
@@ -28,19 +28,26 @@ namespace DesertPaintLab
 
    public partial class RecipeGeneratorWindow : Gtk.Window
 
    {
 
        RecipeGenerator generator;
 
        PlayerProfile profile;
 
        bool canceling = false;
 
        bool running = false;
 
        bool pauseForCheckpoint = false;
 

	
 
        const long RECIPE_SAVE_INTERVAL = 30000; // msec between saving recipes
 
        const long CHECKPOINT_INTERVAL = 17000; // msec between saving out generator state
 
        const string STATE_FILE = "dp_generator_state";
 

	
 
        static Gtk.ListStore colorStore = new Gtk.ListStore(typeof(string));
 

	
 
        List<KeyValuePair<string, string>> missingReactions = new List<KeyValuePair<string, string>>();
 

	
 
        long lastProgressUpdate;
 
        long lastStatusUpdate;
 
        long lastProfileSave;
 
        long lastCheckpoint;
 

	
 
        static public Gtk.ListStore RecipeModel
 
        {
 
            get
 
            {
 
                return colorStore;   
...
 
@@ -64,12 +71,14 @@ namespace DesertPaintLab
 
            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;
 

	
 
            profile.LoadRecipes();
...
 
@@ -77,15 +86,37 @@ namespace DesertPaintLab
 
            // init UI
 
            foreach (string key in profile.Recipes.Keys)
 
            {
 
                colorStore.AppendValues(key);
 
            }
 

	
 
            countLabel.Text = String.Format("{0} / {1}", profile.Recipes.Count, Palette.Count);
 
            canceling = false;
 
            running = false;
 
            pauseForCheckpoint = false;
 

	
 
            generator = new RecipeGenerator(profile.Reactions);
 
            generator.InitRecipes(profile.Recipes);
 

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

	
 
            string stateFile = System.IO.Path.Combine(profile.Directory, STATE_FILE);
 
            if (System.IO.File.Exists(stateFile))
 
            {
 
                generator.LoadState(stateFile);
 
                if (generator.CanResume)
 
                {
 
                    beginButton.Label = "Restart";
 
                    stopResumeButton.Label = "Resume";
 
                    stopResumeButton.Sensitive = true;
 
                }
 
            }
 
            countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
 

	
 
            Destroyed += OnDestroyed;
 
        }
 

	
 
        protected void OnMaxIngredientsChanged(object sender, EventArgs e)
 
        {
 
            // TODO: save profile setting
 
            // TODO: no longer permit resume
...
 
@@ -109,28 +140,22 @@ namespace DesertPaintLab
 
            // TODO: no longer permit resume
 
        }
 

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

	
 
            generator = new RecipeGenerator();
 

	
 
            generator.InitRecipes(profile.Recipes, profile.Reactions);
 
            countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
 

	
 
            generator.Progress += OnProgress;
 
            generator.Finished += OnFinished;
 
            generator.NewRecipe += OnNewRecipe;
 
            // TODO: hook up event notifications
 
            // - progress
 
            // - complete
 
            // - new recipe / recipe update
 

	
 
            // Total recipe search count
...
 
@@ -143,60 +168,87 @@ namespace DesertPaintLab
 
            //}
 
            //System.Console.WriteLine("Will search {0} reagent permutations.", recipePermutations);
 

	
 
            lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
 
            lastStatusUpdate = lastProgressUpdate;
 

	
 
            lastProfileSave = lastProgressUpdate;
 
            lastCheckpoint = lastProgressUpdate;
 

	
 
            running = true;
 
            stopResumeButton.Label = "Stop";
 
            canceling = false;
 
            pauseForCheckpoint = false;
 
            stopResumeButton.Label = "Pause";
 

	
 
            generator.BeginRecipeGeneration(profile.Reactions, (uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
 
            generator.BeginRecipeGeneration((uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
 
        }
 

	
 
        protected void OnStopResume(object sender, EventArgs e)
 
        {
 
            if (generator != null)
 
            {
 
                if (running)
 
                {
 
                    canceling = true;
 
                    pauseForCheckpoint = false;
 
                    generator.Stop();
 
                }
 
                else
 
                {
 
                    // this must be a resume
 
                    // Resume previous run
 
                    ExportToWikiAction.Sensitive = false;
 
                    IngredientsAction.Sensitive = false;
 
                    lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
 
                    lastStatusUpdate = lastProgressUpdate;
 
                    lastProfileSave = lastProgressUpdate;
 
                    lastCheckpoint = lastProgressUpdate;
 

	
 
                    canceling = false;
 
                        pauseForCheckpoint = false;
 
                    running = true;
 
        
 
                    stopResumeButton.Label = "Stop";
 
                    generator.BeginRecipeGeneration(profile.Reactions, (uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
 
                    stopResumeButton.Label = "Pause";
 
                    generator.ResumeRecipeGeneration();
 
                }
 
            }
 
        }
 

	
 
        protected void OnFinished(object sender, EventArgs args)
 
        {
 
            Gtk.Application.Invoke(delegate {
 
                running = false;
 
                beginButton.Sensitive = true;
 
                ExportAction.Sensitive = true;
 
                SettingsAction.Sensitive = true;
 
                stopResumeButton.Sensitive = false;
 
                maxIngredientsSpinButton.Sensitive = true;
 
                maxRecipeSpinButton.Sensitive = true;
 
                fullQuantitySpinButton.Sensitive = true;
 
                fullQuantityDepthSpinButton.Sensitive = true;
 
                //generator = null; // don't. Hang on to generator for resume.
 
                profile.SaveRecipes();
 
                if (canceling)
 
                generator.Wait();
 
                if (pauseForCheckpoint)
 
                {
 
                    pauseForCheckpoint = false;
 
                    generator.SaveState(System.IO.Path.Combine(profile.Directory, STATE_FILE));
 
                    generator.ResumeRecipeGeneration();
 
                }
 
                else
 
                {
 
                    stopResumeButton.Label = "Resume";
 
                    stopResumeButton.Sensitive = true;
 
                    running = false;
 
                    beginButton.Sensitive = true;
 
                    ExportToWikiAction.Sensitive = true;
 
                    IngredientsAction.Sensitive = true;
 
                    stopResumeButton.Sensitive = false;
 
                    maxIngredientsSpinButton.Sensitive = true;
 
                    maxRecipeSpinButton.Sensitive = true;
 
                    fullQuantitySpinButton.Sensitive = true;
 
                    fullQuantityDepthSpinButton.Sensitive = true;
 
                    //generator = null; // don't. Hang on to generator for resume.
 
                    profile.SaveRecipes();
 
                    if (canceling)
 
                    {
 
                        generator.SaveState(System.IO.Path.Combine(profile.Directory, STATE_FILE));
 
                        stopResumeButton.Label = "Resume";
 
                        stopResumeButton.Sensitive = true;
 
                        beginButton.Label = "Restart";
 
                    }
 
                    else
 
                    {
 
                        System.IO.File.Delete(System.IO.Path.Combine(profile.Directory, STATE_FILE));
 
                    }
 
                }
 
            });
 
        }
 

	
 
        protected void OnNewRecipe(object sender, NewRecipeEventArgs args)
 
        {
...
 
@@ -225,12 +277,20 @@ namespace DesertPaintLab
 
                    //    bool isMissingReactions = args.Recipe.CheckMissingReactions(ref missingReactions);
 
                    //    string missingReactionLabel = isMissingReactions ? "X" : "";
 
                    colorStore.AppendValues(args.Color); // , missingReactionLabel);
 
                    countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
 
                }
 
                profile.SetRecipe(recipe);
 

	
 
                long progressTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
 
                long delta = progressTime - lastProfileSave;
 
                if (delta >= RECIPE_SAVE_INTERVAL)
 
                {
 
                    profile.SaveRecipes();
 
                    lastProfileSave = progressTime;
 
                }
 
            });
 
        }
 

	
 
        protected void OnProgress(object sender, EventArgs args)
 
        {
 
            Gtk.Application.Invoke(delegate
...
 
@@ -248,12 +308,20 @@ namespace DesertPaintLab
 
                {
 
                    // Update recipe count label as well
 
                    statusLabel.Text = String.Format("Recipes searched: {0:N00}", generator.RecipeCount);
 
                    lastStatusUpdate = progressTime;
 
                }
 
                //progressBar.Fraction = (double)((generator.RecipeCount / 10000) % 100) / 100.0;
 

	
 
                delta = progressTime - lastCheckpoint;
 
                if (delta > CHECKPOINT_INTERVAL)
 
                {
 
                    pauseForCheckpoint = true;
 
                    lastCheckpoint = progressTime;
 
                    generator.Stop();
 
                }
 
            });
 
        }
 

	
 
        protected void OnColorSelected(object o, EventArgs args)
 
        {
 
            Gtk.TreeModel model;
...
 
@@ -303,9 +371,23 @@ namespace DesertPaintLab
 

	
 
        protected void OnShowIngredients(object sender, EventArgs e)
 
        {
 
            ReagentWindow win = new ReagentWindow(profile);
 
            win.Show();
 
        }
 

	
 
        protected void OnDestroyed(object o, EventArgs args)
 
        {
 
            if (running)
 
            {
 
                // window closed while generator running: stop and save
 
                generator.Finished -= OnFinished;
 
                generator.Progress -= OnProgress;
 
                generator.NewRecipe -= OnNewRecipe;
 
                generator.Stop();
 
                generator.Wait();
 
                generator.SaveState(System.IO.Path.Combine(profile.Directory, STATE_FILE));
 
            }
 
        }
 
    }
 
}
 

	
gtk-gui/gui.stetic
Show inline comments
...
 
@@ -3,13 +3,13 @@
 
  <configuration>
 
    <images-root-path>..</images-root-path>
 
    <target-gtk-version>2.12</target-gtk-version>
 
  </configuration>
 
  <import>
 
    <widget-library name="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <widget-library name="../bin/Debug/DesertPaintLab.exe" internal="true" />
 
    <widget-library name="../bin/Release/DesertPaintLab.exe" internal="true" />
 
  </import>
 
  <widget class="Gtk.Window" id="MainWindow" design-size="629 265">
 
    <action-group name="Default">
 
      <action id="FileAction">
 
        <property name="Type">Action</property>
 
        <property name="Accelerator">&lt;Alt&gt;f</property>
mac/build-mac-bundle.sh
Show inline comments
...
 
@@ -16,11 +16,11 @@ fi
 
/bin/cp ../bin/Release/DesertPaintLab.exe bin/DesertPaintLab.app/Contents/MacOS/
 
/bin/mkdir -p bin/DesertPaintLab.app/Contents/Resources
 
/bin/cp ../bin/Release/colors.txt bin/DesertPaintLab.app/Contents/Resources/
 
/bin/cp ../bin/Release/ingredients.txt bin/DesertPaintLab.app/Contents/Resources/
 
/bin/cp -r ../bin/Release/template bin/DesertPaintLab.app/Contents/Resources/template
 

	
 
/usr/bin/defaults write bin/DesertPaintLab.app/Info.plist CFBundleShortVersionString ${VERSION}
 
/usr/bin/defaults write bin/DesertPaintLab.app/Info.plist CFBundleVersion ${VERSION}
 
/usr/bin/defaults write `pwd`/bin/DesertPaintLab.app/Contents/Info.plist CFBundleShortVersionString ${VERSION}
 
/usr/bin/defaults write `pwd`/bin/DesertPaintLab.app/Contents/Info.plist CFBundleVersion ${VERSION}
 

	
 
# package up into a DMG
 
/usr/bin/hdiutil create -volname DesertPaintLab -srcfolder bin/ -ov -format UDIF -fs HFS+ DesertPaintLab.dmg
0 comments (0 inline, 0 general)