package dev.kske.chess.game.ai; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import dev.kske.chess.board.Bishop; import dev.kske.chess.board.Board; import dev.kske.chess.board.King; import dev.kske.chess.board.Knight; import dev.kske.chess.board.Move; import dev.kske.chess.board.Pawn; import dev.kske.chess.board.Piece; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Queen; import dev.kske.chess.board.Rook; /** * Project: Chess
* File: MoveProcessor.java
* Created: 08.07.2019
* * @since Chess v0.1-alpha * @author Kai S. K. Engelbart */ public class MoveProcessor implements Callable { private final Board board; private final List rootMoves; private final Color color; private final int maxDepth; private final int alphaBetaThreshold; private Move bestMove; private static final Map, int[][]> positionScores; static { positionScores = new HashMap<>(); positionScores.put(King.class, new int[][] { new int[] { -3, -4, -4, -5, -5, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -2, -3, -3, -2, -2, -2, -2, -1 }, new int[] { -1, -2, -2, -2, -2, -2, -2, -1 }, new int[] { 2, 2, 0, 0, 0, 0, 2, 2 }, new int[] { 2, 3, 1, 0, 0, 1, 3, 2 } }); positionScores.put(Queen.class, new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, -2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { 0, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 0, 0, 0, 0, -1 }, new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } }); positionScores.put(Rook.class, new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { 0, 0, 0, 1, 1, 0, 0, 0 } }); positionScores.put(Knight.class, new int[][] { new int[] { -5, -4, -3, -3, -3, -3, -4, -5 }, new int[] { -4, -2, 0, 0, 0, 0, -2, -4 }, new int[] { -3, 0, 1, 2, 2, 1, 0, -3 }, new int[] { -3, 1, 2, 2, 2, 2, 1, -3 }, new int[] { -3, 0, 2, 2, 2, 2, 0, -1 }, new int[] { -3, 1, 1, 2, 2, 1, 1, -3 }, new int[] { -4, -2, 0, 1, 1, 0, -2, -4 }, new int[] { -5, -4, -3, -3, -3, -3, -4, -5 } }); positionScores.put(Bishop.class, new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, 2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 1, 0, 0, 0, 0, 1, -1 }, new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } }); positionScores.put(Pawn.class, new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 5, 5, 5, 5, 5, 5, 5, 5 }, new int[] { 1, 1, 2, 3, 3, 2, 1, 1 }, new int[] { 0, 0, 1, 3, 3, 1, 0, 0 }, new int[] { 0, 0, 0, 2, 2, 0, 0, 0 }, new int[] { 0, 0, -1, 0, 0, -1, 0, 0 }, new int[] { 0, 1, 1, -2, -2, 1, 1, 0 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0 } }); } public MoveProcessor(Board board, List rootMoves, Color color, int maxDepth, int alphaBetaThreshold) { this.board = board; this.rootMoves = rootMoves; this.color = color; this.maxDepth = maxDepth; this.alphaBetaThreshold = alphaBetaThreshold; } @Override public ProcessingResult call() throws Exception { int score = miniMax(board, rootMoves, color, 0); return new ProcessingResult(bestMove, score); } private int miniMax(Board board, List moves, Color color, int depth) { int bestValue = Integer.MIN_VALUE; for (Move move : moves) { board.move(move); int teamValue = evaluate(board, color); int enemyValue = evaluate(board, color.opposite()); int valueChange = teamValue - enemyValue; if (depth < maxDepth && valueChange >= alphaBetaThreshold) valueChange -= miniMax(board, board.getMoves(color.opposite()), color.opposite(), depth + 1); if (valueChange > bestValue) { bestValue = valueChange; if (depth == 0) bestMove = move; } board.revert(); } return bestValue; } /** * Evaluated a board. * * @param color The color to evaluate for * @return An positive number representing how good the position is */ private int evaluate(Board board, Color color) { int score = 0; for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) if (board.getBoardArr()[i][j] != null && board.getBoardArr()[i][j].getColor() == color) { score += board.getBoardArr()[i][j].getValue(); if (positionScores.containsKey(board.getBoardArr()[i][j].getClass())) score += positionScores.get(board.getBoardArr()[i][j].getClass())[i][color == Color.WHITE ? j : 7 - j]; } return score; } }