diff --git a/src/model/Board.java b/src/model/Board.java index c86c246..bb91d0e 100644 --- a/src/model/Board.java +++ b/src/model/Board.java @@ -117,6 +117,19 @@ public class Board { return true; } + public int getEmptySpaces() { + int count = 0; + for (int r = 0; r < NUM_ROWS; r++) { + for (int c = 0; c < NUM_COLS; c++) { + if (getTile(r, c) == TileColor.NONE) { + count++; + } + } + } + + return count; + } + public TileColor getTile(int r, int c) { return board[r][c]; } diff --git a/src/model/Referee.java b/src/model/Referee.java index e192ef7..b1efdf1 100644 --- a/src/model/Referee.java +++ b/src/model/Referee.java @@ -3,6 +3,7 @@ package model; import model.Board.TileColor; import model.comPlayer.HumanPlayer; import model.comPlayer.Player; +import model.playerModel.GameGoal; import model.playerModel.PlayerModel; import org.apache.log4j.Logger; @@ -102,6 +103,9 @@ public class Referee implements Runnable { while (true) { initGame(); mf.updateBoard(); + GameGoal goal = playerModel.getTargetScore(); + System.out.println("Target: " + goal.getTargetScore()); + computerPlayer.setGameGoal(goal); play(); getPlayerModel().logGame(getPlayerScore()); diff --git a/src/model/comPlayer/CountingPlayer.java b/src/model/comPlayer/CountingPlayer.java new file mode 100644 index 0000000..201b91c --- /dev/null +++ b/src/model/comPlayer/CountingPlayer.java @@ -0,0 +1,299 @@ +package model.comPlayer; + +import java.util.ArrayList; + +import model.Board; +import model.Board.TileColor; +import model.CellPointer; +import model.Move; +import model.playerModel.GameGoal; +import model.playerModel.PlayerModel; + +public class CountingPlayer implements Player { + + private static boolean causesConnection(Board board, Move m) { + + CellPointer cell = m.getCell(); + TileColor color = m.getColor(); + boolean causes = false; + + boolean oneUBlank = (cell.r - 1 >= 0) + && board.getTile(cell.r - 1, cell.c) == TileColor.NONE; + boolean oneUColor = (cell.r - 1 >= 0) + && board.getTile(cell.r - 1, cell.c) == color; + boolean twoUBlank = (cell.r - 2 >= 0) + && board.getTile(cell.r - 2, cell.c) == TileColor.NONE; + boolean twoUColor = (cell.r - 2 >= 0) + && board.getTile(cell.r - 2, cell.c) == color; + + boolean oneDBlank = (cell.r + 1 < Board.NUM_ROWS) + && board.getTile(cell.r + 1, cell.c) == TileColor.NONE; + boolean oneDColor = (cell.r + 1 < Board.NUM_ROWS) + && board.getTile(cell.r + 1, cell.c) == color; + boolean twoDBlank = (cell.r + 2 < Board.NUM_ROWS) + && board.getTile(cell.r + 2, cell.c) == TileColor.NONE; + boolean twoDColor = (cell.r + 2 < Board.NUM_ROWS) + && board.getTile(cell.r + 2, cell.c) == color; + + boolean oneLBlank = (cell.c - 1 >= 0) + && board.getTile(cell.r, cell.c - 1) == TileColor.NONE; + boolean oneLColor = (cell.c - 1 >= 0) + && board.getTile(cell.r, cell.c - 1) == color; + boolean twoLBlank = (cell.c - 2 >= 0) + && board.getTile(cell.r, cell.c - 2) == TileColor.NONE; + boolean twoLColor = (cell.c - 2 >= 0) + && board.getTile(cell.r, cell.c - 2) == color; + + boolean oneRBlank = (cell.c + 1 < Board.NUM_ROWS) + && board.getTile(cell.r, cell.c + 1) == TileColor.NONE; + boolean oneRColor = (cell.c + 1 < Board.NUM_ROWS) + && board.getTile(cell.r, cell.c + 1) == color; + boolean twoRBlank = (cell.c + 2 < Board.NUM_ROWS) + && board.getTile(cell.r, cell.c + 2) == TileColor.NONE; + boolean twoRColor = (cell.c + 2 < Board.NUM_ROWS) + && board.getTile(cell.r, cell.c + 2) == color; + + causes = (oneUBlank && twoUColor) || (oneDBlank && twoDColor) + || (oneLBlank && twoLColor) || (oneRBlank && twoRColor) + || (oneDColor && (oneUBlank || twoDBlank)) + || (oneUColor && (oneDBlank || twoUBlank)) + || (oneLColor && (oneRBlank || twoLBlank)) + || (oneRColor && (oneLBlank || twoRBlank)); + + return causes; + } + + private static boolean updateRemovals(Board board, Move m, + boolean[][] canBeRemoved) { + ArrayList remove = new ArrayList(); + ArrayList hold; + CellPointer cp = m.getCell(); + TileColor tile = m.getColor(); + int count; + + // Check up-and-down. + count = 1; + hold = new ArrayList(); + loop: for (int row = cp.r - 1; row >= 0; row--) { + if (board.getTile(row, cp.c) == tile) { + hold.add(new CellPointer(row, cp.c)); + count++; + } else { + break loop; + } + } + + loop: for (int row = cp.r + 1; row < Board.NUM_ROWS; row++) { + if (board.getTile(row, cp.c) == tile) { + hold.add(new CellPointer(row, cp.c)); + count++; + } else { + break loop; + } + } + + if (count >= Board.ROW_REMOVAL_SIZE) { + remove.addAll(hold); + } + + // Check left-and-right. + count = 1; + hold = new ArrayList(); + loop: for (int col = cp.c - 1; col >= 0; col--) { + if (board.getTile(cp.r, col) == tile) { + hold.add(new CellPointer(cp.r, col)); + count++; + } else { + break loop; + } + } + + loop: for (int col = cp.c + 1; col < Board.NUM_COLS; col++) { + if (board.getTile(cp.r, col) == tile) { + hold.add(new CellPointer(cp.r, col)); + count++; + } else { + break loop; + } + } + + if (count >= Board.ROW_REMOVAL_SIZE) { + remove.addAll(hold); + } + + for (CellPointer c : remove) { + canBeRemoved[c.r][c.c] = true; + } + + canBeRemoved[cp.r][cp.c] = false; + + return (remove.size() > 0); + } + + GameGoal goal = null; + + @Override + public void denyMove() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public Move getMove(Board board, PlayerModel player) { + int remainingPlays = goal.getTargetScore() - board.getTurn(); + int prediction = board.getEmptySpaces(); + + ArrayList moves = getLegalMoves(board); + Object[] movesHolder; + + boolean[][] canBeRemoved = new boolean[Board.NUM_ROWS][Board.NUM_COLS]; + int[][] blockScore = new int[Board.NUM_ROWS][Board.NUM_COLS]; + + for (int r = 0; r < canBeRemoved.length; r++) { + for (int c = 0; c < canBeRemoved[0].length; c++) { + canBeRemoved[r][c] = false; + blockScore[r][c] = 0; + } + } + + movesHolder = moves.toArray(); + Move m; + for (Object o : movesHolder) { + m = (Move) o; + if (updateRemovals(board, m, canBeRemoved)) { + // blockScore[m.getCell().r][m.getCell().c] += + // !causesConnection( + // board, m) ? 2 : 1; + blockScore[m.getCell().r][m.getCell().c] += 2; + moves.remove(m); + } + + else if (causesConnection(board, m) + && (remainingPlays <= prediction)) { + moves.remove(m); + } + + else if (causesConnection(board, m)) { + blockScore[m.getCell().r][m.getCell().c]++; + } + + // else if (!causesConnection(board, m)) { + // blockScore[m.getCell().r][m.getCell().c]++; + // } + } + + for (int r = 0; r < canBeRemoved.length; r++) { + for (int c = 0; c < canBeRemoved[0].length; c++) { + prediction += (canBeRemoved[r][c]) ? 1 : 0; + } + } + + // Okay. So now... + // We have a number of plays we WANT to go to and a number of plays we + // EXPECT to go to. If we want to go to a greater number than we expect + // to, then we should pick a crap move. If we want to go to a smaller or + // equal number, we should block. + + if (moves.size() == 0) { + moves = getLegalMoves(board); + return moves.get(PlayerModel.rand.nextInt(moves.size())); + } + + else if (remainingPlays > prediction) { + for (int i = 0;; i++) { + ArrayList choices = new ArrayList(); + for (int r = 0; r < Board.NUM_ROWS; r++) { + for (int c = 0; c < Board.NUM_COLS; c++) { + if (blockScore[r][c] == i) { + for (Move mv : moves) { + if (mv.getCell().r == r && mv.getCell().c == c) { + choices.add(mv); + } + } + } + } + } + + if (choices.size() > 0) { + return choices + .get(PlayerModel.rand.nextInt(choices.size())); + } + } + } + + else { + int bestScore; + while (true) { + bestScore = 0; + ArrayList choices = new ArrayList(); + for (int r = 0; r < Board.NUM_ROWS; r++) { + for (int c = 0; c < Board.NUM_COLS; c++) { + if (blockScore[r][c] > bestScore) { + bestScore = blockScore[r][c]; + choices = new ArrayList(); + } + + if (blockScore[r][c] == bestScore) { + movesHolder = moves.toArray(); + + for (Object o : movesHolder) { + m = (Move) o; + if (m.getCell().r == r && m.getCell().c == c) { + choices.add(m); + moves.remove(m); + } + } + } + + } + } + + if (choices.size() > 0) { + return choices + .get(PlayerModel.rand.nextInt(choices.size())); + } + + else { + for (int r = 0; r < Board.NUM_ROWS; r++) { + for (int c = 0; c < Board.NUM_COLS; c++) { + if (blockScore[r][c] == bestScore) { + blockScore[r][c] = 0; + } + } + } + } + } + } + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setGameGoal(GameGoal target) { + goal = target; + } + + @Override + public String toString() { + return "Counting ComPlayer"; + } + + private ArrayList getLegalMoves(Board board) { + ArrayList moves = new ArrayList(); + + for (int r = 0; r < Board.NUM_ROWS; r++) { + for (int c = 0; c < Board.NUM_COLS; c++) { + if (Board.isLegal(board, new CellPointer(r, c))) { + moves.add(new Move(TileColor.BLUE, r, c)); + moves.add(new Move(TileColor.GREEN, r, c)); + moves.add(new Move(TileColor.RED, r, c)); + moves.add(new Move(TileColor.YELLOW, r, c)); + } + } + } + + return moves; + } +} diff --git a/src/model/playerModel/PlayerModel.java b/src/model/playerModel/PlayerModel.java index 22240ca..c35d988 100644 --- a/src/model/playerModel/PlayerModel.java +++ b/src/model/playerModel/PlayerModel.java @@ -144,6 +144,13 @@ public class PlayerModel implements Serializable { return null; } + public ArrayList getScores() { + GameLog.SORT_BY_SCORE = false; + Collections.sort(scores); + + return scores; + } + public GameGoal getTargetScore() { GameGoal goal; int targetScore; @@ -210,7 +217,7 @@ public class PlayerModel implements Serializable { public void train(boolean[] example) { boolean[] hold = getOutputActivations(); - System.out.println("TRAIN"); + // System.out.println("TRAIN"); if (example.length == outputNode.length) { for (int i = 0; i < outputNode.length; i++) { outputNode[i].learn(example[i] == hold[i]); diff --git a/src/model/playerModel/node/SigmoidNode.java b/src/model/playerModel/node/SigmoidNode.java index 60af9f9..ce78c51 100644 --- a/src/model/playerModel/node/SigmoidNode.java +++ b/src/model/playerModel/node/SigmoidNode.java @@ -37,7 +37,7 @@ public class SigmoidNode implements Node { } } - System.out.println(strength()); + // System.out.println(strength()); } @Override diff --git a/src/view/ParsedArgs.java b/src/view/ParsedArgs.java index e42ed91..dcdeac5 100644 --- a/src/view/ParsedArgs.java +++ b/src/view/ParsedArgs.java @@ -2,6 +2,7 @@ package view; import model.comPlayer.AlphaBetaComPlayer; import model.comPlayer.ComboPlayer; +import model.comPlayer.CountingPlayer; import model.comPlayer.MinimaxComPlayer; import model.comPlayer.MonteCarloComPlayer; import model.comPlayer.NeuralNetworkPlayer; @@ -12,6 +13,7 @@ public class ParsedArgs { public static final String COM_ALPHABETA = "ALPHABETA"; public static final String COM_ANN = "NEURALNET"; public static final String COM_COMBO = "COMBO"; + public static final String COM_COUNTING = "COUNTING"; public static final String COM_DEFAULT = COM_ALPHABETA; public static final String COM_MINIMAX = "MINIMAX"; public static final String COM_MONTECARLO = "MONTECARLO"; @@ -32,6 +34,8 @@ public class ParsedArgs { return new NeuralNetworkPlayer(); } else if (COM_COMBO.equalsIgnoreCase(comPlayer)) { return new ComboPlayer(); + } else if (COM_COUNTING.equalsIgnoreCase(comPlayer)) { + return new CountingPlayer(); } else { System.out.println("Unrecognized comPlayer '" + comPlayer + "', using default: " + COM_DEFAULT); diff --git a/test/model/DumpResults.java b/test/model/DumpResults.java new file mode 100644 index 0000000..e7f197e --- /dev/null +++ b/test/model/DumpResults.java @@ -0,0 +1,35 @@ +package model; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.ObjectInputStream; + +import model.playerModel.GameLog; +import model.playerModel.PlayerModel; + +public class DumpResults { + public static void main(String[] args) { + FileInputStream fin = null; + ObjectInputStream oin = null; + + try { + fin = new FileInputStream(PlayerModel.getPlayerPath("marshall")); + + oin = new ObjectInputStream(fin); + PlayerModel pm = (PlayerModel) oin.readObject(); + oin.close(); + + for (GameLog gl : pm.getScores()) { + System.out.println(gl.getScore()); + } + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +}