diff --git a/strategies/src/lib.rs b/strategies/src/lib.rs index 303014f..389a720 100644 --- a/strategies/src/lib.rs +++ b/strategies/src/lib.rs @@ -13,9 +13,6 @@ * limitations under the License. */ -pub mod submission_macro; -pub mod utils; - #[macro_use] extern crate enum_display_derive; @@ -33,6 +30,9 @@ pub use named::Named; use crate::Move::{X, Y}; +pub mod submission_macro; +pub mod utils; + /// This is the trait that needs to be implemented and submitted pub trait Strategy: Named + Sync { /// Determines the next move for the strategy, taking into account the strategy owner's favored move. @@ -106,7 +106,7 @@ pub type ParticipantName = &'static str; pub type ParticipantPubName = &'static str; /// Represents a participant in the game. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Copy)] pub struct Participant { /// The type of the participant (e.g., System, Remote, Onsite). pub participant_type: ParticipantType, @@ -117,7 +117,7 @@ pub struct Participant { } #[allow(dead_code)] -#[derive(Clone, Debug, Display, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Display, Eq, PartialEq, Hash, Copy)] pub enum ParticipantType { System, Remote, @@ -142,7 +142,7 @@ impl Participant { impl Display for Participant { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { if let ParticipantType::System = self.participant_type { - f.write_str(format!("{}", self.participant_type).as_str()) + f.write_str(format!("{:?}", self.participant_type).as_str()) } else { f.write_str(self.pub_name.to_string().as_str()) } @@ -150,6 +150,7 @@ impl Display for Participant { } /// Struct for holding a strategy and its owner. +#[derive(Clone)] pub struct OwnedStrategy { pub owner: Participant, pub strategy: Rc>>, @@ -174,14 +175,15 @@ impl PartialEq for OwnedStrategy { fn eq(&self, other: &Self) -> bool { self.owner.eq(&other.owner) && self - .strategy - .borrow() - .name() - .eq(other.strategy.borrow().name()) + .strategy + .borrow() + .name() + .eq(other.strategy.borrow().name()) } } impl Eq for OwnedStrategy {} + impl Hash for OwnedStrategy { fn hash(&self, state: &mut H) { self.owner.hash(state); @@ -206,3 +208,42 @@ pub trait Named { fn name(&self) -> &str; } +#[cfg(test)] +mod tests { + use ParticipantType::Onsite; + + use super::*; + + #[derive(Named)] + struct MyStrategy { + pub moves: u8, + } + + impl Strategy for MyStrategy { + fn play_for_favoured_move(&mut self, favoured_move: Move) -> Move { + let m = if self.moves % 2 == 0 { favoured_move } else { favoured_move.opposite() }; + println!("Playing: {} because moves: {}", m, self.moves); + self.moves += 1; + m + } + + fn handle_last_round(&mut self, round: Round, favoured_move: Move) {} + } + + #[test] + fn test_submit_strategy_macro() { + submit_strategy!(MyStrategy { moves: 0 }, Onsite, "MyStrategy", "MyStrategy"); + let (participant, get_strategy) = provide_strategy(); + for matchup in 0..1 { + let s1 = Rc::new(RefCell::new(get_strategy())); + let s2 = Rc::new(RefCell::new(get_strategy())); + let s1_fm = if matchup % 2 == 0 { X } else { Y }; + let s2_fm = if matchup % 2 == 0 { Y } else { X }; + println!("matchup: {}: s1_fm: {} s2_fm: {}", matchup, s1_fm, s2_fm); + assert_eq!(s1.borrow_mut().play_for_favoured_move(s1_fm), X); + assert_eq!(s2.borrow_mut().play_for_favoured_move(s2_fm), Y); + assert_eq!(s1.borrow_mut().play_for_favoured_move(s1_fm), Y); + assert_eq!(s2.borrow_mut().play_for_favoured_move(s2_fm), X); + } + } +} diff --git a/strategies/src/submission_macro.rs b/strategies/src/submission_macro.rs index 3dd1c27..2deb4aa 100644 --- a/strategies/src/submission_macro.rs +++ b/strategies/src/submission_macro.rs @@ -13,55 +13,68 @@ * limitations under the License. */ - - +use crate::{Move, Participant, Round, Strategy}; #[macro_export] macro_rules! submit_strategy { ($strategy:expr, $participant_type:ident, $participant_name:literal, $participant_pub_name:literal) => { - pub fn provide_strategy() -> OwnedStrategy { - OwnedStrategy::new( - Participant::new($participant_type, $participant_name, $participant_pub_name), - Rc::new(RefCell::new(Box::new($strategy))), - ) - } + pub fn provide_strategy() -> (Participant, impl Fn() -> Box) { + ( + Participant::new($participant_type, $participant_name, $participant_pub_name), + move || Box::new($strategy), + ) + } #[cfg(test)] - mod tests { - use super::*; - use std::time::{Duration, Instant}; + mod tests { + use super::*; + use std::time::{Duration, Instant}; - #[test] - fn test_participant_type() { - assert_ne!(ParticipantType::System, $participant_type, "participant type should not be System"); - } + #[test] + fn test_participant_type() { + assert_ne!( + ParticipantType::System, + $participant_type, + "participant type should not be System" + ); + } - #[test] - fn test_strategy_time() { - let max_move_time = Duration::from_millis(100); - let max_handle_round_time = Duration::from_millis(100); - let strategy = OwnedStrategy::new( + #[test] + fn test_strategy_time() { + let max_move_time = Duration::from_millis(100); + let max_handle_round_time = Duration::from_millis(100); + let strategy = OwnedStrategy::new( Participant::new($participant_type, $participant_name, $participant_pub_name), Rc::new(RefCell::new(Box::new($strategy))), ); - let start_time = Instant::now(); - strategy.strategy.borrow_mut().play_for_favoured_move(X); - let elapsed = start_time.elapsed(); - assert!(elapsed < max_move_time, "play_for_favoured_move exceeded timeout. elapsed:{:?}, max:{:?}", elapsed, max_move_time); + let start_time = Instant::now(); + strategy.strategy.borrow_mut().play_for_favoured_move(X); + let elapsed = start_time.elapsed(); + assert!( + elapsed < max_move_time, + "play_for_favoured_move exceeded timeout. elapsed:{:?}, max:{:?}", + elapsed, + max_move_time + ); - let mut rounds = vec![]; - for m1 in vec![X, Y, Z] { - for m2 in vec![X, Y, Z] { - rounds.push(Round::of(m1, m2)); - } - } - for round in rounds { - let start_time = Instant::now(); - strategy.strategy.borrow_mut().handle_last_round(round, X); - let elapsed = start_time.elapsed(); - assert!(elapsed < max_handle_round_time, "handle_last_round exceeded timeout. elapsed:{:?}, max:{:?}", elapsed, max_handle_round_time); - } - } - } - } + let mut rounds = vec![]; + for m1 in vec![X, Y, Z] { + for m2 in vec![X, Y, Z] { + rounds.push(Round::of(m1, m2)); + } + } + for round in rounds { + let start_time = Instant::now(); + strategy.strategy.borrow_mut().handle_last_round(round, X); + let elapsed = start_time.elapsed(); + assert!( + elapsed < max_handle_round_time, + "handle_last_round exceeded timeout. elapsed:{:?}, max:{:?}", + elapsed, + max_handle_round_time + ); + } + } + } + }; } diff --git a/strategies/src/utils.rs b/strategies/src/utils.rs index 2f40f35..c2fc116 100644 --- a/strategies/src/utils.rs +++ b/strategies/src/utils.rs @@ -16,8 +16,8 @@ pub use core::fmt::Debug; pub use std::collections::VecDeque; -use urandom::Random; use urandom::rng::Xoshiro256; +use urandom::Random; use crate::Move; use crate::Move::{X, Y, Z}; @@ -116,4 +116,4 @@ impl Default for RandomMove { let third = 1f32 / 3f32; RandomMove::new(third, third) } -} \ No newline at end of file +}