- Implemented multiple users, including a selection dialog and automatic preference saving and loading.

- Integrated the ANN with the game. The network now predicts a user move, completely ignores it, and trains itself on the players actual move. This integration also included implementing two new functions. The first translates a board state to a boolean array to correspond with input nodes. The second translates a move to a boolean array to correspond with output nodes.
This commit is contained in:
Marshall
2012-04-29 03:22:19 -04:00
parent dc11e2c48b
commit 15ed56134e
6 changed files with 274 additions and 65 deletions

View File

@@ -30,19 +30,38 @@ public class Referee implements Runnable {
private final MainFrame mf; private final MainFrame mf;
private PlayerModel playerModel = null; private PlayerModel playerModel = null;
public Referee(MainFrame mnFrm) { public Referee(MainFrame mnFrm, String player) {
if (PlayerModel.TRY_LOAD && PlayerModel.exists()) { if (PlayerModel.exists(player)) {
playerModel = PlayerModel.load(); PlayerModel.getPlayerPath(player);
playerModel = PlayerModel.load(PlayerModel.getPlayerPath(player));
} }
if (playerModel == null) { if (getPlayerModel() == null) {
playerModel = new PlayerModel(); playerModel = new PlayerModel(player);
} }
mf = mnFrm; mf = mnFrm;
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;
} }
@@ -83,13 +102,13 @@ public class Referee implements Runnable {
initGame(); initGame();
mf.updateBoard(); mf.updateBoard();
play(); play();
playerModel.logGame(getPlayerScore()); getPlayerModel().logGame(getPlayerScore());
if (!playerModel.save()) { if (!getPlayerModel().save()) {
System.err.println("Saving PlayerModel failed."); System.err.println("Saving PlayerModel failed.");
} }
new HighScoreDialog(mf, playerModel); new HighScoreDialog(mf, getPlayerModel());
} }
} }
@@ -97,6 +116,28 @@ public class Referee implements Runnable {
this.boardPanel = boardPanel; this.boardPanel = boardPanel;
} }
private boolean[] getMoveArray(Move mv) {
boolean[] move = new boolean[getPlayerModel().getNumOutputNodes()];
move[0] = (mv.getColor() == TileColor.BLUE);
move[1] = (mv.getColor() == TileColor.GREEN);
move[2] = (mv.getColor() == TileColor.RED);
move[3] = (mv.getColor() == TileColor.YELLOW);
int tile = 0;
for (int r = 0; r < Board.NUM_ROWS; r++) {
for (int c = 0; c < Board.NUM_COLS; c++) {
move[tile] = (mv.getCell().r == r && mv.getCell().c == c);
}
}
return move;
}
private PlayerModel getPlayerModel() {
return playerModel;
}
private ScorePanel getScorePanel() { private ScorePanel getScorePanel() {
return mf.getScorePanel(); return mf.getScorePanel();
} }
@@ -126,6 +167,9 @@ public class Referee implements Runnable {
Move mv = humanPlayer.getMove(board); Move mv = humanPlayer.getMove(board);
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));
getPlayerModel().train(getMoveArray(mv));
} else { } else {
humanPlayer.denyMove(); humanPlayer.denyMove();
} }
@@ -133,6 +177,13 @@ public class Referee implements Runnable {
} else { } else {
Move mv = computerPlayer.getMove(board); Move mv = computerPlayer.getMove(board);
playToken(mv); 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().getPrediction(getBoardState());
} }
mf.updateMessage(getMessage()); mf.updateMessage(getMessage());

View File

