FrenBot/src/models/gogurt_reserves.rs
2025-08-04 17:56:46 -06:00

154 lines
4.0 KiB
Rust

use crate::error::Error;
use crate::user::User;
use chrono::Utc;
use j_db::database::Database;
use log::info;
use poise::serenity_prelude::UserId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::f32::consts::PI;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GogurtReserves {
id: Option<u64>,
pub reserve_contributors: HashMap<UserId, f64>,
pub gogurt_rate_per_pound: f64,
}
impl j_db::model::JdbModel for GogurtReserves {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"GogurtReserves".to_string()
}
}
impl GogurtReserves {
pub fn get_gogurt_reserve(db: &Database) -> Result<Self, Error> {
Ok(db
.filter(|_, _: &Self| true)?
.next()
.unwrap_or(Self::default()))
}
pub fn add_contribution(db: &Database, user: UserId, fc_amount: u64) -> Result<f64, Error> {
let mut gogurt_reserve = Self::get_gogurt_reserve(db)?;
let gogurt_amount = gogurt_reserve.convert_fc_to_pounds(fc_amount);
let contribution = gogurt_reserve
.reserve_contributors
.entry(user)
.or_insert_with(|| 0.0);
User::try_take_funds(db, user, fc_amount as u32)?;
*contribution += gogurt_amount;
db.insert(gogurt_reserve)?;
Ok(gogurt_amount)
}
pub fn take_contribution(
db: &Database,
user: UserId,
pounds_of_gogurt: f64,
) -> Result<u64, Error> {
let mut gogurt_reserve = Self::get_gogurt_reserve(db)?;
let contribution = gogurt_reserve
.reserve_contributors
.entry(user)
.or_insert_with(|| 0.0);
if pounds_of_gogurt.is_sign_negative() {
return Err(Error::CommandError(
"You can't take negative gogurt, stop trying".to_string(),
));
}
if pounds_of_gogurt > *contribution {
Err(Error::NotEnoughGogurt)
} else {
*contribution -= pounds_of_gogurt;
let fc_profit = gogurt_reserve.convert_pounds_to_fc(pounds_of_gogurt);
db.insert(gogurt_reserve)?;
User::give_funds(db, user, fc_profit as u32)?;
Ok(fc_profit)
}
}
pub fn get_contributors(&self) -> Vec<(UserId, f64)> {
let mut contributors: Vec<(UserId, f64)> =
self.reserve_contributors.clone().into_iter().collect();
contributors.sort_by(|(_, a), (_, b)| b.total_cmp(a));
contributors
}
pub fn get_total_amount(&self) -> f64 {
self.reserve_contributors.values().sum()
}
pub fn get_total_worth(&self) -> u64 {
(self.get_total_amount() * self.gogurt_rate_per_pound) as u64
}
pub fn convert_pounds_to_fc(&self, pounds: f64) -> u64 {
(pounds * self.gogurt_rate_per_pound) as u64
}
pub fn convert_fc_to_pounds(&self, fc: u64) -> f64 {
fc as f64 / self.gogurt_rate_per_pound
}
pub fn market_function(time: i64) -> f64 {
let x = time as f64;
(x.sin() + (PI as f64 * x).cos() + (PI as f64 * x).sin() + x.cos()
- (x * 4.0).sin()
- (x * 3.0).cos())
.clamp(0.0, 6.0)
}
pub fn calculate_new_rate(time: i64) -> f64 {
Self::market_function(time)
}
pub fn update_rate(db: &Database) -> Result<(), Error> {
let mut reserves = Self::get_gogurt_reserve(db)?;
let time = Utc::now().timestamp();
let new_rate = Self::calculate_new_rate(time);
info!("Updating rate to '{new_rate}'");
reserves.gogurt_rate_per_pound = new_rate;
db.insert(reserves)?;
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::models::gogurt_reserves::GogurtReserves;
#[test]
fn test_avg_market_function() {
let sum: f64 = (0..100000).map(GogurtReserves::market_function).sum();
let avg = sum / 100000.0;
println!("Average = {avg}");
assert!(avg < 1.0 && avg > 0.0)
}
}