-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:
9
src/model/playerModel/Node.java
Normal file
9
src/model/playerModel/Node.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package model.playerModel;
|
||||||
|
|
||||||
|
public interface Node {
|
||||||
|
|
||||||
|
boolean axon();
|
||||||
|
|
||||||
|
void learn(boolean correct);
|
||||||
|
|
||||||
|
}
|
||||||
134
src/model/playerModel/PlayerModel.java
Normal file
134
src/model/playerModel/PlayerModel.java
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/model/playerModel/node/InputNode.java
Normal file
22
src/model/playerModel/node/InputNode.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/model/playerModel/node/SigmoidNode.java
Normal file
58
src/model/playerModel/node/SigmoidNode.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user