veloren/world/examples/pricing_csv.rs
2023-10-02 16:52:41 +02:00

183 lines
6.0 KiB
Rust

use common::{
terrain::BiomeKind,
trade::{Good, SitePrices},
};
use rayon::ThreadPoolBuilder;
use rusqlite::{Connection, ToSql};
use std::error::Error;
use strum::IntoEnumIterator;
use vek::Vec2;
use veloren_world::{
index::Index,
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
World,
};
fn good_pricing_csv(world: &World, index: &Index) -> Result<(), Box<dyn Error>> {
let mut csv = csv::Writer::from_path("good_pricing.csv")?;
csv.write_record([
"Site",
"XCoord",
"YCoord",
"Flour",
"Meat",
"Transportation",
"Food",
"Wood",
"Stone",
"Tools",
"Armor",
"Ingredients",
"Potions",
"Coin",
"RoadSecurity",
])?;
for civsite in world.civs().sites() {
if let Some(site_id) = civsite.site_tmp {
let site = index.sites.get(site_id);
if site.do_economic_simulation() {
let prices = site.economy.get_site_prices();
//println!("{:?}: {:?} {:?}", site.name(), civsite.center, prices);
csv.write_record([
site.name(),
&format!("{}", civsite.center.x),
&format!("{}", civsite.center.y),
&format!("{}", prices.values.get(&Good::Flour).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Meat).unwrap_or(&0.0)),
&format!(
"{}",
prices.values.get(&Good::Transportation).unwrap_or(&0.0)
),
&format!("{}", prices.values.get(&Good::Food).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Wood).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Stone).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Tools).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Armor).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Ingredients).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Potions).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::Coin).unwrap_or(&0.0)),
&format!("{}", prices.values.get(&Good::RoadSecurity).unwrap_or(&0.0)),
])?;
}
}
}
Ok(())
}
fn economy_sqlite(world: &World, index: &Index) -> Result<(), Box<dyn Error>> {
let conn = Connection::open("economy.sqlite")?;
#[rustfmt::skip]
conn.execute_batch("
DROP TABLE IF EXISTS site;
CREATE TABLE site (
xcoord INTEGER NOT NULL,
ycoord INTEGER NOT NULL,
name TEXT NOT NULL
);
CREATE UNIQUE INDEX site_position ON site(xcoord, ycoord);
DROP TABLE IF EXISTS site_price;
CREATE TABLE site_price (
xcoord INTEGER NOT NULL,
ycoord INTEGER NOT NULL,
good TEXT NOT NULL,
price REAL NOT NULL
);
CREATE UNIQUE INDEX site_good on site_price(xcoord, ycoord, good);
")?;
let mut all_goods = Vec::new();
for good in Good::iter() {
match good {
Good::Territory(_) => {
for biome in BiomeKind::iter() {
all_goods.push(Good::Territory(biome));
}
},
Good::Terrain(_) => {
for biome in BiomeKind::iter() {
all_goods.push(Good::Terrain(biome));
}
},
_ => {
all_goods.push(good);
},
}
}
let mut good_columns = String::new();
let mut good_exprs = String::new();
for good in all_goods.iter() {
good_columns += &format!(", '{:?}'", good);
good_exprs += &format!(
", MAX(CASE WHEN site_price.good = '{:?}' THEN site_price.price END)",
good
);
}
#[rustfmt::skip]
let create_view = format!("
DROP VIEW IF EXISTS site_price_tr;
CREATE VIEW site_price_tr (xcoord, ycoord {})
AS SELECT xcoord, ycoord {}
FROM site NATURAL JOIN site_price
GROUP BY xcoord, ycoord;
", good_columns, good_exprs);
conn.execute_batch(&create_view)?;
let mut insert_price_stmt = conn
.prepare("REPLACE INTO site_price (xcoord, ycoord, good, price) VALUES (?1, ?2, ?3, ?4)")?;
let mut insert_price = move |center: Vec2<i32>, good: Good, prices: &SitePrices| {
let price = prices.values.get(&good).unwrap_or(&0.0);
insert_price_stmt.execute([
&center.x as &dyn ToSql,
&center.y,
&format!("{:?}", good),
&(*price as f64),
])
};
for civsite in world.civs().sites() {
if let Some(site_id) = civsite.site_tmp {
let site = index.sites.get(site_id);
if site.do_economic_simulation() {
let prices = site.economy.get_site_prices();
conn.execute(
"REPLACE INTO site (xcoord, ycoord, name) VALUES (?1, ?2, ?3)",
[
&civsite.center.x as &dyn ToSql,
&civsite.center.y,
&site.name(),
],
)?;
for good in all_goods.iter() {
insert_price(civsite.center, *good, &prices)?;
}
}
}
}
Ok(())
}
fn main() {
common_frontend::init_stdout(None);
println!("Loading world");
let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate(
59686,
WorldOpts {
seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
calendar: None,
},
&pool,
&|_| {},
);
println!("Loaded world");
if let Err(e) = good_pricing_csv(&world, &index) {
println!("Error generating goodpricing csv: {:?}", e);
}
if let Err(e) = economy_sqlite(&world, &index) {
println!("Error generating economy sqlite db: {:?}", e);
}
}