mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
separate orders by professions and everyone
cleanup and fix strictly this population growth logic is wrong, but identical to the existing one standardize on production, remove more dead code fix example + rustfmt separate csv logic from tick (intention is to move it into economy) remove the format call from economy (inactive debugging code) remove more formatting rustfmt small clippy fix Reduce precision on output move csv code move rest of sim2 mod into economy (context) remove more unused parts keep things a bit more coherent remove pub make more functions and fields private remove outdated input, fix other, print names, move output to proper abstraction remove dead code
This commit is contained in:
parent
edffd576d4
commit
6d3ea3172c
@ -1,220 +0,0 @@
|
|||||||
[
|
|
||||||
(
|
|
||||||
name: "Credge",
|
|
||||||
position: (4176, 4080),
|
|
||||||
kind: Settlement,
|
|
||||||
neighbors: [
|
|
||||||
(2, 112),
|
|
||||||
(1, 146),
|
|
||||||
(6, 98),
|
|
||||||
(9, 145),
|
|
||||||
],
|
|
||||||
resources: [
|
|
||||||
(
|
|
||||||
good: Terrain(Lake),
|
|
||||||
amount: 4,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Mountain),
|
|
||||||
amount: 52,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Grassland),
|
|
||||||
amount: 7073,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Ocean),
|
|
||||||
amount: 4929,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Forest),
|
|
||||||
amount: 6360,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Etodren",
|
|
||||||
position: (1200, 2928),
|
|
||||||
kind: Settlement,
|
|
||||||
neighbors: [
|
|
||||||
(2, 258),
|
|
||||||
(0, 146),
|
|
||||||
(6, 60),
|
|
||||||
(9, 158),
|
|
||||||
],
|
|
||||||
resources: [
|
|
||||||
(
|
|
||||||
good: Terrain(Mountain),
|
|
||||||
amount: 288,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Grassland),
|
|
||||||
amount: 4129,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Desert),
|
|
||||||
amount: 230,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Ocean),
|
|
||||||
amount: 2923,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Forest),
|
|
||||||
amount: 3139,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Twige",
|
|
||||||
position: (2000, 7632),
|
|
||||||
kind: Settlement,
|
|
||||||
neighbors: [
|
|
||||||
(0, 112),
|
|
||||||
(1, 258),
|
|
||||||
(6, 109),
|
|
||||||
(9, 32),
|
|
||||||
],
|
|
||||||
resources: [
|
|
||||||
(
|
|
||||||
good: Terrain(Lake),
|
|
||||||
amount: 1,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Mountain),
|
|
||||||
amount: 231,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Grassland),
|
|
||||||
amount: 3308,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Desert),
|
|
||||||
amount: 1695,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Ocean),
|
|
||||||
amount: 487,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Forest),
|
|
||||||
amount: 1338,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Pleed Dungeon",
|
|
||||||
position: (6922, 1034),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Fred Lair",
|
|
||||||
position: (3786, 2250),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Frer Dungeon",
|
|
||||||
position: (6602, 2250),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Inige Castle",
|
|
||||||
position: (1360, 4304),
|
|
||||||
kind: Castle,
|
|
||||||
neighbors: [
|
|
||||||
(0, 98),
|
|
||||||
(1, 60),
|
|
||||||
(2, 109),
|
|
||||||
(9, 99),
|
|
||||||
],
|
|
||||||
resources: [
|
|
||||||
(
|
|
||||||
good: Terrain(Mountain),
|
|
||||||
amount: 424,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Grassland),
|
|
||||||
amount: 958,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Desert),
|
|
||||||
amount: 1285,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Ocean),
|
|
||||||
amount: 669,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Forest),
|
|
||||||
amount: 1781,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Estedock Catacombs",
|
|
||||||
position: (4650, 330),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Oreefey Lair",
|
|
||||||
position: (1578, 3754),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Lasnast Keep",
|
|
||||||
position: (1136, 6832),
|
|
||||||
kind: Castle,
|
|
||||||
neighbors: [
|
|
||||||
(0, 145),
|
|
||||||
(2, 32),
|
|
||||||
(1, 158),
|
|
||||||
(6, 99),
|
|
||||||
],
|
|
||||||
resources: [
|
|
||||||
(
|
|
||||||
good: Terrain(Mountain),
|
|
||||||
amount: 352,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Grassland),
|
|
||||||
amount: 887,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Desert),
|
|
||||||
amount: 2606,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Ocean),
|
|
||||||
amount: 286,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
good: Terrain(Forest),
|
|
||||||
amount: 679,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Oren Lair",
|
|
||||||
position: (6730, 2506),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
name: "Ween Crib",
|
|
||||||
position: (2250, 8010),
|
|
||||||
kind: Dungeon,
|
|
||||||
neighbors: [],
|
|
||||||
resources: [],
|
|
||||||
),
|
|
||||||
]
|
|
@ -4,8 +4,8 @@
|
|||||||
position: (1, 1),
|
position: (1, 1),
|
||||||
kind: Settlement,
|
kind: Settlement,
|
||||||
neighbors: [
|
neighbors: [
|
||||||
(1, 10),
|
1,
|
||||||
(2, 10),
|
2,
|
||||||
],
|
],
|
||||||
resources: [
|
resources: [
|
||||||
(
|
(
|
||||||
@ -19,7 +19,7 @@
|
|||||||
position: (10, 10),
|
position: (10, 10),
|
||||||
kind: Settlement,
|
kind: Settlement,
|
||||||
neighbors: [
|
neighbors: [
|
||||||
(0, 10),
|
0,
|
||||||
],
|
],
|
||||||
resources: [
|
resources: [
|
||||||
(
|
(
|
||||||
@ -33,7 +33,7 @@
|
|||||||
position: (20, 10),
|
position: (20, 10),
|
||||||
kind: Settlement,
|
kind: Settlement,
|
||||||
neighbors: [
|
neighbors: [
|
||||||
(0, 10),
|
0,
|
||||||
],
|
],
|
||||||
resources: [
|
resources: [
|
||||||
(
|
(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use common::trade::Good;
|
use common::trade::Good;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use veloren_world::site::economy::{self, good_list, Economy};
|
use veloren_world::site::economy::{GraphInfo, Labor};
|
||||||
//use regex::Regex::replace_all;
|
//use regex::Regex::replace_all;
|
||||||
|
|
||||||
fn good_name(g: Good) -> String {
|
fn good_name(g: Good) -> String {
|
||||||
@ -9,20 +9,18 @@ fn good_name(g: Good) -> String {
|
|||||||
res.replace(')', "_")
|
res.replace(')', "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn labor_name(l: economy::Labor) -> String {
|
fn labor_name(l: Labor) -> String {
|
||||||
let res = format!("{:?}", l);
|
let res = format!("{:?}", l);
|
||||||
res.replace(' ', "_")
|
res.replace(' ', "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
let eco = Economy::default();
|
let eco = GraphInfo::default();
|
||||||
let o = eco.get_orders();
|
|
||||||
let p = eco.get_productivity();
|
|
||||||
|
|
||||||
let mut f = std::fs::File::create("economy.gv")?;
|
let mut f = std::fs::File::create("economy.gv")?;
|
||||||
writeln!(f, "digraph economy {{")?;
|
writeln!(f, "digraph economy {{")?;
|
||||||
for i in good_list() {
|
for i in eco.good_list() {
|
||||||
let color = if economy::direct_use_goods().contains(&i) {
|
let color = if !eco.can_store(&i) {
|
||||||
"green"
|
"green"
|
||||||
} else {
|
} else {
|
||||||
"orange"
|
"orange"
|
||||||
@ -33,44 +31,42 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(f, "// Professions")?;
|
writeln!(f, "// Professions")?;
|
||||||
writeln!(f, "Everyone [shape=doubleoctagon];")?;
|
writeln!(f, "Everyone [shape=doubleoctagon];")?;
|
||||||
for i in economy::Labor::list() {
|
for i in eco.labor_list() {
|
||||||
writeln!(f, "{:?} [shape=box];", labor_name(i))?;
|
writeln!(f, "{:?} [shape=box];", labor_name(i))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(f, "// Orders")?;
|
writeln!(f, "// Orders")?;
|
||||||
|
let o = eco.get_orders();
|
||||||
for i in o.iter() {
|
for i in o.iter() {
|
||||||
for j in i.1.iter() {
|
for j in i.1.iter() {
|
||||||
if i.0.is_some() {
|
let style = if matches!(j.0.into(), Good::Tools | Good::Armor | Good::Potions) {
|
||||||
let style = if matches!(j.0.into(), Good::Tools)
|
", style=dashed, color=orange"
|
||||||
|| matches!(j.0.into(), Good::Armor)
|
|
||||||
|| matches!(j.0.into(), Good::Potions)
|
|
||||||
{
|
|
||||||
", style=dashed, color=orange"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"{:?} -> {:?} [label=\"{:.1}\"{}];",
|
|
||||||
good_name(j.0.into()),
|
|
||||||
labor_name(i.0.unwrap()),
|
|
||||||
j.1,
|
|
||||||
style
|
|
||||||
)?;
|
|
||||||
} else {
|
} else {
|
||||||
writeln!(
|
""
|
||||||
f,
|
};
|
||||||
"{:?} -> Everyone [label=\"{:.1}\"];",
|
writeln!(
|
||||||
good_name(j.0.into()),
|
f,
|
||||||
j.1
|
"{:?} -> {:?} [label=\"{:.1}\"{}];",
|
||||||
)?;
|
good_name(j.0.into()),
|
||||||
}
|
labor_name(i.0),
|
||||||
|
j.1,
|
||||||
|
style
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for j in eco.get_orders_everyone().iter() {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"{:?} -> Everyone [label=\"{:.1}\"];",
|
||||||
|
good_name(j.0.into()),
|
||||||
|
j.1
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(f, "// Products")?;
|
writeln!(f, "// Products")?;
|
||||||
|
let p = eco.get_production();
|
||||||
for i in p.iter() {
|
for i in p.iter() {
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
use crate::{sim::WorldSim, site::economy::simulate_economy, Index};
|
||||||
|
|
||||||
|
pub fn simulate(index: &mut Index, _world: &mut WorldSim) { simulate_economy(index); }
|
@ -1,11 +1,8 @@
|
|||||||
|
/// this contains global housekeeping info during simulation
|
||||||
use crate::{
|
use crate::{
|
||||||
sim::WorldSim,
|
|
||||||
site::{
|
site::{
|
||||||
economy::{
|
economy::{self, Economy, DAYS_PER_MONTH, DAYS_PER_YEAR, INTER_SITE_TRADE},
|
||||||
good_list, vergleich, LaborIndex, COIN_INDEX, DAYS_PER_MONTH, DAYS_PER_YEAR,
|
SiteKind,
|
||||||
INTER_SITE_TRADE,
|
|
||||||
},
|
|
||||||
Site, SiteKind,
|
|
||||||
},
|
},
|
||||||
Index,
|
Index,
|
||||||
};
|
};
|
||||||
@ -14,43 +11,42 @@ use tracing::{debug, info};
|
|||||||
|
|
||||||
// this is an empty replacement for https://github.com/cpetig/vergleich
|
// this is an empty replacement for https://github.com/cpetig/vergleich
|
||||||
// which can be used to compare values acros runs
|
// which can be used to compare values acros runs
|
||||||
pub mod vergleich {
|
// pub mod vergleich {
|
||||||
pub struct Error {}
|
// pub struct Error {}
|
||||||
impl Error {
|
// impl Error {
|
||||||
pub fn to_string(&self) -> &'static str { "" }
|
// pub fn to_string(&self) -> &'static str { "" }
|
||||||
}
|
// }
|
||||||
pub struct ProgramRun {}
|
// pub struct ProgramRun {}
|
||||||
impl ProgramRun {
|
// impl ProgramRun {
|
||||||
pub fn new(_: &str) -> Result<Self, Error> { Ok(Self {}) }
|
// pub fn new(_: &str) -> Result<Self, Error> { Ok(Self {}) }
|
||||||
|
|
||||||
pub fn set_epsilon(&mut self, _: f32) {}
|
// pub fn set_epsilon(&mut self, _: f32) {}
|
||||||
|
|
||||||
pub fn context(&mut self, _: &str) -> Context { Context {} }
|
// pub fn context(&mut self, _: &str) -> Context { Context {} }
|
||||||
|
|
||||||
//pub fn value(&mut self, _: &str, val: f32) -> f32 { val }
|
// //pub fn value(&mut self, _: &str, val: f32) -> f32 { val }
|
||||||
}
|
// }
|
||||||
pub struct Context {}
|
// pub struct Context {}
|
||||||
impl Context {
|
// impl Context {
|
||||||
#[must_use]
|
// #[must_use]
|
||||||
pub fn context(&mut self, _: &str) -> Context { Context {} }
|
// pub fn context(&mut self, _: &str) -> Context { Context {} }
|
||||||
|
|
||||||
pub fn value(&mut self, _: &str, val: f32) -> f32 { val }
|
// pub fn value(&mut self, _: &str, val: f32) -> f32 { val }
|
||||||
|
|
||||||
pub fn dummy() -> Self { Context {} }
|
// pub fn dummy() -> Self { Context {} }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
const TICK_PERIOD: f32 = 3.0 * DAYS_PER_MONTH; // 3 months
|
const TICK_PERIOD: f32 = 3.0 * DAYS_PER_MONTH; // 3 months
|
||||||
const HISTORY_DAYS: f32 = 500.0 * DAYS_PER_YEAR; // 500 years
|
const HISTORY_DAYS: f32 = 500.0 * DAYS_PER_YEAR; // 500 years
|
||||||
|
|
||||||
|
|
||||||
/// Statistics collector (min, max, avg)
|
/// Statistics collector (min, max, avg)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct EconStatistics {
|
struct EconStatistics {
|
||||||
pub count: u32,
|
count: u32,
|
||||||
pub sum: f32,
|
sum: f32,
|
||||||
pub min: f32,
|
min: f32,
|
||||||
pub max: f32,
|
max: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EconStatistics {
|
impl Default for EconStatistics {
|
||||||
@ -83,122 +79,145 @@ impl EconStatistics {
|
|||||||
fn valid(&self) -> bool { self.min.is_finite() }
|
fn valid(&self) -> bool { self.min.is_finite() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simulate_return(index: &mut Index, world: &mut WorldSim) -> Result<(), std::io::Error> {
|
pub struct Environment {
|
||||||
// ...
|
csv_file: Option<std::fs::File>,
|
||||||
if let Some(f) = f.as_mut() {
|
// context: vergleich::ProgramRun,
|
||||||
writeln!(f)?;
|
}
|
||||||
for site in index.sites.ids() {
|
|
||||||
let site = index.sites.get(site);
|
impl Environment {
|
||||||
csv_entry(f, site)?;
|
pub fn new() -> Result<Self, std::io::Error> {
|
||||||
}
|
// let mut context = vergleich::ProgramRun::new("economy_compare.sqlite")
|
||||||
|
// .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other,
|
||||||
|
// e.to_string()))?; context.set_epsilon(0.1);
|
||||||
|
let csv_file = Economy::csv_open();
|
||||||
|
Ok(Self {
|
||||||
|
csv_file, /* context */
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
fn iteration(&mut self, _: i32) {}
|
||||||
let mut castles = EconStatistics::default();
|
|
||||||
let mut towns = EconStatistics::default();
|
fn end(mut self, index: &Index) {
|
||||||
let mut dungeons = EconStatistics::default();
|
if let Some(f) = self.csv_file.as_mut() {
|
||||||
let giant_trees = EconStatistics::default();
|
use std::io::Write;
|
||||||
for site in index.sites.ids() {
|
let err = writeln!(f);
|
||||||
let site = &index.sites[site];
|
if err.is_ok() {
|
||||||
match site.kind {
|
for site in index.sites.ids() {
|
||||||
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
|
let site = index.sites.get(site);
|
||||||
towns += site.economy.pop
|
if Economy::csv_entry(f, site).is_err() {
|
||||||
},
|
break;
|
||||||
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
|
}
|
||||||
SiteKind::Castle(_) => castles += site.economy.pop,
|
}
|
||||||
SiteKind::Tree(_) => (),
|
}
|
||||||
SiteKind::GiantTree(_) => (),
|
self.csv_file.take();
|
||||||
SiteKind::Gnarling(_) => {},
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut castles = EconStatistics::default();
|
||||||
|
let mut towns = EconStatistics::default();
|
||||||
|
let mut dungeons = EconStatistics::default();
|
||||||
|
for site in index.sites.ids() {
|
||||||
|
let site = &index.sites[site];
|
||||||
|
match site.kind {
|
||||||
|
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
|
||||||
|
towns += site.economy.pop
|
||||||
|
},
|
||||||
|
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
|
||||||
|
SiteKind::Castle(_) => castles += site.economy.pop,
|
||||||
|
SiteKind::Tree(_) => (),
|
||||||
|
SiteKind::GiantTree(_) => (),
|
||||||
|
SiteKind::Gnarling(_) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if towns.valid() {
|
||||||
|
info!(
|
||||||
|
"Towns {:.0}-{:.0} avg {:.0} inhabitants",
|
||||||
|
towns.min,
|
||||||
|
towns.max,
|
||||||
|
towns.sum / (towns.count as f32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if castles.valid() {
|
||||||
|
info!(
|
||||||
|
"Castles {:.0}-{:.0} avg {:.0}",
|
||||||
|
castles.min,
|
||||||
|
castles.max,
|
||||||
|
castles.sum / (castles.count as f32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if dungeons.valid() {
|
||||||
|
info!(
|
||||||
|
"Dungeons {:.0}-{:.0} avg {:.0}",
|
||||||
|
dungeons.min,
|
||||||
|
dungeons.max,
|
||||||
|
dungeons.sum / (dungeons.count as f32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if towns.valid() {
|
|
||||||
info!(
|
|
||||||
"Towns {:.0}-{:.0} avg {:.0} inhabitants",
|
|
||||||
towns.min,
|
|
||||||
towns.max,
|
|
||||||
towns.sum / (towns.count as f32)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if castles.valid() {
|
|
||||||
info!(
|
|
||||||
"Castles {:.0}-{:.0} avg {:.0}",
|
|
||||||
castles.min,
|
|
||||||
castles.max,
|
|
||||||
castles.sum / (castles.count as f32)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if dungeons.valid() {
|
|
||||||
info!(
|
|
||||||
"Dungeons {:.0}-{:.0} avg {:.0}",
|
|
||||||
dungeons.min,
|
|
||||||
dungeons.max,
|
|
||||||
dungeons.sum / (dungeons.count as f32)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if giant_trees.valid() {
|
|
||||||
info!(
|
|
||||||
"Giant Trees {:.0}-{:.0} avg {:.0}",
|
|
||||||
giant_trees.min,
|
|
||||||
giant_trees.max,
|
|
||||||
giant_trees.sum / (giant_trees.count as f32)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
check_money(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(f) = f.as_mut() {
|
fn csv_tick(&mut self, index: &Index) {
|
||||||
if i % 5 == 0 {
|
if let Some(f) = self.csv_file.as_mut() {
|
||||||
if let Some(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(_)))
|
||||||
{
|
{
|
||||||
csv_entry(f, site)?;
|
Economy::csv_entry(f, site).unwrap_or_else(|_| {
|
||||||
|
self.csv_file.take();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simulate_return(index: &mut Index) -> Result<(), std::io::Error> {
|
||||||
|
let mut env = economy::Environment::new()?;
|
||||||
|
|
||||||
tracing::info!("economy simulation start");
|
tracing::info!("economy simulation start");
|
||||||
let mut vr = vergleich::ProgramRun::new("economy_compare.sqlite")
|
|
||||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
|
|
||||||
vr.set_epsilon(0.1);
|
|
||||||
for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 {
|
for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 {
|
||||||
if (index.time / DAYS_PER_YEAR) as i32 % 50 == 0 && (index.time % DAYS_PER_YEAR) as i32 == 0
|
if (index.time / DAYS_PER_YEAR) as i32 % 50 == 0 && (index.time % DAYS_PER_YEAR) as i32 == 0
|
||||||
{
|
{
|
||||||
debug!("Year {}", (index.time / DAYS_PER_YEAR) as i32);
|
debug!("Year {}", (index.time / DAYS_PER_YEAR) as i32);
|
||||||
}
|
}
|
||||||
|
env.iteration(i);
|
||||||
tick(index, world, TICK_PERIOD, vr.context(&i.to_string()));
|
tick(index, TICK_PERIOD, &mut env);
|
||||||
|
if i % 5 == 0 {
|
||||||
|
env.csv_tick(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tracing::info!("economy simulation end");
|
tracing::info!("economy simulation end");
|
||||||
|
env.end(index);
|
||||||
|
// csv_footer(f, index);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate(index: &mut Index, world: &mut WorldSim) {
|
pub fn simulate_economy(index: &mut Index) {
|
||||||
simulate_return(index, world)
|
simulate_return(index)
|
||||||
.unwrap_or_else(|err| info!("I/O error in simulate (economy.csv not writable?): {}", err));
|
.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: &Index) {
|
||||||
let mut sum_stock: f32 = 0.0;
|
// let mut sum_stock: f32 = 0.0;
|
||||||
for site in index.sites.values() {
|
// for site in index.sites.values() {
|
||||||
sum_stock += site.economy.stocks[*COIN_INDEX];
|
// sum_stock += site.economy.stocks[*COIN_INDEX];
|
||||||
}
|
// }
|
||||||
let mut sum_del: f32 = 0.0;
|
// let mut sum_del: f32 = 0.0;
|
||||||
for v in index.trade.deliveries.values() {
|
// for v in index.trade.deliveries.values() {
|
||||||
for del in v.iter() {
|
// for del in v.iter() {
|
||||||
sum_del += del.amount[*COIN_INDEX];
|
// sum_del += del.amount[*COIN_INDEX];
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
info!(
|
// info!(
|
||||||
"Coin amount {} + {} = {}",
|
// "Coin amount {} + {} = {}",
|
||||||
sum_stock,
|
// sum_stock,
|
||||||
sum_del,
|
// sum_del,
|
||||||
sum_stock + sum_del
|
// sum_stock + sum_del
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32, _vc: vergleich::Context) {
|
fn tick(index: &mut Index, dt: f32, _env: &mut Environment) {
|
||||||
if INTER_SITE_TRADE {
|
if INTER_SITE_TRADE {
|
||||||
// move deliverables to recipient cities
|
// move deliverables to recipient cities
|
||||||
for (id, deliv) in index.trade.deliveries.drain() {
|
for (id, deliv) in index.trade.deliveries.drain() {
|
||||||
@ -207,7 +226,7 @@ pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32, _vc: vergleich::C
|
|||||||
}
|
}
|
||||||
index.sites.par_iter_mut().for_each(|(site_id, site)| {
|
index.sites.par_iter_mut().for_each(|(site_id, site)| {
|
||||||
if site.do_economic_simulation() {
|
if site.do_economic_simulation() {
|
||||||
site.economy.tick(site_id, dt, vergleich::Context::dummy());
|
site.economy.tick(site_id, dt);
|
||||||
// helpful for debugging but not compatible with parallel execution
|
// helpful for debugging but not compatible with parallel execution
|
||||||
// vc.context(&site_id.id().to_string()));
|
// vc.context(&site_id.id().to_string()));
|
||||||
}
|
}
|
||||||
@ -241,8 +260,9 @@ pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32, _vc: vergleich::C
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{sim, site::economy::GoodMap, util::seed_expan};
|
use crate::{sim, util::seed_expan};
|
||||||
use common::{store::Id, terrain::BiomeKind, trade::Good};
|
use common::{store::Id, terrain::BiomeKind, trade::Good};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use rand_chacha::ChaChaRng;
|
use rand_chacha::ChaChaRng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -274,14 +294,20 @@ mod tests {
|
|||||||
name: String,
|
name: String,
|
||||||
position: (i32, i32),
|
position: (i32, i32),
|
||||||
kind: common::terrain::site::SitesKind,
|
kind: common::terrain::site::SitesKind,
|
||||||
neighbors: Vec<(u64, usize)>, // id, travel_distance
|
neighbors: Vec<u64>, // id
|
||||||
resources: Vec<ResourcesSetup>,
|
resources: Vec<ResourcesSetup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_economy(sites: &common::store::Store<crate::site::Site>) {
|
fn show_economy(
|
||||||
use crate::site::economy::good_list;
|
sites: &common::store::Store<crate::site::Site>,
|
||||||
|
names: &Option<HashMap<Id<crate::site::Site>, String>>,
|
||||||
|
) {
|
||||||
for (id, site) in sites.iter() {
|
for (id, site) in sites.iter() {
|
||||||
println!("Site id {:?} name {}", id, site.name());
|
let name = names.as_ref().map_or(site.name().into(), |map| {
|
||||||
|
map.get(&id).cloned().unwrap_or(site.name().into())
|
||||||
|
});
|
||||||
|
println!("Site id {:?} name {}", id.id(), name);
|
||||||
|
site.economy.print_details();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +334,7 @@ mod tests {
|
|||||||
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index);
|
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index);
|
||||||
info!("Civs created");
|
info!("Civs created");
|
||||||
crate::sim2::simulate(&mut index, &mut sim);
|
crate::sim2::simulate(&mut index, &mut sim);
|
||||||
show_economy(&index.sites);
|
show_economy(&index.sites, &None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,6 +357,7 @@ mod tests {
|
|||||||
info!("Index created");
|
info!("Index created");
|
||||||
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool);
|
let mut sim = sim::WorldSim::generate(seed, opts, &threadpool);
|
||||||
info!("World loaded");
|
info!("World loaded");
|
||||||
|
let mut names = None;
|
||||||
let regenerate_input = false;
|
let regenerate_input = false;
|
||||||
if regenerate_input {
|
if regenerate_input {
|
||||||
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index);
|
let _civs = crate::civ::Civs::generate(seed, &mut sim, &mut index);
|
||||||
@ -347,12 +374,7 @@ mod tests {
|
|||||||
amount: *a * i.economy.natural_resources.average_yield_per_chunk[good],
|
amount: *a * i.economy.natural_resources.average_yield_per_chunk[good],
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let neighbors = i
|
let neighbors = i.economy.neighbors.iter().map(|j| j.id.id()).collect();
|
||||||
.economy
|
|
||||||
.neighbors
|
|
||||||
.iter()
|
|
||||||
.map(|j| (j.id.id(), j.travel_distance))
|
|
||||||
.collect();
|
|
||||||
let val = EconomySetup {
|
let val = EconomySetup {
|
||||||
name: i.name().into(),
|
name: i.name().into(),
|
||||||
position: (i.get_origin().x, i.get_origin().y),
|
position: (i.get_origin().x, i.get_origin().y),
|
||||||
@ -385,6 +407,7 @@ mod tests {
|
|||||||
.expect("economy_testinput2.ron not found");
|
.expect("economy_testinput2.ron not found");
|
||||||
let econ_testinput: Vec<EconomySetup> =
|
let econ_testinput: Vec<EconomySetup> =
|
||||||
ron::de::from_reader(ron_file).expect("economy_testinput2.ron parse error");
|
ron::de::from_reader(ron_file).expect("economy_testinput2.ron parse error");
|
||||||
|
names = Some(HashMap::new());
|
||||||
for i in econ_testinput.iter() {
|
for i in econ_testinput.iter() {
|
||||||
let wpos = Vec2 {
|
let wpos = Vec2 {
|
||||||
x: i.position.0,
|
x: i.position.0,
|
||||||
@ -418,43 +441,32 @@ mod tests {
|
|||||||
settlement.economy.natural_resources.average_yield_per_chunk
|
settlement.economy.natural_resources.average_yield_per_chunk
|
||||||
[g.good.try_into().unwrap_or_default()] = 1.0;
|
[g.good.try_into().unwrap_or_default()] = 1.0;
|
||||||
}
|
}
|
||||||
index.sites.insert(settlement);
|
let id = index.sites.insert(settlement);
|
||||||
|
names.as_mut().map(|map| map.insert(id, i.name.clone()));
|
||||||
}
|
}
|
||||||
// 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 (id, econ) in econ_testinput.iter().enumerate() {
|
||||||
if let Some(id) = index.sites.recreate_id(i as u64) {
|
if let Some(id) = index.sites.recreate_id(id as u64) {
|
||||||
let mut neighbors: Vec<crate::site::economy::NeighborInformation> = e
|
for nid in econ.neighbors.iter() {
|
||||||
.neighbors
|
if let Some(nid) = index.sites.recreate_id(*nid) {
|
||||||
.iter()
|
let town = &mut index.sites.get_mut(id).economy;
|
||||||
.flat_map(|(nid, dist)| {
|
town.add_neighbor(nid, 0);
|
||||||
index.sites.recreate_id(*nid).map(|i| (i, dist))
|
}
|
||||||
})
|
}
|
||||||
.map(|(nid, dist)| crate::site::economy::NeighborInformation {
|
|
||||||
id: nid,
|
|
||||||
travel_distance: *dist,
|
|
||||||
last_values: GoodMap::from_default(0.0),
|
|
||||||
last_supplies: GoodMap::from_default(0.0),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
index
|
|
||||||
.sites
|
|
||||||
.get_mut(id)
|
|
||||||
.economy
|
|
||||||
.neighbors
|
|
||||||
.append(&mut neighbors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::sim2::simulate(&mut index, &mut sim);
|
crate::sim2::simulate(&mut index, &mut sim);
|
||||||
show_economy(&index.sites);
|
show_economy(&index.sites, &names);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Simenv {
|
struct Simenv {
|
||||||
index: crate::index::Index,
|
index: crate::index::Index,
|
||||||
rng: ChaChaRng,
|
rng: ChaChaRng,
|
||||||
targets: hashbrown::HashMap<Id<crate::site::Site>, f32>,
|
targets: HashMap<Id<crate::site::Site>, f32>,
|
||||||
|
names: HashMap<Id<crate::site::Site>, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -462,9 +474,7 @@ mod tests {
|
|||||||
fn test_economy_moderate_standalone() {
|
fn test_economy_moderate_standalone() {
|
||||||
fn add_settlement(
|
fn add_settlement(
|
||||||
env: &mut Simenv,
|
env: &mut Simenv,
|
||||||
// index: &mut crate::index::Index,
|
name: &str,
|
||||||
// rng: &mut ChaCha20Rng,
|
|
||||||
_name: &str,
|
|
||||||
target: f32,
|
target: f32,
|
||||||
resources: &[(Good, f32)],
|
resources: &[(Good, f32)],
|
||||||
) -> Id<crate::site::Site> {
|
) -> Id<crate::site::Site> {
|
||||||
@ -473,7 +483,6 @@ mod tests {
|
|||||||
wpos,
|
wpos,
|
||||||
None,
|
None,
|
||||||
&mut env.rng,
|
&mut env.rng,
|
||||||
//Some(name),
|
|
||||||
));
|
));
|
||||||
for (good, amount) in resources.iter() {
|
for (good, amount) in resources.iter() {
|
||||||
settlement.economy.natural_resources.chunks_per_resource
|
settlement.economy.natural_resources.chunks_per_resource
|
||||||
@ -483,6 +492,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let id = env.index.sites.insert(settlement);
|
let id = env.index.sites.insert(settlement);
|
||||||
env.targets.insert(id, target);
|
env.targets.insert(id, target);
|
||||||
|
env.names.insert(id, name.into());
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,13 +513,14 @@ mod tests {
|
|||||||
let mut env = Simenv {
|
let mut env = Simenv {
|
||||||
index,
|
index,
|
||||||
rng,
|
rng,
|
||||||
targets: hashbrown::HashMap::new(),
|
targets: HashMap::new(),
|
||||||
|
names: HashMap::new(),
|
||||||
};
|
};
|
||||||
add_settlement(&mut env, "Forest", 5000.0, &[(
|
add_settlement(&mut env, "Forest", 5000.0, &[(
|
||||||
Good::Terrain(BiomeKind::Forest),
|
Good::Terrain(BiomeKind::Forest),
|
||||||
100.0_f32,
|
100.0_f32,
|
||||||
)]);
|
)]);
|
||||||
add_settlement(&mut env, "Grass", 900.0, &[(
|
add_settlement(&mut env, "Grass", 880.0, &[(
|
||||||
Good::Terrain(BiomeKind::Grassland),
|
Good::Terrain(BiomeKind::Grassland),
|
||||||
100.0_f32,
|
100.0_f32,
|
||||||
)]);
|
)]);
|
||||||
@ -550,7 +561,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
crate::sim2::simulate(&mut env.index, &mut sim);
|
crate::sim2::simulate(&mut env.index, &mut sim);
|
||||||
show_economy(&env.index.sites);
|
show_economy(&env.index.sites, &Some(env.names));
|
||||||
// check population (shrinks if economy gets broken)
|
// check population (shrinks if economy gets broken)
|
||||||
for (id, site) in env.index.sites.iter() {
|
for (id, site) in env.index.sites.iter() {
|
||||||
assert!(site.economy.pop >= env.targets[&id]);
|
assert!(site.economy.pop >= env.targets[&id]);
|
||||||
|
@ -194,7 +194,7 @@ pub struct LaborMap<V> {
|
|||||||
data: Vec<V>,
|
data: Vec<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Default + Copy> Default for LaborMap<V> {
|
impl<V: Default + Clone> Default for LaborMap<V> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
LaborMap {
|
LaborMap {
|
||||||
data: std::iter::repeat(V::default()).take(*LABOR_COUNT).collect(),
|
data: std::iter::repeat(V::default()).take(*LABOR_COUNT).collect(),
|
||||||
@ -233,6 +233,13 @@ impl<V> LaborMap<V> {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, v)| (LaborIndex::from_usize(idx), v))
|
.map(|(idx, v)| (LaborIndex::from_usize(idx), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = (LaborIndex, &mut V)> + '_ {
|
||||||
|
(&mut self.data)
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, v)| (LaborIndex::from_usize(idx), v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Copy + Default> LaborMap<V> {
|
impl<V: Copy + Default> LaborMap<V> {
|
||||||
@ -347,6 +354,12 @@ impl Labor {
|
|||||||
|
|
||||||
pub fn is_everyone(&self) -> bool { self.0 == DUMMY_LABOR.0 }
|
pub fn is_everyone(&self) -> bool { self.0 == DUMMY_LABOR.0 }
|
||||||
|
|
||||||
|
pub fn orders_everyone() -> Vec<(GoodIndex, f32)> {
|
||||||
|
LABOR
|
||||||
|
.get(DUMMY_LABOR.0 as usize)
|
||||||
|
.map_or(Vec::new(), |l| l.orders.clone())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn orders(&self) -> Vec<(GoodIndex, f32)> {
|
pub fn orders(&self) -> Vec<(GoodIndex, f32)> {
|
||||||
LABOR
|
LABOR
|
||||||
.get(self.0 as usize)
|
.get(self.0 as usize)
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
/// This file contains a single economy
|
||||||
|
/// and functions to simulate it
|
||||||
|
use crate::world_msg::EconomyInfo;
|
||||||
use crate::{
|
use crate::{
|
||||||
sim::SimChunk,
|
sim::SimChunk,
|
||||||
site::Site,
|
site::Site,
|
||||||
@ -8,51 +11,56 @@ use common::{
|
|||||||
terrain::BiomeKind,
|
terrain::BiomeKind,
|
||||||
trade::{Good, SitePrices},
|
trade::{Good, SitePrices},
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::{cmp::Ordering::Less, convert::TryFrom};
|
use std::{cmp::Ordering::Less, convert::TryFrom};
|
||||||
use tracing::{debug, info, trace, warn};
|
use tracing::{debug, info, trace, warn};
|
||||||
|
|
||||||
use Good::*;
|
use Good::*;
|
||||||
mod map_types;
|
mod map_types;
|
||||||
pub use map_types::{GoodIndex, GoodMap, Labor, LaborIndex, LaborMap, NaturalResources};
|
pub use map_types::Labor;
|
||||||
|
use map_types::{GoodIndex, GoodMap, LaborIndex, LaborMap, NaturalResources};
|
||||||
|
mod context;
|
||||||
|
pub use context::simulate_economy;
|
||||||
|
use context::Environment;
|
||||||
|
|
||||||
pub const INTER_SITE_TRADE: bool = true;
|
const INTER_SITE_TRADE: bool = true;
|
||||||
pub const DAYS_PER_MONTH: f32 = 30.0;
|
const DAYS_PER_MONTH: f32 = 30.0;
|
||||||
pub const DAYS_PER_YEAR: f32 = 12.0 * DAYS_PER_MONTH;
|
const DAYS_PER_YEAR: f32 = 12.0 * DAYS_PER_MONTH;
|
||||||
const GENERATE_CSV: bool = false;
|
const GENERATE_CSV: bool = false;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TradeOrder {
|
pub struct TradeOrder {
|
||||||
pub customer: Id<Site>,
|
customer: Id<Site>,
|
||||||
pub amount: GoodMap<f32>, // positive for orders, negative for exchange
|
amount: GoodMap<f32>, // positive for orders, negative for exchange
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TradeDelivery {
|
pub struct TradeDelivery {
|
||||||
pub supplier: Id<Site>,
|
supplier: Id<Site>,
|
||||||
pub amount: GoodMap<f32>, // positive for orders, negative for exchange
|
amount: GoodMap<f32>, // positive for orders, negative for exchange
|
||||||
pub prices: GoodMap<f32>, // at the time of interaction
|
prices: GoodMap<f32>, // at the time of interaction
|
||||||
pub supply: GoodMap<f32>, // maximum amount available, at the time of interaction
|
supply: GoodMap<f32>, // maximum amount available, at the time of interaction
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct TradeInformation {
|
pub struct TradeInformation {
|
||||||
pub orders: DHashMap<Id<Site>, Vec<TradeOrder>>, // per provider
|
orders: DHashMap<Id<Site>, Vec<TradeOrder>>, // per provider
|
||||||
pub deliveries: DHashMap<Id<Site>, Vec<TradeDelivery>>, // per receiver
|
deliveries: DHashMap<Id<Site>, Vec<TradeDelivery>>, // per receiver
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NeighborInformation {
|
pub struct NeighborInformation {
|
||||||
pub id: Id<Site>,
|
id: Id<Site>,
|
||||||
pub travel_distance: usize,
|
//travel_distance: usize,
|
||||||
|
|
||||||
// remembered from last interaction
|
// remembered from last interaction
|
||||||
pub last_values: GoodMap<f32>,
|
last_values: GoodMap<f32>,
|
||||||
pub last_supplies: GoodMap<f32>,
|
last_supplies: GoodMap<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref COIN_INDEX: GoodIndex = Coin.try_into().unwrap_or_default();
|
static ref COIN_INDEX: GoodIndex = Coin.try_into().unwrap_or_default();
|
||||||
static ref FOOD_INDEX: GoodIndex = Good::Food.try_into().unwrap_or_default();
|
static ref FOOD_INDEX: GoodIndex = Good::Food.try_into().unwrap_or_default();
|
||||||
static ref TRANSPORTATION_INDEX: GoodIndex = Transportation.try_into().unwrap_or_default();
|
static ref TRANSPORTATION_INDEX: GoodIndex = Transportation.try_into().unwrap_or_default();
|
||||||
}
|
}
|
||||||
@ -60,47 +68,47 @@ lazy_static! {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Economy {
|
pub struct Economy {
|
||||||
/// Population
|
/// Population
|
||||||
pub pop: f32,
|
pop: f32,
|
||||||
pub population_limited_by: GoodIndex,
|
population_limited_by: GoodIndex,
|
||||||
|
|
||||||
/// Total available amount of each good
|
/// Total available amount of each good
|
||||||
pub stocks: GoodMap<f32>,
|
stocks: GoodMap<f32>,
|
||||||
/// Surplus stock compared to demand orders
|
/// Surplus stock compared to demand orders
|
||||||
pub surplus: GoodMap<f32>,
|
surplus: GoodMap<f32>,
|
||||||
/// change rate (derivative) of stock in the current situation
|
/// change rate (derivative) of stock in the current situation
|
||||||
pub marginal_surplus: GoodMap<f32>,
|
marginal_surplus: GoodMap<f32>,
|
||||||
/// amount of wares not needed by the economy (helps with trade planning)
|
/// amount of wares not needed by the economy (helps with trade planning)
|
||||||
pub unconsumed_stock: GoodMap<f32>,
|
unconsumed_stock: GoodMap<f32>,
|
||||||
/// Local availability of a good, 4.0 = starved, 2.0 = balanced, 0.1 =
|
/// Local availability of a good, 4.0 = starved, 2.0 = balanced, 0.1 =
|
||||||
/// extra, NULL = way too much
|
/// extra, NULL = way too much
|
||||||
// For some goods, such a goods without any supply, it doesn't make sense to talk about value
|
// For some goods, such a goods without any supply, it doesn't make sense to talk about value
|
||||||
pub values: GoodMap<Option<f32>>,
|
values: GoodMap<Option<f32>>,
|
||||||
/// amount of goods exported/imported during the last cycle
|
/// amount of goods exported/imported during the last cycle
|
||||||
pub last_exports: GoodMap<f32>,
|
last_exports: GoodMap<f32>,
|
||||||
pub active_exports: GoodMap<f32>, // unfinished trade (amount unconfirmed)
|
active_exports: GoodMap<f32>, // unfinished trade (amount unconfirmed)
|
||||||
//pub export_targets: GoodMap<f32>,
|
//pub export_targets: GoodMap<f32>,
|
||||||
/// amount of labor that went into a good, [1 man cycle=1.0]
|
/// amount of labor that went into a good, [1 man cycle=1.0]
|
||||||
pub labor_values: GoodMap<Option<f32>>,
|
labor_values: GoodMap<Option<f32>>,
|
||||||
// this assumes a single source, replace with LaborMap?
|
// this assumes a single source, replace with LaborMap?
|
||||||
pub material_costs: GoodMap<f32>,
|
material_costs: GoodMap<f32>,
|
||||||
|
|
||||||
/// Proportion of individuals dedicated to an industry (sums to roughly 1.0)
|
/// Proportion of individuals dedicated to an industry (sums to roughly 1.0)
|
||||||
pub labors: LaborMap<f32>,
|
labors: LaborMap<f32>,
|
||||||
// Per worker, per year, of their output good
|
// Per worker, per year, of their output good
|
||||||
pub yields: LaborMap<f32>,
|
yields: LaborMap<f32>,
|
||||||
/// [0.0..1.0]
|
/// [0.0..1.0]
|
||||||
pub productivity: LaborMap<f32>,
|
productivity: LaborMap<f32>,
|
||||||
/// Missing raw material which limits production
|
/// Missing raw material which limits production
|
||||||
pub limited_by: LaborMap<GoodIndex>,
|
limited_by: LaborMap<GoodIndex>,
|
||||||
|
|
||||||
pub natural_resources: NaturalResources,
|
natural_resources: NaturalResources,
|
||||||
/// Neighboring sites to trade with
|
/// Neighboring sites to trade with
|
||||||
pub neighbors: Vec<NeighborInformation>,
|
neighbors: Vec<NeighborInformation>,
|
||||||
|
|
||||||
/// outgoing trade, per provider
|
/// outgoing trade, per provider
|
||||||
pub orders: DHashMap<Id<Site>, Vec<TradeOrder>>,
|
orders: DHashMap<Id<Site>, Vec<TradeOrder>>,
|
||||||
/// incoming trade - only towards this site
|
/// incoming trade - only towards this site
|
||||||
pub deliveries: Vec<TradeDelivery>,
|
deliveries: Vec<TradeDelivery>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Economy {
|
impl Default for Economy {
|
||||||
@ -136,10 +144,58 @@ impl Default for Economy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Economy {
|
impl Economy {
|
||||||
pub const MINIMUM_PRICE: f32 = 0.1;
|
const MINIMUM_PRICE: f32 = 0.1;
|
||||||
pub const STARTING_COIN: f32 = 1000.0;
|
const STARTING_COIN: f32 = 1000.0;
|
||||||
const _NATURAL_RESOURCE_SCALE: f32 = 1.0 / 9.0;
|
const _NATURAL_RESOURCE_SCALE: f32 = 1.0 / 9.0;
|
||||||
|
|
||||||
|
pub fn population(&self) -> f32 { self.pop }
|
||||||
|
|
||||||
|
pub fn get_available_stock(&self) -> HashMap<Good, f32> {
|
||||||
|
self.unconsumed_stock
|
||||||
|
.iter()
|
||||||
|
.map(|(g, a)| (g.into(), *a))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_information(&self, id: Id<Site>) -> EconomyInfo {
|
||||||
|
EconomyInfo {
|
||||||
|
id: id.id(),
|
||||||
|
population: self.pop.floor() as u32,
|
||||||
|
stock: self
|
||||||
|
.stocks
|
||||||
|
.iter()
|
||||||
|
.map(|(g, a)| (Good::from(g), *a))
|
||||||
|
.collect(),
|
||||||
|
labor_values: self
|
||||||
|
.labor_values
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(g, a)| a.map(|a| (Good::from(g), a)))
|
||||||
|
.collect(),
|
||||||
|
values: self
|
||||||
|
.values
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(g, a)| a.map(|a| (Good::from(g), a)))
|
||||||
|
.collect(),
|
||||||
|
labors: self.labors.iter().map(|(_, a)| (*a)).collect(),
|
||||||
|
last_exports: self
|
||||||
|
.last_exports
|
||||||
|
.iter()
|
||||||
|
.map(|(g, a)| (Good::from(g), *a))
|
||||||
|
.collect(),
|
||||||
|
resources: self
|
||||||
|
.natural_resources
|
||||||
|
.chunks_per_resource
|
||||||
|
.iter()
|
||||||
|
.map(|(g, a)| {
|
||||||
|
(
|
||||||
|
Good::from(g),
|
||||||
|
((*a) as f32) * self.natural_resources.average_yield_per_chunk[g],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cache_economy(&mut self) {
|
pub fn cache_economy(&mut self) {
|
||||||
for g in good_list() {
|
for g in good_list() {
|
||||||
let amount: f32 = self
|
let amount: f32 = self
|
||||||
@ -161,13 +217,22 @@ impl Economy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_orders(&self) -> DHashMap<Option<LaborIndex>, Vec<(GoodIndex, f32)>> {
|
/// orders per profession (excluding everyone)
|
||||||
Labor::list_full()
|
fn get_orders(&self) -> &'static LaborMap<Vec<(GoodIndex, f32)>> {
|
||||||
.map(|l| (if l.is_everyone() { None } else { Some(l) }, l.orders()))
|
lazy_static! {
|
||||||
.collect()
|
static ref ORDERS: LaborMap<Vec<(GoodIndex, f32)>> = {
|
||||||
|
let mut res = LaborMap::default();
|
||||||
|
res.iter_mut().for_each(|(i, e)| *e = i.orders());
|
||||||
|
res
|
||||||
|
};
|
||||||
|
}
|
||||||
|
&ORDERS
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_productivity(&self) -> LaborMap<(GoodIndex, f32)> {
|
/// resources consumed by everyone (no matter which profession)
|
||||||
|
fn get_orders_everyone(&self) -> Vec<(GoodIndex, f32)> { Labor::orders_everyone() }
|
||||||
|
|
||||||
|
fn get_production(&self) -> LaborMap<(GoodIndex, f32)> {
|
||||||
// cache the site independent part of production
|
// cache the site independent part of production
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref PRODUCTS: LaborMap<(GoodIndex, f32)> = LaborMap::from_iter(
|
static ref PRODUCTS: LaborMap<(GoodIndex, f32)> = LaborMap::from_iter(
|
||||||
@ -187,7 +252,7 @@ impl Economy {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replenish(&mut self, _time: f32) {
|
fn replenish(&mut self, _time: f32) {
|
||||||
for (good, &ch) in self.natural_resources.chunks_per_resource.iter() {
|
for (good, &ch) in self.natural_resources.chunks_per_resource.iter() {
|
||||||
let per_year = self.natural_resources.average_yield_per_chunk[good] * ch;
|
let per_year = self.natural_resources.average_yield_per_chunk[good] * ch;
|
||||||
self.stocks[good] = self.stocks[good].max(per_year);
|
self.stocks[good] = self.stocks[good].max(per_year);
|
||||||
@ -229,11 +294,10 @@ impl Economy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_neighbor(&mut self, id: Id<Site>, distance: usize) {
|
pub fn add_neighbor(&mut self, id: Id<Site>, _distance: usize) {
|
||||||
self.neighbors.push(NeighborInformation {
|
self.neighbors.push(NeighborInformation {
|
||||||
id,
|
id,
|
||||||
travel_distance: distance,
|
//travel_distance: distance,
|
||||||
|
|
||||||
last_values: GoodMap::from_default(Economy::MINIMUM_PRICE),
|
last_values: GoodMap::from_default(Economy::MINIMUM_PRICE),
|
||||||
last_supplies: Default::default(),
|
last_supplies: Default::default(),
|
||||||
});
|
});
|
||||||
@ -511,17 +575,17 @@ impl Economy {
|
|||||||
|
|
||||||
let internal_orders = self.get_orders();
|
let internal_orders = self.get_orders();
|
||||||
let mut next_demand = GoodMap::from_default(0.0);
|
let mut next_demand = GoodMap::from_default(0.0);
|
||||||
for (labor, orders) in &internal_orders {
|
for (labor, orders) in internal_orders.iter() {
|
||||||
let workers = if let Some(labor) = labor {
|
let workers = self.labors[labor] * self.pop;
|
||||||
self.labors[*labor]
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
} * self.pop;
|
|
||||||
for (good, amount) in orders {
|
for (good, amount) in orders {
|
||||||
next_demand[*good] += *amount * workers;
|
next_demand[*good] += *amount * workers;
|
||||||
assert!(next_demand[*good] >= 0.0);
|
assert!(next_demand[*good] >= 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (good, amount) in self.get_orders_everyone().iter() {
|
||||||
|
next_demand[*good] += *amount * self.pop;
|
||||||
|
assert!(next_demand[*good] >= 0.0);
|
||||||
|
}
|
||||||
//info!("Trade {} {}", site.id(), orders.len());
|
//info!("Trade {} {}", site.id(), orders.len());
|
||||||
let mut total_orders: GoodMap<f32> = GoodMap::from_default(0.0);
|
let mut total_orders: GoodMap<f32> = GoodMap::from_default(0.0);
|
||||||
for i in orders.iter() {
|
for i in orders.iter() {
|
||||||
@ -717,72 +781,64 @@ impl Economy {
|
|||||||
/// product becomes available through a mechanism such as trade, an
|
/// product becomes available through a mechanism such as trade, an
|
||||||
/// entire arm of the economy may materialise to take advantage of this.
|
/// entire arm of the economy may materialise to take advantage of this.
|
||||||
|
|
||||||
pub fn tick(
|
pub fn tick(&mut self, site_id: Id<Site>, dt: f32) {
|
||||||
&mut self,
|
|
||||||
//deliveries: Option<&mut Vec<TradeDelivery>>,
|
|
||||||
site_id: Id<Site>,
|
|
||||||
dt: f32,
|
|
||||||
mut vc: vergleich::Context,
|
|
||||||
) {
|
|
||||||
// collect goods from trading
|
// collect goods from trading
|
||||||
if INTER_SITE_TRADE {
|
if INTER_SITE_TRADE {
|
||||||
// if let Some(deliveries) = deliveries {
|
|
||||||
self.collect_deliveries();
|
self.collect_deliveries();
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let orders = self.get_orders();
|
let orders = self.get_orders();
|
||||||
let productivity = self.get_productivity();
|
let production = self.get_production();
|
||||||
|
|
||||||
for i in productivity.iter() {
|
// for i in production.iter() {
|
||||||
vc.context("productivity")
|
// vc.context("production")
|
||||||
.value(&std::format!("{:?}{:?}", i.0, Good::from(i.1.0)), i.1.1);
|
// .value(&std::format!("{:?}{:?}", i.0, Good::from(i.1.0)), i.1.1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut demand = GoodMap::from_default(0.0);
|
let mut demand = GoodMap::from_default(0.0);
|
||||||
for (labor, orders) in &orders {
|
for (labor, orders) in orders.iter() {
|
||||||
let workers = if let Some(labor) = labor {
|
let workers = self.labors[labor] * self.pop;
|
||||||
self.labors[*labor]
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
} * self.pop;
|
|
||||||
for (good, amount) in orders {
|
for (good, amount) in orders {
|
||||||
demand[*good] += *amount * workers;
|
demand[*good] += *amount * workers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (good, amount) in self.get_orders_everyone().iter() {
|
||||||
|
demand[*good] += *amount * self.pop;
|
||||||
|
}
|
||||||
if INTER_SITE_TRADE {
|
if INTER_SITE_TRADE {
|
||||||
demand[*COIN_INDEX] += Economy::STARTING_COIN; // if we spend coin value increases
|
demand[*COIN_INDEX] += Economy::STARTING_COIN; // if we spend coin value increases
|
||||||
}
|
}
|
||||||
|
|
||||||
// which labor is the merchant
|
// which labor is the merchant
|
||||||
let merchant_labor = productivity
|
let merchant_labor = production
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, v)| v.0 == *TRANSPORTATION_INDEX)
|
.find(|(_, v)| v.0 == *TRANSPORTATION_INDEX)
|
||||||
.map(|(l, _)| l);
|
.map(|(l, _)| l)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut supply = self.stocks; //GoodMap::from_default(0.0);
|
let mut supply = self.stocks; //GoodMap::from_default(0.0);
|
||||||
for (labor, goodvec) in productivity.iter() {
|
for (labor, goodvec) in production.iter() {
|
||||||
//for (output_good, _) in goodvec.iter() {
|
//for (output_good, _) in goodvec.iter() {
|
||||||
//info!("{} supply{:?}+={}", site_id.id(), Good::from(goodvec.0),
|
//info!("{} supply{:?}+={}", site_id.id(), Good::from(goodvec.0),
|
||||||
// self.yields[labor] * self.labors[labor] * self.pop);
|
// self.yields[labor] * self.labors[labor] * self.pop);
|
||||||
supply[goodvec.0] += self.yields[labor] * self.labors[labor] * self.pop;
|
supply[goodvec.0] += self.yields[labor] * self.labors[labor] * self.pop;
|
||||||
vc.context(&std::format!("{:?}-{:?}", Good::from(goodvec.0), labor))
|
// vc.context(&std::format!("{:?}-{:?}", Good::from(goodvec.0),
|
||||||
.value("yields", self.yields[labor]);
|
// labor)) .value("yields", self.yields[labor]);
|
||||||
vc.context(&std::format!("{:?}-{:?}", Good::from(goodvec.0), labor))
|
// vc.context(&std::format!("{:?}-{:?}", Good::from(goodvec.0),
|
||||||
.value("labors", self.labors[labor]);
|
// labor)) .value("labors", self.labors[labor]);
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in supply.iter() {
|
// for i in supply.iter() {
|
||||||
vc.context("supply")
|
// vc.context("supply")
|
||||||
.value(&std::format!("{:?}", Good::from(i.0)), *i.1);
|
// .value(&std::format!("{:?}", Good::from(i.0)), *i.1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
let stocks = &self.stocks;
|
let stocks = &self.stocks;
|
||||||
for i in stocks.iter() {
|
// for i in stocks.iter() {
|
||||||
vc.context("stocks")
|
// vc.context("stocks")
|
||||||
.value(&std::format!("{:?}", Good::from(i.0)), *i.1);
|
// .value(&std::format!("{:?}", Good::from(i.0)), *i.1);
|
||||||
}
|
// }
|
||||||
self.surplus = demand.map(|g, demand| supply[g] + stocks[g] - demand);
|
self.surplus = demand.map(|g, demand| supply[g] + stocks[g] - demand);
|
||||||
self.marginal_surplus = demand.map(|g, demand| supply[g] - demand);
|
self.marginal_surplus = demand.map(|g, demand| supply[g] - demand);
|
||||||
|
|
||||||
@ -793,18 +849,14 @@ impl Economy {
|
|||||||
// this is in line with the other professions)
|
// this is in line with the other professions)
|
||||||
let transportation_capacity = self.stocks[*TRANSPORTATION_INDEX];
|
let transportation_capacity = self.stocks[*TRANSPORTATION_INDEX];
|
||||||
let trade = if INTER_SITE_TRADE {
|
let trade = if INTER_SITE_TRADE {
|
||||||
let trade = self.plan_trade_for_site(
|
let trade =
|
||||||
&site_id,
|
self.plan_trade_for_site(&site_id, transportation_capacity, &mut potential_trade);
|
||||||
transportation_capacity,
|
|
||||||
// external_orders,
|
|
||||||
&mut potential_trade,
|
|
||||||
);
|
|
||||||
self.active_exports = GoodMap::from_iter(trade.iter().map(|(g, a)| (g, -*a)), 0.0); // TODO: check for availability?
|
self.active_exports = GoodMap::from_iter(trade.iter().map(|(g, a)| (g, -*a)), 0.0); // TODO: check for availability?
|
||||||
|
|
||||||
// add the wares to sell to demand and the goods to buy to supply
|
// add the wares to sell to demand and the goods to buy to supply
|
||||||
for (g, a) in trade.iter() {
|
for (g, a) in trade.iter() {
|
||||||
vc.context("trade")
|
// vc.context("trade")
|
||||||
.value(&std::format!("{:?}", Good::from(g)), *a);
|
// .value(&std::format!("{:?}", Good::from(g)), *a);
|
||||||
if *a > 0.0 {
|
if *a > 0.0 {
|
||||||
supply[g] += *a;
|
supply[g] += *a;
|
||||||
assert!(supply[g] >= 0.0);
|
assert!(supply[g] >= 0.0);
|
||||||
@ -832,16 +884,17 @@ impl Economy {
|
|||||||
*surplus
|
*surplus
|
||||||
};
|
};
|
||||||
// Value rationalisation
|
// Value rationalisation
|
||||||
let goodname = std::format!("{:?}", Good::from(good));
|
// let goodname = std::format!("{:?}", Good::from(good));
|
||||||
vc.context("old_surplus").value(&goodname, old_surplus);
|
// vc.context("old_surplus").value(&goodname, old_surplus);
|
||||||
vc.context("demand").value(&goodname, demand[good]);
|
// vc.context("demand").value(&goodname, demand[good]);
|
||||||
let val = 2.0f32.powf(1.0 - old_surplus / demand[good]);
|
let val = 2.0f32.powf(1.0 - old_surplus / demand[good]);
|
||||||
let smooth = 0.8;
|
let smooth = 0.8;
|
||||||
values[good] = if val > 0.001 && val < 1000.0 {
|
values[good] = if val > 0.001 && val < 1000.0 {
|
||||||
Some(vc.context("values").value(
|
Some(
|
||||||
&goodname,
|
// vc.context("values").value(
|
||||||
|
// &goodname,
|
||||||
smooth * values[good].unwrap_or(val) + (1.0 - smooth) * val,
|
smooth * values[good].unwrap_or(val) + (1.0 - smooth) * val,
|
||||||
))
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -858,10 +911,10 @@ impl Economy {
|
|||||||
// summing favors merchants too much (as they will provide multiple
|
// summing favors merchants too much (as they will provide multiple
|
||||||
// goods, so we use max instead)
|
// goods, so we use max instead)
|
||||||
let labor_ratios: LaborMap<f32> = LaborMap::from_iter(
|
let labor_ratios: LaborMap<f32> = LaborMap::from_iter(
|
||||||
productivity.iter().map(|(labor, goodvec)| {
|
production.iter().map(|(labor, goodvec)| {
|
||||||
(
|
(
|
||||||
labor,
|
labor,
|
||||||
if Some(labor) == merchant_labor {
|
if labor == merchant_labor {
|
||||||
all_trade_goods
|
all_trade_goods
|
||||||
.iter()
|
.iter()
|
||||||
.chain(std::iter::once(&goodvec.0))
|
.chain(std::iter::once(&goodvec.0))
|
||||||
@ -879,15 +932,15 @@ impl Economy {
|
|||||||
trace!(?labor_ratios);
|
trace!(?labor_ratios);
|
||||||
|
|
||||||
let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::<f32>().max(0.01);
|
let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::<f32>().max(0.01);
|
||||||
let mut labor_context = vc.context("labor");
|
//let mut labor_context = vc.context("labor");
|
||||||
productivity.iter().for_each(|(labor, _)| {
|
production.iter().for_each(|(labor, _)| {
|
||||||
let smooth = 0.8;
|
let smooth = 0.8;
|
||||||
self.labors[labor] = labor_context.value(
|
self.labors[labor] =
|
||||||
&format!("{:?}", labor),
|
// labor_context.value(
|
||||||
|
// &format!("{:?}", labor),
|
||||||
smooth * self.labors[labor]
|
smooth * self.labors[labor]
|
||||||
+ (1.0 - smooth)
|
+ (1.0 - smooth)
|
||||||
* (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum),
|
* (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum);
|
||||||
);
|
|
||||||
assert!(self.labors[labor] >= 0.0);
|
assert!(self.labors[labor] >= 0.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -905,13 +958,9 @@ impl Economy {
|
|||||||
// TODO: trade
|
// TODO: trade
|
||||||
let mut total_outputs = GoodMap::<f32>::default();
|
let mut total_outputs = GoodMap::<f32>::default();
|
||||||
for (labor, orders) in orders.iter() {
|
for (labor, orders) in orders.iter() {
|
||||||
let workers = if let Some(labor) = labor {
|
let workers = self.labors[labor] * self.pop;
|
||||||
self.labors[*labor]
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
} * self.pop;
|
|
||||||
assert!(workers >= 0.0);
|
assert!(workers >= 0.0);
|
||||||
let is_merchant = merchant_labor == *labor;
|
let is_merchant = merchant_labor == labor;
|
||||||
|
|
||||||
// For each order, we try to find the minimum satisfaction rate - this limits
|
// For each order, we try to find the minimum satisfaction rate - this limits
|
||||||
// how much we can produce! For example, if we need 0.25 fish and
|
// how much we can produce! For example, if we need 0.25 fish and
|
||||||
@ -934,13 +983,11 @@ impl Economy {
|
|||||||
panic!("Industry {:?} requires at least one input order", labor)
|
panic!("Industry {:?} requires at least one input order", labor)
|
||||||
});
|
});
|
||||||
assert!(labor_productivity >= 0.0);
|
assert!(labor_productivity >= 0.0);
|
||||||
if let Some(labor) = labor {
|
self.limited_by[labor] = if labor_productivity >= 1.0 {
|
||||||
self.limited_by[*labor] = if labor_productivity >= 1.0 {
|
GoodIndex::default()
|
||||||
GoodIndex::default()
|
} else {
|
||||||
} else {
|
limited_by
|
||||||
limited_by
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut total_materials_cost = 0.0;
|
let mut total_materials_cost = 0.0;
|
||||||
for (good, amount) in orders {
|
for (good, amount) in orders {
|
||||||
@ -1012,38 +1059,39 @@ impl Economy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Industries produce things
|
// Industries produce things
|
||||||
if let Some(labor) = labor {
|
let work_products = &production[labor];
|
||||||
let work_products = &productivity[*labor];
|
self.yields[labor] = labor_productivity * work_products.1;
|
||||||
//let workers = self.labors[*labor] * self.pop;
|
self.productivity[labor] = labor_productivity;
|
||||||
//let final_rate = rate;
|
let (stock, rate) = work_products;
|
||||||
//let yield_per_worker = labor_productivity;
|
let total_output = labor_productivity * *rate * workers;
|
||||||
self.yields[*labor] = labor_productivity * work_products.1;
|
assert!(total_output >= 0.0);
|
||||||
self.productivity[*labor] = labor_productivity;
|
self.stocks[*stock] += total_output;
|
||||||
//let total_product_rate: f32 = work_products.iter().map(|(_, r)| *r).sum();
|
produced_goods[*stock] += total_output;
|
||||||
let (stock, rate) = work_products;
|
|
||||||
let total_output = labor_productivity * *rate * workers;
|
|
||||||
assert!(total_output >= 0.0);
|
|
||||||
self.stocks[*stock] += total_output;
|
|
||||||
produced_goods[*stock] += total_output;
|
|
||||||
|
|
||||||
let produced_amount: f32 = produced_goods.iter().map(|(_, a)| *a).sum();
|
let produced_amount: f32 = produced_goods.iter().map(|(_, a)| *a).sum();
|
||||||
for (stock, amount) in produced_goods.iter() {
|
for (stock, amount) in produced_goods.iter() {
|
||||||
let cost_weight = amount / produced_amount.max(0.001);
|
let cost_weight = amount / produced_amount.max(0.001);
|
||||||
// Materials cost per unit
|
// Materials cost per unit
|
||||||
// TODO: How to handle this reasonably for multiple producers (collect upper and
|
// TODO: How to handle this reasonably for multiple producers (collect upper and
|
||||||
// lower term separately)
|
// lower term separately)
|
||||||
self.material_costs[stock] =
|
self.material_costs[stock] = total_materials_cost / amount.max(0.001) * cost_weight;
|
||||||
total_materials_cost / amount.max(0.001) * cost_weight;
|
// Labor costs
|
||||||
// Labor costs
|
let wages = 1.0;
|
||||||
let wages = 1.0;
|
let total_labor_cost = workers * wages;
|
||||||
let total_labor_cost = workers * wages;
|
|
||||||
|
|
||||||
total_labor_values[stock] +=
|
total_labor_values[stock] +=
|
||||||
(total_materials_cost + total_labor_cost) * cost_weight;
|
(total_materials_cost + total_labor_cost) * cost_weight;
|
||||||
total_outputs[stock] += amount;
|
total_outputs[stock] += amount;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// consume goods needed by everyone
|
||||||
|
for &(good, amount) in self.get_orders_everyone().iter() {
|
||||||
|
let needed = amount * self.pop;
|
||||||
|
let available = stocks_before[good];
|
||||||
|
self.stocks[good] = (self.stocks[good] - needed.min(available)).max(0.0);
|
||||||
|
//info!("Ev {:.1} {:?} {} - {:.1} {:.1}", self.pop, good,
|
||||||
|
// self.stocks[good], needed, available);
|
||||||
|
}
|
||||||
|
|
||||||
// Update labour values per unit
|
// Update labour values per unit
|
||||||
self.labor_values = total_labor_values.map(|stock, tlv| {
|
self.labor_values = total_labor_values.map(|stock, tlv| {
|
||||||
@ -1073,10 +1121,10 @@ impl Economy {
|
|||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
self.pop += vc.value(
|
self.pop += //vc.value(
|
||||||
"pop",
|
//"pop",
|
||||||
dt / DAYS_PER_YEAR * self.pop * (birth_rate - DEATH_RATE),
|
dt / DAYS_PER_YEAR * self.pop * (birth_rate - DEATH_RATE);
|
||||||
);
|
//);
|
||||||
self.population_limited_by = if population_growth {
|
self.population_limited_by = if population_growth {
|
||||||
GoodIndex::default()
|
GoodIndex::default()
|
||||||
} else {
|
} else {
|
||||||
@ -1088,22 +1136,23 @@ impl Economy {
|
|||||||
// orders are static
|
// orders are static
|
||||||
let mut next_demand = GoodMap::from_default(0.0);
|
let mut next_demand = GoodMap::from_default(0.0);
|
||||||
for (labor, orders) in orders.iter() {
|
for (labor, orders) in orders.iter() {
|
||||||
let workers = if let Some(labor) = labor {
|
let workers = self.labors[labor] * self.pop;
|
||||||
self.labors[*labor]
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
} * self.pop;
|
|
||||||
for (good, amount) in orders {
|
for (good, amount) in orders {
|
||||||
next_demand[*good] += *amount * workers;
|
next_demand[*good] += *amount * workers;
|
||||||
assert!(next_demand[*good] >= 0.0);
|
assert!(next_demand[*good] >= 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut us = vc.context("unconsumed");
|
for (good, amount) in self.get_orders_everyone().iter() {
|
||||||
|
next_demand[*good] += *amount * self.pop;
|
||||||
|
assert!(next_demand[*good] >= 0.0);
|
||||||
|
}
|
||||||
|
//let mut us = vc.context("unconsumed");
|
||||||
self.unconsumed_stock = GoodMap::from_iter(
|
self.unconsumed_stock = GoodMap::from_iter(
|
||||||
self.stocks.iter().map(|(g, a)| {
|
self.stocks.iter().map(|(g, a)| {
|
||||||
(
|
(
|
||||||
g,
|
g,
|
||||||
us.value(&format!("{:?}", Good::from(g)), *a - next_demand[g]),
|
//us.value(&format!("{:?}", Good::from(g)),
|
||||||
|
*a - next_demand[g],
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
0.0,
|
0.0,
|
||||||
@ -1111,7 +1160,6 @@ impl Economy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn csv_entry(f: &mut std::fs::File, site: &Site) -> Result<(), std::io::Error> {
|
pub fn csv_entry(f: &mut std::fs::File, site: &Site) -> Result<(), std::io::Error> {
|
||||||
use crate::site::economy::GoodIndex;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
write!(
|
write!(
|
||||||
*f,
|
*f,
|
||||||
@ -1215,32 +1263,43 @@ impl Economy {
|
|||||||
for g in good_list() {
|
for g in good_list() {
|
||||||
write!(f, "{:?} trade,", g)?;
|
write!(f, "{:?} trade,", g)?;
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
writeln!(f)
|
||||||
Ok(())
|
}
|
||||||
|
|
||||||
let mut f = if GENERATE_CSV {
|
pub fn csv_open() -> Option<std::fs::File> {
|
||||||
let mut f = std::fs::File::create("economy.csv")?;
|
if GENERATE_CSV {
|
||||||
Some(f)
|
let mut f = std::fs::File::create("economy.csv").ok()?;
|
||||||
|
if Self::csv_header(&mut f).is_err() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(f)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_sorted(prefix: &str, mut list: Vec<(String, f32)>, threshold: f32, decimals: usize) {
|
|
||||||
print!("{}", prefix);
|
|
||||||
list.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Less));
|
|
||||||
for i in list.iter() {
|
|
||||||
if i.1 >= threshold {
|
|
||||||
print!("{}={:.*} ", i.0, decimals, i.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_economy(sites: &common::store::Store<crate::site::Site>) {
|
#[cfg(test)]
|
||||||
|
fn print_details(&self) {
|
||||||
|
fn print_sorted(
|
||||||
|
prefix: &str,
|
||||||
|
mut list: Vec<(String, f32)>,
|
||||||
|
threshold: f32,
|
||||||
|
decimals: usize,
|
||||||
|
) {
|
||||||
|
print!("{}", prefix);
|
||||||
|
list.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Less));
|
||||||
|
for i in list.iter() {
|
||||||
|
if i.1 >= threshold {
|
||||||
|
print!("{}={:.*} ", i.0, decimals, i.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
print!(" Resources: ");
|
print!(" Resources: ");
|
||||||
for i in good_list() {
|
for i in good_list() {
|
||||||
let amount = site.economy.natural_resources.chunks_per_resource[i];
|
let amount = self.natural_resources.chunks_per_resource[i];
|
||||||
if amount > 0.0 {
|
if amount > 0.0 {
|
||||||
print!("{:?}={} ", i, amount);
|
print!("{:?}={} ", i, amount);
|
||||||
}
|
}
|
||||||
@ -1248,24 +1307,21 @@ impl Economy {
|
|||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
" Population {:.1}, limited by {:?}",
|
" Population {:.1}, limited by {:?}",
|
||||||
site.economy.pop, site.economy.population_limited_by
|
self.pop, self.population_limited_by
|
||||||
);
|
);
|
||||||
let idle: f32 =
|
let idle: f32 = self.pop * (1.0 - self.labors.iter().map(|(_, a)| *a).sum::<f32>());
|
||||||
site.economy.pop * (1.0 - site.economy.labors.iter().map(|(_, a)| *a).sum::<f32>());
|
|
||||||
print_sorted(
|
print_sorted(
|
||||||
&format!(" Professions: idle={:.1} ", idle),
|
&format!(" Professions: idle={:.1} ", idle),
|
||||||
site.economy
|
self.labors
|
||||||
.labors
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(l, a)| (format!("{:?}", l), *a * site.economy.pop))
|
.map(|(l, a)| (format!("{:?}", l), *a * self.pop))
|
||||||
.collect(),
|
.collect(),
|
||||||
site.economy.pop * 0.05,
|
self.pop * 0.05,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
print_sorted(
|
print_sorted(
|
||||||
" Stock: ",
|
" Stock: ",
|
||||||
site.economy
|
self.stocks
|
||||||
.stocks
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(l, a)| (format!("{:?}", l), *a))
|
.map(|(l, a)| (format!("{:?}", l), *a))
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -1274,8 +1330,7 @@ impl Economy {
|
|||||||
);
|
);
|
||||||
print_sorted(
|
print_sorted(
|
||||||
" Values: ",
|
" Values: ",
|
||||||
site.economy
|
self.values
|
||||||
.values
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(l, a)| {
|
.map(|(l, a)| {
|
||||||
(
|
(
|
||||||
@ -1289,8 +1344,7 @@ impl Economy {
|
|||||||
);
|
);
|
||||||
print_sorted(
|
print_sorted(
|
||||||
" Labor Values: ",
|
" Labor Values: ",
|
||||||
site.economy
|
self.labor_values
|
||||||
.labor_values
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(l, a)| (format!("{:?}", l), a.unwrap_or(0.0)))
|
.map(|(l, a)| (format!("{:?}", l), a.unwrap_or(0.0)))
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -1298,36 +1352,28 @@ impl Economy {
|
|||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
print!(" Limited: ");
|
print!(" Limited: ");
|
||||||
for (limit, prod) in site
|
for (limit, prod) in self.limited_by.iter().zip(self.productivity.iter()) {
|
||||||
.economy
|
|
||||||
.limited_by
|
|
||||||
.iter()
|
|
||||||
.zip(site.economy.productivity.iter())
|
|
||||||
{
|
|
||||||
if (0.01..=0.99).contains(prod.1) {
|
if (0.01..=0.99).contains(prod.1) {
|
||||||
print!("{:?}:{:?}={} ", limit.0, limit.1, *prod.1);
|
print!("{:?}:{:?}={:.2} ", limit.0, limit.1, *prod.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
print!(" Trade({}): ", site.economy.neighbors.len());
|
print!(" Trade({}): ", self.neighbors.len());
|
||||||
for (g, &amt) in site.economy.active_exports.iter() {
|
for (g, &amt) in self.active_exports.iter() {
|
||||||
if !(-0.1..=0.1).contains(&amt) {
|
if !(-0.1..=0.1).contains(&amt) {
|
||||||
print!("{:?}={:.2} ", g, amt);
|
print!("{:?}={:.2} ", g, amt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
// check population (shrinks if economy gets broken)
|
|
||||||
// assert!(site.economy.pop >= env.targets[&id]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn good_list() -> impl Iterator<Item = GoodIndex> {
|
fn good_list() -> impl Iterator<Item = GoodIndex> {
|
||||||
(0..GoodIndex::LENGTH).map(GoodIndex::from_usize)
|
(0..GoodIndex::LENGTH).map(GoodIndex::from_usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache in GoodMap ?
|
// cache in GoodMap ?
|
||||||
pub fn transportation_effort(g: GoodIndex) -> f32 {
|
fn transportation_effort(g: GoodIndex) -> f32 {
|
||||||
match Good::from(g) {
|
match Good::from(g) {
|
||||||
Terrain(_) | Territory(_) | RoadSecurity => 0.0,
|
Terrain(_) | Territory(_) | RoadSecurity => 0.0,
|
||||||
Coin => 0.01,
|
Coin => 0.01,
|
||||||
@ -1340,7 +1386,7 @@ pub fn transportation_effort(g: GoodIndex) -> f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decay_rate(g: GoodIndex) -> f32 {
|
fn decay_rate(g: GoodIndex) -> f32 {
|
||||||
match Good::from(g) {
|
match Good::from(g) {
|
||||||
Food => 0.2,
|
Food => 0.2,
|
||||||
Flour => 0.1,
|
Flour => 0.1,
|
||||||
@ -1351,7 +1397,7 @@ pub fn decay_rate(g: GoodIndex) -> f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** you can't accumulate or save these options/resources for later */
|
/** you can't accumulate or save these options/resources for later */
|
||||||
pub fn direct_use_goods() -> &'static [GoodIndex] {
|
fn direct_use_goods() -> &'static [GoodIndex] {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref DIRECT_USE: [GoodIndex; 13] = [
|
static ref DIRECT_USE: [GoodIndex; 13] = [
|
||||||
GoodIndex::try_from(Transportation).unwrap_or_default(),
|
GoodIndex::try_from(Transportation).unwrap_or_default(),
|
||||||
@ -1371,3 +1417,29 @@ pub fn direct_use_goods() -> &'static [GoodIndex] {
|
|||||||
}
|
}
|
||||||
&*DIRECT_USE
|
&*DIRECT_USE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GraphInfo {
|
||||||
|
dummy: Economy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphInfo {
|
||||||
|
pub fn get_orders(&self) -> &'static LaborMap<Vec<(GoodIndex, f32)>> { self.dummy.get_orders() }
|
||||||
|
|
||||||
|
pub fn get_orders_everyone(&self) -> Vec<(GoodIndex, f32)> { self.dummy.get_orders_everyone() }
|
||||||
|
|
||||||
|
pub fn get_production(&self) -> LaborMap<(GoodIndex, f32)> { self.dummy.get_production() }
|
||||||
|
|
||||||
|
pub fn good_list(&self) -> impl Iterator<Item = GoodIndex> { good_list() }
|
||||||
|
|
||||||
|
pub fn labor_list(&self) -> impl Iterator<Item = Labor> { Labor::list() }
|
||||||
|
|
||||||
|
pub fn can_store(&self, g: &GoodIndex) -> bool { direct_use_goods().contains(g) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GraphInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
dummy: Economy::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -187,12 +187,7 @@ impl Site {
|
|||||||
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
|
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
|
||||||
Some(common::trade::SiteInformation {
|
Some(common::trade::SiteInformation {
|
||||||
id: site_id,
|
id: site_id,
|
||||||
unconsumed_stock: self
|
unconsumed_stock: self.economy.get_available_stock(),
|
||||||
.economy
|
|
||||||
.unconsumed_stock
|
|
||||||
.iter()
|
|
||||||
.map(|(g, a)| (g.into(), *a))
|
|
||||||
.collect(),
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
Loading…
Reference in New Issue
Block a user