initial_code
This commit is contained in:
parent
a57debd087
commit
393b3395a5
14 changed files with 2808 additions and 0 deletions
75
crop_rot/src/main.rs
Normal file
75
crop_rot/src/main.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// mod harvest;
|
||||
pub mod model;
|
||||
pub mod weights;
|
||||
|
||||
use model::*;
|
||||
use weights::{ColorWeights, Config};
|
||||
|
||||
use std::fs;
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let config = parse_config("config.toml").unwrap();
|
||||
|
||||
let mut res: Vec<(ColorWeights, Vec<f64>)> = Vec::new();
|
||||
|
||||
for color in gen_possible_color_weights().into_iter() {
|
||||
let val = run_sim(100, &config, &color);
|
||||
println!("{:?} {:?}", color, val);
|
||||
res.push((color, val));
|
||||
}
|
||||
|
||||
println!("{}", res);
|
||||
|
||||
// for i in 0..10 {
|
||||
// let res = lifeforce_after_upgrades(i, 1000000, &config);
|
||||
|
||||
// println!("{i}: {res}");
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
fn parse_config(path: &str) -> Result<Config, Box<dyn std::error::Error>> {
|
||||
let file = fs::read_to_string(path)?;
|
||||
let config: Config = toml::from_str(&file)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn run_sim(n: usize, config: &Config, color_weight: &ColorWeights) -> Vec<f64> {
|
||||
(0..n)
|
||||
.into_par_iter()
|
||||
.map(|_| Harvest::<Field>::new(color_weight, config).recursive_harvest(config))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn gen_possible_color_weights() -> Vec<ColorWeights> {
|
||||
let allowed = [0.55, 0.65, 0.75, 0.80, 0.90, 1.00];
|
||||
|
||||
let mut all: Vec<ColorWeights> = Vec::new();
|
||||
|
||||
for &yellow in &allowed {
|
||||
for &blue in &allowed {
|
||||
for &red in &allowed {
|
||||
if yellow == 0.55 || blue == 0.55 || red == 0.55 {
|
||||
all.push(ColorWeights { yellow, blue, red });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
all
|
||||
}
|
||||
|
||||
fn lifeforce_after_upgrades(n: usize, tries: usize, config: &Config) -> f64 {
|
||||
(0..tries)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut field: Field = Field { color: weights::Color::Yellow, wilted: false, t1_seeds: 23, t2_seeds: 0, t3_seeds: 0, t4_seeds: 0};
|
||||
for i in 0..n {
|
||||
field.upgrade(config);
|
||||
}
|
||||
field.harvest(config).unwrap()
|
||||
}).sum::<f64>() / (tries as f64)
|
||||
}
|
||||
194
crop_rot/src/model.rs
Normal file
194
crop_rot/src/model.rs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
use std::fmt::{self, Debug, Display};
|
||||
use rand::prelude::*;
|
||||
|
||||
use crate::weights::{Config, ColorWeights, Color};
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Field {
|
||||
pub color: Color,
|
||||
pub wilted: bool,
|
||||
pub t1_seeds: i8,
|
||||
pub t2_seeds: i8,
|
||||
pub t3_seeds: i8,
|
||||
pub t4_seeds: i8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FakeField {
|
||||
color: Color,
|
||||
wilted: bool,
|
||||
upgrades: usize,
|
||||
}
|
||||
|
||||
pub trait Harvestable {
|
||||
fn new(color_weights: &ColorWeights) -> Self;
|
||||
|
||||
fn new_ungenerated() -> Self;
|
||||
|
||||
fn harvest(&mut self, config: &Config) -> Result<f64, &'static str>;
|
||||
|
||||
fn upgrade(&mut self, config: &Config);
|
||||
|
||||
fn wilt(&mut self);
|
||||
|
||||
fn is_wilted(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Harvest<T: Harvestable> {
|
||||
fields: [T; 10],
|
||||
value: f64,
|
||||
}
|
||||
|
||||
impl Harvestable for Field {
|
||||
fn new(color_weights: &ColorWeights) -> Self {
|
||||
Field { color: color_weights.color(), wilted: false, t1_seeds: 23, t2_seeds: 0, t3_seeds: 0, t4_seeds: 0 }
|
||||
}
|
||||
|
||||
fn new_ungenerated() -> Self {
|
||||
Field { color: Color::NotGenerated, wilted: true, t1_seeds: 0, t2_seeds: 0, t3_seeds: 0, t4_seeds: 0 }
|
||||
}
|
||||
|
||||
fn harvest(&mut self, config: &Config) -> Result<f64, &'static str> {
|
||||
match self.wilted {
|
||||
false => {
|
||||
self.wilted = true;
|
||||
Ok(self.t1_seeds as f64 * config.seed_lifeforce.t1 + self.t2_seeds as f64 * config.seed_lifeforce.t2 + self.t3_seeds as f64 * config.seed_lifeforce.t3 + self.t4_seeds as f64 * config.seed_lifeforce.t4)
|
||||
},
|
||||
true => Err("cannot harvest wilted crop")
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade(&mut self, config: &Config) {
|
||||
if !self.wilted {
|
||||
let t2 = config.seed_upgrade_chance.t1_to_t2(self.t1_seeds);
|
||||
let t3 = config.seed_upgrade_chance.t2_to_t3(self.t2_seeds);
|
||||
let t4 = config.seed_upgrade_chance.t3_to_t4(self.t3_seeds);
|
||||
self.t1_seeds += - t2;
|
||||
self.t2_seeds += t2 - t3;
|
||||
self.t3_seeds += t3 - t4;
|
||||
self.t4_seeds += t4;
|
||||
}
|
||||
}
|
||||
|
||||
fn wilt(&mut self) {
|
||||
self.wilted = true;
|
||||
}
|
||||
|
||||
fn is_wilted(&self) -> bool {
|
||||
self.wilted
|
||||
}
|
||||
}
|
||||
|
||||
impl Harvestable for FakeField {
|
||||
fn new(color_weights: &ColorWeights) -> FakeField {
|
||||
FakeField { color: color_weights.color(), wilted: false, upgrades: 0 }
|
||||
}
|
||||
|
||||
fn new_ungenerated() -> Self {
|
||||
FakeField {color: Color::NotGenerated, wilted: true, upgrades: 0}
|
||||
}
|
||||
|
||||
fn harvest(&mut self, config: &Config) -> Result<f64, &'static str> {
|
||||
match self.wilted {
|
||||
false => {
|
||||
self.wilted = true;
|
||||
Ok(config.expected_lifeforce[self.upgrades] * config.lifeforce_value.from_color(&self.color))
|
||||
}
|
||||
true => Err("cannot harvest wilted crop"),
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade(&mut self, config: &Config) {
|
||||
if !self.wilted { self.upgrades += 1; }
|
||||
}
|
||||
|
||||
fn wilt(&mut self) {
|
||||
self.wilted = true;
|
||||
}
|
||||
|
||||
fn is_wilted(&self) -> bool {
|
||||
self.wilted
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Harvest<T> where
|
||||
T: Harvestable + Debug + Clone + Display
|
||||
{
|
||||
pub fn new(color_weights: &ColorWeights, config: &Config) -> Harvest<T> {
|
||||
let rolled_fields: usize = rand::distributions::Uniform::new(3, 6).sample(&mut thread_rng()) * 2;
|
||||
|
||||
let mut fields: Vec<T> = Vec::new();
|
||||
|
||||
for _ in 0..rolled_fields {
|
||||
fields.push(T::new(color_weights));
|
||||
}
|
||||
for _ in rolled_fields..10 {
|
||||
fields.push(T::new_ungenerated())
|
||||
}
|
||||
|
||||
Harvest { value: 0.0, fields: fields.try_into().unwrap() }
|
||||
}
|
||||
|
||||
pub fn harvest(&mut self, n: usize, config: &Config) -> Result<f64, &'static str> {
|
||||
match self.fields[n].harvest(config) {
|
||||
Err(x) => Err(x),
|
||||
Ok(gained_val) => {
|
||||
self.value += gained_val;
|
||||
let neighbor = n + 1 - n % 2;
|
||||
|
||||
if thread_rng().gen_bool(0.4) {self.fields[neighbor].wilt();}
|
||||
|
||||
self.fields.iter_mut().for_each(|field| field.upgrade(config));
|
||||
|
||||
Ok(gained_val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recursive_harvest(self, config: &Config) -> f64 {
|
||||
if self.fields.iter().all(|field| field.is_wilted()) {
|
||||
return self.value;
|
||||
} else {
|
||||
return self.fields.iter().enumerate().filter(|(_, field)| !field.is_wilted()).map(|(i, field)| {
|
||||
let mut clone = self.clone();
|
||||
clone.harvest(i, config);
|
||||
clone.recursive_harvest(config)
|
||||
}).reduce(f64::max).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Field {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.wilted {
|
||||
false => write!(f, "{}:{}:{}:{}:{}", match self.color {
|
||||
Color::Yellow => "Y",
|
||||
Color::Blue => "B",
|
||||
Color::Red => "R",
|
||||
Color::NotGenerated => "N/A"
|
||||
}, self.t1_seeds, self.t2_seeds, self.t3_seeds, self.t4_seeds),
|
||||
true => write!(f, "Wilted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FakeField {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Up:{}", self.upgrades)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + Harvestable> fmt::Display for Harvest<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (i, field) in self.fields.iter().enumerate() {
|
||||
write!(f, "{}", field)?;
|
||||
if i % 2 == 0 {
|
||||
write!(f, " <-> ")?;
|
||||
}
|
||||
else { write!(f, " ")?; }
|
||||
}
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
107
crop_rot/src/weights.rs
Normal file
107
crop_rot/src/weights.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use rand::prelude::Distribution;
|
||||
use rand_distr::Binomial;
|
||||
use rand::distributions::WeightedIndex;
|
||||
use rand::thread_rng;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UpgradeProbabilities {
|
||||
pub t1_t2: f64,
|
||||
pub t2_t3: f64,
|
||||
pub t3_t4: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LifeforceValue {
|
||||
pub yellow: f64,
|
||||
pub blue: f64,
|
||||
pub red: f64,
|
||||
}
|
||||
|
||||
impl LifeforceValue {
|
||||
pub fn from_color(&self, color: &Color) -> f64 {
|
||||
match color {
|
||||
Color::Yellow => self.yellow,
|
||||
Color::Blue => self.blue,
|
||||
Color::Red => self.red,
|
||||
_ => 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SeedLifeforce {
|
||||
pub t1: f64,
|
||||
pub t2: f64,
|
||||
pub t3: f64,
|
||||
pub t4: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MapMods {
|
||||
pub pack_size: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Weights {
|
||||
pub upgrade: UpgradeProbabilities,
|
||||
pub value: LifeforceValue,
|
||||
pub lifeforce: SeedLifeforce,
|
||||
pub map_mods: MapMods,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SeedUpgradeChances {
|
||||
pub t1: f64,
|
||||
pub t2: f64,
|
||||
pub t3: f64,
|
||||
}
|
||||
|
||||
impl SeedUpgradeChances {
|
||||
pub fn t1_to_t2(&self, n: i8) -> i8 {
|
||||
Binomial::new(n as u64, self.t1).unwrap().sample(&mut thread_rng()) as i8
|
||||
}
|
||||
pub fn t2_to_t3(&self, n: i8) -> i8 {
|
||||
Binomial::new(n as u64, self.t2).unwrap().sample(&mut thread_rng()) as i8
|
||||
}
|
||||
pub fn t3_to_t4(&self, n: i8) -> i8 {
|
||||
Binomial::new(n as u64, self.t3).unwrap().sample(&mut thread_rng()) as i8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
pub lifeforce_value: LifeforceValue,
|
||||
pub seed_lifeforce: SeedLifeforce,
|
||||
pub map_mods: MapMods,
|
||||
pub seed_upgrade_chance: SeedUpgradeChances,
|
||||
pub expected_lifeforce: [f64; 10],
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Color {
|
||||
Yellow,
|
||||
Blue,
|
||||
Red,
|
||||
NotGenerated
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ColorWeights {
|
||||
pub yellow: f64,
|
||||
pub blue: f64,
|
||||
pub red: f64,
|
||||
}
|
||||
|
||||
impl ColorWeights {
|
||||
pub fn color(&self) -> Color {
|
||||
match WeightedIndex::new([self.yellow, self.blue, self.red]).unwrap().sample(&mut thread_rng()) {
|
||||
0 => Color::Yellow,
|
||||
1 => Color::Blue,
|
||||
2 => Color::Red,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue