public IProductAmountCollection LossPhase()
        {
            var result = new ProductAmountCollection();

            // for each item in storage
            foreach (var pair in Storage)
            {
                var product = pair.Item1;
                var amount  = pair.Item2;

                // get a random amount of failure
                var failedAmount = product.FailedProducts(amount);

                if (failedAmount == 0)
                {
                    continue;
                }

                // remove the failed items from storage
                Storage.SubtractProducts(product, failedAmount);

                // Subtract the removed itmes from result.
                result.AddProducts(product, -failedAmount);
            }

            return(result);
        }
        public IProductAmountCollection UpForSale()
        {
            // Clear old sale space just in case.
            ForSale = new ProductAmountCollection();

            // for each good
            foreach (var pair in Storage)
            {
                // get each item
                var product = pair.Item1;
                var amount  = pair.Item2;

                // if it is desired by the population.
                if (TotalNeeds.Contains(product))
                {
                    // Subtract what is needed.
                    amount = amount - TotalNeeds.GetProductValue(product);
                }

                // If the amount left after removing needs is greater than 0
                // then add to our list for sale.
                if (amount > 0)
                {
                    ForSale.AddProducts(product, amount);
                }
            }

            return(ForSale);
        }
        /// <summary>
        /// The Production Phase of the population.
        /// </summary>
        /// <returns>The change in products because of the phase.</returns>
        public IProductAmountCollection ProductionPhase()
        {
            // Assuming perfection, get full needs expected.
            // inputs times the population divided by the labor needed for the job.
            var requirements = PrimaryJob.Inputs.Multiply(Count);

            // Get Capital Requirements (only one needed per day of labor)
            requirements.AddProducts(PrimaryJob.Capital.Multiply(Count));

            // With expected inputs, see what we can actually satisfy.
            double sat = 1;

            foreach (var pair in requirements)
            {
                // get product and amount
                var product = pair.Item1;
                var amount  = pair.Item2;

                // check if current satisfaction is less than the stored amount of the product
                // the product should be there BC of InitializeStorage
                sat = Math.Min(sat, Storage.GetProductValue(product) / amount);
            }

            // If something cannot be satisfied, we GTFO.
            if (sat == 0)
            {
                return(new ProductAmountCollection());
            }

            // With Satisfaction, get and consume the inputs.
            var inputs = PrimaryJob.Inputs.Multiply(Count / PrimaryJob.LaborRequirements * sat);

            ConsumeGoods(inputs, JobInputSatisfaction);
            // Add what was consumed to the result.
            var result = new ProductAmountCollection();

            result.AddProducts(inputs.Multiply(-1));

            // Get the resulting outputs
            var outputs = PrimaryJob.Outputs.Multiply(Count / PrimaryJob.LaborRequirements * sat);

            Storage.AddProducts(outputs);
            result.AddProducts(outputs);

            // Calculate remaining labor and add that to results
            if (sat < 1)
            {
                var remainder = sat * Count;

                result.AddProducts(JobLabor, remainder);
                Storage.AddProducts(JobLabor, remainder);
            }

            // Don't run breakdown of capital here, run it alongside all other breakdown chances.

            // return results
            return(result);
        }
Example #4
0
        public IProductAmountCollection BuyGoods(IPopulationGroup buyer, IProduct good, double amount,
                                                 IPopulationGroup seller)
        {
            if (buyer is null)
            {
                throw new ArgumentNullException(nameof(buyer));
            }
            if (good is null)
            {
                throw new ArgumentNullException(nameof(good));
            }
            if (seller is null)
            {
                throw new ArgumentNullException(nameof(seller));
            }
            if (amount <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(amount));
            }

            // get the cash we have available.
            var cash = buyer.GetCash(AcceptedCurrencies);

            var result = new ProductAmountCollection();

            // if we have any cash, try to buy with that first.
            if (cash.Any(x => x.Item2 > 0))
            {
                // Buy what we can with our cash.
                var transaction = seller.BuyGood(cash, good, amount, this);

                // With our transaction initiated, complete it on the buyer's end.
                buyer.CompleteTransaction(transaction);

                // Add the transaction to our return value
                result.AddProducts(transaction);

                // Update our desired amount
                amount -= transaction.GetProductValue(good);
            }

            // if we still have more to buy, it means we are out of cash. Begin bartering.
            // check we have things to barter.
            if (amount > 0 && buyer.ForSale.Any(x => x.Item2 > 0) && BarterLegal)
            {
                // Begin Bartering
                var barter = seller.BarterGood(buyer.ForSale, good, amount, this);

                // with the barter complete, finish for buyer.
                buyer.CompleteTransaction(barter);

                // add the transaction to the result
                result.AddProducts(barter);
            }

            // we've bought what we could from the pop, so return.
            return(result);
        }
Example #5
0
        // Territory Breakdown and management.

        #endregion InfoDetails

        public Market(IRandomizer randomizer)
        {
            rand = randomizer;

            ProductPrices   = new ProductAmountCollection();
            Shortfall       = new ProductAmountCollection();
            _surplus        = new ProductAmountCollection();
            _productSupply  = new ProductAmountCollection();
            _purchasedGoods = new ProductAmountCollection();
            _productDemand  = new ProductAmountCollection();
        }
 public PopulationGroup()
 {
     Id                      = Guid.NewGuid();
     SecondaryJobs           = new List <IJob>();
     AvailableStorageDetails = new Dictionary <string, double>();
     Storage                 = new ProductAmountCollection();
     LifeSatisfaction        = new ProductAmountCollection();
     DailySatisfaction       = new ProductAmountCollection();
     LuxurySatisfaction      = new ProductAmountCollection();
     JobInputSatisfaction    = new ProductAmountCollection();
     JobCapitalSatisfaction  = new ProductAmountCollection();
 }
Example #7
0
        /// <summary>
        /// Get's the Value of the currencies in the market.
        /// </summary>
        /// <returns>all currencies and their market value.</returns>
        public IProductAmountCollection CurrencyValues()
        {
            var result = new ProductAmountCollection();

            // for each coin
            foreach (var coin in AcceptedCurrencies)
            {
                // get add that coin and it's current market price to the result.
                result.AddProducts(coin, GetPrice(coin));
            }

            // and return it.
            return(result);
        }
Example #8
0
        public void SellPhase()
        {
            // Get all goods up for sale.
            _productSupply = Populous.SellPhase();

            // Reset Shortfall to zero.
            Shortfall = new ProductAmountCollection();

            // Fill Surplus preemtively, we'll remove bought products later.
            _surplus = ProductSupply.Copy();

            // While we're at it, also get total demand of all products
            _productDemand = Populous.TotalDemand();

            // And the hypothetical total production available.
            _productionCapacity = Populous.TotalProduction();
        }
        /// <summary>
        /// Consumes the given set of goods.
        /// </summary>
        /// <param name="goods">The goods to attempt to consume.</param>
        /// <param name="satisfaction">The satisfaction we'll be filling out as we go.</param>
        /// <returns>The change in products stored.</returns>
        private IProductAmountCollection ConsumeGoods(IProductAmountCollection goods,
                                                      IProductAmountCollection satisfaction)
        {
            var result = new ProductAmountCollection();

            // for each good to consume.
            foreach (var pair in goods)
            {
                // Get the item and amount of the person.
                var product = pair.Item1;
                var amount  = pair.Item2;

                // Assume All items being consumed are in storage already,
                // if they aren't we have a consistency problem.
                // get the satisfaction, capping it at 1.
                var sat = Math.Min(1, Storage.GetProductValue(product) / amount);

                // If satisfaction can't be met, subtract what you can.
                if (sat < 1)
                {
                    result.SubtractProducts(product,
                                            Storage.GetProductValue(product));

                    Storage.SubtractProducts(product,
                                             Storage.GetProductValue(product));
                }
                else // If greater than 1, then substract everything needed.
                {
                    result.SubtractProducts(product, amount);

                    Storage.SubtractProducts(product, amount);
                }

                // Finally, set it's satisfaction.
                satisfaction.SetProductAmount(product, sat);
            }

            // Return change in products stored.
            return(result);
        }
        /// <summary>
        /// Buys good via barter.
        /// </summary>
        /// <param name="buyerStock">The buyer's goods up for trade.</param>
        /// <param name="good">The good being traded for.</param>
        /// <param name="amount">The amount of the good being bought.</param>
        /// <param name="market">The market that the barter is taking place in.</param>
        /// <returns>The resulting change in goods for the buyer.</returns>
        public IProductAmountCollection BarterGood(IProductAmountCollection buyerStock,
                                                   IProduct good, double amount, IMarket market)
        {
            // TODO, update to use the coincidence of wants,
            // it will discourage barter more than anything.
            if (buyerStock == null)
            {
                throw new ArgumentNullException(nameof(buyerStock));
            }
            if (good == null)
            {
                throw new ArgumentNullException(nameof(good));
            }
            if (market == null)
            {
                throw new ArgumentNullException(nameof(market));
            }
            if (amount <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(amount));
            }

            // the return result of the barter
            var result = new ProductAmountCollection();

            // TODO a barter modifier, to discourage or encourage bartering.
            // This should become more flexible later.
            var BarterMod = 1;

            // Get how much we can or want to get
            amount = Math.Min(amount, ForSale.GetProductValue(good));

            // get the price of the good.
            var totalPrice = GetPrice(good, market.GetPrice(good)) * amount * BarterMod;

            // get the total price of what's offered.
            double barterVal = 0;

            foreach (var product in buyerStock)
            {
                barterVal += market.GetPrice(product.Item1) * product.Item2;
            }

            // the barter we're trading for the goods.
            IProductAmountCollection barter;

            // if the available barter is greater than the price, begin bartering
            if (barterVal >= totalPrice)
            {
                // Use get change for the easiest option.
                barter = market.ChangeForPrice(buyerStock, totalPrice);

                // don't go more accurate, barter isn't supposed to be more accurate to coins,
                // no one would accept change in bits of wheat.

                // Add the goods being bought
                result.AddProducts(good, amount);

                // Remove the goods being traded.
                result.AddProducts(barter.Multiply(-1));
            }
            else
            {
                // if it's not enough, throw it all in, and buy what you can.
                double buyableUnits = 0;

                // if the good is fractional
                if (good.Fractional)
                {
                    // just divide
                    buyableUnits = barterVal / (market.GetPrice(good) * BarterMod);
                }
                else
                {
                    // round down
                    buyableUnits = Math.Floor(barterVal / (market.GetPrice(good) * BarterMod));
                }

                // If we can buy anything, do so.
                if (buyableUnits > 0)
                {
                    // add the goods
                    result.AddProducts(good, buyableUnits);
                    // subtract the goods
                    result.AddProducts(buyerStock.Multiply(-1));
                }
            }

            // No change, this is bartering.

            // We've done what we can, move on.
            return(result);
        }
        public IProductAmountCollection BuyGood(IProductAmountCollection cash,
                                                IProduct good, double amount, IMarket market)
        {
            // Sanity check for nulls.
            if (cash is null)
            {
                throw new ArgumentNullException(nameof(cash));
            }
            if (good is null)
            {
                throw new ArgumentNullException(nameof(good));
            }
            if (market is null)
            {
                throw new ArgumentNullException(nameof(market));
            }
            if (amount <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(amount));
            }

            // The collection we're returning.
            var result = new ProductAmountCollection();

            // get how much to buy, what's available, or what's desired.
            var available = Math.Min(amount, ForSale.GetProductValue(good));

            // get the price of that good
            var totalPrice = GetPrice(good, market.GetPrice(good)) * available;

            // get the cash needed for the goods
            var money = market.ChangeForPrice(cash, totalPrice);

            // get our available money total
            var totalMoney = money.Sum(x => market.GetPrice(x.Item1, x.Item2));

            // if the money is enough, just buy outright
            if (totalMoney >= totalPrice)
            {
                // Add what they're buying.
                result.AddProducts(good, available);
                // remove what they spent
                result.AddProducts(money.Multiply(-1));
            }
            else if (!good.Fractional && totalMoney < market.GetPrice(good))
            {// If the total money is not enough for a single unit, and the good can't be divided
                // we can't actually buy anything, so just return an empty transaction
                return(new ProductAmountCollection());
            }
            else // if it's not enough
            {
                // get the buyable units of the good
                double buyableUnits = 0;

                // if we can buy fractionally
                if (good.Fractional)
                {
                    // just do the math straight.
                    buyableUnits = totalMoney / market.GetPrice(good);
                }
                else // if we can't
                {
                    // take what we can and round down, seller should always make more than the buyer here.
                    buyableUnits = Math.Floor(totalMoney / market.GetPrice(good));
                }

                // if we can buy any units
                if (buyableUnits > 0)
                {
                    // buy them add the units to the results
                    result.AddProducts(good, buyableUnits);
                    // subtract the cash.
                    result.AddProducts(money.Multiply(-1));
                }
            }

            // Get change, if any.
            var change = totalMoney - market.GetPrice(good) * result.GetProductValue(good);

            // get the change, if any
            IProductAmountCollection buyersChange = new ProductAmountCollection();

            // if change is greater than 0.
            if (change > 0) // Make said change.
            {
                buyersChange = market.ChangeForPrice(GetCash(market.AcceptedCurrencies), change);
            }

            // add back the buyer's change
            result.AddProducts(buyersChange);

            // TODO, maybe add check to see if the seller shortchanged the buyer.

            // complete the transaction for the seller, and subtract the result.
            CompleteTransaction(result.Multiply(-1));

            // we're done, return the change in the buyer's goods.
            return(result);
        }
Example #12
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);
        }
Example #13
0
        /// <summary>
        /// Buys good from the market.
        /// </summary>
        /// <param name="buyer">The one buying the good.</param>
        /// <param name="good">The good they are trying to buy.</param>
        /// <param name="amount">How much they are trying to buy.</param>
        /// <returns>The Receipt of purchases</returns>
        public IProductAmountCollection BuyGoodsFromMarket(IPopulationGroup buyer,
                                                           IProduct good, double amount)
        {
            if (buyer is null)
            {
                throw new ArgumentNullException(nameof(buyer));
            }
            if (good is null)
            {
                throw new ArgumentNullException(nameof(good));
            }
            if (amount <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(amount));
            }

            // The result of the purchases.
            IProductAmountCollection result = new ProductAmountCollection();

            // First buy from local merchants, they only accept cash.
            result = Populous.Merchants
                     .BuyGood(buyer.GetCash(AcceptedCurrencies), good, amount, this);

            // see how much was bought.
            double remainder = 0;

            // if any was bought, update what we are seeking.
            if (result.Contains(good))
            {
                remainder = amount - result.GetProductValue(good);
                // and complete the transaction
                buyer.CompleteTransaction(result);
            }

            // if no remainder, return
            if (remainder <= 0)
            {
                return(result);
            }

            // Then buy from everyone else via both cash and barter.
            foreach (var seller in Populous.GetPopsSellingProduct(good))
            {
                // If someone is selling the good, buy or barter with them.
                var reciept = BuyGoods(buyer, good, remainder, seller);

                // if something was bought
                if (reciept.Count() > 0)
                {
                    // remove it from the remainder
                    remainder -= reciept.GetProductValue(good);

                    // add it to our result
                    result.AddProducts(reciept);
                }

                // if nothing remains, we're done.
                if (remainder <= 0)
                {
                    return(result);
                }
            }

            // Finish buy going to the travelling merchants, if all else fails.
            foreach (var travSeller in TravellingMerchantsSelling(good))
            {
                var reciept = travSeller.BuyGood(buyer.GetCash(AcceptedCurrencies), good, remainder, this);

                // if something was bought
                if (reciept.Count() > 0)
                {
                    // remove it from remainder
                    remainder -= reciept.GetProductValue(good);

                    // Complete the transaction for the buyer
                    buyer.CompleteTransaction(reciept);

                    // add it to the result
                    result.AddProducts(reciept);
                }

                // if nothing remains, return
                if (remainder <= 0)
                {
                    return(result);
                }
            }

            // return the ultimate receipt.
            return(result);
        }