@@ -17,30 +17,27 @@ import model.playerModel.node.SigmoidNode;
public class PlayerModel implements Serializable { public class PlayerModel implements Serializable {
public static final String PLAYER_MODEL_PATH = "playerModel.dat"; // Path to public static final String DATA_FOLDER = "data/";
// the public static final String PLAYER_MODEL_PREFIX = "playerModel";
// stored public static final String PLAYER_MODEL_SUFFIX = ".dat";
// player
// model.
public static final Random rand = new Random(); // Randomizer object. public static final Random rand = new Random(); // Randomizer object.
public static final boolean TRY_LOAD = true; // Set to false if any existing
// player model should be
// discarded and the next
// game should begin a new
// sequence.
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static boolean exists() { public static boolean exists(String playerName) {
return (new File(PLAYER_MODEL_PATH)).exists(); return (new File(getPlayerPath(playerName))).exists();
} }
public static PlayerModel load() { public static String getPlayerPath(String playerName) {
return DATA_FOLDER + PLAYER_MODEL_PREFIX + "_" + playerName
+ PLAYER_MODEL_SUFFIX;
}
public static PlayerModel load(String path) {
FileInputStream fin = null; FileInputStream fin = null;
ObjectInputStream oin = null; ObjectInputStream oin = null;
try { try {
fin = new FileInputStream(PLAYER_MODEL_PATH); fin = new FileInputStream(path);
oin = new ObjectInputStream(fin); oin = new ObjectInputStream(fin);
PlayerModel pm = (PlayerModel) oin.readObject(); PlayerModel pm = (PlayerModel) oin.readObject();
oin.close(); oin.close();
@@ -54,29 +51,31 @@ public class PlayerModel implements Serializable {
private final SigmoidNode[] hiddenLayer; private final SigmoidNode[] hiddenLayer;
private int highScoresAchieved = 0; private int highScoresAchieved = 0;
// One node for each tile-color combination, plus one for each upcoming // One node for each tile-color combination.
// tile-color combination.
private final InputNode[] inputNode = new InputNode[(Board.NUM_COLS private final InputNode[] inputNode = new InputNode[(Board.NUM_COLS
* Board.NUM_ROWS * (Board.TileColor.values().length - 1))]; * Board.NUM_ROWS * (Board.TileColor.values().length - 1))];
private final String name;
private int nextHighInGames = 0; private int nextHighInGames = 0;
// One node for each tile plus four for the colors to be selected. /*
// outputNode[0] is blue. * One node for each tile plus four for the colors to be selected.
// outputNode[1] is green. * outputNode[0] is blue. outputNode[1] is green. outputNode[2] is red.
// outputNode[2] is red. * outputNode[3] is yellow. outputNode[4] through outputNode[n] represent
// outputNode[3] is yellow. * grid spaces. A true means that the player is predicted to place on that
// outputNode[4] through outputNode[n] represent grid spaces. A true means * tile. They should be read from the top-left to bottom-right, across rows.
// that the player is predicted to place on that tile. * Ideally, the network should return only one true between 0 and 3 and only
// They should be read from the top-left to bottom-right, across rows. * one true between 4 and n, representing one color and the tile in which it
// Ideally, the network should return only one true between 0 and 3 and only * should be placed. See Referee.getMoveArray().
// one true between 4 and n, representing one color and the tile in which it */
// should be placed.
private final SigmoidNode[] outputNode = new SigmoidNode[(Board.NUM_COLS * Board.NUM_ROWS) + 4]; private final SigmoidNode[] outputNode = new SigmoidNode[(Board.NUM_COLS * Board.NUM_ROWS) + 4];
private final ArrayList<GameLog> scores = new ArrayList<GameLog>(); private final ArrayList<GameLog> scores = new ArrayList<GameLog>();
public PlayerModel() { public PlayerModel(String nme) {
name = nme;
hiddenLayer = new SigmoidNode[inputNode.length hiddenLayer = new SigmoidNode[inputNode.length
+ ((inputNode.length * 2) / 3)]; + ((inputNode.length * 2) / 3)];
@@ -121,6 +120,18 @@ public class PlayerModel implements Serializable {
return highScores; return highScores;
} }
public String getName() {
return name;
}
public int getNumInputNodes() {
return inputNode.length;
}
public int getNumOutputNodes() {
return outputNode.length;
}
public boolean[] getPrediction(boolean[] input) { public boolean[] getPrediction(boolean[] input) {
if (input.length == inputNode.length) { if (input.length == inputNode.length) {
boolean[] prediction = new boolean[outputNode.length]; boolean[] prediction = new boolean[outputNode.length];
@@ -187,13 +198,17 @@ public class PlayerModel implements Serializable {
public boolean save() { public boolean save() {
FileOutputStream fout = null; FileOutputStream fout = null;
ObjectOutputStream oout = null; ObjectOutputStream oout = null;
String path = getPlayerPath(getName());
try { try {
fout = new FileOutputStream(PLAYER_MODEL_PATH); (new File(DATA_FOLDER)).mkdirs();
fout = new FileOutputStream(path);
oout = new ObjectOutputStream(fout); oout = new ObjectOutputStream(fout);
oout.writeObject(this); oout.writeObject(this);
oout.close(); oout.close();
return true; return true;
} catch (IOException ex) { } catch (IOException ex) {
ex.printStackTrace();
return false; return false;
} }
} }

View File

@@ -20,28 +20,6 @@ public class HighScoreDialog extends JDialog {
*/ */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static void main(String[] args) {
JFrame x = new JFrame();
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
x.setVisible(true);
PlayerModel pm = new PlayerModel();
pm.logGame(100);
pm.logGame(90);
pm.logGame(80);
pm.logGame(70);
pm.logGame(60);
pm.logGame(50);
pm.logGame(40);
pm.logGame(30);
pm.logGame(20);
pm.logGame(10);
pm.logGame(98);
pm.logGame(105);
new HighScoreDialog(x, pm);
}
public HighScoreDialog(JFrame owner, PlayerModel pm) { public HighScoreDialog(JFrame owner, PlayerModel pm) {
super(owner, "Game over!", true); super(owner, "Game over!", true);

View File

@@ -14,8 +14,7 @@ public class MainFrame extends JFrame {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static void main(String[] args) { public static void main(String[] args) {
MainFrame mainFrame = new MainFrame(); new UserChooserFrame();
mainFrame.playGame();
} }
private BoardPanel bp; private BoardPanel bp;
@@ -24,9 +23,10 @@ public class MainFrame extends JFrame {
private TileSelectionPanel tp; private TileSelectionPanel tp;
ScorePanel sp; ScorePanel sp;
public MainFrame() { public MainFrame(String name) {
super("CS8803 Project 4"); super("CS8803 Project 4");
referee = new Referee(this);
referee = new Referee(this, name);
init(); init();
@@ -35,6 +35,7 @@ public class MainFrame extends JFrame {
setLocationRelativeTo(null); setLocationRelativeTo(null);
setVisible(true); setVisible(true);
playGame();
} }
public ScorePanel getScorePanel() { public ScorePanel getScorePanel() {

View File

@@ -0,0 +1,164 @@
package view;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import model.playerModel.PlayerModel;
public class UserChooserFrame extends JFrame {
public static final String PLAYER_LIST_FILE = "players.dat";
public static final JLabel RULES_TEXT = new JLabel(
"Here should go some rules text. Lorem ipsum and blah blah blah.");
private static final long serialVersionUID = 1L;
private final JButton playButton = new JButton("Play!");
private final JComboBox<String> userNameBox = new JComboBox<String>();
private ArrayList<String> users;
public UserChooserFrame() {
initLayout();
initActions();
pack();
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private String getUserListPath() {
return PlayerModel.DATA_FOLDER + PLAYER_LIST_FILE;
}
private ArrayList<String> getUsers() {
FileInputStream fin = null;
ObjectInputStream oin = null;
try {
fin = new FileInputStream(getUserListPath());
oin = new ObjectInputStream(fin);
@SuppressWarnings("unchecked")
ArrayList<String> list = (ArrayList<String>) oin.readObject();
oin.close();
return list;
} catch (Exception e) {
return new ArrayList<String>();
}
}
private void initActions() {
userNameBox.setEditable(true);
users = getUsers();
Collections.sort(users);
for (int i = 0; i < users.size(); i++) {
userNameBox.addItem(users.get(i));
}
playButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
String name = ((String) userNameBox.getSelectedItem());
name = name == null ? name : name.trim().toLowerCase();
if (name != null && name.compareTo("") != 0) {
UserChooserFrame.this.setVisible(false);
boolean found = false;
for (int i = 0; !found && i < users.size(); i++) {
if (name.compareTo(users.get(i)) == 0) {
found = true;
}
}
if (!found) {
users.add(name);
}
saveUserList();
new MainFrame(name);
}
}
});
}
private void initLayout() {
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints con = new GridBagConstraints();
con.fill = GridBagConstraints.BOTH;
con.gridheight = 1;
con.gridwidth = 5;
con.gridx = 0;
con.gridy = 0;
con.insets = new Insets(20, 20, 5, 20);
con.weightx = 1;
con.weighty = 1;
gbl.setConstraints(RULES_TEXT, con);
con = new GridBagConstraints();
con.fill = GridBagConstraints.BOTH;
con.gridheight = 1;
con.gridwidth = 5;
con.gridx = 0;
con.gridy = 1;
con.insets = new Insets(5, 20, 5, 20);
con.weightx = 1;
con.weighty = 1;
gbl.setConstraints(userNameBox, con);
con = new GridBagConstraints();
con.fill = GridBagConstraints.BOTH;
con.gridheight = 1;
con.gridwidth = 1;
con.gridx = 2;
con.gridy = 2;
con.insets = new Insets(5, 20, 20, 20);
con.weightx = 1;
con.weighty = 1;
gbl.setConstraints(playButton, con);
setLayout(gbl);
add(RULES_TEXT);
add(userNameBox);
add(playButton);
}
private void saveUserList() {
FileOutputStream fout = null;
ObjectOutputStream oout = null;
String path = getUserListPath();
try {
(new File(PlayerModel.DATA_FOLDER)).mkdirs();
fout = new FileOutputStream(path);
oout = new ObjectOutputStream(fout);
oout.writeObject(users);
oout.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

View File

@@ -14,7 +14,7 @@ import model.playerModel.PlayerModel;
public class Tests extends TestCase { public class Tests extends TestCase {
public static PlayerModel getFakePlayerModel() { public static PlayerModel getFakePlayerModel() {
PlayerModel pm = new PlayerModel(); PlayerModel pm = new PlayerModel("marshall");
pm.logGame(25); pm.logGame(25);
pm.logGame(30); pm.logGame(30);