/// <summary> /// Get the maximum production capactity of the target element /// given a defined amount of one input material /// </summary> /// <param name="target"></param> /// <param name="input"></param> /// <returns></returns> public long GetProductionCapacity(string target, ReactionTerm input) { var forOne = GetRawMaterialFor(new ReactionTerm(target, 1)).FirstOrDefault(m => m.Element == input.Element); //The minimum if no waste is produced in the full reaction chain var min = input.Quantity / forOne.Quantity; var max = min * 4; while (min != max) { var middle = min + (max - min) / 2; var need = GetRawMaterialFor(new ReactionTerm(target, middle)).FirstOrDefault(m => m.Element == input.Element); if (need.Quantity == input.Quantity) { max = min = middle; } else if (need.Quantity < input.Quantity) { min = middle; } else { max = middle; } if (max == min + 1) { break; } } return(min); }
/// <summary> /// Get all elements that need to be provided as raw material to start a series /// of reaction which lead to the production of the given target element /// </summary> /// <param name="element"></param> /// <param name="quantity"></param> /// <returns></returns> public List <ReactionTerm> GetRawMaterialFor(ReactionTerm target) { var tryToProduce = new Queue <ReactionTerm>(); var overProductions = new List <ReactionTerm>(); var rawMaterials = new List <ReactionTerm>(); tryToProduce.Enqueue(target); while (tryToProduce.Count != 0) { var next = tryToProduce.Dequeue(); next = UpdateDemand(ref overProductions, next); if (next.Quantity != 0) { var hasReaction = FindReaction(next, out var inputs, out var overProduction); if (hasReaction) { inputs.ForEach(i => tryToProduce.Enqueue(i)); //Add the overproduction next.Quantity = overProduction; AddOverProduction(ref overProductions, next); } else { AddRawMaterial(ref rawMaterials, next); } } } return(rawMaterials); }
/// <summary> /// Adds a new overproduction /// </summary> /// <param name="overProductions"></param> /// <param name="newOverProduction"></param> private void AddOverProduction(ref List <ReactionTerm> overProductions, ReactionTerm newOverProduction) { var existing = overProductions.FirstOrDefault(l => l.Element == newOverProduction.Element); if (existing != null) { var index = overProductions.IndexOf(existing); overProductions[index] = existing + newOverProduction.Quantity; } else { overProductions.Add(newOverProduction); } }
/// <summary> /// Adds new raw material to the list of needed materials /// </summary> private void AddRawMaterial(ref List <ReactionTerm> rawMaterials, ReactionTerm newElement) { var existing = rawMaterials.FirstOrDefault(e => e.Element == newElement.Element); if (existing != null) { var index = rawMaterials.IndexOf(existing); rawMaterials[index] = existing + newElement.Quantity; } else { rawMaterials.Add(newElement); } }
/// <summary> /// Creates a new chemical reaction object /// </summary> /// <param name="reactionEquation"></param> public ChemicalReaction(string reactionEquation) { var firstSplit = reactionEquation.Split("=>"); var inputs = firstSplit[0]; var inputParts = inputs.Split(','); foreach (string part in inputParts) { Inputs.Add(GetReactionTerm(part)); } var output = firstSplit[1]; Output = GetReactionTerm(output); }
/// <summary> /// Gets the list of input elemez to produce a specific element /// with exatly one reaction /// </summary> /// <param name="element"></param> /// <param name="quantity"></param> /// <returns></returns> public bool FindReaction(ReactionTerm target, out List <ReactionTerm> inputs, out long overProduction) { var reaction = Reactions.FirstOrDefault(r => r.Output.Element == target.Element); if (reaction == null) { overProduction = 0; inputs = null; return(false); } else { var multiplier = (long)Math.Ceiling((double)target.Quantity / reaction.Output.Quantity); overProduction = Operations.SafeMultiplication(reaction.Output.Quantity, multiplier) - target.Quantity; inputs = reaction.Inputs.Select(i => i * multiplier).ToList(); return(true); } }
/// <summary> /// Updates the demand on a specific element depending on the overProduction still stored /// </summary> /// <param name="leftOvers"></param> /// <param name="target"></param> /// <returns></returns> private ReactionTerm UpdateDemand(ref List <ReactionTerm> overProductions, ReactionTerm target) { var existing = overProductions.FirstOrDefault(l => l.Element == target.Element); if (existing != null) { var index = overProductions.IndexOf(existing); if (existing.Quantity >= target.Quantity) { overProductions[index] = existing - target.Quantity; target.Quantity = 0; } else { target -= existing.Quantity; overProductions[index].Quantity = 0; } return(target); } return(target); }