Initial commit.

This commit is contained in:
cs6601
2012-08-26 11:48:21 -04:00
commit 36291171e5
41 changed files with 2107 additions and 0 deletions

9
.classpath Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="lib" path="lib/junit-4.10.jar"/>
<classpathentry kind="lib" path="lib/log4j-1.2.16.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
dist
bin
build

17
.project Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>CS6601_P1</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,12 @@
#Wed Feb 08 12:12:07 EST 2012
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7

81
build.xml Normal file
View File

@@ -0,0 +1,81 @@
<project name="GoGame" default="dist" basedir=".">
<description>Simple Framework for Testing Tree Search and Monte-Carlo Go</description>
<property name="src" location="src" />
<property name="build" location="build" />
<property name="dist" location="dist" />
<property name="test" location="test" />
<property name="docs" location="docs" />
<property name="lib" location="lib" />
<path id="build.classpath">
<fileset dir="${lib}">
<include name="**/*.jar" />
</fileset>
</path>
<path id="classpath.test">
<path refid="build.classpath" />
<pathelement location="lib/junit-4.10.jar" />
<pathelement location="${build}" />
</path>
<target name="compile" depends="init" description="compile the source ">
<!-- Compile the java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}" classpathref="build.classpath" debug="true" source="1.6" target="1.6"/>
</target>
<target name="compile-test" depends="compile">
<javac srcdir="${test}" destdir="${build}" debug="true">
<classpath refid="classpath.test" />
</javac>
</target>
<target name="copy-resources">
<copy todir="${dist}">
<fileset dir="data" />
</copy>
</target>
<target name="clean" description="clean up">
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}" />
<delete dir="${dist}" />
</target>
<target name="dist" depends="compile,copy-resources" description="generate the distribution">
<jar jarfile="${dist}/GoGame.jar">
<fileset dir="${build}" excludes="**/*Test.class" />
<manifest>
<attribute name="Main-Class" value="cs6601.p1.GoGame" />
</manifest>
</jar>
</target>
<!-- Creates Javadoc -->
<target name="docs" depends="compile">
<javadoc packagenames="src" sourcepath="${src}" destdir="${docs}">
<!-- Define which files / directory should get included, we include all -->
<fileset dir="${src}">
<include name="**" />
</fileset>
</javadoc>
</target>
<target name="init">
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}" />
</target>
<target name="test" depends="compile-test">
<junit haltonfailure="true">
<classpath refid="classpath.test" />
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${build}" includes="**/*Test.class" />
</batchtest>
</junit>
</target>
</project>

1
data/gogame.bat Normal file
View File

@@ -0,0 +1 @@
java -jar kgsGtp.jar kgsGtp.ini

13
data/kgsGtp.ini Normal file
View File

@@ -0,0 +1,13 @@
engine=java -cp GoGame.jar;log4j-1.2.16.jar cs6601.p1.GoGame alphabeta
name=cs6601p1
password=mwz6fe
room=cs6601p1
mode=custom
talk=I'm a simple Computer Go program that plays random moves.
opponent=cs6601p2
reconnect=t
automatch.rank=20k
rules=chinese
rules.boardSize=9
rules.time=0
rules.komi=5.5

BIN
data/kgsGtp.jar Normal file

Binary file not shown.

BIN
data/log4j-1.2.16.jar Normal file

Binary file not shown.

19
data/log4j.xml Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
<appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
<param name="Threshold" value="INFO" />
<param name="File" value="GoGame.log"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%c{1}] %m %n" />
</layout>
</appender>
<logger name="cs6601.p1" additivity="false" >
<level value="INFO" />
<appender-ref ref="fileAppender"/>
</logger>
</log4j:configuration>

BIN
lib/junit-4.10.jar Normal file

Binary file not shown.

BIN
lib/log4j-1.2.16.jar Normal file

Binary file not shown.

View File

@@ -0,0 +1,39 @@
package cs6601.p1;
public class Command {
public enum TYPE { boardsize, clear_board, final_status_list, genmove, INVALID, komi, list_commands, name, play, quit, version };
private String[] cmdFields;
private String text;
private TYPE type;
public Command(TYPE type, String text, String[] cmdFields) {
this.type = type;
this.text = text;
this.cmdFields = cmdFields;
}
public double getDoubleField(int index) {
return Double.valueOf(cmdFields[index]);
}
public int getIntField(int index) {
return Integer.valueOf(cmdFields[index]);
}
public String getStringField(int index) {
return cmdFields[index];
}
public String getText() {
return text;
}
public TYPE getType() {
return type;
}
public String toString() {
return "[" + type.toString() + "] " + text;
}
}

View File

@@ -0,0 +1,54 @@
package cs6601.p1;
import org.apache.log4j.Logger;
public class CommandParser {
private static final Logger LOGGER = Logger.getLogger(CommandParser.class
.getName());
//private static final String INT_PATTERN = "^(\\d+.*|-\\d+.*)";
/**
* This very simple parser converts a small subset of GTP command strings
* into Commands.
*
* @param commandString
* @return
*/
public static Command parse(String commandString) {
LOGGER.info("Parsing command: " + commandString);
//split on whitespace
if (commandString == null) {
return new Command(Command.TYPE.INVALID,commandString,new String[0]);
}
String cmdFields[] = commandString.split("\\s+");
if (cmdFields.length == 0) {
return new Command(Command.TYPE.INVALID,commandString,new String[0]);
}
if ("boardsize".equals(cmdFields[0])) {
return new Command(Command.TYPE.boardsize,commandString,cmdFields);
} else if ("clear_board".equals(cmdFields[0])) {
return new Command(Command.TYPE.clear_board,commandString,cmdFields);
} else if ("final_status_list".equals(cmdFields[0])) {
return new Command(Command.TYPE.final_status_list,commandString,cmdFields);
} else if ("genmove".equals(cmdFields[0])) {
return new Command(Command.TYPE.genmove,commandString,cmdFields);
} else if ("komi".equals(cmdFields[0])) {
return new Command(Command.TYPE.komi,commandString,cmdFields);
} else if ("list_commands".equals(cmdFields[0])) {
return new Command(Command.TYPE.list_commands,commandString,cmdFields);
} else if ("name".equals(cmdFields[0])) {
return new Command(Command.TYPE.name,commandString,cmdFields);
} else if ("play".equals(cmdFields[0])) {
return new Command(Command.TYPE.play,commandString,cmdFields);
} else if ("quit".equals(cmdFields[0])) {
return new Command(Command.TYPE.quit,commandString,cmdFields);
} else if ("version".equals(cmdFields[0])) {
return new Command(Command.TYPE.version,commandString,cmdFields);
} else {
return new Command(Command.TYPE.INVALID,commandString,cmdFields);
}
}
}

View File

@@ -0,0 +1,210 @@
package cs6601.p1;
import java.util.Arrays;
public class GameBoard {
public static final char BLACK_STONE = 'X';
public static final char BLACK_TERRITORY = 'x';
public static final char EMPTY_INTERSECTION = '.';
public static final char MARKED_GROUP = 'g';
public static final char MARKED_TERRITORY = '?';
public static final char UNOWNED_TERRITORY = '-';
public static final char WHITE_STONE = 'O';
public static final char WHITE_TERRITORY = 'o';
private boolean territoryMarked = false;
private int size;
private char[] board;
public GameBoard(int size) {
this.size = size;
board = new char[size * size];
Arrays.fill(board, '.');
}
public GameBoard(GameBoard that) {
this.size = that.size;
this.board = Arrays.copyOf(that.board, that.board.length);
}
public void clear() {
territoryMarked = false;
Arrays.fill(board, EMPTY_INTERSECTION);
}
public int countSymbols(char... symbols) {
int stoneCount = 0;
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < symbols.length; j++) {
if (board[i] == symbols[j]) {
stoneCount++;
}
}
}
return stoneCount;
}
@Override
//TODO: implement as Zobrist hash.
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(board);
result = prime * result + size;
result = prime * result + (territoryMarked ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GameBoard other = (GameBoard) obj;
if (!Arrays.equals(board, other.board))
return false;
if (size != other.size)
return false;
if (territoryMarked != other.territoryMarked)
return false;
return true;
}
public static int getColumnIndex(char columnLabel) {
if (columnLabel < 'I') {
return columnLabel - 'A';
} else if (columnLabel > 'I') {
return columnLabel - 'A' - 1;
} else {
throw new IllegalArgumentException("Invalid column label: "
+ columnLabel);
}
}
public static char getColumnLabel(int columnIndex) {
if (columnIndex > 7) {
return (char) ('A' + columnIndex + 1);
} else {
return (char) ('A' + columnIndex);
}
}
public static String getCoordinate(int colIndex, int rowIndex) {
return String.valueOf(getColumnLabel(colIndex)) + (rowIndex + 1);
}
public static char getOpponentSymbol(char stoneSymbol) {
if (stoneSymbol == GameBoard.BLACK_STONE) {
return GameBoard.WHITE_STONE;
} else if (stoneSymbol == GameBoard.WHITE_STONE) {
return GameBoard.BLACK_STONE;
} else {
throw new IllegalArgumentException("StoneSymbol must be BLACK_STONE or WHITE_STONE");
}
}
public int getSize() {
return size;
}
/**
* @param colLabel [A..T] (skipping I
* @param rowNumber 1-based
* @return
*/
public char getSymbolAt(char colLabel, int rowNumber) {
return getSymbolAt(getColumnIndex(colLabel), rowNumber - 1);
}
/**
* 0-based.
* @param col
* @param row
* @return
*/
public char getSymbolAt(int col, int row) {
try {
return board[(size - row - 1) * size + col];
} catch (ArrayIndexOutOfBoundsException ae) {
ae.printStackTrace();
throw ae;
}
}
public boolean isEmpty(char col, int row) {
return getSymbolAt(col, row) == EMPTY_INTERSECTION;
}
public boolean isTerritoryMarked() {
return territoryMarked;
}
public boolean isStarPoint(char col, int row) {
switch (size) {
case 3:
if (col == 'B' && row == 2) {
return true;
} else {
return false;
}
case 4:
return false;
default:
return false;
}
}
public boolean markTerritory(int col, int row, char mark) {
char symbol = getSymbolAt(col, row);
if (symbol != '.') {
return false;
}
setSymbolAt(col,row,mark);
return true;
}
public boolean removeStone(char colLabel, int rowNum) {
if (getSymbolAt(colLabel,rowNum) == EMPTY_INTERSECTION) {
return false;
}
setSymbolAt(colLabel,rowNum,EMPTY_INTERSECTION);
return true;
}
public int replaceSymbol(char symbol, char replacement) {
int numReplaced = 0;
for (int i = 0; i < board.length; i++) {
if (board[i] == symbol) {
board[i] = replacement;
numReplaced++;
}
}
return numReplaced;
}
public void setSymbolAt(char colLabel, int rowNumber, char symbol) {
setSymbolAt(getColumnIndex(colLabel),rowNumber-1,symbol);
}
public void setSymbolAt(int col, int row, char symbol) {
board[(size - row - 1) * size + col] = symbol;
}
public void setTerritoryMarked(boolean territoryMarked) {
this.territoryMarked = territoryMarked;
}
public void unmarkTerritory() {
if (territoryMarked == false) {
return;
}
replaceSymbol(BLACK_TERRITORY,EMPTY_INTERSECTION);
replaceSymbol(WHITE_TERRITORY,EMPTY_INTERSECTION);
replaceSymbol(UNOWNED_TERRITORY,EMPTY_INTERSECTION);
territoryMarked = false;
}
}

View File

@@ -0,0 +1,27 @@
package cs6601.p1;
public class GameConfig {
private double komi;
private int timeLimit;
public GameConfig() {
timeLimit = 0;
komi = 0;
}
public double getKomi() {
return komi;
}
public int getTimeLimit(){
return timeLimit;
}
public void setKomi(double komi) {
this.komi = komi;
}
public void setTimeLimit(int timeLimit) {
this.timeLimit = timeLimit;
}
}

View File

@@ -0,0 +1,5 @@
package cs6601.p1;
public class GameController {
}

View File

