mirror of
https://github.com/Noratrieb/minmax.git
synced 2026-01-14 15:25:08 +01:00
works
This commit is contained in:
parent
3d90a3ea0a
commit
d415bb7ae9
6 changed files with 84 additions and 86 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -203,6 +203,14 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minmax_wrapper"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jni",
|
||||||
|
"minmax",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
|
|
@ -293,14 +301,6 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rs-wrapper"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"jni",
|
|
||||||
"minmax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.36.6"
|
version = "0.36.6"
|
||||||
|
|
|
||||||
21
build_and_run.sh
Executable file
21
build_and_run.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if ! which rustc > /dev/null;
|
||||||
|
then
|
||||||
|
echo "ERROR: Rust must be installed: https://www.rust-lang.org/tools/install"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${BASH_SOURCE[0]}"
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
echo "Building Rust library..."
|
||||||
|
cargo build --release "--manifest-path=$SCRIPT_DIR/Cargo.toml"
|
||||||
|
|
||||||
|
export LD_LIBRARY_PATH="$SCRIPT_DIR/target/release"
|
||||||
|
echo "Setting LD_LIBRARY_PATH to $LD_LIBRARY_PATH"
|
||||||
|
|
||||||
|
echo "Running Java tests. If this fails, there's something wrong :/"
|
||||||
|
cd "$SCRIPT_DIR/minmax-java"
|
||||||
|
./gradlew build
|
||||||
|
|
@ -67,65 +67,7 @@ public class Connect4ArenaMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isWinning(Stone[] board, Stone forColor) {
|
boolean isWinning(Stone[] board, Stone forColor) {
|
||||||
var increment = 0;
|
return RustPlayer.isWinning(board, forColor);
|
||||||
for (int i = 27; i >= 0; i--) {
|
|
||||||
if (forColor != board[i]) {
|
|
||||||
increment = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Horizontal reset
|
|
||||||
if (i == 20 || i == 13 || i == 6) {
|
|
||||||
increment = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
increment++;
|
|
||||||
// Horizontal
|
|
||||||
if (increment == 4) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i > 20) {
|
|
||||||
// Vertical
|
|
||||||
var vertical = true;
|
|
||||||
for (int y = i - 7; y >= 0; y -= 7) {
|
|
||||||
if (board[y] != forColor) {
|
|
||||||
vertical = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (vertical) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Diagonally
|
|
||||||
if (i <= 24) {
|
|
||||||
var diagonal = true;
|
|
||||||
for (int y = i; y >= 0; y -= 7 - 1) {
|
|
||||||
if (board[y] != forColor) {
|
|
||||||
diagonal = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (diagonal) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= 24) {
|
|
||||||
var diagonal = true;
|
|
||||||
for (int y = i; y >= 0; y -= 7 + 1) {
|
|
||||||
if (board[y] != forColor) {
|
|
||||||
diagonal = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (diagonal) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Stone {
|
public enum Stone {
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@ package ch.bbw.m411.connect4;
|
||||||
|
|
||||||
public class RustPlayer extends Connect4ArenaMain.DefaultPlayer {
|
public class RustPlayer extends Connect4ArenaMain.DefaultPlayer {
|
||||||
private static native int rustPlay(byte player, byte[] board);
|
private static native int rustPlay(byte player, byte[] board);
|
||||||
|
private static native int rustBoardWinner(byte[] board);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// This actually loads the shared object that we'll be creating.
|
// This actually loads the shared object that we'll be creating.
|
||||||
// The actual location of the .so or .dll may differ based on your
|
// The actual location of the .so or .dll may differ based on your
|
||||||
// platform.
|
// platform.
|
||||||
System.loadLibrary("rs_wrapper");
|
System.loadLibrary("minmax_wrapper");
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] encodeBoard(Connect4ArenaMain.Stone[] board) {
|
static byte[] encodeBoard(Connect4ArenaMain.Stone[] board) {
|
||||||
|
|
@ -27,6 +28,16 @@ public class RustPlayer extends Connect4ArenaMain.DefaultPlayer {
|
||||||
return boardBuf;
|
return boardBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isWinning(Connect4ArenaMain.Stone[] board, Connect4ArenaMain.Stone forColor) {
|
||||||
|
byte[] boardBuf = RustPlayer.encodeBoard(board);
|
||||||
|
byte expectedPlayer = switch (forColor) {
|
||||||
|
case BLUE -> 1;
|
||||||
|
case RED -> 0;
|
||||||
|
};
|
||||||
|
int winner = RustPlayer.rustBoardWinner(boardBuf);
|
||||||
|
return winner == expectedPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int play() {
|
protected int play() {
|
||||||
byte player = switch (this.myColor) {
|
byte player = switch (this.myColor) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "rs-wrapper"
|
name = "minmax_wrapper"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use jni::objects::{JClass, JObject, ReleaseMode};
|
||||||
use jni::sys::{jbyte, jint};
|
use jni::sys::{jbyte, jint};
|
||||||
use jni::JNIEnv;
|
use jni::JNIEnv;
|
||||||
use minmax::{connect4::board::Connect4, GamePlayer};
|
use minmax::{connect4::board::Connect4, GamePlayer};
|
||||||
use minmax::{Game, PerfectPlayer, Player};
|
use minmax::{Game, PerfectPlayer, Player, State};
|
||||||
|
|
||||||
/// We need to map the board.
|
/// We need to map the board.
|
||||||
/// Rust:
|
/// Rust:
|
||||||
|
|
@ -28,7 +28,16 @@ fn map_idx(i: usize) -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crate_board(java_board: &[i8]) -> Connect4 {
|
unsafe fn create_board(env: JNIEnv<'_>, board: JObject<'_>) -> Connect4 {
|
||||||
|
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 java_board = unsafe { std::slice::from_raw_parts(byte_array.as_ptr().cast::<u8>(), 28) };
|
||||||
|
|
||||||
let mut board = Connect4::new();
|
let mut board = Connect4::new();
|
||||||
|
|
||||||
for i in 0..28 {
|
for i in 0..28 {
|
||||||
|
|
@ -37,7 +46,9 @@ fn crate_board(java_board: &[i8]) -> Connect4 {
|
||||||
0 => Some(Player::X),
|
0 => Some(Player::X),
|
||||||
1 => Some(Player::O),
|
1 => Some(Player::O),
|
||||||
2 => None,
|
2 => None,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(
|
||||||
|
"Java code passed an invalid value as the byte of the board: {java_int}"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let rust_index = map_idx(i);
|
let rust_index = map_idx(i);
|
||||||
|
|
@ -51,17 +62,8 @@ fn crate_board(java_board: &[i8]) -> Connect4 {
|
||||||
// 0 -> RED -> X
|
// 0 -> RED -> X
|
||||||
// 1 -> BLUE -> O
|
// 1 -> BLUE -> O
|
||||||
// 2 -> empty
|
// 2 -> empty
|
||||||
pub fn wrap_player(env: JNIEnv<'_>, current_player: i8, board: JObject<'_>) -> i32 {
|
pub fn play_move(env: JNIEnv<'_>, current_player: i8, board: JObject<'_>) -> i32 {
|
||||||
let board_size = env.get_array_length(board.into_raw()).unwrap();
|
let mut board = unsafe { create_board(env, board) };
|
||||||
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 mut player = PerfectPlayer::new(false);
|
||||||
|
|
||||||
|
|
@ -82,8 +84,18 @@ pub fn wrap_player(env: JNIEnv<'_>, current_player: i8, board: JObject<'_>) -> i
|
||||||
java_idx
|
java_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
// This keeps Rust from "mangling" the name and making it unique for this
|
fn board_winner(env: JNIEnv<'_>, board: JObject<'_>) -> i32 {
|
||||||
// crate.
|
let board = unsafe { create_board(env, board) };
|
||||||
|
|
||||||
|
let state = board.result();
|
||||||
|
match state {
|
||||||
|
State::Draw => 2,
|
||||||
|
State::InProgress => 2,
|
||||||
|
State::Winner(Player::X) => 0,
|
||||||
|
State::Winner(Player::O) => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_ch_bbw_m411_connect4_RustPlayer_rustPlay(
|
pub extern "system" fn Java_ch_bbw_m411_connect4_RustPlayer_rustPlay(
|
||||||
env: JNIEnv<'_>,
|
env: JNIEnv<'_>,
|
||||||
|
|
@ -94,6 +106,18 @@ pub extern "system" fn Java_ch_bbw_m411_connect4_RustPlayer_rustPlay(
|
||||||
player: jbyte,
|
player: jbyte,
|
||||||
board: JObject<'_>,
|
board: JObject<'_>,
|
||||||
) -> jint {
|
) -> jint {
|
||||||
std::panic::catch_unwind(|| wrap_player(env, player, board))
|
std::panic::catch_unwind(|| play_move(env, player, board))
|
||||||
.unwrap_or_else(|_| std::process::abort())
|
.unwrap_or_else(|_| std::process::abort())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn Java_ch_bbw_m411_connect4_RustPlayer_rustBoardWinner(
|
||||||
|
env: JNIEnv<'_>,
|
||||||
|
// 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.
|
||||||
|
_: JClass<'_>,
|
||||||
|
board: JObject<'_>,
|
||||||
|
) -> jint {
|
||||||
|
std::panic::catch_unwind(|| board_winner(env, board)).unwrap_or_else(|_| std::process::abort())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue