-PlayerModel classes, including a function to get the target score for a player's game and a neural network to predict a player's moves.

This commit is contained in:
Marshall
2012-04-19 21:05:59 -04:00
parent 10f43ee31c
commit f19c09b573
4 changed files with 223 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
package model.playerModel;
public interface Node {
boolean axon();
void learn(boolean correct);
}

View File

@@ -0,0 +1,134 @@
package model.playerModel;
import java.util.Random;
import model.Board;
import model.playerModel.node.InputNode;
import model.playerModel.node.SigmoidNode;
public class PlayerModel {
public static final int FIRST_SCORE = 50;
public static final Random rand = new Random();
private final SigmoidNode[] hiddenLayer;
private final int[] highScore = new int[10];
private int highScoresAchieved = 0;
// One node for each tile-color combination, plus one for each upcoming
// tile-color combination.
private final InputNode[] inputNode = new InputNode[(Board.NUM_COLS
* Board.NUM_ROWS * (Board.TileColor.values().length - 1))
+ (4 * 2)];
private int nextHighInGames = 0;
// One node for each tile.
private final SigmoidNode[] outputNode = new SigmoidNode[Board.NUM_COLS
* Board.NUM_ROWS];
public PlayerModel() {
hiddenLayer = new SigmoidNode[inputNode.length
+ ((inputNode.length * 2) / 3)];
for (int i = 0; i < inputNode.length; i++) {
inputNode[i] = new InputNode();
}
for (int i = 0; i < hiddenLayer.length; i++) {
hiddenLayer[i] = new SigmoidNode();
for (int j = 0; j < inputNode.length; j++) {
hiddenLayer[i].addNode(inputNode[j], 1);
}
}
for (int i = 0; i < outputNode.length; i++) {
outputNode[i] = new SigmoidNode();
for (int j = 0; j < hiddenLayer.length; j++) {
outputNode[i].addNode(hiddenLayer[j], 1);
}
}
}
public boolean[] getPrediction(boolean[] input) {
if (input.length == inputNode.length) {
boolean[] prediction = new boolean[outputNode.length];
for (int i = 0; i < input.length; i++) {
inputNode[i].setStimulation(input[i]);
}
for (int i = 0; i < prediction.length; i++) {
prediction[i] = outputNode[i].axon();
}
return prediction;
}
return null;
}
public int getTargetScore() {
int targetScore;
int highScore = getHighScore();
if (highScoresAchieved == 0) {
targetScore = FIRST_SCORE;
nextHighInGames = 1;
}
else if (nextHighInGames == 0) {
// Set next game for high score.
nextHighInGames = (int) Math.pow(highScoresAchieved, 2);
// Return high score times increase percentage.
targetScore = highScore / (2 * (highScoresAchieved + 1));
if (targetScore <= highScore) {
targetScore = highScore + 1;
}
}
else {
double scoreReduction = .1 * rand.nextDouble();
targetScore = (int) (highScore - (highScore * scoreReduction));
if (targetScore >= highScore) {
targetScore = highScore - 1;
}
}
return targetScore;
}
public void logGame(int score) {
loop: for (int i = 0; i < highScore.length; i++) {
if (score > highScore[i]) {
for (int j = i; j < highScore.length - 1; j++) {
highScore[j + 1] = highScore[j];
}
highScore[i] = score;
break loop;
}
}
highScoresAchieved += (score == highScore[0]) ? 1 : 0;
nextHighInGames--;
}
public void train(boolean[] example) {
if (example.length == outputNode.length) {
for (int i = 0; i < outputNode.length; i++) {
outputNode[i].learn(example[i]);
}
}
}
private int getHighScore() {
return highScore[0];
}
}

View File

@@ -0,0 +1,22 @@
package model.playerModel.node;
import model.playerModel.Node;
public class InputNode implements Node {
private boolean dendrite = false;
@Override
public boolean axon() {
return dendrite;
}
@Override
public void learn(boolean correct) {
// No learning for input nodes.
}
public void setStimulation(boolean active) {
dendrite = active;
}
}

View File

@@ -0,0 +1,58 @@
package model.playerModel.node;
import java.util.Hashtable;
import model.playerModel.Node;
public class SigmoidNode implements Node {
// Training rate.
private final double A = .15;
private final Hashtable<Node, Double> dendrites = new Hashtable<Node, Double>();
private final double s = 1; // Slope paramater for the activation function.
// It's doing nothing right now, but I figured
// I'd implement it as a tweaking option.
private final double THRESHOLD = .5;
public void addNode(Node n, double weight) {
dendrites.put(n, weight);
}
@Override
public boolean axon() {
double sum = 0;
for (Node n : dendrites.keySet()) {
sum += n.axon() ? dendrites.get(n) : 0;
}
return (activation(sum) > THRESHOLD);
}
@Override
public void learn(boolean correct) {
for (Node n : dendrites.keySet()) {
if (correct && n.axon()) {
dendrites.put(n, dendrites.get(n) + A);
n.learn(correct);
}
}
}
private double activation(double sum) {
// Sigmoid function:
// 1/(1+(e^(-bt)))
double a = -1 * s * sum;
a = Math.exp(a);
a = 1 + a;
a = 1 / a;
return a;
}
}