Merge branch 'christof/de_unwrap2' into 'master'

Replace unwrap in the economy code

See merge request veloren/veloren!1988
This commit is contained in:
Joshua Barretto 2021-03-24 22:22:02 +00:00
commit 5b234a3d08

View File

@ -17,6 +17,7 @@ use common::{
Good::{Coin, Transportation}, Good::{Coin, Transportation},
}, },
}; };
use std::cmp::Ordering::Less;
use tracing::{debug, info}; use tracing::{debug, info};
const MONTH: f32 = 30.0; const MONTH: f32 = 30.0;
@ -59,7 +60,7 @@ impl EconStatistics {
} }
} }
pub fn csv_entry(f: &mut std::fs::File, site: &Site) { pub fn csv_entry(f: &mut std::fs::File, site: &Site) -> Result<(), std::io::Error> {
use std::io::Write; use std::io::Write;
write!( write!(
*f, *f,
@ -68,61 +69,60 @@ pub fn csv_entry(f: &mut std::fs::File, site: &Site) {
site.get_origin().x, site.get_origin().x,
site.get_origin().y, site.get_origin().y,
site.economy.pop site.economy.pop
) )?;
.unwrap();
for g in good_list() { for g in good_list() {
write!(*f, "{:?},", site.economy.values[*g].unwrap_or(-1.0)).unwrap(); write!(*f, "{:?},", site.economy.values[*g].unwrap_or(-1.0))?;
} }
for g in good_list() { for g in good_list() {
write!(f, "{:?},", site.economy.labor_values[*g].unwrap_or(-1.0)).unwrap(); write!(f, "{:?},", site.economy.labor_values[*g].unwrap_or(-1.0))?;
} }
for g in good_list() { for g in good_list() {
write!(f, "{:?},", site.economy.stocks[*g]).unwrap(); write!(f, "{:?},", site.economy.stocks[*g])?;
} }
for g in good_list() { for g in good_list() {
write!(f, "{:?},", site.economy.marginal_surplus[*g]).unwrap(); write!(f, "{:?},", site.economy.marginal_surplus[*g])?;
} }
for l in Labor::list() { for l in Labor::list() {
write!(f, "{:?},", site.economy.labors[l] * site.economy.pop).unwrap(); write!(f, "{:?},", site.economy.labors[l] * site.economy.pop)?;
} }
for l in Labor::list() { for l in Labor::list() {
write!(f, "{:?},", site.economy.productivity[l]).unwrap(); write!(f, "{:?},", site.economy.productivity[l])?;
} }
for l in Labor::list() { for l in Labor::list() {
write!(f, "{:?},", site.economy.yields[l]).unwrap(); write!(f, "{:?},", site.economy.yields[l])?;
} }
writeln!(f).unwrap(); writeln!(f)
} }
pub fn simulate(index: &mut Index, world: &mut WorldSim) { fn simulate_return(index: &mut Index, world: &mut WorldSim) -> Result<(), std::io::Error> {
use std::io::Write; use std::io::Write;
// please not that GENERATE_CSV is off by default, so panicing is not harmful // please not that GENERATE_CSV is off by default, so panicing is not harmful
// here // here
let mut f = if GENERATE_CSV { let mut f = if GENERATE_CSV {
let mut f = std::fs::File::create("economy.csv").unwrap(); let mut f = std::fs::File::create("economy.csv")?;
write!(f, "Site,PosX,PosY,Population,").unwrap(); write!(f, "Site,PosX,PosY,Population,")?;
for g in good_list() { for g in good_list() {
write!(f, "{:?} Value,", g).unwrap(); write!(f, "{:?} Value,", g)?;
} }
for g in good_list() { for g in good_list() {
write!(f, "{:?} LaborVal,", g).unwrap(); write!(f, "{:?} LaborVal,", g)?;
} }
for g in good_list() { for g in good_list() {
write!(f, "{:?} Stock,", g).unwrap(); write!(f, "{:?} Stock,", g)?;
} }
for g in good_list() { for g in good_list() {
write!(f, "{:?} Surplus,", g).unwrap(); write!(f, "{:?} Surplus,", g)?;
} }
for l in Labor::list() { for l in Labor::list() {
write!(f, "{:?} Labor,", l).unwrap(); write!(f, "{:?} Labor,", l)?;
} }
for l in Labor::list() { for l in Labor::list() {
write!(f, "{:?} Productivity,", l).unwrap(); write!(f, "{:?} Productivity,", l)?;
} }
for l in Labor::list() { for l in Labor::list() {
write!(f, "{:?} Yields,", l).unwrap(); write!(f, "{:?} Yields,", l)?;
} }
writeln!(f).unwrap(); writeln!(f)?;
Some(f) Some(f)
} else { } else {
None None
@ -138,27 +138,23 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) {
if let Some(f) = f.as_mut() { if let Some(f) = f.as_mut() {
if i % 5 == 0 { if i % 5 == 0 {
let site = index if let Some(site) = index
.sites .sites
.values() .values()
.find(|s| !matches!(s.kind, SiteKind::Dungeon(_))) .find(|s| !matches!(s.kind, SiteKind::Dungeon(_)))
//match s.kind { {
// SiteKind::Dungeon(_) => false, csv_entry(f, site)?;
// _ => true, }
// })
//.skip(3) // because first three get depopulated over time
.unwrap();
csv_entry(f, site);
} }
} }
} }
tracing::info!("economy simulation end"); tracing::info!("economy simulation end");
if let Some(f) = f.as_mut() { if let Some(f) = f.as_mut() {
writeln!(f).unwrap(); writeln!(f)?;
for site in index.sites.ids() { for site in index.sites.ids() {
let site = index.sites.get(site); let site = index.sites.get(site);
csv_entry(f, site); csv_entry(f, site)?;
} }
} }
@ -196,6 +192,12 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) {
); );
check_money(index); check_money(index);
} }
Ok(())
}
pub fn simulate(index: &mut Index, world: &mut WorldSim) {
simulate_return(index, world)
.unwrap_or_else(|err| info!("I/O error in simulate (economy.csv not writable?): {}", err));
} }
fn check_money(index: &mut Index) { fn check_money(index: &mut Index) {
@ -280,7 +282,7 @@ fn plan_trade_for_site(
) )
}) })
.collect(); .collect();
missing_goods.sort_by(|a, b| b.1.0.partial_cmp(&a.1.0).unwrap()); missing_goods.sort_by(|a, b| b.1.0.partial_cmp(&a.1.0).unwrap_or(Less));
let mut extra_goods: MapVec<Good, f32> = MapVec::from_iter( let mut extra_goods: MapVec<Good, f32> = MapVec::from_iter(
site.economy site.economy
.surplus .surplus
@ -314,7 +316,7 @@ fn plan_trade_for_site(
) )
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
rel_value.sort_by(|a, b| (b.1.0.partial_cmp(&a.1.0).unwrap())); rel_value.sort_by(|a, b| b.1.0.partial_cmp(&a.1.0).unwrap_or(Less));
(n.id, rel_value) (n.id, rel_value)
}) })
.collect(); .collect();
@ -331,7 +333,7 @@ fn plan_trade_for_site(
.filter(|n| n.last_supplies[*g] > 0.0) .filter(|n| n.last_supplies[*g] > 0.0)
.map(|n| (n.id, (n.last_values[*g], n.last_supplies[*g]))) .map(|n| (n.id, (n.last_values[*g], n.last_supplies[*g])))
.collect(); .collect();
neighbor_prices.sort_by(|a, b| a.1.0.partial_cmp(&b.1.0).unwrap()); neighbor_prices.sort_by(|a, b| a.1.0.partial_cmp(&b.1.0).unwrap_or(Less));
neighbor_prices neighbor_prices
}) })
}) })
@ -358,7 +360,8 @@ fn plan_trade_for_site(
// === the actual planning is here === // === the actual planning is here ===
for (g, (_, a)) in missing_goods.iter() { for (g, (_, a)) in missing_goods.iter() {
let mut amount = *a; let mut amount = *a;
for (s, (price, supply)) in good_price.get_mut(g).unwrap().iter_mut() { if let Some(site_price_stock) = good_price.get_mut(g) {
for (s, (price, supply)) in site_price_stock.iter_mut() {
// how much to buy, limit by supply and transport budget // how much to buy, limit by supply and transport budget
let mut buy_target = amount.min(*supply); let mut buy_target = amount.min(*supply);
let effort = transportation_effort(*g); let effort = transportation_effort(*g);
@ -385,6 +388,7 @@ fn plan_trade_for_site(
buy_target, buy_target,
balance, balance,
); );
if let Some(neighbor_orders) = neighbor_orders.get_mut(s) {
// find suitable goods in exchange // find suitable goods in exchange
let mut acute_missing_dispatch: f32 = 0.0; // only count the highest priority (not multiple times) let mut acute_missing_dispatch: f32 = 0.0; // only count the highest priority (not multiple times)
for (g2, (_, price2)) in good_payment[s].iter() { for (g2, (_, price2)) in good_payment[s].iter() {
@ -412,7 +416,7 @@ fn plan_trade_for_site(
extra_goods[*g2] -= amount2; extra_goods[*g2] -= amount2;
debug!("pay {:?} {:?} = {:?}", g2, amount2, balance); debug!("pay {:?} {:?} = {:?}", g2, amount2, balance);
balance -= amount2 * price2; balance -= amount2 * price2;
neighbor_orders.get_mut(s).unwrap()[*g2] -= amount2; neighbor_orders[*g2] -= amount2;
dispatch_capacity = (dispatch_capacity - dispatch).max(0.0); dispatch_capacity = (dispatch_capacity - dispatch).max(0.0);
if balance == 0.0 { if balance == 0.0 {
break; break;
@ -424,7 +428,7 @@ fn plan_trade_for_site(
buy_target -= balance / *price; buy_target -= balance / *price;
buy_target = buy_target.min(amount); buy_target = buy_target.min(amount);
collect_capacity = (collect_capacity - buy_target * effort).max(0.0); collect_capacity = (collect_capacity - buy_target * effort).max(0.0);
neighbor_orders.get_mut(s).unwrap()[*g] += buy_target; neighbor_orders[*g] += buy_target;
amount -= buy_target; amount -= buy_target;
debug!( debug!(
"deal amount {:?} end_balance {:?} price {:?} left {:?}", "deal amount {:?} end_balance {:?} price {:?} left {:?}",
@ -432,13 +436,15 @@ fn plan_trade_for_site(
); );
} }
} }
}
}
// if site_id.id() == 1 { // if site_id.id() == 1 {
// // cut down number of lines printed // // cut down number of lines printed
// info!("orders {:#?}", neighbor_orders,); // info!("orders {:#?}", neighbor_orders,);
// } // }
// TODO: Use planned orders and calculate value, stock etc. accordingly // TODO: Use planned orders and calculate value, stock etc. accordingly
for n in &site.economy.neighbors { for n in &site.economy.neighbors {
let orders = neighbor_orders.get(&n.id).unwrap(); if let Some(orders) = neighbor_orders.get(&n.id) {
for (g, a) in orders.iter() { for (g, a) in orders.iter() {
result[g] += *a; result[g] += *a;
} }
@ -457,6 +463,7 @@ fn plan_trade_for_site(
external_orders.insert(n.id, vec![to]); external_orders.insert(n.id, vec![to]);
} }
} }
}
// return missing transport capacity // return missing transport capacity
//missing_collect.max(missing_dispatch) //missing_collect.max(missing_dispatch)
debug!( debug!(
@ -531,14 +538,14 @@ fn trade_at_site(
.filter(|(_, &a)| a > 0.0) .filter(|(_, &a)| a > 0.0)
.map(|(g, a)| (g, *a, prices[g])) .map(|(g, a)| (g, *a, prices[g]))
.collect(); .collect();
sorted_sell.sort_by(|a, b| (a.2.partial_cmp(&b.2).unwrap())); sorted_sell.sort_by(|a, b| (a.2.partial_cmp(&b.2).unwrap_or(Less)));
let mut sorted_buy: Vec<(Good, f32, f32)> = o let mut sorted_buy: Vec<(Good, f32, f32)> = o
.amount .amount
.iter() .iter()
.filter(|(_, &a)| a < 0.0) .filter(|(_, &a)| a < 0.0)
.map(|(g, a)| (g, *a, prices[g])) .map(|(g, a)| (g, *a, prices[g]))
.collect(); .collect();
sorted_buy.sort_by(|a, b| (b.2.partial_cmp(&a.2).unwrap())); sorted_buy.sort_by(|a, b| (b.2.partial_cmp(&a.2).unwrap_or(Less)));
debug!( debug!(
"with {} {:?} buy {:?}", "with {} {:?} buy {:?}",
o.customer.id(), o.customer.id(),
@ -547,10 +554,8 @@ fn trade_at_site(
); );
let mut good_delivery = MapVec::from_default(0.0); let mut good_delivery = MapVec::from_default(0.0);
for (g, amount, price) in sorted_sell.iter() { for (g, amount, price) in sorted_sell.iter() {
if order_stock_ratio[*g].is_none() { if let Some(order_stock_ratio) = order_stock_ratio[*g] {
continue; let allocated_amount = *amount / order_stock_ratio.max(1.0);
}
let allocated_amount = *amount / order_stock_ratio[*g].unwrap().max(1.0);
let mut balance = allocated_amount * *price; let mut balance = allocated_amount * *price;
for (g2, avail, price2) in sorted_buy.iter_mut() { for (g2, avail, price2) in sorted_buy.iter_mut() {
let amount2 = (-*avail).min(balance / *price2); let amount2 = (-*avail).min(balance / *price2);
@ -571,7 +576,7 @@ fn trade_at_site(
*g, *g,
paid_amount, paid_amount,
allocated_amount, allocated_amount,
order_stock_ratio[*g].unwrap(), order_stock_ratio,
); );
} else { } else {
debug!("bought {:?} {} {}", *g, paid_amount, *price); debug!("bought {:?} {} {}", *g, paid_amount, *price);
@ -584,13 +589,14 @@ fn trade_at_site(
*g, *g,
paid_amount, paid_amount,
total_orders[*g], total_orders[*g],
order_stock_ratio[*g].unwrap(), order_stock_ratio,
next_demand[*g] next_demand[*g]
); );
} }
assert!(economy.stocks[*g] - paid_amount >= 0.0); assert!(economy.stocks[*g] - paid_amount >= 0.0);
economy.stocks[*g] -= paid_amount; economy.stocks[*g] -= paid_amount;
} }
}
for (g, amount, _) in sorted_buy.drain(..) { for (g, amount, _) in sorted_buy.drain(..) {
if amount < 0.0 { if amount < 0.0 {
debug!("shipping back unsold {} of {:?}", amount, g); debug!("shipping back unsold {} of {:?}", amount, g);
@ -809,7 +815,7 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id<Site>, dt: f32) {
.map(|(g, _)| g) .map(|(g, _)| g)
.chain(trade_boost) .chain(trade_boost)
.map(|output_good| site.economy.values[*output_good].unwrap_or(0.0)) .map(|output_good| site.economy.values[*output_good].unwrap_or(0.0))
.max_by(|a, b| a.abs().partial_cmp(&b.abs()).unwrap()) .max_by(|a, b| a.abs().partial_cmp(&b.abs()).unwrap_or(Less))
.unwrap_or(0.0) .unwrap_or(0.0)
* site.economy.productivity[labor] * site.economy.productivity[labor]
}); });
@ -861,7 +867,7 @@ pub fn tick_site_economy(index: &mut Index, site_id: Id<Site>, dt: f32) {
// What proportion of this order is the economy able to satisfy? // What proportion of this order is the economy able to satisfy?
(stocks_before[*good] / demand[*good]).min(1.0) (stocks_before[*good] / demand[*good]).min(1.0)
}) })
.min_by(|a, b| a.partial_cmp(b).unwrap()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(Less))
.unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor)); .unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor));
assert!(labor_productivity >= 0.0); assert!(labor_productivity >= 0.0);
@ -1120,8 +1126,9 @@ mod tests {
outarr.push(val); outarr.push(val);
} }
let pretty = ron::ser::PrettyConfig::new(); let pretty = ron::ser::PrettyConfig::new();
let result = ron::ser::to_string_pretty(&outarr, pretty).unwrap(); if let Ok(result) = ron::ser::to_string_pretty(&outarr, pretty) {
info!("RON {}", result); info!("RON {}", result);
}
} else { } else {
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
let ron_file = std::fs::File::open("economy_testinput.ron") let ron_file = std::fs::File::open("economy_testinput.ron")
@ -1161,12 +1168,14 @@ mod tests {
// we can't add these in the first loop as neighbors will refer to later sites // we can't add these in the first loop as neighbors will refer to later sites
// (which aren't valid in the first loop) // (which aren't valid in the first loop)
for (i, e) in econ_testinput.iter().enumerate() { for (i, e) in econ_testinput.iter().enumerate() {
let id = index.sites.recreate_id(i as u64).unwrap(); if let Some(id) = index.sites.recreate_id(i as u64) {
let mut neighbors: Vec<crate::site::economy::NeighborInformation> = e let mut neighbors: Vec<crate::site::economy::NeighborInformation> = e
.neighbors .neighbors
.iter() .iter()
.map(|(nid, dist)| index.sites.recreate_id(*nid).map(|i| (i, dist)))
.flatten()
.map(|(nid, dist)| crate::site::economy::NeighborInformation { .map(|(nid, dist)| crate::site::economy::NeighborInformation {
id: index.sites.recreate_id(*nid).unwrap(), id: nid,
travel_distance: *dist, travel_distance: *dist,
last_values: MapVec::from_default(0.0), last_values: MapVec::from_default(0.0),
last_supplies: MapVec::from_default(0.0), last_supplies: MapVec::from_default(0.0),
@ -1180,6 +1189,7 @@ mod tests {
.append(&mut neighbors); .append(&mut neighbors);
} }
} }
}
crate::sim2::simulate(&mut index, &mut sim); crate::sim2::simulate(&mut index, &mut sim);
} }
} }