- Reorganized the constructors in Move.java.

- Made the getBoardState method in Referee.java static.
- Created NeuralNetworkPlayer.java, though I don't know how to make the computer use it.
- Updated the Player interface to include passing the PlayerModel. Most of the current com agents ignore the data, but it is now available.
- Updated the train function in PlayerModel.java.
This commit is contained in:
Marshall
2012-04-29 14:19:10 -04:00
parent a756aa242d
commit 60a842d729
10 changed files with 201 additions and 94 deletions

View File

@@ -7,20 +7,20 @@ public class Move {
* Used when genMove() is called but no valid move exists for that player. * Used when genMove() is called but no valid move exists for that player.
*/ */
public static final Move NONE = new Move(TileColor.NONE, -1, -1); public static final Move NONE = new Move(TileColor.NONE, -1, -1);
private final TileColor color; private final TileColor color;
private final CellPointer cp; private final CellPointer cp;
public Move(TileColor color, int row, int column) { public Move(TileColor tlClr, CellPointer cllPntr) {
cp = new CellPointer(row,column);
this.color = color;
}
public Move(CellPointer cllPntr, TileColor tlClr) {
cp = cllPntr; cp = cllPntr;
color = tlClr; color = tlClr;
} }
public Move(TileColor color, int row, int column) {
cp = new CellPointer(row, column);
this.color = color;
}
public CellPointer getCell() { public CellPointer getCell() {
return cp; return cp;
} }
@@ -28,7 +28,7 @@ public class Move {
public TileColor getColor() { public TileColor getColor() {
return color; return color;
} }
@Override @Override
public String toString() { public String toString() {
return "Play " + color + "@" + cp; return "Play " + color + "@" + cp;

View File

@@ -20,12 +20,32 @@ public class Referee implements Runnable {
public static final String PLAYER_TURN = "Waiting for the player's move."; public static final String PLAYER_TURN = "Waiting for the player's move.";
public static boolean[] getBoardState(Board brd) {
boolean[] boardState = new boolean[(Board.NUM_COLS * Board.NUM_ROWS * (Board.TileColor
.values().length - 1))];
int i = 0;
for (int r = 0; r < Board.NUM_ROWS; r++) {
for (int c = 0; c < Board.NUM_COLS; c++) {
boardState[i] = (brd.getTile(r, c) == TileColor.BLUE);
boardState[i + 1] = (brd.getTile(r, c) == TileColor.GREEN);
boardState[i + 2] = (brd.getTile(r, c) == TileColor.RED);
boardState[i + 3] = (brd.getTile(r, c) == TileColor.YELLOW);
i += 4;
}
}
return boardState;
}
private Board board; private Board board;
private BoardPanel boardPanel; private BoardPanel boardPanel;
private final Player computerPlayer;
private final Player computerPlayer;
private final HumanPlayer humanPlayer = new HumanPlayer(); private final HumanPlayer humanPlayer = new HumanPlayer();
private final MainFrame mf; private final MainFrame mf;
private PlayerModel playerModel = null; private PlayerModel playerModel = null;
public Referee(MainFrame mnFrm, String player, Player computerPlayer) { public Referee(MainFrame mnFrm, String player, Player computerPlayer) {
@@ -43,24 +63,6 @@ public class Referee implements Runnable {
initGame(); initGame();
} }
public boolean[] getBoardState() {
boolean[] boardState = new boolean[getPlayerModel().getNumInputNodes()];
int i = 0;
for (int r = 0; r < Board.NUM_ROWS; r++) {
for (int c = 0; c < Board.NUM_COLS; c++) {
boardState[i] = (board.getTile(r, c) == TileColor.BLUE);
boardState[i + 1] = (board.getTile(r, c) == TileColor.GREEN);
boardState[i + 2] = (board.getTile(r, c) == TileColor.RED);
boardState[i + 3] = (board.getTile(r, c) == TileColor.YELLOW);
i += 4;
}
}
return boardState;
}
public Player getComputerPlayer() { public Player getComputerPlayer() {
return computerPlayer; return computerPlayer;
} }
@@ -162,26 +164,30 @@ public class Referee implements Runnable {
System.out System.out
.println("Interrupted while waiting for human to move!"); .println("Interrupted while waiting for human to move!");
} else { } else {
Move mv = humanPlayer.getMove(board); Move mv = humanPlayer.getMove(board, playerModel);
if (board.getTile(mv.getCell().r, mv.getCell().c) == TileColor.NONE) { if (board.getTile(mv.getCell().r, mv.getCell().c) == TileColor.NONE) {
playToken(humanPlayer.getMove(board)); playToken(humanPlayer.getMove(board, playerModel));
getPlayerModel().train(getMoveArray(mv)); getPlayerModel().train(getBoardState(board),
getMoveArray(mv));
} else { } else {
humanPlayer.denyMove(); humanPlayer.denyMove();
} }
} }
} else { } else {
Move mv = computerPlayer.getMove(board); Move mv = computerPlayer.getMove(board, getPlayerModel());
playToken(mv); playToken(mv);
// TODO
// This is the call that gets a prediction of a user's move. // This is the call that gets a prediction of a user's move.
// Some changes will probably be necessary to put it in the // Some changes will probably be necessary to put it in the
// right place and also to get the node weights. But... all in // right place and also to get the node weights. But... all in
// due time. // due time.
getPlayerModel().getOutputNodes(getBoardState()); // UPDATE: I made a neural network agent. This call is in there
// now. If the current agent doesn't use the neural network,
// then this doesn't need to be called. It will just train on
// random, meaningless data.
// getPlayerModel().getOutputNodes(getBoardState(board));
} }
mf.updateMessage(getMessage()); mf.updateMessage(getMessage());

View File

@@ -4,25 +4,26 @@ import model.Board;
import model.Move; import model.Move;
import model.comPlayer.generator.AlphaBetaMoveGenerator; import model.comPlayer.generator.AlphaBetaMoveGenerator;
import model.comPlayer.generator.MoveGenerator; import model.comPlayer.generator.MoveGenerator;
import model.playerModel.PlayerModel;
public class AlphaBetaComPlayer implements Player { public class AlphaBetaComPlayer implements Player {
private MoveGenerator moveGenerator = new AlphaBetaMoveGenerator(); private final MoveGenerator moveGenerator = new AlphaBetaMoveGenerator();
@Override
public Move getMove(Board board) {
return moveGenerator.genMove(board, false);
}
@Override @Override
public void denyMove() { public void denyMove() {
throw new UnsupportedOperationException("Not implemented"); throw new UnsupportedOperationException("Not implemented");
} }
@Override
public Move getMove(Board board, PlayerModel player) {
return moveGenerator.genMove(board, false);
}
@Override @Override
public boolean isReady() { public boolean isReady() {
return true; // always ready to play a random valid move return true; // always ready to play a random valid move
} }
@Override @Override
public String toString() { public String toString() {
return "Alpha-Beta ComPlayer"; return "Alpha-Beta ComPlayer";

View File

@@ -1,32 +1,16 @@
package model.comPlayer; package model.comPlayer;
import model.Board; import model.Board;
import model.Board.TileColor;
import model.CellPointer; import model.CellPointer;
import model.Move; import model.Move;
import model.Board.TileColor; import model.playerModel.PlayerModel;
public class HumanPlayer implements Player { public class HumanPlayer implements Player {
private CellPointer cell = CellPointer.NONE; private CellPointer cell = CellPointer.NONE;
private boolean ready = false;
private TileColor color = TileColor.BLUE; private TileColor color = TileColor.BLUE;
private boolean ready = false;
@Override
public void denyMove() {
ready = false;
}
public TileColor getColor() {
return color;
}
@Override
public Move getMove(Board board) {
ready = false;
return new Move(cell, color);
}
public void decrementColor() { public void decrementColor() {
TileColor[] colors = Board.TileColor.values(); TileColor[] colors = Board.TileColor.values();
@@ -47,6 +31,22 @@ public class HumanPlayer implements Player {
color = colors[currentColor]; color = colors[currentColor];
} }
@Override
public void denyMove() {
ready = false;
}
public TileColor getColor() {
return color;
}
@Override
public Move getMove(Board board, PlayerModel player) {
ready = false;
return new Move(color, cell);
}
public void incrementColor() { public void incrementColor() {
TileColor[] colors = Board.TileColor.values(); TileColor[] colors = Board.TileColor.values();
int currentColor = -1; int currentColor = -1;

View File

@@ -4,29 +4,30 @@ import model.Board;
import model.Move; import model.Move;
import model.comPlayer.generator.MinimaxMoveGenerator; import model.comPlayer.generator.MinimaxMoveGenerator;
import model.comPlayer.generator.MoveGenerator; import model.comPlayer.generator.MoveGenerator;
import model.playerModel.PlayerModel;
public class MinimaxComPlayer implements Player{ public class MinimaxComPlayer implements Player {
private MoveGenerator moveGenerator = new MinimaxMoveGenerator(); private final MoveGenerator moveGenerator = new MinimaxMoveGenerator();
public MinimaxComPlayer() { public MinimaxComPlayer() {
super(); super();
} }
@Override
public Move getMove(Board board) {
return moveGenerator.genMove(board, false);
}
@Override @Override
public void denyMove() { public void denyMove() {
throw new UnsupportedOperationException("Not implemented"); throw new UnsupportedOperationException("Not implemented");
} }
@Override
public Move getMove(Board board, PlayerModel player) {
return moveGenerator.genMove(board, false);
}
@Override @Override
public boolean isReady() { public boolean isReady() {
return true; // always ready to play a random valid move return true; // always ready to play a random valid move
} }
@Override @Override
public String toString() { public String toString() {
return "Minimax ComPlayer"; return "Minimax ComPlayer";

View File

@@ -3,19 +3,24 @@ package model.comPlayer;
import java.util.Random; import java.util.Random;
import model.Board; import model.Board;
import model.Board.TileColor;
import model.CellPointer; import model.CellPointer;
import model.Move; import model.Move;
import model.Board.TileColor; import model.playerModel.PlayerModel;
public class MonteCarloComPlayer implements Player { public class MonteCarloComPlayer implements Player {
private final Random rand = new Random(); private final Random rand = new Random();
@Override @Override
public Move getMove(Board board) { public void denyMove() {
return getRandomMove(board,true); throw new UnsupportedOperationException("Not implemented");
} }
@Override
public Move getMove(Board board, PlayerModel player) {
return getRandomMove(board, true);
}
public Move getRandomMove(Board board, boolean isCompTurn) { public Move getRandomMove(Board board, boolean isCompTurn) {
TileColor tile = TileColor.BLUE; TileColor tile = TileColor.BLUE;
int r = -1; int r = -1;
@@ -42,19 +47,14 @@ public class MonteCarloComPlayer implements Player {
tile = TileColor.YELLOW; tile = TileColor.YELLOW;
} }
return new Move(new CellPointer(r, c), tile); return new Move(tile, new CellPointer(r, c));
}
@Override
public void denyMove() {
throw new UnsupportedOperationException("Not implemented");
} }
@Override @Override
public boolean isReady() { public boolean isReady() {
return true; // always ready to play a random valid move return true; // always ready to play a random valid move
} }
@Override @Override
public String toString() { public String toString() {
return "Monte Carlo ComPlayer"; return "Monte Carlo ComPlayer";

View File

@@ -0,0 +1,91 @@
package model.comPlayer;
import model.Board;
import model.Board.TileColor;
import model.Move;
import model.Referee;
import model.playerModel.Node;
import model.playerModel.PlayerModel;
public class NeuralNetworkPlayer implements Player {
public static int getSmallest(double[] list) {
int index = 0;
for (int i = 0; i < list.length; i++) {
if (list[index] < list[i]) {
index = i;
}
}
return index;
}
@Override
public void denyMove() {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public Move getMove(Board board, PlayerModel player) {
Move mv = null;
Node[] nodes = player.getOutputNodes(Referee.getBoardState(board));
TileColor color = TileColor.BLUE;
double[] colorStrengths = new double[4];
colorStrengths[0] = nodes[0].strength();
colorStrengths[1] = nodes[1].strength();
colorStrengths[2] = nodes[2].strength();
colorStrengths[3] = nodes[3].strength();
switch (getSmallest(colorStrengths)) {
case 1:
color = TileColor.GREEN;
break;
case 2:
color = TileColor.RED;
break;
case 3:
color = TileColor.YELLOW;
break;
case 0:
default:
color = TileColor.BLUE;
}
int index = 4;
for (int i = 4; i < nodes.length; i++) {
if (nodes[i].strength() > nodes[index].strength()) {
index = i;
}
}
int i = 4;
loop: for (int r = 0; r < Board.NUM_ROWS; r++) {
for (int c = 0; c < Board.NUM_COLS; c++) {
if (i == index) {
mv = new Move(color, r, c);
break loop;
}
else {
i++;
}
}
}
return mv;
}
@Override
public boolean isReady() {
return true;
}
@Override
public String toString() {
return "Neural Network Player";
}
}

View File

@@ -2,21 +2,27 @@ package model.comPlayer;
import model.Board; import model.Board;
import model.Move; import model.Move;
import model.playerModel.PlayerModel;
public interface Player { public interface Player {
/** /**
* Instructs the Player to retract the last move. * Instructs the Player to retract the last move.
*/ */
public void denyMove(); public void denyMove();
/** /**
* Gets the player's move given the board state. Blocks if player is not ready. * Gets the player's move given the board state. Blocks if player is not
* ready.
*
* @param board * @param board
* @return * @return
*/ */
public Move getMove(Board board); public Move getMove(Board board, PlayerModel player);
/** /**
* Returns true if the player is ready to make a move (getMove() does not block). * Returns true if the player is ready to make a move (getMove() does not
* block).
*
* @return * @return
*/ */
public boolean isReady(); public boolean isReady();

View File

@@ -3,16 +3,21 @@ package model.comPlayer;
import java.util.Random; import java.util.Random;
import model.Board; import model.Board;
import model.Board.TileColor;
import model.CellPointer; import model.CellPointer;
import model.Move; import model.Move;
import model.Board.TileColor; import model.playerModel.PlayerModel;
public class RandomComPlayer implements Player { public class RandomComPlayer implements Player {
private final Random rand = new Random(); private final Random rand = new Random();
@Override @Override
public Move getMove(Board board) { public void denyMove() {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public Move getMove(Board board, PlayerModel player) {
TileColor tile = TileColor.BLUE; TileColor tile = TileColor.BLUE;
int r = -1; int r = -1;
int c = -1; int c = -1;
@@ -38,19 +43,14 @@ public class RandomComPlayer implements Player {
tile = TileColor.YELLOW; tile = TileColor.YELLOW;
} }
return new Move(new CellPointer(r, c), tile); return new Move(tile, new CellPointer(r, c));
}
@Override
public void denyMove() {
throw new UnsupportedOperationException("Not implemented");
} }
@Override @Override
public boolean isReady() { public boolean isReady() {
return true; // always ready to play a random valid move return true; // always ready to play a random valid move
} }
@Override @Override
public String toString() { public String toString() {
return "Random ComPlayer"; return "Random ComPlayer";

View File

@@ -203,7 +203,9 @@ public class PlayerModel implements Serializable {
} }
} }
public void train(boolean[] example) { public void train(boolean[] boardState, boolean[] example) {
getOutputNodes(boardState);
if (example.length == outputNode.length) { if (example.length == outputNode.length) {
for (int i = 0; i < outputNode.length; i++) { for (int i = 0; i < outputNode.length; i++) {
outputNode[i].learn(example[i]); outputNode[i].learn(example[i]);