@@ -0,0 +1,65 @@
package cs6601.p1;
public class GameScore {
public static final int NORMALIZED_ZERO_SCORE = 379;
private double komi;
private int blackScore;
private int whiteScore;
public GameScore(int blackScore, int whiteScore, double komi) {
this.blackScore = blackScore;
this.komi = komi;
this.whiteScore = whiteScore;
}
public double getBlackScore() {
return (double)blackScore - komi;
}
/**
* 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 NORMALIZED_ZERO_SCORE + 2 * blackScore - ((int)(2 * (whiteScore + komi)));
}
public double getScore(String color) {
if ("w".equals(color)) {
return getWhiteScore();
} else if ("b".equals(color)) {
return getBlackScore();
} else {
return 0.0;
}
}
public double getWhiteScore() {
return (double)whiteScore + komi;
}
public String toString() {
return "B: " + blackScore + "W: "+ whiteScore+"K:" + komi;
}
public String getScoreReport() {
double blackScore = getBlackScore();
double whiteScore = getWhiteScore();
boolean gameTied = Math.abs(blackScore - whiteScore) < 0.5;
if (gameTied) {
return "Black +0 (tie)";
} else if (blackScore > whiteScore) {
return "Black +"
+ (blackScore - whiteScore - komi);
} else {
return "White +"
+ (whiteScore + komi - blackScore);
}
}
}

View File

@@ -0,0 +1,207 @@
package cs6601.p1;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import cs6601.p1.generator.MoveGenerator;
public class GameState {
private static final Logger LOGGER = Logger.getLogger(GameState.class.getName());
private int blackPrisoners = 0;
private int whitePrisoners = 0;
private GameBoard gameBoard;
public GameState(int size) {
if (size < 1 || size > 19) {
throw new IllegalArgumentException("Invalid board size: " + size);
}
gameBoard = new GameBoard(size);
LOGGER.info("Created new GameBoard of size " + size);
}
public GameState(GameState that) {
this.blackPrisoners = that.blackPrisoners;
this.whitePrisoners = that.whitePrisoners;
gameBoard = new GameBoard(that.gameBoard);
}
public void clearBoard() {
blackPrisoners = 0;
whitePrisoners = 0;
gameBoard.clear();
}
public int getBlackPrisoners() {
return blackPrisoners;
}
public List<String> getEmptyCoords() {
List<String> emptyCoords = new ArrayList<String>();
for (int colIndex = 0; colIndex < gameBoard.getSize(); colIndex++) {
for (int rowIndex = 0; rowIndex < gameBoard.getSize(); rowIndex++) {
if (GameBoard.EMPTY_INTERSECTION == gameBoard.getSymbolAt(colIndex, rowIndex));
emptyCoords.add(GameBoard.getCoordinate(colIndex,rowIndex));
}
}
return emptyCoords;
}
public GameBoard getGameBoard() {
return gameBoard;
}
public int getWhitePrisoners() {
return whitePrisoners;
}
public boolean playStone(String player, String coord) {
//Opponent passes? Just ignore it.
if (MoveGenerator.PASS.equalsIgnoreCase(coord)) {
return true;
}
//LOGGER.info("Playing " + player + " at " + coord);
char stoneColor;
if ("b".equals(player)) {
stoneColor = GameBoard.BLACK_STONE;
} else if ("w".equals(player)) {
stoneColor = GameBoard.WHITE_STONE;
} else {
return false;
}
char col = coord.charAt(0);
int row = Integer.valueOf(coord.substring(1));
//LOGGER.info("Playing " + stoneColor + " at " + col + row);
return playStone(col,row, stoneColor);
}
/**
* Places a stone at the requested coordinate. Placement is legal if the
* coordinate is currently empty, has at least one liberty (empty neighbor),
* or at least one neighbor has this (recursive) property, or results in the
* capture and removal of at least one stone to produce a legal placement.
* Note also that placement is illegal if it would result in the capture of
* one's own group. Hence, placement must result in the capture of an
* adjacent stone or stones, if any.
*
* @param colLabel
* @param row
* @param stone
* @return
*/
public boolean playStone(char colLabel, int rowNum, char stoneSymbol) {
char currentStone = gameBoard.getSymbolAt(colLabel, rowNum);
if (currentStone != GameBoard.EMPTY_INTERSECTION) {
return false;
}
//Place stone as requested, then check for (1) captured neighbors and (2) illegal move due to 0 liberties.
gameBoard.setSymbolAt(colLabel, rowNum, stoneSymbol);
//look for captured adjacent groups and increment the prisoner counter
char opponentSymbol = GameBoard.getOpponentSymbol(stoneSymbol);
int col = GameBoard.getColumnIndex(colLabel);
int row = rowNum - 1;
int prisonerCount = 0;
if (col > 0 && gameBoard.getSymbolAt(col-1, row) == opponentSymbol) {
int liberties = LibertyCounter.countLiberties(gameBoard, col-1, row, opponentSymbol,true);
if (liberties == 0) {
prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
} else {
gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
}
}
if (col < gameBoard.getSize() - 1 && gameBoard.getSymbolAt(col+1, row) == opponentSymbol) {
int liberties = LibertyCounter.countLiberties(gameBoard, col+1, row, opponentSymbol,true);
if (liberties == 0) {
prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
} else {
gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
}
}
if (row > 0 && gameBoard.getSymbolAt(col, row-1) == opponentSymbol) {
int liberties = LibertyCounter.countLiberties(gameBoard, col, row-1, opponentSymbol,true);
if (liberties == 0) {
prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
} else {
gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
}
}
if (row < gameBoard.getSize() - 1 && gameBoard.getSymbolAt(col, row+1) == opponentSymbol) {
int liberties = LibertyCounter.countLiberties(gameBoard, col, row+1, opponentSymbol,true);
if (liberties == 0) {
prisonerCount += gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,GameBoard.EMPTY_INTERSECTION);
} else {
gameBoard.replaceSymbol(GameBoard.MARKED_GROUP, opponentSymbol);
}
}
if (stoneSymbol == GameBoard.BLACK_STONE) {
blackPrisoners += prisonerCount;
} else if (stoneSymbol == GameBoard.WHITE_STONE) {
whitePrisoners += prisonerCount;
}
//Moved test for 0 liberties until after attempting to capture neighboring groups.
if (0 == LibertyCounter.countLiberties(gameBoard, colLabel, rowNum, stoneSymbol)) {
gameBoard.removeStone(colLabel,rowNum);
return false;
}
return true;
}
public String toString() {
int boardSize = gameBoard.getSize();
StringBuilder sb = new StringBuilder(" ");
for (int cIndex = 0; cIndex < boardSize; cIndex++) {
sb.append(' ');
if (cIndex < 'I'-'A') {
sb.append((char)('A'+cIndex));
} else {
sb.append((char)('A' + cIndex + 1));
}
}
//note the extra space
sb.append(System.lineSeparator());
for (int rIndex = boardSize - 1; rIndex >= 0; rIndex--) {
if (rIndex < 9) {
sb.append(' ');
}
sb.append(rIndex+1);
for (int cIndex = 0; cIndex < boardSize; cIndex++) {
sb.append(' ');
sb.append(gameBoard.getSymbolAt(cIndex, rIndex));
}
sb.append(" " + (rIndex+1));
if (rIndex == boardSize/2) {
sb.append(" WHITE(O) has captured ");
sb.append(getWhitePrisoners());
sb.append(" stones");
} else if (rIndex == boardSize/2 -1) {
sb.append(" BLACK(X) has captured ");
sb.append(getBlackPrisoners());
sb.append(" stones");
}
sb.append(System.lineSeparator());
}
sb.append(" ");
for (int cIndex = 0; cIndex < boardSize; cIndex++) {
sb.append(' ');
if (cIndex < 'I'-'A') {
sb.append((char)('A'+cIndex));
} else {
sb.append((char)('A' + cIndex + 1));
}
}
return sb.toString();
}
}

149
src/cs6601/p1/GoGame.java Normal file
View File

@@ -0,0 +1,149 @@
package cs6601.p1;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import cs6601.p1.Command.TYPE;
import cs6601.p1.generator.AlphaBetaMoveGenerator;
import cs6601.p1.generator.MinimaxMoveGenerator;
import cs6601.p1.generator.MonteCarloMoveGenerator;
import cs6601.p1.generator.MoveGenerator;
import cs6601.p1.generator.RandomMoveGenerator;
public class GoGame {
private static final int INVALID_MOVE_GENERATOR = 1;
private static final Logger LOGGER = Logger.getLogger(GoGame.class
.getName());
private boolean shutDown = false;
private GameConfig gameConfig = new GameConfig();
private GameState gameState = new GameState(9);
private MoveGenerator moveGenerator;
public GoGame(MoveGenerator moveGenerator) {
this.moveGenerator = moveGenerator;
}
public static void main(String[] args) {
configureLogging();
if (args.length == 0) {
MoveGenerator defaultMoveGenerator = new MonteCarloMoveGenerator();
LOGGER.info("No MoveGenerator specified. Using default: " + defaultMoveGenerator.getClass().getName());
new GoGame(defaultMoveGenerator).play();
} else {
new GoGame(createMoveGenerator(args[0])).play();
}
}
private static MoveGenerator createMoveGenerator(String generatorName) {
if ("random".equals(generatorName)) {
return new RandomMoveGenerator();
} else if ("minimax".equals(generatorName)) {
return new MinimaxMoveGenerator();
} else if ("alphabeta".equals(generatorName)) {
return new AlphaBetaMoveGenerator();
} else if ("montecarlo".equals(generatorName)) {
return new MonteCarloMoveGenerator();
} else {
LOGGER.info("Unable to create MoveGenerator for unsupported name: " + generatorName);
System.exit(INVALID_MOVE_GENERATOR);
//This line will never be executed but prevents the compiler from complaining.
return null;
}
}
private void executeCommand(Command cmd) {
switch (cmd.getType()) {
case INVALID:
LOGGER.info("Invalid command ignored: " + cmd.getText());
System.out.println("? Invalid command: " + cmd.getText() + "\n");
break;
case boardsize:
gameState = new GameState(cmd.getIntField(1));
System.out.println("=\n");
break;
case clear_board:
gameState.clearBoard();
System.out.println("=\n");
break;
case final_status_list:
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
System.out.println("=\n");
break;
case genmove:
LOGGER.info("Generating move for:\n" + gameState);
String player = cmd.getStringField(1);
String nextMove = moveGenerator.genMove(gameConfig, gameState,
player);
gameState.playStone(player, nextMove);
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
System.out.println("="
+ nextMove.toLowerCase() + "\n");
break;
case komi:
gameConfig.setKomi(cmd.getDoubleField(1));
System.out.println("=\n");
break;
case list_commands:
StringBuilder sb = new StringBuilder("= ");
for (TYPE type : Command.TYPE.values()) {
if (type != TYPE.INVALID) {
sb.append(type.toString());
sb.append("\n");
}
}
System.out.println(sb.toString());
break;
case name:
System.out.println("= CS6601P1\n");
break;
case quit:
System.out.println("=\n");
shutDown = true;
break;
case play:
if (gameState.playStone(cmd.getStringField(1), cmd
.getStringField(2).toUpperCase())) {
System.out.println("=\n");
} else {
String errMsg = "Unable to make requested play: "
+ cmd.getText();
LOGGER.info(errMsg);
LOGGER.info("GameState: " + gameState);
System.out.println("?" + errMsg + "\n");
}
LOGGER.info(new StateEvaluator(gameConfig).scoreGame(gameState));
break;
case version:
System.out.println("= 0.1\n");
break;
default:
LOGGER.warn("An error occured. Unhandled command: " + cmd);
}
}
public void play() {
byte[] buf = new byte[128];
int inputLength;
try {
while (!shutDown && (inputLength = System.in.read(buf)) > 0) {
String commandText = new String(buf, 0, inputLength,
Charset.forName("UTF-8"));
LOGGER.info("Command received: " + commandText);
executeCommand(CommandParser.parse(commandText));
}
} catch (IOException ioe) {
LOGGER.warn(ioe.getMessage());
}
}
private static void configureLogging() {
DOMConfigurator.configure("log4j.xml");
}
}

View File

@@ -0,0 +1,48 @@
package cs6601.p1;
public class GtpClient {
/**
* Create a new GTP Client.
*
* @param in
* An input stream that will give us any responses from the
* engine.
* @param out
* An output stream that will go to the engine with our commands.
* @param args
* Our options.
*/
public GtpClient(java.io.InputStream in, java.io.OutputStream out,
Options options) {
}
/**
* Connect to the server and operate the GTP interface.
*
* @return <code>true</code> on success, <code>false</code> if we were
* unable to do what was requested.
*/
public boolean go() {
return false;
}
}
class Options {
/**
* Construct a new set of options for the kgsGtp client by pulling values
* out of a set of properties. Any values that we use will be removed from
* the properties, so anything left over is unused.
*
* @param props
* The properties that holds our options as string values.
* @param logName
* The name of the java logger that we will construct.
* @throws IllegalArgumentException
* If any required values are missing or if any illegal values
* are detected.
*/
public Options(java.util.Properties props, String logName) {
}
}

View File

@@ -0,0 +1,51 @@
package cs6601.p1;
public class LibertyCounter {
public static int countLiberties(GameBoard gameBoard, char colLabel, 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) {
int liberties = markGroup(gameBoard, col, row, groupColor);
if (!markGroup) {
gameBoard.replaceSymbol(GameBoard.MARKED_GROUP,groupColor);
}
return liberties;
}
public static int markGroup(GameBoard gameBoard, int col, int row, char groupColor) {
char stoneSymbol = gameBoard.getSymbolAt(col, row);
if (stoneSymbol == GameBoard.EMPTY_INTERSECTION) {
return 1; // one liberty
}
if (stoneSymbol == GameBoard.MARKED_GROUP) {
return 0; // intersection already counted
}
if (stoneSymbol == groupColor) {
int liberties = 0;
gameBoard.setSymbolAt(col, row, GameBoard.MARKED_GROUP);
if (col > 0) {
liberties += markGroup(gameBoard,col-1,row,groupColor);
}
if (col < gameBoard.getSize() - 1) {
liberties += markGroup(gameBoard,col+1,row,groupColor);
}
if (row > 0) {
liberties += markGroup(gameBoard,col,row-1,groupColor);
}
if (row < gameBoard.getSize() - 1) {
liberties += markGroup(gameBoard,col,row+1,groupColor);
}
return liberties;
}
if (groupColor == GameBoard.WHITE_STONE && stoneSymbol == GameBoard.BLACK_STONE) {
return 0; //opposing stone - not a liberty
}
if (groupColor == GameBoard.BLACK_STONE && 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");
}
}

View File

@@ -0,0 +1,23 @@
package cs6601.p1;
public class StateEvaluator {
private final GameConfig gameConfig;
public StateEvaluator(GameConfig gameConfig) {
this.gameConfig = 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),gameConfig.getKomi());
}
}

View File

