Substantial refactoring to implement correct Naive, UCT Monte Carlo tree search methods.
Removed unnecessary distinction between policy and tree search (tree search is a special kind of policy). Calculation of all valid moves / arbitrary sets of moves is now a seperate class, as it serves a different purpose than a policy. Introduced regression error in AlphaBeta test.
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
package cs6601.p1;
|
|
||||||
|
|
||||||
public class GameController {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class Command {
|
public class Command {
|
||||||
public enum TYPE { boardsize, clear_board, final_status_list, genmove, INVALID, komi, list_commands, name, play, quit, version };
|
public enum TYPE { boardsize, clear_board, final_status_list, genmove, INVALID, komi, list_commands, name, play, quit, version };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class GameConfig {
|
public class GameConfig {
|
||||||
private double komi;
|
private double komi;
|
||||||
5
src/net/woodyfolsom/msproj/GameController.java
Normal file
5
src/net/woodyfolsom/msproj/GameController.java
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
public class GameController {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
|
||||||
public class GameScore {
|
public class GameScore {
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import cs6601.p1.generator.MoveGenerator;
|
|
||||||
|
|
||||||
public class GameState {
|
public class GameState {
|
||||||
private static final Logger LOGGER = Logger.getLogger(GameState.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(GameState.class.getName());
|
||||||
@@ -61,7 +62,7 @@ public class GameState {
|
|||||||
|
|
||||||
public boolean playStone(String player, String coord) {
|
public boolean playStone(String player, String coord) {
|
||||||
//Opponent passes? Just ignore it.
|
//Opponent passes? Just ignore it.
|
||||||
if (MoveGenerator.PASS.equalsIgnoreCase(coord)) {
|
if (Policy.PASS.equalsIgnoreCase(coord)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.Command.TYPE;
|
||||||
|
import net.woodyfolsom.msproj.policy.AlphaBeta;
|
||||||
|
import net.woodyfolsom.msproj.policy.Minimax;
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
import net.woodyfolsom.msproj.policy.MonteCarloUCT;
|
||||||
|
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.apache.log4j.xml.DOMConfigurator;
|
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 {
|
public class GoGame {
|
||||||
private static final int INVALID_MOVE_GENERATOR = 1;
|
private static final int INVALID_MOVE_GENERATOR = 1;
|
||||||
@@ -21,34 +22,34 @@ public class GoGame {
|
|||||||
private boolean shutDown = false;
|
private boolean shutDown = false;
|
||||||
private GameConfig gameConfig = new GameConfig();
|
private GameConfig gameConfig = new GameConfig();
|
||||||
private GameState gameState = new GameState(9);
|
private GameState gameState = new GameState(9);
|
||||||
private MoveGenerator moveGenerator;
|
private Policy moveGenerator;
|
||||||
|
|
||||||
public GoGame(MoveGenerator moveGenerator) {
|
public GoGame(Policy moveGenerator) {
|
||||||
this.moveGenerator = moveGenerator;
|
this.moveGenerator = moveGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
configureLogging();
|
configureLogging();
|
||||||
if (args.length == 0) {
|
if (args.length == 0) {
|
||||||
MoveGenerator defaultMoveGenerator = new MonteCarloMoveGenerator();
|
Policy defaultMoveGenerator = new MonteCarloUCT(new RandomMovePolicy(), 2000L);
|
||||||
LOGGER.info("No MoveGenerator specified. Using default: " + defaultMoveGenerator.getClass().getName());
|
LOGGER.info("No MoveGenerator specified. Using default: " + defaultMoveGenerator.getClass().getName());
|
||||||
new GoGame(defaultMoveGenerator).play();
|
new GoGame(defaultMoveGenerator).play();
|
||||||
} else {
|
} else {
|
||||||
new GoGame(createMoveGenerator(args[0])).play();
|
new GoGame(createPolicy(args[0])).play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MoveGenerator createMoveGenerator(String generatorName) {
|
private static Policy createPolicy(String policyName) {
|
||||||
if ("random".equals(generatorName)) {
|
if ("random".equals(policyName)) {
|
||||||
return new RandomMoveGenerator();
|
return new RandomMovePolicy();
|
||||||
} else if ("minimax".equals(generatorName)) {
|
} else if ("minimax".equals(policyName)) {
|
||||||
return new MinimaxMoveGenerator();
|
return new Minimax();
|
||||||
} else if ("alphabeta".equals(generatorName)) {
|
} else if ("alphabeta".equals(policyName)) {
|
||||||
return new AlphaBetaMoveGenerator();
|
return new AlphaBeta();
|
||||||
} else if ("montecarlo".equals(generatorName)) {
|
} else if ("montecarlo".equals(policyName)) {
|
||||||
return new MonteCarloMoveGenerator();
|
return new MonteCarloUCT(new RandomMovePolicy(), 2000L);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Unable to create MoveGenerator for unsupported name: " + generatorName);
|
LOGGER.info("Unable to create Policy for unsupported name: " + policyName);
|
||||||
System.exit(INVALID_MOVE_GENERATOR);
|
System.exit(INVALID_MOVE_GENERATOR);
|
||||||
//This line will never be executed but prevents the compiler from complaining.
|
//This line will never be executed but prevents the compiler from complaining.
|
||||||
return null;
|
return null;
|
||||||
@@ -77,7 +78,7 @@ public class GoGame {
|
|||||||
LOGGER.info("Generating move for:\n" + gameState);
|
LOGGER.info("Generating move for:\n" + gameState);
|
||||||
|
|
||||||
String player = cmd.getStringField(1);
|
String player = cmd.getStringField(1);
|
||||||
String nextMove = moveGenerator.genMove(gameConfig, gameState,
|
String nextMove = moveGenerator.getAction(gameConfig, gameState,
|
||||||
player);
|
player);
|
||||||
|
|
||||||
gameState.playStone(player, nextMove);
|
gameState.playStone(player, nextMove);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class GtpClient {
|
public class GtpClient {
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class LibertyCounter {
|
public class LibertyCounter {
|
||||||
public static int countLiberties(GameBoard gameBoard, char colLabel, int rowNum, char groupColor) {
|
public static int countLiberties(GameBoard gameBoard, char colLabel, int rowNum, char groupColor) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class StateEvaluator {
|
public class StateEvaluator {
|
||||||
private final GameConfig gameConfig;
|
private final GameConfig gameConfig;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
public class TerritoryMarker {
|
public class TerritoryMarker {
|
||||||
public static final char BLACK_TERRITORY = 'x';
|
public static final char BLACK_TERRITORY = 'x';
|
||||||
27
src/net/woodyfolsom/msproj/ZobristHashGenerator.java
Normal file
27
src/net/woodyfolsom/msproj/ZobristHashGenerator.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
public class ZobristHashGenerator {
|
||||||
|
private long[] randomBitFields;
|
||||||
|
|
||||||
|
private ZobristHashGenerator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ZobristHashGenerator getInstance(int boardSize) {
|
||||||
|
int nRandomFields = 3 * boardSize * boardSize;
|
||||||
|
ZobristHashGenerator zobHashGen = new ZobristHashGenerator();
|
||||||
|
SecureRandom secureRandom = new SecureRandom();
|
||||||
|
zobHashGen.randomBitFields = new long[nRandomFields];
|
||||||
|
byte[] nextBytes = new byte[8];
|
||||||
|
for (int i = 0; i < nRandomFields; i++) {
|
||||||
|
secureRandom.nextBytes(nextBytes);
|
||||||
|
zobHashGen.randomBitFields[i] = new BigInteger(nextBytes)
|
||||||
|
.longValue();
|
||||||
|
}
|
||||||
|
// TODO add check for minimum hamming distance/colinearity check
|
||||||
|
return zobHashGen;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
src/net/woodyfolsom/msproj/policy/ActionGenerator.java
Normal file
13
src/net/woodyfolsom/msproj/policy/ActionGenerator.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
|
public interface ActionGenerator {
|
||||||
|
public static final int ALL_ACTIONS = 0;
|
||||||
|
|
||||||
|
public List<String> getActions(GameConfig gameConfig, GameState gameState,
|
||||||
|
String color, int numActions);
|
||||||
|
}
|
||||||
144
src/net/woodyfolsom/msproj/policy/AlphaBeta.java
Normal file
144
src/net/woodyfolsom/msproj/policy/AlphaBeta.java
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.StateEvaluator;
|
||||||
|
|
||||||
|
//import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
public class AlphaBeta implements Policy {
|
||||||
|
//private static final Logger LOGGER = Logger.getLogger(AlphaBeta.class
|
||||||
|
// .getName());
|
||||||
|
private static final int DEFAULT_RECURSIVE_PLAYS = 3;
|
||||||
|
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
|
||||||
|
|
||||||
|
private String bestPick = Policy.PASS;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAction(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 Policy.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.getActions(gameConfig,
|
||||||
|
gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
|
int value = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (minValue > value) {
|
||||||
|
value = minValue;
|
||||||
|
if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
|
||||||
|
bestPick = 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.getActions(gameConfig,
|
||||||
|
gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
|
int value = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (maxValue < value) {
|
||||||
|
value = maxValue;
|
||||||
|
if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
|
||||||
|
bestPick = nextMove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= alpha) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
beta = Math.min(beta, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/net/woodyfolsom/msproj/policy/GameTreeNode.java
Normal file
60
src/net/woodyfolsom/msproj/policy/GameTreeNode.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
|
public abstract class GameTreeNode {
|
||||||
|
private GameConfig gameConfig;
|
||||||
|
private GameState gameState;
|
||||||
|
private GameTreeNode parent;
|
||||||
|
private Map<String, GameTreeNode> children = new HashMap<String, GameTreeNode>();
|
||||||
|
private String player;
|
||||||
|
|
||||||
|
public GameTreeNode(GameConfig gameConfig, GameState gameState,
|
||||||
|
String player) {
|
||||||
|
this.gameConfig = gameConfig;
|
||||||
|
this.gameState = gameState;
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(String action, GameTreeNode child) {
|
||||||
|
children.put(action, child);
|
||||||
|
child.parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getActions() {
|
||||||
|
return children.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameTreeNode getChild(String action) {
|
||||||
|
return children.get(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChildrenSize() {
|
||||||
|
return children.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameConfig getGameConfig() {
|
||||||
|
return gameConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameState getGameState() {
|
||||||
|
return gameState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameTreeNode getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRoot() {
|
||||||
|
return parent == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +1,30 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
//import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameScore;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.StateEvaluator;
|
||||||
|
|
||||||
import cs6601.p1.GameConfig;
|
//import org.apache.log4j.Logger;
|
||||||
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());
|
public class Minimax implements Policy {
|
||||||
|
//private static final Logger LOGGER = Logger.getLogger(Minimax.class.getName());
|
||||||
//private static final Logger LOGGER = Logger
|
|
||||||
// .getLogger(MonteCarloMoveGenerator.class.getName());
|
|
||||||
|
|
||||||
private static final int DEFAULT_RECURSIVE_PLAYS = 1;
|
private static final int DEFAULT_RECURSIVE_PLAYS = 1;
|
||||||
|
|
||||||
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
|
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String genMove(GameConfig gameConfig, GameState gameState,
|
public String getAction(GameConfig gameConfig, GameState gameState,
|
||||||
String color) {
|
String color) {
|
||||||
MoveCandidate moveCandidate = findBestMinimaxResult(
|
MoveCandidate moveCandidate = findBestMinimaxResult(
|
||||||
DEFAULT_RECURSIVE_PLAYS * 2,
|
DEFAULT_RECURSIVE_PLAYS * 2,
|
||||||
gameConfig, gameState, color, false, PASS);
|
gameConfig, gameState, color, false, Policy.PASS);
|
||||||
|
|
||||||
return moveCandidate.move;
|
return moveCandidate.move;
|
||||||
}
|
}
|
||||||
@@ -40,8 +38,8 @@ public class MinimaxMoveGenerator implements MoveGenerator {
|
|||||||
|
|
||||||
String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
|
String colorPlaying = getColorToPlay(initialColor, playAsOpponent);
|
||||||
|
|
||||||
List<String> validMoves = validMoveGenerator.genMoves(gameConfig,
|
List<String> validMoves = validMoveGenerator.getActions(gameConfig,
|
||||||
gameState, colorPlaying, MoveGenerator.ALL_MOVES);
|
gameState, colorPlaying, ActionGenerator.ALL_ACTIONS);
|
||||||
|
|
||||||
for (String randomMove : validMoves) {
|
for (String randomMove : validMoves) {
|
||||||
GameState stateCopy = new GameState(gameState);
|
GameState stateCopy = new GameState(gameState);
|
||||||
@@ -71,6 +69,7 @@ public class MinimaxMoveGenerator implements MoveGenerator {
|
|||||||
// to make.
|
// to make.
|
||||||
// If evaluating an opponent's move, the best move (for my opponent) is
|
// If evaluating an opponent's move, the best move (for my opponent) is
|
||||||
// my previous move which gives the opponent the highest score.
|
// my previous move which gives the opponent the highest score.
|
||||||
|
// This should only happen if recursionLevels is initially odd.
|
||||||
if (playAsOpponent) {
|
if (playAsOpponent) {
|
||||||
return new MoveCandidate(bestPrevMove, bestMove.score);
|
return new MoveCandidate(bestPrevMove, bestMove.score);
|
||||||
} else { // if evaluating my own move, the move which gives me the
|
} else { // if evaluating my own move, the move which gives me the
|
||||||
@@ -93,15 +92,4 @@ public class MinimaxMoveGenerator implements MoveGenerator {
|
|||||||
return color;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
61
src/net/woodyfolsom/msproj/policy/MonteCarlo.java
Normal file
61
src/net/woodyfolsom/msproj/policy/MonteCarlo.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
|
public abstract class MonteCarlo implements Policy {
|
||||||
|
protected Policy movePolicy;
|
||||||
|
protected long searchTimeLimit;
|
||||||
|
|
||||||
|
public MonteCarlo(Policy movePolicy, long searchTimeLimit) {
|
||||||
|
this.movePolicy = movePolicy;
|
||||||
|
this.searchTimeLimit = searchTimeLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract MonteCarloTreeNode descend(MonteCarloTreeNode node);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAction(GameConfig gameConfig, GameState gameState,
|
||||||
|
String initialColor) {
|
||||||
|
long initialTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
//If for some reason no moves are evaluated within the time limit, pass.
|
||||||
|
//Note that this may lose the game by forfeit even when picking any random move could
|
||||||
|
//result in a win.
|
||||||
|
|
||||||
|
String bestMove = Policy.PASS;
|
||||||
|
|
||||||
|
MonteCarloTreeNode rootNode = new MonteCarloTreeNode(gameConfig, gameState, initialColor);
|
||||||
|
MonteCarloTreeNode selectedNode;
|
||||||
|
while (System.currentTimeMillis() - initialTime < searchTimeLimit) {
|
||||||
|
//TODO these return types may need to be lists for some MC methods
|
||||||
|
selectedNode = descend(rootNode);
|
||||||
|
selectedNode = grow(selectedNode);
|
||||||
|
int reward = rollout(selectedNode);
|
||||||
|
update(selectedNode, reward);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract MonteCarloTreeNode grow(MonteCarloTreeNode node);
|
||||||
|
|
||||||
|
public abstract int rollout(MonteCarloTreeNode node);
|
||||||
|
|
||||||
|
public abstract void update(MonteCarloTreeNode node, int reward);
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/net/woodyfolsom/msproj/policy/MonteCarloTreeNode.java
Normal file
30
src/net/woodyfolsom/msproj/policy/MonteCarloTreeNode.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
|
public class MonteCarloTreeNode extends GameTreeNode {
|
||||||
|
private int numVisits;
|
||||||
|
private int numWins;
|
||||||
|
|
||||||
|
public MonteCarloTreeNode(GameConfig gameConfig, GameState gameState,
|
||||||
|
String player) {
|
||||||
|
super(gameConfig, gameState, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumVisits() {
|
||||||
|
return numVisits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumVisits(int numVisits) {
|
||||||
|
this.numVisits = numVisits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumWins() {
|
||||||
|
return numWins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumWins(int numWins) {
|
||||||
|
this.numWins = numWins;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/net/woodyfolsom/msproj/policy/MonteCarloUCT.java
Normal file
34
src/net/woodyfolsom/msproj/policy/MonteCarloUCT.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
|
||||||
|
public class MonteCarloUCT extends MonteCarlo {
|
||||||
|
|
||||||
|
public MonteCarloUCT(Policy movePolicy, long searchTimeLimit) {
|
||||||
|
super(movePolicy, searchTimeLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MonteCarloTreeNode descend(MonteCarloTreeNode node) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MonteCarloTreeNode grow(MonteCarloTreeNode node) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int rollout(MonteCarloTreeNode node) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(MonteCarloTreeNode node, int reward) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import cs6601.p1.GameScore;
|
import net.woodyfolsom.msproj.GameScore;
|
||||||
|
|
||||||
public class MoveCandidate {
|
public class MoveCandidate {
|
||||||
public final String move;
|
public final String move;
|
||||||
10
src/net/woodyfolsom/msproj/policy/Policy.java
Normal file
10
src/net/woodyfolsom/msproj/policy/Policy.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
|
|
||||||
|
public interface Policy {
|
||||||
|
static final String PASS = "PASS";
|
||||||
|
public String getAction(GameConfig gameConfig, GameState gameState, String color);
|
||||||
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import cs6601.p1.GameConfig;
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
import cs6601.p1.GameState;
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
public class RandomMoveGenerator implements MoveGenerator {
|
|
||||||
|
public class RandomMovePolicy implements Policy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does NOT modify the gameState.
|
* Does NOT modify the gameState.
|
||||||
*/
|
*/
|
||||||
public String genMove(GameConfig gameConfig, GameState gameState,
|
public String getAction(GameConfig gameConfig, GameState gameState,
|
||||||
String color) {
|
String color) {
|
||||||
GameState gameStateCopy = new GameState(gameState);
|
GameState gameStateCopy = new GameState(gameState);
|
||||||
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
|
List<String> emptyCoordinates = gameStateCopy.getEmptyCoords();
|
||||||
@@ -1,26 +1,18 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
import cs6601.p1.GameConfig;
|
//import org.apache.log4j.Logger;
|
||||||
import cs6601.p1.GameState;
|
|
||||||
|
|
||||||
public class ValidMoveGenerator implements MoveGenerator {
|
public class ValidMoveGenerator implements ActionGenerator {
|
||||||
private static final Logger LOGGER = Logger.getLogger(MinimaxMoveGenerator.class.getName());
|
//private static final Logger LOGGER = Logger.getLogger(ValidMoveGenerator.class.getName());
|
||||||
|
|
||||||
@Override
|
|
||||||
public String genMove(GameConfig gameConfig, GameState gameState,
|
|
||||||
String color) {
|
|
||||||
LOGGER.info("ValidMoveGenerator genMove() stub returning PASS");
|
|
||||||
return PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> genMoves(GameConfig gameConfig, GameState gameState,
|
public List<String> getActions(GameConfig gameConfig, GameState gameState,
|
||||||
String color, int nMoves) {
|
String color, int nMoves) {
|
||||||
|
|
||||||
GameState gameStateCopy = new GameState(gameState);
|
GameState gameStateCopy = new GameState(gameState);
|
||||||
@@ -36,7 +28,7 @@ public class ValidMoveGenerator implements MoveGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (validMoves.size() == 0) {
|
if (validMoves.size() == 0) {
|
||||||
validMoves.add(PASS);
|
validMoves.add(Policy.PASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return validMoves;
|
return validMoves;
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameScore;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.StateEvaluator;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class CaptureTest {
|
public class CaptureTest {
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameScore;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class GameScoreTest {
|
public class GameScoreTest {
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class IllegalMoveTest {
|
public class IllegalMoveTest {
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class LegalMoveTest {
|
public class LegalMoveTest {
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameScore;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.StateEvaluator;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cs6601.p1.generator.AlphaBetaMoveGenerator;
|
|
||||||
import cs6601.p1.generator.MoveGenerator;
|
|
||||||
|
|
||||||
public class StateEvaluatorTest {
|
public class StateEvaluatorTest {
|
||||||
GameConfig gameConfig = new GameConfig();
|
GameConfig gameConfig = new GameConfig();
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
package cs6601.p1;
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.TerritoryMarker;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
17
test/net/woodyfolsom/msproj/ZobristHashGeneratorTest.java
Normal file
17
test/net/woodyfolsom/msproj/ZobristHashGeneratorTest.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package net.woodyfolsom.msproj;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.ZobristHashGenerator;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ZobristHashGeneratorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructor5x5() {
|
||||||
|
ZobristHashGenerator zobHashGen = ZobristHashGenerator.getInstance(5);
|
||||||
|
assertNotNull(zobHashGen);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,53 +1,57 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import org.junit.Test;
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.policy.AlphaBeta;
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
|
||||||
import cs6601.p1.GameBoard;
|
import org.junit.Test;
|
||||||
import cs6601.p1.GameConfig;
|
|
||||||
import cs6601.p1.GameState;
|
|
||||||
|
|
||||||
public class AlphaBetaTest {
|
public class AlphaBetaTest {
|
||||||
@Test
|
@Test
|
||||||
public void testGenmoveAsW() {
|
public void testGenmoveAsW() {
|
||||||
MoveGenerator moveGenerator = new AlphaBetaMoveGenerator();
|
Policy treeSearch = new AlphaBeta();
|
||||||
GameState gameState = new GameState(6);
|
GameState gameState = new GameState(6);
|
||||||
gameState.playStone('A', 2, GameBoard.WHITE_STONE);
|
gameState.playStone('A', 2, GameBoard.WHITE_STONE);
|
||||||
gameState.playStone('B', 1, GameBoard.WHITE_STONE);
|
gameState.playStone('B', 1, GameBoard.WHITE_STONE);
|
||||||
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
|
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
|
||||||
gameState.playStone('B', 2, GameBoard.BLACK_STONE);
|
gameState.playStone('B', 2, GameBoard.BLACK_STONE);
|
||||||
|
|
||||||
String move = moveGenerator.genMove(new GameConfig(), gameState, "b");
|
String move = treeSearch.getAction(new GameConfig(), gameState, "b");
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
|
|
||||||
System.out.println("Generated move: " + move);
|
System.out.println("Generated move: " + move);
|
||||||
assertEquals("Expected B3 but was: " + move, "B3", move);
|
assertEquals("Expected B3 but was: " + move, "B3", move);
|
||||||
gameState.playStone("b", move);
|
gameState.playStone("b", move);
|
||||||
|
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
|
|
||||||
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
|
assertEquals(Policy.PASS,
|
||||||
|
treeSearch.getAction(new GameConfig(), gameState, "?"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenmoveAsB() {
|
public void testGenmoveAsB() {
|
||||||
MoveGenerator moveGenerator = new AlphaBetaMoveGenerator();
|
Policy treeSearch = new AlphaBeta();
|
||||||
GameState gameState = new GameState(6);
|
GameState gameState = new GameState(6);
|
||||||
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
|
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
|
||||||
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
|
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
|
||||||
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
|
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
|
||||||
gameState.playStone('B', 2, GameBoard.WHITE_STONE);
|
gameState.playStone('B', 2, GameBoard.WHITE_STONE);
|
||||||
|
|
||||||
String move = moveGenerator.genMove(new GameConfig(), gameState, "b");
|
String move = treeSearch.getAction(new GameConfig(), gameState, "b");
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
|
|
||||||
System.out.println("Generated move: " + move);
|
System.out.println("Generated move: " + move);
|
||||||
assertEquals("Expected B3 but was: " + move, "B3", move);
|
assertEquals("Expected B3 but was: " + move, "B3", move);
|
||||||
gameState.playStone("b", move);
|
gameState.playStone("b", move);
|
||||||
|
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
|
|
||||||
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
|
assertEquals(Policy.PASS,
|
||||||
|
treeSearch.getAction(new GameConfig(), gameState, "?"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,33 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cs6601.p1.GameBoard;
|
|
||||||
import cs6601.p1.GameConfig;
|
|
||||||
import cs6601.p1.GameState;
|
|
||||||
import cs6601.p1.generator.MoveGenerator;
|
|
||||||
|
|
||||||
public class MinimaxTest {
|
public class MinimaxTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenmove() {
|
public void testGenmove() {
|
||||||
MoveGenerator moveGenerator = new MinimaxMoveGenerator();
|
Policy moveGenerator = new Minimax();
|
||||||
GameState gameState = new GameState(5);
|
GameState gameState = new GameState(5);
|
||||||
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
|
gameState.playStone('A', 2, GameBoard.BLACK_STONE);
|
||||||
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
|
gameState.playStone('B', 1, GameBoard.BLACK_STONE);
|
||||||
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
|
gameState.playStone('C', 2, GameBoard.BLACK_STONE);
|
||||||
gameState.playStone('B', 4, GameBoard.BLACK_STONE);
|
gameState.playStone('B', 4, GameBoard.BLACK_STONE);
|
||||||
|
|
||||||
String move = moveGenerator.genMove(new GameConfig(), gameState, "w");
|
String move = moveGenerator.getAction(new GameConfig(), gameState, "w");
|
||||||
System.out.println("Generated move: " + move);
|
System.out.println("Generated move: " + move);
|
||||||
gameState.playStone("w", move);
|
gameState.playStone("w", move);
|
||||||
|
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
|
|
||||||
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
|
assertEquals(Policy.PASS,moveGenerator.getAction(new GameConfig(), gameState, "?"));
|
||||||
|
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,28 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.policy.Policy;
|
||||||
|
import net.woodyfolsom.msproj.policy.RandomMovePolicy;
|
||||||
|
|
||||||
import org.junit.Test;
|
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 {
|
public class RandomTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenmove() {
|
public void testGenmove() {
|
||||||
MoveGenerator moveGenerator = new RandomMoveGenerator();
|
Policy moveGenerator = new RandomMovePolicy();
|
||||||
GameState gameState = new GameState(5);
|
GameState gameState = new GameState(5);
|
||||||
moveGenerator.genMove(new GameConfig(), gameState, "b");
|
moveGenerator.getAction(new GameConfig(), gameState, "b");
|
||||||
gameState = new GameState(5);
|
gameState = new GameState(5);
|
||||||
moveGenerator.genMove(new GameConfig(), gameState, "w");
|
moveGenerator.getAction(new GameConfig(), gameState, "w");
|
||||||
|
|
||||||
assertEquals(MoveGenerator.PASS,moveGenerator.genMove(new GameConfig(), gameState, "?"));
|
assertEquals(Policy.PASS,moveGenerator.getAction(new GameConfig(), gameState, "?"));
|
||||||
|
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ public class RandomTest {
|
|||||||
assertTrue(gameState.playStone('D', 3, GameBoard.WHITE_STONE));
|
assertTrue(gameState.playStone('D', 3, GameBoard.WHITE_STONE));
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
//This is correct - checked vs. MFOG
|
//This is correct - checked vs. MFOG
|
||||||
assertEquals("B3", new RandomMoveGenerator().genMove(new GameConfig(), gameState, "w"));
|
assertEquals("B3", new RandomMovePolicy().getAction(new GameConfig(), gameState, "w"));
|
||||||
System.out.println(gameState);
|
System.out.println(gameState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
package cs6601.p1.generator;
|
package net.woodyfolsom.msproj.policy;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.woodyfolsom.msproj.GameBoard;
|
||||||
|
import net.woodyfolsom.msproj.GameConfig;
|
||||||
|
import net.woodyfolsom.msproj.GameState;
|
||||||
|
import net.woodyfolsom.msproj.policy.ValidMoveGenerator;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cs6601.p1.GameBoard;
|
|
||||||
import cs6601.p1.GameConfig;
|
|
||||||
import cs6601.p1.GameState;
|
|
||||||
|
|
||||||
public class ValidMoveGeneratorTest {
|
public class ValidMoveGeneratorTest {
|
||||||
|
|
||||||
@@ -30,7 +32,7 @@ public class ValidMoveGeneratorTest {
|
|||||||
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
|
gameState.playStone('C', 2, GameBoard.WHITE_STONE);
|
||||||
assertFalse(gameState.playStone('A', 1, GameBoard.BLACK_STONE));
|
assertFalse(gameState.playStone('A', 1, GameBoard.BLACK_STONE));
|
||||||
|
|
||||||
List<String> validMoves = new ValidMoveGenerator().genMoves(new GameConfig(), gameState, "b",0);
|
List<String> validMoves = new ValidMoveGenerator().getActions(new GameConfig(), gameState, "b",0);
|
||||||
assertTrue(validMoves.size() > 0);
|
assertTrue(validMoves.size() > 0);
|
||||||
for (String vm : validMoves) {
|
for (String vm : validMoves) {
|
||||||
System.out.println(vm);
|
System.out.println(vm);
|
||||||
Reference in New Issue
Block a user