This commit is contained in:
nora 2023-01-09 10:47:36 +01:00
parent 5bfabd3cec
commit 6b5347b196
5 changed files with 89 additions and 20 deletions

View file

@ -27,6 +27,10 @@ impl Connect4 {
}
}
pub fn set_pos(&mut self, position: usize, value: Position) {
self.positions[position] = value;
}
pub fn result(&self) -> State {
match self.check_board() {
State::Winner(winner) => State::Winner(winner),

View file

@ -92,6 +92,7 @@ impl Score {
Self(int)
}
#[allow(unused)]
fn randomize(self) -> Self {
let score = self.0 as f32;
let rand = rand::thread_rng();

View file

@ -29,6 +29,10 @@ impl<G: Game> PerfectPlayer<G> {
self
}
pub fn best_move(&self) -> G::Move {
self.best_move.expect("no move made yet")
}
fn minmax(&mut self, board: &mut G, player: Player, depth: usize) -> Score {
if let Some(max_depth) = self.max_depth && depth >= max_depth {
return board.rate(player);

View file

@ -10,10 +10,10 @@ extern "C" {
/*
* Class: ch_bbw_m411_connect4_RustPlayer
* Method: rustPlay
* Signature: (B[Lch/bbw/m411/connect4/Connect4ArenaMain/Stone;)I
* Signature: (B[B)I
*/
JNIEXPORT jint JNICALL Java_ch_bbw_m411_connect4_RustPlayer_rustPlay
(JNIEnv *, jobject, jbyte, jobjectArray);
(JNIEnv *, jclass, jbyte, jbyteArray);
#ifdef __cplusplus
}

View file

@ -1,22 +1,81 @@
// This is the interface to the JVM that we'll call the majority of our
// methods on.
use jni::JNIEnv;
// These objects are what you should use as arguments to your native
// function. They carry extra lifetime information to prevent them escaping
// this context and getting used after being GC'd.
use jni::objects::{JClass, JObject};
// This is just a pointer. We'll be returning it from our function. We
// can't return one of the objects with lifetime information because the
// lifetime checker won't let us.
use jni::objects::{JClass, JObject, ReleaseMode};
use jni::sys::{jbyte, jint};
use jni::JNIEnv;
use minmax::{connect4::board::Connect4, GamePlayer};
use minmax::{PerfectPlayer, Player};
// 0 -> X
// 1 -> O
pub fn wrap_player<P: GamePlayer<Connect4>>(current_player: i8, board: ()) -> i32 {
0
/// We need to map the board.
/// Rust:
/// ```text
/// 0 1 2 3 4 5 6
/// 7 8 9 10 11 12 13
/// 14 15 16 17 18 19 20
/// 21 22 23 24 25 26 27
/// ```
/// Java:
/// ```text
/// 21 22 23 24 25 26 27
/// 14 15 16 17 18 19 20
/// 7 8 9 10 11 12 13
/// 0 1 2 3 4 5 6
/// ```
fn map_idx(i: usize) -> usize {
match () {
() if i < 7 => i + 21,
() if i < 15 => i + 7,
() if i < 21 => i - 7,
() => i - 21,
}
}
fn crate_board(java_board: &[i8]) -> Connect4 {
let mut board = Connect4::new();
for i in 0..28 {
let java_int = java_board[i];
let rust_value = match java_int {
0 => Some(Player::X),
1 => Some(Player::O),
2 => None,
_ => unreachable!(),
};
let rust_index = map_idx(i);
board.set_pos(rust_index, rust_value);
}
board
}
// 0 -> BLUE -> X
// 1 -> RED -> O
// 2 -> empty
pub fn wrap_player(env: JNIEnv<'_>, current_player: i8, board: JObject<'_>) -> i32 {
let board_size = env.get_array_length(board.into_raw()).unwrap();
assert_eq!(board_size, 28);
let byte_array = env
.get_byte_array_elements(board.into_raw(), ReleaseMode::NoCopyBack)
.unwrap();
let slice = unsafe { std::slice::from_raw_parts(byte_array.as_ptr() as *const _, 28) };
let mut board = crate_board(slice);
let mut player = PerfectPlayer::new(false);
let current_player_rust = match current_player {
0 => Player::X,
1 => Player::O,
_ => unreachable!(),
};
player.next_move(&mut board, current_player_rust);
let result_move = player.best_move();
map_idx(result_move) as i32
}
// This keeps Rust from "mangling" the name and making it unique for this
@ -27,9 +86,10 @@ pub extern "system" fn Java_ch_bbw_m411_connect4_RustPlayer_rustPlay(
// This is the class that owns our static method. It's not going to be used,
// but still must be present to match the expected signature of a static
// native method.
class: JClass<'_>,
_: JClass<'_>,
player: jbyte,
board: JObject<'_>,
) -> jint {
0
std::panic::catch_unwind(|| wrap_player(env, player, board))
.unwrap_or_else(|_| std::process::abort())
}