/// <summary> /// Optimize transactions on the provided exchange. This will execute purchases via the exchange methods provided, after /// finding an available trade which can increase utility. /// </summary> /// <returns>a summary of all exchanges made during the optimization</returns> public IList <(ExchangeResult <Resource>?, PurchaseOperationResult <Resource>)> Optimize() { var transactionLedger = new List <(ExchangeResult <Resource>?, PurchaseOperationResult <Resource>)>(); var purchase = PurchaseResult.Purchase(this, selfInventory, otherInventory); if (purchase?.ledger.exchages.Count > 0) { transactionLedger.Add((null, purchase.ledger)); } var iterations = 0; var executesPurchase = true; for (var minUtility = GetHighestSellableValuePerUtility(increment, selfInventory, otherInventory); !EqualityComparer <Resource> .Default.Equals(minUtility, default) && executesPurchase; minUtility = GetHighestSellableValuePerUtility(increment, selfInventory, otherInventory)) { var nextTransaction = SellUntilPurchaseCanHappen(minUtility); if (!nextTransaction.HasValue) { break; } transactionLedger.Add(nextTransaction.Value); iterations++; if (iterations > 1000) { throw new Exception("Attempted to optimize over too many iterations, broke to safegaurd against infinite loop"); } } return(transactionLedger); }
/// <summary> /// Searches thought the inventories for the first possible purchase to execute, attempting to sell more of the resource of minimum utility /// bit by bit until a purchase is possible /// /// There is a fundamental problem with this in terms of optimization: This only looks for the -first- purchase that can happen: not the -best- /// purchase. finding the best purchase option would require evaluating all possible selling amounts, or /// using a completely different search strategy /// To make this work well enough for resource sets with just a couple possible options, this function will never /// return an exchange in which it purchases the same items as it has sold /// </summary> /// <param name="minUtility"></param> /// <returns></returns> private (ExchangeResult <Resource>, PurchaseResult)? GetFirstPossiblePurchase(Resource minUtility) { PurchaseResult purchaseOption = null; ActionOption <ExchangeResult <Resource> > sellOption = null; float purchaseAmount = 0; int iterations = 0; while (purchaseOption == null || purchaseOption.ledger.exchages.Count() <= 0) { var simSelfInventory = CloneSelfInventory(); var simOtherInventory = CloneOtherInventory(); purchaseAmount += increment; // Execute all operations on a simulated inventory to make sure all prices, utilities, // and any constraints on size are respected sellOption = exchange.Sell(minUtility, purchaseAmount, simSelfInventory, simOtherInventory); // If the sold amount is less than the requested amount by more than increment // this means that we aren't going to gain anything at all if (sellOption.info.amount <= (purchaseAmount - increment)) { return(null); } sellOption.Execute(); purchaseOption = PurchaseResult.Purchase(this, simSelfInventory, simOtherInventory, new[] { minUtility }); iterations++; if (iterations > 1000) { throw new Exception("Attempted to find purchase option over too many iterations, broke to safegaurd against infinite loop"); } } return(sellOption.info, purchaseOption); }