This repository has been archived on 2021-02-18. You can view files and clone it, but cannot push or open issues or pull requests.
chess/src/main/java/dev/kske/chess/game/ai/AIPlayer.java

117 lines
3.5 KiB
Java

package dev.kske.chess.game.ai;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
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.Game;
import dev.kske.chess.game.Player;
/**
* Project: <strong>Chess</strong><br>
* File: <strong>AIPlayer.java</strong><br>
* Created: <strong>06.07.2019</strong><br>
*
* @since Chess v0.1-alpha
* @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;
/**
* Creates an instance of {@link AIPlayer}.
*
* @param game the game in which this player will be used
* @param color the piece color this player will control
* @param maxDepth the maximum search depth
* @param alphaBetaThreshold the board evaluation threshold that has to be
* reached to continue searching the children of a
* move
*/
public AIPlayer(
Game game, Color color, int maxDepth, int alphaBetaThreshold
) {
super(game, color);
name = "AIPlayer";
availableProcessors = Runtime.getRuntime().availableProcessors();
this.maxDepth = maxDepth;
this.alphaBetaThreshold = alphaBetaThreshold;
}
@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, false);
List<Move> moves = board.getMoves(color);
// Define move processors and split the available moves between
// them.
int numThreads = Math.min(moves.size(), availableProcessors);
List<MoveProcessor> 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, false), 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<ProcessingResult> results = new ArrayList<>(numThreads);
try {
List<Future<ProcessingResult>> futures
= executor.invokeAll(processors);
for (Future<ProcessingResult> f : futures)
results.add(f.get());
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
} finally {
executor.shutdown();
}
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() {}
}