diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..0cbf9cd --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ae3c172..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/bin/ diff --git a/TheGame/.project b/.project similarity index 92% rename from TheGame/.project rename to .project index fb4cb30..c68be4e 100644 --- a/TheGame/.project +++ b/.project @@ -1,6 +1,6 @@ - frogger + Full Algorithm diff --git a/TheGame/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs old mode 100644 new mode 100755 similarity index 100% rename from TheGame/.settings/org.eclipse.jdt.core.prefs rename to .settings/org.eclipse.jdt.core.prefs diff --git a/TheGame/src/Game.java b/TheGame/src/Game.java deleted file mode 100644 index 72beb2c..0000000 --- a/TheGame/src/Game.java +++ /dev/null @@ -1,105 +0,0 @@ -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; - -import javax.imageio.ImageIO; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.Timer; - -public class Game { - private JFrame frame = new JFrame("Ultra Mario Bros!"); - private JPanel panel; - private Mario m = new Mario(0, 624); - private Keyboard keys = new Keyboard(m); - private int[][] tilelayout = new int[13][13]; - private String[] tileID = {"AIR", "ground", "block"}; - private int offset = 0; -// private int[] tileData = {1, 2}; - private Timer repaint = new Timer(1, new ActionListener(){ - public void actionPerformed(ActionEvent e) { - frame.repaint(); -// frame.dispose(); -// JOptionPane.showMessageDialog(null, "You died!\nPoints: " + ((Integer) movey).toString(), "You lost!", JOptionPane.WARNINGMESSAGE); -// repaint.stop(); -// } - } - }); - - public static void main(String[] args) { - new Game().start(); - } - - private void start() { - makeFrame(); - repaint.start(); - } - - @SuppressWarnings("serial") - private void makeFrame() { - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - panel = new JPanel() { - public void paintComponent(Graphics g) { - super.paintComponent(g); - m.key(keys.r); - draw(g); - } - }; - frame.add(panel); - - for (int x = 0; x < tilelayout.length; x++) { - tilelayout[12][x] = 1; - } - - panel.repaint(); - panel.setPreferredSize(new Dimension(624, 624)); - panel.addKeyListener(keys); - panel.setFocusable(true); - panel.setLayout(null); - frame.pack(); - frame.setVisible(true); - - } - private void loadNext() { - for (int y = 0; y < tilelayout.length; y++) { - for (int x = 1; x < tilelayout.length; x++) { - tilelayout[y][x - 1] = tilelayout[y][x]; - } - } - int[] colay = getNewLine(); - for (int y = 0; y < tilelayout.length; y++) { - tilelayout[y][tilelayout[y].length - 1] = colay[y]; - } - } - - private int[] getNewLine() { - return new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - } - - private void draw(Graphics g) { - offset += m.draw(g, tilelayout); - while (offset >= 48) { - loadNext(); - offset -= 48; - } - - for (int y = 0; y < tilelayout.length; y++) { - for (int x = 0; x < tilelayout.length; x++) { - int tile = tilelayout[y][x]; - if (tile != 0) { - Image img; - try { - img = ImageIO.read(new File(tileID[tile] + ".png")); - g.drawImage(img, x * 48 - offset, y * 48, null); - } catch (IOException e) { - // Auto-generated catch block - } - } - } - } - } -} diff --git a/TheGame/.classpath b/_classpath.xml old mode 100644 new mode 100755 similarity index 79% rename from TheGame/.classpath rename to _classpath.xml index f00af9b..d655412 --- a/TheGame/.classpath +++ b/_classpath.xml @@ -1,6 +1,6 @@ - + diff --git a/_project.xml b/_project.xml new file mode 100755 index 0000000..eab011b --- /dev/null +++ b/_project.xml @@ -0,0 +1,17 @@ + + + Full algorithm + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..8e762e5 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,12 @@ +/Activation.class +/Game$1.class +/Game$2.class +/Game.class +/GeneticAlgorithm.class +/Individual.class +/Keyboard.class +/Mario.class +/NeuralNetwork.class +/NeuralNetworkTester.class +/Neuron.class +/Trainer.class diff --git a/bin/Activation.class b/bin/Activation.class new file mode 100644 index 0000000..be807e6 Binary files /dev/null and b/bin/Activation.class differ diff --git a/bin/Game$1.class b/bin/Game$1.class new file mode 100644 index 0000000..c7a7de3 Binary files /dev/null and b/bin/Game$1.class differ diff --git a/bin/Game$2.class b/bin/Game$2.class new file mode 100644 index 0000000..68799f5 Binary files /dev/null and b/bin/Game$2.class differ diff --git a/bin/Game.class b/bin/Game.class new file mode 100644 index 0000000..34d1182 Binary files /dev/null and b/bin/Game.class differ diff --git a/bin/GeneticAlgorithm.class b/bin/GeneticAlgorithm.class new file mode 100644 index 0000000..907920e Binary files /dev/null and b/bin/GeneticAlgorithm.class differ diff --git a/bin/Individual.class b/bin/Individual.class new file mode 100644 index 0000000..9f9f35f Binary files /dev/null and b/bin/Individual.class differ diff --git a/bin/Keyboard.class b/bin/Keyboard.class new file mode 100644 index 0000000..1de52e4 Binary files /dev/null and b/bin/Keyboard.class differ diff --git a/bin/Mario.class b/bin/Mario.class new file mode 100644 index 0000000..bc84736 Binary files /dev/null and b/bin/Mario.class differ diff --git a/bin/NeuralNetwork.class b/bin/NeuralNetwork.class new file mode 100644 index 0000000..642d6c7 Binary files /dev/null and b/bin/NeuralNetwork.class differ diff --git a/bin/NeuralNetworkTester.class b/bin/NeuralNetworkTester.class new file mode 100644 index 0000000..e5f9e0d Binary files /dev/null and b/bin/NeuralNetworkTester.class differ diff --git a/bin/Neuron.class b/bin/Neuron.class new file mode 100644 index 0000000..4c6c8e7 Binary files /dev/null and b/bin/Neuron.class differ diff --git a/bin/Trainer.class b/bin/Trainer.class new file mode 100644 index 0000000..1cb61b0 Binary files /dev/null and b/bin/Trainer.class differ diff --git a/block.png b/block.png new file mode 100755 index 0000000..93b1848 Binary files /dev/null and b/block.png differ diff --git a/data b/data new file mode 100755 index 0000000..a780344 --- /dev/null +++ b/data @@ -0,0 +1,40 @@ +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 +0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1 +0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 diff --git a/ground.png b/ground.png new file mode 100755 index 0000000..dff8613 Binary files /dev/null and b/ground.png differ diff --git a/mario.png b/mario.png new file mode 100755 index 0000000..c5c1b81 Binary files /dev/null and b/mario.png differ diff --git a/src/Activation.java b/src/Activation.java new file mode 100755 index 0000000..afb6d94 --- /dev/null +++ b/src/Activation.java @@ -0,0 +1,4 @@ + +public enum Activation { + Sigmoid, ReLu, Tanh, None +} diff --git a/src/Game.java b/src/Game.java new file mode 100755 index 0000000..1c321a9 --- /dev/null +++ b/src/Game.java @@ -0,0 +1,248 @@ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.Timer; + +public class Game { + private JFrame frame = new JFrame("Super Mario Bros!"); + private JPanel panel; + private Mario m = new Mario(0, 624); + private Keyboard keys = new Keyboard(m); + private int[][] tilelayout = new int[13][14]; + private String[] tileID = {"AIR", "ground"}; + private int offset = 0; + private double fitness = 0; + boolean isDone = false; + private int frames = 0; + public Individual indiv; + public static int me = 0; + public static int maxFrames = 50; + public boolean play = false; + public BufferedReader in; + private Timer repaint = new Timer(0, new ActionListener(){ + public void actionPerformed(ActionEvent e) { + frame.repaint(); + frames += 1; + if (m.y < 0 || (frames >= maxFrames && !play)) { + //System.out.println("done"+GeneticAlgorithm.numDone); + if (m.y < 0) { + fitness -= 200; + } + + Game.me++; + fitness += m.x; + //System.out.println("ME"+Game.me); + if (indiv != null) + indiv.setDone(true); + isDone = true; + frame.dispose(); + if (isDone) { + repaint.stop(); + try { + in.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + System.out.println("My fit: " + fitness); + } + } + } + }); + + public static void main(String[] args) { + new Game().start(); + } + + void start() { + //System.out.println("THREAD: "+Thread.currentThread().getId()+ " "+getFitness()); + makeFrame(); + repaint.start(); + } + + public double[][] getState() { + double[][] doubles = new double[tilelayout.length][tilelayout[0].length - 1]; + /*/ + for (int i = 0; i < tilelayout.length; i++) { + for (int j = 0; j < tilelayout.length - 1; j++) { + doubles[i][j] = tilelayout[i][j] * 5.0; + } + } + /*/ + for (int i = m.tiley - 6; i < m.tiley + 6; i++) { + if (i < 0) continue; + if (i > 12) continue; + for (int j = m.tilex - 6; j < m.tilex + 6; j++) { + if (j < 0) continue; + if (j > 13) continue; + doubles[i][j] = tilelayout[i][j] * 5.0; + } + } + + + return doubles; + } + + public void jump() { + m.jump(); + } + + public void moveRight() { + m.moveRight(); + } + + public void moveLeft() { + m.moveLeft(); + } + + public double getFitness() { + return fitness; + } + + @SuppressWarnings("serial") + private void makeFrame() { + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + panel = new JPanel() { + public void paintComponent(Graphics g) { + super.paintComponent(g); + m.key(keys.r); + draw(g); + } + }; + panel.setBackground(new Color(111,196,249)); + frame.add(panel); + +// for (int x = 0; x < tilelayout[0].length; x++) { +// tilelayout[tilelayout.length - 1][x] = 1; +// } +// tilelayout[tilelayout.length - 2][13] = 1; + + try { + in = new BufferedReader(new FileReader("data")); + String str; + for (int x = 0; x < tilelayout[0].length; x++) { + str = in.readLine(); + process(str, x); + } + } catch (IOException e) { + System.out.println("BRUHHHHH"); + } + +// printArray(tilelayout); + panel.repaint(); + panel.setPreferredSize(new Dimension(624, 624)); + panel.addKeyListener(keys); + panel.setFocusable(true); + panel.setLayout(null); + frame.pack(); + frame.setVisible(true); + + } + + private void process(String str, int x) { + int[] replace = new int[tilelayout.length]; + for (int y = 0; y < tilelayout.length; y++) { + char s = str.charAt(3 * y); + int i = Character.getNumericValue(s); + replace[y] = i; + } + for (int y = 0; y < tilelayout.length; y++) { + tilelayout[y][x] = replace[y]; + } + } + + /*/ private void printArray(int[][] t) { + for (int [] y : t) { + for (int x : y) { + System.out.print(x + ", "); + } + System.out.println(); + } + } + /*/ + + private void loadNext() { + for (int y = 0; y < tilelayout.length; y++) { + for (int x = 1; x < tilelayout[0].length; x++) { + tilelayout[y][x - 1] = tilelayout[y][x]; + } + } + try { + String str; + str = in.readLine(); + process(str, tilelayout[0].length - 1); + + } catch (Exception e) { +// e.printStackTrace(); + int[] colay = getNewLine(); + for (int y = 0; y < tilelayout.length; y++) { + tilelayout[y][tilelayout[y].length - 1] = colay[y]; + } + } + } + + private int[] getNewLine() { + int[] ans = new int[14]; + int[] lastcol = new int[14]; + int HEIGHT = 1; + + for (int y = 0; y < tilelayout.length; y++) { + lastcol[y] = tilelayout[y][tilelayout[y].length - 2]; + } + + for (int y = 0; y < lastcol.length; y++) { + if (lastcol[y] == 1) { + try { + ans[(int) (Math.random() * (lastcol.length - y + HEIGHT)) + y - HEIGHT] = 1; + } catch (IndexOutOfBoundsException e) { + //System.out.println("ERRRRRROOOOOOOOOOOAAAAAAAARRRRRR"); + //System.out.println(y); + //System.out.println(HEIGHT); + //System.out.println(lastcol.length); + } + } + } + + ans[(int) (Math.random() * (lastcol.length))] = 1; + ans[(int) (Math.random() * (lastcol.length))] = 0; + + return ans; +// return new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + } + + private void draw(Graphics g) { +// System.out.println(offset); + int moved = m.draw(g, tilelayout, offset); + offset += moved; + fitness += moved; + while (offset >= 48) { + loadNext(); + offset -= 48; + } + + for (int y = 0; y < tilelayout.length; y++) { + for (int x = 0; x < tilelayout[0].length; x++) { + int tile = tilelayout[y][x]; + if (tile != 0) { + Image img; + try { + img = ImageIO.read(new File(tileID[tile] + ".png")); + g.drawImage(img, x * 48 - offset, y * 48, null); + } catch (IOException e) { + // Auto-generated catch block + } + } + } + } + } +} diff --git a/src/GeneticAlgorithm.java b/src/GeneticAlgorithm.java new file mode 100755 index 0000000..1e26f82 --- /dev/null +++ b/src/GeneticAlgorithm.java @@ -0,0 +1,147 @@ +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class GeneticAlgorithm { + private ArrayList individuals = new ArrayList (); + private double mutationRate; + private int popSize; + private int numInputs; + public static int numDone = 0; + public int oldBest = 0; + + /** + * @author - Sri Kondapalli + * @param mutationRate - + * @param popSize + * @param numInputs + */ + + public GeneticAlgorithm(double mutationRate, int popSize, int numInputs) { + this.mutationRate = mutationRate; + this.popSize = popSize; + this.numInputs = numInputs; + } + + public void start(int times) throws InterruptedException, ExecutionException { + for (int i = 0; i < times; i++) { + System.out.println("Starting generation "+(i+1)); + main(); + } + } + + boolean firstTime = true; + /** + * @author Sri Kondapalli + * places an Individual ind into a sorted ArrayList + */ + private void main() throws InterruptedException, ExecutionException { + ExecutorService service = Executors.newFixedThreadPool(popSize); + ArrayList> futures = new ArrayList>(); + if (firstTime) { + for(int i = 0; i < popSize; i++) { + Individual ind = new Individual(numInputs); + futures.add(service.submit(ind)); + } + firstTime = false; + } + else { + for (Individual indi : individuals) { + futures.add(service.submit(indi)); + } + } + System.out.println("Population size is " + popSize); + double mean = 0; + double best = 0; + while (true) { + Thread.sleep(1); + //System.out.println("num done: "+numDone); + if (GeneticAlgorithm.numDone >= popSize-1) { + System.out.println("Saving Results..."); + individuals = new ArrayList(); + for (int i = 0; i < futures.size(); i++) { + Individual ind = futures.get(i).get(); + mean += ind.getFitness(); + if (ind.getFitness()>best) + best = ind.getFitness(); + int x = 0; + while (x < individuals.size() && individuals.get(x).getFitness() >= ind.getFitness()) { + x++; + } + individuals.add(x, ind); + } + break; + } + } + System.out.println("Generation Score: " + (mean / individuals.size())); + System.out.println("Best Fitness: "+ (best)); + Game.maxFrames += 10;//(int) ((best - oldBest) / 200.0) + 1; + //mutationRate = mutationRate * (oldBest / best + 0.5); +// if (mutationRate > 1) { +// mutationRate = 0.9999; +// } + //mutationRate = 0.99; + + oldBest = (int) best; + System.out.println(Game.maxFrames); + System.out.println(mutationRate); + GeneticAlgorithm.numDone = 0; + select(); + } + + + private void select() { + + /** + * @author Sri Kondapalli + * + * Adds the best individuals from the individuals arrayList, and reproduces pairs of best 70 individuals + * adds 30 new individuals to maintain population size(100). + */ + System.out.println("Selecting..."); + int initSize = individuals.size(); + ArrayList theBest = new ArrayList(); +// for(int i = 0; i < individuals.size() * 0.3; i++) { +// //System.out.println("WAITING HERE12"); +// theBest.add(individuals.get(i)); +// +// } +// for(int i = 0; i < individuals.size() * 0.7; i++) { +// //System.out.println("WAITING HERE1" + " " + i + " " + individuals.size() * 0.69); +// NeuralNetwork m1 = NeuralNetwork.reproduce(individuals.get((int) (Math.random() * individuals.size() * 0.3)).getNN(), individuals.get(i).getNN(), mutationRate); +//// NeuralNetwork m2 = NeuralNetwork.reproduce(individuals.get(i).getNN(), individuals.get(i + 1).getNN(), mutationRate); +// +// theBest.add(new Individual(m1)); +//// theBest.add(new Individual(m2)); +// } + for (int i=0; i<3; i++) + theBest.add(individuals.get(i)); + + for (int i=0; i<5; i++) { + NeuralNetwork m1 = NeuralNetwork.reproduce(individuals.get(i).getNN(), individuals.get(i+1).getNN(), mutationRate); + theBest.add(new Individual(m1)); + } + + while (theBest.size() < initSize) { + //System.out.println("WAITING HERE"); + theBest.add(new Individual(numInputs)); + } + + mutationRate -= mutationRate*0.06; + + Individual.predictionThreshold += Individual.predictionThreshold*0.03; + individuals = theBest; + System.out.println("Finished generation starting next one"); + System.out.println(Individual.jump+", "+Individual.left+", "+Individual.right); + System.out.println("*********************************"); + } +} + + + + + + + diff --git a/src/Individual.java b/src/Individual.java new file mode 100755 index 0000000..30e4f0a --- /dev/null +++ b/src/Individual.java @@ -0,0 +1,98 @@ +import java.util.ArrayList; +import java.util.concurrent.Callable; + +public class Individual implements Callable { + private NeuralNetwork network; + private Game game; + public Individual(int numInputs) { + network = new NeuralNetwork(numInputs); +// network.addLayer(40, Activation.ReLu); + network.addLayer(4, Activation.Sigmoid); + } + + /** + * @author Sri Kondapalli + * @param NeeralNetwork passed in as a requirement for pairs of individuals to reproduce + */ + public Individual(NeuralNetwork n) { + network = n; + } + + public double getFitness() { + return network.getFitness(); + + } + public static double predictionThreshold = 0.9; + public void play() { +// System.out.println("PLAYING"); + double[][] state = game.getState(); + ArrayList newState = new ArrayList(); + + for(int r = 0; r < state.length; r++) { + for(int c = 0; c < state[r].length; c++) { + newState.add(state[r][c]); + } + } + + ArrayList actions = network.predict(newState, predictionThreshold); + + if (actions.get(0) == -1) { + return; + } + + if (actions.size() >= 1 && actions.get(0) >= 1) { + game.moveRight(); + right++; + } + else if(actions.size() >= 2 && actions.get(1) >= 1) { + game.moveLeft(); + left++; + } + else if(actions.size() >= 3 && actions.get(2) >= 1) { + game.jump(); + jump++; + } + } + public static int right = 0; + public static int left = 0; + public static int jump = 0; + + public boolean isDone = false; + public void setDone(boolean f) { + this.isDone = f; + //GeneticAlgorithm.numDone++; + //network.setFitness(game.getFitness()); + //System.out.println("INDIVDONE "+GeneticAlgorithm.numDone); + } + + public NeuralNetwork getNN() { + return network; + + } + + @Override + public String toString() { + return "Individual"; + } + + @Override + public Individual call() { + game = new Game(); + game.indiv = this; + game.start(); + while (true) { + try { + Thread.sleep(1); + if (game.isDone || isDone) + break; + else + play(); + } + + catch (Exception e) {} + } + network.setFitness(game.getFitness()); + GeneticAlgorithm.numDone++; + return this; + } +} diff --git a/src/Keyboard.java b/src/Keyboard.java new file mode 100755 index 0000000..f0dc987 --- /dev/null +++ b/src/Keyboard.java @@ -0,0 +1,51 @@ + + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +public class Keyboard implements KeyListener{ + Mario m = null; + int[] r = new int[3]; + + public Keyboard(Mario m) { + super(); + this.m = m; + } + + @Override + public void keyTyped(KeyEvent e) { +// System.out.println("What"); +// Hello???? + } + + @Override + public void keyPressed(KeyEvent e) { +// System.out.println("keyPressed = " + KeyEvent.getKeyText(e.getKeyCode())); + useKeys(e, 1); + } + + @Override + public void keyReleased(KeyEvent e) { +// System.out.println("keyReleased = " + KeyEvent.getKeyText(e.getKeyCode())); + useKeys(e, 0); + } + + public void useKeys(KeyEvent e, int yup) { + String s = KeyEvent.getKeyText(e.getKeyCode()); + if ((s.equals("W")) || (s.equals("Up"))) { + r[1] = -1 * yup; + } + if ((s.equals("S")) || (s.equals("Down"))) { + r[1] = 1 * yup; + } + if ((s.equals("D")) || (s.equals("Right"))) { + r[0] = 1 * yup; + } + if ((s.equals("A")) || (s.equals("Left"))) { + r[0] = -1 * yup; + } + if (s.equals("Z")) { + r[2] = 1 * yup; + } + } +} diff --git a/src/Mario.java b/src/Mario.java new file mode 100755 index 0000000..9855fac --- /dev/null +++ b/src/Mario.java @@ -0,0 +1,194 @@ +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +public class Mario{ + int x = 0; + int y = 0; + int tilex = 0; + int tiley = 0; + int prev_x = 0; + int prev_y = 0; + final int MAX_SPEED = 24; + double x_vel = 0; + double y_vel = 0; + boolean inAir = true; + boolean moved = false; + + public Mario(int x, int y) { + this.x = x; + this.y = y; + } + + public boolean collided(int x2, int y2) { +// System.out.println(Math.abs(x - x2)); +// System.out.println(Math.abs((618 - y) - y2)); + return ((Math.abs(x - x2) < 48) && (Math.abs((618 - y) - y2) < 48)); + } + + public void jump() { + if (inAir == false) { + y_vel = 48; +// System.out.println("y"); + } + } + + public void moveRight() { + if (!moved) { + x_vel += 1; + moved = true; + } +// System.out.println("x+"); + } + + public void moveLeft() { + if (!moved) { + x_vel -= 1; + moved = true; + } +// System.out.println("x-"); + } + + public void key(int[] useKeys) { +// System.out.print(useKeys[0] + " "); +// System.out.print(useKeys[1] + " "); +// System.out.println(useKeys[2]); + if (useKeys[1] == -1) { + jump(); + } + if (useKeys[0] == 1) { + moveRight(); + } + if (useKeys[0] == -1) { + moveLeft(); + } + + if ((useKeys[2] == 1) && (useKeys[0] != 0)) { + x_vel = x_vel * 1.2; + } + + } + + public int draw(Graphics g, int[][] t, int offset) { + int answer = 0; + + tilex = (int) Math.ceil(x / 48.0); + tiley = (int) Math.ceil((624 - y) / 48.0); + g.setColor(Color.RED); + g.drawLine((tilex * 48) - offset, (tiley * 48), (tilex * 48) + 48 - offset, (tiley * 48)); + g.drawLine((tilex * 48) - offset, (tiley * 48), (tilex * 48) - offset, (tiley * 48) + 48); + g.drawLine((tilex * 48) - offset, (tiley * 48) + 48, (tilex * 48) + 48 - offset, (tiley * 48) + 48); + g.drawLine((tilex * 48) + 48 - offset, (tiley * 48) + 48, (tilex * 48) + 48 - offset, (tiley * 48)); + if (tiley < 0) { + tiley = 0; + } + if (tiley > 11) { + tiley = 11; + } +// System.out.println(tilex); +// System.out.println(tiley); + if (t[tiley][tilex] == 1 && collided(tilex * 48 - offset, tiley * 48)) { +// System.out.println("SUGONDESE"); +// System.out.println(x); +// System.out.println(y); +// System.out.println(prev_x); +// System.out.println(prev_y); +// System.out.println("CO"); + x = prev_x; + y = prev_y; + } + + boolean ti; + if (tiley == 0) { + ti = true; + } + else { + ti = t[tiley - 1][tilex] == 1 && collided(tilex * 48 - offset, (tiley - 1) * 48); + } + + if (ti && y_vel > 0) { + y_vel = 0; + y = y / 48 * 48; + } + + if (t[tiley + 1][tilex] == 1) { + inAir = false; + y = y / 48 * 48; + if (y_vel < 0) { + y_vel = 0; + } + } + else { + inAir = true; + } + + if (t[tiley][tilex + 1] == 1 && x_vel > 0 && collided((tilex + 1) * 48 - offset, tiley * 48)) { + x_vel = 0; + x = tilex * 48 - offset; + } + + if (tilex == 0) { + ti = true; + } + else { + ti = t[tiley][tilex - 1] == 1 && collided((tilex - 1) * 48 - offset, tiley * 48); + } + + if (ti && x_vel < 0) { + x_vel = 0; + x = tilex * 48 - offset; + } + + Image img; + try { + img = ImageIO.read(new File("mario.png")); + g.drawImage(img, x, 624 - y, null); + } catch (IOException e) { + // 1 2 Oatmeal + } + + if (x_vel > MAX_SPEED) { + x_vel = MAX_SPEED; + } + if (x_vel < -MAX_SPEED) { + x_vel = -MAX_SPEED; + } + if (y_vel < -MAX_SPEED) { + y_vel = -MAX_SPEED; + } + + + prev_x = x; + prev_y = y; + + x = (int) Math.round(x + x_vel); + y = (int) Math.round(y + y_vel); + x_vel = x_vel * 0.9; + if (inAir) { + y_vel = y_vel - 5; + x_vel = x_vel * 0.9; + } + if (x > 312) { + answer = x - 312; + x = 312; + } + if (x < 0) { + x = 0; + if (x_vel < 0) { + x_vel = 0; + } + } + + if (y > 624) { + y = 624; + } +// System.out.println(x_vel); +// System.out.println(y_vel); + moved = false; + return answer; + } +} diff --git a/src/NeuralNetwork.java b/src/NeuralNetwork.java new file mode 100755 index 0000000..c04b4d1 --- /dev/null +++ b/src/NeuralNetwork.java @@ -0,0 +1,156 @@ +import java.util.ArrayList; +import java.util.Random; +import java.io.*; + +public class NeuralNetwork implements Serializable { + private int numInputs = 0; + private double fitness = 0; + private ArrayList> layers = new ArrayList>(); + + /** + * Adds a new layer to the network. No need to add the Input Layer + * + * @author Arjun + * @param numNeurons - Number of Neurons + * @param activation - can choose between ReLu, Sigmoid, Tanh + * @return void + */ + public void addLayer(int numNeurons, Activation activation) { + ArrayList newLayer = new ArrayList(); + for (int i = 0; i < numNeurons; i++) { + if (layers.size() == 0) + newLayer.add(new Neuron(activation, numInputs)); + else + newLayer.add(new Neuron(activation, layers.get(layers.size() - 1).size())); + } + layers.add(newLayer); + } + + /** + * Predicts the result based on current weights and biases. First it takes the input and gives + * it to the first layer, the layer then uses it's neurons to create a new list(neurons use the + * propagate method). This list is the new input and is passed into the next layer. This process + * keeps on happening until the program has reached the last layer, where it then returns the + * index of the highest value neuron(a.k.a the prediction). + * + * @author Arjun + * @param input + * @return prediction + */ + public ArrayList predict(ArrayList input, double thresh) { + ArrayList oldRes = input; + ArrayList newRes = new ArrayList(); + + for (int r=0; r(); + } + return getMaxIndexs(oldRes, thresh); + } + + public ArrayList predict(ArrayList input) { + return predict(input, 0.9); + } + + /** + * Reproduce two Neural Networks. Analogous to recombination in meiosis. + * @author Arjun + * + * @param nn1 - First Neural Network + * @param nn2 - Second Neural Network + * @param mutationRate - Higher number will result in more mutations, number should be between 0 and 1 + * @return returns the offspring of the two neural networks passed in + */ + public static NeuralNetwork reproduce(NeuralNetwork nn1, NeuralNetwork nn2, double mutationRate) { + NeuralNetwork newNN = new NeuralNetwork(nn1.numInputs); + + if (mutationRate >= 1 || mutationRate < 0) + throw new RuntimeException("Mutation Rate given is not between 0 and 1 "); + + ArrayList> newLayers = new ArrayList>(); + for (int r=0; r()); + } + + for (int r=0; r getMaxIndexs(ArrayList l, double thresh) { + ArrayList maxIndexs = new ArrayList(); + boolean didFind = false; + for (int i=1; i thresh) { + maxIndexs.add(i); + didFind = true; + } + else { + maxIndexs.add(0); + } + } + if (!didFind) { + maxIndexs = new ArrayList(); + maxIndexs.add(-1); + } + return maxIndexs; + } + + public void save(String path) { + try { + FileOutputStream f = new FileOutputStream(path); + ObjectOutputStream out = new ObjectOutputStream(f); + out.writeObject(this); + out.close(); + f.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public static NeuralNetwork getFromFile(String path) throws EOFException { + try { + FileInputStream fi = new FileInputStream(path); + ObjectInputStream in; + in = new ObjectInputStream(fi); + NeuralNetwork net = (NeuralNetwork) in.readObject(); + in.close(); + fi.close(); + return net; + } + catch (Exception e) { + } + return null; + } + + + public void setFitness(double f) { + fitness = f; + } + + public double getFitness() { + return fitness; + } + + public NeuralNetwork(int numInputs) { + this.numInputs = numInputs; + } + + public ArrayList> getLayers() { + return new ArrayList>(layers); + } + + public void setLayers(ArrayList> l) { + layers = new ArrayList>(l); + } +} diff --git a/src/NeuralNetworkTester.java b/src/NeuralNetworkTester.java new file mode 100755 index 0000000..4c35ad8 --- /dev/null +++ b/src/NeuralNetworkTester.java @@ -0,0 +1,85 @@ +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class NeuralNetworkTester implements Callable> { + public static void main(String args[]) throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newFixedThreadPool(10); + int times = 0; + while (true) { + times++; + System.out.println(times+" times"); + Callable> callable = new NeuralNetworkTester(); + executor.submit(callable).get(); + executor.submit(callable).get(); + } + } + + @Override + public ArrayList call() throws Exception { + return testPredict(); + } + + public ArrayList testPredict() { + NeuralNetwork nn = new NeuralNetwork(5); + nn.addLayer(4, Activation.ReLu); + nn.addLayer(5, Activation.Sigmoid); + nn.addLayer(5, Activation.Sigmoid); + nn.addLayer(5, Activation.Sigmoid); + nn.addLayer(5, Activation.ReLu); + nn.addLayer(5, Activation.Sigmoid); + nn.addLayer(5, Activation.Tanh); + nn.addLayer(5, Activation.Sigmoid); + ArrayList in = new ArrayList(); + in.add(1.0); + in.add(2.0); + in.add(1.0); + in.add(2.0); + in.add(5.0); + return nn.predict(in); + } + + public void testReproduction() { + NeuralNetwork nn2 = new NeuralNetwork(5); + nn2.addLayer(4, Activation.ReLu); + nn2.addLayer(5, Activation.Sigmoid); + + NeuralNetwork nn1 = new NeuralNetwork(5); + nn1.addLayer(4, Activation.ReLu); + nn1.addLayer(5, Activation.Sigmoid); + + NeuralNetwork nn3 = NeuralNetwork.reproduce(nn1, nn2, 0.1); + + boolean didFail = false; + if (nn3.getLayers().size() != nn1.getLayers().size()) { + didFail = true; + System.out.println("FAIL"); + } + + for (int r=0; r weights; + private double bias; + Activation activation; + + public ArrayList getWeights() { + return new ArrayList(weights); + } + + public void setWeights(ArrayList w) { + weights = new ArrayList(w); + } + + public double getBias() { + return bias; + } + + public void setBias(double newB) { + bias = newB; + } + + public Neuron(Activation activation, int numInputs) { + this.activation = activation; + this.bias = (Math.random() * 2) - 1; + weights = new ArrayList(); + for (int i = 0; i < numInputs; i++) { + weights.add((Math.random() * 2) - 1); + } + } + + public Neuron() { + + } + + /** + * Returns the output of the neuron given the appropriate inputs. First it multiplies each + * input to the corresponding weight, then it sums that value and runs it through an activation + * function so that the output is low and non-linear. + * + * @author Arjun + * @param inputs is an ArrayList + * @return output of the neuron + */ + public double propagate(ArrayList inputs) { + if (inputs.size() != weights.size()) + throw new Error("Input size given is not assigned"); + + double sum = 0; + for (int i = 0; i < inputs.size(); i++) { + sum += inputs.get(i) * weights.get(i); + } + sum += bias; + + if (activation == Activation.Sigmoid) + sum = (1 / (1 + Math.pow(Math.E, (-1 * sum)))); + + else if (activation == Activation.ReLu) + sum = Math.max(0.01 * sum, sum); + + else if (activation == Activation.Tanh) + sum = 2 / (1 + Math.pow(Math.E, (-2 * sum))); + + return sum; + } + + public static void print(ArrayList arrayList) { + for (Integer a : arrayList) { + System.out.print(a.intValue()+", "); + } + System.out.print("\n"); + } + + + /** + * Creates offspring of two parent neurons analogous to meiosis. First it selects one of the two + * parents randomly. Then it copies a random section of the parent's weights and adds it to the + * offspring's weights. + * + * @author Arjun + * @param n1 First Parent Neuron + * @param n2 Second Parent Neuron + * @return offspring of the two neurons + */ + public static Neuron reproduce(Neuron n1, Neuron n2, double mutationRate) { + if (n1.getWeights().size() != n2.getWeights().size()) + throw new RuntimeException("Neuron input sizes are not same while trying to reproduce"); + Neuron n = new Neuron(); + ArrayList newWeights = n1.getWeights(); + int lastRandom = 0; + while (true) { + int rand = randomNum(lastRandom, n1.getWeights().size()-1); + for (int i = lastRandom; i < rand; i++) { + double num = Math.random(); + if (num < mutationRate) { + newWeights.set(i, Math.random()); + } + else if (num < 0.5) { + newWeights.set(i, n2.getWeights().get(i)); // need to clone + } + else { + newWeights.set(i, n1.getWeights().get(i)); + } + } + + if (rand >= n1.getWeights().size()-1) { + break; + } + } + + double num = Math.random(); + if (num < mutationRate) + n.setBias(Math.random()); + else if (num < 0.5) + n.setBias(n1.getBias()); + else + n.setBias(n2.getBias()); + + n.setWeights(newWeights); + return n; + } + + private static int randomNum(int min, int max) { + Random r = new Random(); + return r.nextInt((max - min) + 1) + min; + } + + @SuppressWarnings("null") + public double compareTo(Neuron n) { + double sum = 0.0; + + if (n.getWeights().size() != weights.size()) + return (Double) null; + + for (int i=0; i