mirror of
https://github.com/Noratrieb/minmax.git
synced 2026-01-14 15:25:08 +01:00
port to java
This commit is contained in:
parent
50f4f7edd7
commit
c235e4ec65
3 changed files with 97 additions and 6 deletions
|
|
@ -17,7 +17,7 @@ public class Connect4ArenaMain {
|
|||
static final int NOMOVE = -1;
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Connect4ArenaMain().play(new RustPlayer(), new GreedyPlayer());
|
||||
new Connect4ArenaMain().play(new HumanPlayer(), new PerfectPlayer());
|
||||
}
|
||||
|
||||
static String toDebugString(Stone[] board) {
|
||||
|
|
@ -66,7 +66,7 @@ public class Connect4ArenaMain {
|
|||
return null; // null implies a draw
|
||||
}
|
||||
|
||||
boolean isWinning(Stone[] board, Stone forColor) {
|
||||
public static boolean isWinning(Stone[] board, Stone forColor) {
|
||||
return RustPlayer.isWinning(board, forColor);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package ch.bbw.m411.connect4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PerfectPlayer extends Connect4ArenaMain.DefaultPlayer {
|
||||
private static final int MAX_DEPTH = 8;
|
||||
private static final int WON = 1000;
|
||||
private static final int LOST = -1000;
|
||||
|
||||
private int bestMove = -1;
|
||||
|
||||
private List<Integer> possibleMoves() {
|
||||
var moves = new ArrayList<Integer>();
|
||||
// for all columns
|
||||
for (int i = 0; i < 7; i++) {
|
||||
// walk up
|
||||
for (int j = 0; j < 4; j++) {
|
||||
var position = i + (j * 7);
|
||||
if (board[position] == null) {
|
||||
moves.add(position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return moves;
|
||||
}
|
||||
|
||||
private int minmax(Connect4ArenaMain.Stone maximizingPlayer, int alpha, int beta, int depth) {
|
||||
if (depth >= MAX_DEPTH) {
|
||||
// FIXME rate
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Connect4ArenaMain.isWinning(board, maximizingPlayer)) {
|
||||
return WON;
|
||||
}
|
||||
|
||||
if (Connect4ArenaMain.isWinning(board, maximizingPlayer.opponent())) {
|
||||
return LOST;
|
||||
}
|
||||
|
||||
var moves = possibleMoves();
|
||||
if (moves.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var maxValue = alpha;
|
||||
for (var move : moves) {
|
||||
board[move] = maximizingPlayer;
|
||||
var value = -minmax(maximizingPlayer.opponent(), -beta, -maxValue, depth + 1);
|
||||
board[move] = null;
|
||||
|
||||
if (value > maxValue) {
|
||||
maxValue = value;
|
||||
if (depth == 0) {
|
||||
bestMove = move;
|
||||
}
|
||||
|
||||
if (maxValue >= beta) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int play() {
|
||||
bestMove = -1;
|
||||
minmax(myColor, LOST, WON, 0);
|
||||
return this.bestMove;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,28 @@
|
|||
package ch.bbw.m411.connect4;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RustPlayer extends Connect4ArenaMain.DefaultPlayer {
|
||||
private static native int rustPlay(byte player, byte[] board);
|
||||
|
||||
private static native int rustBoardWinner(byte[] board);
|
||||
|
||||
static {
|
||||
// This actually loads the shared object that we'll be creating.
|
||||
// The actual location of the .so or .dll may differ based on your
|
||||
// platform.
|
||||
System.loadLibrary("minmax_wrapper");
|
||||
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
var path = "target/release/" + System.mapLibraryName("minmax_wrapper");
|
||||
|
||||
try {
|
||||
System.load(new File(path).getCanonicalPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
// This actually loads the shared object that we 'll be creating.
|
||||
// The actual location of the .so or .dll may differ based on your
|
||||
// platform.
|
||||
System.loadLibrary("minmax_wrapper");
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] encodeBoard(Connect4ArenaMain.Stone[] board) {
|
||||
|
|
@ -29,6 +43,7 @@ public class RustPlayer extends Connect4ArenaMain.DefaultPlayer {
|
|||
}
|
||||
|
||||
public static boolean isWinning(Connect4ArenaMain.Stone[] board, Connect4ArenaMain.Stone forColor) {
|
||||
|
||||
byte[] boardBuf = RustPlayer.encodeBoard(board);
|
||||
byte expectedPlayer = switch (forColor) {
|
||||
case BLUE -> 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue