Code cleanup; Initial implementation of root parallelization.
This commit is contained in:
@@ -9,7 +9,6 @@ public class Action {
|
|||||||
private char column = 'x';
|
private char column = 'x';
|
||||||
private int row = 0;
|
private int row = 0;
|
||||||
|
|
||||||
//private Player player;
|
|
||||||
private String move;
|
private String move;
|
||||||
|
|
||||||
public static final Action NONE = new Action("NONE", 'x', 0);
|
public static final Action NONE = new Action("NONE", 'x', 0);
|
||||||
|
|||||||
@@ -1,38 +1,40 @@
|
|||||||
package net.woodyfolsom.msproj;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class Command {
|
public class Command {
|
||||||
public enum TYPE { boardsize, clear_board, final_status_list, genmove, INVALID, komi, list_commands, name, play, quit, version };
|
public enum TYPE {
|
||||||
|
boardsize, clear_board, final_status_list, genmove, INVALID, komi, list_commands, name, play, quit, version
|
||||||
|
};
|
||||||
|
|
||||||
private String[] cmdFields;
|
private String[] cmdFields;
|
||||||
private String text;
|
private String text;
|
||||||
private TYPE type;
|
private TYPE type;
|
||||||
|
|
||||||
public Command(TYPE type, String text, String[] cmdFields) {
|
public Command(TYPE type, String text, String[] cmdFields) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.cmdFields = cmdFields;
|
this.cmdFields = cmdFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDoubleField(int index) {
|
public double getDoubleField(int index) {
|
||||||
return Double.valueOf(cmdFields[index]);
|
return Double.valueOf(cmdFields[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIntField(int index) {
|
public int getIntField(int index) {
|
||||||
return Integer.valueOf(cmdFields[index]);
|
return Integer.valueOf(cmdFields[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStringField(int index) {
|
public String getStringField(int index) {
|
||||||
return cmdFields[index];
|
return cmdFields[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TYPE getType() {
|
public TYPE getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[" + type.toString() + "] " + text;
|
return "[" + type.toString() + "] " + text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ public class CommandParser {
|
|||||||
private static final Logger LOGGER = Logger.getLogger(CommandParser.class
|
private static final Logger LOGGER = Logger.getLogger(CommandParser.class
|
||||||
.getName());
|
.getName());
|
||||||
|
|
||||||
//private static final String INT_PATTERN = "^(\\d+.*|-\\d+.*)";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This very simple parser converts a small subset of GTP command strings
|
* This very simple parser converts a small subset of GTP command strings
|
||||||
* into Commands.
|
* into Commands.
|
||||||
@@ -17,38 +15,43 @@ public class CommandParser {
|
|||||||
*/
|
*/
|
||||||
public static Command parse(String commandString) {
|
public static Command parse(String commandString) {
|
||||||
LOGGER.info("Parsing command: " + commandString);
|
LOGGER.info("Parsing command: " + commandString);
|
||||||
//split on whitespace
|
// split on whitespace
|
||||||
if (commandString == null) {
|
if (commandString == null) {
|
||||||
return new Command(Command.TYPE.INVALID,commandString,new String[0]);
|
return new Command(Command.TYPE.INVALID, commandString,
|
||||||
|
new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
String cmdFields[] = commandString.split("\\s+");
|
String cmdFields[] = commandString.split("\\s+");
|
||||||
if (cmdFields.length == 0) {
|
if (cmdFields.length == 0) {
|
||||||
return new Command(Command.TYPE.INVALID,commandString,new String[0]);
|
return new Command(Command.TYPE.INVALID, commandString,
|
||||||
|
new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("boardsize".equals(cmdFields[0])) {
|
if ("boardsize".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.boardsize,commandString,cmdFields);
|
return new Command(Command.TYPE.boardsize, commandString, cmdFields);
|
||||||
} else if ("clear_board".equals(cmdFields[0])) {
|
} else if ("clear_board".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.clear_board,commandString,cmdFields);
|
return new Command(Command.TYPE.clear_board, commandString,
|
||||||
|
cmdFields);
|
||||||
} else if ("final_status_list".equals(cmdFields[0])) {
|
} else if ("final_status_list".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.final_status_list,commandString,cmdFields);
|
return new Command(Command.TYPE.final_status_list, commandString,
|
||||||
|
cmdFields);
|
||||||
} else if ("genmove".equals(cmdFields[0])) {
|
} else if ("genmove".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.genmove,commandString,cmdFields);
|
return new Command(Command.TYPE.genmove, commandString, cmdFields);
|
||||||
} else if ("komi".equals(cmdFields[0])) {
|
} else if ("komi".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.komi,commandString,cmdFields);
|
return new Command(Command.TYPE.komi, commandString, cmdFields);
|
||||||
} else if ("list_commands".equals(cmdFields[0])) {
|
} else if ("list_commands".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.list_commands,commandString,cmdFields);
|
return new Command(Command.TYPE.list_commands, commandString,
|
||||||
|
cmdFields);
|
||||||
} else if ("name".equals(cmdFields[0])) {
|
} else if ("name".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.name,commandString,cmdFields);
|
return new Command(Command.TYPE.name, commandString, cmdFields);
|
||||||
} else if ("play".equals(cmdFields[0])) {
|
} else if ("play".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.play,commandString,cmdFields);
|
return new Command(Command.TYPE.play, commandString, cmdFields);
|
||||||
} else if ("quit".equals(cmdFields[0])) {
|
} else if ("quit".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.quit,commandString,cmdFields);
|
return new Command(Command.TYPE.quit, commandString, cmdFields);
|
||||||
} else if ("version".equals(cmdFields[0])) {
|
} else if ("version".equals(cmdFields[0])) {
|
||||||
return new Command(Command.TYPE.version,commandString,cmdFields);
|
return new Command(Command.TYPE.version, commandString, cmdFields);
|
||||||
} else {
|
} else {
|
||||||
return new Command(Command.TYPE.INVALID,commandString,cmdFields);
|
return new Command(Command.TYPE.INVALID, commandString, cmdFields);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class GameBoard {
|
|||||||
private char[] board;
|
private char[] board;
|
||||||
private List<Integer> captureList;
|
private List<Integer> captureList;
|
||||||
private List<Long> boardHashHistory = new ArrayList<Long>();
|
private List<Long> boardHashHistory = new ArrayList<Long>();
|
||||||
|
|
||||||
private ZobristHashGenerator zobristHashGenerator;
|
private ZobristHashGenerator zobristHashGenerator;
|
||||||
|
|
||||||
public GameBoard(int size) {
|
public GameBoard(int size) {
|
||||||
@@ -143,11 +143,11 @@ public class GameBoard {
|
|||||||
public char getSymbolAt(char colLabel, int rowNumber) {
|
public char getSymbolAt(char colLabel, int rowNumber) {
|
||||||
return getSymbolAt(getColumnIndex(colLabel), rowNumber - 1);
|
return getSymbolAt(getColumnIndex(colLabel), rowNumber - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getSymbolAt(int index) {
|
public char getSymbolAt(int index) {
|
||||||
return board[index];
|
return board[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 0-based.
|
* 0-based.
|
||||||
*
|
*
|
||||||
@@ -222,7 +222,7 @@ public class GameBoard {
|
|||||||
for (int i = 0; i < board.length; i++) {
|
for (int i = 0; i < board.length; i++) {
|
||||||
if (board[i] == MARKED_GROUP) {
|
if (board[i] == MARKED_GROUP) {
|
||||||
board[i] = opponentSymbol;
|
board[i] = opponentSymbol;
|
||||||
setSymbolAt(i,EMPTY_INTERSECTION);
|
setSymbolAt(i, EMPTY_INTERSECTION);
|
||||||
captureList.add(i);
|
captureList.add(i);
|
||||||
numReplaced++;
|
numReplaced++;
|
||||||
}
|
}
|
||||||
@@ -250,12 +250,13 @@ public class GameBoard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO change boardHashHistory to stack
|
// TODO change boardHashHistory to stack
|
||||||
public void popHashHistory() {
|
public void popHashHistory() {
|
||||||
boardHashHistory.remove(boardHashHistory.get(boardHashHistory.size() - 1));
|
boardHashHistory
|
||||||
|
.remove(boardHashHistory.get(boardHashHistory.size() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushHashHistory() {
|
public void pushHashHistory() {
|
||||||
boardHashHistory.add(boardHashHistory.get(boardHashHistory.size() - 1));
|
boardHashHistory.add(boardHashHistory.get(boardHashHistory.size() - 1));
|
||||||
}
|
}
|
||||||
@@ -266,12 +267,14 @@ public class GameBoard {
|
|||||||
|
|
||||||
public void setSymbolAt(int index, char newSymbol) {
|
public void setSymbolAt(int index, char newSymbol) {
|
||||||
char oldSymbol = board[index];
|
char oldSymbol = board[index];
|
||||||
|
|
||||||
//TODO marked intersections should really be stored in
|
// TODO marked intersections should really be stored in
|
||||||
//a separate array to ensure that the hash code is always in sync with
|
// a separate array to ensure that the hash code is always in sync with
|
||||||
//an actual or transitional board position
|
// an actual or transitional board position
|
||||||
if (oldSymbol == MARKED_GROUP || newSymbol == MARKED_GROUP || oldSymbol == MARKED_TERRITORY || newSymbol == MARKED_TERRITORY) {
|
if (oldSymbol == MARKED_GROUP || newSymbol == MARKED_GROUP
|
||||||
board[index] = newSymbol;
|
|| oldSymbol == MARKED_TERRITORY
|
||||||
|
|| newSymbol == MARKED_TERRITORY) {
|
||||||
|
board[index] = newSymbol;
|
||||||
} else {
|
} else {
|
||||||
int hashIndex = boardHashHistory.size() - 1;
|
int hashIndex = boardHashHistory.size() - 1;
|
||||||
long currentHashCode = boardHashHistory.get(hashIndex);
|
long currentHashCode = boardHashHistory.get(hashIndex);
|
||||||
|
|||||||
@@ -4,49 +4,49 @@ public class GameConfig {
|
|||||||
private double komi;
|
private double komi;
|
||||||
private int size;
|
private int size;
|
||||||
private int timeLimit;
|
private int timeLimit;
|
||||||
|
|
||||||
public GameConfig(int size, double komi) {
|
public GameConfig(int size, double komi) {
|
||||||
this(size);
|
this(size);
|
||||||
this.komi = komi;
|
this.komi = komi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameConfig(int size) {
|
public GameConfig(int size) {
|
||||||
timeLimit = 0;
|
timeLimit = 0;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
switch(size) {
|
switch (size) {
|
||||||
case 9 :
|
case 9:
|
||||||
komi = 6.5;
|
komi = 6.5;
|
||||||
default :
|
default:
|
||||||
komi = 5.5;
|
komi = 5.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameConfig(GameConfig that) {
|
public GameConfig(GameConfig that) {
|
||||||
this.komi = that.komi;
|
this.komi = that.komi;
|
||||||
this.size = that.size;
|
this.size = that.size;
|
||||||
this.timeLimit = that.timeLimit;
|
this.timeLimit = that.timeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getKomi() {
|
public double getKomi() {
|
||||||
return komi;
|
return komi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTimeLimit(){
|
public int getTimeLimit() {
|
||||||
return timeLimit;
|
return timeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKomi(double komi) {
|
public void setKomi(double komi) {
|
||||||
this.komi = komi;
|
this.komi = komi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSize(int size) {
|
public void setSize(int size) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeLimit(int timeLimit) {
|
public void setTimeLimit(int timeLimit) {
|
||||||
this.timeLimit = timeLimit;
|
this.timeLimit = timeLimit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package net.woodyfolsom.msproj;
|
|
||||||
|
|
||||||
public class GameController {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,29 @@
|
|||||||
package net.woodyfolsom.msproj;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
|
||||||
public class GameResult {
|
public class GameResult {
|
||||||
public static final GameResult BLACK_BY_RESIGNATION = new GameResult(RESULT_TYPE.BLACK_BY_RESIGNATION);
|
public static final GameResult BLACK_BY_RESIGNATION = new GameResult(
|
||||||
|
RESULT_TYPE.BLACK_BY_RESIGNATION);
|
||||||
public static final GameResult VOID = new GameResult(RESULT_TYPE.VOID);
|
public static final GameResult VOID = new GameResult(RESULT_TYPE.VOID);
|
||||||
public static final GameResult WHITE_BY_RESIGNATION = new GameResult(RESULT_TYPE.WHITE_BY_RESIGNATION);
|
public static final GameResult WHITE_BY_RESIGNATION = new GameResult(
|
||||||
|
RESULT_TYPE.WHITE_BY_RESIGNATION);
|
||||||
|
|
||||||
private double komi;
|
private double komi;
|
||||||
private int blackScore;
|
private int blackScore;
|
||||||
private int whiteScore;
|
private int whiteScore;
|
||||||
private int normalizedZeroScore;
|
private int normalizedZeroScore;
|
||||||
|
|
||||||
public enum RESULT_TYPE { BLACK_BY_RESIGNATION, WHITE_BY_RESIGNATION, IN_PROGRESS, SCORED, VOID}
|
public enum RESULT_TYPE {
|
||||||
|
BLACK_BY_RESIGNATION, WHITE_BY_RESIGNATION, IN_PROGRESS, SCORED, VOID
|
||||||
|
}
|
||||||
|
|
||||||
private RESULT_TYPE resultType;
|
private RESULT_TYPE resultType;
|
||||||
|
|
||||||
public GameResult(int blackScore, int whiteScore, int size, double komi, boolean finished) {
|
public GameResult(int blackScore, int whiteScore, int size, double komi,
|
||||||
|
boolean finished) {
|
||||||
this.blackScore = blackScore;
|
this.blackScore = blackScore;
|
||||||
this.komi = komi;
|
this.komi = komi;
|
||||||
this.whiteScore = whiteScore;
|
this.whiteScore = whiteScore;
|
||||||
this.normalizedZeroScore = size * size + ((int)(komi * 2));
|
this.normalizedZeroScore = size * size + ((int) (komi * 2));
|
||||||
if (finished) {
|
if (finished) {
|
||||||
resultType = RESULT_TYPE.SCORED;
|
resultType = RESULT_TYPE.SCORED;
|
||||||
} else {
|
} else {
|
||||||
@@ -32,10 +36,10 @@ public class GameResult {
|
|||||||
blackScore = 0;
|
blackScore = 0;
|
||||||
whiteScore = 0;
|
whiteScore = 0;
|
||||||
normalizedZeroScore = 0;
|
normalizedZeroScore = 0;
|
||||||
|
|
||||||
this.resultType = resultType;
|
this.resultType = resultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getBlackScore() {
|
public double getBlackScore() {
|
||||||
return blackScore;
|
return blackScore;
|
||||||
}
|
}
|
||||||
@@ -43,17 +47,20 @@ public class GameResult {
|
|||||||
public int getNormalizedZeroScore() {
|
public int getNormalizedZeroScore() {
|
||||||
return normalizedZeroScore;
|
return normalizedZeroScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a representation for the game score as an integer. Lower numbers are better for white.
|
* Gets a representation for the game score as an integer. Lower numbers are
|
||||||
* The minimum value is 0 (Chinese scoring - white owns every intersection on 19x19 and has 9 stone komi).
|
* better for white. The minimum value is 0 (Chinese scoring - white owns
|
||||||
* Likewise, the maximum value if 379x2 (black owns every intersection with zero komi).
|
* every intersection on 19x19 and has 9 stone komi). Likewise, the maximum
|
||||||
|
* value if 379x2 (black owns every intersection with zero komi).
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int getNormalizedScore() {
|
public int getNormalizedScore() {
|
||||||
return normalizedZeroScore + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
|
return normalizedZeroScore + 2 * blackScore
|
||||||
|
- ((int) (2 * (whiteScore + komi)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getScore(Player color) {
|
public double getScore(Player color) {
|
||||||
if (color == Player.BLACK) {
|
if (color == Player.BLACK) {
|
||||||
return getBlackScore();
|
return getBlackScore();
|
||||||
@@ -63,9 +70,9 @@ public class GameResult {
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getWhiteScore() {
|
public double getWhiteScore() {
|
||||||
return (double)whiteScore + komi;
|
return (double) whiteScore + komi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWinner(Player player) {
|
public boolean isWinner(Player player) {
|
||||||
@@ -75,12 +82,12 @@ public class GameResult {
|
|||||||
return blackScore > whiteScore + komi;
|
return blackScore > whiteScore + komi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
switch (resultType) {
|
switch (resultType) {
|
||||||
case BLACK_BY_RESIGNATION :
|
case BLACK_BY_RESIGNATION:
|
||||||
return "B+R";
|
return "B+R";
|
||||||
case SCORED :
|
case SCORED:
|
||||||
double blackScore = getBlackScore();
|
double blackScore = getBlackScore();
|
||||||
double whiteScore = getWhiteScore();
|
double whiteScore = getWhiteScore();
|
||||||
if (blackScore > whiteScore) {
|
if (blackScore > whiteScore) {
|
||||||
@@ -90,12 +97,12 @@ public class GameResult {
|
|||||||
} else {
|
} else {
|
||||||
return "DRAW";
|
return "DRAW";
|
||||||
}
|
}
|
||||||
case WHITE_BY_RESIGNATION :
|
case WHITE_BY_RESIGNATION:
|
||||||
return "W+R";
|
return "W+R";
|
||||||
case IN_PROGRESS :
|
case IN_PROGRESS:
|
||||||
case VOID : // intentional fall-through
|
case VOID: // intentional fall-through
|
||||||
return "Void";
|
return "Void";
|
||||||
default :
|
default:
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class GameState {
|
|||||||
private GameConfig gameConfig;
|
private GameConfig gameConfig;
|
||||||
private Player playerToMove;
|
private Player playerToMove;
|
||||||
private List<Action> moveHistory = new ArrayList<Action>();
|
private List<Action> moveHistory = new ArrayList<Action>();
|
||||||
|
|
||||||
public GameState(GameConfig gameConfig) {
|
public GameState(GameConfig gameConfig) {
|
||||||
this.gameConfig = gameConfig;
|
this.gameConfig = gameConfig;
|
||||||
int size = gameConfig.getSize();
|
int size = gameConfig.getSize();
|
||||||
@@ -40,27 +40,28 @@ public class GameState {
|
|||||||
public int getBlackPrisoners() {
|
public int getBlackPrisoners() {
|
||||||
return blackPrisoners;
|
return blackPrisoners;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameResult getResult() {
|
public GameResult getResult() {
|
||||||
GameBoard markedBoard;
|
GameBoard markedBoard;
|
||||||
|
|
||||||
if (gameBoard.isTerritoryMarked()) {
|
if (gameBoard.isTerritoryMarked()) {
|
||||||
markedBoard = gameBoard;
|
markedBoard = gameBoard;
|
||||||
} else {
|
} else {
|
||||||
markedBoard = new GameBoard(gameBoard);
|
markedBoard = new GameBoard(gameBoard);
|
||||||
TerritoryMarker.markTerritory(markedBoard);
|
TerritoryMarker.markTerritory(markedBoard);
|
||||||
}
|
}
|
||||||
|
|
||||||
int gameLength = moveHistory.size();
|
int gameLength = moveHistory.size();
|
||||||
|
|
||||||
//This functionality duplicates isTerminal(),
|
// This functionality duplicates isTerminal(),
|
||||||
//however for the result to be reported, we need to know
|
// however for the result to be reported, we need to know
|
||||||
//how the game was terminated to differentiate between resignation, scored complete
|
// how the game was terminated to differentiate between resignation,
|
||||||
//and (eventually) time out/forfeit.
|
// scored complete
|
||||||
|
// and (eventually) time out/forfeit.
|
||||||
boolean isFinished = false;
|
boolean isFinished = false;
|
||||||
|
|
||||||
if (gameLength >= 1) {
|
if (gameLength >= 1) {
|
||||||
if (moveHistory.get(gameLength-1).isResign()) {
|
if (moveHistory.get(gameLength - 1).isResign()) {
|
||||||
if (gameLength % 2 == 1) {
|
if (gameLength % 2 == 1) {
|
||||||
return GameResult.WHITE_BY_RESIGNATION;
|
return GameResult.WHITE_BY_RESIGNATION;
|
||||||
} else {
|
} else {
|
||||||
@@ -68,21 +69,20 @@ public class GameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gameLength >= 2) {
|
if (gameLength >= 2) {
|
||||||
if (moveHistory.get(gameLength-1).isPass()
|
if (moveHistory.get(gameLength - 1).isPass()
|
||||||
&& moveHistory.get(gameLength-2).isPass()) {
|
&& moveHistory.get(gameLength - 2).isPass()) {
|
||||||
isFinished = true;
|
isFinished = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int blackScore = gameBoard.countSymbols(GameBoard.BLACK_STONE,GameBoard.BLACK_TERRITORY);
|
int blackScore = gameBoard.countSymbols(GameBoard.BLACK_STONE,
|
||||||
int whiteScore = gameBoard.countSymbols(GameBoard.WHITE_STONE,GameBoard.WHITE_TERRITORY);
|
GameBoard.BLACK_TERRITORY);
|
||||||
|
int whiteScore = gameBoard.countSymbols(GameBoard.WHITE_STONE,
|
||||||
return new GameResult( blackScore,
|
GameBoard.WHITE_TERRITORY);
|
||||||
whiteScore,
|
|
||||||
markedBoard.getSize(),
|
return new GameResult(blackScore, whiteScore, markedBoard.getSize(),
|
||||||
gameConfig.getKomi(),
|
gameConfig.getKomi(), isFinished);
|
||||||
isFinished);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getEmptyCoords() {
|
public List<String> getEmptyCoords() {
|
||||||
@@ -92,7 +92,8 @@ public class GameState {
|
|||||||
for (int rowIndex = 0; rowIndex < gameBoard.getSize(); rowIndex++) {
|
for (int rowIndex = 0; rowIndex < gameBoard.getSize(); rowIndex++) {
|
||||||
if (GameBoard.EMPTY_INTERSECTION == gameBoard.getSymbolAt(
|
if (GameBoard.EMPTY_INTERSECTION == gameBoard.getSymbolAt(
|
||||||
colIndex, rowIndex))
|
colIndex, rowIndex))
|
||||||
emptyCoords.add(GameBoard.getCoordinate(colIndex, rowIndex));
|
emptyCoords
|
||||||
|
.add(GameBoard.getCoordinate(colIndex, rowIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,11 +107,11 @@ public class GameState {
|
|||||||
public GameConfig getGameConfig() {
|
public GameConfig getGameConfig() {
|
||||||
return gameConfig;
|
return gameConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayerToMove() {
|
public Player getPlayerToMove() {
|
||||||
return playerToMove;
|
return playerToMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getWhitePrisoners() {
|
public int getWhitePrisoners() {
|
||||||
return whitePrisoners;
|
return whitePrisoners;
|
||||||
}
|
}
|
||||||
@@ -135,13 +136,14 @@ public class GameState {
|
|||||||
*/
|
*/
|
||||||
public boolean playStone(Player player, Action action) {
|
public boolean playStone(Player player, Action action) {
|
||||||
if (player != playerToMove) {
|
if (player != playerToMove) {
|
||||||
throw new IllegalArgumentException("Requested " + player + " move, but it is " + playerToMove +"'s turn!");
|
throw new IllegalArgumentException("Requested " + player
|
||||||
|
+ " move, but it is " + playerToMove + "'s turn!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player == Player.NONE) {
|
if (player == Player.NONE) {
|
||||||
throw new IllegalArgumentException("Cannot play as " + player);
|
throw new IllegalArgumentException("Cannot play as " + player);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.isPass()) {
|
if (action.isPass()) {
|
||||||
playerToMove = GoGame.getNextPlayer(player);
|
playerToMove = GoGame.getNextPlayer(player);
|
||||||
moveHistory.add(action);
|
moveHistory.add(action);
|
||||||
@@ -153,11 +155,11 @@ public class GameState {
|
|||||||
moveHistory.add(action);
|
moveHistory.add(action);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.isNone()) {
|
if (action.isNone()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char currentStone = gameBoard.getSymbolAt(action.getColumn(),
|
char currentStone = gameBoard.getSymbolAt(action.getColumn(),
|
||||||
action.getRow());
|
action.getRow());
|
||||||
|
|
||||||
@@ -165,17 +167,17 @@ public class GameState {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//assertCorrectHash();
|
// assertCorrectHash();
|
||||||
|
|
||||||
gameBoard.pushHashHistory();
|
gameBoard.pushHashHistory();
|
||||||
|
|
||||||
gameBoard.clearCaptureList();
|
gameBoard.clearCaptureList();
|
||||||
|
|
||||||
// Place stone as requested, then check for (1) captured neighbors and
|
// Place stone as requested, then check for (1) captured neighbors and
|
||||||
// (2) illegal move due to 0 liberties.
|
// (2) illegal move due to 0 liberties.
|
||||||
char stoneSymbol = player.getStoneSymbol();
|
char stoneSymbol = player.getStoneSymbol();
|
||||||
//Player opponent = GoGame.getNextPlayer(player);
|
// Player opponent = GoGame.getNextPlayer(player);
|
||||||
|
|
||||||
gameBoard.setSymbolAt(action.getColumn(), action.getRow(), stoneSymbol);
|
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
|
||||||
@@ -232,13 +234,14 @@ public class GameState {
|
|||||||
|
|
||||||
// Moved test for 0 liberties until after attempting to capture
|
// Moved test for 0 liberties until after attempting to capture
|
||||||
// neighboring groups.
|
// neighboring groups.
|
||||||
// This will only happen if no neighboring groups were capture, hence there is nothing to undo.
|
// This will only happen if no neighboring groups were capture, hence
|
||||||
|
// there is nothing to undo.
|
||||||
// So return now.
|
// So return now.
|
||||||
if (0 == LibertyCounter.countLiberties(gameBoard, action.getColumn(),
|
if (0 == LibertyCounter.countLiberties(gameBoard, action.getColumn(),
|
||||||
action.getRow(), stoneSymbol)) {
|
action.getRow(), stoneSymbol)) {
|
||||||
gameBoard.removeStone(action.getColumn(), action.getRow());
|
gameBoard.removeStone(action.getColumn(), action.getRow());
|
||||||
gameBoard.popHashHistory();
|
gameBoard.popHashHistory();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,18 +253,19 @@ public class GameState {
|
|||||||
for (int i : captureList) {
|
for (int i : captureList) {
|
||||||
gameBoard.setSymbolAt(i, opponentSymbol);
|
gameBoard.setSymbolAt(i, opponentSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
//And finally, remove the originally played stone, which was never valid due to ko.
|
// And finally, remove the originally played stone, which was never
|
||||||
|
// valid due to ko.
|
||||||
gameBoard.removeStone(action.getColumn(), action.getRow());
|
gameBoard.removeStone(action.getColumn(), action.getRow());
|
||||||
|
|
||||||
gameBoard.clearCaptureList();
|
gameBoard.clearCaptureList();
|
||||||
|
|
||||||
gameBoard.popHashHistory();
|
gameBoard.popHashHistory();
|
||||||
|
|
||||||
//assertCorrectHash();
|
// assertCorrectHash();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
//assertCorrectHash();
|
// assertCorrectHash();
|
||||||
playerToMove = GoGame.getNextPlayer(player);
|
playerToMove = GoGame.getNextPlayer(player);
|
||||||
moveHistory.add(action);
|
moveHistory.add(action);
|
||||||
return true;
|
return true;
|
||||||
@@ -271,12 +275,15 @@ public class GameState {
|
|||||||
public boolean isTerminal() {
|
public boolean isTerminal() {
|
||||||
int nMoves = moveHistory.size();
|
int nMoves = moveHistory.size();
|
||||||
if (nMoves < 2) {
|
if (nMoves < 2) {
|
||||||
return false; //Impossible for a game to be over in 1 move unless the first player resigns
|
return false; // Impossible for a game to be over in 1 move unless
|
||||||
//before the first player has played, the game is considered to be 'not over'
|
// the first player resigns
|
||||||
|
// before the first player has played, the game is considered to be
|
||||||
|
// 'not over'
|
||||||
}
|
}
|
||||||
return moveHistory.get(nMoves-1).isPass() && moveHistory.get(nMoves-2).isPass();
|
return moveHistory.get(nMoves - 1).isPass()
|
||||||
|
&& moveHistory.get(nMoves - 2).isPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
int boardSize = gameBoard.getSize();
|
int boardSize = gameBoard.getSize();
|
||||||
StringBuilder sb = new StringBuilder(" ");
|
StringBuilder sb = new StringBuilder(" ");
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import java.util.Properties;
|
|||||||
import net.woodyfolsom.msproj.Command.TYPE;
|
import net.woodyfolsom.msproj.Command.TYPE;
|
||||||
import net.woodyfolsom.msproj.policy.AlphaBeta;
|
import net.woodyfolsom.msproj.policy.AlphaBeta;
|
||||||
import net.woodyfolsom.msproj.policy.Minimax;
|
import net.woodyfolsom.msproj.policy.Minimax;
|
||||||
import net.woodyfolsom.msproj.policy.Policy;
|
|
||||||
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
|
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
package net.woodyfolsom.msproj;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class LibertyCounter {
|
public class LibertyCounter {
|
||||||
public static int countLiberties(GameBoard gameBoard, char colLabel, int rowNum, char groupColor) {
|
public static int countLiberties(GameBoard gameBoard, char colLabel,
|
||||||
return countLiberties(gameBoard, GameBoard.getColumnIndex(colLabel),rowNum-1,groupColor,false);
|
int rowNum, char groupColor) {
|
||||||
|
return countLiberties(gameBoard, GameBoard.getColumnIndex(colLabel),
|
||||||
|
rowNum - 1, groupColor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int countLiberties(GameBoard gameBoard, int col, int row, char groupColor, boolean markGroup) {
|
public static int countLiberties(GameBoard gameBoard, int col, int row,
|
||||||
|
char groupColor, boolean markGroup) {
|
||||||
int liberties = markGroup(gameBoard, col, row, groupColor);
|
int liberties = markGroup(gameBoard, col, row, groupColor);
|
||||||
|
|
||||||
if (!markGroup) {
|
if (!markGroup) {
|
||||||
gameBoard.unmarkGroup(groupColor);
|
gameBoard.unmarkGroup(groupColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return liberties;
|
return liberties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int markGroup(GameBoard gameBoard, int col, int row, char groupColor) {
|
public static int markGroup(GameBoard gameBoard, int col, int row,
|
||||||
|
char groupColor) {
|
||||||
char stoneSymbol = gameBoard.getSymbolAt(col, row);
|
char stoneSymbol = gameBoard.getSymbolAt(col, row);
|
||||||
if (stoneSymbol == GameBoard.EMPTY_INTERSECTION) {
|
if (stoneSymbol == GameBoard.EMPTY_INTERSECTION) {
|
||||||
return 1; // one liberty
|
return 1; // one liberty
|
||||||
@@ -27,27 +31,30 @@ public class LibertyCounter {
|
|||||||
int liberties = 0;
|
int liberties = 0;
|
||||||
gameBoard.setSymbolAt(col, row, GameBoard.MARKED_GROUP);
|
gameBoard.setSymbolAt(col, row, GameBoard.MARKED_GROUP);
|
||||||
if (col > 0) {
|
if (col > 0) {
|
||||||
liberties += markGroup(gameBoard,col-1,row,groupColor);
|
liberties += markGroup(gameBoard, col - 1, row, groupColor);
|
||||||
}
|
}
|
||||||
if (col < gameBoard.getSize() - 1) {
|
if (col < gameBoard.getSize() - 1) {
|
||||||
liberties += markGroup(gameBoard,col+1,row,groupColor);
|
liberties += markGroup(gameBoard, col + 1, row, groupColor);
|
||||||
}
|
}
|
||||||
if (row > 0) {
|
if (row > 0) {
|
||||||
liberties += markGroup(gameBoard,col,row-1,groupColor);
|
liberties += markGroup(gameBoard, col, row - 1, groupColor);
|
||||||
}
|
}
|
||||||
if (row < gameBoard.getSize() - 1) {
|
if (row < gameBoard.getSize() - 1) {
|
||||||
liberties += markGroup(gameBoard,col,row+1,groupColor);
|
liberties += markGroup(gameBoard, col, row + 1, groupColor);
|
||||||
}
|
}
|
||||||
return liberties;
|
return liberties;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupColor == GameBoard.WHITE_STONE && stoneSymbol == GameBoard.BLACK_STONE) {
|
if (groupColor == GameBoard.WHITE_STONE
|
||||||
return 0; //opposing stone - not a liberty
|
&& stoneSymbol == GameBoard.BLACK_STONE) {
|
||||||
|
return 0; // opposing stone - not a liberty
|
||||||
}
|
}
|
||||||
if (groupColor == GameBoard.BLACK_STONE && stoneSymbol == GameBoard.WHITE_STONE) {
|
if (groupColor == GameBoard.BLACK_STONE
|
||||||
return 0; //opposing stone - not a liberty
|
&& stoneSymbol == GameBoard.WHITE_STONE) {
|
||||||
|
return 0; // opposing stone - not a liberty
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException("Cannot continue marking group - symbol at current location is not empty intersection, white stone, black stone or group marker");
|
throw new IllegalStateException(
|
||||||
|
"Cannot continue marking group - symbol at current location is not empty intersection, white stone, black stone or group marker");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
package net.woodyfolsom.msproj;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class Player {
|
public class Player {
|
||||||
public static final Player BLACK = new Player("BLACK", GameBoard.BLACK_STONE);
|
public static final Player BLACK = new Player("BLACK",
|
||||||
public static final Player NONE = new Player("NONE", GameBoard.EMPTY_INTERSECTION);
|
GameBoard.BLACK_STONE);
|
||||||
public static final Player WHITE = new Player("WHITE", GameBoard.WHITE_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 char stoneSymbol;
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Player(String name, char stoneSymbol) {
|
private Player(String name, char stoneSymbol) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.stoneSymbol = stoneSymbol;
|
this.stoneSymbol = stoneSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Player getInstance(String stoneSymbol) {
|
public static Player getInstance(String stoneSymbol) {
|
||||||
if ("b".equals(stoneSymbol)) {
|
if ("b".equals(stoneSymbol)) {
|
||||||
return Player.BLACK;
|
return Player.BLACK;
|
||||||
@@ -22,15 +25,15 @@ public class Player {
|
|||||||
return Player.NONE;
|
return Player.NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getStoneSymbol() {
|
public char getStoneSymbol() {
|
||||||
return stoneSymbol;
|
return stoneSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNone() {
|
public boolean isNone() {
|
||||||
return "NONE".equals(name);
|
return "NONE".equals(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import net.woodyfolsom.msproj.policy.Policy;
|
|||||||
public class Referee {
|
public class Referee {
|
||||||
private Policy blackPolicy;
|
private Policy blackPolicy;
|
||||||
private Policy whitePolicy;
|
private Policy whitePolicy;
|
||||||
|
|
||||||
public Policy getPolicy(Player player) {
|
public Policy getPolicy(Player player) {
|
||||||
if (Player.BLACK.equals(player)) {
|
if (Player.BLACK.equals(player)) {
|
||||||
return blackPolicy;
|
return blackPolicy;
|
||||||
@@ -37,7 +37,7 @@ public class Referee {
|
|||||||
|
|
||||||
public GameResult play(GameConfig gameConfig) {
|
public GameResult play(GameConfig gameConfig) {
|
||||||
GameState gameState = new GameState(gameConfig);
|
GameState gameState = new GameState(gameConfig);
|
||||||
|
|
||||||
System.out.println("Game started.");
|
System.out.println("Game started.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -55,9 +55,9 @@ public class Referee {
|
|||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return GameResult.VOID;
|
return GameResult.VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameResult result = gameState.getResult();
|
GameResult result = gameState.getResult();
|
||||||
|
|
||||||
System.out.println("Game over. Result: " + result);
|
System.out.println("Game over. Result: " + result);
|
||||||
|
|
||||||
DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssZ");
|
DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssZ");
|
||||||
@@ -84,7 +84,7 @@ public class Referee {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Game finished.");
|
System.out.println("Game finished.");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,22 +7,31 @@ import net.woodyfolsom.msproj.policy.HumanKeyboardInput;
|
|||||||
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
|
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
|
||||||
import net.woodyfolsom.msproj.policy.Policy;
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
||||||
|
import net.woodyfolsom.msproj.policy.RootParallelization;
|
||||||
|
|
||||||
public class StandAloneGame {
|
public class StandAloneGame {
|
||||||
enum PLAYER_TYPE { HUMAN, UCT_FAST, UCT_SLOW };
|
enum PLAYER_TYPE {
|
||||||
|
HUMAN, ROOT_PAR, UCT_FAST, UCT_SLOW
|
||||||
|
};
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
if (args.length != 3) {
|
if (args.length != 3) {
|
||||||
System.out.println("Incorrect # of arguments: use StandAloneGame <Player1Type> <Player2Type> <# rounds>");
|
System.out
|
||||||
System.out.println("For example to play 10 games against MC UCT w/ slow moves: StandAloneGame UCT_SLOW HUMAN 10");
|
.println("Incorrect # of arguments: use StandAloneGame <Player1Type> <Player2Type> <# rounds>");
|
||||||
|
System.out
|
||||||
|
.println("For example to play 10 games against MC UCT w/ slow moves: StandAloneGame UCT_SLOW HUMAN 10");
|
||||||
}
|
}
|
||||||
new StandAloneGame().playGame(parsePlayerType(args[0]), parsePlayerType(args[1]), Integer.valueOf(args[2]));
|
new StandAloneGame().playGame(parsePlayerType(args[0]),
|
||||||
|
parsePlayerType(args[1]), Integer.valueOf(args[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PLAYER_TYPE parsePlayerType(String playerTypeStr) {
|
private static PLAYER_TYPE parsePlayerType(String playerTypeStr) {
|
||||||
if ("UCT_FAST".equalsIgnoreCase(playerTypeStr)) {
|
if ("UCT_FAST".equalsIgnoreCase(playerTypeStr)) {
|
||||||
return PLAYER_TYPE.UCT_FAST;
|
return PLAYER_TYPE.UCT_FAST;
|
||||||
} else if ("UCT_SLOW".equalsIgnoreCase(playerTypeStr) || "UCT".equalsIgnoreCase(playerTypeStr)) {
|
} else if ("ROOT_PAR".equalsIgnoreCase(playerTypeStr)) {
|
||||||
|
return PLAYER_TYPE.ROOT_PAR;
|
||||||
|
} else if ("UCT_SLOW".equalsIgnoreCase(playerTypeStr)
|
||||||
|
|| "UCT".equalsIgnoreCase(playerTypeStr)) {
|
||||||
return PLAYER_TYPE.UCT_SLOW;
|
return PLAYER_TYPE.UCT_SLOW;
|
||||||
} else if ("HUMAN".equalsIgnoreCase(playerTypeStr)) {
|
} else if ("HUMAN".equalsIgnoreCase(playerTypeStr)) {
|
||||||
return PLAYER_TYPE.HUMAN;
|
return PLAYER_TYPE.HUMAN;
|
||||||
@@ -30,38 +39,43 @@ public class StandAloneGame {
|
|||||||
throw new RuntimeException("Unknown player type: " + playerTypeStr);
|
throw new RuntimeException("Unknown player type: " + playerTypeStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playGame(PLAYER_TYPE playerType1, PLAYER_TYPE playerType2, int rounds) {
|
public void playGame(PLAYER_TYPE playerType1, PLAYER_TYPE playerType2,
|
||||||
|
int rounds) {
|
||||||
|
|
||||||
Policy player1 = getPolicy(playerType1);
|
Policy player1 = getPolicy(playerType1);
|
||||||
Policy player2 = getPolicy(playerType2);
|
Policy player2 = getPolicy(playerType2);
|
||||||
|
|
||||||
Referee referee = new Referee();
|
Referee referee = new Referee();
|
||||||
referee.setPolicy(Player.BLACK, player1);
|
referee.setPolicy(Player.BLACK, player1);
|
||||||
referee.setPolicy(Player.WHITE, player2);
|
referee.setPolicy(Player.WHITE, player2);
|
||||||
|
|
||||||
List<GameResult> results = new ArrayList<GameResult>();
|
List<GameResult> results = new ArrayList<GameResult>();
|
||||||
GameConfig gameConfig = new GameConfig(5);
|
GameConfig gameConfig = new GameConfig(5);
|
||||||
for (int round = 0; round < rounds; ) {
|
for (int round = 0; round < rounds; round++) {
|
||||||
results.add(referee.play(gameConfig));
|
results.add(referee.play(gameConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Cumulative results for 10 games (BLACK=" + playerType1 + ", WHITE=" + playerType2 + ")");
|
System.out.println("Cumulative results for " + rounds + " games (BLACK="
|
||||||
for (int i = 0; i < rounds; i ++) {
|
+ playerType1 + ", WHITE=" + playerType2 + ")");
|
||||||
System.out.println(i +". " + results.get(i));
|
for (int i = 0; i < rounds; i++) {
|
||||||
|
System.out.println(i + ". " + results.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Policy getPolicy(PLAYER_TYPE playerType) {
|
private Policy getPolicy(PLAYER_TYPE playerType) {
|
||||||
switch (playerType) {
|
switch (playerType) {
|
||||||
case HUMAN :
|
case HUMAN:
|
||||||
return new HumanKeyboardInput();
|
return new HumanKeyboardInput();
|
||||||
case UCT_SLOW :
|
case ROOT_PAR:
|
||||||
return new MonteCarloUCT(new RandomMovePolicy(), 3000L);
|
return new RootParallelization(3, 4000L);
|
||||||
case UCT_FAST :
|
case UCT_SLOW:
|
||||||
|
return new MonteCarloUCT(new RandomMovePolicy(), 4000L);
|
||||||
|
case UCT_FAST:
|
||||||
return new MonteCarloUCT(new RandomMovePolicy(), 1000L);
|
return new MonteCarloUCT(new RandomMovePolicy(), 1000L);
|
||||||
default :
|
default:
|
||||||
throw new IllegalArgumentException("Invalid PLAYER_TYPE: " + playerType);
|
throw new IllegalArgumentException("Invalid PLAYER_TYPE: "
|
||||||
|
+ playerType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,19 @@ public class TerritoryMarker {
|
|||||||
public static final char WHITE_TERRITORY = 'o';
|
public static final char WHITE_TERRITORY = 'o';
|
||||||
public static final char UNOWNED_TERRITORY = '-';
|
public static final char UNOWNED_TERRITORY = '-';
|
||||||
public static final char TERRITORY_MARKER = '?';
|
public static final char TERRITORY_MARKER = '?';
|
||||||
|
|
||||||
public static final int EMPTY = 0;
|
public static final int EMPTY = 0;
|
||||||
public static final int BLACK = 1;
|
public static final int BLACK = 1;
|
||||||
public static final int WHITE = 2;
|
public static final int WHITE = 2;
|
||||||
|
|
||||||
public static GameBoard markTerritory(GameBoard gameBoard) {
|
public static GameBoard markTerritory(GameBoard gameBoard) {
|
||||||
for (int row = 0; row < gameBoard.getSize(); row++) {
|
for (int row = 0; row < gameBoard.getSize(); row++) {
|
||||||
for (int col = 0; col < gameBoard.getSize(); col++) {
|
for (int col = 0; col < gameBoard.getSize(); col++) {
|
||||||
char symbol = gameBoard.getSymbolAt(col,row);
|
char symbol = gameBoard.getSymbolAt(col, row);
|
||||||
if (symbol != '.') {
|
if (symbol != '.') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int ownedBy = findTerritory(gameBoard,col,row);
|
int ownedBy = findTerritory(gameBoard, col, row);
|
||||||
if (ownedBy == BLACK) {
|
if (ownedBy == BLACK) {
|
||||||
gameBoard.markTerritory(BLACK_TERRITORY);
|
gameBoard.markTerritory(BLACK_TERRITORY);
|
||||||
} else if (ownedBy == WHITE) {
|
} else if (ownedBy == WHITE) {
|
||||||
@@ -30,9 +30,9 @@ public class TerritoryMarker {
|
|||||||
gameBoard.setTerritoryMarked(true);
|
gameBoard.setTerritoryMarked(true);
|
||||||
return gameBoard;
|
return gameBoard;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getStoneColor(GameBoard gameBoard, int col, int row) {
|
public static int getStoneColor(GameBoard gameBoard, int col, int row) {
|
||||||
char symbol = gameBoard.getSymbolAt(col,row);
|
char symbol = gameBoard.getSymbolAt(col, row);
|
||||||
if (symbol == GameBoard.BLACK_STONE) {
|
if (symbol == GameBoard.BLACK_STONE) {
|
||||||
return BLACK;
|
return BLACK;
|
||||||
} else if (symbol == GameBoard.WHITE_STONE) {
|
} else if (symbol == GameBoard.WHITE_STONE) {
|
||||||
@@ -41,9 +41,9 @@ public class TerritoryMarker {
|
|||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int findTerritory(GameBoard gameBoard, int col, int row) {
|
public static int findTerritory(GameBoard gameBoard, int col, int row) {
|
||||||
char symbol = gameBoard.getSymbolAt(col,row);
|
char symbol = gameBoard.getSymbolAt(col, row);
|
||||||
|
|
||||||
if (symbol == GameBoard.BLACK_STONE) {
|
if (symbol == GameBoard.BLACK_STONE) {
|
||||||
return BLACK;
|
return BLACK;
|
||||||
@@ -52,23 +52,23 @@ public class TerritoryMarker {
|
|||||||
} else if (symbol == TERRITORY_MARKER) {
|
} else if (symbol == TERRITORY_MARKER) {
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameBoard.markTerritory(col,row,TERRITORY_MARKER);
|
gameBoard.markTerritory(col, row, TERRITORY_MARKER);
|
||||||
int borderBits = EMPTY;
|
int borderBits = EMPTY;
|
||||||
|
|
||||||
if (col > 0) {
|
if (col > 0) {
|
||||||
borderBits |= findTerritory(gameBoard,col-1,row);
|
borderBits |= findTerritory(gameBoard, col - 1, row);
|
||||||
}
|
}
|
||||||
if (col < gameBoard.getSize() - 1) {
|
if (col < gameBoard.getSize() - 1) {
|
||||||
borderBits |= findTerritory(gameBoard,col+1,row);
|
borderBits |= findTerritory(gameBoard, col + 1, row);
|
||||||
}
|
}
|
||||||
if (row > 0) {
|
if (row > 0) {
|
||||||
borderBits |= findTerritory(gameBoard,col,row-1);
|
borderBits |= findTerritory(gameBoard, col, row - 1);
|
||||||
}
|
}
|
||||||
if (row < gameBoard.getSize() - 1) {
|
if (row < gameBoard.getSize() - 1) {
|
||||||
borderBits |= findTerritory(gameBoard,col,row+1);
|
borderBits |= findTerritory(gameBoard, col, row + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return borderBits;
|
return borderBits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,10 +10,10 @@ 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<Action> getActions(GameConfig gameConfig, GameState gameState,
|
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
||||||
Player color, int numActions);
|
Player color, int numActions);
|
||||||
|
|
||||||
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedMoves, Player color, int numActions);
|
Collection<Action> prohibitedMoves, Player color, int numActions);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class AlphaBeta implements Policy {
|
|||||||
|
|
||||||
private int lookAhead;
|
private int lookAhead;
|
||||||
private int numStateEvaluations = 0;
|
private int numStateEvaluations = 0;
|
||||||
|
|
||||||
public AlphaBeta() {
|
public AlphaBeta() {
|
||||||
this(DEFAULT_LOOKAHEAD);
|
this(DEFAULT_LOOKAHEAD);
|
||||||
}
|
}
|
||||||
@@ -52,8 +52,8 @@ public class AlphaBeta implements Policy {
|
|||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(node
|
||||||
node.getGameState().getGameConfig(), node.getGameState(), player,
|
.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -64,9 +64,9 @@ public class AlphaBeta implements Policy {
|
|||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
gameState.getResult().getNormalizedScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
return bestAction;
|
return bestAction;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -111,8 +111,8 @@ public class AlphaBeta implements Policy {
|
|||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(node
|
||||||
node.getGameState().getGameConfig(), node.getGameState(), player,
|
.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -123,9 +123,9 @@ public class AlphaBeta implements Policy {
|
|||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
gameState.getResult().getNormalizedScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
return bestAction;
|
return bestAction;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -164,15 +164,16 @@ public class AlphaBeta implements Policy {
|
|||||||
return bestAction;
|
return bestAction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getNumStateEvaluations() {
|
public int getNumStateEvaluations() {
|
||||||
return numStateEvaluations;
|
return numStateEvaluations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedActions, Player player) {
|
Collection<Action> prohibitedActions, Player player) {
|
||||||
throw new UnsupportedOperationException("Prohibited actions not supported by this class.");
|
throw new UnsupportedOperationException(
|
||||||
|
"Prohibited actions not supported by this class.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,17 +16,21 @@ public class HumanKeyboardInput implements Policy {
|
|||||||
byte[] inputBytes = new byte[4];
|
byte[] inputBytes = new byte[4];
|
||||||
Action action = null;
|
Action action = null;
|
||||||
String input = "";
|
String input = "";
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
System.out.println(player + " to move: (enter coord or PASS/RESIGN)");
|
System.out.println(player
|
||||||
|
+ " to move: (enter coord or PASS/RESIGN)");
|
||||||
System.in.read(inputBytes);
|
System.in.read(inputBytes);
|
||||||
input = new String(inputBytes).trim().toUpperCase();
|
input = new String(inputBytes).trim().toUpperCase();
|
||||||
|
|
||||||
action = Action.getInstance(input);
|
action = Action.getInstance(input);
|
||||||
|
|
||||||
if (action.isNone()) {
|
if (action.isNone()) {
|
||||||
System.out.println("'" + input +"' is not a valid move. Please enter a coordinate (A1-T19) or 'PASS'.");
|
System.out
|
||||||
|
.println("'"
|
||||||
|
+ input
|
||||||
|
+ "' is not a valid move. Please enter a coordinate (A1-T19) or 'PASS'.");
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -34,27 +38,29 @@ public class HumanKeyboardInput implements Policy {
|
|||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
System.out.println("IOException: " + ioe.getMessage());
|
System.out.println("IOException: " + ioe.getMessage());
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
System.out.println("IllegalArgumentException - '"+input+"' is not a valid action or coordinate.");
|
System.out.println("IllegalArgumentException - '" + input
|
||||||
|
+ "' is not a valid action or coordinate.");
|
||||||
}
|
}
|
||||||
} while (action == null);
|
} while (action == null);
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedActions, Player player) {
|
Collection<Action> prohibitedActions, Player player) {
|
||||||
Action action = getAction(gameConfig,gameState,player);
|
Action action = getAction(gameConfig, gameState, player);
|
||||||
|
|
||||||
if (!prohibitedActions.contains(action)) {
|
if (!prohibitedActions.contains(action)) {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
System.out.println("Sorry, that action is prohibited. Please make another move.");
|
System.out
|
||||||
action = getAction(gameConfig,gameState,player);
|
.println("Sorry, that action is prohibited. Please make another move.");
|
||||||
|
action = getAction(gameConfig, gameState, player);
|
||||||
} while (prohibitedActions.contains(action));
|
} while (prohibitedActions.contains(action));
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class Minimax implements Policy {
|
|||||||
|
|
||||||
private int lookAhead;
|
private int lookAhead;
|
||||||
private int numStateEvaluations = 0;
|
private int numStateEvaluations = 0;
|
||||||
|
|
||||||
public Minimax() {
|
public Minimax() {
|
||||||
this(DEFAULT_LOOKAHEAD);
|
this(DEFAULT_LOOKAHEAD);
|
||||||
}
|
}
|
||||||
@@ -51,8 +51,8 @@ public class Minimax implements Policy {
|
|||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(node
|
||||||
node.getGameState().getGameConfig(), node.getGameState(), player,
|
.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -63,9 +63,9 @@ public class Minimax implements Policy {
|
|||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
gameState.getResult().getNormalizedScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
return bestAction;
|
return bestAction;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -97,8 +97,8 @@ public class Minimax implements Policy {
|
|||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(node
|
||||||
node.getGameState().getGameConfig(), node.getGameState(), player,
|
.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -109,9 +109,9 @@ public class Minimax implements Policy {
|
|||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
gameState.getResult().getNormalizedScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
return bestAction;
|
return bestAction;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ public class Minimax implements Policy {
|
|||||||
return bestAction;
|
return bestAction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getNumStateEvaluations() {
|
public int getNumStateEvaluations() {
|
||||||
return numStateEvaluations;
|
return numStateEvaluations;
|
||||||
@@ -146,6 +146,7 @@ public class Minimax implements Policy {
|
|||||||
@Override
|
@Override
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedActions, Player player) {
|
Collection<Action> prohibitedActions, Player player) {
|
||||||
throw new UnsupportedOperationException("Prohibited actions not supported by this class.");
|
throw new UnsupportedOperationException(
|
||||||
|
"Prohibited actions not supported by this class.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package net.woodyfolsom.msproj.policy;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import net.woodyfolsom.msproj.Action;
|
import net.woodyfolsom.msproj.Action;
|
||||||
import net.woodyfolsom.msproj.GameConfig;
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
@@ -12,88 +14,109 @@ import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
|||||||
|
|
||||||
public abstract class MonteCarlo implements Policy {
|
public abstract class MonteCarlo implements Policy {
|
||||||
protected static final int ROLLOUT_DEPTH_LIMIT = 100;
|
protected static final int ROLLOUT_DEPTH_LIMIT = 100;
|
||||||
|
|
||||||
protected int numStateEvaluations = 0;
|
protected int numStateEvaluations = 0;
|
||||||
protected Policy movePolicy;
|
protected Policy movePolicy;
|
||||||
|
|
||||||
protected long searchTimeLimit;
|
protected long searchTimeLimit;
|
||||||
protected volatile long elapsedTime = 0L;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descend the tree from the specified node and return a list of nodes to grow.
|
* Descend the tree from the specified node and return a list of nodes to
|
||||||
|
* grow.
|
||||||
*
|
*
|
||||||
* @param node
|
* @param node
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public abstract List<GameTreeNode<MonteCarloProperties>> descend(GameTreeNode<MonteCarloProperties> node);
|
public abstract List<GameTreeNode<MonteCarloProperties>> descend(
|
||||||
|
GameTreeNode<MonteCarloProperties> node);
|
||||||
@Override
|
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
private GameTreeNode<MonteCarloProperties> buildTree(GameConfig gameConfig, GameState gameState, Player player) {
|
||||||
Player player) {
|
System.out.println(player + " is thinking for up to "
|
||||||
System.out.println(player + " is thinking for up to " + (searchTimeLimit / 1000.0) + " seconds...");
|
+ (searchTimeLimit / 1000.0) + " seconds...");
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
if (gameState.getPlayerToMove() != player) {
|
if (gameState.getPlayerToMove() != player) {
|
||||||
throw new RuntimeException("getAction(..." + player +") was requested but GameState.playerToMove was " + gameState.getPlayerToMove());
|
throw new RuntimeException("getAction(..." + player
|
||||||
|
+ ") was requested but GameState.playerToMove was "
|
||||||
|
+ gameState.getPlayerToMove());
|
||||||
}
|
}
|
||||||
//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
|
GameTreeNode<MonteCarloProperties> rootNode = new GameTreeNode<MonteCarloProperties>(
|
||||||
//result in a win.
|
gameState, new MonteCarloProperties());
|
||||||
|
|
||||||
GameTreeNode<MonteCarloProperties> rootNode = new GameTreeNode<MonteCarloProperties>(gameState, new MonteCarloProperties());
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
//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
|
||||||
List<GameTreeNode<MonteCarloProperties>> selectedNodes = descend(rootNode);
|
List<GameTreeNode<MonteCarloProperties>> selectedNodes = descend(rootNode);
|
||||||
List<GameTreeNode<MonteCarloProperties>> newLeaves = new ArrayList<GameTreeNode<MonteCarloProperties>>();
|
List<GameTreeNode<MonteCarloProperties>> newLeaves = new ArrayList<GameTreeNode<MonteCarloProperties>>();
|
||||||
|
|
||||||
for (GameTreeNode<MonteCarloProperties> selectedNode: selectedNodes) {
|
for (GameTreeNode<MonteCarloProperties> selectedNode : selectedNodes) {
|
||||||
Player playerToMove = selectedNode.getGameState().getPlayerToMove();
|
Player playerToMove = selectedNode.getGameState()
|
||||||
|
.getPlayerToMove();
|
||||||
for (GameTreeNode<MonteCarloProperties> newLeaf : grow(gameConfig, selectedNode, playerToMove)) {
|
|
||||||
|
for (GameTreeNode<MonteCarloProperties> newLeaf : grow(
|
||||||
|
gameConfig, selectedNode, playerToMove)) {
|
||||||
newLeaves.add(newLeaf);
|
newLeaves.add(newLeaf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GameTreeNode<MonteCarloProperties> newLeaf : newLeaves) {
|
for (GameTreeNode<MonteCarloProperties> newLeaf : newLeaves) {
|
||||||
int reward = rollout(gameConfig, newLeaf, player);
|
int reward = rollout(gameConfig, newLeaf, player);
|
||||||
update(newLeaf, reward);
|
update(newLeaf, reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsedTime = System.currentTimeMillis() - startTime;
|
elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
} while (elapsedTime < searchTimeLimit);
|
} while (elapsedTime < searchTimeLimit);
|
||||||
//TODO: for debugging, temporarily specify the number of state evaluations rather than time limit
|
|
||||||
//} while (numStateEvaluations < searchTimeLimit);
|
return rootNode;
|
||||||
|
|
||||||
return getBestAction(rootNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
Player player) {
|
||||||
|
return getBestAction(buildTree(gameConfig,gameState,player));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Action, MonteCarloProperties> getQvalues(GameConfig gameConfig, GameState gameState,
|
||||||
|
Player player) {
|
||||||
|
GameTreeNode<MonteCarloProperties> rootNode = buildTree(gameConfig,gameState,player);
|
||||||
|
Map<Action,MonteCarloProperties> qValues = new HashMap<Action,MonteCarloProperties>();
|
||||||
|
for (Action action : rootNode.getActions()) {
|
||||||
|
qValues.put(action, rootNode.getChild(action).getProperties());
|
||||||
|
}
|
||||||
|
return qValues;
|
||||||
|
}
|
||||||
|
|
||||||
public long getElapsedTime() {
|
public long getElapsedTime() {
|
||||||
return elapsedTime;
|
return elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Action getBestAction(GameTreeNode<MonteCarloProperties> node);
|
public abstract Action getBestAction(GameTreeNode<MonteCarloProperties> node);
|
||||||
|
|
||||||
public abstract List<GameTreeNode<MonteCarloProperties>> grow(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
|
public abstract List<GameTreeNode<MonteCarloProperties>> grow(
|
||||||
|
GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node,
|
||||||
public abstract int rollout(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
|
Player player);
|
||||||
|
|
||||||
public abstract void update(GameTreeNode<MonteCarloProperties> node, int reward);
|
public abstract int rollout(GameConfig gameConfig,
|
||||||
|
GameTreeNode<MonteCarloProperties> node, Player player);
|
||||||
|
|
||||||
|
public abstract void update(GameTreeNode<MonteCarloProperties> node,
|
||||||
|
int reward);
|
||||||
|
|
||||||
public long getSearchTimeLimit() {
|
public long getSearchTimeLimit() {
|
||||||
return searchTimeLimit;
|
return searchTimeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int doRollout() {
|
public int doRollout() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumStateEvaluations() {
|
public int getNumStateEvaluations() {
|
||||||
return numStateEvaluations;
|
return numStateEvaluations;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,46 +16,55 @@ import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
|||||||
|
|
||||||
public class MonteCarloUCT extends MonteCarlo {
|
public class MonteCarloUCT extends MonteCarlo {
|
||||||
public static final double TUNING_CONSTANT = 0.50;
|
public static final double TUNING_CONSTANT = 0.50;
|
||||||
|
|
||||||
public MonteCarloUCT(Policy movePolicy, long searchTimeLimit) {
|
public MonteCarloUCT(Policy movePolicy, long searchTimeLimit) {
|
||||||
super(movePolicy, searchTimeLimit);
|
super(movePolicy, searchTimeLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GameTreeNode<MonteCarloProperties>> descend(GameTreeNode<MonteCarloProperties> node) {
|
public List<GameTreeNode<MonteCarloProperties>> descend(
|
||||||
|
GameTreeNode<MonteCarloProperties> node) {
|
||||||
double bestScore = Double.NEGATIVE_INFINITY;
|
double bestScore = Double.NEGATIVE_INFINITY;
|
||||||
GameTreeNode<MonteCarloProperties> bestNode = node;
|
GameTreeNode<MonteCarloProperties> bestNode = node;
|
||||||
|
|
||||||
//What if the optimum leaf node is actually a terminal node?
|
// What if the optimum leaf node is actually a terminal node?
|
||||||
//From Kocsis and Szepesvari, the value of an actual terminal node is 0, so it will never be grown.
|
// From Kocsis and Szepesvari, the value of an actual terminal node is
|
||||||
|
// 0, so it will never be grown.
|
||||||
|
|
||||||
double nodeVisits = node.getProperties().getVisits();
|
double nodeVisits = node.getProperties().getVisits();
|
||||||
Set<Action> actionsExplored = node.getActions();
|
Set<Action> actionsExplored = node.getActions();
|
||||||
GameState gameState = node.getGameState();
|
GameState gameState = node.getGameState();
|
||||||
|
|
||||||
Action randomNewChildAction = new RandomMovePolicy().getAction(node.getGameState().getGameConfig(), gameState, actionsExplored, gameState.getPlayerToMove());
|
Action randomNewChildAction = new RandomMovePolicy().getAction(node
|
||||||
|
.getGameState().getGameConfig(), gameState, actionsExplored,
|
||||||
//only descend to the best child if no new actions are available at this node
|
gameState.getPlayerToMove());
|
||||||
|
|
||||||
|
// only descend to the best child if no new actions are available at
|
||||||
|
// this node
|
||||||
if (randomNewChildAction == Action.NONE) {
|
if (randomNewChildAction == Action.NONE) {
|
||||||
for (Action action : actionsExplored) {
|
for (Action action : actionsExplored) {
|
||||||
GameTreeNode<MonteCarloProperties> childNode = node.getChild(action);
|
GameTreeNode<MonteCarloProperties> childNode = node
|
||||||
|
.getChild(action);
|
||||||
double childScore;
|
|
||||||
if (childNode.getGameState().isTerminal()) {
|
double childScore;
|
||||||
childScore = 0.0;
|
if (childNode.getGameState().isTerminal()) {
|
||||||
} else {
|
childScore = 0.0;
|
||||||
MonteCarloProperties properties = childNode.getProperties();
|
} else {
|
||||||
childScore = (double) (properties.getWins() / properties.getVisits()) + (TUNING_CONSTANT * Math.sqrt(Math.log(nodeVisits) / childNode.getProperties().getVisits()));
|
MonteCarloProperties properties = childNode.getProperties();
|
||||||
}
|
childScore = (double) (properties.getWins() / properties
|
||||||
//TODO add random tie breaker?
|
.getVisits())
|
||||||
//otherwise the child that is selected first will be biased
|
+ (TUNING_CONSTANT * Math.sqrt(Math.log(nodeVisits)
|
||||||
if (childScore >= bestScore) {
|
/ childNode.getProperties().getVisits()));
|
||||||
|
}
|
||||||
|
// TODO add random tie breaker?
|
||||||
|
// otherwise the child that is selected first will be biased
|
||||||
|
if (childScore >= bestScore) {
|
||||||
bestScore = childScore;
|
bestScore = childScore;
|
||||||
bestNode = childNode;
|
bestNode = childNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (bestNode == node) {
|
if (bestNode == node) {
|
||||||
List<GameTreeNode<MonteCarloProperties>> bestNodeList = new ArrayList<GameTreeNode<MonteCarloProperties>>();
|
List<GameTreeNode<MonteCarloProperties>> bestNodeList = new ArrayList<GameTreeNode<MonteCarloProperties>>();
|
||||||
bestNodeList.add(bestNode);
|
bestNodeList.add(bestNode);
|
||||||
@@ -70,76 +79,94 @@ public class MonteCarloUCT extends MonteCarlo {
|
|||||||
Action bestAction = Action.NONE;
|
Action bestAction = Action.NONE;
|
||||||
double bestScore = Double.NEGATIVE_INFINITY;
|
double bestScore = Double.NEGATIVE_INFINITY;
|
||||||
GameTreeNode<MonteCarloProperties> bestChild = null;
|
GameTreeNode<MonteCarloProperties> bestChild = null;
|
||||||
|
|
||||||
for (Action action : node.getActions()) {
|
for (Action action : node.getActions()) {
|
||||||
GameTreeNode<MonteCarloProperties> childNode = node.getChild(action);
|
GameTreeNode<MonteCarloProperties> childNode = node
|
||||||
|
.getChild(action);
|
||||||
|
|
||||||
MonteCarloProperties properties = childNode.getProperties();
|
MonteCarloProperties properties = childNode.getProperties();
|
||||||
double childScore = (double) properties.getWins() / properties.getVisits();
|
double childScore = (double) properties.getWins()
|
||||||
|
/ properties.getVisits();
|
||||||
|
|
||||||
if (childScore >= bestScore) {
|
if (childScore >= bestScore) {
|
||||||
bestScore = childScore;
|
bestScore = childScore;
|
||||||
bestAction = action;
|
bestAction = action;
|
||||||
bestChild = childNode;
|
bestChild = childNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestAction == Action.NONE) {
|
if (bestAction == Action.NONE) {
|
||||||
System.out.println("MonteCarloUCT failed - no actions were found for the current game staet (not even PASS).");
|
System.out
|
||||||
|
.println("MonteCarloUCT failed - no actions were found for the current game staet (not even PASS).");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Action " + bestAction + " selected for " + node.getGameState().getPlayerToMove() + " with simulated win ratio of " + (bestScore*100.0 + "%"));
|
System.out.println("Action " + bestAction + " selected for "
|
||||||
System.out.println("It was visited " + bestChild.getProperties().getVisits() + " times out of " + node.getProperties().getVisits() + " rollouts among " + node.getNumChildren() + " valid actions from the current state.");
|
+ node.getGameState().getPlayerToMove()
|
||||||
|
+ " with simulated win ratio of "
|
||||||
|
+ (bestScore * 100.0 + "%"));
|
||||||
|
System.out.println("It was visited "
|
||||||
|
+ bestChild.getProperties().getVisits() + " times out of "
|
||||||
|
+ node.getProperties().getVisits() + " rollouts among "
|
||||||
|
+ node.getNumChildren()
|
||||||
|
+ " valid actions from the current state.");
|
||||||
}
|
}
|
||||||
return bestAction;
|
return bestAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GameTreeNode<MonteCarloProperties>> grow(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player) {
|
public List<GameTreeNode<MonteCarloProperties>> grow(GameConfig gameConfig,
|
||||||
|
GameTreeNode<MonteCarloProperties> node, Player player) {
|
||||||
Policy randomMovePolicy = new RandomMovePolicy();
|
Policy randomMovePolicy = new RandomMovePolicy();
|
||||||
Set<Action> exploredActions = node.getActions();
|
Set<Action> exploredActions = node.getActions();
|
||||||
Action action = randomMovePolicy.getAction(gameConfig, node.getGameState(), exploredActions, player);
|
Action action = randomMovePolicy.getAction(gameConfig,
|
||||||
|
node.getGameState(), exploredActions, player);
|
||||||
if (Action.NONE == action) {
|
if (Action.NONE == action) {
|
||||||
throw new RuntimeException("Unable to grow node - are all actions already explored? Board state: " + node.getGameState() + "\nExplored actions: " + exploredActions);
|
throw new RuntimeException(
|
||||||
|
"Unable to grow node - are all actions already explored? Board state: "
|
||||||
|
+ node.getGameState() + "\nExplored actions: "
|
||||||
|
+ exploredActions);
|
||||||
}
|
}
|
||||||
GameState nextGameState = new GameState(node.getGameState());
|
GameState nextGameState = new GameState(node.getGameState());
|
||||||
nextGameState.playStone(player, action);
|
nextGameState.playStone(player, action);
|
||||||
List<GameTreeNode<MonteCarloProperties>> newChildren = new ArrayList<GameTreeNode<MonteCarloProperties>>();
|
List<GameTreeNode<MonteCarloProperties>> newChildren = new ArrayList<GameTreeNode<MonteCarloProperties>>();
|
||||||
GameTreeNode<MonteCarloProperties> newChild = new GameTreeNode<MonteCarloProperties>(nextGameState,new MonteCarloProperties());
|
GameTreeNode<MonteCarloProperties> newChild = new GameTreeNode<MonteCarloProperties>(
|
||||||
|
nextGameState, new MonteCarloProperties());
|
||||||
|
|
||||||
newChildren.add(newChild);
|
newChildren.add(newChild);
|
||||||
node.addChild(action, newChild);
|
node.addChild(action, newChild);
|
||||||
|
|
||||||
return newChildren;
|
return newChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rollout currently depends on the hardcoded ROLLOUT_DEPTH_LIMIT superclass parameter,
|
* 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.
|
* Even with super-ko detection, a rollout might take an unrealistically long time due to unlikely playouts.
|
||||||
*/
|
*/
|
||||||
public int rollout(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player) {
|
public int rollout(GameConfig gameConfig,
|
||||||
|
GameTreeNode<MonteCarloProperties> node, Player player) {
|
||||||
Policy randomMovePolicy = new RandomMovePolicy();
|
Policy randomMovePolicy = new RandomMovePolicy();
|
||||||
|
|
||||||
Action action;
|
Action action;
|
||||||
int rolloutDepth = 0;
|
int rolloutDepth = 0;
|
||||||
GameState rolloutGameState = new GameState(node.getGameState());
|
GameState rolloutGameState = new GameState(node.getGameState());
|
||||||
Player currentPlayer = rolloutGameState.getPlayerToMove();
|
Player currentPlayer = rolloutGameState.getPlayerToMove();
|
||||||
do {
|
do {
|
||||||
rolloutDepth++;
|
rolloutDepth++;
|
||||||
action = randomMovePolicy.getAction(gameConfig, rolloutGameState, currentPlayer);
|
action = randomMovePolicy.getAction(gameConfig, rolloutGameState,
|
||||||
|
currentPlayer);
|
||||||
if (action != Action.NONE) {
|
if (action != Action.NONE) {
|
||||||
if (!rolloutGameState.playStone(currentPlayer, action)) {
|
if (!rolloutGameState.playStone(currentPlayer, action)) {
|
||||||
throw new RuntimeException("Failed to play move selected by RandomMovePolicy");
|
throw new RuntimeException(
|
||||||
|
"Failed to play move selected by RandomMovePolicy");
|
||||||
}
|
}
|
||||||
currentPlayer = GoGame.getNextPlayer(currentPlayer);
|
currentPlayer = GoGame.getNextPlayer(currentPlayer);
|
||||||
}
|
}
|
||||||
} while (action != Action.NONE && rolloutDepth < ROLLOUT_DEPTH_LIMIT);
|
} while (action != Action.NONE && rolloutDepth < ROLLOUT_DEPTH_LIMIT);
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
GameResult gameScore = rolloutGameState.getResult();
|
GameResult gameScore = rolloutGameState.getResult();
|
||||||
|
|
||||||
if (gameScore.isWinner(player)) {
|
if (gameScore.isWinner(player)) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -161,9 +188,10 @@ public class MonteCarloUCT extends MonteCarlo {
|
|||||||
@Override
|
@Override
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedActions, Player player) {
|
Collection<Action> prohibitedActions, Player player) {
|
||||||
throw new UnsupportedOperationException("Prohibited actions not supported by this class.");
|
throw new UnsupportedOperationException(
|
||||||
|
"Prohibited actions not supported by this class.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "MonteCarloUCT";
|
return "MonteCarloUCT";
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ import net.woodyfolsom.msproj.GameConfig;
|
|||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.Player;
|
import net.woodyfolsom.msproj.Player;
|
||||||
|
|
||||||
|
|
||||||
public interface Policy {
|
public interface Policy {
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState, Player player);
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState, Collection<Action> prohibitedActions, Player player);
|
Player player);
|
||||||
|
|
||||||
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
Collection<Action> prohibitedActions, Player player);
|
||||||
|
|
||||||
public int getNumStateEvaluations();
|
public int getNumStateEvaluations();
|
||||||
}
|
}
|
||||||
@@ -9,21 +9,42 @@ import net.woodyfolsom.msproj.GameConfig;
|
|||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.Player;
|
import net.woodyfolsom.msproj.Player;
|
||||||
|
|
||||||
|
|
||||||
public class RandomMovePolicy implements Policy, ActionGenerator {
|
public class RandomMovePolicy implements Policy, ActionGenerator {
|
||||||
|
private final boolean passWhenLosing = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does NOT modify the gameState.
|
* Does NOT modify the gameState.
|
||||||
*/
|
*/
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedMoves, Player player) {
|
Collection<Action> prohibitedMoves, Player player) {
|
||||||
return getActions(gameConfig, gameState, prohibitedMoves, player, 1).get(0);
|
|
||||||
|
List<Action> randomActions = getActions(gameConfig, gameState, prohibitedMoves, player, 1);
|
||||||
|
|
||||||
|
//Never randomly generate PASS when losing, because a good opponent will immediately recognize the killer
|
||||||
|
//move of also passing, thereby causing the current player to lose.
|
||||||
|
Action firstAction = randomActions.get(0);
|
||||||
|
|
||||||
|
//Pass when losing enabled? Just return the first random action;
|
||||||
|
if (passWhenLosing) {
|
||||||
|
return firstAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
//But if passing is the only valid move, pass even if losing.
|
||||||
|
if (firstAction.isPass() && !gameState.getResult().isWinner(player)) {
|
||||||
|
if (randomActions.size() > 1) {
|
||||||
|
return randomActions.get(1);
|
||||||
|
} else {
|
||||||
|
return firstAction;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return firstAction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to generate up to nMoves random moves on behalf of the specified
|
* Attempts to generate up to nMoves random moves on behalf of the specified
|
||||||
* player. Will return at least one move, which may be 'NONE' if random
|
* player. Will return at least one move, which may be 'NONE' if random
|
||||||
* search does not succeeed in discovering a valid move. Does NOT modify the
|
* search does not succeed in discovering a valid move. Does NOT modify the
|
||||||
* gameState.
|
* gameState.
|
||||||
*
|
*
|
||||||
* @param gameConfig
|
* @param gameConfig
|
||||||
@@ -35,32 +56,35 @@ public class RandomMovePolicy implements Policy, ActionGenerator {
|
|||||||
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
||||||
Collection<Action> prohibitedMoves, Player player, int nMoves) {
|
Collection<Action> prohibitedMoves, Player player, int nMoves) {
|
||||||
if (player != gameState.getPlayerToMove()) {
|
if (player != gameState.getPlayerToMove()) {
|
||||||
throw new IllegalArgumentException("It is not " + player + "'s turn to move!");
|
throw new IllegalArgumentException("It is not " + player
|
||||||
|
+ "'s turn to move!");
|
||||||
}
|
}
|
||||||
|
|
||||||
GameState gameStateCopy = new GameState(gameState);
|
GameState gameStateCopy = new GameState(gameState);
|
||||||
ActionGenerator actionGenerator = new ValidMoveGenerator();
|
ActionGenerator actionGenerator = new ValidMoveGenerator();
|
||||||
|
|
||||||
List<Action> possibleActions = actionGenerator.getActions(gameConfig, gameStateCopy, prohibitedMoves, player, ActionGenerator.ALL_ACTIONS);
|
List<Action> possibleActions = actionGenerator.getActions(gameConfig,
|
||||||
|
gameStateCopy, prohibitedMoves, player,
|
||||||
|
ActionGenerator.ALL_ACTIONS);
|
||||||
List<Action> randomActions = new ArrayList<Action>();
|
List<Action> randomActions = new ArrayList<Action>();
|
||||||
|
|
||||||
while (possibleActions.size() > 0 && randomActions.size() < nMoves) {
|
while (possibleActions.size() > 0 && randomActions.size() < nMoves) {
|
||||||
Action randomAction = possibleActions
|
Action randomAction = possibleActions
|
||||||
.remove((int) (Math.random() * possibleActions.size()));
|
.remove((int) (Math.random() * possibleActions.size()));
|
||||||
|
|
||||||
randomActions.add(randomAction);
|
randomActions.add(randomAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (randomActions.size() == 0) {
|
if (randomActions.size() == 0) {
|
||||||
randomActions.add(Action.NONE);
|
randomActions.add(Action.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomActions;
|
return randomActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RandomMoveGenerator does not evaluate any states, but simply returns elements of
|
* RandomMoveGenerator does not evaluate any states, but simply returns
|
||||||
* a set of uniformly distributed, distinct valid moves.
|
* elements of a set of uniformly distributed, distinct valid moves.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@@ -71,12 +95,13 @@ public class RandomMovePolicy implements Policy, ActionGenerator {
|
|||||||
@Override
|
@Override
|
||||||
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
public List<Action> getActions(GameConfig gameConfig, GameState gameState,
|
||||||
Player color, int numActions) {
|
Player color, int numActions) {
|
||||||
return getActions(gameConfig, gameState, new ArrayList<Action>(), color, numActions);
|
return getActions(gameConfig, gameState, new ArrayList<Action>(),
|
||||||
|
color, numActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Player player) {
|
Player player) {
|
||||||
return getActions(gameConfig,gameState,player,1).get(0);
|
return getActions(gameConfig, gameState, player, 1).get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
145
src/net/woodyfolsom/msproj/policy/RootParallelization.java
Normal file
145
src/net/woodyfolsom/msproj/policy/RootParallelization.java
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.Action;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.Player;
|
||||||
|
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
||||||
|
|
||||||
|
public class RootParallelization implements Policy {
|
||||||
|
private int numTrees = 1;
|
||||||
|
private long timeLimit = 1000L;
|
||||||
|
|
||||||
|
public RootParallelization(int numTrees, long timeLimit) {
|
||||||
|
this.numTrees = numTrees;
|
||||||
|
this.timeLimit = timeLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
Player player) {
|
||||||
|
Action bestAction = Action.NONE;
|
||||||
|
|
||||||
|
List<PolicyRunner> policyRunners = new ArrayList<PolicyRunner>();
|
||||||
|
List<Thread> simulationThreads = new ArrayList<Thread>();
|
||||||
|
|
||||||
|
for (int i = 0; i < numTrees; i++) {
|
||||||
|
PolicyRunner policyRunner = new PolicyRunner(new MonteCarloUCT(
|
||||||
|
new RandomMovePolicy(), timeLimit), gameConfig, gameState,
|
||||||
|
player);
|
||||||
|
policyRunners.add(policyRunner);
|
||||||
|
|
||||||
|
Thread simThread = new Thread(policyRunner);
|
||||||
|
simulationThreads.add(simThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Thread simThread : simulationThreads) {
|
||||||
|
simThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Thread simThread : simulationThreads) {
|
||||||
|
try {
|
||||||
|
simThread.join();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
System.out
|
||||||
|
.println("Interrupted while waiting for Monte Carlo simulations to finish.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Action,Integer> totalReward = new HashMap<Action,Integer>();
|
||||||
|
Map<Action,Integer> numSims = new HashMap<Action,Integer>();
|
||||||
|
|
||||||
|
for (PolicyRunner policyRunner : policyRunners) {
|
||||||
|
Map<Action, MonteCarloProperties> qValues = policyRunner.getQvalues();
|
||||||
|
for (Action action : qValues.keySet()) {
|
||||||
|
if (totalReward.containsKey(action)) {
|
||||||
|
totalReward.put(action, totalReward.get(action) + qValues.get(action).getWins());
|
||||||
|
} else {
|
||||||
|
totalReward.put(action, qValues.get(action).getWins());
|
||||||
|
}
|
||||||
|
if (numSims.containsKey(action)) {
|
||||||
|
numSims.put(action, numSims.get(action) + qValues.get(action).getVisits());
|
||||||
|
} else {
|
||||||
|
numSims.put(action, qValues.get(action).getVisits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double bestValue = 0.0;
|
||||||
|
int bestWins = 0;
|
||||||
|
int totalRollouts = 0;
|
||||||
|
|
||||||
|
for (Action action : totalReward.keySet())
|
||||||
|
{
|
||||||
|
int totalWins = totalReward.get(action);
|
||||||
|
int totalSims = numSims.get(action);
|
||||||
|
|
||||||
|
totalRollouts += totalSims;
|
||||||
|
|
||||||
|
double value = ((double)totalWins) / totalSims;
|
||||||
|
|
||||||
|
if (bestAction.isNone() || bestValue < value) {
|
||||||
|
bestAction = action;
|
||||||
|
bestValue = value;
|
||||||
|
bestWins = totalWins;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Action " + bestAction + " selected for "
|
||||||
|
+ player
|
||||||
|
+ " with simulated win ratio of "
|
||||||
|
+ (bestValue * 100.0 + "% among " + numTrees + " parallel simulations."));
|
||||||
|
System.out.println("It had a value of "
|
||||||
|
+ bestValue + " out of "
|
||||||
|
+ totalRollouts + " rollouts among "
|
||||||
|
+ " valid actions from the current state.");
|
||||||
|
|
||||||
|
return bestAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
Collection<Action> prohibitedActions, Player player) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Prohibited actions not supported by this class.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumStateEvaluations() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PolicyRunner implements Runnable {
|
||||||
|
Map<Action,MonteCarloProperties> qValues;
|
||||||
|
|
||||||
|
private GameConfig gameConfig;
|
||||||
|
private GameState gameState;
|
||||||
|
private Player player;
|
||||||
|
private MonteCarlo policy;
|
||||||
|
|
||||||
|
public PolicyRunner(MonteCarlo policy, GameConfig gameConfig,
|
||||||
|
GameState gameState, Player player) {
|
||||||
|
this.policy = policy;
|
||||||
|
this.gameConfig = gameConfig;
|
||||||
|
this.gameState = gameState;
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Action,MonteCarloProperties> getQvalues() {
|
||||||
|
return qValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
qValues = policy.getQvalues(gameConfig, gameState, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,7 +32,7 @@ public class ValidMoveGenerator implements ActionGenerator {
|
|||||||
if (!prohibitedMoves.contains(nextMove)
|
if (!prohibitedMoves.contains(nextMove)
|
||||||
&& gameStateCopy.playStone(color, nextMove)) {
|
&& gameStateCopy.playStone(color, nextMove)) {
|
||||||
validMoves.add(nextMove);
|
validMoves.add(nextMove);
|
||||||
gameStateCopy = new GameState(gameState); // play successful?
|
gameStateCopy = new GameState(gameState); // play successful?
|
||||||
// regenerate copy
|
// regenerate copy
|
||||||
// of gameState
|
// of gameState
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package net.woodyfolsom.msproj.sgf;
|
|||||||
public class SGFCoord {
|
public class SGFCoord {
|
||||||
private char column;
|
private char column;
|
||||||
private char row;
|
private char row;
|
||||||
|
|
||||||
public SGFCoord(String coords) {
|
public SGFCoord(String coords) {
|
||||||
if (coords == null || coords.length() != 2) {
|
if (coords == null || coords.length() != 2) {
|
||||||
throw new IllegalArgumentException(coords);
|
throw new IllegalArgumentException(coords);
|
||||||
@@ -11,22 +11,22 @@ public class SGFCoord {
|
|||||||
column = coords.charAt(0);
|
column = coords.charAt(0);
|
||||||
row = coords.charAt(1);
|
row = coords.charAt(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SGFCoord(char column, char row) {
|
public SGFCoord(char column, char row) {
|
||||||
this.column = column;
|
this.column = column;
|
||||||
this.row = row;
|
this.row = row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getColumn() {
|
public char getColumn() {
|
||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getRow() {
|
public char getRow() {
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new String(new char[]{column,row});
|
return new String(new char[] { column, row });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,32 +9,31 @@ import net.woodyfolsom.msproj.Player;
|
|||||||
public class SGFGameTree {
|
public class SGFGameTree {
|
||||||
private List<SGFNode> nodeSequence = new ArrayList<SGFNode>();
|
private List<SGFNode> nodeSequence = new ArrayList<SGFNode>();
|
||||||
private List<SGFGameTree> subTrees = new ArrayList<SGFGameTree>();
|
private List<SGFGameTree> subTrees = new ArrayList<SGFGameTree>();
|
||||||
|
|
||||||
|
|
||||||
public int getNodeCount() {
|
public int getNodeCount() {
|
||||||
return nodeSequence.size();
|
return nodeSequence.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SGFNode> getNodeSequence() {
|
public List<SGFNode> getNodeSequence() {
|
||||||
return Collections.unmodifiableList(nodeSequence);
|
return Collections.unmodifiableList(nodeSequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNodeSequence(List<SGFNode> nodeSequence) {
|
public void setNodeSequence(List<SGFNode> nodeSequence) {
|
||||||
this.nodeSequence.clear();
|
this.nodeSequence.clear();
|
||||||
|
|
||||||
for(SGFNode node : nodeSequence) {
|
for (SGFNode node : nodeSequence) {
|
||||||
this.nodeSequence.add(node);
|
this.nodeSequence.add(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSubTree(SGFGameTree subTree) {
|
public void addSubTree(SGFGameTree subTree) {
|
||||||
subTrees.add(subTree);
|
subTrees.add(subTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSubTreeCount() {
|
public int getSubTreeCount() {
|
||||||
return subTrees.size();
|
return subTrees.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toLateXmoves(Player player) {
|
public String toLateXmoves(Player player) {
|
||||||
StringBuilder latexSB = new StringBuilder();
|
StringBuilder latexSB = new StringBuilder();
|
||||||
SGFNode.TYPE nodeType;
|
SGFNode.TYPE nodeType;
|
||||||
@@ -50,7 +49,7 @@ public class SGFGameTree {
|
|||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Invalid player: " + player);
|
throw new RuntimeException("Invalid player: " + player);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean firstMove = true;
|
boolean firstMove = true;
|
||||||
int nMoves = 0;
|
int nMoves = 0;
|
||||||
for (SGFNode node : nodeSequence) {
|
for (SGFNode node : nodeSequence) {
|
||||||
@@ -59,7 +58,7 @@ public class SGFGameTree {
|
|||||||
}
|
}
|
||||||
SGFValue<?> sgfValue = node.getFirstValue(sgfIdent);
|
SGFValue<?> sgfValue = node.getFirstValue(sgfIdent);
|
||||||
if (sgfValue.isEmpty()) {
|
if (sgfValue.isEmpty()) {
|
||||||
//TODO later this will be the LaTeX igo code for 'Pass'?
|
// TODO later this will be the LaTeX igo code for 'Pass'?
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (firstMove) {
|
if (firstMove) {
|
||||||
@@ -83,33 +82,36 @@ public class SGFGameTree {
|
|||||||
latexSB.append("}\n");
|
latexSB.append("}\n");
|
||||||
return latexSB.toString();
|
return latexSB.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toLateX() {
|
public String toLateX() {
|
||||||
StringBuilder latexSB = new StringBuilder();
|
StringBuilder latexSB = new StringBuilder();
|
||||||
|
|
||||||
//Somewhat convoluted logic here because the grammar does not require all root
|
// Somewhat convoluted logic here because the grammar does not require
|
||||||
//properties to be included in the same node in the tree's node sequence, although they should
|
// all root
|
||||||
//each be unique among all node sequences in the tree.
|
// properties to be included in the same node in the tree's node
|
||||||
|
// sequence, although they should
|
||||||
|
// each be unique among all node sequences in the tree.
|
||||||
for (SGFNode node : nodeSequence) {
|
for (SGFNode node : nodeSequence) {
|
||||||
SGFNode.TYPE nodeType = node.getType();
|
SGFNode.TYPE nodeType = node.getType();
|
||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
case ROOT :
|
case ROOT:
|
||||||
latexSB.append("\\gobansize");
|
latexSB.append("\\gobansize");
|
||||||
latexSB.append("{");
|
latexSB.append("{");
|
||||||
latexSB.append(node.getFirstValue(SGFIdentifier.SIZE));
|
latexSB.append(node.getFirstValue(SGFIdentifier.SIZE));
|
||||||
latexSB.append("}\n");
|
latexSB.append("}\n");
|
||||||
latexSB.append("\\shortstack{\\showfullgoban\\\\");
|
latexSB.append("\\shortstack{\\showfullgoban\\\\");
|
||||||
SGFResult result = (SGFResult) node.getFirstValue(SGFIdentifier.RESULT).getValue();
|
SGFResult result = (SGFResult) node.getFirstValue(
|
||||||
latexSB.append(result.getFullText());
|
SGFIdentifier.RESULT).getValue();
|
||||||
latexSB.append("}\n");
|
latexSB.append(result.getFullText());
|
||||||
|
latexSB.append("}\n");
|
||||||
break;
|
break;
|
||||||
default :
|
default:
|
||||||
//ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return latexSB.toString();
|
return latexSB.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toSGF() {
|
public String toSGF() {
|
||||||
StringBuilder sgfFormatString = new StringBuilder("(");
|
StringBuilder sgfFormatString = new StringBuilder("(");
|
||||||
for (SGFNode node : nodeSequence) {
|
for (SGFNode node : nodeSequence) {
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ public class SGFIdentifier {
|
|||||||
public static final SGFIdentifier RESULT = new SGFIdentifier("RE");
|
public static final SGFIdentifier RESULT = new SGFIdentifier("RE");
|
||||||
public static final SGFIdentifier SIZE = new SGFIdentifier("SZ");
|
public static final SGFIdentifier SIZE = new SGFIdentifier("SZ");
|
||||||
public static final SGFIdentifier TIME = new SGFIdentifier("TM");
|
public static final SGFIdentifier TIME = new SGFIdentifier("TM");
|
||||||
|
|
||||||
private String text;
|
private String text;
|
||||||
|
|
||||||
private SGFIdentifier(String value) {
|
private SGFIdentifier(String value) {
|
||||||
this.text = value.toString();
|
this.text = value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return text;
|
return text;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,11 +4,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SGFNode {
|
public class SGFNode {
|
||||||
public enum TYPE { ROOT, MOVE_BLACK, MOVE_WHITE, EMPTY }
|
public enum TYPE {
|
||||||
|
ROOT, MOVE_BLACK, MOVE_WHITE, EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
private List<SGFProperty> properties = new ArrayList<SGFProperty>();
|
private List<SGFProperty> properties = new ArrayList<SGFProperty>();
|
||||||
private TYPE type = TYPE.EMPTY;
|
private TYPE type = TYPE.EMPTY;
|
||||||
|
|
||||||
public void addProperty(SGFProperty property) {
|
public void addProperty(SGFProperty property) {
|
||||||
if (properties.size() == 0) {
|
if (properties.size() == 0) {
|
||||||
SGFIdentifier sgfIdent = property.getIdentifier();
|
SGFIdentifier sgfIdent = property.getIdentifier();
|
||||||
@@ -22,37 +24,39 @@ public class SGFNode {
|
|||||||
}
|
}
|
||||||
properties.add(property);
|
properties.add(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPropertyCount() {
|
public int getPropertyCount() {
|
||||||
return properties.size();
|
return properties.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TYPE getType() {
|
public TYPE getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SGFValue<?> getFirstValue(SGFIdentifier identifier) {
|
public SGFValue<?> getFirstValue(SGFIdentifier identifier) {
|
||||||
for (int i = 0; i < properties.size(); i++) {
|
for (int i = 0; i < properties.size(); i++) {
|
||||||
if (identifier == properties.get(i).getIdentifier()) {
|
if (identifier == properties.get(i).getIdentifier()) {
|
||||||
return properties.get(i).getValues().get(0);
|
return properties.get(i).getValues().get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new RuntimeException("SGFNode does not contain a property with identifier '" + identifier +"'");
|
throw new RuntimeException(
|
||||||
|
"SGFNode does not contain a property with identifier '"
|
||||||
|
+ identifier + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toSGF() {
|
public String toSGF() {
|
||||||
if (properties.size() == 0) {
|
if (properties.size() == 0) {
|
||||||
throw new RuntimeException("SGFNode contains no properties");
|
throw new RuntimeException("SGFNode contains no properties");
|
||||||
}
|
}
|
||||||
|
|
||||||
SGFProperty firstProp = properties.get(0);
|
SGFProperty firstProp = properties.get(0);
|
||||||
|
|
||||||
String sgfFormatString = ";" + firstProp.toSGF();
|
String sgfFormatString = ";" + firstProp.toSGF();
|
||||||
|
|
||||||
for (int i = 1; i < properties.size(); i++) {
|
for (int i = 1; i < properties.size(); i++) {
|
||||||
sgfFormatString += properties.get(i).toSGF();
|
sgfFormatString += properties.get(i).toSGF();
|
||||||
}
|
}
|
||||||
|
|
||||||
return sgfFormatString;
|
return sgfFormatString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,32 +7,32 @@ import net.woodyfolsom.msproj.Player;
|
|||||||
|
|
||||||
public class SGFNodeCollection {
|
public class SGFNodeCollection {
|
||||||
private List<SGFGameTree> gameTrees = new ArrayList<SGFGameTree>();
|
private List<SGFGameTree> gameTrees = new ArrayList<SGFGameTree>();
|
||||||
|
|
||||||
public void add(SGFGameTree gameTree) {
|
public void add(SGFGameTree gameTree) {
|
||||||
gameTrees.add(gameTree);
|
gameTrees.add(gameTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SGFGameTree getGameTree(int index) {
|
public SGFGameTree getGameTree(int index) {
|
||||||
return gameTrees.get(index);
|
return gameTrees.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGameTreeCount() {
|
public int getGameTreeCount() {
|
||||||
return gameTrees.size();
|
return gameTrees.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toLateX() {
|
public String toLateX() {
|
||||||
StringBuilder latexFormatString = new StringBuilder("");
|
StringBuilder latexFormatString = new StringBuilder("");
|
||||||
SGFGameTree gameTree = gameTrees.get(0);
|
SGFGameTree gameTree = gameTrees.get(0);
|
||||||
|
|
||||||
latexFormatString.append(gameTree.toLateXmoves(Player.BLACK));
|
latexFormatString.append(gameTree.toLateXmoves(Player.BLACK));
|
||||||
latexFormatString.append(gameTree.toLateXmoves(Player.WHITE));
|
latexFormatString.append(gameTree.toLateXmoves(Player.WHITE));
|
||||||
latexFormatString.append("\\begin{center}\n");
|
latexFormatString.append("\\begin{center}\n");
|
||||||
latexFormatString.append(gameTree.toLateX());
|
latexFormatString.append(gameTree.toLateX());
|
||||||
latexFormatString.append("\\end{center}");
|
latexFormatString.append("\\end{center}");
|
||||||
|
|
||||||
return latexFormatString.toString();
|
return latexFormatString.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toSGF() {
|
public String toSGF() {
|
||||||
String sgfFormatString = "";
|
String sgfFormatString = "";
|
||||||
for (SGFGameTree gameTree : gameTrees) {
|
for (SGFGameTree gameTree : gameTrees) {
|
||||||
@@ -40,7 +40,7 @@ public class SGFNodeCollection {
|
|||||||
}
|
}
|
||||||
return sgfFormatString;
|
return sgfFormatString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return toSGF();
|
return toSGF();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,27 +2,27 @@ package net.woodyfolsom.msproj.sgf;
|
|||||||
|
|
||||||
public class SGFValue<T> {
|
public class SGFValue<T> {
|
||||||
public static final SGFValue<String> EMPTY = new SGFValue<String>("");
|
public static final SGFValue<String> EMPTY = new SGFValue<String>("");
|
||||||
|
|
||||||
private String text;
|
private String text;
|
||||||
private T value;
|
private T value;
|
||||||
|
|
||||||
public SGFValue(T value) {
|
public SGFValue(T value) {
|
||||||
this.text = value.toString();
|
this.text = value.toString();
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return text.length() == 0;
|
return text.length() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getValue() {
|
public T getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.woodyfolsom.msproj.sgf;
|
|||||||
|
|
||||||
public class StrValue {
|
public class StrValue {
|
||||||
public final String value;
|
public final String value;
|
||||||
|
|
||||||
public StrValue(String value) {
|
public StrValue(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
package net.woodyfolsom.msproj.tree;
|
package net.woodyfolsom.msproj.tree;
|
||||||
|
|
||||||
public class AlphaBetaProperties extends MinimaxProperties {
|
public class AlphaBetaProperties extends MinimaxProperties {
|
||||||
|
|
||||||
double alpha = Double.NEGATIVE_INFINITY;
|
double alpha = Double.NEGATIVE_INFINITY;
|
||||||
double beta = Double.POSITIVE_INFINITY;
|
double beta = Double.POSITIVE_INFINITY;
|
||||||
|
|
||||||
public double getAlpha() {
|
public double getAlpha() {
|
||||||
return alpha;
|
return alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAlpha(double d) {
|
public void setAlpha(double d) {
|
||||||
this.alpha = d;
|
this.alpha = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getBeta() {
|
public double getBeta() {
|
||||||
return beta;
|
return beta;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBeta(double beta) {
|
public void setBeta(double beta) {
|
||||||
this.beta = beta;
|
this.beta = beta;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.util.Set;
|
|||||||
|
|
||||||
import net.woodyfolsom.msproj.Action;
|
import net.woodyfolsom.msproj.Action;
|
||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.Player;
|
|
||||||
|
|
||||||
public class GameTreeNode<T extends GameTreeNodeProperties> {
|
public class GameTreeNode<T extends GameTreeNodeProperties> {
|
||||||
private GameState gameState;
|
private GameState gameState;
|
||||||
@@ -39,7 +38,7 @@ public class GameTreeNode<T extends GameTreeNodeProperties> {
|
|||||||
public GameState getGameState() {
|
public GameState getGameState() {
|
||||||
return gameState;
|
return gameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameTreeNode<T> getParent() {
|
public GameTreeNode<T> getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
@@ -47,7 +46,7 @@ public class GameTreeNode<T extends GameTreeNodeProperties> {
|
|||||||
public T getProperties() {
|
public T getProperties() {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRoot() {
|
public boolean isRoot() {
|
||||||
return parent == null;
|
return parent == null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ package net.woodyfolsom.msproj.tree;
|
|||||||
public class MonteCarloProperties extends GameTreeNodeProperties {
|
public class MonteCarloProperties extends GameTreeNodeProperties {
|
||||||
int visits = 0;
|
int visits = 0;
|
||||||
int wins = 0;
|
int wins = 0;
|
||||||
|
|
||||||
public int getVisits() {
|
public int getVisits() {
|
||||||
return visits;
|
return visits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisits(int visits) {
|
public void setVisits(int visits) {
|
||||||
this.visits = visits;
|
this.visits = visits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getWins() {
|
public int getWins() {
|
||||||
return wins;
|
return wins;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWins(int wins) {
|
public void setWins(int wins) {
|
||||||
this.wins = wins;
|
this.wins = wins;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ public class AlphaBetaTest {
|
|||||||
Policy treeSearch = new AlphaBeta();
|
Policy treeSearch = new AlphaBeta();
|
||||||
GameConfig gameConfig = new GameConfig(6);
|
GameConfig gameConfig = new GameConfig(6);
|
||||||
GameState gameState = new GameState(gameConfig);
|
GameState gameState = new GameState(gameConfig);
|
||||||
|
gameState.playStone(Player.BLACK, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("A2"));
|
gameState.playStone(Player.WHITE, Action.getInstance("A2"));
|
||||||
|
gameState.playStone(Player.BLACK, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("B1"));
|
gameState.playStone(Player.WHITE, Action.getInstance("B1"));
|
||||||
|
gameState.playStone(Player.BLACK, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("C2"));
|
gameState.playStone(Player.WHITE, Action.getInstance("C2"));
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B2"));
|
gameState.playStone(Player.BLACK, Action.getInstance("B2"));
|
||||||
|
|
||||||
@@ -38,7 +41,9 @@ public class AlphaBetaTest {
|
|||||||
GameConfig gameConfig = new GameConfig(6);
|
GameConfig gameConfig = new GameConfig(6);
|
||||||
GameState gameState = new GameState(gameConfig);
|
GameState gameState = new GameState(gameConfig);
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("A2"));
|
gameState.playStone(Player.BLACK, Action.getInstance("A2"));
|
||||||
|
gameState.playStone(Player.WHITE, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B1"));
|
gameState.playStone(Player.BLACK, Action.getInstance("B1"));
|
||||||
|
gameState.playStone(Player.WHITE, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("C2"));
|
gameState.playStone(Player.BLACK, Action.getInstance("C2"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("B2"));
|
gameState.playStone(Player.WHITE, Action.getInstance("B2"));
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ public class MonteCarloUCTTest {
|
|||||||
Policy treeSearch = new MonteCarloUCT(new RandomMovePolicy(),10000L);
|
Policy treeSearch = new MonteCarloUCT(new RandomMovePolicy(),10000L);
|
||||||
GameConfig gameConfig = new GameConfig(6);
|
GameConfig gameConfig = new GameConfig(6);
|
||||||
GameState gameState = new GameState(gameConfig);
|
GameState gameState = new GameState(gameConfig);
|
||||||
|
gameState.playStone(Player.BLACK, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("A2"));
|
gameState.playStone(Player.WHITE, Action.getInstance("A2"));
|
||||||
|
gameState.playStone(Player.BLACK, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("B1"));
|
gameState.playStone(Player.WHITE, Action.getInstance("B1"));
|
||||||
|
gameState.playStone(Player.BLACK, Action.getInstance("PASS"));
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("C2"));
|
gameState.playStone(Player.WHITE, Action.getInstance("C2"));
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B2"));
|
gameState.playStone(Player.BLACK, Action.getInstance("B2"));
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ package net.woodyfolsom.msproj.sgf;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.antlr.runtime.ANTLRInputStream;
|
import org.antlr.runtime.ANTLRInputStream;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package net.woodyfolsom.msproj.sgf;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.antlr.runtime.ANTLRInputStream;
|
import org.antlr.runtime.ANTLRInputStream;
|
||||||
@@ -15,20 +14,21 @@ public class SGFParserTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParse() throws RecognitionException, IOException {
|
public void testParse() throws RecognitionException, IOException {
|
||||||
FileInputStream fis = new FileInputStream(new File("data/games/1334-gokifu-20120916-Gu_Li-Lee_Sedol.sgf"));
|
FileInputStream fis = new FileInputStream(new File(
|
||||||
ANTLRStringStream in = new ANTLRInputStream(fis);
|
"data/games/1334-gokifu-20120916-Gu_Li-Lee_Sedol.sgf"));
|
||||||
SGFLexer lexer = new SGFLexer(in);
|
ANTLRStringStream in = new ANTLRInputStream(fis);
|
||||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
SGFLexer lexer = new SGFLexer(in);
|
||||||
SGFParser parser = new SGFParser(tokens);
|
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||||
SGFNodeCollection nodeCollection = parser.collection();
|
SGFParser parser = new SGFParser(tokens);
|
||||||
|
SGFNodeCollection nodeCollection = parser.collection();
|
||||||
System.out.println("To SGF:");
|
|
||||||
System.out.println(nodeCollection.toSGF());
|
System.out.println("To SGF:");
|
||||||
System.out.println("");
|
System.out.println(nodeCollection.toSGF());
|
||||||
|
System.out.println("");
|
||||||
System.out.println("To LaTeX:");
|
|
||||||
System.out.println(nodeCollection.toLateX());
|
System.out.println("To LaTeX:");
|
||||||
System.out.println("");
|
System.out.println(nodeCollection.toLateX());
|
||||||
|
System.out.println("");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user