Initial commit, implements most of the static_data side.
This commit is contained in:
commit
2a08fb2645
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
.idea/
|
|
@ -0,0 +1,46 @@
|
||||||
|
[package]
|
||||||
|
name = "pkmn_lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Deukhoofd <Deukhoofd@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "pkmn_lib"
|
||||||
|
crate_type = ["cdylib"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 0
|
||||||
|
debug = true
|
||||||
|
debug-assertions = true
|
||||||
|
overflow-checks = true
|
||||||
|
lto = "thin"
|
||||||
|
panic = 'unwind'
|
||||||
|
incremental = true
|
||||||
|
codegen-units = 256
|
||||||
|
rpath = false
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
debug = 1
|
||||||
|
debug-assertions = false
|
||||||
|
overflow-checks = true
|
||||||
|
lto = "fat"
|
||||||
|
panic = 'unwind'
|
||||||
|
incremental = false
|
||||||
|
codegen-units = 16
|
||||||
|
rpath = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Used for PrimInt, so we can check if a generic is an integer
|
||||||
|
num-traits = "0.2"
|
||||||
|
# Used for automatically generating getters
|
||||||
|
derive-getters = "0.2.0"
|
||||||
|
# Allow us to assert whether floats are approximately a value
|
||||||
|
assert_approx_eq = "1.1.0"
|
||||||
|
# Used for time based code (i.e. randomness)
|
||||||
|
chrono = "0.4.19"
|
||||||
|
# Used for RNG
|
||||||
|
rand = "0.8.3"
|
||||||
|
rand_pcg = "0.3.0"
|
||||||
|
# Used for the hashmap!{} macro, creating a simple initializer pattern for hashmaps.
|
||||||
|
maplit = "1.0.2"
|
|
@ -0,0 +1 @@
|
||||||
|
pub type LevelInt = u8;
|
|
@ -0,0 +1,10 @@
|
||||||
|
// The too many arguments is annoying, especially for when we create constructors, disable.
|
||||||
|
#![allow(clippy::too_many_arguments, clippy::needless_range_loop)]
|
||||||
|
#![feature(nll)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate maplit;
|
||||||
|
|
||||||
|
pub mod defines;
|
||||||
|
pub mod static_data;
|
||||||
|
pub mod utils;
|
|
@ -0,0 +1,6 @@
|
||||||
|
use crate::defines::LevelInt;
|
||||||
|
|
||||||
|
pub trait GrowthRate {
|
||||||
|
fn calculate_level(&self, experience: u32) -> LevelInt;
|
||||||
|
fn calculate_experience(&self, level: LevelInt) -> u32;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
use crate::defines::LevelInt;
|
||||||
|
use crate::static_data::growth_rates::growth_rate::GrowthRate;
|
||||||
|
|
||||||
|
pub struct LookupGrowthRate {
|
||||||
|
experience: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LookupGrowthRate {
|
||||||
|
pub fn new(experience: Vec<u32>) -> LookupGrowthRate {
|
||||||
|
LookupGrowthRate { experience }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GrowthRate for LookupGrowthRate {
|
||||||
|
fn calculate_level(&self, experience: u32) -> LevelInt {
|
||||||
|
for (i, v) in self.experience.iter().enumerate() {
|
||||||
|
if *v > experience {
|
||||||
|
return i as LevelInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.experience.len() as LevelInt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_experience(&self, level: LevelInt) -> u32 {
|
||||||
|
self.experience[(level - 1) as usize]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod growth_rate;
|
||||||
|
pub mod lookup_growth_rate;
|
|
@ -0,0 +1,34 @@
|
||||||
|
use super::item_category::{BattleItemCategory, ItemCategory};
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Getters, Debug)]
|
||||||
|
pub struct Item {
|
||||||
|
name: String,
|
||||||
|
category: ItemCategory,
|
||||||
|
battle_category: BattleItemCategory,
|
||||||
|
price: i32,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
pub fn new(
|
||||||
|
name: &str,
|
||||||
|
category: ItemCategory,
|
||||||
|
battle_category: BattleItemCategory,
|
||||||
|
price: i32,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
) -> Item {
|
||||||
|
Item {
|
||||||
|
name: name.to_string(),
|
||||||
|
category,
|
||||||
|
battle_category,
|
||||||
|
price,
|
||||||
|
flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_flag(&self, key: &str) -> bool {
|
||||||
|
self.flags.contains(key)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ItemCategory {
|
||||||
|
MiscItem,
|
||||||
|
Pokeball,
|
||||||
|
Medicine,
|
||||||
|
Berry,
|
||||||
|
TMHM,
|
||||||
|
FormeChanger,
|
||||||
|
KeyItem,
|
||||||
|
Mail,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BattleItemCategory {
|
||||||
|
Healing,
|
||||||
|
StatusHealing,
|
||||||
|
Pokeball,
|
||||||
|
MiscBattleItem,
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod item;
|
||||||
|
pub mod item_category;
|
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::utils::random::Random;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub trait DataLibrary<'a, T: 'a> {
|
||||||
|
fn map(&self) -> &HashMap<String, T>;
|
||||||
|
fn list_values(&self) -> &Vec<String>;
|
||||||
|
fn get_modify(&mut self) -> (&mut HashMap<String, T>, &mut Vec<String>);
|
||||||
|
|
||||||
|
fn add(&mut self, key: &str, value: T) {
|
||||||
|
let modifies = self.get_modify();
|
||||||
|
modifies.0.insert(key.to_string(), value);
|
||||||
|
modifies.1.push(key.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&mut self, key: &str) {
|
||||||
|
let modifies = self.get_modify();
|
||||||
|
let index = modifies.1.iter().position(|r| *r == key).unwrap();
|
||||||
|
modifies.0.remove(key);
|
||||||
|
modifies.1.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, key: &str) -> Option<&T> {
|
||||||
|
self.map().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(&mut self, key: &str) -> Option<&mut T> {
|
||||||
|
self.get_modify().0.get_mut(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.map().len()
|
||||||
|
}
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.map().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_value(&self, rand: &mut Random) -> &T {
|
||||||
|
let i = rand.get_between(0, self.list_values().len() as i32);
|
||||||
|
let key = &self.list_values()[i as usize];
|
||||||
|
return &self.map()[key];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
use crate::defines::LevelInt;
|
||||||
|
use crate::static_data::growth_rates::growth_rate::GrowthRate;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
pub struct GrowthRateLibrary {
|
||||||
|
growth_rates: HashMap<String, Box<dyn GrowthRate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GrowthRateLibrary {
|
||||||
|
pub fn new(capacity: usize) -> GrowthRateLibrary {
|
||||||
|
GrowthRateLibrary {
|
||||||
|
growth_rates: HashMap::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_level(&self, growth_rate: &str, experience: u32) -> LevelInt {
|
||||||
|
self.growth_rates[growth_rate].calculate_level(experience)
|
||||||
|
}
|
||||||
|
pub fn calculate_experience(&self, growth_rate: &str, level: LevelInt) -> u32 {
|
||||||
|
self.growth_rates[growth_rate].calculate_experience(level)
|
||||||
|
}
|
||||||
|
pub fn add_growth_rate(&mut self, key: &str, value: Box<dyn GrowthRate>) {
|
||||||
|
self.growth_rates.insert(key.to_string(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for GrowthRateLibrary {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("GrowthRateLibrary").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::static_data::growth_rates::lookup_growth_rate::LookupGrowthRate;
|
||||||
|
use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_growth_rate_to_library_and_calculate_level() {
|
||||||
|
let mut lib = GrowthRateLibrary::new(1);
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.add_growth_rate("foo", Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100])));
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
assert_eq!(r.calculate_level("foo", 3), 1);
|
||||||
|
assert_eq!(r.calculate_level("foo", 50), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_growth_rate_to_library_and_calculate_experience() {
|
||||||
|
let mut lib = GrowthRateLibrary::new(1);
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.add_growth_rate("foo", Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100])));
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
assert_eq!(r.calculate_experience("foo", 1), 0);
|
||||||
|
assert_eq!(r.calculate_experience("foo", 3), 10);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::static_data::items::item::Item;
|
||||||
|
use crate::static_data::libraries::data_library::DataLibrary;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ItemLibrary {
|
||||||
|
map: HashMap<String, Item>,
|
||||||
|
list: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemLibrary {
|
||||||
|
pub fn new(capacity: usize) -> ItemLibrary {
|
||||||
|
ItemLibrary {
|
||||||
|
map: HashMap::with_capacity(capacity),
|
||||||
|
list: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataLibrary<'_, Item> for ItemLibrary {
|
||||||
|
fn map(&self) -> &HashMap<String, Item> {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_values(&self) -> &Vec<String> {
|
||||||
|
&self.list
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_modify(&mut self) -> (&mut HashMap<String, Item>, &mut Vec<String>) {
|
||||||
|
(&mut self.map, &mut self.list)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::defines::LevelInt;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
|
||||||
|
#[derive(Getters, Debug)]
|
||||||
|
pub struct LibrarySettings {
|
||||||
|
maximum_level: LevelInt,
|
||||||
|
maximum_move_count: u8,
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
pub mod data_library;
|
||||||
|
pub mod growth_rate_library;
|
||||||
|
pub mod item_library;
|
||||||
|
pub mod library_settings;
|
||||||
|
pub mod move_library;
|
||||||
|
pub mod species_library;
|
||||||
|
pub mod static_data;
|
||||||
|
pub mod type_library;
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::static_data::libraries::data_library::DataLibrary;
|
||||||
|
use crate::static_data::moves::move_data::MoveData;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MoveLibrary {
|
||||||
|
map: HashMap<String, MoveData>,
|
||||||
|
list: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoveLibrary {
|
||||||
|
pub fn new(capacity: usize) -> MoveLibrary {
|
||||||
|
MoveLibrary {
|
||||||
|
map: HashMap::with_capacity(capacity),
|
||||||
|
list: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataLibrary<'_, MoveData> for MoveLibrary {
|
||||||
|
fn map(&self) -> &HashMap<String, MoveData> {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_values(&self) -> &Vec<String> {
|
||||||
|
&self.list
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_modify(&mut self) -> (&mut HashMap<String, MoveData>, &mut Vec<String>) {
|
||||||
|
(&mut self.map, &mut self.list)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::static_data::libraries::data_library::DataLibrary;
|
||||||
|
use crate::static_data::species_data::species::Species;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SpeciesLibrary<'a> {
|
||||||
|
map: HashMap<String, Species<'a>>,
|
||||||
|
list: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SpeciesLibrary<'a> {
|
||||||
|
pub fn new(capacity: usize) -> SpeciesLibrary<'a> {
|
||||||
|
SpeciesLibrary {
|
||||||
|
map: HashMap::with_capacity(capacity),
|
||||||
|
list: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DataLibrary<'a, Species<'a>> for SpeciesLibrary<'a> {
|
||||||
|
fn map(&self) -> &HashMap<String, Species<'a>> {
|
||||||
|
&self.map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_values(&self) -> &Vec<String> {
|
||||||
|
&self.list
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_modify(&mut self) -> (&mut HashMap<String, Species<'a>>, &mut Vec<String>) {
|
||||||
|
(&mut self.map, &mut self.list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::static_data::libraries::data_library::DataLibrary;
|
||||||
|
use crate::static_data::libraries::species_library::SpeciesLibrary;
|
||||||
|
use crate::static_data::species_data::form::Form;
|
||||||
|
use crate::static_data::species_data::learnable_moves::LearnableMoves;
|
||||||
|
use crate::static_data::species_data::species::Species;
|
||||||
|
use crate::static_data::statistic_set::StatisticSet;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
fn build_species<'a>() -> Species<'a> {
|
||||||
|
Species::new(
|
||||||
|
0,
|
||||||
|
"foo",
|
||||||
|
0.5,
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
Form::new(
|
||||||
|
"default",
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0,
|
||||||
|
Vec::new(),
|
||||||
|
StatisticSet::default(),
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
LearnableMoves::new(),
|
||||||
|
HashSet::new(),
|
||||||
|
),
|
||||||
|
HashSet::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_species_to_library_and_fetch() {
|
||||||
|
let mut lib = SpeciesLibrary::new(1);
|
||||||
|
let species = build_species();
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.add("foo", species);
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
let mon = r.get("foo");
|
||||||
|
assert!(mon.is_some());
|
||||||
|
assert_eq!(*mon.unwrap().id(), 0_u16);
|
||||||
|
assert_eq!(mon.unwrap().name(), "foo");
|
||||||
|
assert_eq!(r.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_species_to_library_then_remove() {
|
||||||
|
let mut lib = SpeciesLibrary::new(1);
|
||||||
|
let species = build_species();
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.add("foo", species);
|
||||||
|
w.remove("foo");
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
let mon = r.get("foo");
|
||||||
|
assert!(mon.is_none());
|
||||||
|
assert_eq!(r.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary;
|
||||||
|
use crate::static_data::libraries::item_library::ItemLibrary;
|
||||||
|
use crate::static_data::libraries::library_settings::LibrarySettings;
|
||||||
|
use crate::static_data::libraries::move_library::MoveLibrary;
|
||||||
|
use crate::static_data::libraries::species_library::SpeciesLibrary;
|
||||||
|
use crate::static_data::libraries::type_library::TypeLibrary;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
|
||||||
|
#[derive(Getters, Debug)]
|
||||||
|
struct StaticData<'a> {
|
||||||
|
settings: LibrarySettings,
|
||||||
|
species: SpeciesLibrary<'a>,
|
||||||
|
moves: MoveLibrary,
|
||||||
|
items: ItemLibrary,
|
||||||
|
growth_rates: GrowthRateLibrary,
|
||||||
|
types: TypeLibrary,
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TypeLibrary {
|
||||||
|
types: HashMap<String, u8>,
|
||||||
|
effectiveness: Vec<Vec<f32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeLibrary {
|
||||||
|
pub fn new(capacity: usize) -> TypeLibrary {
|
||||||
|
TypeLibrary {
|
||||||
|
types: HashMap::with_capacity(capacity),
|
||||||
|
effectiveness: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_type_id(&self, key: &str) -> u8 {
|
||||||
|
self.types[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_single_effectiveness(&self, attacking: u8, defending: u8) -> f32 {
|
||||||
|
self.effectiveness[attacking as usize][defending as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_effectiveness(&self, attacking: u8, defending: &[u8]) -> f32 {
|
||||||
|
let mut e = 1.0;
|
||||||
|
for def in defending {
|
||||||
|
e *= self.get_single_effectiveness(attacking, *def);
|
||||||
|
}
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_type(&mut self, name: &str) -> u8 {
|
||||||
|
let id = self.types.len() as u8;
|
||||||
|
self.types.insert(name.to_string(), id);
|
||||||
|
self.effectiveness.resize((id + 1) as usize, vec![]);
|
||||||
|
for effectiveness in &mut self.effectiveness {
|
||||||
|
effectiveness.resize((id + 1) as usize, 1.0)
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_effectiveness(&mut self, attacking: u8, defending: u8, effectiveness: f32) {
|
||||||
|
self.effectiveness[attacking as usize][defending as usize] = effectiveness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::static_data::libraries::type_library::TypeLibrary;
|
||||||
|
use assert_approx_eq::assert_approx_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_two_types_retrieve_them() {
|
||||||
|
let mut lib = TypeLibrary::new(2);
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.register_type("foo");
|
||||||
|
w.register_type("bar");
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
assert_eq!(r.get_type_id("foo"), 0);
|
||||||
|
assert_eq!(r.get_type_id("bar"), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_two_types_set_effectiveness_retrieve() {
|
||||||
|
let mut lib = TypeLibrary::new(2);
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.register_type("foo");
|
||||||
|
w.register_type("bar");
|
||||||
|
w.set_effectiveness(0, 1, 0.5);
|
||||||
|
w.set_effectiveness(1, 0, 2.0);
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
assert_approx_eq!(r.get_single_effectiveness(0, 1), 0.5);
|
||||||
|
assert_approx_eq!(r.get_single_effectiveness(1, 0), 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_two_types_get_aggregate_effectiveness() {
|
||||||
|
let mut lib = TypeLibrary::new(2);
|
||||||
|
|
||||||
|
// Borrow as mut so we can insert
|
||||||
|
let w = &mut lib;
|
||||||
|
w.register_type("foo");
|
||||||
|
w.register_type("bar");
|
||||||
|
w.set_effectiveness(0, 1, 0.5);
|
||||||
|
w.set_effectiveness(1, 0, 2.0);
|
||||||
|
// Drops borrow as mut
|
||||||
|
|
||||||
|
// Borrow as read so we can read
|
||||||
|
let r = &lib;
|
||||||
|
assert_approx_eq!(r.get_effectiveness(0, &[1_u8, 1_u8]), 0.25);
|
||||||
|
assert_approx_eq!(r.get_effectiveness(1, &[0_u8, 0_u8]), 4.0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
pub mod growth_rates;
|
||||||
|
pub mod items;
|
||||||
|
pub mod libraries;
|
||||||
|
pub mod moves;
|
||||||
|
pub mod species_data;
|
||||||
|
pub mod statistic_set;
|
||||||
|
pub mod statistics;
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod move_data;
|
||||||
|
pub mod secondary_effect;
|
|
@ -0,0 +1,75 @@
|
||||||
|
use self::super::secondary_effect::SecondaryEffect;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum MoveCategory {
|
||||||
|
Physical,
|
||||||
|
Special,
|
||||||
|
Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum MoveTarget {
|
||||||
|
Adjacent,
|
||||||
|
AdjacentAlly,
|
||||||
|
AdjacentAllySelf,
|
||||||
|
AdjacentOpponent,
|
||||||
|
|
||||||
|
All,
|
||||||
|
AllAdjacent,
|
||||||
|
AllAdjacentOpponent,
|
||||||
|
AllAlly,
|
||||||
|
AllOpponent,
|
||||||
|
|
||||||
|
Any,
|
||||||
|
|
||||||
|
RandomOpponent,
|
||||||
|
SelfUse,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub struct MoveData {
|
||||||
|
name: String,
|
||||||
|
move_type: u8,
|
||||||
|
category: MoveCategory,
|
||||||
|
base_power: u8,
|
||||||
|
accuracy: u8,
|
||||||
|
base_usages: u8,
|
||||||
|
target: MoveTarget,
|
||||||
|
priority: i8,
|
||||||
|
secondary_effect: SecondaryEffect,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoveData {
|
||||||
|
pub fn new(
|
||||||
|
name: String,
|
||||||
|
move_type: u8,
|
||||||
|
category: MoveCategory,
|
||||||
|
base_power: u8,
|
||||||
|
accuracy: u8,
|
||||||
|
base_usages: u8,
|
||||||
|
target: MoveTarget,
|
||||||
|
priority: i8,
|
||||||
|
secondary_effect: SecondaryEffect,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
) -> MoveData {
|
||||||
|
MoveData {
|
||||||
|
name,
|
||||||
|
move_type,
|
||||||
|
category,
|
||||||
|
base_power,
|
||||||
|
accuracy,
|
||||||
|
base_usages,
|
||||||
|
target,
|
||||||
|
priority,
|
||||||
|
secondary_effect,
|
||||||
|
flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_flag(&self, key: &str) -> bool {
|
||||||
|
self.flags.contains(key)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
use derive_getters::Getters;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum EffectParameter {
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f32),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Getters)]
|
||||||
|
pub struct SecondaryEffect {
|
||||||
|
chance: f32,
|
||||||
|
effect_name: String,
|
||||||
|
parameters: Vec<EffectParameter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecondaryEffect {
|
||||||
|
pub fn empty() -> SecondaryEffect {
|
||||||
|
SecondaryEffect {
|
||||||
|
chance: 0.0,
|
||||||
|
effect_name: "".to_string(),
|
||||||
|
parameters: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(
|
||||||
|
chance: f32,
|
||||||
|
effect_name: String,
|
||||||
|
parameters: Vec<EffectParameter>,
|
||||||
|
) -> SecondaryEffect {
|
||||||
|
SecondaryEffect {
|
||||||
|
chance,
|
||||||
|
effect_name,
|
||||||
|
parameters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::static_data::moves::secondary_effect::SecondaryEffect;
|
||||||
|
use assert_approx_eq::assert_approx_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_secondary_effect() {
|
||||||
|
let empty = SecondaryEffect::empty();
|
||||||
|
assert_approx_eq!(empty.chance, 0.0);
|
||||||
|
assert_eq!(empty.effect_name, "");
|
||||||
|
assert_eq!(empty.parameters.len(), 0);
|
||||||
|
let set = SecondaryEffect::new(50.0, "foo".to_string(), Vec::new());
|
||||||
|
assert_approx_eq!(set.chance, 50.0);
|
||||||
|
assert_eq!(set.effect_name, "foo");
|
||||||
|
assert_eq!(set.parameters.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct AbilityIndex {
|
||||||
|
pub hidden: bool,
|
||||||
|
pub index: u8,
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
use self::super::learnable_moves::LearnableMoves;
|
||||||
|
use crate::static_data::species_data::ability_index::AbilityIndex;
|
||||||
|
use crate::static_data::statistic_set::StatisticSet;
|
||||||
|
use crate::static_data::statistics::Statistic;
|
||||||
|
use crate::utils::random::Random;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Getters, Debug)]
|
||||||
|
pub struct Form<'a> {
|
||||||
|
name: String,
|
||||||
|
height: f32,
|
||||||
|
weight: f32,
|
||||||
|
base_experience: u32,
|
||||||
|
types: Vec<u8>,
|
||||||
|
base_stats: StatisticSet<u16>,
|
||||||
|
abilities: Vec<String>,
|
||||||
|
hidden_abilities: Vec<String>,
|
||||||
|
moves: LearnableMoves<'a>,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Form<'a> {
|
||||||
|
pub fn new(
|
||||||
|
name: &str,
|
||||||
|
height: f32,
|
||||||
|
weight: f32,
|
||||||
|
base_experience: u32,
|
||||||
|
types: Vec<u8>,
|
||||||
|
base_stats: StatisticSet<u16>,
|
||||||
|
abilities: Vec<String>,
|
||||||
|
hidden_abilities: Vec<String>,
|
||||||
|
moves: LearnableMoves<'a>,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
) -> Form<'a> {
|
||||||
|
Form {
|
||||||
|
name: name.to_string(),
|
||||||
|
height,
|
||||||
|
weight,
|
||||||
|
base_experience,
|
||||||
|
types,
|
||||||
|
base_stats,
|
||||||
|
abilities,
|
||||||
|
hidden_abilities,
|
||||||
|
moves,
|
||||||
|
flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_type(&self, index: usize) -> u8 {
|
||||||
|
self.types[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_base_stat(&self, stat: Statistic) -> u16 {
|
||||||
|
self.base_stats.get_stat(stat)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_ability_index(&self, ability: &str) -> Option<AbilityIndex> {
|
||||||
|
for (index, a) in self.abilities.iter().enumerate() {
|
||||||
|
if a == ability {
|
||||||
|
return Some(AbilityIndex {
|
||||||
|
hidden: false,
|
||||||
|
index: index as u8,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, a) in self.hidden_abilities.iter().enumerate() {
|
||||||
|
if a == ability {
|
||||||
|
return Some(AbilityIndex {
|
||||||
|
hidden: true,
|
||||||
|
index: index as u8,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_random_ability(&self, rand: &mut Random) -> &String {
|
||||||
|
&self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize]
|
||||||
|
}
|
||||||
|
pub fn get_random_hidden_ability(&self, rand: &mut Random) -> &String {
|
||||||
|
&self.hidden_abilities
|
||||||
|
[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_flag(&self, key: &str) -> bool {
|
||||||
|
self.flags.contains(key)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature
|
||||||
|
// that allows for a more progressive gender system for those that want it?
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Gender {
|
||||||
|
Male,
|
||||||
|
Female,
|
||||||
|
Genderless,
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
use crate::defines::LevelInt;
|
||||||
|
use crate::static_data::moves::move_data::MoveData;
|
||||||
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Debug)]
|
||||||
|
pub struct LearnableMoves<'a> {
|
||||||
|
learned_by_level: HashMap<LevelInt, Vec<&'a MoveData>>,
|
||||||
|
distinct_level_moves: Vec<&'a MoveData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LearnableMoves<'a> {
|
||||||
|
pub fn new() -> LearnableMoves<'a> {
|
||||||
|
LearnableMoves::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_level_move(&mut self, level: LevelInt, m: &'a MoveData) {
|
||||||
|
match self.learned_by_level.entry(level) {
|
||||||
|
Occupied(x) => {
|
||||||
|
x.into_mut().push(m);
|
||||||
|
}
|
||||||
|
Vacant(_) => {
|
||||||
|
self.learned_by_level.insert(level, vec![m]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.distinct_level_moves.contains(&m) {
|
||||||
|
self.distinct_level_moves.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<&'a MoveData>> {
|
||||||
|
self.learned_by_level.get(&level)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_distinct_level_moves(&self) -> &Vec<&'a MoveData> {
|
||||||
|
&self.distinct_level_moves
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::static_data::moves::move_data::{MoveCategory, MoveData, MoveTarget};
|
||||||
|
use crate::static_data::moves::secondary_effect::SecondaryEffect;
|
||||||
|
use crate::static_data::species_data::learnable_moves::LearnableMoves;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_level_moves() {
|
||||||
|
let mut moves = LearnableMoves::new();
|
||||||
|
let move1 = MoveData::new(
|
||||||
|
"foo".to_string(),
|
||||||
|
0,
|
||||||
|
MoveCategory::Physical,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
MoveTarget::Adjacent,
|
||||||
|
0,
|
||||||
|
SecondaryEffect::empty(),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
let move2 = MoveData::new(
|
||||||
|
"bar".to_string(),
|
||||||
|
0,
|
||||||
|
MoveCategory::Physical,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
MoveTarget::Adjacent,
|
||||||
|
0,
|
||||||
|
SecondaryEffect::empty(),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
moves.add_level_move(1, &move1);
|
||||||
|
moves.add_level_move(1, &move2);
|
||||||
|
|
||||||
|
let m = moves.get_learned_by_level(1u8).unwrap();
|
||||||
|
assert_eq!(m.len(), 2);
|
||||||
|
assert_eq!(m[0], &move1);
|
||||||
|
assert_eq!(m[1], &move2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_two_same_moves_at_different_level() {
|
||||||
|
let mut moves = LearnableMoves::new();
|
||||||
|
let move1 = MoveData::new(
|
||||||
|
"foo".to_string(),
|
||||||
|
0,
|
||||||
|
MoveCategory::Physical,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
MoveTarget::Adjacent,
|
||||||
|
0,
|
||||||
|
SecondaryEffect::empty(),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
moves.add_level_move(1, &move1);
|
||||||
|
moves.add_level_move(5, &move1);
|
||||||
|
|
||||||
|
let m = moves.get_learned_by_level(1u8).unwrap();
|
||||||
|
assert_eq!(m.len(), 1);
|
||||||
|
assert_eq!(m[0], &move1);
|
||||||
|
let m2 = moves.get_learned_by_level(5u8).unwrap();
|
||||||
|
assert_eq!(m2.len(), 1);
|
||||||
|
assert_eq!(m2[0], &move1);
|
||||||
|
let distinct = moves.get_distinct_level_moves();
|
||||||
|
assert_eq!(distinct.len(), 1);
|
||||||
|
assert_eq!(distinct[0], &move1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod ability_index;
|
||||||
|
pub mod form;
|
||||||
|
pub mod gender;
|
||||||
|
pub mod learnable_moves;
|
||||||
|
pub mod species;
|
|
@ -0,0 +1,62 @@
|
||||||
|
use self::super::form::Form;
|
||||||
|
use crate::static_data::species_data::gender::Gender;
|
||||||
|
use crate::utils::random::Random;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
#[derive(Debug, Getters)]
|
||||||
|
pub struct Species<'a> {
|
||||||
|
id: u16,
|
||||||
|
name: String,
|
||||||
|
gender_rate: f32,
|
||||||
|
growth_rate: String,
|
||||||
|
capture_rate: u8,
|
||||||
|
forms: HashMap<String, Form<'a>>,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Species<'a> {
|
||||||
|
pub fn new(
|
||||||
|
id: u16,
|
||||||
|
name: &str,
|
||||||
|
gender_rate: f32,
|
||||||
|
growth_rate: &str,
|
||||||
|
capture_rate: u8,
|
||||||
|
default_form: Form<'a>,
|
||||||
|
flags: HashSet<String>,
|
||||||
|
) -> Species<'a> {
|
||||||
|
Species {
|
||||||
|
id,
|
||||||
|
name: name.to_string(),
|
||||||
|
gender_rate,
|
||||||
|
growth_rate: growth_rate.to_string(),
|
||||||
|
capture_rate,
|
||||||
|
forms: hashmap! {
|
||||||
|
"default".to_string() => default_form,
|
||||||
|
},
|
||||||
|
flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_form(&mut self, id: String, form: Form<'a>) {
|
||||||
|
self.forms.insert(id, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_form(&self, id: &str) -> Option<&Form> {
|
||||||
|
self.forms.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_random_gender(&self, rand: &mut Random) -> Gender {
|
||||||
|
if self.gender_rate < 0.0 {
|
||||||
|
Gender::Genderless
|
||||||
|
} else if rand.get_float() >= self.gender_rate {
|
||||||
|
Gender::Female
|
||||||
|
} else {
|
||||||
|
Gender::Male
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_flag(&self, key: &str) -> bool {
|
||||||
|
self.flags.contains(key)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
use super::statistics::Statistic;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use num_traits::PrimInt;
|
||||||
|
|
||||||
|
#[derive(Default, Eq, PartialEq, Debug, Getters)]
|
||||||
|
pub struct StatisticSet<T>
|
||||||
|
where
|
||||||
|
T: PrimInt,
|
||||||
|
{
|
||||||
|
hp: T,
|
||||||
|
attack: T,
|
||||||
|
defense: T,
|
||||||
|
special_attack: T,
|
||||||
|
special_defense: T,
|
||||||
|
speed: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> StatisticSet<T>
|
||||||
|
where
|
||||||
|
T: PrimInt,
|
||||||
|
{
|
||||||
|
pub fn get_stat(&self, stat: Statistic) -> T {
|
||||||
|
match stat {
|
||||||
|
Statistic::HP => self.hp,
|
||||||
|
Statistic::Attack => self.attack,
|
||||||
|
Statistic::Defense => self.defense,
|
||||||
|
Statistic::SpecialAttack => self.special_attack,
|
||||||
|
Statistic::SpecialDefense => self.special_defense,
|
||||||
|
Statistic::Speed => self.speed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_stat(&mut self, stat: Statistic, value: T) {
|
||||||
|
match stat {
|
||||||
|
Statistic::HP => self.hp = value,
|
||||||
|
Statistic::Attack => self.attack = value,
|
||||||
|
Statistic::Defense => self.defense = value,
|
||||||
|
Statistic::SpecialAttack => self.special_attack = value,
|
||||||
|
Statistic::SpecialDefense => self.special_defense = value,
|
||||||
|
Statistic::Speed => self.speed = value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increase_stat(&mut self, stat: Statistic, value: T) {
|
||||||
|
match stat {
|
||||||
|
Statistic::HP => self.hp = self.hp + value,
|
||||||
|
Statistic::Attack => self.attack = self.attack + value,
|
||||||
|
Statistic::Defense => self.defense = self.defense + value,
|
||||||
|
Statistic::SpecialAttack => self.special_attack = self.special_attack + value,
|
||||||
|
Statistic::SpecialDefense => self.special_defense = self.special_defense + value,
|
||||||
|
Statistic::Speed => self.speed = self.speed + value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrease_stat(&mut self, stat: Statistic, value: T) {
|
||||||
|
match stat {
|
||||||
|
Statistic::HP => self.hp = self.hp - value,
|
||||||
|
Statistic::Attack => self.attack = self.attack - value,
|
||||||
|
Statistic::Defense => self.defense = self.defense - value,
|
||||||
|
Statistic::SpecialAttack => self.special_attack = self.special_attack - value,
|
||||||
|
Statistic::SpecialDefense => self.special_defense = self.special_defense - value,
|
||||||
|
Statistic::Speed => self.speed = self.speed - value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Statistic {
|
||||||
|
HP,
|
||||||
|
Attack,
|
||||||
|
Defense,
|
||||||
|
SpecialAttack,
|
||||||
|
SpecialDefense,
|
||||||
|
Speed,
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod random;
|
|
@ -0,0 +1,179 @@
|
||||||
|
use rand::distributions::{Distribution, Uniform};
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use rand_pcg::Pcg32;
|
||||||
|
|
||||||
|
pub struct Random {
|
||||||
|
seed: u128,
|
||||||
|
distribution: Uniform<f64>,
|
||||||
|
random_gen: Pcg32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Random {
|
||||||
|
fn default() -> Self {
|
||||||
|
let seed = chrono::Utc::now().timestamp_nanos() as u128;
|
||||||
|
Random {
|
||||||
|
seed,
|
||||||
|
distribution: Uniform::from(0.0..1.0),
|
||||||
|
random_gen: Pcg32::from_seed(seed.to_be_bytes()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Random {
|
||||||
|
pub fn new(seed: u128) -> Self {
|
||||||
|
Random {
|
||||||
|
seed,
|
||||||
|
distribution: Uniform::from(0.0..1.0),
|
||||||
|
random_gen: Pcg32::from_seed(seed.to_be_bytes()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_seed(&self) -> u128 {
|
||||||
|
self.seed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self) -> i32 {
|
||||||
|
self.random_gen.gen()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max(&mut self, max: i32) -> i32 {
|
||||||
|
assert!(max > 0);
|
||||||
|
Uniform::from(0..max).sample(&mut self.random_gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_between(&mut self, min: i32, max: i32) -> i32 {
|
||||||
|
assert!(max > min);
|
||||||
|
Uniform::from(min..max).sample(&mut self.random_gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_unsigned(&mut self) -> u32 {
|
||||||
|
self.random_gen.gen()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_unsigned(&mut self, max: u32) -> u32 {
|
||||||
|
assert!(max > 0);
|
||||||
|
Uniform::from(0..max).sample(&mut self.random_gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_between_unsigned(&mut self, min: u32, max: u32) -> u32 {
|
||||||
|
assert!(max > min);
|
||||||
|
Uniform::from(min..max).sample(&mut self.random_gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_float(&mut self) -> f32 {
|
||||||
|
self.get_double() as f32
|
||||||
|
}
|
||||||
|
pub fn get_double(&mut self) -> f64 {
|
||||||
|
self.distribution.sample(&mut self.random_gen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::utils::random::Random;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_random() {
|
||||||
|
let _default = Random::default();
|
||||||
|
let _empty = Random::new(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_random_with_seed() {
|
||||||
|
let mut v = Random::new(10);
|
||||||
|
assert_eq!(v.get(), 1755576946);
|
||||||
|
assert_eq!(v.get(), 1254514019);
|
||||||
|
assert_eq!(v.get(), 1735834837);
|
||||||
|
assert_eq!(v.get(), 51079449);
|
||||||
|
assert_eq!(v.get(), 506997516);
|
||||||
|
assert_eq!(v.get(), -173527621);
|
||||||
|
assert_eq!(v.get(), 683138464);
|
||||||
|
assert_eq!(v.get(), 580236580);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_random_with_limit_with_seed() {
|
||||||
|
let mut v = Random::new(10);
|
||||||
|
assert_eq!(v.get_max(10), 4);
|
||||||
|
assert_eq!(v.get_max(10), 2);
|
||||||
|
assert_eq!(v.get_max(10), 4);
|
||||||
|
assert_eq!(v.get_max(10), 0);
|
||||||
|
assert_eq!(v.get_max(10), 1);
|
||||||
|
assert_eq!(v.get_max(10), 9);
|
||||||
|
assert_eq!(v.get_max(10), 1);
|
||||||
|
assert_eq!(v.get_max(10), 1);
|
||||||
|
|
||||||
|
assert_eq!(v.get_max(2), 0);
|
||||||
|
assert_eq!(v.get_max(2), 1);
|
||||||
|
assert_eq!(v.get_max(2), 1);
|
||||||
|
assert_eq!(v.get_max(2), 1);
|
||||||
|
assert_eq!(v.get_max(2), 0);
|
||||||
|
assert_eq!(v.get_max(2), 1);
|
||||||
|
assert_eq!(v.get_max(2), 0);
|
||||||
|
assert_eq!(v.get_max(2), 0);
|
||||||
|
assert_eq!(v.get_max(2), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_random_with_limit_with_range() {
|
||||||
|
let mut v = Random::new(10);
|
||||||
|
assert_eq!(v.get_between(10, 30), 18);
|
||||||
|
assert_eq!(v.get_between(10, 30), 15);
|
||||||
|
assert_eq!(v.get_between(10, 30), 18);
|
||||||
|
assert_eq!(v.get_between(10, 30), 10);
|
||||||
|
assert_eq!(v.get_between(10, 30), 12);
|
||||||
|
assert_eq!(v.get_between(10, 30), 29);
|
||||||
|
assert_eq!(v.get_between(10, 30), 13);
|
||||||
|
assert_eq!(v.get_between(10, 30), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_random_distribution() {
|
||||||
|
let mut v = Random::new(10);
|
||||||
|
const AMOUNT: usize = 100_000;
|
||||||
|
|
||||||
|
let mut arr: [i32; AMOUNT] = [0; AMOUNT];
|
||||||
|
for i in 0..AMOUNT {
|
||||||
|
arr[i] = v.get_between(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut num_zeros = 0;
|
||||||
|
let mut num_ones = 0;
|
||||||
|
for v in arr.iter() {
|
||||||
|
if *v == 0 {
|
||||||
|
num_zeros += 1;
|
||||||
|
} else if *v == 1 {
|
||||||
|
num_ones += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let div = num_zeros as f32 / num_ones as f32;
|
||||||
|
assert_approx_eq::assert_approx_eq!(div, 1.0, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_random_distribution_0_to_3() {
|
||||||
|
let mut v = Random::new(10);
|
||||||
|
const AMOUNT: usize = 100_000;
|
||||||
|
|
||||||
|
let mut arr: [i32; AMOUNT] = [0; AMOUNT];
|
||||||
|
for i in 0..AMOUNT {
|
||||||
|
arr[i] = v.get_between(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut num_zeros = 0;
|
||||||
|
let mut num_ones = 0;
|
||||||
|
let mut num_twos = 0;
|
||||||
|
for v in arr.iter() {
|
||||||
|
if *v == 0 {
|
||||||
|
num_zeros += 1;
|
||||||
|
} else if *v == 1 {
|
||||||
|
num_ones += 1;
|
||||||
|
} else if *v == 2 {
|
||||||
|
num_twos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_ones as f32, 1.0, 0.01);
|
||||||
|
assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_twos as f32, 1.0, 0.01);
|
||||||
|
assert_approx_eq::assert_approx_eq!(num_ones as f32 / num_twos as f32, 1.0, 0.01);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue