Consolidated StateEvaluator class into GameState.getResult(). Fixed some issues with komi and normalized score calculation.
This commit is contained in:
@@ -12,34 +12,48 @@ public class Action {
|
|||||||
//private Player player;
|
//private Player player;
|
||||||
private String move;
|
private String move;
|
||||||
|
|
||||||
public static final Action NONE = new Action("NONE", /*Player.NONE,*/ 'x', 0);
|
public static final Action NONE = new Action("NONE", 'x', 0);
|
||||||
public static final Action PASS = new Action("PASS", /*Player.NONE,*/ 'x', 0);
|
public static final Action PASS = new Action("PASS", 'x', 0);
|
||||||
|
public static final Action RESIGN = new Action("RESIGN", 'x', 0);
|
||||||
|
|
||||||
private Action(String move, /*Player player,*/ char column, int row) {
|
private Action(String move, char column, int row) {
|
||||||
this.move = move;
|
this.move = move;
|
||||||
//this.player = player;
|
|
||||||
this.column = column;
|
this.column = column;
|
||||||
this.row = row;
|
this.row = row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Action getInstance(String move) {
|
public static final Action getInstance(String move) {
|
||||||
if (move == null || "NONE".equals(move)) {
|
if (move.length() < 2) {
|
||||||
throw new IllegalArgumentException("Illegal move: " + move);
|
return Action.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("NONE".equals(move)) {
|
||||||
|
return Action.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("PASS".equals(move)) {
|
if ("PASS".equals(move)) {
|
||||||
return Action.PASS;
|
return Action.PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("RESIGN".equals(move)) {
|
||||||
|
return Action.RESIGN;
|
||||||
|
}
|
||||||
|
|
||||||
if (actionMap.containsKey(move)) {
|
if (actionMap.containsKey(move)) {
|
||||||
return actionMap.get(move);
|
return actionMap.get(move);
|
||||||
}
|
}
|
||||||
|
|
||||||
char col = move.charAt(0);
|
Action action = Action.NONE;
|
||||||
int row = Integer.valueOf(move.substring(1));
|
|
||||||
|
|
||||||
Action action = new Action(move, col, row);
|
try {
|
||||||
actionMap.put(move, action);
|
char col = move.charAt(0);
|
||||||
|
int row = Integer.valueOf(move.substring(1));
|
||||||
|
|
||||||
|
action = new Action(move, col, row);
|
||||||
|
actionMap.put(move, action);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return Action.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
@@ -60,6 +74,10 @@ public class Action {
|
|||||||
return this == Action.PASS;
|
return this == Action.PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isResign() {
|
||||||
|
return this == Action.RESIGN;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return move;
|
return move;
|
||||||
|
|||||||
100
src/net/woodyfolsom/msproj/GameResult.java
Normal file
100
src/net/woodyfolsom/msproj/GameResult.java
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
|
||||||
|
public class GameResult {
|
||||||
|
public static final GameResult BLACK_BY_RESIGNATION = new GameResult(RESULT_TYPE.BLACK_BY_RESIGNATION);
|
||||||
|
public static final GameResult WHITE_BY_RESIGNATION = new GameResult(RESULT_TYPE.WHITE_BY_RESIGNATION);
|
||||||
|
|
||||||
|
private double komi;
|
||||||
|
private int blackScore;
|
||||||
|
private int whiteScore;
|
||||||
|
private int normalizedZeroScore;
|
||||||
|
|
||||||
|
public enum RESULT_TYPE { BLACK_BY_RESIGNATION, WHITE_BY_RESIGNATION, IN_PROGRESS, SCORED}
|
||||||
|
|
||||||
|
private RESULT_TYPE resultType;
|
||||||
|
|
||||||
|
public GameResult(int blackScore, int whiteScore, int size, double komi, boolean finished) {
|
||||||
|
this.blackScore = blackScore;
|
||||||
|
this.komi = komi;
|
||||||
|
this.whiteScore = whiteScore;
|
||||||
|
this.normalizedZeroScore = size * size + ((int)(komi * 2));
|
||||||
|
if (finished) {
|
||||||
|
resultType = RESULT_TYPE.SCORED;
|
||||||
|
} else {
|
||||||
|
resultType = RESULT_TYPE.IN_PROGRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GameResult(RESULT_TYPE resultType) {
|
||||||
|
komi = 0.0;
|
||||||
|
blackScore = 0;
|
||||||
|
whiteScore = 0;
|
||||||
|
normalizedZeroScore = 0;
|
||||||
|
|
||||||
|
this.resultType = resultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBlackScore() {
|
||||||
|
return blackScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNormalizedZeroScore() {
|
||||||
|
return normalizedZeroScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a representation for the game score as an integer. Lower numbers are better for white.
|
||||||
|
* The minimum value is 0 (Chinese scoring - white owns every intersection on 19x19 and has 9 stone komi).
|
||||||
|
* Likewise, the maximum value if 379x2 (black owns every intersection with zero komi).
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getNormalizedScore() {
|
||||||
|
return normalizedZeroScore + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getScore(Player color) {
|
||||||
|
if (color == Player.BLACK) {
|
||||||
|
return getBlackScore();
|
||||||
|
} else if (color == Player.WHITE) {
|
||||||
|
return getWhiteScore();
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWhiteScore() {
|
||||||
|
return (double)whiteScore + komi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWinner(Player player) {
|
||||||
|
if (Player.WHITE == player) {
|
||||||
|
return whiteScore + komi > blackScore;
|
||||||
|
} else {
|
||||||
|
return blackScore > whiteScore + komi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
switch (resultType) {
|
||||||
|
case BLACK_BY_RESIGNATION :
|
||||||
|
return "B+R";
|
||||||
|
case SCORED :
|
||||||
|
double blackScore = getBlackScore();
|
||||||
|
double whiteScore = getWhiteScore();
|
||||||
|
if (blackScore > whiteScore) {
|
||||||
|
return "B+" + (whiteScore - blackScore);
|
||||||
|
} else if (whiteScore > blackScore) {
|
||||||
|
return "W+" + (whiteScore - blackScore);
|
||||||
|
} else {
|
||||||
|
return "DRAW";
|
||||||
|
}
|
||||||
|
case WHITE_BY_RESIGNATION :
|
||||||
|
return "W+R";
|
||||||
|
case IN_PROGRESS :
|
||||||
|
return "Void";
|
||||||
|
default :
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package net.woodyfolsom.msproj;
|
|
||||||
|
|
||||||
|
|
||||||
public class GameScore {
|
|
||||||
private double komi;
|
|
||||||
private int blackScore;
|
|
||||||
private int whiteScore;
|
|
||||||
private int normalizedZeroScore;
|
|
||||||
|
|
||||||
public GameScore(int size) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameScore(int blackScore, int whiteScore, int size, double komi) {
|
|
||||||
this.blackScore = blackScore;
|
|
||||||
this.komi = komi;
|
|
||||||
this.whiteScore = whiteScore;
|
|
||||||
this.normalizedZeroScore = size * size + ((int)(komi * 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getBlackScore() {
|
|
||||||
return (double)blackScore - komi;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNormalizedZeroScore() {
|
|
||||||
return normalizedZeroScore;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Gets a representation for the game score as an integer. Lower numbers are better for white.
|
|
||||||
* The minimum value is 0 (Chinese scoring - white owns every intersection on 19x19 and has 9 stone komi).
|
|
||||||
* Likewise, the maximum value if 379x2 (black owns every intersection with zero komi).
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public int getAggregateScore() {
|
|
||||||
return normalizedZeroScore + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getScore(Player color) {
|
|
||||||
if (color == Player.BLACK) {
|
|
||||||
return getBlackScore();
|
|
||||||
} else if (color == Player.WHITE) {
|
|
||||||
return getWhiteScore();
|
|
||||||
} else {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getWhiteScore() {
|
|
||||||
return (double)whiteScore + komi;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWinner(Player player) {
|
|
||||||
if (Player.WHITE == player) {
|
|
||||||
return whiteScore + komi > blackScore;
|
|
||||||
} else {
|
|
||||||
return blackScore > whiteScore + komi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return "B: " + blackScore + "W: "+ whiteScore+"K:" + komi;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getScoreReport() {
|
|
||||||
double blackScore = getBlackScore();
|
|
||||||
double whiteScore = getWhiteScore();
|
|
||||||
|
|
||||||
boolean gameTied = Math.abs(blackScore - whiteScore) < 0.5;
|
|
||||||
|
|
||||||
if (gameTied) {
|
|
||||||
return "Black +0 (tie)";
|
|
||||||
} else if (blackScore > whiteScore) {
|
|
||||||
return "Black +"
|
|
||||||
+ (blackScore - whiteScore - komi);
|
|
||||||
} else {
|
|
||||||
return "White +"
|
|
||||||
+ (whiteScore + komi - blackScore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,6 +40,50 @@ public class GameState {
|
|||||||
public int getBlackPrisoners() {
|
public int getBlackPrisoners() {
|
||||||
return blackPrisoners;
|
return blackPrisoners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GameResult getResult() {
|
||||||
|
GameBoard markedBoard;
|
||||||
|
|
||||||
|
if (gameBoard.isTerritoryMarked()) {
|
||||||
|
markedBoard = gameBoard;
|
||||||
|
} else {
|
||||||
|
markedBoard = new GameBoard(gameBoard);
|
||||||
|
TerritoryMarker.markTerritory(markedBoard);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gameLength = moveHistory.size();
|
||||||
|
|
||||||
|
//This functionality duplicates isTerminal(),
|
||||||
|
//however for the result to be reported, we need to know
|
||||||
|
//how the game was terminated to differentiate between resignation, scored complete
|
||||||
|
//and (eventually) time out/forfeit.
|
||||||
|
boolean isFinished = false;
|
||||||
|
|
||||||
|
if (gameLength >= 1) {
|
||||||
|
if (moveHistory.get(gameLength-1).isResign()) {
|
||||||
|
if (gameLength % 2 == 1) {
|
||||||
|
return GameResult.WHITE_BY_RESIGNATION;
|
||||||
|
} else {
|
||||||
|
return GameResult.BLACK_BY_RESIGNATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gameLength >= 2) {
|
||||||
|
if (moveHistory.get(gameLength-1).isPass()
|
||||||
|
&& moveHistory.get(gameLength-2).isPass()) {
|
||||||
|
isFinished = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int blackScore = gameBoard.countSymbols(GameBoard.BLACK_STONE,GameBoard.BLACK_TERRITORY);
|
||||||
|
int whiteScore = gameBoard.countSymbols(GameBoard.WHITE_STONE,GameBoard.WHITE_TERRITORY);
|
||||||
|
|
||||||
|
return new GameResult( blackScore,
|
||||||
|
whiteScore,
|
||||||
|
markedBoard.getSize(),
|
||||||
|
gameConfig.getKomi(),
|
||||||
|
isFinished);
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getEmptyCoords() {
|
public List<String> getEmptyCoords() {
|
||||||
List<String> emptyCoords = new ArrayList<String>();
|
List<String> emptyCoords = new ArrayList<String>();
|
||||||
@@ -100,11 +144,16 @@ public class GameState {
|
|||||||
|
|
||||||
if (action.isPass()) {
|
if (action.isPass()) {
|
||||||
playerToMove = GoGame.getNextPlayer(player);
|
playerToMove = GoGame.getNextPlayer(player);
|
||||||
//TODO will need to record player as well
|
|
||||||
moveHistory.add(action);
|
moveHistory.add(action);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.isResign()) {
|
||||||
|
playerToMove = Player.NONE;
|
||||||
|
moveHistory.add(action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (action.isNone()) {
|
if (action.isNone()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -218,25 +267,6 @@ public class GameState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@Deprecated
|
|
||||||
private void assertCorrectHash() {
|
|
||||||
long hashFromHistory = gameBoard.getZobristHash();
|
|
||||||
|
|
||||||
int boardSize = gameBoard.getSize();
|
|
||||||
ZobristHashGenerator zhg = ZobristHashGenerator.getInstance(boardSize);
|
|
||||||
long recalculatedHash = zhg.getEmptyBoardHash();
|
|
||||||
|
|
||||||
for (int i = 0; i < boardSize * boardSize; i ++) {
|
|
||||||
recalculatedHash ^= zhg.getHashCode(i, GameBoard.EMPTY_INTERSECTION);
|
|
||||||
recalculatedHash ^= zhg.getHashCode(i, gameBoard.getSymbolAt(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hashFromHistory != recalculatedHash) {
|
|
||||||
throw new RuntimeException("Zobrist hash code mismatch");
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public boolean isTerminal() {
|
public boolean isTerminal() {
|
||||||
int nMoves = moveHistory.size();
|
int nMoves = moveHistory.size();
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class GoGame implements Runnable {
|
|||||||
break;
|
break;
|
||||||
case final_status_list:
|
case final_status_list:
|
||||||
System.out.println("Final game score: ");
|
System.out.println("Final game score: ");
|
||||||
System.out.println(new StateEvaluator(gameConfig).scoreGame(gameState));
|
System.out.println(gameState.getResult());
|
||||||
System.out.println();
|
System.out.println();
|
||||||
localOutput.println("=\n");
|
localOutput.println("=\n");
|
||||||
break;
|
break;
|
||||||
@@ -159,7 +159,7 @@ public class GoGame implements Runnable {
|
|||||||
player);
|
player);
|
||||||
|
|
||||||
gameState.playStone(player, nextMove);
|
gameState.playStone(player, nextMove);
|
||||||
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
|
LOGGER.info(gameState.getResult());
|
||||||
localOutput.println("="
|
localOutput.println("="
|
||||||
+ nextMove.toString() + "\n");
|
+ nextMove.toString() + "\n");
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ public class GoGame implements Runnable {
|
|||||||
LOGGER.info("GameState: " + gameState);
|
LOGGER.info("GameState: " + gameState);
|
||||||
localOutput.println("?" + errMsg + "\n");
|
localOutput.println("?" + errMsg + "\n");
|
||||||
}
|
}
|
||||||
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
|
LOGGER.info(gameState.getResult());
|
||||||
break;
|
break;
|
||||||
case version:
|
case version:
|
||||||
localOutput.println("= 0.1\n");
|
localOutput.println("= 0.1\n");
|
||||||
|
|||||||
@@ -1,5 +1,75 @@
|
|||||||
package net.woodyfolsom.msproj;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class Referee {
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
|
||||||
|
public class Referee {
|
||||||
|
private GameConfig gameConfig = new GameConfig(9);
|
||||||
|
private GameState gameState = new GameState(gameConfig);
|
||||||
|
private Policy blackPolicy;
|
||||||
|
private Policy whitePolicy;
|
||||||
|
|
||||||
|
public Policy getPolicy(Player player) {
|
||||||
|
if (Player.BLACK.equals(player)) {
|
||||||
|
return blackPolicy;
|
||||||
|
} else if (Player.WHITE.equals(player)) {
|
||||||
|
return whitePolicy;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Player must be one of {Player.WHITE, Player.BLACK}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPolicy(Player player, Policy policy) {
|
||||||
|
if (Player.BLACK.equals(player)) {
|
||||||
|
blackPolicy = policy;
|
||||||
|
} else if (Player.WHITE.equals(player)) {
|
||||||
|
whitePolicy = policy;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Player must be one of {Player.WHITE, Player.BLACK}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void play() {
|
||||||
|
System.out.println("Game started.");
|
||||||
|
|
||||||
|
Player currentPlayer = Player.BLACK;
|
||||||
|
|
||||||
|
while (!gameState.isTerminal()) {
|
||||||
|
System.out.println(gameState);
|
||||||
|
|
||||||
|
Action action = getPolicy(currentPlayer).getAction(gameConfig, gameState, currentPlayer);
|
||||||
|
gameState.playStone(currentPlayer, action);
|
||||||
|
currentPlayer = GoGame.getNextPlayer(currentPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Game over. Result: " + gameState.getResult());
|
||||||
|
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmssZ");
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
File sgfFile = new File("gogame-" + dateFormat.format(new Date())+ ".sgf");
|
||||||
|
FileOutputStream fos = new FileOutputStream(sgfFile);
|
||||||
|
try {
|
||||||
|
SGFWriter.writeSGF(gameState, fos);
|
||||||
|
System.out.println("Game saved as " + sgfFile.getAbsolutePath());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
fos.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(IOException ioe) {
|
||||||
|
System.out.println("Unable to save game file due to IOException: " + ioe.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Game finished.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/net/woodyfolsom/msproj/SGFWriter.java
Normal file
22
src/net/woodyfolsom/msproj/SGFWriter.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.sgf.SGFIdentifier;
|
||||||
|
|
||||||
|
public class SGFWriter {
|
||||||
|
|
||||||
|
public static void writeSGF(GameState gameState, OutputStream os)
|
||||||
|
throws IOException {
|
||||||
|
writeNode(os, SGFIdentifier.FILE_FORMAT, Integer.valueOf(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeNode(OutputStream os, SGFIdentifier sgfIdent,
|
||||||
|
Object nodeValue) throws IOException {
|
||||||
|
os.write(sgfIdent.toString().getBytes());
|
||||||
|
os.write("[".getBytes());
|
||||||
|
os.write(nodeValue.toString().getBytes());
|
||||||
|
os.write("];".getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/net/woodyfolsom/msproj/StandAloneGame.java
Normal file
24
src/net/woodyfolsom/msproj/StandAloneGame.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.policy.HumanKeyboardInput;
|
||||||
|
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
||||||
|
|
||||||
|
public class StandAloneGame {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Policy player1 = new HumanKeyboardInput();
|
||||||
|
Policy player2 = new MonteCarloUCT(new RandomMovePolicy(), 5000L);
|
||||||
|
|
||||||
|
Referee referee = new Referee();
|
||||||
|
referee.setPolicy(Player.BLACK, player1);
|
||||||
|
referee.setPolicy(Player.WHITE, player2);
|
||||||
|
|
||||||
|
referee.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package net.woodyfolsom.msproj;
|
|
||||||
|
|
||||||
public class StateEvaluator {
|
|
||||||
private final GameConfig gameConfig;
|
|
||||||
|
|
||||||
public StateEvaluator(GameConfig gameConfig) {
|
|
||||||
this.gameConfig = gameConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameConfig getGameConfig() {
|
|
||||||
return gameConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameScore scoreGame(GameState gameState) {
|
|
||||||
GameBoard gameBoard;
|
|
||||||
if (gameState.getGameBoard().isTerritoryMarked()) {
|
|
||||||
gameBoard = gameState.getGameBoard();
|
|
||||||
} else {
|
|
||||||
gameBoard = new GameBoard(gameState.getGameBoard());
|
|
||||||
TerritoryMarker.markTerritory(gameBoard);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO include komi from gameConfig
|
|
||||||
return new GameScore(gameBoard.countSymbols(GameBoard.BLACK_STONE,GameBoard.BLACK_TERRITORY),
|
|
||||||
gameBoard.countSymbols(GameBoard.WHITE_STONE,GameBoard.WHITE_TERRITORY),gameState.getGameBoard().getSize(),gameConfig.getKomi());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ import net.woodyfolsom.msproj.GameConfig;
|
|||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.GoGame;
|
import net.woodyfolsom.msproj.GoGame;
|
||||||
import net.woodyfolsom.msproj.Player;
|
import net.woodyfolsom.msproj.Player;
|
||||||
import net.woodyfolsom.msproj.StateEvaluator;
|
|
||||||
import net.woodyfolsom.msproj.tree.AlphaBetaProperties;
|
import net.woodyfolsom.msproj.tree.AlphaBetaProperties;
|
||||||
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
||||||
|
|
||||||
@@ -33,16 +32,14 @@ public class AlphaBeta implements Policy {
|
|||||||
Player player) {
|
Player player) {
|
||||||
|
|
||||||
numStateEvaluations = 0;
|
numStateEvaluations = 0;
|
||||||
|
|
||||||
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
|
|
||||||
|
|
||||||
GameTreeNode<AlphaBetaProperties> rootNode = new GameTreeNode<AlphaBetaProperties>(
|
GameTreeNode<AlphaBetaProperties> rootNode = new GameTreeNode<AlphaBetaProperties>(
|
||||||
gameState, new AlphaBetaProperties());
|
gameState, new AlphaBetaProperties());
|
||||||
|
|
||||||
if (player == Player.BLACK) {
|
if (player == Player.BLACK) {
|
||||||
return getMax(lookAhead * 2, stateEvaluator, rootNode, player);
|
return getMax(lookAhead * 2, rootNode, player);
|
||||||
} else {
|
} else {
|
||||||
return getMin(lookAhead * 2, stateEvaluator, rootNode, player);
|
return getMin(lookAhead * 2, rootNode, player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,13 +47,13 @@ public class AlphaBeta implements Policy {
|
|||||||
return recursionLevels == 0 || nValidMoves == 0;
|
return recursionLevels == 0 || nValidMoves == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action getMax(int recursionLevels, StateEvaluator stateEvaluator,
|
private Action getMax(int recursionLevels,
|
||||||
GameTreeNode<AlphaBetaProperties> node, Player player) {
|
GameTreeNode<AlphaBetaProperties> node, Player player) {
|
||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(
|
||||||
stateEvaluator.getGameConfig(), node.getGameState(), player,
|
node.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -66,7 +63,7 @@ public class AlphaBeta implements Policy {
|
|||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
stateEvaluator.scoreGame(gameState).getAggregateScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
@@ -85,7 +82,7 @@ public class AlphaBeta implements Policy {
|
|||||||
node.getProperties().getBeta());
|
node.getProperties().getBeta());
|
||||||
|
|
||||||
node.addChild(nextMove, childNode);
|
node.addChild(nextMove, childNode);
|
||||||
getMin(recursionLevels - 1, stateEvaluator, childNode,
|
getMin(recursionLevels - 1, childNode,
|
||||||
GoGame.getNextPlayer(player));
|
GoGame.getNextPlayer(player));
|
||||||
|
|
||||||
double gameScore = childNode.getProperties().getReward();
|
double gameScore = childNode.getProperties().getReward();
|
||||||
@@ -109,13 +106,13 @@ public class AlphaBeta implements Policy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action getMin(int recursionLevels, StateEvaluator stateEvaluator,
|
private Action getMin(int recursionLevels,
|
||||||
GameTreeNode<AlphaBetaProperties> node, Player player) {
|
GameTreeNode<AlphaBetaProperties> node, Player player) {
|
||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(
|
||||||
stateEvaluator.getGameConfig(), node.getGameState(), player,
|
node.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -125,7 +122,7 @@ public class AlphaBeta implements Policy {
|
|||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
stateEvaluator.scoreGame(gameState).getAggregateScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
@@ -144,7 +141,7 @@ public class AlphaBeta implements Policy {
|
|||||||
node.getProperties().getBeta());
|
node.getProperties().getBeta());
|
||||||
|
|
||||||
node.addChild(nextMove, childNode);
|
node.addChild(nextMove, childNode);
|
||||||
getMax(recursionLevels - 1, stateEvaluator, childNode,
|
getMax(recursionLevels - 1, childNode,
|
||||||
GoGame.getNextPlayer(player));
|
GoGame.getNextPlayer(player));
|
||||||
|
|
||||||
double gameScore = childNode.getProperties().getReward();
|
double gameScore = childNode.getProperties().getReward();
|
||||||
|
|||||||
67
src/net/woodyfolsom/msproj/policy/HumanKeyboardInput.java
Normal file
67
src/net/woodyfolsom/msproj/policy/HumanKeyboardInput.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.Action;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.Player;
|
||||||
|
|
||||||
|
public class HumanKeyboardInput implements Policy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
Player player) {
|
||||||
|
byte[] inputBytes = new byte[4];
|
||||||
|
Action action = null;
|
||||||
|
String input = "";
|
||||||
|
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
System.out.println(player + " to move: (enter coord or PASS/RESIGN)");
|
||||||
|
System.in.read(inputBytes);
|
||||||
|
input = new String(inputBytes).trim().toUpperCase();
|
||||||
|
|
||||||
|
action = Action.getInstance(input);
|
||||||
|
|
||||||
|
if (action.isNone()) {
|
||||||
|
System.out.println("'" + input +"' is not a valid move. Please enter a coordinate (A1-T19) or 'PASS'.");
|
||||||
|
System.out.println(gameState);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
System.out.println("IOException: " + ioe.getMessage());
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
System.out.println("IllegalArgumentException - '"+input+"' is not a valid action or coordinate.");
|
||||||
|
}
|
||||||
|
} while (action == null);
|
||||||
|
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
Collection<Action> prohibitedActions, Player player) {
|
||||||
|
Action action = getAction(gameConfig,gameState,player);
|
||||||
|
|
||||||
|
if (!prohibitedActions.contains(action)) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
System.out.println("Sorry, that action is prohibited. Please make another move.");
|
||||||
|
action = getAction(gameConfig,gameState,player);
|
||||||
|
} while (prohibitedActions.contains(action));
|
||||||
|
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumStateEvaluations() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import net.woodyfolsom.msproj.GameConfig;
|
|||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.GoGame;
|
import net.woodyfolsom.msproj.GoGame;
|
||||||
import net.woodyfolsom.msproj.Player;
|
import net.woodyfolsom.msproj.Player;
|
||||||
import net.woodyfolsom.msproj.StateEvaluator;
|
|
||||||
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
||||||
import net.woodyfolsom.msproj.tree.MinimaxProperties;
|
import net.woodyfolsom.msproj.tree.MinimaxProperties;
|
||||||
|
|
||||||
@@ -32,16 +31,14 @@ public class Minimax implements Policy {
|
|||||||
public Action getAction(GameConfig gameConfig, GameState gameState,
|
public Action getAction(GameConfig gameConfig, GameState gameState,
|
||||||
Player player) {
|
Player player) {
|
||||||
numStateEvaluations = 0;
|
numStateEvaluations = 0;
|
||||||
|
|
||||||
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
|
|
||||||
|
|
||||||
GameTreeNode<MinimaxProperties> rootNode = new GameTreeNode<MinimaxProperties>(
|
GameTreeNode<MinimaxProperties> rootNode = new GameTreeNode<MinimaxProperties>(
|
||||||
gameState, new MinimaxProperties());
|
gameState, new MinimaxProperties());
|
||||||
|
|
||||||
if (player == Player.BLACK) {
|
if (player == Player.BLACK) {
|
||||||
return getMax(lookAhead * 2, stateEvaluator, rootNode, player);
|
return getMax(lookAhead * 2, rootNode, player);
|
||||||
} else {
|
} else {
|
||||||
return getMin(lookAhead * 2, stateEvaluator, rootNode, player);
|
return getMin(lookAhead * 2, rootNode, player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +46,13 @@ public class Minimax implements Policy {
|
|||||||
return recursionLevels == 0 || nValidMoves == 0;
|
return recursionLevels == 0 || nValidMoves == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action getMax(int recursionLevels, StateEvaluator stateEvaluator,
|
private Action getMax(int recursionLevels,
|
||||||
GameTreeNode<MinimaxProperties> node, Player player) {
|
GameTreeNode<MinimaxProperties> node, Player player) {
|
||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(
|
||||||
stateEvaluator.getGameConfig(), node.getGameState(), player,
|
node.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -65,7 +62,7 @@ public class Minimax implements Policy {
|
|||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
stateEvaluator.scoreGame(gameState).getAggregateScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
@@ -79,7 +76,7 @@ public class Minimax implements Policy {
|
|||||||
nextState, new MinimaxProperties());
|
nextState, new MinimaxProperties());
|
||||||
node.addChild(nextMove, childNode);
|
node.addChild(nextMove, childNode);
|
||||||
|
|
||||||
getMin(recursionLevels - 1, stateEvaluator, childNode,
|
getMin(recursionLevels - 1, childNode,
|
||||||
GoGame.getNextPlayer(player));
|
GoGame.getNextPlayer(player));
|
||||||
|
|
||||||
double gameScore = childNode.getProperties().getReward();
|
double gameScore = childNode.getProperties().getReward();
|
||||||
@@ -95,13 +92,13 @@ public class Minimax implements Policy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action getMin(int recursionLevels, StateEvaluator stateEvaluator,
|
private Action getMin(int recursionLevels,
|
||||||
GameTreeNode<MinimaxProperties> node, Player player) {
|
GameTreeNode<MinimaxProperties> node, Player player) {
|
||||||
|
|
||||||
GameState gameState = new GameState(node.getGameState());
|
GameState gameState = new GameState(node.getGameState());
|
||||||
|
|
||||||
List<Action> validMoves = validMoveGenerator.getActions(
|
List<Action> validMoves = validMoveGenerator.getActions(
|
||||||
stateEvaluator.getGameConfig(), node.getGameState(), player,
|
node.getGameState().getGameConfig(), node.getGameState(), player,
|
||||||
ActionGenerator.ALL_ACTIONS);
|
ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
boolean terminal = isTerminal(validMoves.size(), recursionLevels);
|
||||||
@@ -111,7 +108,7 @@ public class Minimax implements Policy {
|
|||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
node.getProperties().setReward(
|
node.getProperties().setReward(
|
||||||
stateEvaluator.scoreGame(gameState).getAggregateScore());
|
gameState.getResult().getNormalizedScore());
|
||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
@@ -125,7 +122,7 @@ public class Minimax implements Policy {
|
|||||||
nextState, new MinimaxProperties());
|
nextState, new MinimaxProperties());
|
||||||
node.addChild(nextMove, childNode);
|
node.addChild(nextMove, childNode);
|
||||||
|
|
||||||
getMax(recursionLevels - 1, stateEvaluator, childNode,
|
getMax(recursionLevels - 1, childNode,
|
||||||
GoGame.getNextPlayer(player));
|
GoGame.getNextPlayer(player));
|
||||||
|
|
||||||
double gameScore = childNode.getProperties().getReward();
|
double gameScore = childNode.getProperties().getReward();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import net.woodyfolsom.msproj.Action;
|
|||||||
import net.woodyfolsom.msproj.GameConfig;
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.Player;
|
import net.woodyfolsom.msproj.Player;
|
||||||
import net.woodyfolsom.msproj.StateEvaluator;
|
|
||||||
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
||||||
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ public abstract class MonteCarlo implements Policy {
|
|||||||
//result in a win.
|
//result in a win.
|
||||||
|
|
||||||
GameTreeNode<MonteCarloProperties> rootNode = new GameTreeNode<MonteCarloProperties>(gameState, new MonteCarloProperties());
|
GameTreeNode<MonteCarloProperties> rootNode = new GameTreeNode<MonteCarloProperties>(gameState, new MonteCarloProperties());
|
||||||
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
|
|
||||||
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
|
||||||
@@ -62,7 +61,7 @@ public abstract class MonteCarlo implements Policy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (GameTreeNode<MonteCarloProperties> newLeaf : newLeaves) {
|
for (GameTreeNode<MonteCarloProperties> newLeaf : newLeaves) {
|
||||||
int reward = rollout(gameConfig, stateEvaluator, newLeaf, player);
|
int reward = rollout(gameConfig, newLeaf, player);
|
||||||
update(newLeaf, reward);
|
update(newLeaf, reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +81,7 @@ public abstract class MonteCarlo implements Policy {
|
|||||||
|
|
||||||
public abstract List<GameTreeNode<MonteCarloProperties>> grow(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
|
public abstract List<GameTreeNode<MonteCarloProperties>> grow(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
|
||||||
|
|
||||||
public abstract int rollout(GameConfig gameConfig, StateEvaluator stateEvaluator, GameTreeNode<MonteCarloProperties> node, Player player);
|
public abstract int rollout(GameConfig gameConfig, GameTreeNode<MonteCarloProperties> node, Player player);
|
||||||
|
|
||||||
public abstract void update(GameTreeNode<MonteCarloProperties> node, int reward);
|
public abstract void update(GameTreeNode<MonteCarloProperties> node, int reward);
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import java.util.Set;
|
|||||||
|
|
||||||
import net.woodyfolsom.msproj.Action;
|
import net.woodyfolsom.msproj.Action;
|
||||||
import net.woodyfolsom.msproj.GameConfig;
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
import net.woodyfolsom.msproj.GameScore;
|
import net.woodyfolsom.msproj.GameResult;
|
||||||
import net.woodyfolsom.msproj.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
import net.woodyfolsom.msproj.GoGame;
|
import net.woodyfolsom.msproj.GoGame;
|
||||||
import net.woodyfolsom.msproj.Player;
|
import net.woodyfolsom.msproj.Player;
|
||||||
import net.woodyfolsom.msproj.StateEvaluator;
|
|
||||||
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
import net.woodyfolsom.msproj.tree.GameTreeNode;
|
||||||
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
import net.woodyfolsom.msproj.tree.MonteCarloProperties;
|
||||||
|
|
||||||
@@ -119,7 +118,7 @@ public class MonteCarloUCT extends MonteCarlo {
|
|||||||
* Rollout currently depends on the hardcoded ROLLOUT_DEPTH_LIMIT superclass parameter,
|
* 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, StateEvaluator stateEvaluator, 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;
|
||||||
@@ -139,18 +138,11 @@ public class MonteCarloUCT extends MonteCarlo {
|
|||||||
|
|
||||||
numStateEvaluations++;
|
numStateEvaluations++;
|
||||||
|
|
||||||
GameScore gameScore = stateEvaluator.scoreGame(rolloutGameState);
|
GameResult gameScore = rolloutGameState.getResult();
|
||||||
|
|
||||||
if (gameScore.isWinner(player)) {
|
if (gameScore.isWinner(player)) {
|
||||||
//System.out.println();
|
|
||||||
//System.out.println("Win for " + player + ":\n" + rolloutGameState);
|
|
||||||
//System.out.println(gameScore.getScoreReport());
|
|
||||||
//System.out.println();
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
//System.out.println();
|
|
||||||
//System.out.println("Loss for " + player + ":\n" + rolloutGameState);
|
|
||||||
//System.out.println(gameScore.getScoreReport());
|
|
||||||
//System.out.println();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package net.woodyfolsom.msproj.sgf;
|
|
||||||
|
|
||||||
public class SGFReader {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -54,8 +54,7 @@ public class CaptureTest {
|
|||||||
|
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
|
|
||||||
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState);
|
System.out.println(gameState.getResult());
|
||||||
System.out.println(gameScore.getScoreReport());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.woodyfolsom.msproj;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import net.woodyfolsom.msproj.GameScore;
|
import net.woodyfolsom.msproj.GameResult;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -10,19 +10,19 @@ public class GameScoreTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAggregateScoreZero() {
|
public void testGetAggregateScoreZero() {
|
||||||
GameScore gameScore = new GameScore(0,0,19,0);
|
GameResult gameScore = new GameResult(0,0,19,0, true);
|
||||||
assertEquals(gameScore.getNormalizedZeroScore(), gameScore.getAggregateScore());
|
assertEquals(gameScore.getNormalizedZeroScore(), gameScore.getNormalizedScore());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAggregateScoreBlackWinsNoKomi() {
|
public void testGetAggregateScoreBlackWinsNoKomi() {
|
||||||
GameScore gameScore = new GameScore(25,2,19,0);
|
GameResult gameScore = new GameResult(25,2,19,0, true);
|
||||||
assertEquals(425, gameScore.getAggregateScore());
|
assertEquals(407, gameScore.getNormalizedScore());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAggregateScoreWhiteWinsWithKomi() {
|
public void testGetAggregateScoreWhiteWinsWithKomi() {
|
||||||
GameScore gameScore = new GameScore(10,12,19,6.5);
|
GameResult gameScore = new GameResult(10,12,19,6.5, true);
|
||||||
assertEquals(362, gameScore.getAggregateScore());
|
assertEquals(357, gameScore.getNormalizedScore());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package net.woodyfolsom.msproj;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class StateEvaluatorTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testScoreEmptyBoard() {
|
|
||||||
GameConfig gameConfig = new GameConfig(5);
|
|
||||||
GameState gameState = new GameState(gameConfig);
|
|
||||||
GameScore gameScore = new StateEvaluator(gameConfig)
|
|
||||||
.scoreGame(gameState);
|
|
||||||
|
|
||||||
assertEquals(0.0, gameScore.getWhiteScore(), 0.5);
|
|
||||||
assertEquals(0.0, gameScore.getBlackScore(), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testScoreFirstMove() {
|
|
||||||
GameConfig gameConfig = new GameConfig(5);
|
|
||||||
GameState gameState = new GameState(gameConfig);
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B3"));
|
|
||||||
|
|
||||||
GameScore gameScore = new StateEvaluator(gameConfig)
|
|
||||||
.scoreGame(gameState);
|
|
||||||
|
|
||||||
System.out.println(gameScore.getScoreReport());
|
|
||||||
|
|
||||||
assertEquals(0.0, gameScore.getWhiteScore(), 0.5);
|
|
||||||
assertEquals(25.0, gameScore.getBlackScore(), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testScoreTiedAtMove2() {
|
|
||||||
GameConfig gameConfig = new GameConfig(5);
|
|
||||||
GameState gameState = new GameState(gameConfig);
|
|
||||||
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B3"));
|
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("A1"));
|
|
||||||
GameScore gameScore = new StateEvaluator(gameConfig)
|
|
||||||
.scoreGame(gameState);
|
|
||||||
|
|
||||||
System.out.println(gameScore.getScoreReport());
|
|
||||||
|
|
||||||
assertEquals(1.0, gameScore.getWhiteScore(), 0.5);
|
|
||||||
assertEquals(1.0, gameScore.getBlackScore(), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testScoreTerritory() {
|
|
||||||
GameConfig gameConfig = new GameConfig(5);
|
|
||||||
GameState gameState = new GameState(gameConfig);
|
|
||||||
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("A2"));
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B3"));
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("C2"));
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B1"));
|
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("E5"));
|
|
||||||
|
|
||||||
System.out.println(gameState);
|
|
||||||
GameScore gameScore = new StateEvaluator(gameConfig)
|
|
||||||
.scoreGame(gameState);
|
|
||||||
|
|
||||||
System.out.println(gameScore.getScoreReport());
|
|
||||||
|
|
||||||
assertEquals(1.0, gameScore.getWhiteScore(), 0.5);
|
|
||||||
assertEquals(6.0, gameScore.getBlackScore(), 0.5);
|
|
||||||
// Black should be up by 5 if Black's territory at A1 & B2 is scored
|
|
||||||
// correctly.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCaptureAggScore() {
|
|
||||||
GameConfig gameConfig = new GameConfig(9);
|
|
||||||
GameState gameState = new GameState(gameConfig);
|
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("A2"));
|
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("B1"));
|
|
||||||
gameState.playStone(Player.WHITE, Action.getInstance("C2"));
|
|
||||||
gameState.playStone(Player.BLACK, Action.getInstance("B2"));
|
|
||||||
|
|
||||||
GameState moveToA1 = new GameState(gameState);
|
|
||||||
GameState capAtB3 = new GameState(gameState);
|
|
||||||
|
|
||||||
moveToA1.playStone(Player.WHITE, Action.getInstance("A1"));
|
|
||||||
capAtB3.playStone(Player.WHITE, Action.getInstance("B3"));
|
|
||||||
|
|
||||||
System.out.println(moveToA1);
|
|
||||||
System.out.println(capAtB3);
|
|
||||||
|
|
||||||
StateEvaluator eval = new StateEvaluator(gameConfig);
|
|
||||||
int scoreA1 = eval.scoreGame(moveToA1).getAggregateScore();
|
|
||||||
int scoreB3 = eval.scoreGame(capAtB3).getAggregateScore();
|
|
||||||
|
|
||||||
System.out.println("Score at A1: " + scoreA1);
|
|
||||||
System.out.println("Score at B3: " + scoreB3);
|
|
||||||
// moving as white, lower is better
|
|
||||||
assertTrue(scoreA1 > scoreB3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user