예제 #1
0
        /// <summary>
        /// Readjust and Recalulate Prices.
        /// </summary>
        public void RecalculatePrices()
        {
            // TODO Allow for this to be modified by variations
            // TODO Create a way to allow for faster Price changes, when supply/demand differences are large.
            // for each product in the market.
            foreach (var pair in ProductPrices)
            {
                // Get the product
                var product = pair.Item1;

                double surplus;
                double shortfall;

                try
                {
                    // get surplus product not spent
                    surplus = Surplus.GetProductValue(product);

                    // get product that was desired to buy.
                    shortfall = Shortfall.GetProductValue(product);
                }
                catch (KeyNotFoundException)
                {
                    // if the item does not exist in surplus or shortfal, then it probably was not
                    // sold or desired in the market. Give it a boost to denote it's rarity, and try and encourage it.
                    ProductPrices.AddProducts(product, 0.01);
                    continue;
                }
                // the amount of change to make to the good's price.
                double priceChange = 0;

                // If any surplus and shortfall exists, price was too high
                if (surplus > 0 && shortfall > 0)
                {
                    priceChange += -0.01;
                }
                else if (surplus > 0)
                {
                    // If no shortfall but still surplus, try lowering price to sell it, oversupply is not good.
                    priceChange += -0.01;
                }
                else if (shortfall > 0)
                { // if shortfall but no surplus, price is too low.
                    priceChange += 0.01;
                }
                // In no surplus nor shortfall, then we have hit equilibrium.
                // No change in price.

                // add the change in price to the new price
                // TODO make this more flexible and reactive.
                // going in 0.01 ABS price unit sized steps is too small
                // and may make prices too stagnant
                var newPrice = ProductPrices.GetProductValue(product) + priceChange;

                // update to said price.
                ProductPrices.SetProductAmount(product, newPrice);
            }
        }
예제 #2
0
        /// <summary>
        /// Gets a price for a good.
        /// </summary>
        /// <param name="product">The product we are pricing</param>
        /// <returns>The price in abstract currency.</returns>
        /// <exception cref="ArgumentNullException">
        /// If Product is null.
        /// </exception>
        public double GetPrice(IProduct product)
        {
            if (product is null)
            {
                throw new ArgumentNullException(nameof(product));
            }

            return(ProductPrices.GetProductValue(product));
        }
예제 #3
0
        /// <summary>
        /// Given a set of cash and a desired price, get the amount of the cash
        /// to meet the price (rounded up for safety)
        /// </summary>
        /// <param name="AvailableCash">The amount of cash avaliable.</param>
        /// <param name="price">The price to meet (roughly)</param>
        /// <returns>The appropriate cash for the price.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="AvailableCash"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="price"/> is less than or equal to 0.</exception>
        public IProductAmountCollection ChangeForPrice(IProductAmountCollection AvailableCash, double price)
        {
            // ensure cash is not null.
            if (AvailableCash is null)
            {
                throw new ArgumentNullException(nameof(AvailableCash));
            }
            // ensure that the price is greater than 0
            if (price <= 0) // TODO, allow this to savely give change for 0. It's not that hard.
            {
                throw new ArgumentOutOfRangeException("Price must be greater than 0.");
            }

            // first, check that all available cash can meet the price.
            var totalCash = AvailableCash.Sum(x => x.Item2 * ProductPrices.GetProductValue(x.Item1));

            // If the total cash available is less than the price sought, return a copy of the available cash.
            if (totalCash < price)
            {
                return(AvailableCash.Copy());
            }

            // since we have more than we need,
            var result = new ProductAmountCollection();

            // start from the best and start making change, highest to lowest value.
            foreach (var coin in AvailableCash.OrderByDescending(x => ProductPrices.GetProductValue(x.Item1)))
            {
                // if none of that coin exist
                if (coin.Item2 == 0)
                {
                    // add it as zero
                    result.AddProducts(coin.Item1, 0);
                    // and skip to the next loop
                    continue;
                }

                // coin type
                var curr = coin.Item1;
                // coin value
                var val = ProductPrices.GetProductValue(curr);
                // if value is higher then remaining price.
                if (val > price)
                {
                    // add it as zero
                    result.AddProducts(curr, 0);
                    // and skip
                    continue;
                }

                // coin amount
                var amt = coin.Item2;

                // how many whole coins can fit into the price.
                var count = Math.Floor(price / val);

                // select cap coins at the available level.
                count = Math.Min(count, amt);

                // add to our change
                result.AddProducts(curr, count);

                // subtract the value we took out
                price -= val * count;
                // then go to the next coin.
            }

            // if there is a remainder
            if (price > 0)
            {
                // find the smallest coin available
                foreach (var coin in AvailableCash.OrderBy(x => ProductPrices.GetProductValue(x.Item1)))
                {
                    // if we have any available
                    if (result.GetProductValue(coin.Item1) < AvailableCash.GetProductValue(coin.Item1))
                    {
                        // add one
                        result.AddProducts(coin.Item1, 1);

                        // and move on.
                        break;

                        // by logic, only one should be needed as we guaranteed have
                        // more value in coins then the requested price and the
                        // remainder should be smaller than the smallest coin.
                    }
                }
            }

            // return the change.
            return(result);
        }