|
@@ -2,6 +2,8 @@ use bitboard::*;
|
|
|
use movegen::*;
|
|
|
use game::Game;
|
|
|
use evaluate::*;
|
|
|
+use log::info;
|
|
|
+use rand::prelude::*;
|
|
|
|
|
|
enum MoveUndo {
|
|
|
Default {
|
|
@@ -35,36 +37,80 @@ pub fn search(game: &Game, depth: i32) -> Move {
|
|
|
return best_move;
|
|
|
}
|
|
|
|
|
|
- let moves = generate_moves(game, game.turn);
|
|
|
- for mov in moves {
|
|
|
- let new_game = apply_move(game, mov);
|
|
|
- let val = -negamax(&new_game, depth - 1);
|
|
|
- if val > best {
|
|
|
- best = val;
|
|
|
- best_move = mov;
|
|
|
- }
|
|
|
+ let mut moves = generate_legal_moves(game, game.turn);
|
|
|
+ //info!("mov list: {:?}", moves.iter().map(|x| x.to_string()).collect::<Vec<String>>());
|
|
|
+
|
|
|
+ let mut rng = rand::thread_rng();
|
|
|
+
|
|
|
+ /*moves.shuffle(&mut rng);
|
|
|
+
|
|
|
+ // return random move
|
|
|
+ if moves.len() >= 1 {
|
|
|
+ return moves[0];
|
|
|
+ }*/
|
|
|
+
|
|
|
+ let mut valued_moves = moves.iter().map(|mov| {
|
|
|
+ let new_game = apply_move(game, *mov);
|
|
|
+ (*mov, negamax(&new_game, i32::min_value(), i32::max_value() - 1, depth - 1))
|
|
|
+ }).collect::<Vec<(Move, i32)>>();
|
|
|
+
|
|
|
+ valued_moves.sort_by_key(|mv| mv.1);
|
|
|
+
|
|
|
+ if valued_moves.len() > 0 {
|
|
|
+ let min_val = valued_moves[0].1;
|
|
|
+ let best_moves = valued_moves.iter().filter(|mv| mv.1 <= min_val).map(|(mov, _)| *mov).collect::<Vec<Move>>();
|
|
|
+
|
|
|
+ return best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return Move::Default{ mov: SimpleMove{ from: 0, to: 0 }, piece_type: PAWN };
|
|
|
}
|
|
|
+
|
|
|
+ //info!("best is {}", best_move.to_string());
|
|
|
return best_move;
|
|
|
}
|
|
|
|
|
|
-fn negamax(game: &Game, depth: i32) -> i32 {
|
|
|
+fn negamax(game: &Game, mut alpha: i32, beta: i32, depth: i32) -> i32 {
|
|
|
if depth == 0 {
|
|
|
return evaluate(game);
|
|
|
}
|
|
|
|
|
|
- let mut best = i32::min_value();
|
|
|
- let mut best_move = Move::default();
|
|
|
+ const MIN_VALUE: i32 = i32::min_value() + 1;
|
|
|
+
|
|
|
+ let mut best = MIN_VALUE;
|
|
|
+ //let mut best_move = Move::default();
|
|
|
+ //info!(" -> generate_legal_moves");
|
|
|
let moves = generate_moves(game, game.turn);
|
|
|
+
|
|
|
+ if moves.len() == 0 {
|
|
|
+ if is_check(game, game.turn) {
|
|
|
+ // mate
|
|
|
+ return MIN_VALUE;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // stalemate
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //info!(" -> generated_legal_moves {:?}", moves);
|
|
|
for mov in moves {
|
|
|
let new_game = apply_move(game, mov);
|
|
|
- let val = -negamax(&new_game, depth - 1);
|
|
|
+ //info!(" -> applied {} -> {}", mov.to_string(), depth - 1);
|
|
|
+ let val = -negamax(&new_game, -beta, -alpha, depth - 1);
|
|
|
+ if val >= beta {
|
|
|
+ return beta;
|
|
|
+ }
|
|
|
+ if val > alpha {
|
|
|
+ alpha = val;
|
|
|
+ }
|
|
|
+ //info!(" -> negamaxed {} -> {}", mov.to_string(), depth - 1);
|
|
|
if val > best {
|
|
|
best = val;
|
|
|
- best_move = mov;
|
|
|
+ //best_move = mov;
|
|
|
}
|
|
|
}
|
|
|
- println!("bestmove {:?}", best_move);
|
|
|
- return 0;
|
|
|
+ //println!("best {}", best);
|
|
|
+ return best;
|
|
|
}
|
|
|
|
|
|
pub fn apply_move(game: &Game, mov: Move) -> Game {
|
|
@@ -74,7 +120,7 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
|
|
|
let friends = game.get_all_side(side);
|
|
|
let others = game.get_all_side(!side);
|
|
|
match mov {
|
|
|
- Move::Default { mov: mov, piece_type: pt } => {
|
|
|
+ Move::Default { mov, piece_type: pt } => {
|
|
|
if from_square(mov.to) | others != 0 {
|
|
|
new_game.apply_mask(!from_square(mov.to));
|
|
|
}
|
|
@@ -96,8 +142,7 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
|
|
|
new_game.set_piece(KING, side, new_king);
|
|
|
new_game.set_piece(ROOK, side, new_rook);
|
|
|
},
|
|
|
- Move::EnPassant { side: side, column: col } => {
|
|
|
-
|
|
|
+ Move::EnPassant { side, column: _ } => {
|
|
|
let pawns = game.pawns(side);
|
|
|
if let Some(ep) = game.en_passant {
|
|
|
if pawns & A_FILE >> ep & (ROW_4 | ROW_5) != 0 {
|
|
@@ -108,7 +153,22 @@ pub fn apply_move(game: &Game, mov: Move) -> Game {
|
|
|
}
|
|
|
panic!("oh no");
|
|
|
},
|
|
|
- Move::Promotion { mov: mov, promote_to: pt } => { panic!("oh no"); },
|
|
|
+ Move::Promotion { mov, promote_to } => {
|
|
|
+ if from_square(mov.to) | others != 0 {
|
|
|
+ new_game.apply_mask(!from_square(mov.to));
|
|
|
+ }
|
|
|
+ new_game.apply_mask(!from_square(mov.from));
|
|
|
+
|
|
|
+ match promote_to {
|
|
|
+ QUEEN => { *new_game.queens_mut(side) |= from_square(mov.to); }
|
|
|
+ ROOK => { *new_game.rooks_mut(side) |= from_square(mov.to); }
|
|
|
+ BISHOP => { *new_game.bishops_mut(side) |= from_square(mov.to); }
|
|
|
+ KNIGHT => { *new_game.knights_mut(side) |= from_square(mov.to); }
|
|
|
+ _ => {
|
|
|
+ info!("internal error");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
}
|
|
|
|
|
|
new_game.turn = !new_game.turn;
|