package chess.ai; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import chess.controller.CommandExecutor; import chess.controller.commands.MoveCommand; import chess.controller.commands.PromoteCommand; import chess.controller.commands.PromoteCommand.PromoteType; import chess.model.ChessBoard; import chess.model.Color; import chess.model.Coordinate; import chess.model.Move; import chess.model.Piece; public class AlphaBetaAI extends AI { private final int searchDepth; private final PieceCost pieceCost; private final PiecePosCost piecePosCost; private final GameSimulation simulation; private static final float MAX_FLOAT = Float.MAX_VALUE; private static final float MIN_FLOAT = -MAX_FLOAT; private static final int GREAT_MOVE = -9999; private static final int HORRIBLE_MOVE = -GREAT_MOVE; private final ExecutorService threadPool; public AlphaBetaAI(CommandExecutor commandExecutor, Color color, int searchDepth) { super(commandExecutor, color); this.searchDepth = searchDepth; this.pieceCost = new PieceCost(color); this.piecePosCost = new PiecePosCost(color); this.simulation = new GameSimulation(); commandExecutor.addListener(simulation); this.threadPool = Executors.newSingleThreadExecutor(); System.out.println(Runtime.getRuntime().availableProcessors()); } private float getBoardEvaluation() { final ChessBoard board = this.simulation.getBoard(); float result = 0; for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { Coordinate coordinate = new Coordinate(i, j); Piece piece = board.pieceAt(coordinate); result += pieceCost.getCost(piece) + piecePosCost.getEvaluation(piece, coordinate); } } if (this.simulation.getPlayerTurn() != color) return -result; return result; } private float getEndGameEvaluation() { Color currentTurn = this.simulation.getPlayerTurn(); if (currentTurn == this.color) { return HORRIBLE_MOVE; } else { if (this.simulation.getBoard().isKingInCheck(currentTurn)) return GREAT_MOVE; return getBoardEvaluation() + PieceCost.PAWN; } } private float getMoveValue(Move move) { this.simulation.tryMove(move); float value = -negaMax(this.searchDepth - 1, MIN_FLOAT, MAX_FLOAT); this.simulation.undoMove(); return value; } private float negaMax(int depth, float alpha, float beta) { if (depth == 0) return getBoardEvaluation(); float value = MIN_FLOAT; List moves = this.simulation.getAllowedMoves(); if (moves.isEmpty()) return getEndGameEvaluation(); for (Move move : moves) { this.simulation.tryMove(move); value = Float.max(value, -negaMax(depth - 1, -beta, -alpha)); this.simulation.undoMove(); alpha = Float.max(alpha, value); if (alpha >= beta) return value; } return value; } private Move getBestMove() { List moves = this.simulation.getAllowedMoves(); List> moveEvaluations = new ArrayList<>(50); float bestMoveValue = MIN_FLOAT; Move bestMove = null; for (Move move : moves) { moveEvaluations.add(this.threadPool.submit(() -> { return getMoveValue(move); })); } for (int i = 0; i < moves.size(); i++) { Move move = moves.get(i); float value = MIN_FLOAT; try { value = moveEvaluations.get(i).get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } if (value > bestMoveValue) { bestMoveValue = value; bestMove = move; } } System.out.println("Best move : " + bestMoveValue); return bestMove; } @Override public void onGameEnd() { this.threadPool.close(); } @Override protected void play() { long current = System.currentTimeMillis(); Move move = getBestMove(); long elapsed = System.currentTimeMillis() - current; System.out.println("Took " + elapsed + "ms"); sendCommand(new MoveCommand(move)); } @Override protected void promote(Coordinate pawnCoords) { sendCommand(new PromoteCommand(PromoteType.Queen)); } }