- Created a new ComPlayer. It targets a particular score, and does it pretty well. It does this by counting the number of empty spaces and the number of empty spaces that can potentially be created by removing tiles then either playing to block or playing to extend the game.
- Created DumpResults.java, which simply outputs all scores in a player model so that they can be graphed.
This commit is contained in:
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
299
src/model/comPlayer/CountingPlayer.java
Normal file
299
src/model/comPlayer/CountingPlayer.java
Normal file
@@ -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<CellPointer> remove = new ArrayList<CellPointer>();
|
||||
ArrayList<CellPointer> hold;
|
||||
CellPointer cp = m.getCell();
|
||||
TileColor tile = m.getColor();
|
||||
int count;
|
||||
|
||||
// Check up-and-down.
|
||||
count = 1;
|
||||
hold = new ArrayList<CellPointer>();
|
||||
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<CellPointer>();
|
||||
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<Move> 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<Move> choices = new ArrayList<Move>();
|
||||
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<Move> choices = new ArrayList<Move>();
|
||||
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<Move>();
|
||||
}
|
||||
|
||||
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<Move> getLegalMoves(Board board) {
|
||||
ArrayList<Move> moves = new ArrayList<Move>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -144,6 +144,13 @@ public class PlayerModel implements Serializable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ArrayList<GameLog> 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]);
|
||||
|
||||
@@ -37,7 +37,7 @@ public class SigmoidNode implements Node {
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(strength());
|
||||
// System.out.println(strength());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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);
|
||||
|
||||
35
test/model/DumpResults.java
Normal file
35
test/model/DumpResults.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user