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)
}
|