@@ -0,0 +1,74 @@
package cs6601.p1;
public class TerritoryMarker {
public static final char BLACK_TERRITORY = 'x';
public static final char WHITE_TERRITORY = 'o';
public static final char UNOWNED_TERRITORY = '-';
public static final char TERRITORY_MARKER = '?';
public static final int EMPTY = 0;
public static final int BLACK = 1;
public static final int WHITE = 2;
public static GameBoard markTerritory(GameBoard gameBoard) {
for (int row = 0; row < gameBoard.getSize(); row++) {
for (int col = 0; col < gameBoard.getSize(); col++) {
char symbol = gameBoard.getSymbolAt(col,row);
if (symbol != '.') {
continue;
}
int ownedBy = findTerritory(gameBoard,col,row);
if (ownedBy == BLACK) {
gameBoard.replaceSymbol(TERRITORY_MARKER, BLACK_TERRITORY);
} else if (ownedBy == WHITE) {
gameBoard.replaceSymbol(TERRITORY_MARKER, WHITE_TERRITORY);
} else {
gameBoard.replaceSymbol(TERRITORY_MARKER, UNOWNED_TERRITORY);
}
}
}
gameBoard.setTerritoryMarked(true);
return gameBoard;
}
public static int getStoneColor(GameBoard gameBoard, int col, int row) {
char symbol = gameBoard.getSymbolAt(col,row);
if (symbol == GameBoard.BLACK_STONE) {
return BLACK;
} else if (symbol == GameBoard.WHITE_STONE) {
return WHITE;
} else {
return EMPTY;
}
}
public static int findTerritory(GameBoard gameBoard, int col, int row) {
char symbol = gameBoard.getSymbolAt(col,row);
if (symbol == GameBoard.BLACK_STONE) {
return BLACK;
} else if (symbol == GameBoard.WHITE_STONE) {
return WHITE;
} else if (symbol == TERRITORY_MARKER) {
return EMPTY;
}
gameBoard.markTerritory(col,row,TERRITORY_MARKER);
int borderBits = EMPTY;
if (col > 0) {
borderBits |= findTerritory(gameBoard,col-1,row);
}
if (col < gameBoard.getSize() - 1) {
borderBits |= findTerritory(gameBoard,col+1,row);
}
if (row > 0) {
borderBits |= findTerritory(gameBoard,col,row-1);
}
if (row < gameBoard.getSize() - 1) {
borderBits |= findTerritory(gameBoard,col,row+1);
}
return borderBits;
}
}

View File

@@ -0,0 +1,168 @@
package cs6601.p1.generator;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
import cs6601.p1.StateEvaluator;
public class AlphaBetaMoveGenerator implements MoveGenerator {
private static final Logger LOGGER = Logger.getLogger(AlphaBetaMoveGenerator.class.getName());
private static final int DEFAULT_RECURSIVE_PLAYS = 3;
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
private String bestPick = MoveGenerator.PASS;
@Override
public String genMove(GameConfig gameConfig, GameState gameState,
String initialColor) {
int alpha = Integer.MIN_VALUE;
int beta = Integer.MAX_VALUE;
if ("b".equals(initialColor)) {
getMaxValue(gameConfig,gameState,initialColor,false,DEFAULT_RECURSIVE_PLAYS*2,alpha,beta);
return bestPick;
} else if ("w".equals(initialColor)) {
getMinValue(gameConfig,gameState,initialColor,false,DEFAULT_RECURSIVE_PLAYS*2,alpha,beta);
return bestPick;
} else {
return MoveGenerator.PASS;
}
}
private int getMaxValue(GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, int recursionLevel, int alpha, int beta) {
if (terminalTest(recursionLevel)) {
return getUtility(gameConfig,gameState);
}
String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
List<String> validMoves = validMoveGenerator.genMoves(gameConfig,
gameState, colorPlaying, MoveGenerator.ALL_MOVES);
int value = Integer.MIN_VALUE;
//Map<Integer,String> firstMovesByScore = new HashMap<Integer,String> ();
for (String nextMove : validMoves) {
GameState nextState = new GameState(gameState);
if (!nextState.playStone(colorPlaying, nextMove)) {
throw new RuntimeException("Illegal move attempted during search!");
}
int minValue = getMinValue(gameConfig, nextState,
initialColor, !playAsOpponent, recursionLevel-1, alpha, beta);
//value = Math.max(value, minResult);
if (minValue > value) {
value = minValue;
if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
bestPick = nextMove;
}
}
///
//if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
// firstMovesByScore.put(value, nextMove);
//}
///
if (value >= beta) {
return value;
}
alpha = Math.max(alpha,value);
}
return value;
}
private int getMinValue(GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, int recursionLevel, int alpha, int beta) {
if (terminalTest(recursionLevel)) {
return getUtility(gameConfig,gameState);
}
String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
List<String> validMoves = validMoveGenerator.genMoves(gameConfig,
gameState, colorPlaying, MoveGenerator.ALL_MOVES);
int value = Integer.MAX_VALUE;
//Map<Integer,String> firstMovesByScore = new HashMap<Integer,String> ();
for (String nextMove : validMoves) {
GameState nextState = new GameState(gameState);
if (!nextState.playStone(colorPlaying, nextMove)) {
throw new RuntimeException("Illegal move attempted during search!");
}
int maxValue = getMaxValue(gameConfig, nextState,
initialColor, !playAsOpponent, recursionLevel-1, alpha, beta);
//value = Math.min(value, maxValue);
if (maxValue < value) {
value = maxValue;
if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
//firstMovesByScore.put(value, nextMove);
bestPick = nextMove;
}
}
///
//if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
// firstMovesByScore.put(value, nextMove);
//}
///
if (value <= alpha) {
return value;
}
beta = Math.min(beta,value);
}
//if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
// bestPick = firstMovesByScore.get(value);
//}
return value;
}
private boolean terminalTest(int recursionLevel) {
return recursionLevel < 1;
}
private int getUtility(GameConfig gameConfig, GameState gameState) {
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
return stateEvaluator.scoreGame(gameState).getAggregateScore();
}
private String getColorToPlay(String color, boolean playAsOpponent) {
if (playAsOpponent) {
if ("w".equals(color)) {
return "b";
} else if ("b".equals(color)) {
return "w";
} else {
return "?"; // invalid color will cause randomMoveGenerator to
// PASS
}
} else {
return color;
}
}
/**
* AlphaBetaMoveGenerator2 does not support this method.
*/
@Override
public List<String> genMoves(GameConfig gameConfig, GameState gameState,
String color, int nMoves) {
String[] pass = new String[] {PASS};
LOGGER.info("Minimax genMoves() stub returning [PASS]");
return Arrays.asList(pass);
}
}

View File

@@ -0,0 +1,107 @@
package cs6601.p1.generator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
import cs6601.p1.GameConfig;
import cs6601.p1.GameScore;
import cs6601.p1.GameState;
import cs6601.p1.StateEvaluator;
public class MinimaxMoveGenerator implements MoveGenerator {
private static final Logger LOGGER = Logger.getLogger(MinimaxMoveGenerator.class.getName());
//private static final Logger LOGGER = Logger
// .getLogger(MonteCarloMoveGenerator.class.getName());
private static final int DEFAULT_RECURSIVE_PLAYS = 1;
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
@Override
public String genMove(GameConfig gameConfig, GameState gameState,
String color) {
MoveCandidate moveCandidate = findBestMinimaxResult(
DEFAULT_RECURSIVE_PLAYS * 2,
gameConfig, gameState, color, false, PASS);
return moveCandidate.move;
}
private MoveCandidate findBestMinimaxResult(int recursionLevels,
GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, String bestPrevMove) {
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
List<MoveCandidate> randomMoveCandidates = new ArrayList<MoveCandidate>();
String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
List<String> validMoves = validMoveGenerator.genMoves(gameConfig,
gameState, colorPlaying, MoveGenerator.ALL_MOVES);
for (String randomMove : validMoves) {
GameState stateCopy = new GameState(gameState);
stateCopy.playStone(colorPlaying, randomMove);
if (recursionLevels > 1) {
randomMoveCandidates.add(findBestMinimaxResult(recursionLevels - 1,
gameConfig, stateCopy, initialColor,
!playAsOpponent, randomMove));
} else {
GameScore score = stateEvaluator.scoreGame(stateCopy);
randomMoveCandidates.add(new MoveCandidate(randomMove, score));
}
}
// TODO use a sorted list and just return the last element
MoveCandidate bestMove = randomMoveCandidates.get(0);
double bestScoreSoFar = bestMove.score.getScore(colorPlaying);
for (MoveCandidate moveCandidate : randomMoveCandidates) {
if (moveCandidate.score.getScore(colorPlaying) > bestScoreSoFar) {
bestMove = moveCandidate;
bestScoreSoFar = moveCandidate.score.getScore(colorPlaying);
}
}
// Fix to prevent thinking that the _opponent's_ best move is the move
// to make.
// If evaluating an opponent's move, the best move (for my opponent) is
// my previous move which gives the opponent the highest score.
if (playAsOpponent) {
return new MoveCandidate(bestPrevMove, bestMove.score);
} else { // if evaluating my own move, the move which gives me the
// highest score is the best.
return bestMove;
}
}
private String getColorToPlay(String color, boolean playAsOpponent) {
if (playAsOpponent) {
if ("w".equals(color)) {
return "b";
} else if ("b".equals(color)) {
return "w";
} else {
return "?"; // invalid color will cause randomMoveGenerator to
// PASS
}
} else {
return color;
}
}
/**
* MinimaxMoveGenerator does not support this method.
*/
@Override
public List<String> genMoves(GameConfig gameConfig, GameState gameState,
String color, int nMoves) {
String[] pass = new String[] {PASS};
LOGGER.info("Minimax genMoves() stub returning [PASS]");
return Arrays.asList(pass);
}
}

