Initial commit.
This commit is contained in:
9
.classpath
Normal file
9
.classpath
Normal 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
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dist
|
||||||
|
bin
|
||||||
|
build
|
||||||
17
.project
Normal file
17
.project
Normal 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>
|
||||||
12
.settings/org.eclipse.jdt.core.prefs
Normal file
12
.settings/org.eclipse.jdt.core.prefs
Normal 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
81
build.xml
Normal 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
1
data/gogame.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
java -jar kgsGtp.jar kgsGtp.ini
|
||||||
13
data/kgsGtp.ini
Normal file
13
data/kgsGtp.ini
Normal 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
BIN
data/kgsGtp.jar
Normal file
Binary file not shown.
BIN
data/log4j-1.2.16.jar
Normal file
BIN
data/log4j-1.2.16.jar
Normal file
Binary file not shown.
19
data/log4j.xml
Normal file
19
data/log4j.xml
Normal 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
BIN
lib/junit-4.10.jar
Normal file
Binary file not shown.
BIN
lib/log4j-1.2.16.jar
Normal file
BIN
lib/log4j-1.2.16.jar
Normal file
Binary file not shown.
39
src/cs6601/p1/Command.java
Normal file
39
src/cs6601/p1/Command.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/cs6601/p1/CommandParser.java
Normal file
54
src/cs6601/p1/CommandParser.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
210
src/cs6601/p1/GameBoard.java
Normal file
210
src/cs6601/p1/GameBoard.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/cs6601/p1/GameConfig.java
Normal file
27
src/cs6601/p1/GameConfig.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/cs6601/p1/GameController.java
Normal file
5
src/cs6601/p1/GameController.java
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package cs6601.p1;
|
||||||
|
|
||||||
|
public class GameController {
|
||||||
|
|
||||||
|
}
|
||||||
65
src/cs6601/p1/GameScore.java
Normal file
65
src/cs6601/p1/GameScore.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
207
src/cs6601/p1/GameState.java
Normal file
207
src/cs6601/p1/GameState.java
Normal 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
149
src/cs6601/p1/GoGame.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/cs6601/p1/GtpClient.java
Normal file
48
src/cs6601/p1/GtpClient.java
Normal 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/cs6601/p1/LibertyCounter.java
Normal file
51
src/cs6601/p1/LibertyCounter.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/cs6601/p1/StateEvaluator.java
Normal file
23
src/cs6601/p1/StateEvaluator.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/cs6601/p1/TerritoryMarker.java
Normal file
74
src/cs6601/p1/TerritoryMarker.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/cs6601/p1/generator/AlphaBetaMoveGenerator.java
Normal file
168
src/cs6601/p1/generator/AlphaBetaMoveGenerator.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/cs6601/p1/generator/MinimaxMoveGenerator.java
Normal file
107
src/cs6601/p1/generator/MinimaxMoveGenerator.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/cs6601/p1/generator/MonteCarloMoveGenerator.java
Normal file
109
src/cs6601/p1/generator/MonteCarloMoveGenerator.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/cs6601/p1/generator/MoveCandidate.java
Normal file
13
src/cs6601/p1/generator/MoveCandidate.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/cs6601/p1/generator/MoveGenerator.java
Normal file
13
src/cs6601/p1/generator/MoveGenerator.java
Normal 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);
|
||||||
|
}
|
||||||
65
src/cs6601/p1/generator/RandomMoveGenerator.java
Normal file
65
src/cs6601/p1/generator/RandomMoveGenerator.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/cs6601/p1/generator/ValidMoveGenerator.java
Normal file
44
src/cs6601/p1/generator/ValidMoveGenerator.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
80
test/cs6601/p1/CaptureTest.java
Normal file
80
test/cs6601/p1/CaptureTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
test/cs6601/p1/GameScoreTest.java
Normal file
26
test/cs6601/p1/GameScoreTest.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
62
test/cs6601/p1/IllegalMoveTest.java
Normal file
62
test/cs6601/p1/IllegalMoveTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
test/cs6601/p1/LegalMoveTest.java
Normal file
17
test/cs6601/p1/LegalMoveTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
96
test/cs6601/p1/StateEvaluatorTest.java
Normal file
96
test/cs6601/p1/StateEvaluatorTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
test/cs6601/p1/TerritoryFinderTest.java
Normal file
19
test/cs6601/p1/TerritoryFinderTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
test/cs6601/p1/generator/AlphaBetaTest.java
Normal file
53
test/cs6601/p1/generator/AlphaBetaTest.java
Normal 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, "?"));
|
||||||
|
}
|
||||||
|
}
|
||||||
33
test/cs6601/p1/generator/MinimaxTest.java
Normal file
33
test/cs6601/p1/generator/MinimaxTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
test/cs6601/p1/generator/RandomTest.java
Normal file
52
test/cs6601/p1/generator/RandomTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
test/cs6601/p1/generator/ValidMoveGeneratorTest.java
Normal file
43
test/cs6601/p1/generator/ValidMoveGeneratorTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user