package dev.kske.chess.game.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 java.util.concurrent.TimeUnit; import javax.swing.SwingUtilities; import dev.kske.chess.board.Board; import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.game.Player; /** * Project: Chess
* File: AIPlayer.java
* Created: 06.07.2019
* Author: Kai S. K. Engelbart */ public class AIPlayer extends Player { private int availableProcessors; private int maxDepth; private int alphaBetaThreshold; private volatile boolean exitRequested; private volatile ExecutorService executor; public AIPlayer(Color color, int maxDepth, int alphaBetaThreshold) { super(color); name = "AIPlayer"; availableProcessors = Runtime.getRuntime().availableProcessors(); this.maxDepth = maxDepth; this.alphaBetaThreshold = alphaBetaThreshold; exitRequested = false; } @Override public void requestMove() { exitRequested = false; /* * Define some processing threads, split the available moves between them and * retrieve the result after their execution. */ new Thread(() -> { /* * Get a copy of the board and the available moves. */ Board board = new Board(this.board); List moves = board.getMoves(color); /* * Define move processors and split the available moves between them. */ int numThreads = Math.min(moves.size(), availableProcessors); List processors = new ArrayList<>(numThreads); final int step = moves.size() / numThreads; int rem = moves.size() % numThreads; int beginIndex = 0, endIndex = 0; for (int i = 0; i < numThreads; i++) { if (rem-- > 0) ++endIndex; endIndex += step; processors.add(new MoveProcessor(new Board(board), moves.subList(beginIndex, endIndex), color, maxDepth, alphaBetaThreshold)); beginIndex = endIndex; } /* * Execute processors, get the best result and pass it back to the Game class */ executor = Executors.newFixedThreadPool(numThreads); List results = new ArrayList<>(numThreads); try { List> futures = executor.invokeAll(processors); for (Future f : futures) results.add(f.get()); executor.shutdown(); } catch (InterruptedException | ExecutionException ex) { ex.printStackTrace(); } results.sort((r1, r2) -> Integer.compare(r2.score, r1.score)); if (!exitRequested) SwingUtilities.invokeLater(() -> game.onMove(this, results.get(0).move)); }, "AIPlayer calculation setup").start(); } @Override public void cancelMove() { exitRequested = true; if (executor != null) { executor.shutdownNow(); try { executor.awaitTermination(500, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void disconnect() {} }