Consolidated StateEvaluator class into GameState.getResult(). Fixed some issues with komi and normalized score calculation.

This commit is contained in:
2012-10-15 18:13:18 -04:00
parent def3b5979b
commit db1bed43b4
18 changed files with 400 additions and 298 deletions

View File

@@ -12,34 +12,48 @@ public class Action {
//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);
public static final Action NONE = new Action("NONE", 'x', 0);
public static final Action PASS = new Action("PASS", 'x', 0);
public static final Action RESIGN = new Action("RESIGN", 'x', 0);
private Action(String move, /*Player player,*/ char column, int row) {
private Action(String move, char column, int row) {
this.move = move;
//this.player = player;
this.column = column;
this.row = row;
}
public static final Action getInstance(String move) {
if (move == null || "NONE".equals(move)) {
throw new IllegalArgumentException("Illegal move: " + move);
if (move.length() < 2) {
return Action.NONE;
}
if ("NONE".equals(move)) {
return Action.NONE;
}
if ("PASS".equals(move)) {
return Action.PASS;
}
if ("RESIGN".equals(move)) {
return Action.RESIGN;
}
if (actionMap.containsKey(move)) {
return actionMap.get(move);
}
char col = move.charAt(0);
int row = Integer.valueOf(move.substring(1));
Action action = Action.NONE;
Action action = new Action(move, col, row);
actionMap.put(move, action);
try {
char col = move.charAt(0);
int row = Integer.valueOf(move.substring(1));
action = new Action(move, col, row);
actionMap.put(move, action);
} catch (NumberFormatException nfe) {
return Action.NONE;
}
return action;
}
@@ -60,6 +74,10 @@ public class Action {
return this == Action.PASS;
}
public boolean isResign() {
return this == Action.RESIGN;
}
@Override
public String toString() {
return move;

View File

@@ -0,0 +1,100 @@
package net.woodyfolsom.msproj;
public class GameResult {
public static final GameResult BLACK_BY_RESIGNATION = new GameResult(RESULT_TYPE.BLACK_BY_RESIGNATION);
public static final GameResult WHITE_BY_RESIGNATION = new GameResult(RESULT_TYPE.WHITE_BY_RESIGNATION);
private double komi;
private int blackScore;
private int whiteScore;
private int normalizedZeroScore;
public enum RESULT_TYPE { BLACK_BY_RESIGNATION, WHITE_BY_RESIGNATION, IN_PROGRESS, SCORED}
private RESULT_TYPE resultType;
public GameResult(int blackScore, int whiteScore, int size, double komi, boolean finished) {
this.blackScore = blackScore;
this.komi = komi;
this.whiteScore = whiteScore;
this.normalizedZeroScore = size * size + ((int)(komi * 2));
if (finished) {
resultType = RESULT_TYPE.SCORED;
} else {
resultType = RESULT_TYPE.IN_PROGRESS;
}
}
private GameResult(RESULT_TYPE resultType) {
komi = 0.0;
blackScore = 0;
whiteScore = 0;
normalizedZeroScore = 0;
this.resultType = resultType;
}
public double getBlackScore() {
return blackScore;
}
public int getNormalizedZeroScore() {
return normalizedZeroScore;
}
/**
* Gets a representation for the game score as an integer. Lower numbers are better for white.
* The minimum value is 0 (Chinese scoring - white owns every intersection on 19x19 and has 9 stone komi).
* Likewise, the maximum value if 379x2 (black owns every intersection with zero komi).
* @return
*/
public int getNormalizedScore() {
return normalizedZeroScore + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
}
public double getScore(Player color) {
if (color == Player.BLACK) {
return getBlackScore();
} else if (color == Player.WHITE) {
return getWhiteScore();
} else {
return 0.0;
}
}
public double getWhiteScore() {
return (double)whiteScore + komi;
}
public boolean isWinner(Player player) {
if (Player.WHITE == player) {
return whiteScore + komi > blackScore;
} else {
return blackScore > whiteScore + komi;
}
}
public String toString() {
switch (resultType) {
case BLACK_BY_RESIGNATION :
return "B+R";
case SCORED :
double blackScore = getBlackScore();
double whiteScore = getWhiteScore();
if (blackScore > whiteScore) {
return "B+" + (whiteScore - blackScore);
} else if (whiteScore > blackScore) {
return "W+" + (whiteScore - blackScore);
} else {
return "DRAW";
}
case WHITE_BY_RESIGNATION :
return "W+R";
case IN_PROGRESS :
return "Void";
default :
return "?";
}
}
}

View File

@@ -1,79 +0,0 @@
package net.woodyfolsom.msproj;
public class GameScore {
private double komi;
private int blackScore;
private int whiteScore;
private int normalizedZeroScore;
public GameScore(int size) {
}
public GameScore(int blackScore, int whiteScore, int size, double komi) {
this.blackScore = blackScore;
this.komi = komi;
this.whiteScore = whiteScore;
this.normalizedZeroScore = size * size + ((int)(komi * 2));
}
public double getBlackScore() {
return (double)blackScore - komi;
}
public int getNormalizedZeroScore() {
return normalizedZeroScore;
}
/**
* Gets a representation for the game score as an integer. Lower numbers are better for white.
* The minimum value is 0 (Chinese scoring - white owns every intersection on 19x19 and has 9 stone komi).
* Likewise, the maximum value if 379x2 (black owns every intersection with zero komi).
* @return
*/
public int getAggregateScore() {
return normalizedZeroScore + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
}
public double getScore(Player color) {
if (color == Player.BLACK) {
return getBlackScore();
} else if (color == Player.WHITE) {
return getWhiteScore();
} else {
return 0.0;
}
}
public double getWhiteScore() {
return (double)whiteScore + komi;
}
public boolean isWinner(Player player) {
if (Player.WHITE == player) {
return whiteScore + komi > blackScore;
} else {
return blackScore > whiteScore + komi;
}
}
public String toString() {
return "B: " + blackScore + "W: "+ whiteScore+"K:" + komi;
}
public String getScoreReport() {
double blackScore = getBlackScore();
double whiteScore = getWhiteScore();
boolean gameTied = Math.abs(blackScore - whiteScore) < 0.5;
if (gameTied) {
return "Black +0 (tie)";
} else if (blackScore > whiteScore) {
return "Black +"
+ (blackScore - whiteScore - komi);
} else {
return "White +"
+ (whiteScore + komi - blackScore);
}
}
}

View File

@@ -41,6 +41,50 @@ public class GameState {
return blackPrisoners;
}
public GameResult getResult() {
GameBoard markedBoard;
if (gameBoard.isTerritoryMarked()) {
markedBoard = gameBoard;
} else {
markedBoard = new GameBoard(gameBoard);
TerritoryMarker.markTerritory(markedBoard);
}
int gameLength = moveHistory.size();
//This functionality duplicates isTerminal(),
//however for the result to be reported, we need to know
//how the game was terminated to differentiate between resignation, scored complete
//and (eventually) time out/forfeit.
boolean isFinished = false;
if (gameLength >= 1) {
if (moveHistory.get(gameLength-1).isResign()) {
if (gameLength % 2 == 1) {
return GameResult.WHITE_BY_RESIGNATION;
} else {
return GameResult.BLACK_BY_RESIGNATION;
}
}
if (gameLength >= 2) {
if (moveHistory.get(gameLength-1).isPass()
&& moveHistory.get(gameLength-2).isPass()) {
isFinished = true;
}
}
}
int blackScore = gameBoard.countSymbols(GameBoard.BLACK_STONE,GameBoard.BLACK_TERRITORY);
int whiteScore = gameBoard.countSymbols(GameBoard.WHITE_STONE,GameBoard.WHITE_TERRITORY);
return new GameResult( blackScore,
whiteScore,
markedBoard.getSize(),
gameConfig.getKomi(),
isFinished);
}
public List<String> getEmptyCoords() {
List<String> emptyCoords = new ArrayList<String>();
@@ -100,7 +144,12 @@ public class GameState {
if (action.isPass()) {
playerToMove = GoGame.getNextPlayer(player);
//TODO will need to record player as well
moveHistory.add(action);
return true;
}
if (action.isResign()) {
playerToMove = Player.NONE;
moveHistory.add(action);
return true;
}
@@ -219,25 +268,6 @@ public class GameState {
}
}
/*
@Deprecated
private void assertCorrectHash() {
long hashFromHistory = gameBoard.getZobristHash();
int boardSize = gameBoard.getSize();
ZobristHashGenerator zhg = ZobristHashGenerator.getInstance(boardSize);
long recalculatedHash = zhg.getEmptyBoardHash();
for (int i = 0; i < boardSize * boardSize; i ++) {
recalculatedHash ^= zhg.getHashCode(i, GameBoard.EMPTY_INTERSECTION);
recalculatedHash ^= zhg.getHashCode(i, gameBoard.getSymbolAt(i));
}
if (hashFromHistory != recalculatedHash) {
throw new RuntimeException("Zobrist hash code mismatch");
}
}*/
public boolean isTerminal() {
int nMoves = moveHistory.size();
if (nMoves < 2) {

View File

@@ -137,7 +137,7 @@ public class GoGame implements Runnable {
break;
case final_status_list:
System.out.println("Final game score: ");
System.out.println(new StateEvaluator(gameConfig).scoreGame(gameState));
System.out.println(gameState.getResult());
System.out.println();
localOutput.println("=\n");
break;
@@ -159,7 +159,7 @@ public class GoGame implements Runnable {
player);
gameState.playStone(player, nextMove);
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
LOGGER.info(gameState.getResult());
localOutput.println("="
+ nextMove.toString() + "\n");
@@ -198,7 +198,7 @@ public class GoGame implements Runnable {
LOGGER.info("GameState: " + gameState);
localOutput.println("?" + errMsg + "\n");
}
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
LOGGER.info(gameState.getResult());
break;
case version:
localOutput.println("= 0.1\n");

View File

@@ -1,5 +1,75 @@
package net.woodyfolsom.msproj;
public class Referee {
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.woodyfolsom.msproj.policy.Policy;
public class Referee {
private GameConfig gameConfig = new GameConfig(9);
private GameState gameState = new GameState(gameConfig);
private Policy blackPolicy;
private Policy whitePolicy;
public Policy getPolicy(Player player) {
if (Player.BLACK.equals(player)) {
return blackPolicy;
} else if (Player.WHITE.equals(player)) {
return whitePolicy;
} else {
throw new IllegalArgumentException("Player must be one of {Player.WHITE, Player.BLACK}.");
}
}
public void setPolicy(Player player, Policy policy) {
if (Player.BLACK.equals(player)) {
blackPolicy = policy;
} else if (Player.WHITE.equals(player)) {
whitePolicy = policy;
} else {
throw new IllegalArgumentException("Player must be one of {Player.WHITE, Player.BLACK}.");
}
}
public void play() {
System.out.println("Game started.");
Player currentPlayer = Player.BLACK;
while (!gameState.isTerminal()) {
System.out.println(gameState);
Action action = getPolicy(currentPlayer).getAction(gameConfig, gameState, currentPlayer);
gameState.playStone(currentPlayer, action);
currentPlayer = GoGame.getNextPlayer(currentPlayer);
}
System.out.println("Game over. Result: " + gameState.getResult());
DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssZ");
try {
File sgfFile = new File("gogame-" + dateFormat.format(new Date())+ ".sgf");
FileOutputStream fos = new FileOutputStream(sgfFile);
try {
SGFWriter.writeSGF(gameState, fos);
System.out.println("Game saved as " + sgfFile.getAbsolutePath());
} finally {
try {
fos.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
} catch(IOException ioe) {
System.out.println("Unable to save game file due to IOException: " + ioe.getMessage());
}
System.out.println("Game finished.");
}
}

View File

@@ -0,0 +1,22 @@
package net.woodyfolsom.msproj;
import java.io.IOException;
import java.io.OutputStream;
import net.woodyfolsom.msproj.sgf.SGFIdentifier;
public class SGFWriter {
public static void writeSGF(GameState gameState, OutputStream os)
throws IOException {
writeNode(os, SGFIdentifier.FILE_FORMAT, Integer.valueOf(4));
}
private static void writeNode(OutputStream os, SGFIdentifier sgfIdent,
Object nodeValue) throws IOException {
os.write(sgfIdent.toString().getBytes());
os.write("[".getBytes());
os.write(nodeValue.toString().getBytes());
os.write("];".getBytes());
}
}

View File

@@ -0,0 +1,24 @@
package net.woodyfolsom.msproj;
import net.woodyfolsom.msproj.policy.HumanKeyboardInput;
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
import net.woodyfolsom.msproj.policy.Policy;
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
public class StandAloneGame {
/**
* @param args
*/
public static void main(String[] args) {
Policy player1 = new HumanKeyboardInput();
Policy player2 = new MonteCarloUCT(new RandomMovePolicy(), 5000L);
Referee referee = new Referee();
referee.setPolicy(Player.BLACK, player1);
referee.setPolicy(Player.WHITE, player2);
referee.play();
}
}

View File

@@ -1,27 +0,0 @@
package net.woodyfolsom.msproj;
public class StateEvaluator {
private final GameConfig gameConfig;
public StateEvaluator(GameConfig gameConfig) {
this.gameConfig = gameConfig;
}
public GameConfig getGameConfig() {
return gameConfig;
}
public GameScore scoreGame(GameState gameState) {
GameBoard gameBoard;
if (gameState.getGameBoard().isTerritoryMarked()) {
gameBoard = gameState.getGameBoard();
} else {
gameBoard = new GameBoard(gameState.getGameBoard());
TerritoryMarker.markTerritory(gameBoard);
}
//TODO include komi from gameConfig
return new GameScore(gameBoard.countSymbols(GameBoard.BLACK_STONE,GameBoard.BLACK_TERRITORY),
gameBoard.countSymbols(GameBoard.WHITE_STONE,GameBoard.WHITE_TERRITORY),gameState.getGameBoard().getSize(),gameConfig.getKomi());
}
}

View File

@@ -8,7 +8,6 @@ import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.GoGame;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator;
import net.woodyfolsom.msproj.tree.AlphaBetaProperties;
import net.woodyfolsom.msproj.tree.GameTreeNode;
@@ -34,15 +33,13 @@ public class AlphaBeta implements Policy {
numStateEvaluations = 0;
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
GameTreeNode<AlphaBetaProperties> rootNode = new GameTreeNode<AlphaBetaProperties>(
gameState, new AlphaBetaProperties());
if (player == Player.BLACK) {
return getMax(lookAhead * 2, stateEvaluator, rootNode, player);
return getMax(lookAhead * 2, rootNode, player);
} else {
return getMin(lookAhead * 2, stateEvaluator, rootNode, player);
return getMin(lookAhead * 2, rootNode, player);
}
}
@@ -50,13 +47,13 @@ public class AlphaBeta implements Policy {
return recursionLevels == 0 || nValidMoves == 0;
}
private Action getMax(int recursionLevels, StateEvaluator stateEvaluator,
private Action getMax(int recursionLevels,
GameTreeNode<AlphaBetaProperties> node, Player player) {
GameState gameState = new GameState(node.getGameState());
List<Action> validMoves = validMoveGenerator.getActions(
stateEvaluator.getGameConfig(), node.getGameState(), player,
node.getGameState().getGameConfig(), node.getGameState(), player,
ActionGenerator.ALL_ACTIONS);
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
@@ -66,7 +63,7 @@ public class AlphaBeta implements Policy {
if (terminal) {
node.getProperties().setReward(
stateEvaluator.scoreGame(gameState).getAggregateScore());
gameState.getResult().getNormalizedScore());
numStateEvaluations++;
@@ -85,7 +82,7 @@ public class AlphaBeta implements Policy {
node.getProperties().getBeta());
node.addChild(nextMove, childNode);
getMin(recursionLevels - 1, stateEvaluator, childNode,
getMin(recursionLevels - 1, childNode,
GoGame.getNextPlayer(player));
double gameScore = childNode.getProperties().getReward();
@@ -109,13 +106,13 @@ public class AlphaBeta implements Policy {
}
}
private Action getMin(int recursionLevels, StateEvaluator stateEvaluator,
private Action getMin(int recursionLevels,
GameTreeNode<AlphaBetaProperties> node, Player player) {
GameState gameState = new GameState(node.getGameState());
List<Action> validMoves = validMoveGenerator.getActions(
stateEvaluator.getGameConfig(), node.getGameState(), player,
node.getGameState().getGameConfig(), node.getGameState(), player,
ActionGenerator.ALL_ACTIONS);
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
@@ -125,7 +122,7 @@ public class AlphaBeta implements Policy {
if (terminal) {
node.getProperties().setReward(
stateEvaluator.scoreGame(gameState).getAggregateScore());
gameState.getResult().getNormalizedScore());
numStateEvaluations++;
@@ -144,7 +141,7 @@ public class AlphaBeta implements Policy {
node.getProperties().getBeta());
node.addChild(nextMove, childNode);
getMax(recursionLevels - 1, stateEvaluator, childNode,
getMax(recursionLevels - 1, childNode,
GoGame.getNextPlayer(player));
double gameScore = childNode.getProperties().getReward();

View File

@@ -0,0 +1,67 @@
package net.woodyfolsom.msproj.policy;
import java.io.IOException;
import java.util.Collection;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
public class HumanKeyboardInput implements Policy {
@Override
public Action getAction(GameConfig gameConfig, GameState gameState,
Player player) {
byte[] inputBytes = new byte[4];
Action action = null;
String input = "";
do {
try {
System.out.println(player + " to move: (enter coord or PASS/RESIGN)");
System.in.read(inputBytes);
input = new String(inputBytes).trim().toUpperCase();
action = Action.getInstance(input);
if (action.isNone()) {
System.out.println("'" + input +"' is not a valid move. Please enter a coordinate (A1-T19) or 'PASS'.");
System.out.println(gameState);
continue;
}
} catch (IOException ioe) {
System.out.println("IOException: " + ioe.getMessage());
} catch (IllegalArgumentException iae) {
System.out.println("IllegalArgumentException - '"+input+"' is not a valid action or coordinate.");
}
} while (action == null);
return action;
}
@Override
public Action getAction(GameConfig gameConfig, GameState gameState,
Collection<Action> prohibitedActions, Player player) {
Action action = getAction(gameConfig,gameState,player);
if (!prohibitedActions.contains(action)) {
return action;
}
do {
System.out.println("Sorry, that action is prohibited. Please make another move.");
action = getAction(gameConfig,gameState,player);
} while (prohibitedActions.contains(action));
return action;
}
@Override
public int getNumStateEvaluations() {
// TODO Auto-generated method stub
return 1;
}
}

View File

@@ -8,7 +8,6 @@ import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.GoGame;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator;
import net.woodyfolsom.msproj.tree.GameTreeNode;
import net.woodyfolsom.msproj.tree.MinimaxProperties;
@@ -33,15 +32,13 @@ public class Minimax implements Policy {
Player player) {
numStateEvaluations = 0;
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
GameTreeNode<MinimaxProperties> rootNode = new GameTreeNode<MinimaxProperties>(
gameState, new MinimaxProperties());
if (player == Player.BLACK) {
return getMax(lookAhead * 2, stateEvaluator, rootNode, player);
return getMax(lookAhead * 2, rootNode, player);
} else {
return getMin(lookAhead * 2, stateEvaluator, rootNode, player);
return getMin(lookAhead * 2, rootNode, player);
}
}
@@ -49,13 +46,13 @@ public class Minimax implements Policy {
return recursionLevels == 0 || nValidMoves == 0;
}
private Action getMax(int recursionLevels, StateEvaluator stateEvaluator,
private Action getMax(int recursionLevels,
GameTreeNode<MinimaxProperties> node, Player player) {
GameState gameState = new GameState(node.getGameState());
List<Action> validMoves = validMoveGenerator.getActions(
stateEvaluator.getGameConfig(), node.getGameState(), player,
node.getGameState().getGameConfig(), node.getGameState(), player,
ActionGenerator.ALL_ACTIONS);
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
@@ -65,7 +62,7 @@ public class Minimax implements Policy {
if (terminal) {
node.getProperties().setReward(
stateEvaluator.scoreGame(gameState).getAggregateScore());
gameState.getResult().getNormalizedScore());
numStateEvaluations++;
@@ -79,7 +76,7 @@ public class Minimax implements Policy {
nextState, new MinimaxProperties());
node.addChild(nextMove, childNode);
getMin(recursionLevels - 1, stateEvaluator, childNode,
getMin(recursionLevels - 1, childNode,
GoGame.getNextPlayer(player));
double gameScore = childNode.getProperties().getReward();
@@ -95,13 +92,13 @@ public class Minimax implements Policy {
}
}
private Action getMin(int recursionLevels, StateEvaluator stateEvaluator,
private Action getMin(int recursionLevels,
GameTreeNode<MinimaxProperties> node, Player player) {
GameState gameState = new GameState(node.getGameState());
List<Action> validMoves = validMoveGenerator.getActions(
stateEvaluator.getGameConfig(), node.getGameState(), player,
node.getGameState().getGameConfig(), node.getGameState(), player,
ActionGenerator.ALL_ACTIONS);
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
@@ -111,7 +108,7 @@ public class Minimax implements Policy {
if (terminal) {
node.getProperties().setReward(
stateEvaluator.scoreGame(gameState).getAggregateScore());
gameState.getResult().getNormalizedScore());
numStateEvaluations++;
@@ -125,7 +122,7 @@ public class Minimax implements Policy {
nextState, new MinimaxProperties());
node.addChild(nextMove, childNode);
getMax(recursionLevels - 1, stateEvaluator, childNode,
getMax(recursionLevels - 1, childNode,
GoGame.getNextPlayer(player));
double gameScore = childNode.getProperties().getReward();

View File

@@ -7,7 +7,6 @@ import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator;
import net.woodyfolsom.msproj.tree.GameTreeNode;
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
@@ -46,7 +45,7 @@ public abstract class MonteCarlo implements Policy {
//result in a win.
GameTreeNode<MonteCarloProperties> rootNode = new GameTreeNode<MonteCarloProperties>(gameState, new MonteCarloProperties());
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
do {
//TODO these return types may need to be lists for some MC methods
@@ -62,7 +61,7 @@ public abstract class MonteCarlo implements Policy {
}
for (GameTreeNode<MonteCarloProperties> newLeaf : newLeaves) {
int reward = rollout(gameConfig, stateEvaluator, newLeaf, player);
int reward = rollout(gameConfig, newLeaf, player);
update(newLeaf, reward);
}
@@ -82,7 +81,7 @@ public abstract class MonteCarlo implements Policy {
public abstract List<GameTreeNode<MonteCarloProperties>> grow(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
public abstract int rollout(GameConfig gameConfig, StateEvaluator stateEvaluator, GameTreeNode<MonteCarloProperties> node, Player player);
public abstract int rollout(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
public abstract void update(GameTreeNode<MonteCarloProperties> node, int reward);

View File

@@ -7,11 +7,10 @@ import java.util.Set;
import net.woodyfolsom.msproj.Action;
import net.woodyfolsom.msproj.GameConfig;
import net.woodyfolsom.msproj.GameScore;
import net.woodyfolsom.msproj.GameResult;
import net.woodyfolsom.msproj.GameState;
import net.woodyfolsom.msproj.GoGame;
import net.woodyfolsom.msproj.Player;
import net.woodyfolsom.msproj.StateEvaluator;
import net.woodyfolsom.msproj.tree.GameTreeNode;
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
@@ -119,7 +118,7 @@ public class MonteCarloUCT extends MonteCarlo {
* Rollout currently depends on the hardcoded ROLLOUT_DEPTH_LIMIT superclass parameter,
* Even with super-ko detection, a rollout might take an unrealistically long time due to unlikely playouts.
*/
public int rollout(GameConfig gameConfig, StateEvaluator stateEvaluator, GameTreeNode<MonteCarloProperties> node, Player player) {
public int rollout(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player) {
Policy randomMovePolicy = new RandomMovePolicy();
Action action;
@@ -139,18 +138,11 @@ public class MonteCarloUCT extends MonteCarlo {
numStateEvaluations++;
GameScore gameScore = stateEvaluator.scoreGame(rolloutGameState);
GameResult gameScore = rolloutGameState.getResult();
if (gameScore.isWinner(player)) {
//System.out.println();
//System.out.println("Win for " + player + ":\n" + rolloutGameState);
//System.out.println(gameScore.getScoreReport());
//System.out.println();
return 1;
} else {
//System.out.println();
//System.out.println("Loss for " + player + ":\n" + rolloutGameState);
//System.out.println(gameScore.getScoreReport());
//System.out.println();
return 0;
}
}

View File

@@ -1,5 +0,0 @@
package net.woodyfolsom.msproj.sgf;
public class SGFReader {
}

View File

@@ -54,8 +54,7 @@ public class CaptureTest {
System.out.println(gameState);
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState);
System.out.println(gameScore.getScoreReport());
System.out.println(gameState.getResult());
}
@Test

View File

@@ -2,7 +2,7 @@ package net.woodyfolsom.msproj;
import static org.junit.Assert.assertEquals;
import net.woodyfolsom.msproj.GameScore;
import net.woodyfolsom.msproj.GameResult;
import org.junit.Test;
@@ -10,19 +10,19 @@ public class GameScoreTest {
@Test
public void testGetAggregateScoreZero() {
GameScore gameScore = new GameScore(0,0,19,0);
assertEquals(gameScore.getNormalizedZeroScore(), gameScore.getAggregateScore());
GameResult gameScore = new GameResult(0,0,19,0, true);
assertEquals(gameScore.getNormalizedZeroScore(), gameScore.getNormalizedScore());
}
@Test
public void testGetAggregateScoreBlackWinsNoKomi() {
GameScore gameScore = new GameScore(25,2,19,0);
assertEquals(425, gameScore.getAggregateScore());
GameResult gameScore = new GameResult(25,2,19,0, true);
assertEquals(407, gameScore.getNormalizedScore());
}
@Test
public void testGetAggregateScoreWhiteWinsWithKomi() {
GameScore gameScore = new GameScore(10,12,19,6.5);
assertEquals(362, gameScore.getAggregateScore());
GameResult gameScore = new GameResult(10,12,19,6.5, true);
assertEquals(357, gameScore.getNormalizedScore());
}
}

View File

@@ -1,102 +0,0 @@
package net.woodyfolsom.msproj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class StateEvaluatorTest {
@Test
public void testScoreEmptyBoard() {
GameConfig gameConfig = new GameConfig(5);
GameState gameState = new GameState(gameConfig);
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() {
GameConfig gameConfig = new GameConfig(5);
GameState gameState = new GameState(gameConfig);
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);
}
@Test
public void testScoreTiedAtMove2() {
GameConfig gameConfig = new GameConfig(5);
GameState gameState = new GameState(gameConfig);
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);
}
@Test
public void testScoreTerritory() {
GameConfig gameConfig = new GameConfig(5);
GameState gameState = new GameState(gameConfig);
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);
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.
}
@Test
public void testCaptureAggScore() {
GameConfig gameConfig = new GameConfig(9);
GameState gameState = new GameState(gameConfig);
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(Player.WHITE, Action.getInstance("A1"));
capAtB3.playStone(Player.WHITE, Action.getInstance("B3"));
System.out.println(moveToA1);
System.out.println(capAtB3);
StateEvaluator eval = new StateEvaluator(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
assertTrue(scoreA1 > scoreB3);
}
}