浏览代码

improved eval + PosValue

Nicolas Winkler 3 年之前
父节点
当前提交
022c126849
共有 6 个文件被更改,包括 114 次插入54 次删除
  1. 10 14
      src/engine.rs
  2. 80 22
      src/evaluate.rs
  3. 0 1
      src/interface.rs
  4. 9 0
      src/main.rs
  5. 0 1
      src/movegen.rs
  6. 15 16
      src/search.rs

+ 10 - 14
src/engine.rs

@@ -1,4 +1,3 @@
-use search::apply_move;
 use bitboard::Bitboard;
 use game::Game;
 use search::*;
@@ -6,7 +5,7 @@ use movegen::*;
 use evaluate::PosValue;
 use log::{info};
 use std::time::{Duration, Instant};
-use std::collections::{VecDeque, HashMap};
+use std::collections::{VecDeque};
 use zobrist::ZobristTable;
 use hash::Cache;
 
@@ -153,20 +152,17 @@ impl Engine {
                         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());
+                        if let Some(turns) = crate::evaluate::is_mate_in_p1(bv) {
+                            println!("info depth {} score mate {} time {} nodes {} nps {} pv {}", depth, turns, elapsed.as_millis(), nodes, nps, best_move.to_string());
+                            info!("info depth {} score mate {} time {} nodes {} nps {} pv {}", depth, turns, elapsed.as_millis(), nodes, nps, best_move.to_string());
+                            break;
+                        }
+                        else {
+                            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;
                     }

+ 80 - 22
src/evaluate.rs

@@ -2,45 +2,79 @@ use game::*;
 use bitboard::*;
 use movegen::*;
 
-use std::f32::*;
+use std::i32;
 
-pub type PosValue = f32;
-pub const MIN_VALUE: PosValue = -1000000.0f32;
-pub const MAX_VALUE: PosValue = 1000000.0f32;
+pub type PosValue = i32;
 
-fn value_castling_rights(game: &Game, side: Side) -> PosValue {
-    let mut val: PosValue = 0.0f32;
+pub const MIN_VALUE: PosValue = i32::MIN + 1;
+pub const MAX_VALUE: PosValue = i32::MAX;
 
-    for i in 0..2 {
-        if game.castling_rights[i] { val += 12.0f32; }
+const MATE_SHIFT: usize = 24;
+
+pub fn mate() -> PosValue {
+    mate_in_p1(1)
+}
+
+/**
+ * constructs a PosValue that indicates that from a given position mate
+ * can be achieved in turns-1 moves (not halfmoves)
+ */
+pub fn mate_in_p1(turns: i8) -> PosValue {
+    if turns >= 0 || turns == -128 {
+        (((127 - turns) as PosValue) << MATE_SHIFT) as i32
     }
-    for i in 2..4 {
-        if game.castling_rights[i] { val -= 12.0f32; }
+    else {
+        -mate_in_p1(-turns)
     }
-    
-    if side == BLACK {
-        return -val;
+}
+
+/**
+ * returns Some(turns) if the pos_val indicates that mate
+ * can be reached in turns-1 moves
+ * 
+ * turns is negative if the moving side is getting mated
+ * in turns moves
+ */
+pub fn is_mate_in_p1(pos_val: PosValue) -> Option<i8> {
+    let highest_byte = (pos_val >> MATE_SHIFT) as i8;
+
+    if highest_byte != 0 && highest_byte != -1 {
+        if pos_val < 0 {
+            Some(-127 - highest_byte)
+        }
+        else {
+            Some(127 - highest_byte)
+        }
+
     }
     else {
-        return val;
+        None
     }
 }
 
+
 fn side_value(game: &Game, side: Side) -> u32 {
+    let adv_pawn_mask = match side { WHITE => ROW_7, BLACK => ROW_2 };
+    let semi_adv_pawn_mask = match side { WHITE => ROW_6, BLACK => ROW_3 };
+    let advanced_pawns = (game.get_piece(PAWN, side) & adv_pawn_mask).count_ones() * 200;
+    let semi_advanced_pawns = (game.get_piece(PAWN, side) & semi_adv_pawn_mask).count_ones() * 50;
+    advanced_pawns + semi_advanced_pawns
+}
+
+fn knight_value(game: &Game, side: Side) -> PosValue {
     let knights = game.get_piece(KNIGHT, side);
     let ks = BitboardIterator(knights);
     let mut k_attacks: u32 = 0;
 
     for k in ks {
         let targets = get_knight_targets(square(k));
-        k_attacks += targets.count_ones() * 4;
+        k_attacks += targets.count_ones() * 8;
     }
 
-    let adv_pawn_mask = match side { WHITE => ROW_7, BLACK => ROW_2 };
-    let advanced_pawns = (game.get_piece(PAWN, side) & adv_pawn_mask).count_ones() * 200;
-
-    advanced_pawns + k_attacks
-
+    let num_opp_pawns = game.get_piece(PAWN, !side).count_ones() as PosValue;
+    let num_knights = game.get_piece(KNIGHT, side).count_ones() as PosValue;
+    
+    k_attacks as PosValue + ((num_knights * 75 * num_opp_pawns) / 8) as PosValue
 }
 
 fn material_value(game: &Game, side: Side) -> PosValue {
@@ -51,10 +85,34 @@ fn material_value(game: &Game, side: Side) -> PosValue {
         + game.get_piece(QUEEN, side).count_ones() * 700) as PosValue
 }
 
+fn king_safety(game: &Game, side: Side) -> PosValue {
+    let king = game.get_piece(KING, side);
+    let area = north_one(king)
+             | south_one(king)
+             | east_one(king)
+             | west_one(king)
+             | northeast_one(king)
+             | northwest_one(king)
+             | southwest_one(king)
+             | southeast_one(king);
+    
+    let guards = game.get_all_side(side) & area;
+    let attackers = game.get_all_side(!side) & area;
+
+    guards.count_ones() as PosValue * 10 - attackers.count_ones() as PosValue * 35
+}
+
+fn king_there(game: &Game, side: Side) -> PosValue {
+    if game.get_piece(KING, side) != 0 { 10000 } else { 0 }
+}
+
 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);
+    let mat_val = ((material_value_us as f32).powf(0.995f32) - (material_value_them as f32).powf(0.995f32)) as PosValue;
+    let kv = knight_value(game, game.turn) - knight_value(game, !game.turn);
+    let king_safety = king_safety(game, game.turn) - king_safety(game, !game.turn);
+    let king_there = king_there(game, game.turn) - king_there(game, !game.turn);
+    return sv + kv + king_safety + mat_val + king_there;
 }

+ 0 - 1
src/interface.rs

@@ -8,7 +8,6 @@ use std::process::exit;
 use engine::{EngineMsg, InterfaceMsg, SearchInfo};
 use game::{Game};
 
-use std::thread::sleep_ms;
 use log::info;
 
 

+ 9 - 0
src/main.rs

@@ -19,6 +19,8 @@ use std::{thread, fs::File};
 use log::*;
 use engine::Engine;
 
+use evaluate::*;
+
 fn main() {
     /*let mut builder = Builder::from_default_env();
     builder
@@ -30,6 +32,13 @@ fn main() {
     
     //assert_eq!(std::mem::size_of::<crate::movegen::Move>(), 6);
 
+    let mio = mate_in_p1(1);
+    let mit = mate_in_p1(17);
+    //println!("{}", is_mate_in_p1(-mioo).unwrap());
+    if mio <= mit {
+        println!("ohno");
+    }
+
     let logfile = File::create("/home/nicolas/debug.log").unwrap();
     simplelog::WriteLogger::init(LevelFilter::Info, Config::default(), logfile).unwrap();
 

+ 0 - 1
src/movegen.rs

@@ -2,7 +2,6 @@ use bitboard::Bitboard;
 use bitboard::Square;
 use bitboard::*;
 use game::Game;
-use zobrist::ZobristTable;
 use log::info;
 
 pub type Side = bool;

+ 15 - 16
src/search.rs

@@ -16,7 +16,6 @@ pub struct SearchControl<'a> {
 
 pub enum SearchResult {
     Finished(Move, PosValue),
-    Mate(Move, i32),
     Cancelled(Option<(Move, PosValue)>),
     Invalid
 }
@@ -91,11 +90,11 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, depth:
             break;
         }
 
-        if val >= MAX_VALUE {
-            // mate in 1
+        if val >= mate() {
+            // mate in 1 --- can't get better than that
             info!("yay mate!");
             game.undo_move(undo);
-            return SearchResult::Mate(mov, 1);
+            return SearchResult::Finished(mov, val);
         }
 
         //info!("searched {} -> {}", mov.to_string(), val);
@@ -114,7 +113,7 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, depth:
 
     //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 * 1000.0f32) as i64);
+    valued_moves.sort_by_key(|mv| mv.1);
 
     //info!("best movvalues: {:?}", valued_moves.iter().map(|mv| mv.0.to_string() + " - " + &mv.1.to_string()).collect::<Vec<String>>());
 
@@ -125,11 +124,11 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, depth:
         //info!("bestmove value {}", -min_val);
         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)));
+            return SearchResult::Cancelled(Some((chosen_mov.0, -chosen_mov.1)));
         }
         else {
             hash.cache(game, CacheEntry::new_value(depth, chosen_mov.1));
-            return SearchResult::Finished(chosen_mov.0, chosen_mov.1);
+            return SearchResult::Finished(chosen_mov.0, -chosen_mov.1);
         }
     }
     else {
@@ -139,12 +138,7 @@ pub fn search(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, depth:
 
 fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, 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.0f32 {
-            //info!("eval: {}", eval);
-        }
-        //return eval;
+        return (quiescence_search(game, sc, alpha, beta, 9), false);
     }
 
     sc.nodes += 1;
@@ -165,7 +159,7 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
     if moves.len() == 0 {
         if is_check(game, game.turn) {
             // mate
-            return (MIN_VALUE, false);
+            return (-mate(), false);
         }
         else {
             // stalemate
@@ -178,6 +172,12 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
         let (mut val, ret) = negamax(game, sc, hash, -beta, -alpha, depth - 1);
         val = -val;
 
+        if let Some(turns) = is_mate_in_p1(val) {
+            if turns < 0 {
+                val = mate_in_p1(turns - 1);
+            }
+        }
+
         if ret {
             game.undo_move(undo);
             return (alpha, ret)
@@ -192,7 +192,6 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
         }
         //info!(" -> negamaxed {} -> {}", mov.to_string(), depth - 1);
         if val > best {
-
             best = val;
             //best_move = mov;
         }
@@ -201,7 +200,7 @@ fn negamax(game: &mut Game, sc: &mut SearchControl, hash: &mut Cache, mut alpha:
 
 
     //info!("best alpha {}", best);
-    return ((alpha as f64 * 0.99) as _, false);
+    return (alpha, false);
 }
 
 fn quiescence_search(game: &mut Game, sc: &mut SearchControl, mut alpha: PosValue, beta: PosValue, depth: i32) -> PosValue {