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