summaryrefslogtreecommitdiff
path: root/engine/src/c_abi.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/c_abi.rs')
-rwxr-xr-xengine/src/c_abi.rs403
1 files changed, 403 insertions, 0 deletions
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<Evaluation>,
+ best_move: Option<Box<Move>>,
+}
+
+#[no_mangle]
+extern "C" fn ampere_new_engine(hash_size: c_ulonglong, frontend: &CFrontend) -> Box<Engine<'_>> {
+ 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<CheckersBitBoard> {
+ 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<Engine<'_>>) {
+ 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<CheckersBitBoard> {
+ Box::new(CheckersBitBoard::starting_position())
+}
+
+#[no_mangle]
+extern "C" fn ampere_board_new(
+ pieces: u32,
+ color: u32,
+ kings: u32,
+ turn: PieceColor,
+) -> Box<CheckersBitBoard> {
+ Box::new(CheckersBitBoard::new(pieces, color, kings, turn))
+}
+
+#[no_mangle]
+extern "C" fn ampere_clock_unlimited() -> Box<Clock> {
+ Box::new(Clock::Unlimited)
+}
+
+#[no_mangle]
+extern "C" fn ampere_clock_timepermove(millis: c_int) -> Box<Clock> {
+ 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<Clock> {
+ 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<Clock>) {
+ drop(clock)
+}
+
+#[no_mangle]
+extern "C" fn ampere_board_clone(board: &CheckersBitBoard) -> Box<CheckersBitBoard> {
+ 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<CheckersBitBoard>) {
+ 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<Evaluation>) {
+ drop(evaluation)
+}
+
+#[no_mangle]
+extern "C" fn ampere_move_new(start: c_int, direction: MoveDirection, jump: bool) -> Box<Move> {
+ Box::new(Move::new(start as usize, direction, jump))
+}
+
+#[no_mangle]
+extern "C" fn ampere_move_clone(ampere_move: &Move) -> Box<Move> {
+ 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<Move>) {
+ drop(ampere_move)
+}