/* * 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; } } public uint MinConcentration { get; set; } = 10; bool[] reagentInUse; List costSortedReagents; PaintRecipe testRecipe = null; public PaintRecipe TestRecipe { get { return testRecipe; } set { testRecipe = value; } } public uint CurrentTargetQuantity { get; set; } public uint MaxConcentration { get; set; } public uint UsedQuantity { get; private set; } public uint CatalystCount { get; set; } public uint FullQuantityDepth { get; set; } public uint FullQuantity { get; set; } public uint MinReagents { 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(RecipeSearchNode other) { this.costSortedReagents = new List(other.costSortedReagents); this.reagents = new uint[costSortedReagents.Count]; INVALID_REAGENT = (uint)costSortedReagents.Count; for (int i = 0; i < costSortedReagents.Count; ++i) { this.reagents[i] = other.reagents[i]; } reagentInUse = new bool[costSortedReagents.Count]; for (uint i = 0; i < costSortedReagents.Count; ++i) { reagentInUse[i] = other.reagentInUse[i]; } nextReagentPos = other.nextReagentPos; CurrentTargetQuantity = other.CurrentTargetQuantity; MaxConcentration = other.MaxConcentration; UsedQuantity = other.UsedQuantity; CatalystCount = other.CatalystCount; FullQuantityDepth = other.FullQuantityDepth; FullQuantity = other.FullQuantity; MinReagents = other.MinReagents; MaxReagents = other.MaxReagents; for (int i = 0; i < MaxReagents; ++i) { currentWeights[i] = other.currentWeights[i]; } } 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; } } MinReagents = (uint)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); MinReagents = 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); MinReagents = 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 < (MinConcentration + 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("MinReagents: {0}", MinReagents); 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("MinConcentration: {0}", MinConcentration); writer.WriteLine("MaxConcentration: {0}", MaxConcentration); writer.WriteLine("UsedQuantity: {0}", UsedQuantity); writer.WriteLine("CatalystCount: {0}", CatalystCount); 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); MaxConcentration = value; } break; case "MinConcentration": { uint value = uint.Parse(match.Groups[2].Value); MinConcentration = value; } break; case "MaxConcentration": { uint value = uint.Parse(match.Groups[2].Value); MaxConcentration = value; } break; case "MinReagents": { uint value = uint.Parse(match.Groups[2].Value); MinReagents = 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": { uint value = uint.Parse(match.Groups[2].Value); MinReagents = 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; } } }