View File

@@ -0,0 +1,109 @@
package cs6601.p1.generator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import cs6601.p1.GameConfig;
import cs6601.p1.GameScore;
import cs6601.p1.GameState;
import cs6601.p1.StateEvaluator;
public class MonteCarloMoveGenerator implements MoveGenerator {
//private static final Logger LOGGER = Logger
// .getLogger(MonteCarloMoveGenerator.class.getName());
private static final int DEFAULT_RECURSIVE_PLAYS = 3;
private static final int DEFAULT_PLAYS_PER_LEVEL = 10;
//private static final int MAX_RANDOM_TRIES = 10;
private final RandomMoveGenerator randomMoveGenerator = new RandomMoveGenerator();
@Override
public String genMove(GameConfig gameConfig, GameState gameState,
String color) {
MoveCandidate moveCandidate = findBestMonteCarloResult(
DEFAULT_RECURSIVE_PLAYS * 2, DEFAULT_PLAYS_PER_LEVEL,
gameConfig, gameState, color, false, PASS);
return moveCandidate.move;
}
private MoveCandidate findBestMonteCarloResult(int recursionLevels,
int playsPerLevel, GameConfig gameConfig, GameState gameState,
String initialColor, boolean playAsOpponent, String bestPrevMove) {
StateEvaluator stateEvaluator = new StateEvaluator(gameConfig);
List<MoveCandidate> randomMoveCandidates = new ArrayList<MoveCandidate>();
String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
List<String> randomMoves = randomMoveGenerator.genMoves(gameConfig,
gameState, colorPlaying, DEFAULT_PLAYS_PER_LEVEL);
for (String randomMove : randomMoves) {
GameState stateCopy = new GameState(gameState);
stateCopy.playStone(colorPlaying, randomMove);
if (recursionLevels > 1) {
randomMoveCandidates.add(findBestMonteCarloResult(recursionLevels - 1,
playsPerLevel, gameConfig, stateCopy, initialColor,
!playAsOpponent, randomMove));
} else {
GameScore score = stateEvaluator.scoreGame(stateCopy);
randomMoveCandidates.add(new MoveCandidate(randomMove, score));
}
}
// TODO use a sorted list and just return the last element
MoveCandidate bestMove = randomMoveCandidates.get(0);
double bestScoreSoFar = bestMove.score.getScore(colorPlaying);
for (MoveCandidate moveCandidate : randomMoveCandidates) {
if (moveCandidate.score.getScore(colorPlaying) > bestScoreSoFar) {
bestMove = moveCandidate;
bestScoreSoFar = moveCandidate.score.getScore(colorPlaying);
}
}
// Fix to prevent thinking that the _opponent's_ best move is the move
// to make.
// If evaluating an opponent's move, the best move (for my opponent) is
// my previous move which gives the opponent the highest score.
if (playAsOpponent) {
return new MoveCandidate(bestPrevMove, bestMove.score);
} else { // if evaluating my own move, the move which gives me the
// highest score is the best.
return bestMove;
}
}
private String getColorToPlay(String color, boolean playAsOpponent) {
if (playAsOpponent) {
if ("w".equals(color)) {
return "b";
} else if ("b".equals(color)) {
return "w";
} else {
return "?"; // invalid color will cause randomMoveGenerator to
// PASS
}
} else {
return color;
}
}
/**
* MonteCarloMoveGenerator does not support this method - just pass.
*
* @param gameConfig
* @param gameState
* @param color
* @return
*/
@Override
public List<String> genMoves(GameConfig gameConfig, GameState gameState,
String color, int nMoves) {
String[] pass = new String[] {PASS};
return Arrays.asList(pass);
}
}

View File

@@ -0,0 +1,13 @@
package cs6601.p1.generator;
import cs6601.p1.GameScore;
public class MoveCandidate {
public final String move;
public final GameScore score;
public MoveCandidate(String move, GameScore score) {
this.move = move;
this.score = score;
}
}

View File

@@ -0,0 +1,13 @@
package cs6601.p1.generator;
import java.util.List;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
public interface MoveGenerator {
static final String PASS = "PASS";
static final int ALL_MOVES = 0;
public String genMove(GameConfig gameConfig, GameState gameState, String color);
public List<String> genMoves(GameConfig gameConfig, GameState gameState, String color, int nMoves);
}

View File

@@ -0,0 +1,65 @@
package cs6601.p1.generator;
import java.util.ArrayList;
import java.util.List;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
public class RandomMoveGenerator implements MoveGenerator {
/**
* Does NOT modify the gameState.
*/
public String genMove(GameConfig gameConfig, GameState gameState,
String color) {
GameState gameStateCopy = new GameState(gameState);
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
while (emptyCoordinates.size() > 0) {
String randomMove = emptyCoordinates
.get((int) (Math.random() * emptyCoordinates.size()));
if (gameStateCopy.playStone(color, randomMove)) {
return randomMove;
} else {
emptyCoordinates.remove(randomMove);
}
}
return PASS;
}
/**
* Attempts to generate up to nMoves random moves on behalf of the specified
* player. Will return at least one move, which may be 'pass' if random
* search does not success in discovering a valid move. Does NOT modify the
* gameState.
*
* @param gameConfig
* @param gameState
* @param color
*
* @return
*/
public List<String> genMoves(GameConfig gameConfig, GameState gameState,
String color, int nMoves) {
GameState gameStateCopy = new GameState(gameState);
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
List<String> randomMoves = new ArrayList<String>();
while (emptyCoordinates.size() > 0 && randomMoves.size() < nMoves) {
String randomMove = emptyCoordinates
.get((int) (Math.random() * emptyCoordinates.size()));
if (gameStateCopy.playStone(color, randomMove)) {
randomMoves.add(randomMove);
}
emptyCoordinates.remove(randomMove);
}
if (randomMoves.size() == 0) {
randomMoves.add(PASS);
}
return randomMoves;
}
}

