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/MoveProcessor.java

284 lines
6.2 KiB
Java

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.*;
import dev.kske.chess.board.Piece.Color;
/**
* Implements a basic minimax move search algorithm for testing purposes.<br>
* <br>
* Project: <strong>Chess</strong><br>
* File: <strong>MoveProcessor.java</strong><br>
* Created: <strong>08.07.2019</strong><br>
*
* @since Chess v0.1-alpha
* @author Kai S. K. Engelbart
*/
public class MoveProcessor implements Callable<ProcessingResult> {
private final Board board;
private final List<Move> rootMoves;
private final Color color;
private final int maxDepth;
private final int alphaBetaThreshold;
private Move bestMove;
private static final Map<Class<? extends Piece>, 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
}
}
);
}
/**
* Creates an instance of {@link MoveProcessor}.
*
* @param board the board to search
* @param rootMoves the moves on which the search is based
* @param color the color for which to search
* @param maxDepth the maximal recursion depth to search to
* @param alphaBetaThreshold the threshold necessary to continue a search
* for a
* specific move
*/
public MoveProcessor(
Board board, List<Move> 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<Move> 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 board the board to evaluate
* @param color The color to evaluate for
* @return a 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;
}
}