From 566e517ec373ee0f17a24d51f1a84aa71bca6216 Mon Sep 17 00:00:00 2001 From: SrTobi Date: Fri, 20 Sep 2019 19:57:11 +0200 Subject: [PATCH] added FullSearchStrategy --- ailib/src/lib.rs | 37 +------- ailib/src/strategies/full_search_strategy.rs | 97 ++++++++++++++++++++ ailib/src/strategies/mod.rs | 4 +- tictactoe-console/src/main.rs | 15 ++- tictactoe/src/lib.rs | 4 +- 5 files changed, 119 insertions(+), 38 deletions(-) create mode 100644 ailib/src/strategies/full_search_strategy.rs diff --git a/ailib/src/lib.rs b/ailib/src/lib.rs index eedcb44..c0da626 100644 --- a/ailib/src/lib.rs +++ b/ailib/src/lib.rs @@ -2,10 +2,12 @@ use rand::Rng; use std::fmt; use std::iter::{once, Once}; +#[macro_use] +extern crate derive_builder; pub mod strategies; -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum Winner { Player(P), Draw, @@ -24,7 +26,7 @@ impl fmt::Display for Winner

{ pub trait State: Clone { type Action: fmt::Display; - type Player: fmt::Display; + type Player: Eq + Copy + fmt::Display; type ActionEffect; type ActionEffectIterator: Iterator; @@ -40,7 +42,7 @@ pub trait State: Clone { pub trait DeterministicState: Clone { type Action: fmt::Display; - type Player: fmt::Display; + type Player: Eq + Copy + fmt::Display; fn possible_actions(&self) -> Vec; @@ -111,35 +113,6 @@ pub trait Strategy { -/* - -pub struct FullSearchStrategy; - -impl FullSearchStrategy { - pub fn new() -> FullSearchStrategy { - FullSearchStrategy - } -} - -impl Strategy for FullSearchStrategy { - type Rating = bool; - - fn rated_actions(&self, state: &S) -> Vec<(S::Action, Self::Rating)> { - let actions = state.possible_actions(); - for action in actions { - - } - - unimplemented!(); - } - - fn find_best_action(&self, state: &S) -} - -*/ - - - mod test { #![allow(dead_code)] diff --git a/ailib/src/strategies/full_search_strategy.rs b/ailib/src/strategies/full_search_strategy.rs new file mode 100644 index 0000000..edbaf9b --- /dev/null +++ b/ailib/src/strategies/full_search_strategy.rs @@ -0,0 +1,97 @@ +use crate::*; +use noisy_float::prelude::*; + +#[derive(Builder, Copy, Clone, PartialEq)] +pub struct FullSearchStrategy { + #[builder(default = "n64(1.0)")] + win_reward: N64, + + #[builder(default = "n64(0.2)")] + draw_reward: N64, + + #[builder(default = "n64(0.0)")] + loose_reward: N64, +} + +impl FullSearchStrategy { + pub fn new() -> FullSearchStrategy { + Default::default() + } + + fn rate_action(&self, state: &mut S, action: &S::Action, player: S::Player) -> N64 { + state + .action_effects(action) + .map(|effect| { + let possibility = n64(state.apply_effect(&effect)); + let rating = self.rate_state(state, player); + state.unapply_effect(&effect); + possibility * rating + }) + .sum() + } + + fn rate_state(&self, state: &mut S, player: S::Player) -> N64 { + if let Some(result) = state.winner() { + match result { + Winner::Draw => return self.draw_reward, + Winner::Player(winner) => { + return + if winner == player { + self.win_reward + } else { + self.loose_reward + } + } + } + } + + let try_win = state.player() == player; + + let actions = state.possible_actions(); + let mut best = + if try_win { + -N64::infinity() + } else { + N64::infinity() + }; + assert!(!actions.is_empty()); + for action in actions { + let rate = self.rate_action(state, &action, player); + let is_better = + if try_win { + rate > best + } else { + rate < best + }; + if is_better { + best = rate; + } + } + best + } +} + + +impl Strategy for FullSearchStrategy { + type Rating = N64; + + fn rated_actions(&self, state: &S) -> Vec<(S::Action, Self::Rating)> { + let mut mut_state = state.clone(); + state + .possible_actions() + .into_iter() + .map(|action| { + let rating = self.rate_action(&mut mut_state, &action, state.player()); + (action, rating) + }) + .collect() + } +} + +impl Default for FullSearchStrategy { + fn default() -> FullSearchStrategy { + FullSearchStrategyBuilder::default() + .build() + .unwrap() + } +} \ No newline at end of file diff --git a/ailib/src/strategies/mod.rs b/ailib/src/strategies/mod.rs index 77c4efc..1f03e0a 100644 --- a/ailib/src/strategies/mod.rs +++ b/ailib/src/strategies/mod.rs @@ -1,3 +1,5 @@ mod random_strategy; +mod full_search_strategy; -pub use random_strategy::RandomStrategy; \ No newline at end of file +pub use random_strategy::RandomStrategy; +pub use full_search_strategy::FullSearchStrategy; \ No newline at end of file diff --git a/tictactoe-console/src/main.rs b/tictactoe-console/src/main.rs index 0b381ed..349366b 100644 --- a/tictactoe-console/src/main.rs +++ b/tictactoe-console/src/main.rs @@ -1,16 +1,25 @@ use tictactoe::*; use ailib::{Strategy, DeterministicState}; -use ailib::strategies::RandomStrategy; +use ailib::strategies::{RandomStrategy, FullSearchStrategy}; +use std::collections::HashMap; fn main() { let mut state = TicTacToeState::initial(); + fn to_fn(strat: impl Strategy) + -> impl Fn(&TicTacToeState) -> Option { + move |state| strat.best_action(state) + } loop { println!("{}\n", state); - let strat: &dyn Strategy = &RandomStrategy::new(); + let mut strats: HashMap Option> = Default::default(); + let random_strat = to_fn(RandomStrategy::default()); + let fullsearch_strat = to_fn(FullSearchStrategy::default()); + strats.insert(Stone::Circle, &random_strat); + strats.insert(Stone::Cross, &fullsearch_strat); - match strat.best_action(&state) { + match strats[&state.player()](&state) { Some(action) => state.apply_action(&action), None => { println!("Winner: {}\n", state.winner().unwrap()); diff --git a/tictactoe/src/lib.rs b/tictactoe/src/lib.rs index 89b858a..969d22c 100644 --- a/tictactoe/src/lib.rs +++ b/tictactoe/src/lib.rs @@ -5,13 +5,13 @@ use ailib::{Winner, DeterministicState}; use std::fmt; use std::fmt::Display; -#[derive(Copy, Clone, Eq, PartialEq, Display, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Display, Debug, Hash)] pub enum Stone { Circle, Cross } -#[derive(Clone)] +#[derive(Clone, Eq, PartialEq, Hash)] pub struct TicTacToeState { fields: [[Option; 3]; 3], turn: u32,