package vn.kienutt.chess; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /** Negamax + alpha-beta, iterative deepening, time budget and cancellation. */ public final class ChessAi { public static final class Result { public final Move move; public final int depth, score; public final long nodes; Result(Move m, int d, int s, long n) { move = m; depth = d; score = s; nodes = n; } } private static final int MATE = 100000, INF = 1000000; private long deadline, nodes; private static final class Stop extends RuntimeException { Stop() { super(null, null, false, false); } } private void budget() { if (Thread.currentThread().isInterrupted() || System.nanoTime() - deadline >= 0) throw new Stop(); } private int order(GamePosition p, Move m) { return Math.abs(p.pieceAt(m.to)) * 100 + m.promotion * 80; } private List ordered(GamePosition p, List moves) { List copy = new ArrayList<>(moves); copy.sort(Comparator.comparingInt((Move m) -> order(p, m)).reversed()); return copy; } private int negamax(GamePosition p, int depth, int alpha, int beta, int ply) { budget(); nodes++; List moves = p.legalMoves(); if (moves.isEmpty()) return p.inCheck() || p.lossOnStalemate() ? -MATE + ply : 0; if (p.drawn()) return 0; if (depth == 0) return p.evaluate(); int best = -INF; for (Move m : ordered(p, moves)) { int value = -negamax(p.after(m), depth - 1, -beta, -alpha, ply + 1); if (value > best) best = value; if (value > alpha) alpha = value; if (alpha >= beta) break; } return best; } public Result choose(GamePosition p, int maxDepth, int millis) { if (maxDepth < 1 || millis < 1) throw new IllegalArgumentException("Ngân sách không hợp lệ"); deadline = System.nanoTime() + millis * 1000000L; nodes = 0; List roots = p.legalMoves(); if (roots.isEmpty() || p.drawn()) return new Result(null, 0, 0, nodes); Move completedMove = roots.get(0); int completedDepth = 0, completedScore = 0; for (int depth = 1; depth <= maxDepth; depth++) { Move best = null; int value = -INF, alpha = -INF; try { for (Move m : ordered(p, roots)) { budget(); int v = -negamax(p.after(m), depth - 1, -INF, -alpha, 1); if (v > value) { value = v; best = m; } alpha = Math.max(alpha, v); } completedMove = best; completedDepth = depth; completedScore = value; } catch (Stop stop) { break; } } return new Result(completedMove, completedDepth, completedScore, nodes); } }