1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
//! Game simulator with shared memory for efficient tree searching //! //! It provides simulation structure and some utilies to make next decision in policy. //! //! It shares possible selections and board by `Node` to make simulation without copying it. //! It generate new simulation with `simulate` and recover the shared memory `Node` when it drop. //! It can simulate itself mutablely by `simulate_in` and recover it by `rollback_in`. //! //! # Examples //! ```rust //! # extern crate connect6; //! # use connect6::{game::{Game, Player}, policy::Simulate}; //! let game = Game::new(); //! let sim = Simulate::from_game(&game); //! { //! let sim2 = sim.simulate(0, 0); //! let board = sim2.board(); //! assert_eq!(board[0][0], Player::Black); //! } //! let board = sim.board(); //! assert_eq!(board[0][0], Player::None); //! ``` use game::{search, Game, Player}; use {Board, BOARD_SIZE}; use std::cell::RefCell; use std::rc::Rc; #[cfg(test)] mod tests; /// Shared memory for making simulation wihout copying board and possible selections. pub struct Node { pub board: Board, pub possible: Vec<(usize, usize)>, } impl Node { /// Make all possible selections within the board. #[inline] fn possible() -> Vec<(usize, usize)> { (0..BOARD_SIZE) .flat_map(|x| (0..BOARD_SIZE).map(move |y| (x, y))) .collect() } /// Construct a new `Node` fn new() -> Node { Node { board: [[Player::None; BOARD_SIZE]; BOARD_SIZE], possible: Self::possible(), } } /// Construct a `Node` from the board /// /// It make possible selections depending on the board status. fn from_board(board: &Board) -> Node { let possible = Self::possible() .into_iter() .filter(|(r, c)| board[*r][*c] == Player::None) .collect(); Node { board: *board, possible, } } } /// Game simulator with shared memory for efficient tree searching /// /// It provides simulation structure and some utilities to make next decision in policy. /// /// It shares possible selections and board by struct `Node` to make simulation without copying it. /// It generate new simulation with method `simulate` and recover the shared memory `Node` when it dropped. /// It can also simulate itself mutablely by `simulate_in` and recover it by `rollback_in`. /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::{Game, Player}, policy::Simulate}; /// let game = Game::new(); /// let sim = Simulate::from_game(&game); /// { /// let sim2 = sim.simulate(0, 0); /// let board = sim2.board(); /// assert_eq!(board[0][0], Player::Black); /// } /// let board = sim.board(); /// assert_eq!(board[0][0], Player::None); /// ``` pub struct Simulate { pub turn: Player, pub num_remain: i32, pub pos: Option<(usize, usize)>, pub node: Rc<RefCell<Node>>, } impl Simulate { /// Construct a new `Simulate`. pub fn new() -> Simulate { Simulate { turn: Player::Black, num_remain: 1, pos: None, node: Rc::new(RefCell::new(Node::new())), } } /// Construct a `Simulate` from the game /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::Game, policy::Simulate}; /// let game = Game::new(); /// let sim = Simulate::from_game(&game); /// ``` pub fn from_game(game: &Game) -> Simulate { Simulate { turn: game.get_turn(), num_remain: game.get_remain(), pos: None, node: Rc::new(RefCell::new(Node::from_board(game.get_board()))), } } /// Deep clone the simulation. /// /// With `Rc<RefCell<Node>>`, the default `Clone` implementation make the shallow copy of `Node`. /// By this reason, we require the `deep_clone` implementation which makes the *deep* copy of `Node`. pub fn deep_clone(&self) -> Simulate { let node = self.node.borrow(); let board = node.board; Simulate { turn: self.turn, num_remain: self.num_remain, pos: None, node: Rc::new(RefCell::new(Node::from_board(&board))), } } /// Get the board from node. pub fn board(&self) -> Board { let node = self.node.borrow(); node.board } /// Get the possible selections from node. pub fn possible(&self) -> Vec<(usize, usize)> { let node = self.node.borrow(); node.possible.clone() } /// Find the winner of game /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::Game, policy::Simulate}; /// let game = Game::new(); /// let sim = Simulate::from_game(&game); /// assert_eq!(game.is_game_end(), sim.search_winner()); /// ``` pub fn search_winner(&self) -> Player { let board = &self.node.borrow().board; search(board) } /// Validate the position, check invalid position err or already selected position err. /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::Game, policy::Simulate}; /// let game = Game::new(); /// let sim = Simulate::from_game(&game); /// assert!(sim.validate(0, 0)); /// /// let sim2 = sim.simulate(0, 0); /// assert!(!sim2.validate(0, 0)); /// ``` pub fn validate(&self, row: usize, col: usize) -> bool { if row >= BOARD_SIZE || col >= BOARD_SIZE { return false; } let board = &self.node.borrow().board; if board[row][col] != Player::None { return false; } true } /// Return next turn of game. pub fn next_turn(&self) -> Player { if self.num_remain <= 1 { self.turn.switch() } else { self.turn } } /// Make the new simulation with given position. /// /// By memory sharing of structure `Node`, once the new simulation is created, /// node of parents simulations would be also modified. /// This means, valid simulation is only *one* in the same time. /// Simulation will recover the shared memory when it dropped. /// It can make the tree searching more efficiently and precisely under the borrowing system of Rust. /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::{Game, Player}, policy::Simulate}; /// let sim = Simulate::new(); /// assert_eq!(sim.board()[0][0], Player::None); /// { /// let sim2 = sim.simulate(0, 0); /// assert_eq!(sim.board()[0][0], Player::Black); /// } /// assert_eq!(sim.board()[0][0], Player::None); /// ``` pub fn simulate(&self, row: usize, col: usize) -> Simulate { let mut node = self.node.borrow_mut(); // remove given position from possible selections let item = node.possible.iter().position(|x| *x == (row, col)); node.possible.remove(item.unwrap()); node.board[row][col] = self.turn; // switching turn let (turn, num_remain) = if self.num_remain <= 1 { (self.turn.switch(), 2) } else { (self.turn, 1) }; Simulate { turn, num_remain, pos: Some((row, col)), node: self.node.clone(), } } /// Modify the current state to simulate given position. /// /// It is for making simulation in the loop. /// It modify the inner state to simulate given position and do not make the new one. /// If you want to recover the inner state, reference [rollback_in](#method.rollback_in) /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::Player, policy::Simulate}; /// let mut sim = Simulate::new(); /// assert_eq!(sim.board()[0][0], Player::None); /// sim.simulate_in(0, 0); /// assert_eq!(sim.board()[0][0], Player::Black); /// ``` pub fn simulate_in(&mut self, row: usize, col: usize) { let mut node = self.node.borrow_mut(); let item = node.possible.iter().position(|x| *x == (row, col)); node.possible.remove(item.unwrap()); node.board[row][col] = self.turn; self.num_remain -= 1; if self.num_remain <= 0 { self.num_remain = 2; self.turn.mut_switch(); } } /// Modify the inner state to recover the given simulation. /// /// It clear the given position and switching turn properly. /// /// # Examples /// ```rust /// # extern crate connect6; /// # use connect6::{game::Player, policy::Simulate}; /// let mut sim = Simulate::new(); /// sim.simulate_in(0, 0); /// assert_eq!(sim.board()[0][0], Player::Black); /// sim.rollback_in(0, 0); /// assert_eq!(sim.board()[0][0], Player::None); /// ``` pub fn rollback_in(&mut self, row: usize, col: usize) { let mut node = self.node.borrow_mut(); node.board[row][col] = Player::None; node.possible.push((row, col)); self.num_remain += 1; if self.num_remain > 2 { self.num_remain = 1; self.turn.mut_switch(); } } } impl Drop for Simulate { fn drop(&mut self) { if let Some((row, col)) = self.pos { let mut node = self.node.borrow_mut(); node.possible.push((row, col)); node.board[row][col] = Player::None; } } }