diff --git a/src/model/Move.java b/src/model/Move.java index 6445bf2..ef629eb 100644 --- a/src/model/Move.java +++ b/src/model/Move.java @@ -7,20 +7,20 @@ public class Move { * Used when genMove() is called but no valid move exists for that player. */ public static final Move NONE = new Move(TileColor.NONE, -1, -1); - + private final TileColor color; private final CellPointer cp; - public Move(TileColor color, int row, int column) { - cp = new CellPointer(row,column); - this.color = color; - } - - public Move(CellPointer cllPntr, TileColor tlClr) { + public Move(TileColor tlClr, CellPointer cllPntr) { cp = cllPntr; color = tlClr; } + public Move(TileColor color, int row, int column) { + cp = new CellPointer(row, column); + this.color = color; + } + public CellPointer getCell() { return cp; } @@ -28,7 +28,7 @@ public class Move { public TileColor getColor() { return color; } - + @Override public String toString() { return "Play " + color + "@" + cp; diff --git a/src/model/Referee.java b/src/model/Referee.java index 16338b5..1735f92 100644 --- a/src/model/Referee.java +++ b/src/model/Referee.java @@ -20,12 +20,32 @@ public class Referee implements Runnable { 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 BoardPanel boardPanel; - private final Player computerPlayer; + private final Player computerPlayer; private final HumanPlayer humanPlayer = new HumanPlayer(); private final MainFrame mf; + private PlayerModel playerModel = null; public Referee(MainFrame mnFrm, String player, Player computerPlayer) { @@ -43,24 +63,6 @@ public class Referee implements Runnable { 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() { return computerPlayer; } @@ -162,26 +164,30 @@ public class Referee implements Runnable { System.out .println("Interrupted while waiting for human to move!"); } else { - Move mv = humanPlayer.getMove(board); + Move mv = humanPlayer.getMove(board, playerModel); 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 { humanPlayer.denyMove(); } } } else { - Move mv = computerPlayer.getMove(board); + Move mv = computerPlayer.getMove(board, getPlayerModel()); playToken(mv); - // TODO // This is the call that gets a prediction of a user's move. // Some changes will probably be necessary to put it in the // right place and also to get the node weights. But... all in // 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()); diff --git a/src/model/comPlayer/AlphaBetaComPlayer.java b/src/model/comPlayer/AlphaBetaComPlayer.java index 9850684..aee56f7 100644 --- a/src/model/comPlayer/AlphaBetaComPlayer.java +++ b/src/model/comPlayer/AlphaBetaComPlayer.java @@ -4,25 +4,26 @@ import model.Board; import model.Move; import model.comPlayer.generator.AlphaBetaMoveGenerator; import model.comPlayer.generator.MoveGenerator; +import model.playerModel.PlayerModel; public class AlphaBetaComPlayer implements Player { - private MoveGenerator moveGenerator = new AlphaBetaMoveGenerator(); - - @Override - public Move getMove(Board board) { - return moveGenerator.genMove(board, false); - } + private final MoveGenerator moveGenerator = new AlphaBetaMoveGenerator(); @Override public void denyMove() { throw new UnsupportedOperationException("Not implemented"); } + @Override + public Move getMove(Board board, PlayerModel player) { + return moveGenerator.genMove(board, false); + } + @Override public boolean isReady() { return true; // always ready to play a random valid move } - + @Override public String toString() { return "Alpha-Beta ComPlayer"; diff --git a/src/model/comPlayer/HumanPlayer.java b/src/model/comPlayer/HumanPlayer.java index c0ae924..f86c472 100644 --- a/src/model/comPlayer/HumanPlayer.java +++ b/src/model/comPlayer/HumanPlayer.java @@ -1,32 +1,16 @@ package model.comPlayer; import model.Board; +import model.Board.TileColor; import model.CellPointer; import model.Move; -import model.Board.TileColor; - +import model.playerModel.PlayerModel; public class HumanPlayer implements Player { private CellPointer cell = CellPointer.NONE; - private boolean ready = false; private TileColor color = TileColor.BLUE; - - @Override - public void denyMove() { - ready = false; - } - - public TileColor getColor() { - return color; - } - - @Override - public Move getMove(Board board) { - ready = false; - return new Move(cell, color); - - } + private boolean ready = false; public void decrementColor() { TileColor[] colors = Board.TileColor.values(); @@ -47,6 +31,22 @@ public class HumanPlayer implements Player { 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() { TileColor[] colors = Board.TileColor.values(); int currentColor = -1; diff --git a/src/model/comPlayer/MinimaxComPlayer.java b/src/model/comPlayer/MinimaxComPlayer.java index 82fa55a..81c6fdc 100644 --- a/src/model/comPlayer/MinimaxComPlayer.java +++ b/src/model/comPlayer/MinimaxComPlayer.java @@ -4,29 +4,30 @@ import model.Board; import model.Move; import model.comPlayer.generator.MinimaxMoveGenerator; import model.comPlayer.generator.MoveGenerator; +import model.playerModel.PlayerModel; -public class MinimaxComPlayer implements Player{ - private MoveGenerator moveGenerator = new MinimaxMoveGenerator(); +public class MinimaxComPlayer implements Player { + private final MoveGenerator moveGenerator = new MinimaxMoveGenerator(); public MinimaxComPlayer() { super(); } - - @Override - public Move getMove(Board board) { - return moveGenerator.genMove(board, false); - } @Override public void denyMove() { throw new UnsupportedOperationException("Not implemented"); } + @Override + public Move getMove(Board board, PlayerModel player) { + return moveGenerator.genMove(board, false); + } + @Override public boolean isReady() { return true; // always ready to play a random valid move } - + @Override public String toString() { return "Minimax ComPlayer"; diff --git a/src/model/comPlayer/MonteCarloComPlayer.java b/src/model/comPlayer/MonteCarloComPlayer.java index 013f150..f916d7f 100644 --- a/src/model/comPlayer/MonteCarloComPlayer.java +++ b/src/model/comPlayer/MonteCarloComPlayer.java @@ -3,19 +3,24 @@ package model.comPlayer; import java.util.Random; import model.Board; +import model.Board.TileColor; import model.CellPointer; import model.Move; -import model.Board.TileColor; - +import model.playerModel.PlayerModel; public class MonteCarloComPlayer implements Player { private final Random rand = new Random(); @Override - public Move getMove(Board board) { - return getRandomMove(board,true); + public void denyMove() { + throw new UnsupportedOperationException("Not implemented"); } - + + @Override + public Move getMove(Board board, PlayerModel player) { + return getRandomMove(board, true); + } + public Move getRandomMove(Board board, boolean isCompTurn) { TileColor tile = TileColor.BLUE; int r = -1; @@ -42,19 +47,14 @@ public class MonteCarloComPlayer implements Player { tile = TileColor.YELLOW; } - return new Move(new CellPointer(r, c), tile); - } - - @Override - public void denyMove() { - throw new UnsupportedOperationException("Not implemented"); + return new Move(tile, new CellPointer(r, c)); } @Override public boolean isReady() { return true; // always ready to play a random valid move } - + @Override public String toString() { return "Monte Carlo ComPlayer"; diff --git a/src/model/comPlayer/NeuralNetworkPlayer.java b/src/model/comPlayer/NeuralNetworkPlayer.java new file mode 100644 index 0000000..78b3a6f --- /dev/null +++ b/src/model/comPlayer/NeuralNetworkPlayer.java @@ -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"; + } +} diff --git a/src/model/comPlayer/Player.java b/src/model/comPlayer/Player.java index 2467a42..b3c37bb 100644 --- a/src/model/comPlayer/Player.java +++ b/src/model/comPlayer/Player.java @@ -2,21 +2,27 @@ package model.comPlayer; import model.Board; import model.Move; - +import model.playerModel.PlayerModel; public interface Player { /** * Instructs the Player to retract the last move. */ 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 * @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 */ public boolean isReady(); diff --git a/src/model/comPlayer/RandomComPlayer.java b/src/model/comPlayer/RandomComPlayer.java index 5ae1561..4ed1b41 100644 --- a/src/model/comPlayer/RandomComPlayer.java +++ b/src/model/comPlayer/RandomComPlayer.java @@ -3,16 +3,21 @@ package model.comPlayer; import java.util.Random; import model.Board; +import model.Board.TileColor; import model.CellPointer; import model.Move; -import model.Board.TileColor; - +import model.playerModel.PlayerModel; public class RandomComPlayer implements Player { private final Random rand = new Random(); @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; int r = -1; int c = -1; @@ -38,19 +43,14 @@ public class RandomComPlayer implements Player { tile = TileColor.YELLOW; } - return new Move(new CellPointer(r, c), tile); - } - - @Override - public void denyMove() { - throw new UnsupportedOperationException("Not implemented"); + return new Move(tile, new CellPointer(r, c)); } @Override public boolean isReady() { return true; // always ready to play a random valid move } - + @Override public String toString() { return "Random ComPlayer"; diff --git a/src/model/playerModel/PlayerModel.java b/src/model/playerModel/PlayerModel.java index 047bb30..2570d1d 100644 --- a/src/model/playerModel/PlayerModel.java +++ b/src/model/playerModel/PlayerModel.java @@ -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) { for (int i = 0; i < outputNode.length; i++) { outputNode[i].learn(example[i]);