package net.woodyfolsom.msproj.policy; import java.util.Collection; import java.util.List; import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GoGame; import net.woodyfolsom.msproj.Player; import net.woodyfolsom.msproj.StateEvaluator; import net.woodyfolsom.msproj.tree.GameTreeNode; import net.woodyfolsom.msproj.tree.MinimaxProperties; public class Minimax implements Policy { private static final int DEFAULT_LOOKAHEAD = 1; private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator(); private int lookAhead; private int numStateEvaluations = 0; public Minimax() { this(DEFAULT_LOOKAHEAD); } public Minimax(int lookAhead) { this.lookAhead = lookAhead; } @Override public Action getAction(GameConfig gameConfig, GameState gameState, Player player) { numStateEvaluations = 0; StateEvaluator stateEvaluator = new StateEvaluator(gameConfig); GameTreeNode rootNode = new GameTreeNode( gameState, new MinimaxProperties()); if (player == Player.BLACK) { return getMax(lookAhead * 2, stateEvaluator, rootNode, player); } else { return getMin(lookAhead * 2, stateEvaluator, rootNode, player); } } private boolean isTerminal(int nValidMoves, int recursionLevels) { return recursionLevels == 0 || nValidMoves == 0; } private Action getMax(int recursionLevels, StateEvaluator stateEvaluator, GameTreeNode node, Player player) { GameState gameState = new GameState(node.getGameState()); List validMoves = validMoveGenerator.getActions( stateEvaluator.getGameConfig(), node.getGameState(), player, ActionGenerator.ALL_ACTIONS); boolean terminal = isTerminal(validMoves.size(), recursionLevels); double maxScore = Double.NEGATIVE_INFINITY; Action bestAction = Action.NONE; if (terminal) { node.getProperties().setReward( stateEvaluator.scoreGame(gameState).getAggregateScore()); numStateEvaluations++; return bestAction; } else { for (Action nextMove : validMoves) { GameState nextState = new GameState(gameState); nextState.playStone(player, nextMove); GameTreeNode childNode = new GameTreeNode( nextState, new MinimaxProperties()); node.addChild(nextMove, childNode); getMin(recursionLevels - 1, stateEvaluator, childNode, GoGame.getColorToPlay(player, true)); double gameScore = childNode.getProperties().getReward(); if (gameScore > maxScore) { maxScore = gameScore; bestAction = nextMove; } } node.getProperties().setReward(maxScore); return bestAction; } } private Action getMin(int recursionLevels, StateEvaluator stateEvaluator, GameTreeNode node, Player player) { GameState gameState = new GameState(node.getGameState()); List validMoves = validMoveGenerator.getActions( stateEvaluator.getGameConfig(), node.getGameState(), player, ActionGenerator.ALL_ACTIONS); boolean terminal = isTerminal(validMoves.size(), recursionLevels); double minScore = Double.POSITIVE_INFINITY; Action bestAction = Action.NONE; if (terminal) { node.getProperties().setReward( stateEvaluator.scoreGame(gameState).getAggregateScore()); numStateEvaluations++; return bestAction; } else { for (Action nextMove : validMoves) { GameState nextState = new GameState(gameState); nextState.playStone(player, nextMove); GameTreeNode childNode = new GameTreeNode( nextState, new MinimaxProperties()); node.addChild(nextMove, childNode); getMax(recursionLevels - 1, stateEvaluator, childNode, GoGame.getColorToPlay(player, true)); double gameScore = childNode.getProperties().getReward(); if (gameScore < minScore) { minScore = gameScore; bestAction = nextMove; } } node.getProperties().setReward(minScore); return bestAction; } } @Override public int getNumStateEvaluations() { return numStateEvaluations; } @Override public Action getAction(GameConfig gameConfig, GameState gameState, Collection prohibitedActions, Player player) { throw new UnsupportedOperationException("Prohibited actions not supported by this class."); } }