mirror of https://github.com/Paoda/blue-gacha.git
Initial Commit
This commit is contained in:
commit
4962ee3d79
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,175 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "ba-gacha"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.123"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.123"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.62"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_repr"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "ba-gacha"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Rekai Musuka <rekai@musuka.dev>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "^0.8"
|
||||||
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
|
serde_json = "^1.0"
|
||||||
|
serde_repr = "^0.1"
|
|
@ -0,0 +1,121 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use rand::{prelude::SliceRandom, Rng};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
gacha::Rarity,
|
||||||
|
student::{self, Student},
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
gacha::{Gacha, Recruitment},
|
||||||
|
i18n::{I18nString, Language},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct BannerBuilder {
|
||||||
|
name: I18nString,
|
||||||
|
rates: Option<Gacha>,
|
||||||
|
sparkable: Option<Vec<Student>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BannerBuilder {
|
||||||
|
pub fn new(jpn_name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: I18nString::new(jpn_name),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_name_translation(mut self, language: Language, name: &str) -> Self {
|
||||||
|
self.name.update(language, name);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_rates(self, rates: &Gacha) -> Self {
|
||||||
|
Self {
|
||||||
|
rates: Some(rates.to_owned()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_sparkable_students(self, students: Vec<Student>) -> Self {
|
||||||
|
Self {
|
||||||
|
sparkable: Some(students),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(self) -> Option<Banner> {
|
||||||
|
Some(Banner {
|
||||||
|
name: self.name,
|
||||||
|
rates: self.rates?,
|
||||||
|
sparkable: self.sparkable?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Banner {
|
||||||
|
pub name: I18nString,
|
||||||
|
rates: Gacha,
|
||||||
|
sparkable: Vec<Student>, // FIXME: Can we safely assume this will only contain 2 studnets?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Banner {
|
||||||
|
fn get_random_student(&self, rarity: Rarity) -> Student {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let mut filtered_students: Vec<&Student> = self
|
||||||
|
.rates
|
||||||
|
.pool
|
||||||
|
.iter()
|
||||||
|
.filter(|student| student.rarity == rarity)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
filtered_students.shuffle(&mut rng);
|
||||||
|
let index: usize = rng.gen_range(0..filtered_students.len());
|
||||||
|
|
||||||
|
filtered_students[index].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_random_rarity(&self) -> Rarity {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let random_num: usize = rng.gen_range(0..1000);
|
||||||
|
|
||||||
|
let one_rate = self.rates.get_rate(Rarity::One);
|
||||||
|
let two_rate = self.rates.get_rate(Rarity::Two);
|
||||||
|
let three_rate = self.rates.get_rate(Rarity::Three);
|
||||||
|
|
||||||
|
if random_num < three_rate {
|
||||||
|
Rarity::Three
|
||||||
|
} else if random_num < three_rate + two_rate {
|
||||||
|
Rarity::Two
|
||||||
|
} else {
|
||||||
|
Rarity::One
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Recruitment for Banner {
|
||||||
|
fn roll(&self) -> Student {
|
||||||
|
self.get_random_student(self.get_random_rarity())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roll10(&self) -> [Student; 10] {
|
||||||
|
let mut students: [Student; 10] = vec![Default::default(); 10].try_into().unwrap();
|
||||||
|
|
||||||
|
// Fill students with 10 random students
|
||||||
|
for student in students.iter_mut() {
|
||||||
|
*student = self.get_random_student(self.get_random_rarity());
|
||||||
|
}
|
||||||
|
|
||||||
|
let two_star_present = students.iter().any(|student| student.rarity == Rarity::Two);
|
||||||
|
|
||||||
|
if !two_star_present {
|
||||||
|
if students[students.len() - 1].rarity != Rarity::Three {
|
||||||
|
students[students.len() - 1] = self.get_random_student(Rarity::Two);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
students
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
use crate::student::Student;
|
||||||
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
/// The Available Rarities in Blue Archive's Gacha System
|
||||||
|
pub enum Rarity {
|
||||||
|
One = 1,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Rarity {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Rarity::One => f.write_str("1★"),
|
||||||
|
Rarity::Two => f.write_str("2★"),
|
||||||
|
Rarity::Three => f.write_str("3★"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Rarity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::One
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recruitment is a trait that consists of two methods
|
||||||
|
/// Representing single and 10-rolls
|
||||||
|
///
|
||||||
|
/// Every Banner is expected to implement the Recruitment trait
|
||||||
|
pub trait Recruitment {
|
||||||
|
fn roll(&self) -> Student;
|
||||||
|
fn roll10(&self) -> [Student; 10];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gacha Structs are built using this GachaBuilder Class
|
||||||
|
///
|
||||||
|
/// By Default, GachaBuilder assumes the rates:
|
||||||
|
/// * 1★: 79.0%
|
||||||
|
/// * 2★: 18.5%
|
||||||
|
/// * 3★: 2.5%
|
||||||
|
pub struct GachaBuilder {
|
||||||
|
rates: Option<(usize, usize, usize)>,
|
||||||
|
pool: Option<Vec<Student>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GachaBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
rates: Some((790, 185, 25)),
|
||||||
|
pool: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GachaBuilder {
|
||||||
|
/// Creates a new instance of a GachaBuilder.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `one` - The percent chance of pulling a 1★ Student
|
||||||
|
/// * `two` - The percent chance of pulling a 2★ Student
|
||||||
|
/// * `three` - The percent chance of pulling a 3★ Student
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use ba_gacha::gacha::GachaBuilder;
|
||||||
|
/// let gacha_builder: GachaBuilder = GachaBuilder::new(79.0, 18.5, 2.5)
|
||||||
|
/// .with_pool(Vec::new());
|
||||||
|
/// ```
|
||||||
|
pub fn new(one: f32, two: f32, three: f32) -> Self {
|
||||||
|
let one = (one * 10.0) as usize;
|
||||||
|
let two = (two * 10.0) as usize;
|
||||||
|
let three = (three * 10.0) as usize;
|
||||||
|
|
||||||
|
assert_eq!(one + two + three, 1000);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
rates: Some((one, two, three)),
|
||||||
|
pool: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attaches a Student Gacha Pool to the GachaBuilder
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `students` - A Vector containing every single pullable student in the Gacha
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use ba_gacha::gacha::{GachaBuilder, Rarity};
|
||||||
|
/// # use ba_gacha::student::Student;
|
||||||
|
///
|
||||||
|
/// let aru = Student::new("アル", Rarity::Three);
|
||||||
|
/// let hina = Student::new("ヒナ", Rarity::Three);
|
||||||
|
/// let gacha_builder = GachaBuilder::default().with_pool(vec![aru, hina]);
|
||||||
|
/// ```
|
||||||
|
pub fn with_pool(self, students: Vec<Student>) -> Self {
|
||||||
|
Self {
|
||||||
|
pool: Some(students),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes a GachaBuilder and retuns a Gacha Struct.
|
||||||
|
///
|
||||||
|
/// Will return `None` if the `rates` or `pool` property of
|
||||||
|
/// GachaBuilder have not been set.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use ba_gacha::gacha::{GachaBuilder, Gacha, Rarity};
|
||||||
|
/// # use ba_gacha::student::Student;
|
||||||
|
/// let aru = Student::new("アル", Rarity::Three);
|
||||||
|
/// let hina = Student::new("ヒナ", Rarity::Three);
|
||||||
|
/// let gacha = GachaBuilder::default()
|
||||||
|
/// .with_pool(vec![aru, hina])
|
||||||
|
/// .finish().unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn finish(self) -> Option<Gacha> {
|
||||||
|
Some(Gacha {
|
||||||
|
rates: self.rates?,
|
||||||
|
pool: self.pool?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides the necessary information to facilitate a "pull", which is
|
||||||
|
/// to randomly select a Student from the gacha pool
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct Gacha {
|
||||||
|
pub rates: (usize, usize, usize), // One Star, Two Star, Three Star
|
||||||
|
pub pool: Vec<Student>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gacha {
|
||||||
|
/// Returns a usize representing the percent chance of pulling a specific rarity
|
||||||
|
/// (in terms of 1000)
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `rarity` - The Rarity who's gacha pull rate will be returned
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use ba_gacha::gacha::{GachaBuilder, Rarity};
|
||||||
|
/// let gacha = GachaBuilder::new(79.0, 18.5, 2.5)
|
||||||
|
/// .with_pool(Vec::new())
|
||||||
|
/// .finish()
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// assert_eq!(gacha.get_rate(Rarity::One), 790);
|
||||||
|
/// ```
|
||||||
|
pub fn get_rate(&self, rarity: Rarity) -> usize {
|
||||||
|
match rarity {
|
||||||
|
Rarity::One => self.rates.0,
|
||||||
|
Rarity::Two => self.rates.1,
|
||||||
|
Rarity::Three => self.rates.2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// A String-like Type which allows for easy Internationalization
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct I18nString {
|
||||||
|
translations: HashMap<Language, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for I18nString {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.translations.get(&Language::Japanese).unwrap().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for I18nString {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut map: HashMap<Language, String> = Default::default();
|
||||||
|
let _ = map.insert(Language::Japanese, "".to_string());
|
||||||
|
Self { translations: map }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I18nString {
|
||||||
|
/// Blue Archive is a Japanese Game, therefore I18nString's constructor
|
||||||
|
/// expects Japanese text.
|
||||||
|
pub fn new(jpn_message: &str) -> Self {
|
||||||
|
let mut map: HashMap<Language, String> = Default::default();
|
||||||
|
let _ = map.insert(Language::Japanese, jpn_message.to_string());
|
||||||
|
|
||||||
|
Self { translations: map }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method wraps HashMap's insert, which means that:
|
||||||
|
/// * If the Language doesn't already have a translation, one will be added
|
||||||
|
/// * If a translation for the Language already exists, it will be replaced.
|
||||||
|
pub fn update(&mut self, language: Language, message: &str) {
|
||||||
|
let _ = self.translations.insert(language, message.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Language's translation as an Owned String
|
||||||
|
///
|
||||||
|
/// Will return None if there is no translation for the given language
|
||||||
|
pub fn get(&self, language: Language) -> Option<String> {
|
||||||
|
self.translations
|
||||||
|
.get(&language)
|
||||||
|
.map(|message| message.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This enum represents all Languages this Gacha Simulator **must** support.
|
||||||
|
///
|
||||||
|
/// This enum follows ISO-639-2/T
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum Language {
|
||||||
|
#[serde(rename = "eng")]
|
||||||
|
English,
|
||||||
|
#[serde(rename = "jpn")]
|
||||||
|
Japanese,
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod banner;
|
||||||
|
pub mod gacha;
|
||||||
|
pub mod i18n;
|
||||||
|
pub mod student;
|
|
@ -0,0 +1,174 @@
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use ba_gacha::{
|
||||||
|
banner::BannerBuilder,
|
||||||
|
gacha::{GachaBuilder, Rarity, Recruitment},
|
||||||
|
i18n::Language,
|
||||||
|
student::Student,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Create a Struct representing Shiroko
|
||||||
|
let mut shiroko = Student::new("シロコ", Rarity::Three);
|
||||||
|
shiroko.add_translation(Language::English, "Shiroko"); // Yes, this is a translation.
|
||||||
|
|
||||||
|
// Create a Struct representing Hoshino
|
||||||
|
let mut hoshino = Student::new("ホシノ", Rarity::Three);
|
||||||
|
hoshino.add_translation(Language::English, "Hoshino");
|
||||||
|
|
||||||
|
// Create a Vector Containing all the Sparkable Students
|
||||||
|
let sparkable_students = vec![shiroko, hoshino];
|
||||||
|
|
||||||
|
let rates = GachaBuilder::new(79.0, 18.5, 2.5)
|
||||||
|
.with_pool(pickup_banner_2021_02_11())
|
||||||
|
.finish()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Create the Banner Itself
|
||||||
|
let pickup_banner = BannerBuilder::new("ピックアップ募集")
|
||||||
|
.with_name_translation(Language::English, "Priority")
|
||||||
|
.with_sparkable_students(sparkable_students)
|
||||||
|
.with_rates(&rates)
|
||||||
|
.finish()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Roll Banner 1 Time
|
||||||
|
let student = pickup_banner.roll();
|
||||||
|
|
||||||
|
// Roll Banner 10 Times
|
||||||
|
let students = pickup_banner.roll10();
|
||||||
|
|
||||||
|
// Display Results
|
||||||
|
println!("{} Single: {}", pickup_banner.name, student);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
println!("{} 10-roll:", pickup_banner.name);
|
||||||
|
for student in students.iter() {
|
||||||
|
println!("{}", student);
|
||||||
|
}
|
||||||
|
|
||||||
|
// let mut file = std::fs::File::create("students.json").unwrap();
|
||||||
|
// let students = all_students();
|
||||||
|
// let json = serde_json::to_string(&students).unwrap();
|
||||||
|
// file.write_all(json.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_student(jpn_name: &str, eng_name: &str, rarity: Rarity) -> Student {
|
||||||
|
let mut student = Student::new(jpn_name, rarity);
|
||||||
|
student.add_translation(Language::English, eng_name);
|
||||||
|
|
||||||
|
student
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pickup_banner_2021_02_11() -> Vec<Student> {
|
||||||
|
let mut test: Vec<Student> = vec![];
|
||||||
|
|
||||||
|
// 3 Star
|
||||||
|
test.push(create_student("ヒナ", "Hina", Rarity::Three));
|
||||||
|
test.push(create_student("イオリ", "Iori", Rarity::Three));
|
||||||
|
test.push(create_student("ハルナ", "Haruna", Rarity::Three));
|
||||||
|
test.push(create_student("イズミ", "Izumi", Rarity::Three));
|
||||||
|
test.push(create_student("アル", "Aru", Rarity::Three));
|
||||||
|
test.push(create_student("スミレ", "Sumire", Rarity::Three));
|
||||||
|
test.push(create_student("エイミ", "Iemi", Rarity::Three));
|
||||||
|
test.push(create_student("カリン", "Karin", Rarity::Three));
|
||||||
|
test.push(create_student("ネル", "Neru", Rarity::Three));
|
||||||
|
test.push(create_student("マキ", "Maki", Rarity::Three));
|
||||||
|
test.push(create_student("ヒビキ", "Hibiki", Rarity::Three));
|
||||||
|
test.push(create_student("サヤ", "Saya", Rarity::Three));
|
||||||
|
test.push(create_student("シュン", "Shun", Rarity::Three));
|
||||||
|
test.push(create_student("シロコ", "Shiroko", Rarity::Three));
|
||||||
|
test.push(create_student("ホシノ", "Hoshino", Rarity::Three));
|
||||||
|
test.push(create_student("ヒフミ", "Hifumi", Rarity::Three));
|
||||||
|
test.push(create_student("ツルギ", "Tsurugi", Rarity::Three));
|
||||||
|
|
||||||
|
// Two Star
|
||||||
|
test.push(create_student("アカリ", "Akari", Rarity::Two));
|
||||||
|
test.push(create_student("ジュンコ", "Junko", Rarity::Two));
|
||||||
|
test.push(create_student("ムツキ", "Mutsuki", Rarity::Two));
|
||||||
|
test.push(create_student("カヨコ", "Kayoko", Rarity::Two));
|
||||||
|
test.push(create_student("フウカ", "Fuuka", Rarity::Two));
|
||||||
|
test.push(create_student("ユウカ", "Yuuka", Rarity::Two));
|
||||||
|
test.push(create_student("アカネ", "Akane", Rarity::Two));
|
||||||
|
test.push(create_student("ハル", "Haru", Rarity::Two));
|
||||||
|
test.push(create_student("ウタハ", "Utaha", Rarity::Two));
|
||||||
|
test.push(create_student("チセ", "Chise", Rarity::Two));
|
||||||
|
test.push(create_student("ツバキ", "Tsubaki", Rarity::Two));
|
||||||
|
test.push(create_student("セリカ", "Serika", Rarity::Two));
|
||||||
|
test.push(create_student("アヤネ", "Ayane", Rarity::Two));
|
||||||
|
test.push(create_student("ハスミ", "Hasumi", Rarity::Two));
|
||||||
|
test.push(create_student("ハナエ", "Hanae", Rarity::Two));
|
||||||
|
test.push(create_student("アイリ", "Airi", Rarity::Two));
|
||||||
|
|
||||||
|
// 1 Star:
|
||||||
|
test.push(create_student("チナツ", "Chinatsu", Rarity::One));
|
||||||
|
test.push(create_student("ハルカ", "Haruka", Rarity::One));
|
||||||
|
test.push(create_student("ジュリ", "Juri", Rarity::One));
|
||||||
|
test.push(create_student("コタマ", "Kotama", Rarity::One));
|
||||||
|
test.push(create_student("アスナ", "Asuna", Rarity::One));
|
||||||
|
test.push(create_student("コトリ", "Kotori", Rarity::One));
|
||||||
|
test.push(create_student("フィーナ", "Pina", Rarity::One));
|
||||||
|
test.push(create_student("スズミ", "Suzumi", Rarity::One));
|
||||||
|
test.push(create_student("シミコ", "Shimiko", Rarity::One));
|
||||||
|
test.push(create_student("セリナ", "Serina", Rarity::One));
|
||||||
|
test.push(create_student("ヨシミ", "Yoshimi", Rarity::One));
|
||||||
|
|
||||||
|
test
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_students() -> Vec<Student> {
|
||||||
|
let mut students: Vec<Student> = Vec::with_capacity(45);
|
||||||
|
|
||||||
|
// 3 Star
|
||||||
|
students.push(create_student("ヒナ", "Hina", Rarity::Three));
|
||||||
|
students.push(create_student("イオリ", "Iori", Rarity::Three));
|
||||||
|
students.push(create_student("ハルナ", "Haruna", Rarity::Three));
|
||||||
|
students.push(create_student("イズミ", "Izumi", Rarity::Three));
|
||||||
|
students.push(create_student("アル", "Aru", Rarity::Three));
|
||||||
|
students.push(create_student("スミレ", "Sumire", Rarity::Three));
|
||||||
|
students.push(create_student("エイミ", "Iemi", Rarity::Three));
|
||||||
|
students.push(create_student("カリン", "Karin", Rarity::Three));
|
||||||
|
students.push(create_student("ネル", "Neru", Rarity::Three));
|
||||||
|
students.push(create_student("マキ", "Maki", Rarity::Three));
|
||||||
|
students.push(create_student("ヒビキ", "Hibiki", Rarity::Three));
|
||||||
|
students.push(create_student("サヤ", "Saya", Rarity::Three));
|
||||||
|
students.push(create_student("シュン", "Shun", Rarity::Three));
|
||||||
|
students.push(create_student("シロコ", "Shiroko", Rarity::Three));
|
||||||
|
students.push(create_student("ホシノ", "Hoshino", Rarity::Three));
|
||||||
|
students.push(create_student("ヒフミ", "Hifumi", Rarity::Three));
|
||||||
|
students.push(create_student("ツルギ", "Tsurugi", Rarity::Three));
|
||||||
|
|
||||||
|
// Two Star
|
||||||
|
students.push(create_student("アカリ", "Akari", Rarity::Two));
|
||||||
|
students.push(create_student("ジュンコ", "Junko", Rarity::Two));
|
||||||
|
students.push(create_student("ムツキ", "Mutsuki", Rarity::Two));
|
||||||
|
students.push(create_student("カヨコ", "Kayoko", Rarity::Two));
|
||||||
|
students.push(create_student("フウカ", "Fuuka", Rarity::Two));
|
||||||
|
students.push(create_student("ユウカ", "Yuuka", Rarity::Two));
|
||||||
|
students.push(create_student("アカネ", "Akane", Rarity::Two));
|
||||||
|
students.push(create_student("ハル", "Haru", Rarity::Two));
|
||||||
|
students.push(create_student("ウタハ", "Utaha", Rarity::Two));
|
||||||
|
students.push(create_student("チセ", "Chise", Rarity::Two));
|
||||||
|
students.push(create_student("ツバキ", "Tsubaki", Rarity::Two));
|
||||||
|
students.push(create_student("セリカ", "Serika", Rarity::Two));
|
||||||
|
students.push(create_student("アヤネ", "Ayane", Rarity::Two));
|
||||||
|
students.push(create_student("ハスミ", "Hasumi", Rarity::Two));
|
||||||
|
students.push(create_student("ハナエ", "Hanae", Rarity::Two));
|
||||||
|
students.push(create_student("アイリ", "Airi", Rarity::Two));
|
||||||
|
students.push(create_student("ノゾミ", "Nozomi", Rarity::Two)); // Not a part of Gacha
|
||||||
|
|
||||||
|
// 1 Star:
|
||||||
|
students.push(create_student("チナツ", "Chinatsu", Rarity::One));
|
||||||
|
students.push(create_student("ハルカ", "Haruka", Rarity::One));
|
||||||
|
students.push(create_student("ジュリ", "Juri", Rarity::One));
|
||||||
|
students.push(create_student("コタマ", "Kotama", Rarity::One));
|
||||||
|
students.push(create_student("アスナ", "Asuna", Rarity::One));
|
||||||
|
students.push(create_student("コトリ", "Kotori", Rarity::One));
|
||||||
|
students.push(create_student("フィーナ", "Pina", Rarity::One));
|
||||||
|
students.push(create_student("スズミ", "Suzumi", Rarity::One));
|
||||||
|
students.push(create_student("シミコ", "Shimiko", Rarity::One));
|
||||||
|
students.push(create_student("セリナ", "Serina", Rarity::One));
|
||||||
|
students.push(create_student("ヨシミ", "Yoshimi", Rarity::One));
|
||||||
|
|
||||||
|
students
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::gacha::Rarity;
|
||||||
|
use crate::i18n::I18nString;
|
||||||
|
use crate::i18n::Language;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
/// A Student which has just the information necessary for gacha associated with it
|
||||||
|
pub struct Student {
|
||||||
|
/// There is guaranteed to be a Japanese Name available at all times
|
||||||
|
pub name: I18nString,
|
||||||
|
pub rarity: Rarity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Student {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}\t{}", self.name, self.rarity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Student {
|
||||||
|
/// Creates a new Instance of a Student
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `jpn_name` - The name of the Student from the Japanese Version of Blue Archive
|
||||||
|
/// * `rarity` - The Rarity of the Student
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use ba_gacha::gacha::Rarity;
|
||||||
|
/// # use ba_gacha::student::Student;
|
||||||
|
///
|
||||||
|
/// let mutsuki = Student::new("ムツキ", Rarity::Two);
|
||||||
|
/// ```
|
||||||
|
pub fn new(jpn_name: &str, rarity: Rarity) -> Self {
|
||||||
|
Self {
|
||||||
|
name: I18nString::new(jpn_name),
|
||||||
|
rarity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new Translation to the internal [`I18nString`].
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `language` - The Language of the translation to be added
|
||||||
|
/// * `name` - The Translated name, in the language of the previous argument.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use ba_gacha::gacha::Rarity;
|
||||||
|
/// # use ba_gacha::student::Student;
|
||||||
|
/// # use ba_gacha::i18n::Language;
|
||||||
|
///
|
||||||
|
/// let mut mutsuki = Student::new("ムツキ", Rarity::Two);
|
||||||
|
/// mutsuki.add_translation(Language::English, "Mutsuki");
|
||||||
|
/// ```
|
||||||
|
pub fn add_translation(&mut self, language: Language, name: &str) {
|
||||||
|
self.name.update(language, name);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue