Skip to content

Commit

Permalink
Working 'go infinite' cmd.
Browse files Browse the repository at this point in the history
This uses duplicate MoveTables to circumvent the "use of moved value"
issue that move closures love to cause. It's a very hacky workaround,
but it's a constant-time cost, and only when the GUI sends a request to
look for a move.
  • Loading branch information
ethanbarry committed Dec 1, 2024
1 parent a51cd4c commit c7c8c67
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 43 deletions.
15 changes: 14 additions & 1 deletion src/enginemanager.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
use vampirc_uci::UciMove;

use crate::{gamemanager::GameManager, movetable::{noarc::NoArc, MoveTable}};
use crate::{
gamemanager::GameManager,
movetable::{noarc::NoArc, MoveTable},
};

pub struct Engine {
pub tbl: NoArc<MoveTable>,
pub move_history: Vec<UciMove>,
pub board: GameManager,
//pub set_new_game: bool,
}

impl Default for Engine {
fn default() -> Self {
Self {
tbl: NoArc::new(MoveTable::default()),
move_history: Vec::new(),
board: GameManager::default(),
}
}
}
26 changes: 15 additions & 11 deletions src/gamemanager/legal_moves/search.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
Arc, Mutex,
};

use rayon::prelude::*;
Expand All @@ -12,10 +12,17 @@ use super::{GameManager, MoveTable, NoArc};
/// A Negamax search routine that runs in parallel.
pub fn root_negamax(
depth: u16,
gm: &GameManager,
gm: GameManager,
tbl: &NoArc<MoveTable>,
flag: Arc<AtomicBool>,
) -> (Move, GameManager) {
best_move: Arc<Mutex<(Square, Square, MoveType)>>,
) {
/* ************************************************************************************* */
/* NOTE: This acquires the lock around best_move. If the lock is freed before the best */
/* move has been written, the UCI thread will print out an invalid move. DO NOT */
/* FREE THIS LOCK EARLY! */
/* ************************************************************************************* */
let mut state = best_move.lock().unwrap();
let moves = gm.legal_moves(tbl);

if moves.len() == 0 {
Expand All @@ -40,20 +47,17 @@ pub fn root_negamax(
.map(|(s, movetuple)| (s, (movetuple.0, movetuple.1)))
.collect();

if depth % 2 == 0 {
scored_moves.sort_by(|a, b| a.0.cmp(&b.0));
} else {
scored_moves.sort_by(|a, b| a.0.cmp(&b.0));
}
scored_moves.sort_by(|a, b| a.0.cmp(&b.0));

let best = scored_moves
.into_iter()
.inspect(|m| println!("{}: {}{}", m.0, m.1 .0 .1.to_str(), m.1 .0 .2.to_str()))
.inspect(|m| eprintln!("{}: {}{}", m.0, m.1 .0 .1.to_str(), m.1 .0 .2.to_str()))
.last()
.expect("Should be a move here!");

println!("Best score: {}", best.0);
best.1
state.0 = best.1 .0 .1; // from
state.1 = best.1 .0 .2; // to
state.2 = best.1 .0 .3; // movetype
}

fn negamax(
Expand Down
20 changes: 19 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#![allow(dead_code)]

use std::{
sync::{atomic::AtomicBool, Arc, Mutex},
thread,
};

use enginemanager::Engine;
use types::{MoveType, Square};

mod bitboard;
mod enginemanager;
mod gamemanager;
Expand All @@ -8,7 +16,17 @@ mod types;
mod ucimanager;

fn main() {
ucimanager::communicate();
let e = Engine::default();
let search_flag = Arc::new(AtomicBool::new(false)); // Continue searching? Default to no.
let best_move = Arc::new(Mutex::new((Square::A1, Square::A1, MoveType::QuietMove)));

let uci_handle = thread::spawn(move || {
ucimanager::communicate(e, search_flag, best_move);
});

uci_handle
.join()
.expect("Joining thread uci_handle failed; the engine probably crashed.");
}

#[cfg(test)]
Expand Down
75 changes: 45 additions & 30 deletions src/ucimanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@ use crate::{
gamemanager::GameManager,
movetable::{noarc::NoArc, MoveTable},
};
use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::sync::{Arc, Mutex};
use std::thread::{self, sleep};
use std::time::Duration;
use std::{io, u16};
use vampirc_uci::{UciMessage, UciMove, UciPiece};

pub fn communicate() {
let mut stop_engine = false;
let start_search_flag = Arc::new(AtomicBool::new(false));
let mut e: Engine = Engine {
tbl: NoArc::new(MoveTable::default()),
board: GameManager::default(),
move_history: Vec::<UciMove>::new(),
//set_new_game: false,
};

while !stop_engine {
pub fn communicate(
mut e: Engine,
search_flag: Arc<AtomicBool>,
best_move: Arc<Mutex<(Square, Square, MoveType)>>,
) {
loop {
let mut text = String::new();

io::stdin()
Expand All @@ -46,9 +42,9 @@ pub fn communicate() {
fen,
moves,
} => {
//For now, we'll reinitalize the engine's data
//(minus movetable) each time we receive a
//'position' command.
// For now, we'll reinitalize the engine's data
// (minus movetable) each time we receive a
// 'position' command.
if startpos {
e.board = GameManager::default();
} else {
Expand All @@ -66,42 +62,61 @@ pub fn communicate() {
time_control,
search_control,
} => {
start_search_flag.store(true, Ordering::Relaxed);
let flag = start_search_flag.clone();
search_flag.store(true, Ordering::Relaxed);
{
let mut lock = best_move.lock().unwrap();
*lock = (Square::A1, Square::A1, MoveType::QuietMove); // Reinitialize best_move.
} // Lock dropped here.
let flag = search_flag.clone();
let best_move = best_move.clone();
let table = NoArc::new(MoveTable::default()); // TODO: Really hurts to create a whole new table...
let gm = e.board.clone();

if let Some(timectl) = time_control {
match timectl {
vampirc_uci::UciTimeControl::Infinite => {
for depth in 0..20
/* or any big number */
{
root_negamax(depth, &e.board, &e.tbl, flag.clone());
}
thread::spawn(move || {
root_negamax(20, gm, &table, flag, best_move);
});
}
vampirc_uci::UciTimeControl::MoveTime(time) => {}
vampirc_uci::UciTimeControl::MoveTime(_) => unimplemented!(),
vampirc_uci::UciTimeControl::Ponder => unimplemented!(),
vampirc_uci::UciTimeControl::TimeLeft { .. } => unimplemented!(),
}
}
}
UciMessage::Stop => {
start_search_flag.store(false, Ordering::Relaxed);
search_flag.store(false, Ordering::Relaxed);
{
let lock = best_move.lock().unwrap();
use MoveType::*;
let promo = match lock.2 {
QPromotion | QPromoCapture => "q",
RPromotion | RPromoCapture => "r",
BPromotion | BPromoCapture => "b",
NPromotion | NPromoCapture => "n",
_ => "",
};
let outstr =
format!("bestmove {}{}{}", lock.0.to_str(), lock.1.to_str(), promo);
println!("{}", outstr);
}
}
UciMessage::Quit => {
start_search_flag.store(false, Ordering::Relaxed);
stop_engine = true;
search_flag.store(false, Ordering::Relaxed);
break;
}
_ => {
println!("Some other message was received.");
}
}
}
} // End of the input loop. UCI terminates.
}

fn make_move(board: &GameManager, tbl: &NoArc<MoveTable>, m: UciMove) -> GameManager {
let h_from = Square::from_str(&m.from.to_string()).unwrap();
let h_to = Square::from_str(&m.to.to_string()).unwrap();
let legal_moves = board.legal_moves(tbl);
let legal_moves = board.legal_moves(&tbl);
let updated_data = legal_moves
.iter()
.find(|data| {
Expand Down

0 comments on commit c7c8c67

Please sign in to comment.