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;
        }
    }
}