Made MoveGenerators protected for unit testing. Added unit test for some analysis of MoveGenerator speeds. Fixed MDP to consider all states where #turns = maxTurns terminal.
122 lines
3.1 KiB
Java
122 lines
3.1 KiB
Java
package model.comPlayer.generator;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
import model.Board;
|
|
import model.BoardScorer;
|
|
import model.Move;
|
|
import model.SearchResult;
|
|
|
|
public class AlphaBetaMoveGenerator implements MoveGenerator {
|
|
private static final int DEFAULT_RECURSIVE_PLAYS = 1;
|
|
private int lookahead = DEFAULT_RECURSIVE_PLAYS;
|
|
private final BoardScorer scorer = new BoardScorer();
|
|
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
|
|
|
|
@Override
|
|
public Move genMove(Board board, boolean asHuman) {
|
|
|
|
if (!asHuman) {
|
|
return getMaxValue(board, asHuman, lookahead * 2,
|
|
Integer.MIN_VALUE, Integer.MAX_VALUE).move;
|
|
} else {
|
|
return getMinValue(board, asHuman, lookahead * 2,
|
|
Integer.MIN_VALUE, Integer.MAX_VALUE).move;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* AlphaBetaMoveGenerator2 does not support this method.
|
|
*/
|
|
@Override
|
|
public List<Move> genMoves(Board board, boolean asHuman, int nMoves) {
|
|
Move[] doNothing = new Move[] { Move.NONE };
|
|
return Arrays.asList(doNothing);
|
|
}
|
|
|
|
private SearchResult getMaxValue(Board board, boolean asHuman,
|
|
int recursionLevel, int alpha, int beta) {
|
|
if (recursionLevel < 1) {
|
|
return new SearchResult(Move.NONE, scorer.getScore(board));
|
|
}
|
|
|
|
List<Move> validMoves = validMoveGenerator.genMoves(board, asHuman,
|
|
MoveGenerator.ALL_MOVES);
|
|
|
|
SearchResult bestResult = new SearchResult(Move.NONE, Integer.MIN_VALUE);
|
|
|
|
if (validMoves.size() == 0) {
|
|
return bestResult;
|
|
}
|
|
|
|
for (Move nextMove : validMoves) {
|
|
Board nextBoard = new Board(board);
|
|
|
|
if (!nextBoard.playTile(nextMove.getCell(), nextMove.getColor())) {
|
|
throw new RuntimeException(
|
|
"Illegal move attempted during search!");
|
|
}
|
|
|
|
SearchResult searchResult = new SearchResult(nextMove, getMinValue(
|
|
nextBoard, !asHuman, recursionLevel - 1, alpha, beta).score);
|
|
|
|
if (searchResult.compareTo(bestResult) > 0) {
|
|
bestResult = searchResult;
|
|
}
|
|
|
|
if (bestResult.score >= beta) {
|
|
return bestResult;
|
|
}
|
|
|
|
alpha = Math.max(alpha, bestResult.score);
|
|
}
|
|
|
|
return bestResult;
|
|
}
|
|
|
|
private SearchResult getMinValue(Board board, boolean asHuman,
|
|
int recursionLevel, int alpha, int beta) {
|
|
if (recursionLevel < 1) {
|
|
return new SearchResult(Move.NONE, scorer.getScore(board));
|
|
}
|
|
|
|
List<Move> validMoves = validMoveGenerator.genMoves(board, asHuman,
|
|
MoveGenerator.ALL_MOVES);
|
|
|
|
SearchResult bestResult = new SearchResult(Move.NONE, Integer.MAX_VALUE);
|
|
|
|
if (validMoves.size() == 0) {
|
|
return bestResult;
|
|
}
|
|
|
|
for (Move nextMove : validMoves) {
|
|
Board nextBoard = new Board(board);
|
|
|
|
if (!nextBoard.playTile(nextMove.getCell(), nextMove.getColor())) {
|
|
throw new RuntimeException(
|
|
"Illegal move attempted during search!");
|
|
}
|
|
|
|
SearchResult searchResult = new SearchResult(nextMove, getMaxValue(
|
|
nextBoard, !asHuman, recursionLevel - 1, alpha, beta).score);
|
|
|
|
if (searchResult.compareTo(bestResult) < 0) {
|
|
bestResult = searchResult;
|
|
}
|
|
|
|
if (bestResult.score <= alpha) {
|
|
return bestResult;
|
|
}
|
|
|
|
beta = Math.min(beta, bestResult.score);
|
|
}
|
|
|
|
return bestResult;
|
|
}
|
|
@Override
|
|
public boolean setLookahead(int lookahead) {
|
|
this.lookahead = lookahead;
|
|
return true;
|
|
}
|
|
} |