204 lines
4.7 KiB
Rust
204 lines
4.7 KiB
Rust
use crate::error::Error;
|
|
use crate::user::UserError;
|
|
use j_db::database::Database;
|
|
use j_db::error::JDbError;
|
|
use j_db::model::JdbModel;
|
|
use poise::serenity_prelude::{Emoji, EmojiId, MessageBuilder, UserId};
|
|
use rand::{rng, Rng};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fmt::{Display, Formatter};
|
|
|
|
pub const RACE_SIZE: usize = 25;
|
|
const BASE_SPEED: f32 = 0.5;
|
|
pub const NUMBER_OF_RACERS: usize = 5;
|
|
|
|
#[derive(Debug)]
|
|
pub enum RaceError {
|
|
RacerNotFound,
|
|
BetFundError(UserError),
|
|
UserHasBet,
|
|
GenericError(Error),
|
|
}
|
|
|
|
impl Display for RaceError {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
RaceError::RacerNotFound => write!(f, "Racer not found"),
|
|
RaceError::BetFundError(e) => write!(f, "Fund error: {}", e),
|
|
RaceError::UserHasBet => write!(f, "User already bet"),
|
|
RaceError::GenericError(e) => write!(f, "Sorry the horse died: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<UserError> for RaceError {
|
|
fn from(value: UserError) -> Self {
|
|
Self::BetFundError(value)
|
|
}
|
|
}
|
|
|
|
impl From<Error> for RaceError {
|
|
fn from(value: Error) -> Self {
|
|
Self::GenericError(value)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for RaceError {}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct Bet {
|
|
id: Option<u64>,
|
|
pub emoji: EmojiId,
|
|
pub amount: u32,
|
|
pub author: UserId,
|
|
}
|
|
|
|
impl JdbModel for Bet {
|
|
fn id(&self) -> Option<u64> {
|
|
self.id
|
|
}
|
|
|
|
fn set_id(&mut self, id: u64) {
|
|
self.id = Some(id)
|
|
}
|
|
|
|
fn tree() -> String {
|
|
"Bet".to_string()
|
|
}
|
|
}
|
|
|
|
impl Bet {
|
|
pub fn new(emoji: EmojiId, amount: u32, author: UserId) -> Self {
|
|
Self {
|
|
id: None,
|
|
emoji,
|
|
amount,
|
|
author,
|
|
}
|
|
}
|
|
|
|
pub fn add_bet(db: &Database, bet: Self) -> Result<Self, Error> {
|
|
Ok(db.insert(bet)?)
|
|
}
|
|
|
|
pub fn get_bets(db: &Database) -> Result<Vec<Self>, Error> {
|
|
Ok(db.filter(|_, _bet: &Self| true)?.collect())
|
|
}
|
|
|
|
pub fn clear_bet(db: &Database) -> Result<(), Error> {
|
|
let bets = Self::get_bets(db)?;
|
|
|
|
for bet in &bets {
|
|
db.remove::<Self>(bet.id().unwrap())?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn has_user_bet(db: &Database, user: UserId) -> Result<bool, Error> {
|
|
Ok(db
|
|
.filter(|_, bet: &Self| bet.author == user)?
|
|
.next()
|
|
.is_some())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct Racer {
|
|
id: Option<u64>,
|
|
pub emoji: Emoji,
|
|
pub speed: f32,
|
|
pub pos: f32,
|
|
}
|
|
|
|
impl PartialEq<Self> for Racer {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.emoji.id == other.emoji.id
|
|
}
|
|
}
|
|
|
|
impl Eq for Racer {}
|
|
|
|
impl Racer {
|
|
pub fn new(emoji: Emoji) -> Self {
|
|
let genetic_stat = (emoji.id.get() & 0xff) as f32 / 255.0;
|
|
let random_stat = rng().random_range(0.0..0.5);
|
|
let speed = BASE_SPEED + (0.50 * genetic_stat) + (0.50 * random_stat);
|
|
|
|
Racer {
|
|
id: None,
|
|
emoji,
|
|
speed,
|
|
pos: 0.0,
|
|
}
|
|
}
|
|
|
|
pub fn update_pos(&mut self) {
|
|
let speed_boost = rng().gen_range(-0.75..0.75);
|
|
self.pos += self.speed + (self.speed * speed_boost);
|
|
|
|
self.pos = self.pos.clamp(0.0, RACE_SIZE as f32);
|
|
}
|
|
|
|
pub fn get_race_pos(&self) -> usize {
|
|
self.pos.floor() as usize
|
|
}
|
|
|
|
pub fn add_racer(db: &Database, emoji: Emoji) -> Result<Self, Error> {
|
|
let racer = Self::new(emoji);
|
|
let racer = db.insert(racer)?;
|
|
Ok(racer)
|
|
}
|
|
|
|
pub fn get_racers(db: &Database) -> Result<Vec<Self>, Error> {
|
|
Ok(db.filter(|_, _racer: &Self| true)?.collect())
|
|
}
|
|
|
|
pub fn clear_racers(db: &Database) -> Result<(), Error> {
|
|
let racers = Self::get_racers(db)?;
|
|
|
|
for racer in &racers {
|
|
db.remove::<Self>(racer.id().unwrap())?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl JdbModel for Racer {
|
|
fn id(&self) -> Option<u64> {
|
|
self.id
|
|
}
|
|
|
|
fn set_id(&mut self, id: u64) {
|
|
self.id = Some(id)
|
|
}
|
|
|
|
fn tree() -> String {
|
|
"Racer".to_string()
|
|
}
|
|
}
|
|
|
|
pub fn draw_race(racers: &Vec<Racer>) -> String {
|
|
let mut msg = MessageBuilder::default();
|
|
|
|
for racer in racers {
|
|
let mut race_line = MessageBuilder::new();
|
|
race_line.push("|");
|
|
race_line.push("=".repeat(racer.get_race_pos()));
|
|
race_line.push("E");
|
|
|
|
if race_line.0.len() < RACE_SIZE {
|
|
let padding_needed = RACE_SIZE - race_line.0.len() - 1;
|
|
race_line.push("=".repeat(padding_needed));
|
|
race_line.push("🔳");
|
|
}
|
|
|
|
let line = race_line.build();
|
|
let line = line.replace('E', &racer.emoji.to_string());
|
|
msg.push_line(line);
|
|
}
|
|
|
|
msg.build()
|
|
}
|