From fdb2804883deb31e3aeb15bbe588dcc9b7b76bd0 Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 8 Dec 2025 19:56:48 -0500 Subject: Stuff --- engine/src/c_abi.rs | 403 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100755 engine/src/c_abi.rs (limited to 'engine/src/c_abi.rs') diff --git a/engine/src/c_abi.rs b/engine/src/c_abi.rs new file mode 100755 index 0000000..6a7f35f --- /dev/null +++ b/engine/src/c_abi.rs @@ -0,0 +1,403 @@ +use core::ffi::{c_int, c_ulonglong}; +use std::ffi::CString; +use std::num::{NonZeroU8, NonZeroUsize}; +use std::sync::atomic::AtomicBool; +use std::time::Duration; + +use model::{CheckersBitBoard, Move, MoveDirection, PieceColor}; + +use crate::{ + ActualLimit, Clock, Engine, EvalInfo, Evaluation, EvaluationSettings, Frontend, SearchLimit, +}; + +#[repr(C)] +struct CFrontend { + debug_fn: extern "C" fn(*const u8), + info_fn: extern "C" fn(*const EvalInfo), + bestmove_fn: extern "C" fn(*const Move), +} + +impl Frontend for CFrontend { + fn debug(&self, msg: &str) { + (self.debug_fn)(msg.as_bytes().as_ptr()) + } + + fn info(&self, info: EvalInfo) { + (self.info_fn)(&info) + } + + fn report_best_move(&self, best_move: Move) { + (self.bestmove_fn)(&best_move) + } +} + +#[repr(C)] +struct EvalResult { + evaluation: Box, + best_move: Option>, +} + +#[no_mangle] +extern "C" fn ampere_new_engine(hash_size: c_ulonglong, frontend: &CFrontend) -> Box> { + Box::new(Engine::new(hash_size as usize, frontend)) +} + +#[no_mangle] +extern "C" fn ampere_set_debug(engine: &Engine<'_>, debug: bool) { + engine.set_debug(debug) +} + +#[no_mangle] +extern "C" fn ampere_islegal(engine: &Engine<'_>, ampere_move: &Move) -> bool { + engine.is_legal_move(*ampere_move) +} + +#[no_mangle] +extern "C" fn ampere_current_position(engine: &Engine<'_>) -> Box { + Box::new(engine.current_position()) +} + +#[no_mangle] +extern "C" fn ampere_reset_position(engine: &Engine<'_>) { + engine.reset_position(); +} + +#[no_mangle] +extern "C" fn ampere_set_position(engine: &Engine<'_>, board: &CheckersBitBoard) { + engine.set_position(*board); +} + +#[no_mangle] +extern "C" fn ampere_play_move(engine: &Engine<'_>, ampere_move: &Move) -> bool { + engine.apply_move(*ampere_move).is_some() +} + +#[no_mangle] +extern "C" fn ampere_evaluate( + engine: &'static Engine<'_>, + cancel: Option<&AtomicBool>, + nodes: c_int, + depth: c_int, + time: Option<&Clock>, +) -> EvalResult { + let limits = if nodes == 0 && depth == 0 && time.is_none() { + SearchLimit::Auto + } else { + let time = time.cloned().unwrap_or(Clock::Unlimited); + + SearchLimit::Limited(ActualLimit { + nodes: NonZeroUsize::new(nodes as usize), + depth: NonZeroU8::new(depth as u8), + time: Some(time.recommended_time(engine.current_position().turn)), + }) + }; + + let (eval, best) = engine.evaluate( + cancel, + EvaluationSettings { + restrict_moves: None, + ponder: false, + clock: Clock::Unlimited, + search_until: limits, + }, + ); + + let evaluation = Box::new(eval); + let best_move = best.map(Box::new); + + EvalResult { + evaluation, + best_move, + } +} + +#[no_mangle] +extern "C" fn ampere_starteval_limited( + engine: &'static Engine<'_>, + ponder: bool, + nodes: c_int, + depth: c_int, + time: c_int, +) { + let limits = if nodes == 0 && depth == 0 && time == 0 { + SearchLimit::Auto + } else { + let time = if time == 0 { + None + } else { + Some(Duration::from_millis(time as u64)) + }; + + SearchLimit::Limited(ActualLimit { + nodes: NonZeroUsize::new(nodes as usize), + depth: NonZeroU8::new(depth as u8), + time, + }) + }; + + engine.start_evaluation(EvaluationSettings { + restrict_moves: None, + ponder, + clock: Clock::Unlimited, + search_until: limits, + }) +} + +#[no_mangle] +extern "C" fn ampere_starteval_unlimited(engine: &'static Engine<'_>, ponder: bool) { + engine.start_evaluation(EvaluationSettings { + restrict_moves: None, + ponder, + clock: Clock::Unlimited, + search_until: SearchLimit::Infinite, + }) +} + +#[no_mangle] +extern "C" fn ampere_stopeval(engine: &Engine<'_>) -> bool { + engine.stop_evaluation().is_some() +} + +#[no_mangle] +extern "C" fn ampere_destroy_engine(engine: Box>) { + drop(engine) +} + +#[no_mangle] +extern "C" fn ampere_evalinfo_nodes(info: &EvalInfo) -> c_ulonglong { + info.nodes_searched as c_ulonglong +} + +#[no_mangle] +extern "C" fn ampere_evalinfo_evaluation(info: &EvalInfo) -> *const Evaluation { + &info.evaluation +} + +#[no_mangle] +extern "C" fn ampere_evalinfo_bestmove(info: &EvalInfo) -> Option<&Move> { + info.current_best_move.as_ref() +} + +#[no_mangle] +extern "C" fn ampere_evalinfo_depth(info: &EvalInfo) -> c_int { + info.current_depth as c_int +} + +#[no_mangle] +extern "C" fn ampere_evalinfo_nodespersec(info: &EvalInfo) -> c_ulonglong { + info.nodes_per_second() as c_ulonglong +} + +#[no_mangle] +extern "C" fn ampere_evalinfo_elapsed(info: &EvalInfo) -> c_ulonglong { + info.elapsed_milliseconds() as c_ulonglong +} + +#[no_mangle] +extern "C" fn ampere_board_starting_position() -> Box { + Box::new(CheckersBitBoard::starting_position()) +} + +#[no_mangle] +extern "C" fn ampere_board_new( + pieces: u32, + color: u32, + kings: u32, + turn: PieceColor, +) -> Box { + Box::new(CheckersBitBoard::new(pieces, color, kings, turn)) +} + +#[no_mangle] +extern "C" fn ampere_clock_unlimited() -> Box { + Box::new(Clock::Unlimited) +} + +#[no_mangle] +extern "C" fn ampere_clock_timepermove(millis: c_int) -> Box { + Box::new(Clock::TimePerMove(Duration::from_millis(millis as u64))) +} + +#[no_mangle] +extern "C" fn ampere_clock_incremental( + white_time: c_int, + black_time: c_int, + white_inc: c_int, + black_inc: c_int, + moves_to_time_control: c_int, + time_control: c_int, +) -> Box { + let moves_until_next_time_control = if time_control == 0 { + None + } else { + Some(( + moves_to_time_control as u32, + Duration::from_millis(time_control as u64), + )) + }; + + Box::new(Clock::Incremental { + white_time_remaining: Duration::from_millis(white_time as u64), + black_time_remaining: Duration::from_millis(black_time as u64), + white_increment: Duration::from_millis(white_inc as u64), + black_increment: Duration::from_millis(black_inc as u64), + moves_until_next_time_control, + }) +} + +#[no_mangle] +extern "C" fn ampere_clock_destroy(clock: Box) { + drop(clock) +} + +#[no_mangle] +extern "C" fn ampere_board_clone(board: &CheckersBitBoard) -> Box { + Box::new(*board) +} + +#[no_mangle] +extern "C" fn ampere_board_equal(a: &CheckersBitBoard, b: &CheckersBitBoard) -> bool { + *a == *b +} + +#[no_mangle] +extern "C" fn ampere_board_hash(board: &CheckersBitBoard) -> u64 { + board.hash_code() +} + +#[no_mangle] +extern "C" fn ampere_board_pieces(board: &mut CheckersBitBoard) -> &mut u32 { + &mut board.pieces +} + +#[no_mangle] +extern "C" fn ampere_board_colors(board: &mut CheckersBitBoard) -> &mut u32 { + &mut board.color +} + +#[no_mangle] +extern "C" fn ampere_board_kings(board: &mut CheckersBitBoard) -> &mut u32 { + &mut board.kings +} + +#[no_mangle] +extern "C" fn ampere_board_turn(board: &mut CheckersBitBoard) -> &mut PieceColor { + &mut board.turn +} + +#[no_mangle] +extern "C" fn ampere_board_has_piece_at(board: &CheckersBitBoard, square: c_int) -> bool { + board.piece_at(square as usize) +} + +#[no_mangle] +unsafe extern "C" fn ampere_board_color_at(board: &CheckersBitBoard, square: c_int) -> PieceColor { + board.color_at_unchecked(square as usize) +} + +#[no_mangle] +unsafe extern "C" fn ampere_board_king_at(board: &CheckersBitBoard, square: c_int) -> bool { + board.king_at_unchecked(square as usize) +} + +#[no_mangle] +unsafe extern "C" fn ampere_board_move_piece( + board: &mut CheckersBitBoard, + start: c_int, + dest: c_int, +) { + *board = board.move_piece_to_unchecked(start as usize, dest as usize); +} + +#[no_mangle] +extern "C" fn ampere_board_clear_piece(board: &mut CheckersBitBoard, square: c_int) { + *board = board.clear_piece(square as usize); +} + +#[no_mangle] +extern "C" fn ampere_board_destroy(board: Box) { + drop(board) +} + +#[no_mangle] +extern "C" fn ampere_eval_is_force_win(evaluation: &Evaluation) -> bool { + evaluation.is_force_win() +} + +#[no_mangle] +extern "C" fn ampere_eval_is_force_loss(evaluation: &Evaluation) -> bool { + evaluation.is_force_loss() +} + +#[no_mangle] +extern "C" fn ampere_eval_is_force_seq(evaluation: &Evaluation) -> bool { + evaluation.is_force_sequence() +} + +#[no_mangle] +unsafe extern "C" fn ampere_eval_forceseq_len(evaluation: &Evaluation) -> c_int { + evaluation.force_sequence_length().unwrap_unchecked() as c_int +} + +#[no_mangle] +unsafe extern "C" fn ampere_eval_tofloat(evaluation: &Evaluation) -> f32 { + evaluation.to_f32_unchecked() +} + +#[no_mangle] +extern "C" fn ampere_eval_destroy(evaluation: Box) { + drop(evaluation) +} + +#[no_mangle] +extern "C" fn ampere_move_new(start: c_int, direction: MoveDirection, jump: bool) -> Box { + Box::new(Move::new(start as usize, direction, jump)) +} + +#[no_mangle] +extern "C" fn ampere_move_clone(ampere_move: &Move) -> Box { + Box::new(*ampere_move) +} + +#[no_mangle] +extern "C" fn ampere_move_equal(a: &Move, b: &Move) -> bool { + *a == *b +} + +#[no_mangle] +unsafe extern "C" fn ampere_move_string(m: &Move, buffer: *mut u8) { + let buffer = std::slice::from_raw_parts_mut(buffer, 6); + let string = CString::new(m.to_string().as_bytes()).unwrap_unchecked(); + let bytes = string.as_bytes_with_nul(); + buffer[..bytes.len()].copy_from_slice(bytes) +} + +#[no_mangle] +extern "C" fn ampere_move_start(ampere_move: &Move) -> c_int { + ampere_move.start() as c_int +} + +#[no_mangle] +extern "C" fn ampere_move_direction(ampere_move: &Move) -> MoveDirection { + ampere_move.direction() +} + +#[no_mangle] +extern "C" fn ampere_move_is_jump(ampere_move: &Move) -> bool { + ampere_move.is_jump() +} + +#[no_mangle] +unsafe extern "C" fn ampere_move_jump_position(ampere_move: &Move) -> c_int { + ampere_move.jump_position() as c_int +} + +#[no_mangle] +extern "C" fn ampere_move_end(ampere_move: &Move) -> c_int { + ampere_move.end_position() as c_int +} + +#[no_mangle] +extern "C" fn ampere_move_destroy(ampere_move: Box) { + drop(ampere_move) +} -- cgit v1.2.3