diff --git a/DesertPaintLab.csproj b/DesertPaintLab.csproj --- a/DesertPaintLab.csproj +++ b/DesertPaintLab.csproj @@ -84,6 +84,7 @@ + diff --git a/PaintRecipe.cs b/PaintRecipe.cs --- a/PaintRecipe.cs +++ b/PaintRecipe.cs @@ -101,6 +101,17 @@ namespace DesertPaintLab } } + override public string ToString() + { + string result = Palette.FindNearest(ReactedColor); + result += " |"; + foreach (RecipeIngredient ingr in recipe) + { + result += " " + ingr.ToString(); + } + return result; + } + public List Ingredients { get { diff --git a/PlayerProfile.cs b/PlayerProfile.cs --- a/PlayerProfile.cs +++ b/PlayerProfile.cs @@ -406,8 +406,14 @@ namespace DesertPaintLab public void ExportWikiRecipes(string file) { + StreamWriter writer = new StreamWriter(file); + ExportWikiFormat(writer); + } + + public void ExportWikiFormat(TextWriter writer) + { PaintRecipe recipe; - using (StreamWriter writer = new StreamWriter(file)) + using (writer) { writer.WriteLine("{| class='wikitable sortable' border=\"1\" style=\"background-color:#DEB887;\""); writer.WriteLine("! Color !! Recipe !! Verified"); @@ -439,7 +445,7 @@ namespace DesertPaintLab writer.WriteLine("|}"); } } - + public Reaction FindReaction(Reagent reagent1, Reagent reagent2) { return reactions.Find(reagent1, reagent2); diff --git a/RecipeGenerator.cs b/RecipeGenerator.cs --- a/RecipeGenerator.cs +++ b/RecipeGenerator.cs @@ -57,404 +57,17 @@ namespace DesertPaintLab public class RecipeGenerator { - protected class SearchNode - { - //int initialReagentCount; - uint[] reagents; - public uint[] Reagents - { - get - { - return reagents; - } - } - uint INVALID_REAGENT; - int nextReagentPos; - public int ReagentCount - { - get - { - return nextReagentPos; - } - } - - bool[] reagentInUse; - List costSortedReagents; - PaintRecipe testRecipe = null; - public PaintRecipe TestRecipe - { - get - { - return testRecipe; - } - 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; - } - set - { - maxReagents = value; - currentWeights = new uint[maxReagents]; - } - } - - uint[] currentWeights; - public uint[] CurrentWeights - { - get - { - return currentWeights; - } - } - - public SearchNode(List costSortedReagents, uint[] reagents) - { - this.costSortedReagents = new List(costSortedReagents); - this.reagents = new uint[costSortedReagents.Count]; - INVALID_REAGENT = (uint)costSortedReagents.Count; - nextReagentPos = reagents.Length; - for (int i = this.reagents.Length-1; i >= this.reagents.Length; --i) - { - reagents[i] = INVALID_REAGENT; - } - for (int i = reagents.Length-1; i >= 0; --i) - { - this.reagents[i] = reagents[i]; - if (reagents[i] == INVALID_REAGENT) - { - nextReagentPos = i; - } - } - reagentInUse = new bool[costSortedReagents.Count]; - for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) - { - reagentInUse[reagentIdx] = false; - } - foreach (uint reagentIdx in this.reagents) - { - if (reagentIdx != INVALID_REAGENT) - { - reagentInUse[reagentIdx] = true; - } - } - InitialCount = nextReagentPos; - MaxReagents = (uint)nextReagentPos; // better set this later! - UsedQuantity = 0; - } - - // top-level search - public SearchNode(List costSortedReagents, uint startReagent) - { - this.costSortedReagents = new List(costSortedReagents); - this.reagents = new uint[costSortedReagents.Count]; - INVALID_REAGENT = (uint)costSortedReagents.Count; - nextReagentPos = 0; - for (int i = 0; i < reagents.Length; ++i) - { - this.reagents[i] = INVALID_REAGENT; - } - reagentInUse = new bool[costSortedReagents.Count]; - for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) - { - reagentInUse[reagentIdx] = false; - } - this.reagents[nextReagentPos++] = NextFreeReagent(startReagent); - InitialCount = 1; // don't iterate up beyond the start reagent - MaxReagents = 1; - UsedQuantity = 0; - } - - public SearchNode(List costSortedReagents) - { - this.costSortedReagents = costSortedReagents; - this.reagents = new uint[costSortedReagents.Count]; - INVALID_REAGENT = (uint)costSortedReagents.Count; - nextReagentPos = 0; - for (int i = 0; i < reagents.Length; ++i) - { - this.reagents[i] = INVALID_REAGENT; - } - reagentInUse = new bool[costSortedReagents.Count]; - for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) - { - reagentInUse[reagentIdx] = false; - } - this.reagents[nextReagentPos++] = NextFreeReagent(0); - InitialCount = 0; - MaxReagents = 1; - UsedQuantity = 0; - } - - public Reagent Reagent(int idx) - { - return costSortedReagents[(int)reagents[idx]]; - } - - public uint LastReagent - { - get - { - return reagents[nextReagentPos - 1]; - } - } - - public void RemoveLastReagent() - { - uint reagentIdx = reagents[nextReagentPos-1]; - ReleaseReagent(reagentIdx); - if (costSortedReagents[(int)reagentIdx].IsCatalyst) - { - --CatalystCount; - } - reagents[nextReagentPos-1] = INVALID_REAGENT; - --nextReagentPos; - } - - public void ReplaceLastReagent(uint reagentIdx) - { - uint oldReagentIdx = reagents[nextReagentPos-1]; - ReleaseReagent(oldReagentIdx); - reagents[nextReagentPos-1] = reagentIdx; - if (costSortedReagents[(int)oldReagentIdx].IsCatalyst) - { - --CatalystCount; - } - if (costSortedReagents[(int)reagentIdx].IsCatalyst) - { - ++CatalystCount; - } - } + public enum SearchType { + DEPTH_FIRST, + BREADTH_FIRST + }; - public uint NextFreeReagent(uint startIdx) - { - uint idx = startIdx; - for (; idx < costSortedReagents.Count; ++idx) - { - bool inUse = reagentInUse[idx]; - if ((inUse == false) && (costSortedReagents[(int)idx].Enabled)) - { - //Console.WriteLine("Found free reagent idx {0}", idx); - reagentInUse[idx] = true; - return idx; - } - } - //Console.WriteLine("Failed to find free reagent."); - return (uint)costSortedReagents.Count; - } - - private void ReleaseReagent(uint reagentIdx) - { - reagentInUse[reagentIdx] = false; - } - - public bool AddNextReagent() - { - bool ok = (nextReagentPos < MaxReagents); - if (ok) - { - uint nextReagent = NextFreeReagent(0); - reagents[nextReagentPos++] = nextReagent; - if (costSortedReagents[(int)nextReagent].IsCatalyst) - { - ++CatalystCount; - } - InitForQuantity(CurrentTargetQuantity); - } - return ok; - } - - public void InitForQuantity(uint quantity) - { - //System.Console.WriteLine("Target quantity: {0}, reagent count: {1}", quantity, reagents.Count); - CurrentTargetQuantity = quantity; - if (CurrentTargetQuantity < (10 + CatalystCount)) - { - return; - } - UsedQuantity = 0; - uint remainingReagents = ((uint)nextReagentPos - CatalystCount); - uint remainingWeight = CurrentTargetQuantity - CatalystCount; - for (int i = 0; i < nextReagentPos; ++i) - { - Reagent reagent = Reagent(i); - - if (reagent.IsCatalyst) - { - currentWeights[i] = 1; - ++UsedQuantity; - } - else - { - uint weight = (uint)Math.Min(remainingWeight - (remainingReagents-1), reagent.RecipeMax); - remainingWeight -= weight; - currentWeights[i] = weight; - UsedQuantity += weight; - } - --remainingReagents; - } - } - - 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}", nextReagentPos); - for (int i = 0; i < nextReagentPos; ++i) - { - uint idx = reagents[i]; - uint weight = currentWeights[i]; - writer.WriteLine("Reagent: {0},{1},{2}", idx, reagentInUse[idx] ? 1 : 0, weight); - } - // pulled from parent: List 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(@"(?\d+),(?\d+),(?\d+)"); - public bool LoadState(StreamReader reader) - { - string line = reader.ReadLine(); - if (!line.Equals("---SearchNode---")) - { - return false; - } + public SearchType Mode { get; set; } - bool success = true; - Match match; - 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); - for (int i = 0; i < reagents.Length; ++i) - { - reagents[i] = INVALID_REAGENT; - reagentInUse[i] = false; - } - nextReagentPos = 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[nextReagentPos] = reagentId; - currentWeights[nextReagentPos] = weight; - if (isInUse != 0) - { - if (reagentId != INVALID_REAGENT) - { - reagentInUse[reagentId] = true; - } - } - ++nextReagentPos; - } - 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 + public uint MaxQuantity { get; private set; } // maximum number of total ingredients + public uint MaxReagents { get; private set; } // maximum number of reagents to use in the recipe + public uint FullQuantityDepth { get; private set; } // at or equal this number of reagents, ignore ingredient settings for max quantity + public uint FullQuantity { get; private set; } // The max number of a reagent to use at full quantity ReactionSet reactions; bool running = false; @@ -468,15 +81,17 @@ namespace DesertPaintLab List costSortedReagents = new List(); - ConcurrentQueue searchQueue = new ConcurrentQueue(); + ConcurrentQueue searchQueue = new ConcurrentQueue(); - int recipeCount = 0; + ulong recipeCount = 0; List generatorThreads = new List(); Object workerLock = new Object(); bool requestCancel = false; + StreamWriter log; + // events public event EventHandler Finished; public event EventHandler Progress; @@ -484,6 +99,7 @@ namespace DesertPaintLab public RecipeGenerator(ReactionSet reactions) { + Mode = SearchType.BREADTH_FIRST; this.reactions = reactions; foreach (PaintColor color in Palette.Colors) { @@ -500,7 +116,7 @@ namespace DesertPaintLab } } - public int RecipeCount + public ulong RecipeCount { get { @@ -516,6 +132,21 @@ namespace DesertPaintLab } } + public string Log + { + set + { + if (value != null) + { + log = new StreamWriter(value); + } + else + { + log = null; + } + } + } + private class ReagentCostSort : IComparer { public int Compare(Reagent reagent1, Reagent reagent2) @@ -556,50 +187,63 @@ namespace DesertPaintLab return; } - //this.maxQuantity = maxQuantity; - this.maxReagents = maxReagents; - this.fullQuantity = fullQuantity; - this.fullQuantityDepth = fullQuantityDepth; + 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 // 2) any previously-generated recipes + int enabledReagentCount = 0; PaintRecipe recipe = new PaintRecipe(); recipe.Reactions = reactions; foreach (Reagent reagent in costSortedReagents) { - if (!reagent.IsCatalyst && reagent.RecipeMax >= 10 && reagent.Enabled) + if (reagent.Enabled) { - recipe.Clear(); - recipe.AddReagent(reagent.Name, 10); - AddCheapestRecipe(recipe); + if (!reagent.IsCatalyst && ((reagent.RecipeMax >= 10) || ((FullQuantityDepth > 0) && (FullQuantity >= 10)))) + { + recipe.Clear(); + recipe.AddReagent(reagent.Name, 10); + AddCheapestRecipe(recipe); + } + ++enabledReagentCount; } } + this.MaxReagents = (uint)Math.Min(enabledReagentCount, this.MaxReagents); while (!searchQueue.IsEmpty) { - SearchNode node; + RecipeSearchNode node; searchQueue.TryDequeue(out node); } for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) { if (costSortedReagents[(int)reagentIdx].Enabled) { - SearchNode initialNode = new SearchNode(costSortedReagents, reagentIdx); + RecipeSearchNode initialNode = new RecipeSearchNode(costSortedReagents, reagentIdx); + initialNode.FullQuantity = FullQuantity; + initialNode.FullQuantityDepth = FullQuantityDepth; initialNode.MaxQuantity = maxQuantity; initialNode.MaxReagents = maxReagents; searchQueue.Enqueue(initialNode); } } + + recipeCount = 0; + + if (log != null) + { + log.WriteLine("Begin recipe generation: MaxQuantity={0} MaxReagents={1} FullQuantity={2} FullQuantityDepth={3}", MaxQuantity, MaxReagents, FullQuantity, FullQuantityDepth); + } // start worker threads to do the actual work - ResumeRecipeGeneration(); } @@ -613,10 +257,13 @@ namespace DesertPaintLab running = true; requestCancel = false; - //System.Console.WriteLine("Resuming recipe generation: pre-threads={0} reagent count={1} search queue={2}", runningThreads, costSortedReagents.Count, searchQueue.Count); + if (log != null) + { + log.WriteLine("Resuming recipe generation: pre-threads={0} reagent count={1} search queue={2}", runningThreads, costSortedReagents.Count, searchQueue.Count); + } runningThreads = 0; // presumably! - int threadCount = Math.Min(costSortedReagents.Count, searchQueue.Count); + int threadCount = 1; // Math.Min(costSortedReagents.Count, searchQueue.Count); if (threadCount == 0) { if (Finished != null) @@ -646,11 +293,12 @@ namespace DesertPaintLab using (StreamWriter writer = new StreamWriter(file, false)) { - writer.WriteLine("MaxReagents: {0}", maxReagents); - writer.WriteLine("FullQuantityDepth: {0}", fullQuantityDepth); - writer.WriteLine("FullQuantity: {0}", fullQuantity); + writer.WriteLine("MaxReagents: {0}", MaxReagents); + writer.WriteLine("FullQuantityDepth: {0}", FullQuantityDepth); + writer.WriteLine("FullQuantity: {0}", FullQuantity); writer.WriteLine("TotalReagents: {0}", totalReagents); writer.WriteLine("RecipeCount: {0}", recipeCount); + writer.WriteLine("SearchType: {0}", Mode.ToString()); foreach (KeyValuePair pair in recipes) { PaintRecipe recipe = pair.Value; @@ -663,7 +311,7 @@ namespace DesertPaintLab writer.WriteLine("EndRecipe: {0}", colorName); } writer.WriteLine("SearchNodes: {0}", searchQueue.Count); - foreach (SearchNode node in searchQueue) + foreach (RecipeSearchNode node in searchQueue) { node.SaveState(writer); } @@ -698,19 +346,24 @@ namespace DesertPaintLab switch(match.Groups["key"].Value) { case "MaxReagents": - maxReagents = uint.Parse(value); + MaxReagents = uint.Parse(value); break; case "FullQuantityDepth": - fullQuantityDepth = uint.Parse(value); + FullQuantityDepth = uint.Parse(value); break; case "FullQuantity": - fullQuantity = uint.Parse(value); + FullQuantity = uint.Parse(value); break; case "TotalReagents": totalReagents = uint.Parse(value); break; case "RecipeCount": - recipeCount = int.Parse(value); + 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(); @@ -745,7 +398,11 @@ namespace DesertPaintLab int nodeCount = int.Parse(match.Groups["value"].Value); for (int i = 0; i < nodeCount; ++i) { - SearchNode node = new SearchNode(costSortedReagents); + RecipeSearchNode node = new RecipeSearchNode(costSortedReagents); + node.FullQuantity = FullQuantity; + node.FullQuantityDepth = FullQuantityDepth; + node.MaxReagents = MaxReagents; + node.MaxQuantity = MaxQuantity; success = success && node.LoadState(reader); if (success) { @@ -753,6 +410,9 @@ namespace DesertPaintLab } } break; + case "SearchType": + Mode = (SearchType)Enum.Parse(typeof(SearchType), match.Groups["value"].Value); + break; default: success = false; break; @@ -770,7 +430,7 @@ namespace DesertPaintLab private void Generate() { - SearchNode node; + RecipeSearchNode node; lock(workerLock) { @@ -786,17 +446,38 @@ namespace DesertPaintLab } if (ok) { - uint targetQuantity = node.MaxQuantity + 1; - do { - --targetQuantity; - node.InitForQuantity(targetQuantity); - } while (targetQuantity > 10 && (node.CurrentTargetQuantity != node.UsedQuantity)); - - while ((ok = Iterate(node)) && !requestCancel) + if (Mode == SearchType.DEPTH_FIRST) { - if (Progress != null) + uint targetQuantity = (node.ReagentCount <= FullQuantityDepth) ? ((uint)node.ReagentCount * FullQuantity) : node.MaxQuantity + 1; + do { + --targetQuantity; + node.InitForQuantity(targetQuantity); + } while (targetQuantity > 10 && (node.CurrentTargetQuantity != node.UsedQuantity)); + + while ((ok = IterateDepthFirst(node)) && !requestCancel) { - Progress(this, null); + if (Progress != null) + { + Progress(this, null); + } + } + } + else + { + // breadth-first search + uint targetQuantity = 9; + uint quantityLimit = (node.ReagentCount <= FullQuantityDepth) ? (FullQuantity * (uint)node.ReagentCount) : node.MaxQuantity; + do { + ++targetQuantity; + node.InitForQuantity(targetQuantity); + } while ((targetQuantity <= quantityLimit) && (node.CurrentTargetQuantity != node.UsedQuantity)); + + while ((ok = IterateBreadthFirst(node)) && !requestCancel) + { + if (Progress != null) + { + Progress(this, null); + } } } if (ok) @@ -882,8 +563,10 @@ namespace DesertPaintLab //} } - private bool Iterate(SearchNode node) + private bool IterateDepthFirst(RecipeSearchNode node) { + TestCurrentRecipe(node); + // pick recipe quantities at current recipe ingredients/size if (NextRecipe(node)) { @@ -897,7 +580,7 @@ namespace DesertPaintLab if (NextRecipeSize(node)) { - //System.Console.WriteLine("Found next recipee size {0}", node.CurrentTargetQuantity); + //System.Console.WriteLine("Found next recipe size {0}", node.CurrentTargetQuantity); return true; } @@ -950,9 +633,146 @@ namespace DesertPaintLab return true; } - private bool NextRecipe(SearchNode node) + private bool IterateBreadthFirst(RecipeSearchNode node) { - // First, run the current recipe + // 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.MaxQuantity; + 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); } } + return true; + } + } + } while (newQuantity < quantityLimit); + + // 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.MaxQuantity, node.UsedQuantity); + node.InitForQuantity(10+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 > node.InitialCount) + { + while (node.ReagentCount > node.InitialCount) + { + 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 == node.InitialCount) + { + // 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); } } + } + } + } + else + { + //Console.WriteLine("Pop last reagent"); + node.RemoveLastReagent(); + if (node.ReagentCount == node.InitialCount) + { + // 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); } } + } + } + } + // fill in the nodes up to the current depth + if (node.ReagentCount >= node.InitialCount) + { + 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); } } + recipeFound = false; + } + } + } + } + //Console.WriteLine("Catalysts: {0} Reagents: {1} Initial: {2}", node.CatalystCount, node.ReagentCount, node.InitialCount); + } while ((node.CatalystCount >= node.ReagentCount) && (node.ReagentCount >= node.InitialCount)); // 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); } } + } + } while (currentDepth <= node.MaxReagents); + if (recipeFound) + { + node.InitForQuantity(10+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); } + } + } + + return recipeFound; + } + + private void TestCurrentRecipe(RecipeSearchNode node) + { if (node.TestRecipe == null) { node.TestRecipe = new PaintRecipe(); @@ -964,12 +784,17 @@ namespace DesertPaintLab 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 < 10) { // not possible to make a valid recipe + Console.WriteLine("Insufficient remaining weight"); return false; } //uint remainingReagents = (uint)node.Reagents.Count - node.CatalystCount; @@ -991,7 +816,7 @@ namespace DesertPaintLab { --reagentsBelow; Reagent reagent = node.Reagent(j); - uint allocated = (uint)Math.Min(reagent.IsCatalyst ? 1 : (depth <= fullQuantityDepth ? fullQuantity : reagent.RecipeMax), weightToConsume - reagentsBelow); + uint allocated = (uint)Math.Min(reagent.IsCatalyst ? 1 : (depth <= FullQuantityDepth ? FullQuantity : reagent.RecipeMax), weightToConsume - reagentsBelow); if (allocated > 100) { Console.WriteLine("ACK: allocated = {0}", allocated); @@ -1004,7 +829,7 @@ namespace DesertPaintLab else { Reagent reagent = node.Reagent(i); - spaceBelow += (reagent.IsCatalyst ? 1 : (depth <= fullQuantityDepth ? fullQuantity : reagent.RecipeMax)); + spaceBelow += (reagent.IsCatalyst ? 1 : (depth <= FullQuantityDepth ? FullQuantity : reagent.RecipeMax)); weightToConsume += currentWeight; ++reagentsBelow; } @@ -1027,7 +852,7 @@ namespace DesertPaintLab return (weightToConsume == 0); } - private bool NextRecipeSize(SearchNode node) + private bool NextRecipeSize(RecipeSearchNode node) { uint newQuantity = node.CurrentTargetQuantity - 1; if (newQuantity < (10 + node.CatalystCount)) diff --git a/RecipeGeneratorWindow.cs b/RecipeGeneratorWindow.cs --- a/RecipeGeneratorWindow.cs +++ b/RecipeGeneratorWindow.cs @@ -21,6 +21,7 @@ */ using System; +using System.IO; using System.Collections.Generic; using System.Collections.Concurrent; @@ -61,6 +62,9 @@ namespace DesertPaintLab ConcurrentQueue pendingNewRecipes = new ConcurrentQueue(); + Gtk.ListStore reagentListStore; + // end reagent view + public RecipeGeneratorWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel) { this.profile = profile; @@ -73,6 +77,7 @@ namespace DesertPaintLab fullQuantityDepthSpinButton.SetRange(0, ReagentManager.Names.Count); maxIngredientsSpinButton.SetRange(0, ReagentManager.Names.Count); + Gtk.TreeViewColumn recipeColorColumn = new Gtk.TreeViewColumn(); Gtk.CellRendererText recipeColumnCell = new Gtk.CellRendererText(); recipeColorColumn.PackStart(recipeColumnCell, true); @@ -88,6 +93,10 @@ namespace DesertPaintLab 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(); // init UI @@ -120,8 +129,14 @@ namespace DesertPaintLab beginButton.Label = "Restart"; stopResumeButton.Label = "Resume"; stopResumeButton.Sensitive = true; + + maxRecipeSpinButton.Value = Math.Max(generator.MaxQuantity, 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); Destroyed += OnDestroyed; @@ -129,6 +144,65 @@ namespace DesertPaintLab 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.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 (); + 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; + 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; + 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.AppendColumn(reagentEnabledColumn); + reagentListView.AppendColumn(reagentNameColumn); + reagentListView.AppendColumn(reagentCostColumn); + reagentListView.AppendColumn(reagentMaxColumn); + + ShowAll(); } protected void OnMaxIngredientsChanged(object sender, EventArgs e) @@ -165,6 +239,7 @@ namespace DesertPaintLab stopResumeButton.Sensitive = true; fullQuantitySpinButton.Sensitive = false; fullQuantityDepthSpinButton.Sensitive = false; + reagentListView.Sensitive = false; countLabel.Text = String.Format("{0} / {1}", profile.RecipeCount, Palette.Count); @@ -212,13 +287,22 @@ namespace DesertPaintLab // Resume previous run ExportToWikiAction.Sensitive = false; IngredientsAction.Sensitive = false; + reagentListView.Sensitive = false; + maxIngredientsSpinButton.Sensitive = false; + maxRecipeSpinButton.Sensitive = false; + beginButton.Sensitive = false; + stopResumeButton.Sensitive = true; + fullQuantitySpinButton.Sensitive = false; + fullQuantityDepthSpinButton.Sensitive = false; + reagentListView.Sensitive = false; + lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; lastStatusUpdate = lastProgressUpdate; lastProfileSave = lastProgressUpdate; lastCheckpoint = lastProgressUpdate; canceling = false; - pauseForCheckpoint = false; + pauseForCheckpoint = false; running = true; stopResumeButton.Label = "Pause"; @@ -247,6 +331,7 @@ namespace DesertPaintLab maxRecipeSpinButton.Sensitive = true; fullQuantitySpinButton.Sensitive = true; fullQuantityDepthSpinButton.Sensitive = true; + reagentListView.Sensitive = false; //generator = null; // don't. Hang on to generator for resume. profile.SaveRecipes(); if (canceling) @@ -366,19 +451,15 @@ namespace DesertPaintLab PaintRecipe recipe; if (profile.Recipes.TryGetValue(colorName, out recipe)) { - foreach (Gtk.Widget child in recipeListBox.AllChildren) - { - recipeListBox.Remove(child); - } + Gtk.ListStore store = (Gtk.ListStore)recipeIngredientsView.Model; + store.Clear(); if (recipe.CheckMissingReactions(ref missingReactions)) { statusLabel.Text = "WARNING: This recipe includes reactions that have not yet been recorded."; } foreach (PaintRecipe.RecipeIngredient ingredient in recipe.Ingredients) { - Gtk.Label label = new Gtk.Label(ingredient.quantity.ToString() + " " + ingredient.name); - recipeListBox.PackStart(label); - label.Show(); + store.AppendValues(ingredient.quantity.ToString(), ingredient.name); } } paintSwatch.Color = recipe.ReactedColor; @@ -421,6 +502,107 @@ namespace DesertPaintLab generator.SaveState(System.IO.Path.Combine(profile.Directory, STATE_FILE)); } } + + // Reagent view handling + private void RenderReagentToggle (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) + { + Reagent reagent = (Reagent) model.GetValue (iter, 0); + Gtk.CellRendererToggle toggle = (cell as Gtk.CellRendererToggle); + toggle.Active = reagent.Enabled; + toggle.Activatable = !reagent.IsCatalyst; + } + private void RenderReagentCost (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) + { + Reagent reagent = (Reagent) model.GetValue (iter, 0); + (cell as Gtk.CellRendererText).Text = reagent.Cost.ToString(); + } + private void RenderReagentQuantity (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) + { + Reagent reagent = (Reagent) model.GetValue (iter, 0); + (cell as Gtk.CellRendererText).Text = reagent.RecipeMax.ToString(); + } + + private void OnReagentCostChanged(object o, Gtk.EditedArgs args) + { + uint newCost; + if (uint.TryParse(args.NewText, out newCost)) + { + Gtk.TreeIter iter; + reagentListStore.GetIter (out iter, new Gtk.TreePath (args.Path)); + + Reagent reagent = (Reagent) reagentListStore.GetValue (iter, 0); + if (reagent.Cost != newCost) + { + reagent.Cost = newCost; + SaveReagentSettings(); + } + } + } + + private void OnReagentQuantityChanged(object o, Gtk.EditedArgs args) + { + uint newMax; + if (uint.TryParse(args.NewText, out newMax)) + { + Gtk.TreeIter iter; + reagentListStore.GetIter (out iter, new Gtk.TreePath (args.Path)); + + Reagent reagent = (Reagent) reagentListStore.GetValue (iter, 0); + if (reagent.RecipeMax != newMax) + { + reagent.RecipeMax = newMax; + SaveReagentSettings(); + } + } + } + + private void OnReagentEnableToggled(object o, Gtk.ToggledArgs args) + { + Gtk.CellRendererToggle btn = (Gtk.CellRendererToggle)o; + Gtk.TreeIter iter; + reagentListStore.GetIter (out iter, new Gtk.TreePath (args.Path)); + + Reagent reagent = (Reagent) reagentListStore.GetValue (iter, 0); + if (btn.Active != reagent.Enabled) + { + reagent.Enabled = btn.Active; + SaveReagentSettings(); + } + } + + private void SaveReagentSettings() + { + // save out state + ReagentManager.SaveProfileReagents(profile.ReagentFile); + } + + protected void OnCopyRecipeToClipboard(object sender, EventArgs e) + { + Gtk.TreeModel model; + Gtk.TreeIter iter; + Gtk.Clipboard clipboard = recipeIngredientsView.GetClipboard(Gdk.Selection.Clipboard); + + Gtk.TreeSelection selection = recipeList.Selection; + if ((selection != null) && selection.GetSelected(out model, out iter)) + { + string colorName = (string)colorStore.GetValue(iter, 0); + PaintRecipe recipe; + if (profile.Recipes.TryGetValue(colorName, out recipe)) + { + clipboard.Text = recipe.ToString(); + } + } + } + + protected void OnCopyRecipeListToClipboard(object sender, EventArgs e) + { + Gtk.Clipboard clipboard = recipeList.GetClipboard(Gdk.Selection.Clipboard); + + StringWriter writer = new StringWriter(); + profile.ExportWikiFormat(writer); + + clipboard.Text = writer.ToString(); + } } } diff --git a/RecipeSearchNode.cs b/RecipeSearchNode.cs new file mode 100644 --- /dev/null +++ b/RecipeSearchNode.cs @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2015, Jason Maltzen + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + 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.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace DesertPaintLab +{ + public class RecipeSearchNode + { + //int initialReagentCount; + uint[] reagents; + public uint[] Reagents + { + get + { + return reagents; + } + } + uint INVALID_REAGENT; + int nextReagentPos; + public int ReagentCount + { + get + { + return nextReagentPos; + } + } + + bool[] reagentInUse; + List costSortedReagents; + PaintRecipe testRecipe = null; + public PaintRecipe TestRecipe + { + get + { + return testRecipe; + } + 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; } + public uint FullQuantityDepth { get; set; } + public uint FullQuantity { get; set; } + + uint maxReagents; + public uint MaxReagents + { + get + { + return maxReagents; + } + set + { + maxReagents = value; + currentWeights = new uint[maxReagents]; + } + } + + uint[] currentWeights; + public uint[] CurrentWeights + { + get + { + return currentWeights; + } + } + + public RecipeSearchNode(List costSortedReagents, uint[] reagents) + { + this.costSortedReagents = new List(costSortedReagents); + this.reagents = new uint[costSortedReagents.Count]; + INVALID_REAGENT = (uint)costSortedReagents.Count; + nextReagentPos = reagents.Length; + for (int i = this.reagents.Length-1; i >= this.reagents.Length; --i) + { + reagents[i] = INVALID_REAGENT; + } + for (int i = reagents.Length-1; i >= 0; --i) + { + this.reagents[i] = reagents[i]; + if (reagents[i] == INVALID_REAGENT) + { + nextReagentPos = i; + } + } + reagentInUse = new bool[costSortedReagents.Count]; + for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) + { + reagentInUse[reagentIdx] = false; + } + foreach (uint reagentIdx in this.reagents) + { + if (reagentIdx != INVALID_REAGENT) + { + reagentInUse[reagentIdx] = true; + } + } + InitialCount = nextReagentPos; + MaxReagents = (uint)nextReagentPos; // better set this later! + UsedQuantity = 0; + } + + // top-level search + public RecipeSearchNode(List costSortedReagents, uint startReagent) + { + this.costSortedReagents = new List(costSortedReagents); + this.reagents = new uint[costSortedReagents.Count]; + INVALID_REAGENT = (uint)costSortedReagents.Count; + nextReagentPos = 0; + for (int i = 0; i < reagents.Length; ++i) + { + this.reagents[i] = INVALID_REAGENT; + } + reagentInUse = new bool[costSortedReagents.Count]; + for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) + { + reagentInUse[reagentIdx] = false; + } + this.reagents[nextReagentPos++] = NextFreeReagent(startReagent); + //Console.WriteLine("Added reagent {0} at pos {1}", this.reagents[nextReagentPos-1], nextReagentPos-1); + InitialCount = 1; // don't iterate up beyond the start reagent + MaxReagents = 1; + UsedQuantity = 0; + } + + public RecipeSearchNode(List costSortedReagents) + { + this.costSortedReagents = costSortedReagents; + this.reagents = new uint[costSortedReagents.Count]; + INVALID_REAGENT = (uint)costSortedReagents.Count; + nextReagentPos = 0; + for (int i = 0; i < reagents.Length; ++i) + { + this.reagents[i] = INVALID_REAGENT; + } + reagentInUse = new bool[costSortedReagents.Count]; + for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx) + { + reagentInUse[reagentIdx] = false; + } + this.reagents[nextReagentPos++] = NextFreeReagent(0); + InitialCount = 0; + MaxReagents = 1; + UsedQuantity = 0; + } + + public Reagent Reagent(int idx) + { + return costSortedReagents[(int)reagents[idx]]; + } + + public uint LastReagent + { + get + { + return reagents[nextReagentPos - 1]; + } + } + + public void RemoveLastReagent() + { + uint reagentIdx = reagents[nextReagentPos-1]; + ReleaseReagent(reagentIdx); + if (costSortedReagents[(int)reagentIdx].IsCatalyst) + { + --CatalystCount; + } + reagents[nextReagentPos-1] = INVALID_REAGENT; + --nextReagentPos; + } + + public void ReplaceLastReagent(uint reagentIdx) + { + uint oldReagentIdx = reagents[nextReagentPos-1]; + ReleaseReagent(oldReagentIdx); + reagents[nextReagentPos-1] = reagentIdx; + if (costSortedReagents[(int)oldReagentIdx].IsCatalyst) + { + --CatalystCount; + } + if (costSortedReagents[(int)reagentIdx].IsCatalyst) + { + ++CatalystCount; + } + } + + public uint NextFreeReagent(uint startIdx) + { + uint idx = startIdx; + for (; idx < costSortedReagents.Count; ++idx) + { + bool inUse = reagentInUse[idx]; + if ((inUse == false) && (costSortedReagents[(int)idx].Enabled)) + { + //Console.WriteLine("Found free reagent idx {0}", idx); + reagentInUse[idx] = true; + return idx; + } + } + //Console.WriteLine("Failed to find free reagent."); + return (uint)costSortedReagents.Count; + } + + private void ReleaseReagent(uint reagentIdx) + { + reagentInUse[reagentIdx] = false; + } + + public bool AddNextReagent() + { + bool ok = (nextReagentPos < MaxReagents); + if (ok) + { + uint nextReagent = NextFreeReagent(0); + reagents[nextReagentPos++] = nextReagent; + if (costSortedReagents[(int)nextReagent].IsCatalyst) + { + ++CatalystCount; + } + InitForQuantity(CurrentTargetQuantity); + } + return ok; + } + + public void InitForQuantity(uint quantity) + { + //System.Console.WriteLine("Init for quantity: {0}, reagent count: {1} ({2} catalysts)", quantity, ReagentCount, CatalystCount); + CurrentTargetQuantity = quantity; + if (CurrentTargetQuantity < (10 + CatalystCount)) + { + // invalid quantity + return; + } + UsedQuantity = 0; + uint remainingReagents = ((uint)nextReagentPos - CatalystCount); + uint remainingWeight = CurrentTargetQuantity - CatalystCount; + for (int i = 0; i < nextReagentPos; ++i) + { + Reagent reagent = Reagent(i); + + if (reagent.IsCatalyst) + { + //Console.WriteLine("Init catalyst {0} weight 1", reagent.Name); + currentWeights[i] = 1; + ++UsedQuantity; + } + else + { + uint reagentMaxWeight = reagent.RecipeMax; + if (ReagentCount <= FullQuantityDepth) + { + reagentMaxWeight = Math.Max(FullQuantity, reagentMaxWeight); + } + uint weight = (uint)Math.Min(remainingWeight - (remainingReagents-1), reagentMaxWeight); + //Console.WriteLine("Init reagent {0} weight {1}", reagent.Name, weight); + remainingWeight -= weight; + currentWeights[i] = weight; + UsedQuantity += weight; + } + --remainingReagents; + } + } + + 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}", nextReagentPos); + for (int i = 0; i < nextReagentPos; ++i) + { + uint idx = reagents[i]; + uint weight = currentWeights[i]; + writer.WriteLine("Reagent: {0},{1},{2}", idx, reagentInUse[idx] ? 1 : 0, weight); + } + // pulled from parent: List 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("FullQuantity: {0}", FullQuantity); + writer.WriteLine("FullQuantityDepth: {0}", FullQuantityDepth); + writer.WriteLine("---EndNode---"); + + } + + static Regex keyValueRegex = new Regex(@"(\w+)\:\s*(.*)\s*$"); + static Regex reagentPartsRegex = new Regex(@"(?\d+),(?\d+),(?\d+)"); + public bool LoadState(StreamReader reader) + { + string line = reader.ReadLine(); + if (!line.Equals("---SearchNode---")) + { + return false; + } + + bool success = true; + Match match; + 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); + for (int i = 0; i < reagents.Length; ++i) + { + reagents[i] = INVALID_REAGENT; + reagentInUse[i] = false; + } + nextReagentPos = 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[nextReagentPos] = reagentId; + currentWeights[nextReagentPos] = weight; + if (isInUse != 0) + { + if (reagentId != INVALID_REAGENT) + { + reagentInUse[reagentId] = true; + } + } + ++nextReagentPos; + } + 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; + case "FullQuantity": + { + uint value = uint.Parse(match.Groups[2].Value); + FullQuantity = value; + } + break; + case "FullQuantityDepth": + { + uint value = uint.Parse(match.Groups[2].Value); + FullQuantityDepth = value; + } + break; + default: + success = false; + break; + } + } + else + { + success = false; + break; + } + } + return success; + } + } +} + diff --git a/bin/Debug/DesertPaintLab/colors.txt b/bin/Debug/DesertPaintLab/colors.txt deleted file mode 100644 --- a/bin/Debug/DesertPaintLab/colors.txt +++ /dev/null @@ -1,142 +0,0 @@ -#F0F8FF AliceBlue -#FAEBD7 AntiqueWhite -#00FFFF Aqua -#7FFFD4 Aquamarine -#F0FFFF Azure -#F5F5DC Beige -#FFE4C4 Bisque -#010101 Black -#FFEBCD BlanchedAlmond -#0000FF Blue -#8A2BE2 BlueViolet -#A52A2A Brown -#DEB887 Burlywood -#5F9EA0 CadetBlue -#E07020 Carrot -#7FFF00 Chartreuse -#D2691E Chocolate -#FF7F50 Coral -#6495ED CornflowerBlue -#FFF8DC Cornsilk -#DC143C Crimson -#00008B DarkBlue -#008B8B DarkCyan -#B8860B DarkGoldenrod -#A9A9A9 DarkGray -#006400 DarkGreen -#BDB76B DarkKhaki -#8B008B DarkMagenta -#556B2F DarkOliveGreen -#FF8C00 DarkOrange -#9932CC DarkOrchid -#8B0000 DarkRed -#E9967A DarkSalmon -#8FBC8F DarkSeaGreen -#483D8B DarkSlateBlue -#2F4F4F DarkSlateGray -#00CED1 DarkTurquoise -#9400D3 DarkViolet -#FF1493 DeepPink -#00BFFF DeepSkyBlue -#696969 DimGray -#1E90FF DodgerBlue -#D19275 Feldspar -#B22222 FireBrick -#FFFAF0 FloralWhite -#228B22 ForestGreen -#FF00FF Fuchsia -#DCDCDC Gainsboro -#F8F8FF GhostWhite -#FFD700 Gold -#DAA520 Goldenrod -#808080 Gray -#008000 Green -#ADFF2F GreenYellow -#F0FFF0 Honeydew -#FF69B4 HotPink -#CD5C5C IndianRed -#4B0082 Indigo -#FFFFF0 Ivory -#F0E68C Khaki -#E6E6FA Lavender -#FFF0F5 LavenderBlush -#7CFC00 LawnGreen -#FFFACD LemonChiffon -#ADD8E6 LightBlue -#F08080 LightCoral -#E0FFFF LightCyan -#FAFAD2 LightGoldenrodYellow -#90EE90 LightGreen -#D3D3D3 LightGrey -#FFB6C1 LightPink -#FFA07A LightSalmon -#20B2AA LightSeaGreen -#87CEFA LightSkyBlue -#8470FF LightSlateBlue -#778899 LightSlateGray -#B0C4DE LightSteelBlue -#FFFFE0 LightYellow -#00FF00 Lime -#32CD32 LimeGreen -#FAF0E6 Linen -#800000 Maroon -#66CDAA MediumAquamarine -#0000CD MediumBlue -#BA55D3 MediumOrchid -#9370DB MediumPurple -#3CB371 MediumSeaGreen -#7B68EE MediumSlateBlue -#00FA9A MediumSpringGreen -#48D1CC MediumTurquoise -#C71585 MediumVioletRed -#191970 MidnightBlue -#F5FFFA MintCream -#FFE4E1 MistyRose -#FFE4B5 Moccasin -#FFDEAD NavajoWhite -#000080 Navy -#FDF5E6 OldLace -#808000 Olive -#6B8E23 OliveDrab -#FFA500 Orange -#FF4500 OrangeRed -#DA70D6 Orchid -#EEE8AA PaleGoldenrod -#98FB98 PaleGreen -#AFEEEE PaleTurquoise -#DB7093 PaleVioletRed -#FFEFD5 PapayaWhip -#FFDAB9 PeachPuff -#CD853F Peru -#FFC0CB Pink -#DDA0DD Plum -#B0E0E6 PowderBlue -#800080 Purple -#FF0000 Red -#BC8F8F RosyBrown -#4169E1 RoyalBlue -#8B4513 SaddleBrown -#FA8072 Salmon -#F4A460 SandyBrown -#2E8B57 SeaGreen -#FFF5EE Seashell -#A0522D Sienna -#C0C0C0 Silver -#87CEEB SkyBlue -#6A5ACD SlateBlue -#708090 SlateGray -#FFFAFA Snow -#00FF7F SpringGreen -#4682B4 SteelBlue -#D2B48C Tan -#008080 Teal -#D8BFD8 Thistle -#FF6347 Tomato -#40E0D0 Turquoise -#EE82EE Violet -#D02090 VioletRed -#F5DEB3 Wheat -#FFFFFF White -#F5F5F5 WhiteSmoke -#FFFF00 Yellow -#9ACD32 YellowGreen \ No newline at end of file diff --git a/bin/Debug/DesertPaintLab/template/dp_reactions.txt b/bin/Debug/DesertPaintLab/template/dp_reactions.txt deleted file mode 100644 --- a/bin/Debug/DesertPaintLab/template/dp_reactions.txt +++ /dev/null @@ -1,1 +0,0 @@ - \ No newline at end of file diff --git a/bin/Debug/DesertPaintLab/template/ingredients.txt b/bin/Debug/DesertPaintLab/template/ingredients.txt deleted file mode 100644 --- a/bin/Debug/DesertPaintLab/template/ingredients.txt +++ /dev/null @@ -1,21 +0,0 @@ -// Ingredients are in the form: -// Name | RGB values | cost | enabled (Y/N) | bulk/normal | max items per paint (1-20) -// -// It is recommended to only change the cost value -// It is not recommended to set many of the ingredients above 10 per paint - -Cabbage | 128, 64, 144 | 8 | Y | bulk | 10 -Carrot | 224, 112, 32 | 8 | Y | bulk | 10 -Clay | 128, 96, 32 | 10 | Y | bulk | 20 -DeadTongue | 112, 64, 64 | 500 | Y | normal | 4 -ToadSkin | 48, 96, 48 | 500 | Y | normal | 4 -EarthLight | 128, 240, 224 | 10000 | Y | normal | 4 -RedSand | 144, 16, 24 | 4 | Y | bulk | 20 -Lead | 80, 80, 96 | 50 | Y | normal | 6 -Silver | 16, 16, 32 | 50 | Y | normal | 6 -Iron | 96, 48, 32 | 30 | Y | normal | 8 -Copper | 64, 192, 192 | 30 | Y | normal | 8 -Sulfur | catalyst | 15 | Y | normal | 1 -Potash | catalyst | 50 | Y | normal | 1 -Lime | catalyst | 20 | Y | normal | 1 -Saltpeter | catalyst | 10 | Y | normal | 1 diff --git a/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs b/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs --- a/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs +++ b/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs @@ -14,6 +14,8 @@ namespace DesertPaintLab private global::Gtk.Action IngredientsAction; + private global::Gtk.Action CopyToClipboardAction; + private global::Gtk.VBox vbox2; private global::Gtk.MenuBar menubar1; @@ -22,25 +24,47 @@ namespace DesertPaintLab private global::Gtk.VBox vbox8; - private global::Gtk.Label label3; + private global::Gtk.HBox hbox2; private global::Gtk.SpinButton maxIngredientsSpinButton; + private global::Gtk.Label label3; + private global::Gtk.HSeparator hseparator3; - private global::Gtk.Label label4; + private global::Gtk.HBox hbox4; private global::Gtk.SpinButton maxRecipeSpinButton; + private global::Gtk.Label label4; + private global::Gtk.HSeparator hseparator4; + private global::Gtk.HBox hbox5; + + private global::Gtk.SpinButton fullQuantityDepthSpinButton; + private global::Gtk.Label label8; - private global::Gtk.SpinButton fullQuantityDepthSpinButton; + private global::Gtk.HSeparator hseparator5; + + private global::Gtk.HBox hbox6; + + private global::Gtk.SpinButton fullQuantitySpinButton; private global::Gtk.Label label9; - private global::Gtk.SpinButton fullQuantitySpinButton; + private global::Gtk.Frame frame3; + + private global::Gtk.Alignment GtkAlignment1; + + private global::Gtk.ScrolledWindow scrolledwindow2; + + private global::Gtk.TreeView reagentListView; + + private global::Gtk.Label GtkLabel4; + + private global::Gtk.HSeparator hseparator6; private global::Gtk.ScrolledWindow GtkScrolledWindow1; @@ -50,14 +74,14 @@ namespace DesertPaintLab private global::Gtk.Frame frame2; - private global::Gtk.Alignment GtkAlignment; + private global::Gtk.ScrolledWindow GtkScrolledWindow; - private global::Gtk.ScrolledWindow scrolledwindow1; - - private global::Gtk.VBox recipeListBox; + private global::Gtk.TreeView recipeIngredientsView; private global::Gtk.Label recipeLabel; + private global::Gtk.Button button919; + private global::DesertPaintLab.PaintSwatch paintSwatch; private global::Gtk.HBox hbox3; @@ -94,6 +118,9 @@ namespace DesertPaintLab this.IngredientsAction = new global::Gtk.Action ("IngredientsAction", "Ingredients", null, null); this.IngredientsAction.ShortLabel = "Ingredients"; w1.Add (this.IngredientsAction, null); + this.CopyToClipboardAction = new global::Gtk.Action ("CopyToClipboardAction", "Copy to Clipboard", null, null); + this.CopyToClipboardAction.ShortLabel = "Copy to Clipboard"; + w1.Add (this.CopyToClipboardAction, null); this.UIManager.InsertActionGroup (w1, 0); this.AddAccelGroup (this.UIManager.AccelGroup); this.Name = "DesertPaintLab.RecipeGeneratorWindow"; @@ -105,7 +132,7 @@ namespace DesertPaintLab this.vbox2.Spacing = 6; this.vbox2.BorderWidth = ((uint)(8)); // Container child vbox2.Gtk.Box+BoxChild - this.UIManager.AddUiFromString (""); + this.UIManager.AddUiFromString (""); this.menubar1 = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar1"))); this.menubar1.Name = "menubar1"; this.vbox2.Add (this.menubar1); @@ -122,113 +149,196 @@ namespace DesertPaintLab this.vbox8.Name = "vbox8"; this.vbox8.Spacing = 6; // Container child vbox8.Gtk.Box+BoxChild - this.label3 = new global::Gtk.Label (); - this.label3.Name = "label3"; - this.label3.LabelProp = "Maximum Ingredients"; - this.vbox8.Add (this.label3); - global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label3])); - w3.Position = 0; - w3.Expand = false; - w3.Fill = false; - // Container child vbox8.Gtk.Box+BoxChild + this.hbox2 = new global::Gtk.HBox (); + this.hbox2.Name = "hbox2"; + this.hbox2.Spacing = 6; + // Container child hbox2.Gtk.Box+BoxChild this.maxIngredientsSpinButton = new global::Gtk.SpinButton (0, 14, 1); this.maxIngredientsSpinButton.CanFocus = true; this.maxIngredientsSpinButton.Name = "maxIngredientsSpinButton"; this.maxIngredientsSpinButton.Adjustment.PageIncrement = 10; this.maxIngredientsSpinButton.ClimbRate = 1; this.maxIngredientsSpinButton.Numeric = true; - this.vbox8.Add (this.maxIngredientsSpinButton); - global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.maxIngredientsSpinButton])); + this.hbox2.Add (this.maxIngredientsSpinButton); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.maxIngredientsSpinButton])); + w3.Position = 0; + w3.Expand = false; + w3.Fill = false; + // Container child hbox2.Gtk.Box+BoxChild + this.label3 = new global::Gtk.Label (); + this.label3.Name = "label3"; + this.label3.LabelProp = "Maximum Ingredients"; + this.hbox2.Add (this.label3); + global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.label3])); w4.Position = 1; w4.Expand = false; w4.Fill = false; + this.vbox8.Add (this.hbox2); + global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hbox2])); + w5.Position = 0; + w5.Expand = false; + w5.Fill = false; // Container child vbox8.Gtk.Box+BoxChild this.hseparator3 = new global::Gtk.HSeparator (); this.hseparator3.Name = "hseparator3"; this.vbox8.Add (this.hseparator3); - global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator3])); - w5.Position = 2; - w5.Expand = false; - w5.Fill = false; - // Container child vbox8.Gtk.Box+BoxChild - this.label4 = new global::Gtk.Label (); - this.label4.Name = "label4"; - this.label4.LabelProp = "Max Total Quantity"; - this.label4.UseMarkup = true; - this.label4.Wrap = true; - this.vbox8.Add (this.label4); - global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label4])); - w6.Position = 3; + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator3])); + w6.Position = 1; w6.Expand = false; w6.Fill = false; - w6.Padding = ((uint)(8)); // Container child vbox8.Gtk.Box+BoxChild + this.hbox4 = new global::Gtk.HBox (); + this.hbox4.Name = "hbox4"; + this.hbox4.Spacing = 6; + // Container child hbox4.Gtk.Box+BoxChild this.maxRecipeSpinButton = new global::Gtk.SpinButton (0, 100, 1); this.maxRecipeSpinButton.CanFocus = true; this.maxRecipeSpinButton.Name = "maxRecipeSpinButton"; this.maxRecipeSpinButton.Adjustment.PageIncrement = 10; this.maxRecipeSpinButton.ClimbRate = 1; this.maxRecipeSpinButton.Numeric = true; - this.vbox8.Add (this.maxRecipeSpinButton); - global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.maxRecipeSpinButton])); - w7.Position = 4; + this.hbox4.Add (this.maxRecipeSpinButton); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.hbox4 [this.maxRecipeSpinButton])); + w7.Position = 0; w7.Expand = false; w7.Fill = false; + // Container child hbox4.Gtk.Box+BoxChild + this.label4 = new global::Gtk.Label (); + this.label4.Name = "label4"; + this.label4.LabelProp = "Max Total Quantity"; + this.label4.UseMarkup = true; + this.label4.Wrap = true; + this.hbox4.Add (this.label4); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.hbox4 [this.label4])); + w8.Position = 1; + w8.Expand = false; + w8.Fill = false; + this.vbox8.Add (this.hbox4); + global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hbox4])); + w9.Position = 2; + w9.Expand = false; + w9.Fill = false; // Container child vbox8.Gtk.Box+BoxChild this.hseparator4 = new global::Gtk.HSeparator (); this.hseparator4.Name = "hseparator4"; this.vbox8.Add (this.hseparator4); - global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator4])); - w8.Position = 5; - w8.Expand = false; - w8.Fill = false; + global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator4])); + w10.Position = 3; + w10.Expand = false; + w10.Fill = false; + w10.Padding = ((uint)(8)); // Container child vbox8.Gtk.Box+BoxChild - this.label8 = new global::Gtk.Label (); - this.label8.Name = "label8"; - this.label8.LabelProp = "Full Quantity Depth"; - this.vbox8.Add (this.label8); - global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label8])); - w9.Position = 6; - w9.Expand = false; - w9.Fill = false; - // Container child vbox8.Gtk.Box+BoxChild + this.hbox5 = new global::Gtk.HBox (); + this.hbox5.Name = "hbox5"; + this.hbox5.Spacing = 6; + // Container child hbox5.Gtk.Box+BoxChild this.fullQuantityDepthSpinButton = new global::Gtk.SpinButton (0, 15, 1); this.fullQuantityDepthSpinButton.CanFocus = true; this.fullQuantityDepthSpinButton.Name = "fullQuantityDepthSpinButton"; this.fullQuantityDepthSpinButton.Adjustment.PageIncrement = 10; this.fullQuantityDepthSpinButton.ClimbRate = 1; this.fullQuantityDepthSpinButton.Numeric = true; - this.vbox8.Add (this.fullQuantityDepthSpinButton); - global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.fullQuantityDepthSpinButton])); - w10.Position = 7; - w10.Expand = false; - w10.Fill = false; - // Container child vbox8.Gtk.Box+BoxChild - this.label9 = new global::Gtk.Label (); - this.label9.Name = "label9"; - this.label9.LabelProp = "FullQuantity"; - this.vbox8.Add (this.label9); - global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label9])); - w11.Position = 8; + this.hbox5.Add (this.fullQuantityDepthSpinButton); + global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.hbox5 [this.fullQuantityDepthSpinButton])); + w11.Position = 0; w11.Expand = false; w11.Fill = false; + // Container child hbox5.Gtk.Box+BoxChild + this.label8 = new global::Gtk.Label (); + this.label8.Name = "label8"; + this.label8.LabelProp = "Full Quantity Depth"; + this.hbox5.Add (this.label8); + global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.hbox5 [this.label8])); + w12.Position = 1; + w12.Expand = false; + w12.Fill = false; + this.vbox8.Add (this.hbox5); + global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hbox5])); + w13.Position = 4; + w13.Expand = false; + w13.Fill = false; // Container child vbox8.Gtk.Box+BoxChild + this.hseparator5 = new global::Gtk.HSeparator (); + this.hseparator5.Name = "hseparator5"; + this.vbox8.Add (this.hseparator5); + global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator5])); + w14.Position = 5; + w14.Expand = false; + w14.Fill = false; + // Container child vbox8.Gtk.Box+BoxChild + this.hbox6 = new global::Gtk.HBox (); + this.hbox6.Name = "hbox6"; + this.hbox6.Spacing = 6; + // Container child hbox6.Gtk.Box+BoxChild this.fullQuantitySpinButton = new global::Gtk.SpinButton (0, 30, 1); this.fullQuantitySpinButton.CanFocus = true; this.fullQuantitySpinButton.Name = "fullQuantitySpinButton"; this.fullQuantitySpinButton.Adjustment.PageIncrement = 10; this.fullQuantitySpinButton.ClimbRate = 1; this.fullQuantitySpinButton.Numeric = true; - this.vbox8.Add (this.fullQuantitySpinButton); - global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.fullQuantitySpinButton])); - w12.Position = 9; - w12.Expand = false; - w12.Fill = false; + this.hbox6.Add (this.fullQuantitySpinButton); + global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox6 [this.fullQuantitySpinButton])); + w15.Position = 0; + w15.Expand = false; + w15.Fill = false; + // Container child hbox6.Gtk.Box+BoxChild + this.label9 = new global::Gtk.Label (); + this.label9.Name = "label9"; + this.label9.LabelProp = "FullQuantity"; + this.hbox6.Add (this.label9); + global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.hbox6 [this.label9])); + w16.Position = 1; + w16.Expand = false; + w16.Fill = false; + this.vbox8.Add (this.hbox6); + global::Gtk.Box.BoxChild w17 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hbox6])); + w17.Position = 6; + w17.Expand = false; + w17.Fill = false; + // Container child vbox8.Gtk.Box+BoxChild + this.frame3 = new global::Gtk.Frame (); + this.frame3.Name = "frame3"; + this.frame3.ShadowType = ((global::Gtk.ShadowType)(0)); + // Container child frame3.Gtk.Container+ContainerChild + this.GtkAlignment1 = new global::Gtk.Alignment (0F, 0F, 1F, 1F); + this.GtkAlignment1.Name = "GtkAlignment1"; + this.GtkAlignment1.LeftPadding = ((uint)(12)); + // Container child GtkAlignment1.Gtk.Container+ContainerChild + this.scrolledwindow2 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow2.CanFocus = true; + this.scrolledwindow2.Name = "scrolledwindow2"; + this.scrolledwindow2.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow2.Gtk.Container+ContainerChild + this.reagentListView = new global::Gtk.TreeView (); + this.reagentListView.WidthRequest = 300; + this.reagentListView.CanFocus = true; + this.reagentListView.Name = "reagentListView"; + this.scrolledwindow2.Add (this.reagentListView); + this.GtkAlignment1.Add (this.scrolledwindow2); + this.frame3.Add (this.GtkAlignment1); + this.GtkLabel4 = new global::Gtk.Label (); + this.GtkLabel4.Name = "GtkLabel4"; + this.GtkLabel4.LabelProp = "Ingredients"; + this.GtkLabel4.UseMarkup = true; + this.frame3.LabelWidget = this.GtkLabel4; + this.vbox8.Add (this.frame3); + global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.frame3])); + w21.PackType = ((global::Gtk.PackType)(1)); + w21.Position = 7; + // Container child vbox8.Gtk.Box+BoxChild + this.hseparator6 = new global::Gtk.HSeparator (); + this.hseparator6.Name = "hseparator6"; + this.vbox8.Add (this.hseparator6); + global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator6])); + w22.PackType = ((global::Gtk.PackType)(1)); + w22.Position = 8; + w22.Expand = false; + w22.Fill = false; this.hbox1.Add (this.vbox8); - global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox8])); - w13.Position = 0; - w13.Expand = false; - w13.Fill = false; + global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox8])); + w23.Position = 0; + w23.Expand = false; + w23.Fill = false; // Container child hbox1.Gtk.Box+BoxChild this.GtkScrolledWindow1 = new global::Gtk.ScrolledWindow (); this.GtkScrolledWindow1.Name = "GtkScrolledWindow1"; @@ -240,8 +350,8 @@ namespace DesertPaintLab this.recipeList.Name = "recipeList"; this.GtkScrolledWindow1.Add (this.recipeList); this.hbox1.Add (this.GtkScrolledWindow1); - global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.GtkScrolledWindow1])); - w15.Position = 1; + global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.GtkScrolledWindow1])); + w25.Position = 1; // Container child hbox1.Gtk.Box+BoxChild this.vbox3 = new global::Gtk.VBox (); this.vbox3.Name = "vbox3"; @@ -253,51 +363,50 @@ namespace DesertPaintLab this.frame2.Name = "frame2"; this.frame2.ShadowType = ((global::Gtk.ShadowType)(0)); // Container child frame2.Gtk.Container+ContainerChild - this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F); - this.GtkAlignment.Name = "GtkAlignment"; - this.GtkAlignment.LeftPadding = ((uint)(12)); - // Container child GtkAlignment.Gtk.Container+ContainerChild - this.scrolledwindow1 = new global::Gtk.ScrolledWindow (); - this.scrolledwindow1.CanFocus = true; - this.scrolledwindow1.Name = "scrolledwindow1"; - this.scrolledwindow1.ShadowType = ((global::Gtk.ShadowType)(1)); - // Container child scrolledwindow1.Gtk.Container+ContainerChild - global::Gtk.Viewport w16 = new global::Gtk.Viewport (); - w16.ShadowType = ((global::Gtk.ShadowType)(0)); - // Container child GtkViewport.Gtk.Container+ContainerChild - this.recipeListBox = new global::Gtk.VBox (); - this.recipeListBox.Name = "recipeListBox"; - this.recipeListBox.Homogeneous = true; - this.recipeListBox.Spacing = 6; - this.recipeListBox.BorderWidth = ((uint)(1)); - w16.Add (this.recipeListBox); - this.scrolledwindow1.Add (w16); - this.GtkAlignment.Add (this.scrolledwindow1); - this.frame2.Add (this.GtkAlignment); + this.GtkScrolledWindow = new global::Gtk.ScrolledWindow (); + this.GtkScrolledWindow.Name = "GtkScrolledWindow"; + this.GtkScrolledWindow.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child GtkScrolledWindow.Gtk.Container+ContainerChild + this.recipeIngredientsView = new global::Gtk.TreeView (); + this.recipeIngredientsView.CanFocus = true; + this.recipeIngredientsView.Name = "recipeIngredientsView"; + this.GtkScrolledWindow.Add (this.recipeIngredientsView); + this.frame2.Add (this.GtkScrolledWindow); this.recipeLabel = new global::Gtk.Label (); this.recipeLabel.Name = "recipeLabel"; this.recipeLabel.LabelProp = "Recipe"; this.recipeLabel.UseMarkup = true; this.frame2.LabelWidget = this.recipeLabel; this.vbox3.Add (this.frame2); - global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.frame2])); - w21.Position = 0; + global::Gtk.Box.BoxChild w28 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.frame2])); + w28.Position = 0; + // Container child vbox3.Gtk.Box+BoxChild + this.button919 = new global::Gtk.Button (); + this.button919.CanFocus = true; + this.button919.Name = "button919"; + this.button919.UseUnderline = true; + this.button919.Label = "Copy"; + this.vbox3.Add (this.button919); + global::Gtk.Box.BoxChild w29 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.button919])); + w29.Position = 1; + w29.Expand = false; + w29.Fill = false; // Container child vbox3.Gtk.Box+BoxChild this.paintSwatch = new global::DesertPaintLab.PaintSwatch (); this.paintSwatch.HeightRequest = 200; this.paintSwatch.Events = ((global::Gdk.EventMask)(256)); this.paintSwatch.Name = "paintSwatch"; this.vbox3.Add (this.paintSwatch); - global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.paintSwatch])); - w22.Position = 1; + global::Gtk.Box.BoxChild w30 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.paintSwatch])); + w30.Position = 2; this.hbox1.Add (this.vbox3); - global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox3])); - w23.Position = 2; - w23.Expand = false; - w23.Fill = false; + global::Gtk.Box.BoxChild w31 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox3])); + w31.Position = 2; + w31.Expand = false; + w31.Fill = false; this.vbox2.Add (this.hbox1); - global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox1])); - w24.Position = 1; + global::Gtk.Box.BoxChild w32 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox1])); + w32.Position = 1; // Container child vbox2.Gtk.Box+BoxChild this.hbox3 = new global::Gtk.HBox (); this.hbox3.Name = "hbox3"; @@ -306,8 +415,8 @@ namespace DesertPaintLab this.hseparator2 = new global::Gtk.HSeparator (); this.hseparator2.Name = "hseparator2"; this.hbox3.Add (this.hseparator2); - global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator2])); - w25.Position = 0; + global::Gtk.Box.BoxChild w33 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator2])); + w33.Position = 0; // Container child hbox3.Gtk.Box+BoxChild this.stopResumeButton = new global::Gtk.Button (); this.stopResumeButton.Sensitive = false; @@ -316,21 +425,21 @@ namespace DesertPaintLab this.stopResumeButton.UseUnderline = true; this.stopResumeButton.Label = "Stop"; this.hbox3.Add (this.stopResumeButton); - global::Gtk.Box.BoxChild w26 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.stopResumeButton])); - w26.Position = 1; - w26.Expand = false; - w26.Fill = false; - w26.Padding = ((uint)(20)); + global::Gtk.Box.BoxChild w34 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.stopResumeButton])); + w34.Position = 1; + w34.Expand = false; + w34.Fill = false; + w34.Padding = ((uint)(20)); // Container child hbox3.Gtk.Box+BoxChild this.countLabel = new global::Gtk.Label (); this.countLabel.Name = "countLabel"; this.countLabel.LabelProp = "0/192"; this.hbox3.Add (this.countLabel); - global::Gtk.Box.BoxChild w27 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.countLabel])); - w27.Position = 2; - w27.Expand = false; - w27.Fill = false; - w27.Padding = ((uint)(40)); + global::Gtk.Box.BoxChild w35 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.countLabel])); + w35.Position = 2; + w35.Expand = false; + w35.Fill = false; + w35.Padding = ((uint)(40)); // Container child hbox3.Gtk.Box+BoxChild this.beginButton = new global::Gtk.Button (); this.beginButton.CanFocus = true; @@ -338,53 +447,55 @@ namespace DesertPaintLab this.beginButton.UseUnderline = true; this.beginButton.Label = "Begin"; this.hbox3.Add (this.beginButton); - global::Gtk.Box.BoxChild w28 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.beginButton])); - w28.Position = 3; - w28.Expand = false; - w28.Fill = false; - w28.Padding = ((uint)(20)); + global::Gtk.Box.BoxChild w36 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.beginButton])); + w36.Position = 3; + w36.Expand = false; + w36.Fill = false; + w36.Padding = ((uint)(20)); // Container child hbox3.Gtk.Box+BoxChild this.hseparator1 = new global::Gtk.HSeparator (); this.hseparator1.Name = "hseparator1"; this.hbox3.Add (this.hseparator1); - global::Gtk.Box.BoxChild w29 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator1])); - w29.Position = 4; + global::Gtk.Box.BoxChild w37 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator1])); + w37.Position = 4; this.vbox2.Add (this.hbox3); - global::Gtk.Box.BoxChild w30 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox3])); - w30.Position = 2; - w30.Expand = false; - w30.Fill = false; + global::Gtk.Box.BoxChild w38 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox3])); + w38.Position = 2; + w38.Expand = false; + w38.Fill = false; // Container child vbox2.Gtk.Box+BoxChild this.statusLabel = new global::Gtk.Label (); this.statusLabel.Name = "statusLabel"; this.vbox2.Add (this.statusLabel); - global::Gtk.Box.BoxChild w31 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.statusLabel])); - w31.PackType = ((global::Gtk.PackType)(1)); - w31.Position = 3; - w31.Expand = false; - w31.Fill = false; + global::Gtk.Box.BoxChild w39 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.statusLabel])); + w39.PackType = ((global::Gtk.PackType)(1)); + w39.Position = 3; + w39.Expand = false; + w39.Fill = false; // Container child vbox2.Gtk.Box+BoxChild this.progressBar = new global::Gtk.ProgressBar (); this.progressBar.Name = "progressBar"; this.vbox2.Add (this.progressBar); - global::Gtk.Box.BoxChild w32 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.progressBar])); - w32.PackType = ((global::Gtk.PackType)(1)); - w32.Position = 4; - w32.Expand = false; - w32.Fill = false; + global::Gtk.Box.BoxChild w40 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.progressBar])); + w40.PackType = ((global::Gtk.PackType)(1)); + w40.Position = 4; + w40.Expand = false; + w40.Fill = false; this.Add (this.vbox2); if ((this.Child != null)) { this.Child.ShowAll (); } this.DefaultWidth = 887; - this.DefaultHeight = 570; + this.DefaultHeight = 577; this.Show (); this.ExportToWikiAction.Activated += new global::System.EventHandler (this.OnExportToWiki); this.IngredientsAction.Activated += new global::System.EventHandler (this.OnShowIngredients); + this.CopyToClipboardAction.Activated += new global::System.EventHandler (this.OnCopyRecipeListToClipboard); this.maxIngredientsSpinButton.ValueChanged += new global::System.EventHandler (this.OnMaxIngredientsChanged); this.maxRecipeSpinButton.ValueChanged += new global::System.EventHandler (this.OnMaxRecipeChanged); this.fullQuantityDepthSpinButton.ValueChanged += new global::System.EventHandler (this.OnFullQuantityDepthChanged); - this.fullQuantitySpinButton.Input += new global::Gtk.InputHandler (this.OnFullQuantityChanged); + this.fullQuantitySpinButton.ValueChanged += new global::System.EventHandler (this.OnFullQuantityChanged); + this.button919.Clicked += new global::System.EventHandler (this.OnCopyRecipeToClipboard); this.stopResumeButton.Clicked += new global::System.EventHandler (this.OnStopResume); this.beginButton.Clicked += new global::System.EventHandler (this.OnBegin); } diff --git a/gtk-gui/gui.stetic b/gtk-gui/gui.stetic --- a/gtk-gui/gui.stetic +++ b/gtk-gui/gui.stetic @@ -1154,7 +1154,7 @@ You can either import an existing Practi - + Action @@ -1178,6 +1178,12 @@ You can either import an existing Practi Ingredients + + Action + Copy to Clipboard + Copy to Clipboard + + Recipe Generator @@ -1193,9 +1199,7 @@ You can either import an existing Practi - - - + @@ -1215,9 +1219,39 @@ You can either import an existing Practi 6 - + - Maximum Ingredients + 6 + + + + True + 14 + 10 + 1 + 1 + True + + + + 0 + True + False + False + + + + + + Maximum Ingredients + + + 1 + True + False + False + + 0 @@ -1227,28 +1261,54 @@ You can either import an existing Practi - - - True - 14 - 10 - 1 - 1 - True - - - - 1 - True - False - False - - - + 1 + True + False + False + + + + + + 6 + + + + True + 100 + 10 + 1 + 1 + True + + + + 0 + True + False + False + + + + + + Max Total Quantity + True + True + + + 1 + True + False + False + + + + 2 True False @@ -1256,11 +1316,8 @@ You can either import an existing Practi - + - Max Total Quantity - True - True 3 @@ -1271,15 +1328,39 @@ You can either import an existing Practi - + - True - 100 - 10 - 1 - 1 - True - + 6 + + + + True + 15 + 10 + 1 + 1 + True + + + + 0 + True + False + False + + + + + + Full Quantity Depth + + + 1 + True + False + False + + 4 @@ -1289,7 +1370,7 @@ You can either import an existing Practi - + @@ -1300,9 +1381,39 @@ You can either import an existing Practi - + - Full Quantity Depth + 6 + + + + True + 30 + 10 + 1 + 1 + True + + + + 0 + True + False + False + + + + + + FullQuantity + + + 1 + True + False + False + + 6 @@ -1312,53 +1423,60 @@ You can either import an existing Practi - + - True - 15 - 10 - 1 - 1 - True - + None + + + + 0 + 0 + 12 + + + + True + In + + + + 300 + True + + + + + + + + + + <b>Ingredients</b> + True + + + label_item + + + End 7 True - False - False - + - FullQuantity + End 8 True False False - - - - True - 30 - 10 - 1 - 1 - True - - - - 9 - True - False - False - - 0 @@ -1396,39 +1514,14 @@ You can either import an existing Practi 200 None - + - 0 - 0 - 12 + In - + True - In - - - - None - - - - True - 6 - 1 - - - - - - - - - - - - - + True @@ -1450,13 +1543,29 @@ You can either import an existing Practi + + + True + TextOnly + Copy + True + + + + 1 + True + False + False + + + 200 ButtonPressMask - 1 + 2 True