diff --git a/pom.xml b/pom.xml
index bd4eaa6..22a906f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,7 @@
25UTF-8
- 1.10.4
+ 1.12.01.0.01.20.06.1.2
diff --git a/src/main/java/dicechess/bot/OnnxStrategy.java b/src/main/java/dicechess/bot/OnnxStrategy.java
index 16e26cd..ef2b9fe 100644
--- a/src/main/java/dicechess/bot/OnnxStrategy.java
+++ b/src/main/java/dicechess/bot/OnnxStrategy.java
@@ -1,43 +1,26 @@
package dicechess.bot;
-import dicechess.engine.domain.FenParser;
import dicechess.engine.domain.GameState;
-import dicechess.engine.search.TurnGenerator;
+import dicechess.engine.jvmapi.JvmApi;
import lv.id.jc.dicechess.runtime.TurnContext;
-import scala.collection.immutable.List;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
-import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
/**
- * Strategy implementation using an ONNX value model (or fallback engine evaluation)
- * over all legal full-turn paths generated by TurnGenerator.
+ * Strategy implementation using an ONNX value model (or fallback engine evaluation) over every legal full turn
+ * available from the current position, via the engine's {@link JvmApi} facade.
*
- *
TurnGenerator returns {@code List[List[Move]]} where {@code Move} is an opaque
- * Int with layout: bits 0-5 = destination (to), bits 6-11 = source (from),
- * bits 12-15 = flags.
+ *
{@link TurnContext#legalMoves()} is not consulted here: scoring a candidate turn needs the resulting board
+ * position, not just its UCI tokens, so the engine's own enumeration ({@link JvmApi#legalTurns(GameState)}) has to
+ * run regardless of whether the platform's inline tree was present.
*/
public class OnnxStrategy implements Strategy {
private static final Logger logger = System.getLogger(OnnxStrategy.class.getName());
- private static final MethodHandle MAKE_MOVE_MH;
-
- static {
- try {
- var clazz = Class.forName("dicechess.engine.domain.Position$package");
- var methodType = MethodType.methodType(GameState.class, GameState.class, int.class);
- MAKE_MOVE_MH = MethodHandles.lookup().findStatic(clazz, "makeMove_Move", methodType);
- } catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
private final OnnxEvaluator evaluator;
@@ -51,120 +34,40 @@ public OnnxStrategy(OnnxEvaluator evaluator) {
}
@Override
- public java.util.List chooseMoves(TurnContext context) {
+ public List chooseMoves(TurnContext context) {
if (context == null || context.dfen() == null || context.dfen().isBlank()) {
logger.log(Level.WARNING, "Received empty or null DFEN context");
return Collections.emptyList();
}
- var parseResult = FenParser.parse(context.dfen());
- if (parseResult.isLeft()) {
- var errorMsg = parseResult.left().toOption().isDefined()
- ? parseResult.left().toOption().get()
- : "Unknown FEN error";
- logger.log(Level.ERROR, "Failed to parse DFEN ''{0}'': {1}", context.dfen(), errorMsg);
+ GameState initialState;
+ try {
+ initialState = JvmApi.parseDfen(context.dfen());
+ } catch (IllegalArgumentException e) {
+ logger.log(Level.ERROR, "Failed to parse DFEN ''{0}'': {1}", context.dfen(), e.getMessage());
return Collections.emptyList();
}
- var initialState = parseResult.toOption().get();
- var activeColor = initialState.flags() & 1; // Bit 0 of flags is activeColor (0 = White, 1 = Black)
-
- // TurnGenerator returns List[List[Move]]
- @SuppressWarnings("unchecked")
- var legalTurnPaths =
- (List>) (Object) TurnGenerator.generateAllLegalTurnPaths(initialState);
+ var activeColor = JvmApi.activeColor(initialState);
+ var turns = JvmApi.legalTurns(initialState);
- if (legalTurnPaths.isEmpty()) {
+ if (turns.isEmpty()) {
logger.log(Level.INFO, "No legal turn paths available for DFEN: {0}", context.dfen());
return Collections.emptyList();
}
- java.util.List bestPath = null;
+ JvmApi.Turn bestTurn = null;
float bestScore = -Float.MAX_VALUE;
- for (var pathScala : scala.jdk.javaapi.CollectionConverters.asJava(legalTurnPaths)) {
- var pathJava = toJavaIntegerList(pathScala);
-
- // Apply sequence of Move values using makeMove_Move to get final turn state
- var currentState = initialState;
- for (var moveInt : pathJava) {
- currentState = applyMove(currentState, moveInt);
- }
-
- // Score final state from initial mover's perspective
- var score = evaluator.evaluate(currentState, activeColor);
-
- if (score > bestScore || bestPath == null) {
+ for (var turn : turns) {
+ var score = evaluator.evaluate(turn.finalState(), activeColor);
+ if (bestTurn == null || score > bestScore) {
bestScore = score;
- bestPath = pathJava;
+ bestTurn = turn;
}
}
- if (bestPath == null || bestPath.isEmpty()) {
- return Collections.emptyList();
- }
-
- var moveNotations = new ArrayList();
- for (var moveInt : bestPath) {
- moveNotations.add(moveToNotation(moveInt));
- }
-
- logger.log(Level.DEBUG, "Chosen turn path: {0} with score {1}", moveNotations, bestScore);
- return moveNotations;
- }
-
- private static GameState applyMove(GameState state, int moveInt) {
- try {
- return (GameState) MAKE_MOVE_MH.invokeExact(state, moveInt);
- } catch (Throwable e) {
- throw new IllegalStateException("Failed to apply move via engine: " + moveInt, e);
- }
- }
-
- /**
- * Converts a packed Move int to long algebraic notation (e.g. "e2e4", "e7e8q").
- *
- *
Move layout (from Move.scala):
- *
- *
Bits 0-5: destination square (to)
- *
Bits 6-11: source square (from)
- *
Bits 12-15: move flags
- *
- */
- static String moveToNotation(int move) {
- var to = move & 0x3f;
- var from = (move >>> 6) & 0x3f;
- var flags = (move >>> 12) & 0x0f;
-
- var fromFile = (char) ('a' + (from % 8));
- var fromRank = (from / 8) + 1;
- var toFile = (char) ('a' + (to % 8));
- var toRank = (to / 8) + 1;
-
- // Promotion: flags bit 3 set (flags >= 8)
- var promStr = "";
- if ((flags & 8) != 0) {
- promStr = switch (flags & 3) {
- case 0 -> "n"; // KnightPromotion(8) or KnightPromoCapture(12)
- case 1 -> "b"; // BishopPromotion(9) or BishopPromoCapture(13)
- case 2 -> "r"; // RookPromotion(10) or RookPromoCapture(14)
- case 3 -> "q"; // QueenPromotion(11) or QueenPromoCapture(15)
- default -> "";
- };
- }
-
- return "" + fromFile + fromRank + toFile + toRank + promStr;
- }
-
- private static java.util.List toJavaIntegerList(List