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); }
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); }
// 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(); }
/// <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); }
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); }
/// <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); }
/// <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); }