From 74a2757df9ed08230ee971d67fa2a87e4ddcddf1 Mon Sep 17 00:00:00 2001 From: andy Date: Thu, 1 Jul 2021 17:32:12 +0100 Subject: [PATCH] beginning computer --- .github/workflows/test.yml | 2 +- src/board/enums.rs | 8 ++ src/board/mod.rs | 263 ++++++++++++++++++++++--------------- src/board/tests.rs | 46 +++---- src/comp/mod.rs | 147 +++++++++++++++++++++ src/comp/tests.rs | 81 ++++++++++++ src/game/mod.rs | 53 ++------ src/game/tests.rs | 6 +- src/lib.rs | 1 + 9 files changed, 426 insertions(+), 181 deletions(-) create mode 100644 src/comp/mod.rs create mode 100644 src/comp/tests.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d3b9558..8bd2314 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: run: wasm-pack test --firefox --chrome --headless - name: Build Rust for WASM - run: wasm-pack build + run: wasm-pack build --release - name: Install Node uses: actions/setup-node@v2 diff --git a/src/board/enums.rs b/src/board/enums.rs index 61a2c5b..7cf29dc 100644 --- a/src/board/enums.rs +++ b/src/board/enums.rs @@ -3,6 +3,14 @@ use wasm_bindgen::prelude::*; use std::fmt::{Display, Write}; +#[wasm_bindgen] +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MoveType { + Move = 0, + Jump = 1, +} + #[wasm_bindgen] #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/board/mod.rs b/src/board/mod.rs index ca8ddf7..8896bbe 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -26,8 +26,8 @@ pub const STD_HEIGHT: usize = 8; #[wasm_bindgen] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Piece { - team: Team, - strength: Strength + pub team: Team, + pub strength: Strength } #[wasm_bindgen] @@ -74,13 +74,14 @@ impl Direction { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Square { /// Game piece if square is occupied - occupant: Option, + pub occupant: Option, /// Description of whether the square is occupied or an unplayable, i.e. off-lattice square - state: SquareState + pub state: SquareState } #[wasm_bindgen] impl Square { + /// Standard constructor function to form square from state and occupant #[wasm_bindgen(constructor)] pub fn new(state: SquareState, occupant: Option) -> Square{ Square { @@ -89,6 +90,7 @@ impl Square { } } + /// Helper function for a well-formed piece square by team and strength pub fn pc(team: Team, strength: Strength) -> Square { Square { occupant: Some(Piece::new(team, strength)), @@ -96,12 +98,21 @@ impl Square { } } + /// Helper function for a well-formed empty square pub fn empty() -> Square { Square { occupant: None, state: Empty, } } + + /// Helper function for a well-formed unplayable square + pub fn unplay() -> Square { + Square { + occupant: None, + state: Unplayable, + } + } } /// Rank 2 tensor index to identify a board square by row and column @@ -133,19 +144,19 @@ impl Display for BrdIdx { /// Single state of a checkers board #[wasm_bindgen] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Board { /// 1D backing array of board squares for the 2D game board cells: Vec, - width: usize, - height: usize, + pub width: usize, + pub height: usize, - current_turn: Team + pub current_turn: Team } -/////////////////// -// PRIV FUNCS -/////////////////// +//////////////////// +// UNBOUND FUNCS +//////////////////// impl Board { /// Get a mutable reference to a board square by 1D array index @@ -160,7 +171,7 @@ impl Board { /// /// Some(Vec): A variable length vector of 1D indices for diagonally adjacent squares. /// Vector may be between 1 and 4 items long depending on the location of the given square - pub fn diagonal_indices(&self, idx: BrdIdx) -> Option> { + pub fn adjacent_indices(&self, idx: BrdIdx) -> Option> { if self.cell_state(self.cell_idx(idx)) == Unplayable { return None; } @@ -204,7 +215,7 @@ impl Board { /// Get a [`Direction`] structure for the diagonally adjacent squares of a given board square /// - /// Similar to the [`Board::diagonal_indices`] function with differently formatted results + /// Similar to the [`Board::adjacent_indices`] function with differently formatted results /// /// # Returns /// [`None`]: If the given square is unplayable @@ -243,7 +254,7 @@ impl Board { Some(dir) } - /// Filter an array of diagonal indices (Like those from [`Board::diagonal_indices`], [`Board::jumpable_indices`]) for those that are legal for a team's man, i.e. solely up or down the board + /// Filter an array of diagonal indices (Like those from [`Board::adjacent_indices`], [`Board::jumpable_indices`]) for those that are legal for a team's man, i.e. solely up or down the board pub fn filter_indices(&self, idx: BrdIdx, player: Team, indices: Vec) -> Vec { indices.into_iter().filter(|i| { match player { @@ -253,8 +264,8 @@ impl Board { }).collect() } - pub fn player_diagonal_indices(&self, idx: BrdIdx, player: Team) -> Option> { - match self.diagonal_indices(idx) { + pub fn player_adjacent_indices(&self, idx: BrdIdx, player: Team) -> Option> { + match self.adjacent_indices(idx) { Some(x) => Some(self.filter_indices(idx, player, x)), None => None } @@ -357,7 +368,7 @@ impl Board { } } - /// cast the signed + /// Get the difference between two [`BrdIdx`] objects pub fn idx_diffs(from: BrdIdx, to: BrdIdx) -> (isize, isize) { // cast to signed ints so that -1 will work for black moves (to.row as isize - from.row as isize, to.col as isize - from.col as isize) @@ -572,93 +583,6 @@ impl Board { ) } - /// Unwrap the jumpee piece from the square and [`Board::check_jumpee_team`] with [`Moveable`] response - pub fn validate_jumpee(jumpee: Square, from_occ: Piece) -> Moveable { - // check whether jumpee is an opponent's piece - match jumpee.occupant { - None => panic!("No occupant found when checking the from: {:?} , jumpee: {:?}", from_occ, jumpee), - Some(jumpee_occupant_uw) => { - if Board::check_jumpee_team(from_occ, jumpee_occupant_uw) { - return Moveable::Allowed; - } - else { - return Moveable::JumpingSameTeam; - } - }, - } - } - - /// Check that the source piece and the jumpee are of opposing teams - pub fn check_jumpee_team(from: Piece, jumpee: Piece) -> bool { - return from.team.opponent() == jumpee.team - } - - /// Initialise a game board without game pieces - #[wasm_bindgen(constructor)] - pub fn new(width: usize, height: usize, current_turn: Team) -> Board { - let total_cells = width * height; - - let mut cells: Vec = Vec::with_capacity(total_cells); - let mut playable = false; - - for i in 0..height { - for _ in 0..width { - if playable { - cells.push(Square::new(Empty, None)); - } - else { - cells.push(Square::new(Unplayable, None)); - } - playable = !playable; - } - playable = i % 2 == 0; - } - - Board { - cells, - width, - height, - - current_turn - } - } - - /// Reset the given board to a starting layout with 3 rows of opposing pieces - pub fn init_game(board: Board, piece_rows: usize) -> Board { - let mut new_board = board.clone(); - for (idx, row) in RowSquareIterator::new(&board).enumerate() { - for (jdx, square) in row.iter().enumerate() { - - if square.state == Empty || square.state == Occupied { - if idx < piece_rows { - let cell_idx = new_board.cell_index(idx, jdx); - new_board.cells[cell_idx] = Square::pc(White, Man); - } else if idx >= board.height - piece_rows { - let cell_idx = new_board.cell_index(idx, jdx); - new_board.cells[cell_idx] = Square::pc(Black, Man); - } else { - let cell_idx = new_board.cell_index(idx, jdx); - new_board.cells[cell_idx] = Square::new( - Empty, - None - ); - } - } - } - } - - new_board - } - - /// Get the [`Board::current_turn`] parameter - pub fn current_turn(&self) -> Team { - self.current_turn - } - - pub fn set_turn(&mut self, new_team: Team) { - self.current_turn = new_team; - } - /// Get a pointer to the backing array of board squares, [`Board::cells`] pub fn cells(&self) -> *const Square { self.cells.as_ptr() @@ -716,6 +640,137 @@ impl Board { pub fn cell_state(&self, idx: usize) -> SquareState { self.cell(idx).state } + + pub fn apply_move(&self, from: BrdIdx, to: BrdIdx) -> Board { + let mut new = self.clone(); + + let from_idx = self.cell_idx(from); + let to_idx = self.cell_idx(to); + + // make move update + new.set_cell( + to_idx, // destination square + self.cell(from_idx) // source piece + ); + + // remove old piece + new.set_cell( + from_idx, // destination square + Square::empty() // empty piece + ); + + new + } + + pub fn apply_jump(&self, from: BrdIdx, to: BrdIdx) -> Board { + let mut new = self.clone(); + + let from_idx = self.cell_idx(from); + let to_idx = self.cell_idx(to); + + // make move update + new.set_cell( + to_idx, // destination square + self.cell(from_idx) // source piece + ); + + // remove old piece + new.set_cell( + from_idx, // destination square + Square::empty() // empty piece + ); + + // remove jumpee + new.set_cell( + self.jumpee_idx(from, to), // destination square + Square::empty() // empty piece + ); + + + new + } +} + +///////////////////////// +// BOUND TYPE FUNCS +///////////////////////// + +#[wasm_bindgen] +impl Board { + /// Unwrap the jumpee piece from the square and [`Board::check_jumpee_team`] with [`Moveable`] response + pub fn validate_jumpee(jumpee: Square, from_occ: Piece) -> Moveable { + // check whether jumpee is an opponent's piece + match jumpee.occupant { + None => panic!("No occupant found when checking the from: {:?} , jumpee: {:?}", from_occ, jumpee), + Some(jumpee_occupant_uw) => { + if Board::check_jumpee_team(from_occ, jumpee_occupant_uw) { + return Moveable::Allowed; + } + else { + return Moveable::JumpingSameTeam; + } + }, + } + } + + /// Check that the source piece and the jumpee are of opposing teams + pub fn check_jumpee_team(from: Piece, jumpee: Piece) -> bool { + return from.team.opponent() == jumpee.team + } + + /// Initialise a game board without game pieces + #[wasm_bindgen(constructor)] + pub fn new(width: usize, height: usize, current_turn: Team) -> Board { + let total_cells = width * height; + + let mut cells: Vec = Vec::with_capacity(total_cells); + let mut playable = false; + + for i in 0..height { + for _ in 0..width { + if playable { + cells.push(Square::empty()); + } + else { + cells.push(Square::unplay()); + } + playable = !playable; + } + playable = i % 2 == 0; + } + + Board { + cells, + width, + height, + + current_turn + } + } + + /// Reset the given board to a starting layout with 3 rows of opposing pieces + pub fn init_game(board: Board, piece_rows: usize) -> Board { + let mut new_board = board.clone(); + for (idx, row) in RowSquareIterator::new(&board).enumerate() { + for (jdx, square) in row.iter().enumerate() { + + if square.state == Empty || square.state == Occupied { + if idx < piece_rows { + let cell_idx = new_board.cell_index(idx, jdx); + new_board.cells[cell_idx] = Square::pc(White, Man); + } else if idx >= board.height - piece_rows { + let cell_idx = new_board.cell_index(idx, jdx); + new_board.cells[cell_idx] = Square::pc(Black, Man); + } else { + let cell_idx = new_board.cell_index(idx, jdx); + new_board.cells[cell_idx] = Square::empty(); + } + } + } + } + + new_board + } } impl Display for Board { diff --git a/src/board/tests.rs b/src/board/tests.rs index e296e07..d76871d 100644 --- a/src/board/tests.rs +++ b/src/board/tests.rs @@ -43,7 +43,7 @@ fn set_cell() { let idx = 1; let mut board = Board::new(8, 8, Black); - let square = Square::new(Occupied, Some(Piece::new(White, Man))); + let square = Square::pc(White, Man); board.set_cell(idx, square); assert_eq!(square, board.cell(idx)); @@ -106,51 +106,51 @@ fn first_square_row_5_unplayable() { #[wasm_bindgen_test] fn moveable_indices_unplayable() { let board = Board::new(8, 8, Black); - assert_eq!(None, board.diagonal_indices(BrdIdx::from(7, 7))); - assert_eq!(None, board.diagonal_indices(BrdIdx::from(0, 0))); - assert_eq!(None, board.diagonal_indices(BrdIdx::from(1, 1))); + assert_eq!(None, board.adjacent_indices(BrdIdx::from(7, 7))); + assert_eq!(None, board.adjacent_indices(BrdIdx::from(0, 0))); + assert_eq!(None, board.adjacent_indices(BrdIdx::from(1, 1))); } #[wasm_bindgen_test] fn moveable_indices_central() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![1, 3, 17, 19]), board.diagonal_indices(BrdIdx::from(1, 2))); + assert_eq!(Some(vec![1, 3, 17, 19]), board.adjacent_indices(BrdIdx::from(1, 2))); } #[wasm_bindgen_test] fn moveable_indices_top_row() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![8, 10]), board.diagonal_indices(BrdIdx::from(0, 1))); + assert_eq!(Some(vec![8, 10]), board.adjacent_indices(BrdIdx::from(0, 1))); } #[wasm_bindgen_test] fn moveable_indices_left_column() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![1, 17]), board.diagonal_indices(BrdIdx::from(1, 0))); + assert_eq!(Some(vec![1, 17]), board.adjacent_indices(BrdIdx::from(1, 0))); } #[wasm_bindgen_test] fn moveable_indices_bottom_row() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![49, 51]), board.diagonal_indices(BrdIdx::from(7, 2))); + assert_eq!(Some(vec![49, 51]), board.adjacent_indices(BrdIdx::from(7, 2))); } #[wasm_bindgen_test] fn moveable_indices_right_column() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![14, 30]), board.diagonal_indices(BrdIdx::from(2, 7))); + assert_eq!(Some(vec![14, 30]), board.adjacent_indices(BrdIdx::from(2, 7))); } #[wasm_bindgen_test] fn moveable_indices_top_right() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![14]), board.diagonal_indices(BrdIdx::from(0, 7))); + assert_eq!(Some(vec![14]), board.adjacent_indices(BrdIdx::from(0, 7))); } #[wasm_bindgen_test] fn moveable_indices_bottom_left() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![49]), board.diagonal_indices(BrdIdx::from(7, 0))); + assert_eq!(Some(vec![49]), board.adjacent_indices(BrdIdx::from(7, 0))); } ////////////////////// @@ -214,15 +214,15 @@ fn jumpable_indices_bottom_left() { } #[wasm_bindgen_test] -fn black_diagonal_indices() { +fn black_adjacent_indices() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![1, 3]), board.player_diagonal_indices(BrdIdx::from(1, 2), Black)); + assert_eq!(Some(vec![1, 3]), board.player_adjacent_indices(BrdIdx::from(1, 2), Black)); } #[wasm_bindgen_test] -fn white_diagonal_indices() { +fn white_adjacent_indices() { let board = Board::new(8, 8, Black); - assert_eq!(Some(vec![17, 19]), board.player_diagonal_indices(BrdIdx::from(1, 2), White)); + assert_eq!(Some(vec![17, 19]), board.player_adjacent_indices(BrdIdx::from(1, 2), White)); } //////////////// @@ -253,22 +253,12 @@ fn check_jumpee_same_teams() { #[wasm_bindgen_test] fn check_validate_jumpee_opposing_teams() { - let jumpee_square = Square::new( - Occupied, - Some( - Piece::new(White, Man) - ) - ); + let jumpee_square = Square::pc(White, Man); let from_piece = Piece::new(Black, Man); assert_eq!(Board::validate_jumpee(jumpee_square, from_piece), Moveable::Allowed); - let jumpee_square = Square::new( - Occupied, - Some( - Piece::new(Black, Man) - ) - ); + let jumpee_square = Square::pc(Black, Man); let from_piece = Piece::new(White, Man); assert_eq!(Board::validate_jumpee(jumpee_square, from_piece), Moveable::Allowed); @@ -445,7 +435,7 @@ fn can_move() { let to = BrdIdx::from(0, 0); assert_eq!(board.can_move(from, to), Moveable::Unplayable); - board.set_turn(Black); + board.current_turn = Black; // wrong teams piece let from = BrdIdx::from(2, 1); diff --git a/src/comp/mod.rs b/src/comp/mod.rs new file mode 100644 index 0000000..2a9702d --- /dev/null +++ b/src/comp/mod.rs @@ -0,0 +1,147 @@ +//! AI player logic + +use indextree::{Arena, NodeId, Node}; + +extern crate wasm_bindgen; +use wasm_bindgen::prelude::*; + +use crate::log; + +use crate::board::{Square, Board, BrdIdx}; +use crate::board::enums::{SquareState, MoveType, Moveable, Team, Strength}; +use crate::board::iter::{PieceIterator}; + +use Team::*; +use Strength::*; +use SquareState::*; + +use std::fmt::{Display, Write}; + +#[cfg(test)] pub mod tests; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Move { + from: BrdIdx, + to: BrdIdx, + mv_type: MoveType, +} + +impl Move { + pub fn new(from: BrdIdx, to: BrdIdx, mv_type: MoveType) -> Move { + Move { + from, to, mv_type + } + } +} + +/// Root-level structure for managing the game as a collection of board states +#[derive(Debug)] +pub struct Computer { + pub tree: Arena, + pub root_node_id: NodeId, + pub search_depth: usize, + pub team: Team, +} + +impl Computer { + pub fn new(initial_board: Board, search_depth: usize, team: Team) -> Computer { + let mut tree = Arena::new(); + let root_node_id = tree.new_node(initial_board); + Computer { + tree, + root_node_id, + search_depth, + team + } + } + + pub fn update_board(&mut self, new_board: Board) { + let mut tree = Arena::new(); + tree.new_node(new_board); + self.tree = tree; + } + + pub fn available_turns(&self, board: &Board) -> Vec { + + // allocate capacity for 2 moves per piece, likely too much but will be shrunk + // to reduce memory re-allocations + let mut moves = Vec::with_capacity(board.num_player(board.current_turn) * 2); + + // get all pieces in the board + for (idx, square) in PieceIterator::new(board) { + + match square.occupant { + None => continue, + Some(piece) => { + + // filter for current team's pieces + if piece.team == board.current_turn { + let from_brd_idx = board.board_index(idx); + let adj_op = board.adjacent_indices(from_brd_idx); + let jump_op = board.jumpable_indices(from_brd_idx); + + // iterate over adjacent indices + if let Some(adj) = adj_op { + for i in adj { + let to_brd_idx = board.board_index(i); + + // check if can move + if board.can_move(from_brd_idx, to_brd_idx) == Moveable::Allowed { + moves.push(Move::new(from_brd_idx, to_brd_idx, MoveType::Move)); + } + } + } else { + panic!("Unable to unwrap adjacent indices, from: {}, brd: {}", from_brd_idx, board); + } + + // iterate over adjacent indices + if let Some(jump) = jump_op { + for i in jump { + let to_brd_idx = board.board_index(i); + + // check if can move + if board.can_move(from_brd_idx, to_brd_idx) == Moveable::Allowed { + moves.push(Move::new(from_brd_idx, to_brd_idx, MoveType::Jump)); + } + } + } else { + panic!("Unable to unwrap adjacent indices, from: {}, brd: {}", from_brd_idx, board); + } + } + }, + } + } + + moves.shrink_to_fit(); + moves + } + + pub fn gen_tree(&mut self, board: Board) { + + } + + pub fn insert_boards(&mut self, boards: Vec) -> Vec { + + boards + .into_iter().map( + |b| self.tree.new_node(b) + ).collect() + } + + pub fn get_move_boards(&self, board: &Board) -> Vec { + + self.available_turns(board) + .into_iter().map( + |m| { + match m.mv_type { + MoveType::Move => board.apply_move(m.from, m.to), + MoveType::Jump => board.apply_jump(m.from, m.to), + } + } + ).collect() + } + + pub fn get_move(&self) { + + } +} \ No newline at end of file diff --git a/src/comp/tests.rs b/src/comp/tests.rs new file mode 100644 index 0000000..8d13ccd --- /dev/null +++ b/src/comp/tests.rs @@ -0,0 +1,81 @@ +use super::*; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn initial_tree_size() { + let brd = Board::new(3, 2, White); + let comp = Computer::new(brd, 3, White); + + assert!(!comp.tree.is_empty()); + assert_eq!(comp.tree.count(), 1); +} + +#[wasm_bindgen_test] +fn available_moves() { + // . W . + // _ . _ + + let brd = Board::new(3, 2, White); + let mut brd2 = brd.clone(); + let comp = Computer::new(brd, 3, White); + + // log!("{}", brd2); + + // can move left and right from central square + brd2.set_cell(brd2.cell_index(0, 1), Square::pc(White, Man)); + + // log!("{}", brd2); + + let moves = comp.available_turns(&brd2); + + // log!("{:?}", moves); + + assert_eq!(moves.len(), 2); + assert!(moves.into_iter().all(|m| m.mv_type == MoveType::Move)); +} + +#[wasm_bindgen_test] +fn available_moves_jumps() { + // . W . _ + // _ . B . + // . _ . _ + // _ . _ . + + let brd = Board::new(4, 4, White); + let mut brd2 = brd.clone(); + let comp = Computer::new(brd, 3, White); + + // log!("{}", brd2); + + brd2.set_cell(brd2.cell_index(0, 1), Square::pc(White, Man)); + brd2.set_cell(brd2.cell_index(1, 2), Square::pc(Black, Man)); + + // log!("{}", brd2); + + let moves = comp.available_turns(&brd2); + + // log!("{:?}", moves); + + assert_eq!(moves.len(), 2); + assert!(moves[0].mv_type == MoveType::Move); + assert!(moves[1].mv_type == MoveType::Jump); +} + +#[wasm_bindgen_test] +fn available_moves_std_brd() { + let brd = Board::init_game(Board::new(8, 8, White), 3); + let mut brd2 = brd.clone(); + let comp = Computer::new(brd, 3, White); + + // log!("{}", brd2); + + let moves = comp.available_turns(&brd2); + + // log!("{:?}", moves); + + // 2 diag moves per piece except the last + assert_eq!(moves.len(), 7); + assert!(moves.into_iter().all(|m| m.mv_type == MoveType::Move)); +} diff --git a/src/game/mod.rs b/src/game/mod.rs index a581088..9b7ffc8 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,5 +1,4 @@ use crate::board::Board; -use indextree::Arena; extern crate wasm_bindgen; use wasm_bindgen::prelude::*; @@ -22,7 +21,6 @@ use std::fmt::{Display, Write}; pub struct Game { current: Board, previous_boards: Vec, - // tree: Arena } impl Game { @@ -39,6 +37,11 @@ impl Game { #[wasm_bindgen] impl Game { + /// Current turn's team + pub fn current_turn(&self) -> Team { + self.current.current_turn + } + /// Attempt to make a move given a source and destination index pub fn make_move(&mut self, from: BrdIdx, to: BrdIdx) { let able = self.current.can_move(from, to); @@ -58,59 +61,19 @@ impl Game { } // board has been changed, update player turn - self.current.set_turn(self.current.current_turn().opponent()); + self.current.current_turn = self.current.current_turn.opponent(); } /// Update board state with given move and push new board into current state pub fn execute_move(&mut self, from: BrdIdx, to: BrdIdx) { - let mut new_board = self.current.clone(); - - let from_idx = self.current.cell_idx(from); - let to_idx = self.current.cell_idx(to); - - // make move update - new_board.set_cell( - to_idx, // destination square - self.current.cell(from_idx) // source piece - ); - - // remove old piece - new_board.set_cell( - from_idx, // destination square - Square::empty() // empty piece - ); - // set new board to current and push current to stack - self.push_new_board(new_board); + self.push_new_board(self.current.apply_move(from, to)); } /// Update board state with given jump move and push new board into current state pub fn execute_jump(&mut self, from: BrdIdx, to: BrdIdx) { - let mut new_board = self.current.clone(); - - let from_idx = self.current.cell_idx(from); - let to_idx = self.current.cell_idx(from); - - // make move update - new_board.set_cell( - to_idx, // destination square - self.current.cell(from_idx) // source piece - ); - - // remove old piece - new_board.set_cell( - from_idx, // destination square - Square::empty() // empty piece - ); - - // remove jumpee - new_board.set_cell( - self.current.jumpee_idx(from, to), // destination square - Square::empty() // empty piece - ); - // set new board to current and push current to stack - self.push_new_board(new_board); + self.push_new_board(self.current.apply_jump(from, to)); } /// Push current board into the previous turns and set given board to current diff --git a/src/game/tests.rs b/src/game/tests.rs index f8d8532..51ebf4a 100644 --- a/src/game/tests.rs +++ b/src/game/tests.rs @@ -11,7 +11,7 @@ use crate::board::enums::Strength::*; #[wasm_bindgen_test] fn make_move() { let mut game = Game::new(8, 8, 3, Black); - log!("{}", game); + // log!("{}", game); // log!("{:?}", game); let from = BrdIdx::from(5, 2); @@ -22,7 +22,7 @@ fn make_move() { assert_eq!(board.cell(board.cell_index(4, 1)), Square::pc(Black, Man)); - log!("{}", game); + // log!("{}", game); let from = BrdIdx::from(2, 1); let to = BrdIdx::from(3, 2); @@ -32,7 +32,7 @@ fn make_move() { assert_eq!(board.cell(board.cell_index(3, 2)), Square::pc(White, Man)); - log!("{}", game); + // log!("{}", game); // log!("{}", game.previous_board(0)); } diff --git a/src/lib.rs b/src/lib.rs index 1c5706d..9398d4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod board; pub mod utils; pub mod game; pub mod player; +pub mod comp; extern crate wasm_bindgen; use wasm_bindgen::prelude::*;