Refactoring in progress.

Player and Action classes are now singletons (factory pattern) rather than String values.
Implementing more general treesearch code for minimax, alpha-beta, monte carlo using simplified backup logic.
This commit is contained in:
cs6601
2012-08-30 08:41:03 -04:00
parent b44b666663
commit 2e40440838
26 changed files with 647 additions and 433 deletions

View File

@@ -0,0 +1,73 @@
package net.woodyfolsom.msproj;
import java.util.HashMap;
import java.util.Map;
public class Action {
private static final Map<String, Action> actionMap = new HashMap<String,Action>();
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;
}
}

View File

@@ -28,11 +28,11 @@ public class GameScore {
return NORMALIZED_ZERO_SCORE + 2 * blackScore - ((int)(2 * (whiteScore + komi))); return NORMALIZED_ZERO_SCORE + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
} }
public double getScore(String color) { public double getScore(Player color) {
if ("w".equals(color)) { if (color == Player.BLACK) {
return getWhiteScore();
} else if ("b".equals(color)) {
return getBlackScore(); return getBlackScore();
} else if (color == Player.WHITE) {
return getWhiteScore();
} else { } else {
return 0.0; return 0.0;
} }
@@ -42,6 +42,14 @@ public class GameScore {
return (double)whiteScore + komi; 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() { public String toString() {
return "B: " + blackScore + "W: "+ whiteScore+"K:" + komi; return "B: " + blackScore + "W: "+ whiteScore+"K:" + komi;
} }

View File

@@ -3,8 +3,6 @@ package net.woodyfolsom.msproj;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.woodyfolsom.msproj.policy.Policy;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@@ -60,28 +58,19 @@ public class GameState {
return whitePrisoners; return whitePrisoners;
} }
public boolean playStone(String player, String coord) { public boolean playStone(Player player, String move) {
//Opponent passes? Just ignore it. //Opponent passes? Just ignore it.
if (Policy.PASS.equalsIgnoreCase(coord)) { Action action = Action.getInstance(move);
if (action.isPass()) {
return true; return true;
} }
//LOGGER.info("Playing " + player + " at " + coord); if (action.isNone()) {
char stoneColor;
if ("b".equals(player)) {
stoneColor = GameBoard.BLACK_STONE;
} else if ("w".equals(player)) {
stoneColor = GameBoard.WHITE_STONE;
} else {
return false; return false;
} }
char col = coord.charAt(0); return playStone(player, action);
int row = Integer.valueOf(coord.substring(1));
//LOGGER.info("Playing " + stoneColor + " at " + col + row);
return playStone(col,row, stoneColor);
} }
/** /**
* Places a stone at the requested coordinate. Placement is legal if the * Places a stone at the requested coordinate. Placement is legal if the
@@ -97,20 +86,22 @@ public class GameState {
* @param stone * @param stone
* @return * @return
*/ */
public boolean playStone(char colLabel, int rowNum, char stoneSymbol) { public boolean playStone(Player player, Action action) {
char currentStone = gameBoard.getSymbolAt(colLabel, rowNum); char currentStone = gameBoard.getSymbolAt(action.getColumn(), action.getRow());
if (currentStone != GameBoard.EMPTY_INTERSECTION) { if (currentStone != GameBoard.EMPTY_INTERSECTION) {
return false; return false;
} }
//Place stone as requested, then check for (1) captured neighbors and (2) illegal move due to 0 liberties. //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 //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 col = GameBoard.getColumnIndex(action.getColumn());
int row = rowNum - 1; int row = action.getRow() - 1;
int prisonerCount = 0; int prisonerCount = 0;
if (col > 0 && gameBoard.getSymbolAt(col-1, row) == opponentSymbol) { 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. //Moved test for 0 liberties until after attempting to capture neighboring groups.
if (0 == LibertyCounter.countLiberties(gameBoard, colLabel, rowNum, stoneSymbol)) { if (0 == LibertyCounter.countLiberties(gameBoard, action.getColumn(), action.getRow(), stoneSymbol)) {
gameBoard.removeStone(colLabel,rowNum); gameBoard.removeStone(action.getColumn(),action.getRow());
return false; return false;
} }
return true; return true;

View File

@@ -77,14 +77,24 @@ public class GoGame {
case genmove: case genmove:
LOGGER.info("Generating move for:\n" + gameState); LOGGER.info("Generating move for:\n" + gameState);
String player = cmd.getStringField(1); String playerName = cmd.getStringField(1);
String nextMove = moveGenerator.getAction(gameConfig, gameState,
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); player);
gameState.playStone(player, nextMove); gameState.playStone(player, nextMove);
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState)); LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
System.out.println("=" System.out.println("="
+ nextMove.toLowerCase() + "\n"); + nextMove.toString() + "\n");
break; break;
case komi: case komi:
@@ -109,7 +119,7 @@ public class GoGame {
shutDown = true; shutDown = true;
break; break;
case play: case play:
if (gameState.playStone(cmd.getStringField(1), cmd if (gameState.playStone(Player.getInstance(cmd.getStringField(1)), cmd
.getStringField(2).toUpperCase())) { .getStringField(2).toUpperCase())) {
System.out.println("=\n"); System.out.println("=\n");
} else { } else {
@@ -147,4 +157,18 @@ public class GoGame {
private static void configureLogging() { private static void configureLogging() {
DOMConfigurator.configure("log4j.xml"); 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;
}
}
} }

View File

@@ -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;
}
}

View File

@@ -2,12 +2,14 @@ package net.woodyfolsom.msproj.policy;
import java.util.List; import java.util.List;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
public interface ActionGenerator { public interface ActionGenerator {
public static final int ALL_ACTIONS = 0; public static final int ALL_ACTIONS = 0;
public List<String> getActions(GameConfig gameConfig, GameState gameState, public List<Action> getActions(GameConfig gameConfig, GameState gameState,
String color, int numActions); Player color, int numActions);
} }

View File

@@ -2,55 +2,55 @@ package net.woodyfolsom.msproj.policy;
import java.util.List; import java.util.List;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.GoGame;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator; import net.woodyfolsom.msproj.StateEvaluator;
//import org.apache.log4j.Logger;
public class AlphaBeta implements Policy { public class AlphaBeta implements Policy {
//private static final Logger LOGGER = Logger.getLogger(AlphaBeta.class private static final int DEFAULT_RECURSIVE_PLAYS = 1;
// .getName());
private static final int DEFAULT_RECURSIVE_PLAYS = 3;
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator(); private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
private String bestPick = Policy.PASS; private Action bestPick = Action.PASS;
@Override @Override
public String getAction(GameConfig gameConfig, GameState gameState, public Action getAction(GameConfig gameConfig, GameState gameState,
String initialColor) { Player player) {
int alpha = Integer.MIN_VALUE; int alpha = Integer.MIN_VALUE;
int beta = Integer.MAX_VALUE; int beta = Integer.MAX_VALUE;
if ("b".equals(initialColor)) { if (player == Player.BLACK) {
getMaxValue(gameConfig, gameState, initialColor, false, getMaxValue(gameConfig, gameState, player, false,
DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta); DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta);
return bestPick; return bestPick;
} else if ("w".equals(initialColor)) { } else if (player == Player.WHITE) {
getMinValue(gameConfig, gameState, initialColor, false, getMinValue(gameConfig, gameState, player, false,
DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta); DEFAULT_RECURSIVE_PLAYS * 2, alpha, beta);
return bestPick; return bestPick;
} else { } else {
return Policy.PASS; return Action.PASS;
} }
} }
private int getMaxValue(GameConfig gameConfig, GameState gameState, private int getMaxValue(GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, int recursionLevel, Player initialColor, boolean playAsOpponent, int recursionLevel,
int alpha, int beta) { int alpha, int beta) {
if (terminalTest(recursionLevel)) { if (terminalTest(recursionLevel)) {
return getUtility(gameConfig, gameState); return getUtility(gameConfig, gameState);
} }
String colorPlaying = getColorToPlay(initialColor, playAsOpponent); Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent);
List<String> validMoves = validMoveGenerator.getActions(gameConfig, List<Action> validMoves = validMoveGenerator.getActions(gameConfig,
gameState, colorPlaying, ActionGenerator.ALL_ACTIONS); gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);
int value = Integer.MIN_VALUE; int value = Integer.MIN_VALUE;
for (String nextMove : validMoves) { for (Action nextMove : validMoves) {
GameState nextState = new GameState(gameState); GameState nextState = new GameState(gameState);
if (!nextState.playStone(colorPlaying, nextMove)) { if (!nextState.playStone(colorPlaying, nextMove)) {
@@ -78,20 +78,20 @@ public class AlphaBeta implements Policy {
} }
private int getMinValue(GameConfig gameConfig, GameState gameState, private int getMinValue(GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, int recursionLevel, Player initialColor, boolean playAsOpponent, int recursionLevel,
int alpha, int beta) { int alpha, int beta) {
if (terminalTest(recursionLevel)) { if (terminalTest(recursionLevel)) {
return getUtility(gameConfig, gameState); return getUtility(gameConfig, gameState);
} }
String colorPlaying = getColorToPlay(initialColor, playAsOpponent); Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent);
List<String> validMoves = validMoveGenerator.getActions(gameConfig, List<Action> validMoves = validMoveGenerator.getActions(gameConfig,
gameState, colorPlaying, ActionGenerator.ALL_ACTIONS); gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);
int value = Integer.MAX_VALUE; int value = Integer.MAX_VALUE;
for (String nextMove : validMoves) { for (Action nextMove : validMoves) {
GameState nextState = new GameState(gameState); GameState nextState = new GameState(gameState);
if (!nextState.playStone(colorPlaying, nextMove)) { if (!nextState.playStone(colorPlaying, nextMove)) {
@@ -126,19 +126,4 @@ public class AlphaBeta implements Policy {
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig); StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
return stateEvaluator.scoreGame(gameState).getAggregateScore(); 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;
}
}
} }

View File

@@ -4,57 +4,66 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
public abstract class GameTreeNode { public class GameTreeNode {
private GameConfig gameConfig;
private GameState gameState; private GameState gameState;
private GameTreeNode parent; private GameTreeNode parent;
private Map<String, GameTreeNode> children = new HashMap<String, GameTreeNode>(); private int numVisits;
private String player; private int numWins;
private Map<Action, GameTreeNode> children = new HashMap<Action, GameTreeNode>();
public GameTreeNode(GameConfig gameConfig, GameState gameState, public GameTreeNode(GameState gameState) {
String player) {
this.gameConfig = gameConfig;
this.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); children.put(action, child);
child.parent = this; child.parent = this;
} }
public Set<String> getActions() { public Set<Action> getActions() {
return children.keySet(); return children.keySet();
} }
public GameTreeNode getChild(String action) { public GameTreeNode getChild(Action action) {
return children.get(action); return children.get(action);
} }
public int getChildrenSize() { public int getNumChildren() {
return children.size(); return children.size();
} }
public GameConfig getGameConfig() {
return gameConfig;
}
public GameState getGameState() { public GameState getGameState() {
return gameState; return gameState;
} }
public GameTreeNode getParent() { public int getNumVisits() {
return parent; return numVisits;
} }
public String getPlayer() { public int getNumWins() {
return player; return numWins;
}
public GameTreeNode getParent() {
return parent;
} }
public boolean isRoot() { public boolean isRoot() {
return parent == null; return parent == null;
} }
public boolean isTerminal() {
return children.size() == 0;
}
public void incrementVisits() {
numVisits++;
}
public void incrementWins() {
numWins++;
}
} }

View File

@@ -1,47 +1,44 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import java.util.ArrayList; import java.util.ArrayList;
//import java.util.Arrays;
import java.util.List; import java.util.List;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameScore; import net.woodyfolsom.msproj.GameScore;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.GoGame;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator; import net.woodyfolsom.msproj.StateEvaluator;
//import org.apache.log4j.Logger;
public class Minimax implements Policy { public class Minimax implements Policy {
//private static final Logger LOGGER = Logger.getLogger(Minimax.class.getName());
private static final int DEFAULT_RECURSIVE_PLAYS = 1; private static final int DEFAULT_RECURSIVE_PLAYS = 1;
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator(); private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
@Override @Override
public String getAction(GameConfig gameConfig, GameState gameState, public Action getAction(GameConfig gameConfig, GameState gameState,
String color) { Player color) {
MoveCandidate moveCandidate = findBestMinimaxResult( MoveCandidate moveCandidate = findBestMinimaxResult(
DEFAULT_RECURSIVE_PLAYS * 2, DEFAULT_RECURSIVE_PLAYS * 2,
gameConfig, gameState, color, false, Policy.PASS); gameConfig, gameState, color, false, Action.PASS);
return moveCandidate.move; return moveCandidate.move;
} }
private MoveCandidate findBestMinimaxResult(int recursionLevels, private MoveCandidate findBestMinimaxResult(int recursionLevels,
GameConfig gameConfig, GameState gameState, GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, String bestPrevMove) { Player initialColor, boolean playAsOpponent, Action bestPrevMove) {
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig); StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
List<MoveCandidate> randomMoveCandidates = new ArrayList<MoveCandidate>(); List<MoveCandidate> randomMoveCandidates = new ArrayList<MoveCandidate>();
String colorPlaying = getColorToPlay(initialColor, playAsOpponent); Player colorPlaying = GoGame.getColorToPlay(initialColor, playAsOpponent);
List<String> validMoves = validMoveGenerator.getActions(gameConfig, List<Action> validMoves = validMoveGenerator.getActions(gameConfig,
gameState, colorPlaying, ActionGenerator.ALL_ACTIONS); gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);
for (String randomMove : validMoves) { for (Action randomMove : validMoves) {
GameState stateCopy = new GameState(gameState); GameState stateCopy = new GameState(gameState);
stateCopy.playStone(colorPlaying, randomMove); stateCopy.playStone(colorPlaying, randomMove);
if (recursionLevels > 1) { if (recursionLevels > 1) {
@@ -77,19 +74,4 @@ public class Minimax implements Policy {
return bestMove; 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;
}
}
} }

View File

@@ -1,61 +1,81 @@
package net.woodyfolsom.msproj.policy; 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.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
public abstract class MonteCarlo implements Policy { public abstract class MonteCarlo implements Policy {
protected Policy movePolicy; protected Policy movePolicy;
protected long searchTimeLimit; protected long searchTimeLimit;
protected volatile long elapsedTime = 0L;
public MonteCarlo(Policy movePolicy, long searchTimeLimit) { public MonteCarlo(Policy movePolicy, long searchTimeLimit) {
this.movePolicy = movePolicy; this.movePolicy = movePolicy;
this.searchTimeLimit = searchTimeLimit; 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<GameTreeNode> descend(GameTreeNode node);
@Override @Override
public String getAction(GameConfig gameConfig, GameState gameState, public Action getAction(GameConfig gameConfig, GameState gameState,
String initialColor) { Player initialColor) {
long initialTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
//If for some reason no moves are evaluated within the time limit, pass. //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 //Note that this may lose the game by forfeit even when picking any random move could
//result in a win. //result in a win.
String bestMove = Policy.PASS; GameTreeNode rootNode = new GameTreeNode(gameState);
MonteCarloTreeNode rootNode = new MonteCarloTreeNode(gameConfig, gameState, initialColor); do {
MonteCarloTreeNode selectedNode;
while (System.currentTimeMillis() - initialTime < searchTimeLimit) {
//TODO these return types may need to be lists for some MC methods //TODO these return types may need to be lists for some MC methods
selectedNode = descend(rootNode); List<GameTreeNode> selectedNodes = descend(rootNode);
selectedNode = grow(selectedNode); List<GameTreeNode> newLeaves = new ArrayList<GameTreeNode>();
int reward = rollout(selectedNode);
update(selectedNode, reward); for (GameTreeNode selectedNode: selectedNodes) {
for (GameTreeNode newLeaf : grow(selectedNode)) {
newLeaves.add(newLeaf);
}
} }
return bestMove; for (GameTreeNode newLeaf : newLeaves) {
int reward = rollout(newLeaf);
update(newLeaf, reward);
} }
public abstract MonteCarloTreeNode grow(MonteCarloTreeNode node); elapsedTime = System.currentTimeMillis() - startTime;
} while (elapsedTime < searchTimeLimit);
public abstract int rollout(MonteCarloTreeNode node); return getBestAction(rootNode);
}
public abstract void update(MonteCarloTreeNode node, int reward); public long getElapsedTime() {
return elapsedTime;
}
static String getColorToPlay(String color, boolean playAsOpponent) { public abstract Action getBestAction(GameTreeNode node);
if (playAsOpponent) {
if ("w".equals(color)) { public abstract List<GameTreeNode> grow(GameTreeNode node);
return "b";
} else if ("b".equals(color)) { public abstract int rollout(GameTreeNode node);
return "w";
} else { public abstract void update(GameTreeNode node, int reward);
return "?"; // invalid color will cause randomMoveGenerator to
// PASS public long getSearchTimeLimit() {
} return searchTimeLimit;
} else {
return color;
} }
public int doRollout() {
return 0;
} }
} }

View File

@@ -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;
}
}

View File

@@ -1,5 +1,9 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import java.util.ArrayList;
import java.util.List;
import net.woodyfolsom.msproj.Action;
public class MonteCarloUCT extends MonteCarlo { public class MonteCarloUCT extends MonteCarlo {
@@ -8,25 +12,63 @@ public class MonteCarloUCT extends MonteCarlo {
} }
@Override @Override
public MonteCarloTreeNode descend(MonteCarloTreeNode node) { public List<GameTreeNode> 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<GameTreeNode> bestNodeList = new ArrayList<GameTreeNode>();
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<GameTreeNode> grow(GameTreeNode node) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@Override @Override
public MonteCarloTreeNode grow(MonteCarloTreeNode node) { public int rollout(GameTreeNode node) {
// TODO Auto-generated method stub
return null;
}
@Override
public int rollout(MonteCarloTreeNode node) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return 0; return 0;
} }
@Override @Override
public void update(MonteCarloTreeNode node, int reward) { public void update(GameTreeNode node, int reward) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }

View File

@@ -1,12 +1,13 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameScore; import net.woodyfolsom.msproj.GameScore;
public class MoveCandidate { public class MoveCandidate {
public final String move; public final Action move;
public final GameScore score; public final GameScore score;
public MoveCandidate(String move, GameScore score) { public MoveCandidate(Action move, GameScore score) {
this.move = move; this.move = move;
this.score = score; this.score = score;
} }

View File

@@ -1,10 +1,11 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
public interface Policy { public interface Policy {
static final String PASS = "PASS"; public Action getAction(GameConfig gameConfig, GameState gameState, Player player);
public String getAction(GameConfig gameConfig, GameState gameState, String color);
} }

View File

@@ -3,8 +3,10 @@ package net.woodyfolsom.msproj.policy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
public class RandomMovePolicy implements Policy { public class RandomMovePolicy implements Policy {
@@ -12,14 +14,15 @@ public class RandomMovePolicy implements Policy {
/** /**
* Does NOT modify the gameState. * Does NOT modify the gameState.
*/ */
public String getAction(GameConfig gameConfig, GameState gameState, public Action getAction(GameConfig gameConfig, GameState gameState,
String color) { Player color) {
GameState gameStateCopy = new GameState(gameState); GameState gameStateCopy = new GameState(gameState);
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords(); List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
while (emptyCoordinates.size() > 0) { while (emptyCoordinates.size() > 0) {
String randomMove = emptyCoordinates Action randomMove = Action.getInstance(emptyCoordinates
.get((int) (Math.random() * emptyCoordinates.size())); .get((int) (Math.random() * emptyCoordinates.size())));
if (gameStateCopy.playStone(color, randomMove)) { if (gameStateCopy.playStone(color, randomMove)) {
return randomMove; return randomMove;
} else { } 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 * @return
*/ */
public List<String> genMoves(GameConfig gameConfig, GameState gameState, public List<Action> genMoves(GameConfig gameConfig, GameState gameState,
String color, int nMoves) { Player color, int nMoves) {
GameState gameStateCopy = new GameState(gameState); GameState gameStateCopy = new GameState(gameState);
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords(); List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
List<String> randomMoves = new ArrayList<String>(); List<Action> randomMoves = new ArrayList<Action>();
while (emptyCoordinates.size() > 0 && randomMoves.size() < nMoves) { while (emptyCoordinates.size() > 0 && randomMoves.size() < nMoves) {
String randomMove = emptyCoordinates Action randomMove = Action.getInstance(emptyCoordinates
.get((int) (Math.random() * emptyCoordinates.size())); .get((int) (Math.random() * emptyCoordinates.size())));
if (gameStateCopy.playStone(color, randomMove)) { if (gameStateCopy.playStone(color, randomMove)) {
randomMoves.add(randomMove); randomMoves.add(randomMove);
} }
@@ -58,7 +61,7 @@ public class RandomMovePolicy implements Policy {
} }
if (randomMoves.size() == 0) { if (randomMoves.size() == 0) {
randomMoves.add(PASS); randomMoves.add(Action.PASS);
} }
return randomMoves; return randomMoves;

View File

@@ -3,8 +3,10 @@ package net.woodyfolsom.msproj.policy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
//import org.apache.log4j.Logger; //import org.apache.log4j.Logger;
@@ -12,23 +14,24 @@ public class ValidMoveGenerator implements ActionGenerator {
//private static final Logger LOGGER = Logger.getLogger(ValidMoveGenerator.class.getName()); //private static final Logger LOGGER = Logger.getLogger(ValidMoveGenerator.class.getName());
@Override @Override
public List<String> getActions(GameConfig gameConfig, GameState gameState, public List<Action> getActions(GameConfig gameConfig, GameState gameState,
String color, int nMoves) { Player color, int nMoves) {
GameState gameStateCopy = new GameState(gameState); GameState gameStateCopy = new GameState(gameState);
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords(); List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
List<String> validMoves = new ArrayList<String>(); List<Action> validMoves = new ArrayList<Action>();
while (emptyCoordinates.size() > 0) { 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)) { if (gameStateCopy.playStone(color, nextMove)) {
validMoves.add(nextMove); validMoves.add(nextMove);
gameStateCopy = new GameState(gameState); // play successful? regenerate copy of gameState 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) { if (validMoves.size() == 0) {
validMoves.add(Policy.PASS); validMoves.add(Action.PASS);
} }
return validMoves; return validMoves;

View File

@@ -4,27 +4,21 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; 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; import org.junit.Test;
public class CaptureTest { public class CaptureTest {
@Test @Test
public void testCapture() { public void testCapture() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("B2")));
assertEquals(0,gameState.getBlackPrisoners()); assertEquals(0,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners()); 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(1,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners()); assertEquals(0,gameState.getWhitePrisoners());
@@ -37,25 +31,25 @@ public class CaptureTest {
GameConfig gameConfig = new GameConfig(); GameConfig gameConfig = new GameConfig();
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
gameState.playStone('C', 4, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C4"));
gameState.playStone('D', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("D3"));
assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("B2")));
assertTrue(gameState.playStone('C', 3, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("C3")));
assertEquals(0,gameState.getBlackPrisoners()); assertEquals(0,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners()); 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(2,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners()); assertEquals(0,gameState.getWhitePrisoners());
assertFalse(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("B2")));
assertFalse(gameState.playStone('C', 3, GameBoard.WHITE_STONE)); assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("C3")));
System.out.println(gameState); System.out.println(gameState);
@@ -67,20 +61,21 @@ public class CaptureTest {
public void testCaptureFromEye() { public void testCaptureFromEye() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A1"));
gameState.playStone('B', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B2"));
gameState.playStone('C', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C1"));
gameState.playStone('A', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("A2"));
gameState.playStone('B', 3, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B3"));
gameState.playStone('C', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("C2"));
//This capture should be allowed. //This capture should be allowed.
assertTrue("Capture from within single eye should have been allowed but move was rejected.", System.out.println("State before WHITE move: ");
gameState.playStone('B', 1, GameBoard.WHITE_STONE));
assertEquals(0,gameState.getBlackPrisoners());
assertEquals(2,gameState.getWhitePrisoners());
System.out.println(gameState); 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());
} }
} }

View File

@@ -3,9 +3,6 @@ package net.woodyfolsom.msproj;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameState;
import org.junit.Test; import org.junit.Test;
public class IllegalMoveTest { public class IllegalMoveTest {
@@ -13,53 +10,57 @@ public class IllegalMoveTest {
@Test @Test
public void testIllegalMoveOnOwnStone() { public void testIllegalMoveOnOwnStone() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('B', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
assertFalse(gameState.playStone('B', 3, GameBoard.BLACK_STONE)); assertFalse(gameState.playStone(Player.BLACK, Action.getInstance("B3")));
} }
@Test @Test
public void testIllegalMoveOnOtherStone() { public void testIllegalMoveOnOtherStone() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('B', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
assertFalse(gameState.playStone('B', 3, GameBoard.WHITE_STONE)); assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("B3")));
} }
@Test @Test
public void testIllegalMoveNoLiberties() { public void testIllegalMoveNoLiberties() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('C', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C2"));
assertFalse(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); System.out.println(gameState);
assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("B2")));
System.out.println(gameState); System.out.println(gameState);
} }
@Test @Test
public void testIllegalMoveFormsTrappedGroup() { public void testIllegalMoveFormsTrappedGroup() {
GameState gameState = new GameState(9); GameState gameState = new GameState(9);
gameState.playStone('A', 5, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A5"));
gameState.playStone('B', 6, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B6"));
gameState.playStone('B', 7, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B7"));
gameState.playStone('A', 8, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A8"));
assertTrue(gameState.playStone('A', 6, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("A6")));
assertFalse(gameState.playStone('A', 7, GameBoard.WHITE_STONE)); assertFalse(gameState.playStone(Player.WHITE, Action.getInstance("A7")));
System.out.println(gameState); System.out.println(gameState);
} }
@Test @Test
public void testIllegalMoveFormsTrappedGroup2() { public void testIllegalMoveFormsTrappedGroup2() {
GameState gameState = new GameState(9); GameState gameState = new GameState(9);
gameState.playStone('G', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("G1"));
gameState.playStone('H', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("H2"));
gameState.playStone('H', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("H3"));
gameState.playStone('J', 4, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("J4"));
gameState.playStone('A', 8, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('H', 1, GameBoard.WHITE_STONE)); gameState.playStone(Player.BLACK, Action.getInstance("A8"));
assertTrue(gameState.playStone('J', 2, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('J', 3, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("H1")));
System.out.println(gameState); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("J2")));
assertFalse(gameState.playStone('J', 1, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("J3")));
System.out.println("State before move: ");
System.out.println(gameState); System.out.println(gameState);
assertFalse("Play by WHITE at J1 should have failed.",gameState.playStone(Player.WHITE, Action.getInstance("J1")));
} }
} }

View File

@@ -2,19 +2,16 @@ package net.woodyfolsom.msproj;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameState;
import org.junit.Test; import org.junit.Test;
public class LegalMoveTest { public class LegalMoveTest {
@Test @Test
public void testLegalMove1Liberty() { public void testLegalMove1Liberty() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE)); assertTrue(gameState.playStone(Player.WHITE, Action.getInstance("B2")));
System.out.println(gameState); System.out.println(gameState);
} }
} }

View File

@@ -3,22 +3,16 @@ package net.woodyfolsom.msproj;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; 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; import org.junit.Test;
public class StateEvaluatorTest { public class StateEvaluatorTest {
GameConfig gameConfig = new GameConfig(); GameConfig gameConfig = new GameConfig();
@Test @Test
public void testScoreEmptyBoard() { public void testScoreEmptyBoard() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); GameScore gameScore = new StateEvaluator(gameConfig)
.scoreGame(gameState);
assertEquals(0.0, gameScore.getWhiteScore(), 0.5); assertEquals(0.0, gameScore.getWhiteScore(), 0.5);
assertEquals(0.0, gameScore.getBlackScore(), 0.5); assertEquals(0.0, gameScore.getBlackScore(), 0.5);
@@ -27,9 +21,10 @@ public class StateEvaluatorTest {
@Test @Test
public void testScoreFirstMove() { public void testScoreFirstMove() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('B',3,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); GameScore gameScore = new StateEvaluator(gameConfig)
.scoreGame(gameState);
System.out.println(gameScore.getScoreReport()); System.out.println(gameScore.getScoreReport());
@@ -41,9 +36,10 @@ public class StateEvaluatorTest {
public void testScoreTiedAtMove2() { public void testScoreTiedAtMove2() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('B',3,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('A',1,GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("A1"));
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); GameScore gameScore = new StateEvaluator(gameConfig)
.scoreGame(gameState);
System.out.println(gameScore.getScoreReport()); System.out.println(gameScore.getScoreReport());
@@ -55,35 +51,37 @@ public class StateEvaluatorTest {
public void testScoreTerritory() { public void testScoreTerritory() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A',2,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B',3,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('C',2,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C2"));
gameState.playStone('B',1,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
gameState.playStone('E',5,GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("E5"));
System.out.println(gameState); System.out.println(gameState);
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState); GameScore gameScore = new StateEvaluator(gameConfig)
.scoreGame(gameState);
System.out.println(gameScore.getScoreReport()); System.out.println(gameScore.getScoreReport());
assertEquals(1.0, gameScore.getWhiteScore(), 0.5); assertEquals(1.0, gameScore.getWhiteScore(), 0.5);
assertEquals(6.0, gameScore.getBlackScore(), 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. // Black should be up by 5 if Black's territory at A1 & B2 is scored
// correctly.
} }
@Test @Test
public void testCaptureAggScore() { public void testCaptureAggScore() {
GameState gameState = new GameState(9); GameState gameState = new GameState(9);
gameState.playStone('A', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("A2"));
gameState.playStone('B', 1, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B1"));
gameState.playStone('C', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("C2"));
gameState.playStone('B', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B2"));
GameState moveToA1 = new GameState(gameState); GameState moveToA1 = new GameState(gameState);
GameState capAtB3 = new GameState(gameState); GameState capAtB3 = new GameState(gameState);
moveToA1.playStone("w","A1"); moveToA1.playStone(Player.WHITE, Action.getInstance("A1"));
capAtB3.playStone("w", "B3"); capAtB3.playStone(Player.WHITE, Action.getInstance("B3"));
System.out.println(moveToA1); System.out.println(moveToA1);
System.out.println(capAtB3); System.out.println(capAtB3);

View File

@@ -1,9 +1,5 @@
package net.woodyfolsom.msproj; package net.woodyfolsom.msproj;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.TerritoryMarker;
import org.junit.Test; import org.junit.Test;
public class TerritoryFinderTest { public class TerritoryFinderTest {
@@ -11,11 +7,11 @@ public class TerritoryFinderTest {
public void testMarkTerritory() { public void testMarkTerritory() {
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A',2,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B',3,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B3"));
gameState.playStone('C',2,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C2"));
gameState.playStone('B',1,GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
gameState.playStone('E',5,GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("E5"));
TerritoryMarker.markTerritory(gameState.getGameBoard()); TerritoryMarker.markTerritory(gameState.getGameBoard());
System.out.println(gameState); System.out.println(gameState);

View File

@@ -1,12 +1,10 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.policy.AlphaBeta; import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.policy.Policy;
import org.junit.Test; import org.junit.Test;
@@ -15,43 +13,40 @@ public class AlphaBetaTest {
public void testGenmoveAsW() { public void testGenmoveAsW() {
Policy treeSearch = new AlphaBeta(); Policy treeSearch = new AlphaBeta();
GameState gameState = new GameState(6); GameState gameState = new GameState(6);
gameState.playStone('A', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("A2"));
gameState.playStone('B', 1, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B1"));
gameState.playStone('C', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("C2"));
gameState.playStone('B', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B2"));
Action move = treeSearch.getAction(new GameConfig(), gameState, Player.WHITE);
String move = treeSearch.getAction(new GameConfig(), gameState, "b");
System.out.println(gameState); System.out.println(gameState);
System.out.println("Generated move: " + move); System.out.println("Generated move: " + move);
assertEquals("Expected B3 but was: " + move, "B3", move); assertEquals("Expected B3 but was: " + move, Action.getInstance("B3"), move);
gameState.playStone("b", move); gameState.playStone(Player.WHITE, move);
System.out.println("Final board state:");
System.out.println(gameState); System.out.println(gameState);
assertEquals(Policy.PASS,
treeSearch.getAction(new GameConfig(), gameState, "?"));
} }
@Test @Test
public void testGenmoveAsB() { public void testGenmoveAsB() {
Policy treeSearch = new AlphaBeta(); Policy treeSearch = new AlphaBeta();
GameState gameState = new GameState(6); GameState gameState = new GameState(6);
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
gameState.playStone('C', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C2"));
gameState.playStone('B', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B2"));
Action move = treeSearch.getAction(new GameConfig(), gameState, Player.BLACK);
String move = treeSearch.getAction(new GameConfig(), gameState, "b");
System.out.println(gameState); System.out.println(gameState);
System.out.println("Generated move: " + move); System.out.println("Generated move: " + move);
assertEquals("Expected B3 but was: " + move, "B3", move); assertEquals("Expected B3 but was: " + move, Action.getInstance("B3"), move);
gameState.playStone("b", move); gameState.playStone(Player.BLACK, move);
System.out.println("Final board state:");
System.out.println(gameState); System.out.println(gameState);
assertEquals(Policy.PASS,
treeSearch.getAction(new GameConfig(), gameState, "?"));
} }
} }

View File

@@ -1,33 +1,52 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.policy.Policy; import net.woodyfolsom.msproj.Player;
import org.junit.Test; import org.junit.Test;
public class MinimaxTest { public class MinimaxTest {
@Test
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);
System.out.println("Generated move: " + move);
assertEquals("Expected B3 but was: " + move, "B3", move);
gameState.playStone(Player.WHITE, move);
System.out.println(gameState);
}
@Test @Test
public void testGenmove() { public void testGenmoveAsB() {
Policy moveGenerator = new Minimax(); Policy treeSearch = new Minimax();
GameState gameState = new GameState(5); GameState gameState = new GameState(6);
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));
gameState.playStone('C', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C2"));
gameState.playStone('B', 4, GameBoard.BLACK_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B2"));
Action move = treeSearch.getAction(new GameConfig(), gameState,
Player.BLACK);
System.out.println(gameState);
String move = moveGenerator.getAction(new GameConfig(), gameState, "w");
System.out.println("Generated move: " + move); System.out.println("Generated move: " + move);
gameState.playStone("w", move); assertEquals("Expected B3 but was: " + move, "B3", move);
gameState.playStone(Player.BLACK, move);
System.out.println(gameState);
assertEquals(Policy.PASS,moveGenerator.getAction(new GameConfig(), gameState, "?"));
System.out.println(gameState); System.out.println(gameState);
} }

View File

@@ -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);
}
}

View File

@@ -1,53 +1,56 @@
package net.woodyfolsom.msproj.policy; package net.woodyfolsom.msproj.policy;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameBoard;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.policy.Policy; import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
import org.junit.Test; import org.junit.Test;
public class RandomTest { public class RandomTest {
@Test @Test(expected = IllegalArgumentException.class)
public void testGenmove() { public void testGenmoveForNone() {
Policy moveGenerator = new RandomMovePolicy(); Policy moveGenerator = new RandomMovePolicy();
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
moveGenerator.getAction(new GameConfig(), gameState, "b"); moveGenerator.getAction(new GameConfig(), gameState, Player.BLACK);
gameState = new GameState(5); 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, "?")); assertEquals(Action.PASS, moveGenerator.getAction(new GameConfig(), gameState, Player.NONE));
System.out.println(gameState);
} }
@Test @Test
public void testAlternativeToIllegalMove() { public void testAlternativeToIllegalMove() {
GameState gameState = new GameState(4); GameState gameState = new GameState(4);
gameState.playStone('A', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A1"));
gameState.playStone('A', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A2"));
gameState.playStone('A', 3, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A3"));
gameState.playStone('A', 4, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("A4"));
gameState.playStone('B', 1, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B1"));;
gameState.playStone('B', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B2"));
//gameState.playStone('B', 3, GameBoard.BLACK_STONE); //gameState.playStone('B', 3, GameBoard.BLACK_STONE);
gameState.playStone('B', 4, GameBoard.BLACK_STONE);
gameState.playStone('C', 2, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("B4"));
gameState.playStone('C', 3, GameBoard.BLACK_STONE);
gameState.playStone('C', 4, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C2"));
gameState.playStone('D', 4, GameBoard.BLACK_STONE); gameState.playStone(Player.BLACK, Action.getInstance("C3"));
assertTrue(gameState.playStone('C', 1, GameBoard.WHITE_STONE)); gameState.playStone(Player.BLACK, Action.getInstance("C4"));
assertTrue(gameState.playStone('D', 2, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('D', 3, GameBoard.WHITE_STONE)); 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); System.out.println(gameState);
//This is correct - checked vs. MFOG //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); System.out.println(gameState);
} }
} }

View File

@@ -1,13 +1,14 @@
package net.woodyfolsom.msproj.policy; 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 java.util.List;
import net.woodyfolsom.msproj.GameBoard; import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig; import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState; import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.policy.ValidMoveGenerator; import net.woodyfolsom.msproj.Player;
import org.junit.Test; import org.junit.Test;
@@ -26,18 +27,22 @@ public class ValidMoveGeneratorTest {
A B C D E A B C D E
*/ */
GameState gameState = new GameState(5); GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("A2"));
gameState.playStone('B', 1, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B1"));
gameState.playStone('B', 4, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("B4"));
gameState.playStone('C', 2, GameBoard.WHITE_STONE); gameState.playStone(Player.WHITE, Action.getInstance("C2"));
assertFalse(gameState.playStone('A', 1, GameBoard.BLACK_STONE));
assertFalse(gameState.playStone(Player.BLACK, Action.getInstance("A1")));
List<Action> validMoves = new ValidMoveGenerator().getActions(new GameConfig(), gameState, Player.BLACK,0);
List<String> validMoves = new ValidMoveGenerator().getActions(new GameConfig(), gameState, "b",0);
assertTrue(validMoves.size() > 0); assertTrue(validMoves.size() > 0);
for (String vm : validMoves) {
for (Action vm : validMoves) {
System.out.println(vm); System.out.println(vm);
} }
assertFalse(validMoves.contains("A1"));
assertFalse(validMoves.contains(Action.getInstance("A1")));
System.out.println(gameState); System.out.println(gameState);
} }