use rand::seq::SliceRandom; use rhai::{ serde::{from_dynamic, to_dynamic}, Dynamic, Engine, EvalAltResult, Map, }; use std::fmt::{Debug, Display}; use crate::games::run::types::Card; use super::{ types::{CardId, Data, Player, RhaiResult, RunningPile}, }; // TODO Write somekind of documentation on functions available & stuff pub fn setup_engine() -> Engine { let mut engine = Engine::new(); engine.set_max_expr_depths(0, 0); engine.register_result_fn("shuffle", shuffle_pile); engine .on_print(|x| log::info!(target: "`Rhai::STDOUT`", "{}", x)) .on_debug(|x, _src, _pos| log::debug!(target: "`Rhai::STDOUT`", "{}", x)); // Register Player type, with its functions engine .register_type::() .register_get("val", Player::get) .register_set_result("val", Player::set) .register_result_fn("add", Player::add) .register_result_fn("sub", Player::sub) .register_fn("to_string", |p: &mut Player| p.to_string()) .register_fn("print", |p: &mut Player| p.to_string()) .register_fn("debug", |p: &mut Player| format!("{:?}", p)) .register_fn("+", |s: &str, p: Player| format!("{}{}", s, p)) .register_fn("+", |p: Player, s: &str| p.to_string().push_str(s)) .register_fn("==", |a: Player, b: Player| a.num == b.num) .register_fn("==", |a: Player, b: i64| a.num == b as u32) .register_fn("==", |a: i64, b: Player| b.num == a as u32); engine .register_type::() .register_get("kind", Card::get_kind) .register_get("uuid", Card::get_uuid) .register_fn("to_string", |c: &mut Card| c.to_string()) .register_fn("print", |c: &mut Card| c.to_string()) .register_fn("debug", |c: &mut Card| format!("{:?}", c)) .register_fn("+", |s: &str, c: Card| format!("{}{}", s, c)) .register_fn("+", |c: Card, s: &str| c.to_string().push_str(s)); engine .register_type::() // TODO add constructor for CardId .register_result_fn("get_card", get_card_from_id) .register_result_fn("pop_card", pop_card_from_id) .register_get("pile_kind", |p: &mut CardId| match p.pile_kind { super::PileKind::Owned(i) => Dynamic::from(i), super::PileKind::Common => Dynamic::from("common"), }) .register_get("pile_name", |p: &mut CardId| p.pile_name.clone()) .register_get("card_idx", |p: &mut CardId| match p.idx { super::CardIdx::Indexed(i) => Dynamic::from(i), super::CardIdx::Top => Dynamic::from("top"), super::CardIdx::Bottom => Dynamic::from("bottom"), }) .register_fn("to_string", |p: &mut CardId| p.to_string()) .register_fn("print", |p: &mut CardId| p.to_string()) .register_fn("debug", |p: &mut CardId| p.to_string()) .register_fn("+", |s: &str, p: CardId| format!("{}{}", s, p)) .register_fn("+", |p: CardId, s: &str| p.to_string().push_str(s)); engine } fn shuffle_pile(pile: Map) -> Result> { let mut pile = RunningPile::from_rhai_map(pile)?; let mut rng = rand::thread_rng(); pile.cards.shuffle(&mut rng); to_dynamic(pile) } fn get_card_from_id(data: &mut Map, card: CardId) -> Result> { let dynamic: Dynamic = >::from(data.clone()); let data: Data = from_dynamic(&dynamic)?; let pile = &match card.pile_kind { super::PileKind::Common => &data.piles, super::PileKind::Owned(i) => &data.player_piles[i as usize], }[&card.pile_name]; let card_maybe = match card.idx { super::CardIdx::Top => pile.cards.first(), super::CardIdx::Bottom => pile.cards.last(), super::CardIdx::Indexed(i) => pile.cards.get(i), }; card_maybe .map(|x| to_dynamic(x)) .unwrap_or(Ok(Dynamic::default())) } fn pop_card_from_id(data_dyn: &mut Map, card: CardId) -> Result> { let mut dynamic: Dynamic = >::from(data_dyn.clone()); let mut data: Data = from_dynamic(&dynamic)?; let pile = match card.pile_kind { super::PileKind::Common => &mut data.piles, super::PileKind::Owned(i) => &mut data.player_piles[i as usize], } .get_mut(&card.pile_name) .unwrap(); let card = match card.idx { // FIXME panics if 0 or i is out of bounds super::CardIdx::Top => to_dynamic(&pile.cards.remove(0)), super::CardIdx::Bottom => pile .cards .pop() .map(|x| to_dynamic(x)) .unwrap_or(Ok(Dynamic::default())), super::CardIdx::Indexed(i) => to_dynamic(&pile.cards.remove(i)), }; dynamic = to_dynamic(&data)?; *data_dyn = dynamic.cast(); card } struct GenericError(T); impl Debug for GenericError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.0) } } impl Display for GenericError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl std::error::Error for GenericError {} pub fn rhai_error(s: T) -> RhaiResult<()> { Err(Box::new(EvalAltResult::ErrorSystem( s.to_string(), Box::new(GenericError(s)), ))) }