Nicolas Winkler 3 gadi atpakaļ
vecāks
revīzija
44d0c44a7c
7 mainītis faili ar 133 papildinājumiem un 48 dzēšanām
  1. 35 11
      src/engine.rs
  2. 25 12
      src/evaluate.rs
  3. 5 1
      src/game.rs
  4. 5 1
      src/interface.rs
  5. 1 0
      src/main.rs
  6. 44 23
      src/search.rs
  7. 18 0
      src/zobrist.rs

+ 35 - 11
src/engine.rs

@@ -3,6 +3,7 @@ use bitboard::Bitboard;
 use game::Game;
 use search::*;
 use movegen::*;
+use evaluate::PosValue;
 use log::{info};
 use std::time::{Duration, Instant};
 use std::collections::VecDeque;
@@ -99,15 +100,17 @@ impl Engine {
                 };
                 let mut sc = SearchControl{ nodes: 0, check: &mut check_fn };
                 let before = Instant::now();
-                let best_move = search(&self.game, &mut sc, *depth as i32);
+                let search_result = search(&self.game, &mut sc, *depth as i32);
 
-                let time = before.elapsed();
-                let nps = (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64;
-                info!("bestmove {}", best_move.to_string());
-                info!("searched {} nodes in {} ms ({} nodes/s)", sc.nodes, time.as_millis(), (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64);
-
-                println!("info nps {}", nps.to_string());
-                println!("bestmove {}", best_move.to_string());
+                if let SearchResult::Finished(best_move, best_val) = search_result {
+                    let time = before.elapsed();
+                    let nps = (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64;
+                    info!("bestmove {}", best_move.to_string());
+                    info!("searched {} nodes in {} ms ({} nodes/s)", sc.nodes, time.as_millis(), (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64);
+    
+                    println!("info nps {}", nps.to_string());
+                    println!("bestmove {}", best_move.to_string());
+                }
             },
             SearchInfo::Movetime(millis) => {
                 let before = Instant::now();
@@ -131,16 +134,37 @@ impl Engine {
 
                 let mut sc = SearchControl{ nodes: 0, check: &mut check_fn };
                 let mut best_move = Move::default();
+                let mut best_val: PosValue = i32::max_value() as _;
                 loop {
-                    let bm = search(&mut self.game, &mut sc, depth as i32);
-                    if bm != Move::default() {
+                    let search_result = search(&mut self.game, &mut sc, depth as i32);
+                    if let SearchResult::Finished(bm, bv) = search_result {
+                        info!("depth: {} bm: {}, bv: {}", depth, bm.to_string(), bv);
                         best_move = bm;
+                        best_val = bv;
+    
+                        let elapsed = before.elapsed();
+                        let nodes = sc.nodes;
+                        let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64;
+                        let cp = best_val as i64;
+                        println!("info depth {} score cp {} time {} nodes {} nps {} pv {}", depth, cp, elapsed.as_millis(), nodes, nps, best_move.to_string());
+                        info!("info depth {} score cp {} time {} nodes {} nps {} pv {}", depth, cp, elapsed.as_millis(), nodes, nps, best_move.to_string());
+                        depth += 1;
+                    }
+                    else if let SearchResult::Mate(bm, in_turns) = search_result {
+                        best_move = bm;
+                        let elapsed = before.elapsed();
+                        let nodes = sc.nodes;
+                        let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64;
+                        let pv = best_val as i64;
+                        println!("info depth {} score mate {} time {} nodes {} nps {} pv {}", depth, in_turns, elapsed.as_millis(), nodes, nps, bm.to_string());
+                        info!("info depth {} score mate {} time {} nodes {} nps {} pv {}", depth, pv, elapsed.as_millis(), nodes, nps, bm.to_string());
+                        break;
                     }
                     else {
                         break;
                     }
-                    depth += 1;
                 }
+                info!("bestmove {}", best_move.to_string());
 
                 println!("bestmove {}", best_move.to_string());
             },

+ 25 - 12
src/evaluate.rs

@@ -2,14 +2,20 @@ use game::*;
 use bitboard::*;
 use movegen::*;
 
-fn value_castling_rights(game: &Game, side: Side) -> i32 {
-    let mut val: i32 = 0;
+use std::f32::*;
+
+pub type PosValue = f32;
+pub const MIN_VALUE: PosValue = -1000000.0f32;
+pub const MAX_VALUE: PosValue = 1000000.0f32;
+
+fn value_castling_rights(game: &Game, side: Side) -> PosValue {
+    let mut val: PosValue = 0.0f32;
 
     for i in 0..2 {
-        if game.castling_rights[i] { val += 12; }
+        if game.castling_rights[i] { val += 12.0f32; }
     }
     for i in 2..4 {
-        if game.castling_rights[i] { val -= 12; }
+        if game.castling_rights[i] { val -= 12.0f32; }
     }
     
     if side == BLACK {
@@ -34,14 +40,21 @@ fn side_value(game: &Game, side: Side) -> u32 {
     let advanced_pawns = (game.get_piece(PAWN, side) & adv_pawn_mask).count_ones() * 200;
 
     advanced_pawns + k_attacks
-    + game.get_piece(PAWN, side).count_ones() * 100
-    + knights.count_ones() * 300
-    + game.get_piece(BISHOP, side).count_ones() * 320
-    + game.get_piece(ROOK, side).count_ones() * 400
-    + game.get_piece(QUEEN, side).count_ones() * 700
-    + game.get_piece(KING, side).count_ones() * 100000
+
+}
+
+fn material_value(game: &Game, side: Side) -> PosValue {
+    (game.get_piece(PAWN, side).count_ones() * 100
+        + game.get_piece(KNIGHT, side).count_ones() * 300
+        + game.get_piece(BISHOP, side).count_ones() * 320
+        + game.get_piece(ROOK, side).count_ones() * 400
+        + game.get_piece(QUEEN, side).count_ones() * 700) as PosValue
 }
 
-pub fn evaluate(game: &Game) -> i32 {
-    return side_value(game, game.turn) as i32 - side_value(game, !game.turn) as i32 + value_castling_rights(game, game.turn);
+pub fn evaluate(game: &Game) -> PosValue {
+    let sv = side_value(game, game.turn) as PosValue - side_value(game, !game.turn) as PosValue;
+    let material_value_us = material_value(game, game.turn);
+    let material_value_them = material_value(game, !game.turn);
+    let mat_val = material_value_us.powf(0.995f32) - material_value_them.powf(0.995f32);
+    return mat_val + value_castling_rights(game, game.turn);
 }

+ 5 - 1
src/game.rs

@@ -40,7 +40,7 @@ impl Default for Game {
             ],
             turn: WHITE,
             en_passant: None,
-            castling_rights: [true, true, true, true],
+            castling_rights: [true; 4],
             halfmoves_since_last_event: 0,
             turn_number: 0
         }
@@ -416,4 +416,8 @@ impl Game {
 
         return board;
     }
+
+    pub fn calculate_zobrist(&self) -> u64 {
+        0
+    }
 }

+ 5 - 1
src/interface.rs

@@ -80,7 +80,11 @@ fn cmd_position(mut args: Vec<&str>, r: &Receiver<InterfaceMsg>, s: &Sender<Engi
 }
 
 fn cmd_go(args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {
-    s.send(EngineMsg::Search(SearchInfo::Movetime(1000))).unwrap();
+    let mut movetime = 1000;
+    if args.len() > 1 && args[0] == "movetime" {
+        movetime = args[1].parse::<isize>().unwrap_or(1000);
+    }
+    s.send(EngineMsg::Search(SearchInfo::Movetime(movetime))).unwrap();
 }
 
 fn cmd_stop(_args: Vec<&str>, _r: &Receiver<InterfaceMsg>, s: &Sender<EngineMsg>) {

+ 1 - 0
src/main.rs

@@ -5,6 +5,7 @@ mod engine;
 mod game;
 mod evaluate;
 mod search;
+mod zobrist;
 
 extern crate log;
 extern crate simplelog;

+ 44 - 23
src/search.rs

@@ -26,11 +26,21 @@ pub struct SearchControl<'a> {
     pub check: &'a mut dyn FnMut() -> bool,
 }
 
+pub enum SearchResult {
+    Finished(Move, PosValue),
+    Mate(Move, i32),
+    Cancelled(Option<(Move, PosValue)>),
+    Invalid
+}
+
 
-pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
+/**
+ * searches for moves and returns the best move found plus its value
+ */
+pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> SearchResult {
 
     if depth == 0 {
-        return Move::default();
+        return SearchResult::Invalid;
     }
 
     let mut moves = generate_legal_moves(game, game.turn);
@@ -49,14 +59,15 @@ pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
     
     info!("moves: {:?}", moves.iter().map(|mv| mv.to_string()).collect::<Vec<String>>());
 
-    let mut alpha = i32::min_value() + 1;
-    let mut beta = i32::max_value();
+    let mut alpha: PosValue = MIN_VALUE;
+    let mut beta: PosValue = MAX_VALUE;
 
     // use a slight offset for the alpha value in the root node in order to
     // determine possibly multiple good moves
-    const ALPHA_OFFSET: i32 = 50;
+    const ALPHA_OFFSET: PosValue = 50 as _;
 
-    let mut valued_moves: Vec<(Move, i32)> = Vec::with_capacity(moves.len());
+    let mut valued_moves: Vec<(Move, PosValue)> = Vec::with_capacity(moves.len());
+    let mut cancelled = false;
     for mov in moves {
         let new_game = apply_move(game, mov);
         //info!("searching {}", mov.to_string());
@@ -64,10 +75,18 @@ pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
         val = -val;
 
         if ret {
-            return Move::default();
+            //return (Move::default(), 0);
+            cancelled = true;
+            break;
         }
 
-        info!("searched {} -> {}", mov.to_string(), val);
+        if val >= MAX_VALUE {
+            // mate in 1
+            info!("yay mate!");
+            return SearchResult::Mate(mov, 1);
+        }
+
+        //info!("searched {} -> {}", mov.to_string(), val);
 
         if val > alpha {
             alpha = val - ALPHA_OFFSET;
@@ -79,38 +98,42 @@ pub fn search(game: &Game, sc: &mut SearchControl, depth: i32) -> Move {
 
     //info!("movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
 
-    valued_moves.sort_by_key(|mv| mv.1);
+    valued_moves.sort_by_key(|mv| (mv.1 * 1000.0f32) as i64);
 
     //info!("best movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
 
     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>>();
+        let best_moves = valued_moves.iter().filter(|mv| mv.1 == min_val).collect::<Vec<&(Move, PosValue)>>();
 
         //info!("bestmove value {}", -min_val);
-        return best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
+        let chosen_mov = best_moves[(rng.next_u64() % best_moves.len() as u64) as usize];
+        if cancelled {
+            return SearchResult::Cancelled(Some((chosen_mov.0, chosen_mov.1)));
+        }
+        else {
+            return SearchResult::Finished(chosen_mov.0, chosen_mov.1);
+        }
     }
     else {
-        return Move::Default{ mov: SimpleMove{ from: 0, to: 0 }, piece_type: PAWN };
+        return SearchResult::Invalid;
     }
 }
 
-fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth: i32) -> (i32, bool) {
+fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> (PosValue, bool) {
     if depth == 0 {
         return (quiescence_search(game, sc, alpha, beta, 7), false);
         let eval = evaluate(game);
-        if eval != 0 {
+        if eval != 0.0f32 {
             //info!("eval: {}", eval);
         }
         //return eval;
     }
 
-    const MIN_VALUE: i32 = i32::min_value() + 1;
-
     sc.nodes += 1;
     if sc.nodes % 128 == 0 {
         if (sc.check)() {
-            return (0, true);
+            return (0 as _, true);
         }
     }
 
@@ -129,7 +152,7 @@ fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth
         }
         else {
             // stalemate
-            return (0, false);
+            return (0 as _, false);
         }
     }
     for mov in moves {
@@ -145,7 +168,7 @@ fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth
             return (beta, false);
         }
         if val > alpha {
-            alpha = (val as f64 * 0.95) as i32;
+            alpha = (val as f64 * 0.95) as _;
         }
         //info!(" -> negamaxed {} -> {}", mov.to_string(), depth - 1);
         if val > best {
@@ -155,12 +178,10 @@ fn negamax(game: &Game, sc: &mut SearchControl, mut alpha: i32, beta: i32, depth
         }
     }
     //info!("best alpha {}", best);
-    return ((best as f64 * 0.99) as i32, false);
+    return ((best as f64 * 0.99) as _, false);
 }
 
-fn quiescence_search(game: &Game, si: &mut SearchControl, mut alpha: i32, beta: i32, depth: i32) -> i32 {
-    const MIN_VALUE: i32 = i32::min_value() + 1;
-
+fn quiescence_search(game: &Game, si: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {
     let val = evaluate(game);
     si.nodes += 1;
 

+ 18 - 0
src/zobrist.rs

@@ -0,0 +1,18 @@
+
+
+type Hash = u64;
+
+pub static table: [[Hash; 12]; 64] = initialize();
+
+pub const fn initialize() -> [[Hash; 12]; 64] {
+    let mut val: u64 = 0xc7ad7b510a86ab2e_u64;
+
+    let mut tab: [[Hash; 12]; 64] = [[0; 12]; 64];
+
+    /*for piece in unsafe { &mut table } {
+        for sq in piece {
+            *sq = 3;
+        }
+    }*/
+    tab
+}