View File

@@ -0,0 +1,44 @@
package cs6601.p1.generator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
public class ValidMoveGenerator implements MoveGenerator {
private static final Logger LOGGER = Logger.getLogger(MinimaxMoveGenerator.class.getName());
@Override
public String genMove(GameConfig gameConfig, GameState gameState,
String color) {
LOGGER.info("ValidMoveGenerator genMove() stub returning PASS");
return PASS;
}
@Override
public List<String> genMoves(GameConfig gameConfig, GameState gameState,
String color, int nMoves) {
GameState gameStateCopy = new GameState(gameState);
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
List<String> validMoves = new ArrayList<String>();
while (emptyCoordinates.size() > 0) {
String nextMove = emptyCoordinates.remove(emptyCoordinates.size()-1);
if (gameStateCopy.playStone(color, nextMove)) {
validMoves.add(nextMove);
gameStateCopy = new GameState(gameState); // play successful? regenerate copy of gameState
}
}
if (validMoves.size() == 0) {
validMoves.add(PASS);
}
return validMoves;
}
}

View File

@@ -0,0 +1,80 @@
package cs6601.p1;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class CaptureTest {
@Test
public void testCapture() {
GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 3, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE));
assertEquals(0,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners());
assertTrue(gameState.playStone('C', 2, GameBoard.BLACK_STONE));
assertEquals(1,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners());
System.out.println(gameState);
}
@Test
public void testMultiGroupCapture() {
GameConfig gameConfig = new GameConfig();
GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 3, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
gameState.playStone('C', 4, GameBoard.BLACK_STONE);
gameState.playStone('D', 3, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('C', 3, GameBoard.WHITE_STONE));
assertEquals(0,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners());
assertTrue(gameState.playStone('C', 2, GameBoard.BLACK_STONE));
assertEquals(2,gameState.getBlackPrisoners());
assertEquals(0,gameState.getWhitePrisoners());
assertFalse(gameState.playStone('B', 2, GameBoard.WHITE_STONE));
assertFalse(gameState.playStone('C', 3, GameBoard.WHITE_STONE));
System.out.println(gameState);
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState);
System.out.println(gameScore.getScoreReport());
}
@Test
public void testCaptureFromEye() {
GameState gameState = new GameState(5);
gameState.playStone('A', 1, GameBoard.BLACK_STONE);
gameState.playStone('B', 2, GameBoard.BLACK_STONE);
gameState.playStone('C', 1, GameBoard.BLACK_STONE);
gameState.playStone('A', 2, GameBoard.WHITE_STONE);
gameState.playStone('B', 3, GameBoard.WHITE_STONE);
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
//This capture should be allowed.
assertTrue("Capture from within single eye should have been allowed but move was rejected.",
gameState.playStone('B', 1, GameBoard.WHITE_STONE));
assertEquals(0,gameState.getBlackPrisoners());
assertEquals(2,gameState.getWhitePrisoners());
System.out.println(gameState);
}
}

View File

@@ -0,0 +1,26 @@
package cs6601.p1;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class GameScoreTest {
@Test
public void testGetAggregateScoreZero() {
GameScore gameScore = new GameScore(0,0,0);
assertEquals(GameScore.NORMALIZED_ZERO_SCORE, gameScore.getAggregateScore());
}
@Test
public void testGetAggregateScoreBlackWinsNoKomi() {
GameScore gameScore = new GameScore(25,2,0);
assertEquals(425, gameScore.getAggregateScore());
}
@Test
public void testGetAggregateScoreWhiteWinsWithKomi() {
GameScore gameScore = new GameScore(10,12,6.5);
assertEquals(362, gameScore.getAggregateScore());
}
}

View File

@@ -0,0 +1,62 @@
package cs6601.p1;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class IllegalMoveTest {
@Test
public void testIllegalMoveOnOwnStone() {
GameState gameState = new GameState(5);
gameState.playStone('B', 3, GameBoard.BLACK_STONE);
assertFalse(gameState.playStone('B', 3, GameBoard.BLACK_STONE));
}
@Test
public void testIllegalMoveOnOtherStone() {
GameState gameState = new GameState(5);
gameState.playStone('B', 3, GameBoard.BLACK_STONE);
assertFalse(gameState.playStone('B', 3, GameBoard.WHITE_STONE));
}
@Test
public void testIllegalMoveNoLiberties() {
GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 3, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
assertFalse(gameState.playStone('B', 2, GameBoard.WHITE_STONE));
System.out.println(gameState);
}
@Test
public void testIllegalMoveFormsTrappedGroup() {
GameState gameState = new GameState(9);
gameState.playStone('A', 5, GameBoard.BLACK_STONE);
gameState.playStone('B', 6, GameBoard.BLACK_STONE);
gameState.playStone('B', 7, GameBoard.BLACK_STONE);
gameState.playStone('A', 8, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('A', 6, GameBoard.WHITE_STONE));
assertFalse(gameState.playStone('A', 7, GameBoard.WHITE_STONE));
System.out.println(gameState);
}
@Test
public void testIllegalMoveFormsTrappedGroup2() {
GameState gameState = new GameState(9);
gameState.playStone('G', 1, GameBoard.BLACK_STONE);
gameState.playStone('H', 2, GameBoard.BLACK_STONE);
gameState.playStone('H', 3, GameBoard.BLACK_STONE);
gameState.playStone('J', 4, GameBoard.BLACK_STONE);
gameState.playStone('A', 8, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('H', 1, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('J', 2, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('J', 3, GameBoard.WHITE_STONE));
System.out.println(gameState);
assertFalse(gameState.playStone('J', 1, GameBoard.WHITE_STONE));
System.out.println(gameState);
}
}

View File

@@ -0,0 +1,17 @@
package cs6601.p1;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class LegalMoveTest {
@Test
public void testLegalMove1Liberty() {
GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 3, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('B', 2, GameBoard.WHITE_STONE));
System.out.println(gameState);
}
}

View File

@@ -0,0 +1,96 @@
package cs6601.p1;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import cs6601.p1.generator.AlphaBetaMoveGenerator;
import cs6601.p1.generator.MoveGenerator;
public class StateEvaluatorTest {
GameConfig gameConfig = new GameConfig();
@Test
public void testScoreEmptyBoard() {
GameState gameState = new GameState(5);
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState);
assertEquals(0.0,gameScore.getWhiteScore(),0.5);
assertEquals(0.0,gameScore.getBlackScore(),0.5);
}
@Test
public void testScoreFirstMove() {
GameState gameState = new GameState(5);
gameState.playStone('B',3,GameBoard.BLACK_STONE);
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() {
GameState gameState = new GameState(5);
gameState.playStone('B',3,GameBoard.BLACK_STONE);
gameState.playStone('A',1,GameBoard.WHITE_STONE);
GameScore gameScore = new StateEvaluator(gameConfig).scoreGame(gameState);
System.out.println(gameScore.getScoreReport());
assertEquals(1.0,gameScore.getWhiteScore(),0.5);
assertEquals(1.0,gameScore.getBlackScore(),0.5);
}
@Test
public void testScoreTerritory() {
GameState gameState = new GameState(5);
gameState.playStone('A',2,GameBoard.BLACK_STONE);
gameState.playStone('B',3,GameBoard.BLACK_STONE);
gameState.playStone('C',2,GameBoard.BLACK_STONE);
gameState.playStone('B',1,GameBoard.BLACK_STONE);
gameState.playStone('E',5,GameBoard.WHITE_STONE);
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() {
GameState gameState = new GameState(9);
gameState.playStone('A', 2, GameBoard.WHITE_STONE);
gameState.playStone('B', 1, GameBoard.WHITE_STONE);
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
gameState.playStone('B', 2, GameBoard.BLACK_STONE);
GameState moveToA1 = new GameState(gameState);
GameState capAtB3 = new GameState(gameState);
moveToA1.playStone("w","A1");
capAtB3.playStone("w", "B3");
System.out.println(moveToA1);
System.out.println(capAtB3);
StateEvaluator eval = new StateEvaluator(new GameConfig());
int scoreA1 = eval.scoreGame(moveToA1).getAggregateScore();
int scoreB3 = eval.scoreGame(capAtB3).getAggregateScore();
System.out.println("Score at A1: " + scoreA1);
System.out.println("Score at B3: " + scoreB3);
//moving as white, lower is better
assertTrue(scoreA1 > scoreB3);
}
}

View File

@@ -0,0 +1,19 @@
package cs6601.p1;
import org.junit.Test;
public class TerritoryFinderTest {
@Test
public void testMarkTerritory() {
GameState gameState = new GameState(5);
gameState.playStone('A',2,GameBoard.BLACK_STONE);
gameState.playStone('B',3,GameBoard.BLACK_STONE);
gameState.playStone('C',2,GameBoard.BLACK_STONE);
gameState.playStone('B',1,GameBoard.BLACK_STONE);
gameState.playStone('E',5,GameBoard.WHITE_STONE);
TerritoryMarker.markTerritory(gameState.getGameBoard());
System.out.println(gameState);
}
}

View File

@@ -0,0 +1,53 @@
package cs6601.p1.generator;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import cs6601.p1.GameBoard;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
public class AlphaBetaTest {
@Test
public void testGenmoveAsW() {
MoveGenerator moveGenerator = new AlphaBetaMoveGenerator();
GameState gameState = new GameState(6);
gameState.playStone('A', 2, GameBoard.WHITE_STONE);
gameState.playStone('B', 1, GameBoard.WHITE_STONE);
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
gameState.playStone('B', 2, GameBoard.BLACK_STONE);
String move = moveGenerator.genMove(new GameConfig(), gameState, "b");
System.out.println(gameState);
System.out.println("Generated move: " + move);
assertEquals("Expected B3 but was: " + move, "B3", move);
gameState.playStone("b", move);
System.out.println(gameState);
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
}
@Test
public void testGenmoveAsB() {
MoveGenerator moveGenerator = new AlphaBetaMoveGenerator();
GameState gameState = new GameState(6);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 2, GameBoard.WHITE_STONE);
String move = moveGenerator.genMove(new GameConfig(), gameState, "b");
System.out.println(gameState);
System.out.println("Generated move: " + move);
assertEquals("Expected B3 but was: " + move, "B3", move);
gameState.playStone("b", move);
System.out.println(gameState);
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
}
}

View File

@@ -0,0 +1,33 @@
package cs6601.p1.generator;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import cs6601.p1.GameBoard;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
import cs6601.p1.generator.MoveGenerator;
public class MinimaxTest {
@Test
public void testGenmove() {
MoveGenerator moveGenerator = new MinimaxMoveGenerator();
GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
gameState.playStone('B', 4, GameBoard.BLACK_STONE);
String move = moveGenerator.genMove(new GameConfig(), gameState, "w");
System.out.println("Generated move: " + move);
gameState.playStone("w", move);
System.out.println(gameState);
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
System.out.println(gameState);
}
}

View File

@@ -0,0 +1,52 @@
package cs6601.p1.generator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import cs6601.p1.GameBoard;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
import cs6601.p1.generator.MoveGenerator;
import cs6601.p1.generator.RandomMoveGenerator;
public class RandomTest {
@Test
public void testGenmove() {
MoveGenerator moveGenerator = new RandomMoveGenerator();
GameState gameState = new GameState(5);
moveGenerator.genMove(new GameConfig(), gameState, "b");
gameState = new GameState(5);
moveGenerator.genMove(new GameConfig(), gameState, "w");
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
System.out.println(gameState);
}
@Test
public void testAlternativeToIllegalMove() {
GameState gameState = new GameState(4);
gameState.playStone('A', 1, GameBoard.BLACK_STONE);
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
gameState.playStone('A', 3, GameBoard.BLACK_STONE);
gameState.playStone('A', 4, GameBoard.BLACK_STONE);
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
gameState.playStone('B', 2, GameBoard.BLACK_STONE);
//gameState.playStone('B', 3, GameBoard.BLACK_STONE);
gameState.playStone('B', 4, GameBoard.BLACK_STONE);
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
gameState.playStone('C', 3, GameBoard.BLACK_STONE);
gameState.playStone('C', 4, GameBoard.BLACK_STONE);
gameState.playStone('D', 4, GameBoard.BLACK_STONE);
assertTrue(gameState.playStone('C', 1, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('D', 2, GameBoard.WHITE_STONE));
assertTrue(gameState.playStone('D', 3, GameBoard.WHITE_STONE));
System.out.println(gameState);
//This is correct - checked vs. MFOG
assertEquals("B3", new RandomMoveGenerator().genMove(new GameConfig(), gameState, "w"));
System.out.println(gameState);
}
}

View File

@@ -0,0 +1,43 @@
package cs6601.p1.generator;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import cs6601.p1.GameBoard;
import cs6601.p1.GameConfig;
import cs6601.p1.GameState;
public class ValidMoveGeneratorTest {
@Test
public void test() {
/* move generator should not include A1 here when playing as black:
A B C D E
5 . . . . . 5
4 . O . . . 4
3 . . . . . 3 WHITE(O) has captured 0 stones
2 O . O . . 2 BLACK(X) has captured 0 stones
1 . O . . . 1
A B C D E
*/
GameState gameState = new GameState(5);
gameState.playStone('A', 2, GameBoard.WHITE_STONE);
gameState.playStone('B', 1, GameBoard.WHITE_STONE);
gameState.playStone('B', 4, GameBoard.WHITE_STONE);
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
assertFalse(gameState.playStone('A', 1, GameBoard.BLACK_STONE));
List<String> validMoves = new ValidMoveGenerator().genMoves(new GameConfig(), gameState, "b",0);
assertTrue(validMoves.size() > 0);
for (String vm : validMoves) {
System.out.println(vm);
}
assertFalse(validMoves.contains("A1"));
System.out.println(gameState);
}
}