diff --git a/src/net/woodyfolsom/msproj/Action.java b/src/net/woodyfolsom/msproj/Action.java new file mode 100644 index 0000000..8bdde8f --- /dev/null +++ b/src/net/woodyfolsom/msproj/Action.java @@ -0,0 +1,73 @@ +package net.woodyfolsom.msproj; + +import java.util.HashMap; +import java.util.Map; + +public class Action { + private static final Map actionMap = new HashMap(); + + private char column = 'x'; + private int row = 0; + + //private Player player; + private String move; + + public static final Action NONE = new Action("NONE", /*Player.NONE,*/ 'x', 0); + public static final Action PASS = new Action("PASS", /*Player.NONE,*/ 'x', 0); + + private Action(String move, /*Player player,*/ char column, int row) { + this.move = move; + //this.player = player; + this.column = column; + this.row = row; + } + + public static final Action getInstance(/*String playerName,*/ String move) { + if (move == null || "NONE".equals(move) || "PASS".equals(move)) { + throw new IllegalArgumentException("Illegal move: " + move); + } + + if (actionMap.containsKey(move)) { + return actionMap.get(move); + } + + /* + Player player; + if ("b".equals(playerName)) { + player = Player.BLACK; + } else if ("w".equals(playerName)) { + player = Player.WHITE; + } else { + return Action.NONE; + }*/ + + char col = move.charAt(0); + int row = Integer.valueOf(move.substring(1)); + + Action action = new Action(move, col, row); + actionMap.put(move, action); + + return action; + } + + public char getColumn() { + return column; + } + + public int getRow() { + return row; + } + + public boolean isNone() { + return this == Action.NONE; + } + + public boolean isPass() { + return this == Action.PASS; + } + + @Override + public String toString() { + return move; + } +} \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/GameScore.java b/src/net/woodyfolsom/msproj/GameScore.java index eda0e01..7f535b8 100644 --- a/src/net/woodyfolsom/msproj/GameScore.java +++ b/src/net/woodyfolsom/msproj/GameScore.java @@ -28,11 +28,11 @@ public class GameScore { return NORMALIZED_ZERO_SCORE + 2 * blackScore - ((int)(2 * (whiteScore + komi))); } - public double getScore(String color) { - if ("w".equals(color)) { - return getWhiteScore(); - } else if ("b".equals(color)) { + public double getScore(Player color) { + if (color == Player.BLACK) { return getBlackScore(); + } else if (color == Player.WHITE) { + return getWhiteScore(); } else { return 0.0; } @@ -42,6 +42,14 @@ public class GameScore { return (double)whiteScore + komi; } + public boolean isWinner(String color) { + if ("w".equals(color)) { + return getWhiteScore() < NORMALIZED_ZERO_SCORE; + } else { + return getBlackScore() > NORMALIZED_ZERO_SCORE; + } + } + public String toString() { return "B: " + blackScore + "W: "+ whiteScore+"K:" + komi; } diff --git a/src/net/woodyfolsom/msproj/GameState.java b/src/net/woodyfolsom/msproj/GameState.java index 55c1cf6..4e3ea80 100644 --- a/src/net/woodyfolsom/msproj/GameState.java +++ b/src/net/woodyfolsom/msproj/GameState.java @@ -3,8 +3,6 @@ package net.woodyfolsom.msproj; import java.util.ArrayList; import java.util.List; -import net.woodyfolsom.msproj.policy.Policy; - import org.apache.log4j.Logger; @@ -14,7 +12,7 @@ public class GameState { private int blackPrisoners = 0; private int whitePrisoners = 0; private GameBoard gameBoard; - + public GameState(int size) { if (size < 1 || size > 19) { throw new IllegalArgumentException("Invalid board size: " + size); @@ -60,28 +58,19 @@ public class GameState { return whitePrisoners; } - public boolean playStone(String player, String coord) { + public boolean playStone(Player player, String move) { //Opponent passes? Just ignore it. - if (Policy.PASS.equalsIgnoreCase(coord)) { + Action action = Action.getInstance(move); + + if (action.isPass()) { return true; } - //LOGGER.info("Playing " + player + " at " + coord); - - char stoneColor; - if ("b".equals(player)) { - stoneColor = GameBoard.BLACK_STONE; - } else if ("w".equals(player)) { - stoneColor = GameBoard.WHITE_STONE; - } else { + if (action.isNone()) { return false; } - char col = coord.charAt(0); - int row = Integer.valueOf(coord.substring(1)); - - //LOGGER.info("Playing " + stoneColor + " at " + col + row); - return playStone(col,row, stoneColor); + return playStone(player, action); } /** * Places a stone at the requested coordinate. Placement is legal if the @@ -97,20 +86,22 @@ public class GameState { * @param stone * @return */ - public boolean playStone(char colLabel, int rowNum, char stoneSymbol) { - char currentStone = gameBoard.getSymbolAt(colLabel, rowNum); + public boolean playStone(Player player, Action action) { + char currentStone = gameBoard.getSymbolAt(action.getColumn(), action.getRow()); + if (currentStone != GameBoard.EMPTY_INTERSECTION) { return false; } //Place stone as requested, then check for (1) captured neighbors and (2) illegal move due to 0 liberties. - gameBoard.setSymbolAt(colLabel, rowNum, stoneSymbol); + char stoneSymbol = player.getStoneSymbol(); + gameBoard.setSymbolAt(action.getColumn(), action.getRow(), stoneSymbol); //look for captured adjacent groups and increment the prisoner counter - char opponentSymbol = GameBoard.getOpponentSymbol(stoneSymbol); + char opponentSymbol = GameBoard.getOpponentSymbol(player.getStoneSymbol()); - int col = GameBoard.getColumnIndex(colLabel); - int row = rowNum - 1; + int col = GameBoard.getColumnIndex(action.getColumn()); + int row = action.getRow() - 1; int prisonerCount = 0; if (col > 0 && gameBoard.getSymbolAt(col-1, row) == opponentSymbol) { @@ -153,8 +144,8 @@ public class GameState { } //Moved test for 0 liberties until after attempting to capture neighboring groups. - if (0 == LibertyCounter.countLiberties(gameBoard, colLabel, rowNum, stoneSymbol)) { - gameBoard.removeStone(colLabel,rowNum); + if (0 == LibertyCounter.countLiberties(gameBoard, action.getColumn(), action.getRow(), stoneSymbol)) { + gameBoard.removeStone(action.getColumn(),action.getRow()); return false; } return true; diff --git a/src/net/woodyfolsom/msproj/GoGame.java b/src/net/woodyfolsom/msproj/GoGame.java index ffa836e..30aa282 100644 --- a/src/net/woodyfolsom/msproj/GoGame.java +++ b/src/net/woodyfolsom/msproj/GoGame.java @@ -77,14 +77,24 @@ public class GoGame { case genmove: LOGGER.info("Generating move for:\n" + gameState); - String player = cmd.getStringField(1); - String nextMove = moveGenerator.getAction(gameConfig, gameState, + String playerName = cmd.getStringField(1); + + Player player; + if ("b".equals(playerName)) { + player = Player.BLACK; + } else if ("w".equals(playerName)) { + player = Player.WHITE; + } else { + throw new RuntimeException("Invalid player name - cannot generator move for:" + cmd); + } + + Action nextMove = moveGenerator.getAction(gameConfig, gameState, player); gameState.playStone(player, nextMove); LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState)); System.out.println("=" - + nextMove.toLowerCase() + "\n"); + + nextMove.toString() + "\n"); break; case komi: @@ -109,7 +119,7 @@ public class GoGame { shutDown = true; break; case play: - if (gameState.playStone(cmd.getStringField(1), cmd + if (gameState.playStone(Player.getInstance(cmd.getStringField(1)), cmd .getStringField(2).toUpperCase())) { System.out.println("=\n"); } else { @@ -147,4 +157,18 @@ public class GoGame { private static void configureLogging() { DOMConfigurator.configure("log4j.xml"); } + + public static Player getColorToPlay(Player player, boolean playAsOpponent) { + if (playAsOpponent) { + if (player == Player.WHITE) { + return Player.BLACK; + } else if (player == Player.BLACK) { + return Player.WHITE; + } else { + return Player.NONE; + } + } else { + return player; + } + } } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/Player.java b/src/net/woodyfolsom/msproj/Player.java new file mode 100644 index 0000000..328e362 --- /dev/null +++ b/src/net/woodyfolsom/msproj/Player.java @@ -0,0 +1,38 @@ +package net.woodyfolsom.msproj; + +public class Player { + public static final Player BLACK = new Player("BLACK", GameBoard.BLACK_STONE); + public static final Player NONE = new Player("NONE", GameBoard.EMPTY_INTERSECTION); + public static final Player WHITE = new Player("WHITE", GameBoard.WHITE_STONE); + + private char stoneSymbol; + private String name; + + private Player(String name, char stoneSymbol) { + this.name = name; + this.stoneSymbol = stoneSymbol; + } + + public static Player getInstance(String stoneSymbol) { + if ("b".equals(stoneSymbol)) { + return Player.BLACK; + } else if ("w".equals(stoneSymbol)) { + return Player.WHITE; + } else { + return Player.NONE; + } + } + + public char getStoneSymbol() { + return stoneSymbol; + } + + public boolean isNone() { + return "NONE".equals(name); + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/ActionGenerator.java b/src/net/woodyfolsom/msproj/policy/ActionGenerator.java index ed0166a..adf66cc 100644 --- a/src/net/woodyfolsom/msproj/policy/ActionGenerator.java +++ b/src/net/woodyfolsom/msproj/policy/ActionGenerator.java @@ -2,12 +2,14 @@ package net.woodyfolsom.msproj.policy; import java.util.List; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.Player; public interface ActionGenerator { public static final int ALL_ACTIONS = 0; - public List getActions(GameConfig gameConfig, GameState gameState, - String color, int numActions); + public List getActions(GameConfig gameConfig, GameState gameState, + Player color, int numActions); } diff --git a/src/net/woodyfolsom/msproj/policy/AlphaBeta.java b/src/net/woodyfolsom/msproj/policy/AlphaBeta.java index be966a6..62e1de1 100644 --- a/src/net/woodyfolsom/msproj/policy/AlphaBeta.java +++ b/src/net/woodyfolsom/msproj/policy/AlphaBeta.java @@ -2,55 +2,55 @@ package net.woodyfolsom.msproj.policy; 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 org.apache.log4j.Logger; public class AlphaBeta implements Policy { - //private static final Logger LOGGER = Logger.getLogger(AlphaBeta.class - // .getName()); - private static final int DEFAULT_RECURSIVE_PLAYS = 3; + private static final int DEFAULT_RECURSIVE_PLAYS = 1; private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator(); - private String bestPick = Policy.PASS; + private Action bestPick = Action.PASS; @Override - public String getAction(GameConfig gameConfig, GameState gameState, - String initialColor) { + public Action getAction(GameConfig gameConfig, GameState gameState, + Player player) { int alpha = Integer.MIN_VALUE; int beta = Integer.MAX_VALUE; - if ("b".equals(initialColor)) { - getMaxValue(gameConfig, gameState, initialColor, false, + if (player == Player.BLACK) { + getMaxValue(gameConfig, gameState, player, false, DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta); return bestPick; - } else if ("w".equals(initialColor)) { - getMinValue(gameConfig, gameState, initialColor, false, + } else if (player == Player.WHITE) { + getMinValue(gameConfig, gameState, player, false, DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta); return bestPick; } else { - return Policy.PASS; + return Action.PASS; } } private int getMaxValue(GameConfig gameConfig, GameState gameState, - String initialColor, boolean playAsOpponent, int recursionLevel, + Player initialColor, boolean playAsOpponent, int recursionLevel, int alpha, int beta) { if (terminalTest(recursionLevel)) { return getUtility(gameConfig, gameState); } - String colorPlaying = getColorToPlay(initialColor, playAsOpponent); + Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent); - List validMoves = validMoveGenerator.getActions(gameConfig, + List validMoves = validMoveGenerator.getActions(gameConfig, gameState, colorPlaying, ActionGenerator.ALL_ACTIONS); int value = Integer.MIN_VALUE; - for (String nextMove : validMoves) { + for (Action nextMove : validMoves) { GameState nextState = new GameState(gameState); if (!nextState.playStone(colorPlaying, nextMove)) { @@ -78,20 +78,20 @@ public class AlphaBeta implements Policy { } private int getMinValue(GameConfig gameConfig, GameState gameState, - String initialColor, boolean playAsOpponent, int recursionLevel, + Player initialColor, boolean playAsOpponent, int recursionLevel, int alpha, int beta) { if (terminalTest(recursionLevel)) { return getUtility(gameConfig, gameState); } - String colorPlaying = getColorToPlay(initialColor, playAsOpponent); + Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent); - List validMoves = validMoveGenerator.getActions(gameConfig, + List validMoves = validMoveGenerator.getActions(gameConfig, gameState, colorPlaying, ActionGenerator.ALL_ACTIONS); int value = Integer.MAX_VALUE; - for (String nextMove : validMoves) { + for (Action nextMove : validMoves) { GameState nextState = new GameState(gameState); if (!nextState.playStone(colorPlaying, nextMove)) { @@ -126,19 +126,4 @@ public class AlphaBeta implements Policy { StateEvaluator stateEvaluator = new StateEvaluator(gameConfig); return stateEvaluator.scoreGame(gameState).getAggregateScore(); } - - private String getColorToPlay(String color, boolean playAsOpponent) { - if (playAsOpponent) { - if ("w".equals(color)) { - return "b"; - } else if ("b".equals(color)) { - return "w"; - } else { - return "?"; // invalid color will cause randomMoveGenerator to - // PASS - } - } else { - return color; - } - } } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/GameTreeNode.java b/src/net/woodyfolsom/msproj/policy/GameTreeNode.java index 2ef927f..b826f95 100644 --- a/src/net/woodyfolsom/msproj/policy/GameTreeNode.java +++ b/src/net/woodyfolsom/msproj/policy/GameTreeNode.java @@ -4,57 +4,66 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import net.woodyfolsom.msproj.GameConfig; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameState; -public abstract class GameTreeNode { - private GameConfig gameConfig; +public class GameTreeNode { private GameState gameState; private GameTreeNode parent; - private Map children = new HashMap(); - private String player; + private int numVisits; + private int numWins; + private Map children = new HashMap(); - public GameTreeNode(GameConfig gameConfig, GameState gameState, - String player) { - this.gameConfig = gameConfig; + public GameTreeNode(GameState gameState) { this.gameState = gameState; - this.player = player; } - - public void addChild(String action, GameTreeNode child) { + + public void addChild(Action action, GameTreeNode child) { children.put(action, child); child.parent = this; } - public Set getActions() { + public Set getActions() { return children.keySet(); } - - public GameTreeNode getChild(String action) { + + public GameTreeNode getChild(Action action) { return children.get(action); } - - public int getChildrenSize() { + + public int getNumChildren() { return children.size(); } - - public GameConfig getGameConfig() { - return gameConfig; - } public GameState getGameState() { return gameState; } - public GameTreeNode getParent() { - return parent; + public int getNumVisits() { + return numVisits; } - public String getPlayer() { - return player; + public int getNumWins() { + return numWins; + } + + public GameTreeNode getParent() { + return parent; } public boolean isRoot() { return parent == null; } + + public boolean isTerminal() { + return children.size() == 0; + } + + public void incrementVisits() { + numVisits++; + } + + public void incrementWins() { + numWins++; + } } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/Minimax.java b/src/net/woodyfolsom/msproj/policy/Minimax.java index f11174c..4e8053e 100644 --- a/src/net/woodyfolsom/msproj/policy/Minimax.java +++ b/src/net/woodyfolsom/msproj/policy/Minimax.java @@ -1,47 +1,44 @@ package net.woodyfolsom.msproj.policy; import java.util.ArrayList; -//import java.util.Arrays; import java.util.List; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameScore; import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.GoGame; +import net.woodyfolsom.msproj.Player; import net.woodyfolsom.msproj.StateEvaluator; -//import org.apache.log4j.Logger; - - public class Minimax implements Policy { - //private static final Logger LOGGER = Logger.getLogger(Minimax.class.getName()); - private static final int DEFAULT_RECURSIVE_PLAYS = 1; private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator(); @Override - public String getAction(GameConfig gameConfig, GameState gameState, - String color) { + public Action getAction(GameConfig gameConfig, GameState gameState, + Player color) { MoveCandidate moveCandidate = findBestMinimaxResult( DEFAULT_RECURSIVE_PLAYS * 2, - gameConfig, gameState, color, false, Policy.PASS); + gameConfig, gameState, color, false, Action.PASS); return moveCandidate.move; } private MoveCandidate findBestMinimaxResult(int recursionLevels, GameConfig gameConfig, GameState gameState, - String initialColor, boolean playAsOpponent, String bestPrevMove) { + Player initialColor, boolean playAsOpponent, Action bestPrevMove) { StateEvaluator stateEvaluator = new StateEvaluator(gameConfig); List randomMoveCandidates = new ArrayList(); - String colorPlaying = getColorToPlay(initialColor, playAsOpponent); + Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent); - List validMoves = validMoveGenerator.getActions(gameConfig, + List validMoves = validMoveGenerator.getActions(gameConfig, gameState, colorPlaying, ActionGenerator.ALL_ACTIONS); - for (String randomMove : validMoves) { + for (Action randomMove : validMoves) { GameState stateCopy = new GameState(gameState); stateCopy.playStone(colorPlaying, randomMove); if (recursionLevels > 1) { @@ -77,19 +74,4 @@ public class Minimax implements Policy { return bestMove; } } - - private String getColorToPlay(String color, boolean playAsOpponent) { - if (playAsOpponent) { - if ("w".equals(color)) { - return "b"; - } else if ("b".equals(color)) { - return "w"; - } else { - return "?"; // invalid color will cause randomMoveGenerator to - // PASS - } - } else { - return color; - } - } } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/MonteCarlo.java b/src/net/woodyfolsom/msproj/policy/MonteCarlo.java index aacef92..a6d4615 100644 --- a/src/net/woodyfolsom/msproj/policy/MonteCarlo.java +++ b/src/net/woodyfolsom/msproj/policy/MonteCarlo.java @@ -1,61 +1,81 @@ package net.woodyfolsom.msproj.policy; +import java.util.ArrayList; +import java.util.List; + +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.Player; public abstract class MonteCarlo implements Policy { protected Policy movePolicy; protected long searchTimeLimit; + protected volatile long elapsedTime = 0L; public MonteCarlo(Policy movePolicy, long searchTimeLimit) { this.movePolicy = movePolicy; this.searchTimeLimit = searchTimeLimit; } - public abstract MonteCarloTreeNode descend(MonteCarloTreeNode node); + /** + * Descend the tree from the specified node and return a list of nodes to grow. + * + * @param node + * @return + */ + public abstract List descend(GameTreeNode node); @Override - public String getAction(GameConfig gameConfig, GameState gameState, - String initialColor) { - long initialTime = System.currentTimeMillis(); + public Action getAction(GameConfig gameConfig, GameState gameState, + Player initialColor) { + long startTime = System.currentTimeMillis(); //If for some reason no moves are evaluated within the time limit, pass. //Note that this may lose the game by forfeit even when picking any random move could //result in a win. - String bestMove = Policy.PASS; + GameTreeNode rootNode = new GameTreeNode(gameState); - MonteCarloTreeNode rootNode = new MonteCarloTreeNode(gameConfig, gameState, initialColor); - MonteCarloTreeNode selectedNode; - while (System.currentTimeMillis() - initialTime < searchTimeLimit) { + do { //TODO these return types may need to be lists for some MC methods - selectedNode = descend(rootNode); - selectedNode = grow(selectedNode); - int reward = rollout(selectedNode); - update(selectedNode, reward); - } + List selectedNodes = descend(rootNode); + List newLeaves = new ArrayList(); + + for (GameTreeNode selectedNode: selectedNodes) { + for (GameTreeNode newLeaf : grow(selectedNode)) { + newLeaves.add(newLeaf); + } + } + + for (GameTreeNode newLeaf : newLeaves) { + int reward = rollout(newLeaf); + update(newLeaf, reward); + } + + elapsedTime = System.currentTimeMillis() - startTime; + } while (elapsedTime < searchTimeLimit); - return bestMove; + return getBestAction(rootNode); } - public abstract MonteCarloTreeNode grow(MonteCarloTreeNode node); + public long getElapsedTime() { + return elapsedTime; + } - public abstract int rollout(MonteCarloTreeNode node); + public abstract Action getBestAction(GameTreeNode node); - public abstract void update(MonteCarloTreeNode node, int reward); + public abstract List grow(GameTreeNode node); - static String getColorToPlay(String color, boolean playAsOpponent) { - if (playAsOpponent) { - if ("w".equals(color)) { - return "b"; - } else if ("b".equals(color)) { - return "w"; - } else { - return "?"; // invalid color will cause randomMoveGenerator to - // PASS - } - } else { - return color; - } + public abstract int rollout(GameTreeNode node); + + public abstract void update(GameTreeNode node, int reward); + + public long getSearchTimeLimit() { + return searchTimeLimit; + } + + public int doRollout() { + return 0; } } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/MonteCarloTreeNode.java b/src/net/woodyfolsom/msproj/policy/MonteCarloTreeNode.java deleted file mode 100644 index e9bff00..0000000 --- a/src/net/woodyfolsom/msproj/policy/MonteCarloTreeNode.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.woodyfolsom.msproj.policy; - -import net.woodyfolsom.msproj.GameConfig; -import net.woodyfolsom.msproj.GameState; - -public class MonteCarloTreeNode extends GameTreeNode { - private int numVisits; - private int numWins; - - public MonteCarloTreeNode(GameConfig gameConfig, GameState gameState, - String player) { - super(gameConfig, gameState, player); - } - - public int getNumVisits() { - return numVisits; - } - - public void setNumVisits(int numVisits) { - this.numVisits = numVisits; - } - - public int getNumWins() { - return numWins; - } - - public void setNumWins(int numWins) { - this.numWins = numWins; - } -} diff --git a/src/net/woodyfolsom/msproj/policy/MonteCarloUCT.java b/src/net/woodyfolsom/msproj/policy/MonteCarloUCT.java index 829ec47..b503880 100644 --- a/src/net/woodyfolsom/msproj/policy/MonteCarloUCT.java +++ b/src/net/woodyfolsom/msproj/policy/MonteCarloUCT.java @@ -1,5 +1,9 @@ package net.woodyfolsom.msproj.policy; +import java.util.ArrayList; +import java.util.List; + +import net.woodyfolsom.msproj.Action; public class MonteCarloUCT extends MonteCarlo { @@ -8,27 +12,65 @@ public class MonteCarloUCT extends MonteCarlo { } @Override - public MonteCarloTreeNode descend(MonteCarloTreeNode node) { + public List descend(GameTreeNode node) { + double bestScore = (double) node.getNumWins() / node.getNumVisits(); + GameTreeNode bestNode = node; + + //This appears slightly redundant with getBestAction() but it is not - + //descend() may pick the current node rather than a child to expand (if a child has a good score but high/low uncertainty) + //but getBestAction specifically asks for the optimum action to take from the current node, + //even if it results in a worse next state. + for (Action action : node.getActions()) { + GameTreeNode childNode = node.getChild(action); + double childScore = (double) childNode.getNumWins() / childNode.getNumVisits(); + if (childScore >= bestScore) { + bestScore = childScore; + bestNode = childNode; + } + } + + if (bestNode == node) { + List bestNodeList = new ArrayList(); + bestNodeList.add(bestNode); + return bestNodeList; + } else { + return descend(bestNode); + } + } + + @Override + public Action getBestAction(GameTreeNode node) { + Action bestAction = Action.NONE; + double bestScore = Double.NEGATIVE_INFINITY; + + for (Action action : node.getActions()) { + GameTreeNode childNode = node.getChild(action); + double childScore = (double) childNode.getNumWins() / childNode.getNumVisits(); + if (childScore >= bestScore) { + bestScore = childScore; + bestAction = action; + } + } + + return bestAction; + } + + @Override + public List grow(GameTreeNode node) { // TODO Auto-generated method stub return null; } @Override - public MonteCarloTreeNode grow(MonteCarloTreeNode node) { - // TODO Auto-generated method stub - return null; - } - - @Override - public int rollout(MonteCarloTreeNode node) { + public int rollout(GameTreeNode node) { // TODO Auto-generated method stub return 0; } @Override - public void update(MonteCarloTreeNode node, int reward) { + public void update(GameTreeNode node, int reward) { // TODO Auto-generated method stub } - + } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/MoveCandidate.java b/src/net/woodyfolsom/msproj/policy/MoveCandidate.java index c49b9f1..b79c07a 100644 --- a/src/net/woodyfolsom/msproj/policy/MoveCandidate.java +++ b/src/net/woodyfolsom/msproj/policy/MoveCandidate.java @@ -1,12 +1,13 @@ package net.woodyfolsom.msproj.policy; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameScore; public class MoveCandidate { - public final String move; + public final Action move; public final GameScore score; - public MoveCandidate(String move, GameScore score) { + public MoveCandidate(Action move, GameScore score) { this.move = move; this.score = score; } diff --git a/src/net/woodyfolsom/msproj/policy/Policy.java b/src/net/woodyfolsom/msproj/policy/Policy.java index 79d66fd..6d62e22 100644 --- a/src/net/woodyfolsom/msproj/policy/Policy.java +++ b/src/net/woodyfolsom/msproj/policy/Policy.java @@ -1,10 +1,11 @@ package net.woodyfolsom.msproj.policy; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.Player; public interface Policy { - static final String PASS = "PASS"; - public String getAction(GameConfig gameConfig, GameState gameState, String color); + public Action getAction(GameConfig gameConfig, GameState gameState, Player player); } \ No newline at end of file diff --git a/src/net/woodyfolsom/msproj/policy/RandomMovePolicy.java b/src/net/woodyfolsom/msproj/policy/RandomMovePolicy.java index 5f2668c..cba21f8 100644 --- a/src/net/woodyfolsom/msproj/policy/RandomMovePolicy.java +++ b/src/net/woodyfolsom/msproj/policy/RandomMovePolicy.java @@ -3,8 +3,10 @@ package net.woodyfolsom.msproj.policy; import java.util.ArrayList; import java.util.List; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.Player; public class RandomMovePolicy implements Policy { @@ -12,14 +14,15 @@ public class RandomMovePolicy implements Policy { /** * Does NOT modify the gameState. */ - public String getAction(GameConfig gameConfig, GameState gameState, - String color) { + public Action getAction(GameConfig gameConfig, GameState gameState, + Player color) { GameState gameStateCopy = new GameState(gameState); List emptyCoordinates = gameStateCopy.getEmptyCoords(); while (emptyCoordinates.size() > 0) { - String randomMove = emptyCoordinates - .get((int) (Math.random() * emptyCoordinates.size())); + Action randomMove = Action.getInstance(emptyCoordinates + .get((int) (Math.random() * emptyCoordinates.size()))); + if (gameStateCopy.playStone(color, randomMove)) { return randomMove; } else { @@ -27,7 +30,7 @@ public class RandomMovePolicy implements Policy { } } - return PASS; + return Action.PASS; } /** @@ -42,15 +45,15 @@ public class RandomMovePolicy implements Policy { * * @return */ - public List genMoves(GameConfig gameConfig, GameState gameState, - String color, int nMoves) { + public List genMoves(GameConfig gameConfig, GameState gameState, + Player color, int nMoves) { GameState gameStateCopy = new GameState(gameState); List emptyCoordinates = gameStateCopy.getEmptyCoords(); - List randomMoves = new ArrayList(); + List randomMoves = new ArrayList(); while (emptyCoordinates.size() > 0 && randomMoves.size() < nMoves) { - String randomMove = emptyCoordinates - .get((int) (Math.random() * emptyCoordinates.size())); + Action randomMove = Action.getInstance(emptyCoordinates + .get((int) (Math.random() * emptyCoordinates.size()))); if (gameStateCopy.playStone(color, randomMove)) { randomMoves.add(randomMove); } @@ -58,7 +61,7 @@ public class RandomMovePolicy implements Policy { } if (randomMoves.size() == 0) { - randomMoves.add(PASS); + randomMoves.add(Action.PASS); } return randomMoves; diff --git a/src/net/woodyfolsom/msproj/policy/ValidMoveGenerator.java b/src/net/woodyfolsom/msproj/policy/ValidMoveGenerator.java index f174a78..b7dc0a9 100644 --- a/src/net/woodyfolsom/msproj/policy/ValidMoveGenerator.java +++ b/src/net/woodyfolsom/msproj/policy/ValidMoveGenerator.java @@ -3,8 +3,10 @@ package net.woodyfolsom.msproj.policy; import java.util.ArrayList; import java.util.List; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.Player; //import org.apache.log4j.Logger; @@ -12,23 +14,24 @@ public class ValidMoveGenerator implements ActionGenerator { //private static final Logger LOGGER = Logger.getLogger(ValidMoveGenerator.class.getName()); @Override - public List getActions(GameConfig gameConfig, GameState gameState, - String color, int nMoves) { + public List getActions(GameConfig gameConfig, GameState gameState, + Player color, int nMoves) { GameState gameStateCopy = new GameState(gameState); List emptyCoordinates = gameStateCopy.getEmptyCoords(); - List validMoves = new ArrayList(); + List validMoves = new ArrayList(); while (emptyCoordinates.size() > 0) { - String nextMove = emptyCoordinates.remove(emptyCoordinates.size()-1); + Action nextMove = Action.getInstance(emptyCoordinates.remove(emptyCoordinates.size()-1)); if (gameStateCopy.playStone(color, nextMove)) { validMoves.add(nextMove); gameStateCopy = new GameState(gameState); // play successful? regenerate copy of gameState } } + //Passing is always a VALID move. It may not be a GOOD move. if (validMoves.size() == 0) { - validMoves.add(Policy.PASS); + validMoves.add(Action.PASS); } return validMoves; diff --git a/test/net/woodyfolsom/msproj/CaptureTest.java b/test/net/woodyfolsom/msproj/CaptureTest.java index e6fa493..b5de7dc 100644 --- a/test/net/woodyfolsom/msproj/CaptureTest.java +++ b/test/net/woodyfolsom/msproj/CaptureTest.java @@ -4,27 +4,21 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import net.woodyfolsom.msproj.GameBoard; -import net.woodyfolsom.msproj.GameConfig; -import net.woodyfolsom.msproj.GameScore; -import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.StateEvaluator; - import org.junit.Test; public class CaptureTest { @Test public void testCapture() { GameState gameState = new GameState(5); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 3, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("B2"))); assertEquals(0,gameState.getBlackPrisoners()); assertEquals(0,gameState.getWhitePrisoners()); - assertTrue(gameState.playStone('C', 2, GameBoard.BLACK_STONE)); + assertTrue(gameState.playStone(Player.BLACK, Action.getInstance("C2"))); assertEquals(1,gameState.getBlackPrisoners()); assertEquals(0,gameState.getWhitePrisoners()); @@ -37,25 +31,25 @@ public class CaptureTest { GameConfig gameConfig = new GameConfig(); GameState gameState = new GameState(5); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 3, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - gameState.playStone('C', 4, GameBoard.BLACK_STONE); - gameState.playStone('D', 3, GameBoard.BLACK_STONE); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.BLACK, Action.getInstance("C4")); + gameState.playStone(Player.BLACK, Action.getInstance("D3")); - assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); - assertTrue(gameState.playStone('C', 3, GameBoard.WHITE_STONE)); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("B2"))); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("C3"))); assertEquals(0,gameState.getBlackPrisoners()); assertEquals(0,gameState.getWhitePrisoners()); - assertTrue(gameState.playStone('C', 2, GameBoard.BLACK_STONE)); + assertTrue(gameState.playStone(Player.BLACK, Action.getInstance("C2"))); assertEquals(2,gameState.getBlackPrisoners()); assertEquals(0,gameState.getWhitePrisoners()); - assertFalse(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); - assertFalse(gameState.playStone('C', 3, GameBoard.WHITE_STONE)); + assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("B2"))); + assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("C3"))); System.out.println(gameState); @@ -67,20 +61,21 @@ public class CaptureTest { public void testCaptureFromEye() { GameState gameState = new GameState(5); - gameState.playStone('A', 1, GameBoard.BLACK_STONE); - gameState.playStone('B', 2, GameBoard.BLACK_STONE); - gameState.playStone('C', 1, GameBoard.BLACK_STONE); - gameState.playStone('A', 2, GameBoard.WHITE_STONE); - gameState.playStone('B', 3, GameBoard.WHITE_STONE); - gameState.playStone('C', 2, GameBoard.WHITE_STONE); + gameState.playStone(Player.BLACK, Action.getInstance("A1")); + gameState.playStone(Player.BLACK, Action.getInstance("B2")); + gameState.playStone(Player.BLACK, Action.getInstance("C1")); + gameState.playStone(Player.WHITE, Action.getInstance("A2")); + gameState.playStone(Player.WHITE, Action.getInstance("B3")); + gameState.playStone(Player.WHITE, Action.getInstance("C2")); //This capture should be allowed. - assertTrue("Capture from within single eye should have been allowed but move was rejected.", - gameState.playStone('B', 1, GameBoard.WHITE_STONE)); - - assertEquals(0,gameState.getBlackPrisoners()); - assertEquals(2,gameState.getWhitePrisoners()); - + System.out.println("State before WHITE move: "); System.out.println(gameState); + + assertTrue("Capture from within single eye should have been allowed but move was rejected.", + gameState.playStone(Player.WHITE, Action.getInstance("B1"))); + + assertEquals("BLACK should have 0 prisoners.",0,gameState.getBlackPrisoners()); + assertEquals("WHITE should have 2 prisoners.",2,gameState.getWhitePrisoners()); } } diff --git a/test/net/woodyfolsom/msproj/IllegalMoveTest.java b/test/net/woodyfolsom/msproj/IllegalMoveTest.java index e102dc3..d9d0db7 100644 --- a/test/net/woodyfolsom/msproj/IllegalMoveTest.java +++ b/test/net/woodyfolsom/msproj/IllegalMoveTest.java @@ -3,9 +3,6 @@ package net.woodyfolsom.msproj; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import net.woodyfolsom.msproj.GameBoard; -import net.woodyfolsom.msproj.GameState; - import org.junit.Test; public class IllegalMoveTest { @@ -13,53 +10,57 @@ public class IllegalMoveTest { @Test public void testIllegalMoveOnOwnStone() { GameState gameState = new GameState(5); - gameState.playStone('B', 3, GameBoard.BLACK_STONE); - assertFalse(gameState.playStone('B', 3, GameBoard.BLACK_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + assertFalse(gameState.playStone(Player.BLACK, Action.getInstance("B3"))); } @Test public void testIllegalMoveOnOtherStone() { GameState gameState = new GameState(5); - gameState.playStone('B', 3, GameBoard.BLACK_STONE); - assertFalse(gameState.playStone('B', 3, GameBoard.WHITE_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("B3"))); } @Test public void testIllegalMoveNoLiberties() { GameState gameState = new GameState(5); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 3, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - gameState.playStone('C', 2, GameBoard.BLACK_STONE); - assertFalse(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + System.out.println(gameState); + assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("B2"))); System.out.println(gameState); } @Test public void testIllegalMoveFormsTrappedGroup() { GameState gameState = new GameState(9); - gameState.playStone('A', 5, GameBoard.BLACK_STONE); - gameState.playStone('B', 6, GameBoard.BLACK_STONE); - gameState.playStone('B', 7, GameBoard.BLACK_STONE); - gameState.playStone('A', 8, GameBoard.BLACK_STONE); - assertTrue(gameState.playStone('A', 6, GameBoard.WHITE_STONE)); - assertFalse(gameState.playStone('A', 7, GameBoard.WHITE_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("A5")); + gameState.playStone(Player.BLACK, Action.getInstance("B6")); + gameState.playStone(Player.BLACK, Action.getInstance("B7")); + gameState.playStone(Player.BLACK, Action.getInstance("A8")); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("A6"))); + assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("A7"))); System.out.println(gameState); } @Test public void testIllegalMoveFormsTrappedGroup2() { GameState gameState = new GameState(9); - gameState.playStone('G', 1, GameBoard.BLACK_STONE); - gameState.playStone('H', 2, GameBoard.BLACK_STONE); - gameState.playStone('H', 3, GameBoard.BLACK_STONE); - gameState.playStone('J', 4, GameBoard.BLACK_STONE); - gameState.playStone('A', 8, GameBoard.BLACK_STONE); - assertTrue(gameState.playStone('H', 1, GameBoard.WHITE_STONE)); - assertTrue(gameState.playStone('J', 2, GameBoard.WHITE_STONE)); - assertTrue(gameState.playStone('J', 3, GameBoard.WHITE_STONE)); - System.out.println(gameState); - assertFalse(gameState.playStone('J', 1, GameBoard.WHITE_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("G1")); + gameState.playStone(Player.BLACK, Action.getInstance("H2")); + gameState.playStone(Player.BLACK, Action.getInstance("H3")); + gameState.playStone(Player.BLACK, Action.getInstance("J4")); + + gameState.playStone(Player.BLACK, Action.getInstance("A8")); + + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("H1"))); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("J2"))); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("J3"))); + + System.out.println("State before move: "); System.out.println(gameState); + assertFalse("Play by WHITE at J1 should have failed.",gameState.playStone(Player.WHITE, Action.getInstance("J1"))); } } \ No newline at end of file diff --git a/test/net/woodyfolsom/msproj/LegalMoveTest.java b/test/net/woodyfolsom/msproj/LegalMoveTest.java index 6f0e499..4572a60 100644 --- a/test/net/woodyfolsom/msproj/LegalMoveTest.java +++ b/test/net/woodyfolsom/msproj/LegalMoveTest.java @@ -2,19 +2,16 @@ package net.woodyfolsom.msproj; import static org.junit.Assert.assertTrue; -import net.woodyfolsom.msproj.GameBoard; -import net.woodyfolsom.msproj.GameState; - import org.junit.Test; public class LegalMoveTest { @Test public void testLegalMove1Liberty() { GameState gameState = new GameState(5); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 3, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("B2"))); System.out.println(gameState); } } \ No newline at end of file diff --git a/test/net/woodyfolsom/msproj/StateEvaluatorTest.java b/test/net/woodyfolsom/msproj/StateEvaluatorTest.java index 0767f99..6379642 100644 --- a/test/net/woodyfolsom/msproj/StateEvaluatorTest.java +++ b/test/net/woodyfolsom/msproj/StateEvaluatorTest.java @@ -3,98 +3,96 @@ package net.woodyfolsom.msproj; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import net.woodyfolsom.msproj.GameBoard; -import net.woodyfolsom.msproj.GameConfig; -import net.woodyfolsom.msproj.GameScore; -import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.StateEvaluator; - import org.junit.Test; - public class StateEvaluatorTest { GameConfig gameConfig = new GameConfig(); - + @Test public void testScoreEmptyBoard() { GameState gameState = new GameState(5); - GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); - - assertEquals(0.0,gameScore.getWhiteScore(),0.5); - assertEquals(0.0,gameScore.getBlackScore(),0.5); + GameScore gameScore = new StateEvaluator(gameConfig) + .scoreGame(gameState); + + assertEquals(0.0, gameScore.getWhiteScore(), 0.5); + assertEquals(0.0, gameScore.getBlackScore(), 0.5); } - + @Test public void testScoreFirstMove() { GameState gameState = new GameState(5); - gameState.playStone('B',3,GameBoard.BLACK_STONE); - - GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); - + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + + GameScore gameScore = new StateEvaluator(gameConfig) + .scoreGame(gameState); + System.out.println(gameScore.getScoreReport()); - - assertEquals(0.0,gameScore.getWhiteScore(),0.5); - assertEquals(25.0,gameScore.getBlackScore(),0.5); + + assertEquals(0.0, gameScore.getWhiteScore(), 0.5); + assertEquals(25.0, gameScore.getBlackScore(), 0.5); } - + @Test public void testScoreTiedAtMove2() { GameState gameState = new GameState(5); - - gameState.playStone('B',3,GameBoard.BLACK_STONE); - gameState.playStone('A',1,GameBoard.WHITE_STONE); - GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); - + + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.WHITE, Action.getInstance("A1")); + GameScore gameScore = new StateEvaluator(gameConfig) + .scoreGame(gameState); + System.out.println(gameScore.getScoreReport()); - - assertEquals(1.0,gameScore.getWhiteScore(),0.5); - assertEquals(1.0,gameScore.getBlackScore(),0.5); + + assertEquals(1.0, gameScore.getWhiteScore(), 0.5); + assertEquals(1.0, gameScore.getBlackScore(), 0.5); } - + @Test public void testScoreTerritory() { GameState gameState = new GameState(5); - - gameState.playStone('A',2,GameBoard.BLACK_STONE); - gameState.playStone('B',3,GameBoard.BLACK_STONE); - gameState.playStone('C',2,GameBoard.BLACK_STONE); - gameState.playStone('B',1,GameBoard.BLACK_STONE); - gameState.playStone('E',5,GameBoard.WHITE_STONE); - + + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("E5")); + System.out.println(gameState); - GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); - + GameScore gameScore = new StateEvaluator(gameConfig) + .scoreGame(gameState); + System.out.println(gameScore.getScoreReport()); - - assertEquals(1.0,gameScore.getWhiteScore(),0.5); - assertEquals(6.0,gameScore.getBlackScore(),0.5); - //Black should be up by 5 if Black's territory at A1 & B2 is scored correctly. + + assertEquals(1.0, gameScore.getWhiteScore(), 0.5); + assertEquals(6.0, gameScore.getBlackScore(), 0.5); + // Black should be up by 5 if Black's territory at A1 & B2 is scored + // correctly. } - + @Test public void testCaptureAggScore() { GameState gameState = new GameState(9); - gameState.playStone('A', 2, GameBoard.WHITE_STONE); - gameState.playStone('B', 1, GameBoard.WHITE_STONE); - gameState.playStone('C', 2, GameBoard.WHITE_STONE); - gameState.playStone('B', 2, GameBoard.BLACK_STONE); - + gameState.playStone(Player.WHITE, Action.getInstance("A2")); + gameState.playStone(Player.WHITE, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("B2")); + GameState moveToA1 = new GameState(gameState); GameState capAtB3 = new GameState(gameState); - - moveToA1.playStone("w","A1"); - capAtB3.playStone("w", "B3"); - + + moveToA1.playStone(Player.WHITE, Action.getInstance("A1")); + capAtB3.playStone(Player.WHITE, Action.getInstance("B3")); + System.out.println(moveToA1); System.out.println(capAtB3); - + StateEvaluator eval = new StateEvaluator(new GameConfig()); int scoreA1 = eval.scoreGame(moveToA1).getAggregateScore(); int scoreB3 = eval.scoreGame(capAtB3).getAggregateScore(); - + System.out.println("Score at A1: " + scoreA1); System.out.println("Score at B3: " + scoreB3); - //moving as white, lower is better + // moving as white, lower is better assertTrue(scoreA1 > scoreB3); } } \ No newline at end of file diff --git a/test/net/woodyfolsom/msproj/TerritoryFinderTest.java b/test/net/woodyfolsom/msproj/TerritoryFinderTest.java index 1e27b67..40c1894 100644 --- a/test/net/woodyfolsom/msproj/TerritoryFinderTest.java +++ b/test/net/woodyfolsom/msproj/TerritoryFinderTest.java @@ -1,22 +1,18 @@ package net.woodyfolsom.msproj; -import net.woodyfolsom.msproj.GameBoard; -import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.TerritoryMarker; - import org.junit.Test; public class TerritoryFinderTest { @Test public void testMarkTerritory() { GameState gameState = new GameState(5); - - gameState.playStone('A',2,GameBoard.BLACK_STONE); - gameState.playStone('B',3,GameBoard.BLACK_STONE); - gameState.playStone('C',2,GameBoard.BLACK_STONE); - gameState.playStone('B',1,GameBoard.BLACK_STONE); - gameState.playStone('E',5,GameBoard.WHITE_STONE); - + + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B3")); + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("E5")); + TerritoryMarker.markTerritory(gameState.getGameBoard()); System.out.println(gameState); } diff --git a/test/net/woodyfolsom/msproj/policy/AlphaBetaTest.java b/test/net/woodyfolsom/msproj/policy/AlphaBetaTest.java index 80df6cd..4d58ba7 100644 --- a/test/net/woodyfolsom/msproj/policy/AlphaBetaTest.java +++ b/test/net/woodyfolsom/msproj/policy/AlphaBetaTest.java @@ -1,12 +1,10 @@ package net.woodyfolsom.msproj.policy; import static org.junit.Assert.assertEquals; - -import net.woodyfolsom.msproj.GameBoard; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.policy.AlphaBeta; -import net.woodyfolsom.msproj.policy.Policy; +import net.woodyfolsom.msproj.Player; import org.junit.Test; @@ -15,43 +13,40 @@ public class AlphaBetaTest { public void testGenmoveAsW() { Policy treeSearch = new AlphaBeta(); GameState gameState = new GameState(6); - gameState.playStone('A', 2, GameBoard.WHITE_STONE); - gameState.playStone('B', 1, GameBoard.WHITE_STONE); - gameState.playStone('C', 2, GameBoard.WHITE_STONE); - gameState.playStone('B', 2, GameBoard.BLACK_STONE); + gameState.playStone(Player.WHITE, Action.getInstance("A2")); + gameState.playStone(Player.WHITE, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("B2")); - String move = treeSearch.getAction(new GameConfig(), gameState, "b"); + Action move = treeSearch.getAction(new GameConfig(), gameState, Player.WHITE); + System.out.println(gameState); System.out.println("Generated move: " + move); - assertEquals("Expected B3 but was: " + move, "B3", move); - gameState.playStone("b", move); - + assertEquals("Expected B3 but was: " + move, Action.getInstance("B3"), move); + gameState.playStone(Player.WHITE, move); + System.out.println("Final board state:"); System.out.println(gameState); - - assertEquals(Policy.PASS, - treeSearch.getAction(new GameConfig(), gameState, "?")); } @Test public void testGenmoveAsB() { Policy treeSearch = new AlphaBeta(); GameState gameState = new GameState(6); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - gameState.playStone('C', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 2, GameBoard.WHITE_STONE); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + gameState.playStone(Player.WHITE, Action.getInstance("B2")); - String move = treeSearch.getAction(new GameConfig(), gameState, "b"); + Action move = treeSearch.getAction(new GameConfig(), gameState, Player.BLACK); + System.out.println(gameState); System.out.println("Generated move: " + move); - assertEquals("Expected B3 but was: " + move, "B3", move); - gameState.playStone("b", move); - + assertEquals("Expected B3 but was: " + move, Action.getInstance("B3"), move); + gameState.playStone(Player.BLACK, move); + + System.out.println("Final board state:"); System.out.println(gameState); - - assertEquals(Policy.PASS, - treeSearch.getAction(new GameConfig(), gameState, "?")); } } \ No newline at end of file diff --git a/test/net/woodyfolsom/msproj/policy/MinimaxTest.java b/test/net/woodyfolsom/msproj/policy/MinimaxTest.java index 148a47e..8dc0923 100644 --- a/test/net/woodyfolsom/msproj/policy/MinimaxTest.java +++ b/test/net/woodyfolsom/msproj/policy/MinimaxTest.java @@ -1,34 +1,53 @@ package net.woodyfolsom.msproj.policy; import static org.junit.Assert.assertEquals; - -import net.woodyfolsom.msproj.GameBoard; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.policy.Policy; +import net.woodyfolsom.msproj.Player; import org.junit.Test; - public class MinimaxTest { - @Test - public void testGenmove() { - Policy moveGenerator = new Minimax(); - GameState gameState = new GameState(5); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - gameState.playStone('C', 2, GameBoard.BLACK_STONE); - gameState.playStone('B', 4, GameBoard.BLACK_STONE); - - String move = moveGenerator.getAction(new GameConfig(), gameState, "w"); - System.out.println("Generated move: " + move); - gameState.playStone("w", move); - + public void testGenmoveAsW() { + Policy treeSearch = new Minimax(); + GameState gameState = new GameState(6); + gameState.playStone(Player.WHITE, Action.getInstance("A2")); + gameState.playStone(Player.WHITE, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("B2")); + + Action move = treeSearch.getAction(new GameConfig(), gameState, + Player.WHITE); + System.out.println(gameState); - - assertEquals(Policy.PASS,moveGenerator.getAction(new GameConfig(), gameState, "?")); - + + System.out.println("Generated move: " + move); + assertEquals("Expected B3 but was: " + move, "B3", move); + gameState.playStone(Player.WHITE, move); + System.out.println(gameState); } -} + + @Test + public void testGenmoveAsB() { + Policy treeSearch = new Minimax(); + GameState gameState = new GameState(6); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + gameState.playStone(Player.WHITE, Action.getInstance("B2")); + + Action move = treeSearch.getAction(new GameConfig(), gameState, + Player.BLACK); + + System.out.println(gameState); + + System.out.println("Generated move: " + move); + assertEquals("Expected B3 but was: " + move, "B3", move); + gameState.playStone(Player.BLACK, move); + + System.out.println(gameState); + } +} \ No newline at end of file diff --git a/test/net/woodyfolsom/msproj/policy/MonteCarloUCTTest.java b/test/net/woodyfolsom/msproj/policy/MonteCarloUCTTest.java new file mode 100644 index 0000000..297a047 --- /dev/null +++ b/test/net/woodyfolsom/msproj/policy/MonteCarloUCTTest.java @@ -0,0 +1,53 @@ +package net.woodyfolsom.msproj.policy; + +import static org.junit.Assert.assertEquals; + +import net.woodyfolsom.msproj.Action; +import net.woodyfolsom.msproj.GameConfig; +import net.woodyfolsom.msproj.GameState; +import net.woodyfolsom.msproj.Player; +import net.woodyfolsom.msproj.policy.Policy; + +import org.junit.Test; + +public class MonteCarloUCTTest { + @Test + public void testGenmoveAsW() { + Policy treeSearch = new MonteCarloUCT(new RandomMovePolicy(),2000L); + GameState gameState = new GameState(6); + gameState.playStone(Player.WHITE, Action.getInstance("A2")); + gameState.playStone(Player.WHITE, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("B2")); + + Action move = treeSearch.getAction(new GameConfig(), gameState, Player.WHITE); + + System.out.println(gameState); + + System.out.println("Generated move: " + move); + assertEquals("Expected B3 but was: " + move, "B3", move); + gameState.playStone(Player.WHITE, move); + + System.out.println(gameState); + } + + @Test + public void testGenmoveAsB() { + Policy treeSearch = new MonteCarloUCT(new RandomMovePolicy(),2000L); + GameState gameState = new GameState(6); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("B1")); + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + gameState.playStone(Player.WHITE, Action.getInstance("B2")); + + Action move = treeSearch.getAction(new GameConfig(), gameState, Player.BLACK); + + System.out.println(gameState); + + System.out.println("Generated move: " + move); + assertEquals("Expected B3 but was: " + move, "B3", move); + gameState.playStone(Player.BLACK, move); + + System.out.println(gameState); + } +} \ No newline at end of file diff --git a/test/net/woodyfolsom/msproj/policy/RandomTest.java b/test/net/woodyfolsom/msproj/policy/RandomTest.java index b6f9415..8934784 100644 --- a/test/net/woodyfolsom/msproj/policy/RandomTest.java +++ b/test/net/woodyfolsom/msproj/policy/RandomTest.java @@ -1,53 +1,56 @@ package net.woodyfolsom.msproj.policy; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import net.woodyfolsom.msproj.GameBoard; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.policy.Policy; -import net.woodyfolsom.msproj.policy.RandomMovePolicy; +import net.woodyfolsom.msproj.Player; import org.junit.Test; public class RandomTest { - @Test - public void testGenmove() { + @Test(expected = IllegalArgumentException.class) + public void testGenmoveForNone() { Policy moveGenerator = new RandomMovePolicy(); GameState gameState = new GameState(5); - moveGenerator.getAction(new GameConfig(), gameState, "b"); + moveGenerator.getAction(new GameConfig(), gameState, Player.BLACK); gameState = new GameState(5); - moveGenerator.getAction(new GameConfig(), gameState, "w"); + moveGenerator.getAction(new GameConfig(), gameState, Player.WHITE); - assertEquals(Policy.PASS,moveGenerator.getAction(new GameConfig(), gameState, "?")); - - System.out.println(gameState); + assertEquals(Action.PASS, moveGenerator.getAction(new GameConfig(), gameState, Player.NONE)); } @Test public void testAlternativeToIllegalMove() { GameState gameState = new GameState(4); - gameState.playStone('A', 1, GameBoard.BLACK_STONE); - gameState.playStone('A', 2, GameBoard.BLACK_STONE); - gameState.playStone('A', 3, GameBoard.BLACK_STONE); - gameState.playStone('A', 4, GameBoard.BLACK_STONE); - gameState.playStone('B', 1, GameBoard.BLACK_STONE); - gameState.playStone('B', 2, GameBoard.BLACK_STONE); + gameState.playStone(Player.BLACK, Action.getInstance("A1")); + gameState.playStone(Player.BLACK, Action.getInstance("A2")); + gameState.playStone(Player.BLACK, Action.getInstance("A3")); + gameState.playStone(Player.BLACK, Action.getInstance("A4")); + gameState.playStone(Player.BLACK, Action.getInstance("B1"));; + gameState.playStone(Player.BLACK, Action.getInstance("B2")); + //gameState.playStone('B', 3, GameBoard.BLACK_STONE); - gameState.playStone('B', 4, GameBoard.BLACK_STONE); - gameState.playStone('C', 2, GameBoard.BLACK_STONE); - gameState.playStone('C', 3, GameBoard.BLACK_STONE); - gameState.playStone('C', 4, GameBoard.BLACK_STONE); - gameState.playStone('D', 4, GameBoard.BLACK_STONE); - assertTrue(gameState.playStone('C', 1, GameBoard.WHITE_STONE)); - assertTrue(gameState.playStone('D', 2, GameBoard.WHITE_STONE)); - assertTrue(gameState.playStone('D', 3, GameBoard.WHITE_STONE)); + + gameState.playStone(Player.BLACK, Action.getInstance("B4")); + + gameState.playStone(Player.BLACK, Action.getInstance("C2")); + gameState.playStone(Player.BLACK, Action.getInstance("C3")); + gameState.playStone(Player.BLACK, Action.getInstance("C4")); + + gameState.playStone(Player.BLACK, Action.getInstance("D4")); + + gameState.playStone(Player.WHITE, Action.getInstance("C1")); + gameState.playStone(Player.WHITE, Action.getInstance("D2")); + gameState.playStone(Player.WHITE, Action.getInstance("D3")); + + System.out.println("State before random WHITE move selection:"); System.out.println(gameState); //This is correct - checked vs. MFOG - assertEquals("B3", new RandomMovePolicy().getAction(new GameConfig(), gameState, "w")); + assertEquals(Action.getInstance("B3"), new RandomMovePolicy().getAction(new GameConfig(), gameState, Player.WHITE)); + System.out.println(gameState); } } diff --git a/test/net/woodyfolsom/msproj/policy/ValidMoveGeneratorTest.java b/test/net/woodyfolsom/msproj/policy/ValidMoveGeneratorTest.java index 9e240a7..d2ec6af 100644 --- a/test/net/woodyfolsom/msproj/policy/ValidMoveGeneratorTest.java +++ b/test/net/woodyfolsom/msproj/policy/ValidMoveGeneratorTest.java @@ -1,13 +1,14 @@ package net.woodyfolsom.msproj.policy; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.util.List; -import net.woodyfolsom.msproj.GameBoard; +import net.woodyfolsom.msproj.Action; import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameState; -import net.woodyfolsom.msproj.policy.ValidMoveGenerator; +import net.woodyfolsom.msproj.Player; import org.junit.Test; @@ -26,18 +27,22 @@ public class ValidMoveGeneratorTest { A B C D E */ GameState gameState = new GameState(5); - gameState.playStone('A', 2, GameBoard.WHITE_STONE); - gameState.playStone('B', 1, GameBoard.WHITE_STONE); - gameState.playStone('B', 4, GameBoard.WHITE_STONE); - gameState.playStone('C', 2, GameBoard.WHITE_STONE); - assertFalse(gameState.playStone('A', 1, GameBoard.BLACK_STONE)); + gameState.playStone(Player.WHITE, Action.getInstance("A2")); + gameState.playStone(Player.WHITE, Action.getInstance("B1")); + gameState.playStone(Player.WHITE, Action.getInstance("B4")); + gameState.playStone(Player.WHITE, Action.getInstance("C2")); + + assertFalse(gameState.playStone(Player.BLACK, Action.getInstance("A1"))); + + List validMoves = new ValidMoveGenerator().getActions(new GameConfig(), gameState, Player.BLACK,0); - List validMoves = new ValidMoveGenerator().getActions(new GameConfig(), gameState, "b",0); assertTrue(validMoves.size() > 0); - for (String vm : validMoves) { + + for (Action vm : validMoves) { System.out.println(vm); } - assertFalse(validMoves.contains("A1")); + + assertFalse(validMoves.contains(Action.getInstance("A1"))); System.out.println(gameState); }