From 52f6f55e2d6c9ff4af6b92b63a8e40416b2b493b Mon Sep 17 00:00:00 2001
From: Majd Kontar <97162452+MAJDKONTAR@users.noreply.github.com>
Date: Mon, 14 Mar 2022 00:04:06 +0200
Subject: [PATCH] Add files via upload
---
.gitignore | 2 +
Rumba.iml | 49 ++
pom.xml | 126 ++++
src/main/java/com/iea/gui/Agent.java | 676 ++++++++++++++++++
src/main/java/com/iea/gui/AgentBad.java | 153 ++++
src/main/java/com/iea/gui/AgentGood.java | 214 ++++++
src/main/java/com/iea/gui/GuiAdv.java | 213 ++++++
src/main/java/com/iea/gui/GuiCompare.java | 126 ++++
src/main/java/com/iea/gui/GuiDimensions.java | 49 ++
src/main/java/com/iea/gui/GuiFully.java | 220 ++++++
src/main/java/com/iea/gui/GuiGenetic.java | 47 ++
src/main/java/com/iea/gui/GuiMain.java | 52 ++
src/main/java/com/iea/gui/GuiPartly.java | 115 +++
src/main/java/com/iea/gui/GuiRandom.java | 34 +
src/main/java/com/iea/gui/Launcher.java | 7 +
src/main/java/com/iea/gui/Main.java | 31 +
src/main/java/com/iea/gui/Path.java | 119 +++
src/main/java/com/iea/gui/Point.java | 102 +++
src/main/java/com/iea/gui/Room.java | 280 ++++++++
src/main/java/com/iea/gui/Tile.java | 204 ++++++
src/main/java/com/iea/gui/Wall.java | 42 ++
src/main/java/module-info.java | 17 +
src/main/resources/META-INF/MANIFEST.MF | 11 +
.../resources/com/iea/gui/GUIGenetic.fxml | 69 ++
src/main/resources/com/iea/gui/GuiAdv.fxml | 149 ++++
.../resources/com/iea/gui/GuiCompare.fxml | 108 +++
.../resources/com/iea/gui/GuiDimensions.fxml | 54 ++
src/main/resources/com/iea/gui/GuiFully.fxml | 128 ++++
src/main/resources/com/iea/gui/GuiMain.fxml | 55 ++
src/main/resources/com/iea/gui/GuiPartly.fxml | 87 +++
src/main/resources/com/iea/gui/GuiRandom.fxml | 55 ++
src/main/resources/com/iea/gui/Images/Bad.png | Bin 0 -> 31087 bytes
.../resources/com/iea/gui/Images/Dirt.png | Bin 0 -> 1228835 bytes
.../resources/com/iea/gui/Images/Garfield.png | Bin 0 -> 156920 bytes
.../resources/com/iea/gui/Images/Rumba.png | Bin 0 -> 108660 bytes
.../resources/com/iea/gui/Images/tile.png | Bin 0 -> 883707 bytes
target/classes/META-INF/MANIFEST.MF | 11 +
target/classes/com/iea/gui/Agent$1.class | Bin 0 -> 1133 bytes
target/classes/com/iea/gui/Agent$2.class | Bin 0 -> 1103 bytes
target/classes/com/iea/gui/Agent$3.class | Bin 0 -> 1438 bytes
target/classes/com/iea/gui/Agent$4.class | Bin 0 -> 1437 bytes
target/classes/com/iea/gui/Agent.class | Bin 0 -> 19839 bytes
target/classes/com/iea/gui/AgentBad.class | Bin 0 -> 5232 bytes
target/classes/com/iea/gui/AgentGood.class | Bin 0 -> 6746 bytes
target/classes/com/iea/gui/GUIGenetic.fxml | 69 ++
target/classes/com/iea/gui/GuiAdv$1.class | Bin 0 -> 1981 bytes
target/classes/com/iea/gui/GuiAdv.class | Bin 0 -> 8018 bytes
target/classes/com/iea/gui/GuiAdv.fxml | 149 ++++
target/classes/com/iea/gui/GuiCompare$1.class | Bin 0 -> 1999 bytes
target/classes/com/iea/gui/GuiCompare.class | Bin 0 -> 5623 bytes
target/classes/com/iea/gui/GuiCompare.fxml | 108 +++
.../classes/com/iea/gui/GuiDimensions.class | Bin 0 -> 3306 bytes
target/classes/com/iea/gui/GuiDimensions.fxml | 54 ++
target/classes/com/iea/gui/GuiFully$1.class | Bin 0 -> 1232 bytes
target/classes/com/iea/gui/GuiFully$2.class | Bin 0 -> 1353 bytes
target/classes/com/iea/gui/GuiFully$3.class | Bin 0 -> 1353 bytes
target/classes/com/iea/gui/GuiFully$4.class | Bin 0 -> 1351 bytes
target/classes/com/iea/gui/GuiFully.class | Bin 0 -> 7326 bytes
target/classes/com/iea/gui/GuiFully.fxml | 128 ++++
target/classes/com/iea/gui/GuiGenetic.class | Bin 0 -> 2579 bytes
target/classes/com/iea/gui/GuiMain.class | Bin 0 -> 2015 bytes
target/classes/com/iea/gui/GuiMain.fxml | 55 ++
target/classes/com/iea/gui/GuiPartly.class | Bin 0 -> 5271 bytes
target/classes/com/iea/gui/GuiPartly.fxml | 87 +++
target/classes/com/iea/gui/GuiRandom.class | Bin 0 -> 1926 bytes
target/classes/com/iea/gui/GuiRandom.fxml | 55 ++
target/classes/com/iea/gui/Images/Bad.png | Bin 0 -> 31087 bytes
target/classes/com/iea/gui/Images/Dirt.png | Bin 0 -> 1228835 bytes
.../classes/com/iea/gui/Images/Garfield.png | Bin 0 -> 156920 bytes
target/classes/com/iea/gui/Images/Rumba.png | Bin 0 -> 108660 bytes
target/classes/com/iea/gui/Images/tile.png | Bin 0 -> 883707 bytes
target/classes/com/iea/gui/Launcher.class | Bin 0 -> 431 bytes
target/classes/com/iea/gui/Main.class | Bin 0 -> 2187 bytes
target/classes/com/iea/gui/Path.class | Bin 0 -> 3729 bytes
target/classes/com/iea/gui/Point.class | Bin 0 -> 4324 bytes
target/classes/com/iea/gui/Room$1.class | Bin 0 -> 3896 bytes
target/classes/com/iea/gui/Room.class | Bin 0 -> 6557 bytes
target/classes/com/iea/gui/Tile.class | Bin 0 -> 4939 bytes
target/classes/com/iea/gui/Wall.class | Bin 0 -> 1113 bytes
target/classes/com/iea/gui/compare.class | Bin 0 -> 714 bytes
target/classes/module-info.class | Bin 0 -> 522 bytes
81 files changed, 4310 insertions(+)
create mode 100644 .gitignore
create mode 100644 Rumba.iml
create mode 100644 pom.xml
create mode 100644 src/main/java/com/iea/gui/Agent.java
create mode 100644 src/main/java/com/iea/gui/AgentBad.java
create mode 100644 src/main/java/com/iea/gui/AgentGood.java
create mode 100644 src/main/java/com/iea/gui/GuiAdv.java
create mode 100644 src/main/java/com/iea/gui/GuiCompare.java
create mode 100644 src/main/java/com/iea/gui/GuiDimensions.java
create mode 100644 src/main/java/com/iea/gui/GuiFully.java
create mode 100644 src/main/java/com/iea/gui/GuiGenetic.java
create mode 100644 src/main/java/com/iea/gui/GuiMain.java
create mode 100644 src/main/java/com/iea/gui/GuiPartly.java
create mode 100644 src/main/java/com/iea/gui/GuiRandom.java
create mode 100644 src/main/java/com/iea/gui/Launcher.java
create mode 100644 src/main/java/com/iea/gui/Main.java
create mode 100644 src/main/java/com/iea/gui/Path.java
create mode 100644 src/main/java/com/iea/gui/Point.java
create mode 100644 src/main/java/com/iea/gui/Room.java
create mode 100644 src/main/java/com/iea/gui/Tile.java
create mode 100644 src/main/java/com/iea/gui/Wall.java
create mode 100644 src/main/java/module-info.java
create mode 100644 src/main/resources/META-INF/MANIFEST.MF
create mode 100644 src/main/resources/com/iea/gui/GUIGenetic.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiAdv.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiCompare.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiDimensions.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiFully.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiMain.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiPartly.fxml
create mode 100644 src/main/resources/com/iea/gui/GuiRandom.fxml
create mode 100644 src/main/resources/com/iea/gui/Images/Bad.png
create mode 100644 src/main/resources/com/iea/gui/Images/Dirt.png
create mode 100644 src/main/resources/com/iea/gui/Images/Garfield.png
create mode 100644 src/main/resources/com/iea/gui/Images/Rumba.png
create mode 100644 src/main/resources/com/iea/gui/Images/tile.png
create mode 100644 target/classes/META-INF/MANIFEST.MF
create mode 100644 target/classes/com/iea/gui/Agent$1.class
create mode 100644 target/classes/com/iea/gui/Agent$2.class
create mode 100644 target/classes/com/iea/gui/Agent$3.class
create mode 100644 target/classes/com/iea/gui/Agent$4.class
create mode 100644 target/classes/com/iea/gui/Agent.class
create mode 100644 target/classes/com/iea/gui/AgentBad.class
create mode 100644 target/classes/com/iea/gui/AgentGood.class
create mode 100644 target/classes/com/iea/gui/GUIGenetic.fxml
create mode 100644 target/classes/com/iea/gui/GuiAdv$1.class
create mode 100644 target/classes/com/iea/gui/GuiAdv.class
create mode 100644 target/classes/com/iea/gui/GuiAdv.fxml
create mode 100644 target/classes/com/iea/gui/GuiCompare$1.class
create mode 100644 target/classes/com/iea/gui/GuiCompare.class
create mode 100644 target/classes/com/iea/gui/GuiCompare.fxml
create mode 100644 target/classes/com/iea/gui/GuiDimensions.class
create mode 100644 target/classes/com/iea/gui/GuiDimensions.fxml
create mode 100644 target/classes/com/iea/gui/GuiFully$1.class
create mode 100644 target/classes/com/iea/gui/GuiFully$2.class
create mode 100644 target/classes/com/iea/gui/GuiFully$3.class
create mode 100644 target/classes/com/iea/gui/GuiFully$4.class
create mode 100644 target/classes/com/iea/gui/GuiFully.class
create mode 100644 target/classes/com/iea/gui/GuiFully.fxml
create mode 100644 target/classes/com/iea/gui/GuiGenetic.class
create mode 100644 target/classes/com/iea/gui/GuiMain.class
create mode 100644 target/classes/com/iea/gui/GuiMain.fxml
create mode 100644 target/classes/com/iea/gui/GuiPartly.class
create mode 100644 target/classes/com/iea/gui/GuiPartly.fxml
create mode 100644 target/classes/com/iea/gui/GuiRandom.class
create mode 100644 target/classes/com/iea/gui/GuiRandom.fxml
create mode 100644 target/classes/com/iea/gui/Images/Bad.png
create mode 100644 target/classes/com/iea/gui/Images/Dirt.png
create mode 100644 target/classes/com/iea/gui/Images/Garfield.png
create mode 100644 target/classes/com/iea/gui/Images/Rumba.png
create mode 100644 target/classes/com/iea/gui/Images/tile.png
create mode 100644 target/classes/com/iea/gui/Launcher.class
create mode 100644 target/classes/com/iea/gui/Main.class
create mode 100644 target/classes/com/iea/gui/Path.class
create mode 100644 target/classes/com/iea/gui/Point.class
create mode 100644 target/classes/com/iea/gui/Room$1.class
create mode 100644 target/classes/com/iea/gui/Room.class
create mode 100644 target/classes/com/iea/gui/Tile.class
create mode 100644 target/classes/com/iea/gui/Wall.class
create mode 100644 target/classes/com/iea/gui/compare.class
create mode 100644 target/classes/module-info.class
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ab9f633
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+# Project exclude paths
+/target/
\ No newline at end of file
diff --git a/Rumba.iml b/Rumba.iml
new file mode 100644
index 0000000..d1c6094
--- /dev/null
+++ b/Rumba.iml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7afaddb
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,126 @@
+
+
+ 4.0.0
+ com.IEA
+ GUI
+ 1.0-SNAPSHOT
+ GUI
+
+
+ UTF-8
+ 5.7.1
+
+
+
+
+ org.openjfx
+ javafx-controls
+ 17-ea+11
+
+
+ org.openjfx
+ javafx-fxml
+ 17-ea+11
+
+
+ org.openjfx
+ javafx-web
+ 17-ea+11
+
+
+ org.controlsfx
+ controlsfx
+ 11.1.0
+
+
+ com.dlsc.formsfx
+ formsfx-core
+ 11.3.2
+
+
+ org.openjfx
+ *
+
+
+
+
+ net.synedra
+ validatorfx
+ 0.1.13
+
+
+ org.openjfx
+ *
+
+
+
+
+ org.kordamp.ikonli
+ ikonli-javafx
+ 12.2.0
+
+
+ org.kordamp.bootstrapfx
+ bootstrapfx-core
+ 0.4.0
+
+
+ eu.hansolo
+ tilesfx
+ 11.48
+
+
+ org.openjfx
+ *
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+ org.openjfx
+ javafx-graphics
+ 17-ea+11
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 14
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.6
+
+
+
+ default-cli
+
+ com.iea.gui/com.iea.gui.Main
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/iea/gui/Agent.java b/src/main/java/com/iea/gui/Agent.java
new file mode 100644
index 0000000..762b861
--- /dev/null
+++ b/src/main/java/com/iea/gui/Agent.java
@@ -0,0 +1,676 @@
+package com.iea.gui;
+
+import javafx.scene.image.Image;
+import javafx.scene.shape.Rectangle;
+
+import java.util.*;
+
+public class Agent extends Rectangle {
+ private Point position;
+ int cost, combinations;
+ long time;
+ long t;
+ String mode;
+ int deltaX;
+ int deltaY;
+ int[][] adjacencyMatrix;
+ int[] path;
+ Path[][] pathBetweenNode;
+ public Tile[][] observedRoom;
+ boolean[][] visited;
+ Stack stack;
+ ArrayList memory;
+ ArrayList roomTiles;
+ public int status;
+ int delay = 300;
+ String type;
+ int epoch = 10000;
+ int population = 5000;
+ double crossOverRate = 0.2;
+ double mutationRate = 0.7;
+
+ public Agent(Point position, int x, int y, int w, int h, String type) {
+ super(w, h);
+ this.type = type;
+ this.position = new Point(position);
+ setTranslateX(x);
+ setTranslateY(y);
+ cost = Integer.MAX_VALUE;
+ combinations = 0;
+ time = 0;
+ deltaX = w + 5;
+ deltaY = h + 5;
+ memory = new ArrayList<>();
+ roomTiles = new ArrayList<>();
+ }
+
+ public Agent(Agent agent, int x, int y, int w) {
+ super(w, w);
+ this.type = agent.type;
+ this.position = new Point(agent.position);
+ setTranslateX(x);
+ setTranslateY(y);
+ cost = agent.cost;
+ combinations = agent.combinations;
+ time = agent.time;
+ deltaX = w + 5;
+ deltaY = w + 5;
+ memory = new ArrayList<>(agent.memory);
+ roomTiles = new ArrayList<>(agent.roomTiles);
+ }
+
+ public Agent(Agent agent) {
+ this.position = new Point(agent.getPosition());
+ this.type = agent.type;
+ this.status = agent.status;
+ }
+
+ void moveLeft() {
+ setTranslateX(getTranslateX() - deltaX);
+ position.translate(-1, 0);
+ }
+
+ void moveRight() {
+ setTranslateX(getTranslateX() + deltaX);
+ position.translate(1, 0);
+ }
+
+ void moveUp() {
+ setTranslateY(getTranslateY() - deltaY);
+ position.translate(0, -1);
+ }
+
+ void moveDown() {
+ setTranslateY(getTranslateY() + deltaY);
+ position.translate(0, 1);
+ }
+
+ int[][] generateAdjacencyMatrix(List cities, Tile[][] tiles, boolean messageDisplayed) {
+ int distance;
+ int[][] tileWeights = new int[Main.game.getHeight()][Main.game.getWidth()];
+ int dirt = cities.size();
+ cities.add(new Point(getPosition()));
+ int[][] adjMatrix = new int[dirt + 1][dirt + 1];
+ pathBetweenNode = new Path[dirt + 1][dirt + 1];
+ Arrays.stream(adjMatrix).forEach(a -> Arrays.fill(a, -1));
+ Queue frontier = new ArrayDeque<>();
+ for (int i = dirt; i > 0; i--) {
+ frontier.clear();
+ frontier.add(cities.get(i));
+ Arrays.stream(tileWeights).forEach(a -> Arrays.fill(a, -1));
+ tileWeights[frontier.peek().y][frontier.peek().x] = 0;
+ for (int j = i - 1; j >= 0; j--) {
+ if (tileWeights[cities.get(j).y][cities.get(j).x] != -1) {
+ distance = tileWeights[cities.get(j).y][cities.get(j).x];
+ } else {
+ distance = getDistanceBFS(frontier, cities.get(j), tileWeights, tiles);
+ }
+ if (distance == -1) {
+ cities.remove(j);
+ if (!messageDisplayed) {
+// GUI.displayMessage("ERROR", "Can not reach dirty tile!");
+ }
+ return generateAdjacencyMatrix(cities, tiles, true);
+ } else {
+ Path p = new Path(backtrack(cities.get(i), cities.get(j), tileWeights));
+ p.setCost(distance);
+ pathBetweenNode[i][j] = new Path(p);
+ pathBetweenNode[j][i] = new Path(p.reverse());
+ adjMatrix[i][j] = adjMatrix[j][i] = distance;
+ }
+ }
+ }
+ adjacencyMatrix = adjMatrix;
+ return adjMatrix;
+ }
+
+
+ int getDistance(Room room, Point destination) {
+ int[][] tileWeights = new int[Main.game.getHeight()][Main.game.getWidth()];
+ Arrays.stream(tileWeights).forEach(a -> Arrays.fill(a, -1));
+ Queue frontier = new ArrayDeque<>();
+ frontier.add(new Point(getPosition()));
+ tileWeights[getPosition().y][getPosition().x] = 0;
+ while (!frontier.isEmpty()) {
+ Point source = frontier.remove();
+ Point current = new Point(source);
+ ArrayList neighbors = current.getNeighbors(room, destination);
+ for (Point p : neighbors) {
+ if (tileWeights[p.y][p.x] == -1) {
+ tileWeights[p.y][p.x] = tileWeights[source.y][source.x] + 1;
+ frontier.add(new Point(p));
+ }
+ }
+ if (source.equals(destination)) {
+ return tileWeights[destination.y][destination.x];
+ }
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ public int start(Room room, boolean isMaximizer) {
+ return -1;
+ }
+
+ static int getDistanceBFS(Queue frontier, Point destination, int[][] tileWeights, Tile[][] tiles) {
+ while (!frontier.isEmpty()) {
+ Point source = frontier.remove();
+ Point current = new Point(source);
+ ArrayList neighbors = current.getNeighbors();
+ for (Point p : neighbors) {
+ if ((tiles[p.y][p.x].isValid()) && tileWeights[p.y][p.x] == -1) {
+ tileWeights[p.y][p.x] = tileWeights[source.y][source.x] + 1;
+ frontier.add(new Point(p));
+ }
+ }
+ if (source.equals(destination)) {
+ return tileWeights[destination.y][destination.x];
+ }
+ }
+ return -1;
+ }
+
+ static Path backtrack(Point source, Point destination, int[][] weights) {
+ Path path = new Path();
+ Point current = new Point(destination);
+ path.addNode(destination);
+ cont:
+ while (!current.equals(source)) {
+ ArrayList neighbors = current.getNeighbors(true);
+ for (Point p : neighbors) {
+ if (weights[p.y][p.x] == weights[destination.y][destination.x] - 1) {
+ path.addNodeFirst(new Point(p));
+ destination = new Point(p);
+ current = new Point(p);
+ continue cont;
+ }
+ }
+ current = new Point(path.path().remove(0));
+ }
+ return path;
+ }
+
+ public void partiallyObservable() {
+ mode="prev";
+ stack = new Stack<>();
+ observedRoom = new Tile[Main.game.getHeight()][Main.game.getWidth()];
+ visited = new boolean[Main.game.getHeight()][Main.game.getWidth()];
+ stack.push(position);
+ visited[position.y][position.x] = true;
+ observedRoom[position.y][position.x] = new Tile(position.x, position.y);
+ observedRoom[position.y][position.x].setStatus(3);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ getRoom();
+ if (stack.isEmpty()) {
+ timer.purge();
+ timer.cancel();
+ t = System.currentTimeMillis();
+ try {
+ phase2();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }, 0, delay);
+ }
+
+ private void phase2() throws InterruptedException {
+ Timer timer1 = new Timer();
+ timer1.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ if (System.currentTimeMillis() - t > 1000) {
+ if(mode.equals("prev")){
+ mode="sweep";
+ System.out.println("Cleaning Previous");
+ cleanPrevs();}
+ else if(mode.equals("sweep")){
+ mode="prev";
+ System.out.println("Cleaning All");
+ sweepRoom();
+ }
+ }
+ }
+ }, 0, delay);
+ }
+
+ private void cleanPrevs() {
+ generateAdjacencyMatrix(memory, observedRoom, false);
+ nearestNeighbor();
+ Path path = Main.game.getPath(Main.game.agent);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ t=System.currentTimeMillis();
+ newDirtyTiles();
+ Point p = path.path().remove(0);
+ Main.game.goToNext(Main.game.agent, p);
+ } catch (Exception e) {
+ t=System.currentTimeMillis();
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, delay);
+ }
+
+ public void newDirtyTiles() {
+ ArrayList neighbors = position.getNeighbors();
+ for (Point n : neighbors) {
+ if (Main.game.tiles[n.y][n.x].isDirty() && !memory.contains(n)) {
+ memory.add(n);
+ observedRoom[n.y][n.x].incrementCount();
+ }
+ }
+ }
+
+ private void sweepRoom() {
+ generateAdjacencyMatrix(roomTiles, observedRoom, false);
+ nearestNeighbor();
+ Path path = Main.game.getPath(Main.game.agent);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ t=System.currentTimeMillis();
+ newDirtyTiles();
+ Point p = path.path().remove(0);
+ Main.game.goToNext(Main.game.agent, p);
+ } catch (Exception e) {
+ t=System.currentTimeMillis();
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, delay);
+ }
+
+ private void getRoom() {
+ ArrayList neighbors = position.getNeighbors();
+ roomTiles.add(new Point(position));
+ observedRoom[position.y][position.x] = new Tile(position.x, position.y);
+ observedRoom[position.y][position.x].hasWallRight = Main.game.tiles[position.y][position.x].hasWallRight();
+ observedRoom[position.y][position.x].hasWallLeft = Main.game.tiles[position.y][position.x].hasWallLeft();
+ observedRoom[position.y][position.x].hasWallUp = Main.game.tiles[position.y][position.x].hasWallUp();
+ observedRoom[position.y][position.x].hasWallDown = Main.game.tiles[position.y][position.x].hasWallDown();
+ for (Point n : neighbors) {
+ if (!visited[n.y][n.x]) {
+ if (!roomTiles.contains(n)) {
+ roomTiles.add(new Point(n));
+ }
+ observedRoom[n.y][n.x] = new Tile(n.x, n.y);
+ if (Main.game.tiles[n.y][n.x].isClean()) {
+ observedRoom[n.y][n.x].setStatus(0);
+ } else {
+ visited[n.y][n.x] = true;
+ observedRoom[n.y][n.x].incrementCount();
+ if (!memory.contains(new Point(n)))
+ memory.add(new Point(n));
+ stack.push(new Point(n));
+ Main.game.goToNext(Main.game.agent, n);
+ return;
+ }
+ }
+ }
+ for (Point n : neighbors) {
+ if (!visited[n.y][n.x]) {
+ visited[n.y][n.x] = true;
+ stack.push(new Point(n));
+ Main.game.goToNext(Main.game.agent, n);
+ return;
+ }
+ }
+ try {
+ stack.pop();
+ Main.game.goToNext(Main.game.agent, stack.peek());
+ } catch (Exception e) {
+ }
+ }
+
+ public void bruteForce() {
+ long startTime = System.currentTimeMillis();
+ int size = getAdjacencyMatrix().length;
+ path = new int[size];
+ setCombinations(0);
+ setCost(Integer.MAX_VALUE);
+ boolean[] visited = new boolean[size - 1];
+ getPath()[0] = size - 1;
+ bruteForceRecursive(size - 1, visited, 0, 1, getPath());
+ setTime(System.currentTimeMillis() - startTime);
+ }
+
+ private void bruteForceRecursive(int source, boolean[] visited, int cost, int level, int[] p) {
+ if (level == getPath().length && cost < getCost()) {
+ setCombinations(getCombinations() + 1);
+ setCost(cost);
+ path = p.clone();
+ return;
+ }
+ for (int i = 0; i < p.length - 1; i++) {
+ if (!visited[i]) {
+ visited[i] = true;
+ int edgeCost = getAdjacencyMatrix()[source][i];
+ p[level] = i;
+ cost += edgeCost;
+ if (cost < getCost()) {
+ bruteForceRecursive(i, visited, cost, level + 1, p);
+ }
+ visited[i] = false;
+ cost -= edgeCost;
+ }
+ }
+ }
+
+ public void branchAndBound() {
+ long startTime = System.currentTimeMillis();
+ int size = getAdjacencyMatrix().length;
+ int[][] minEdgesFromNode = new int[size][2];
+ int[][] weight = getAdjacencyMatrix().clone();
+ boolean[] visited = new boolean[size];
+ path = new int[size];
+ int cost = 0;
+ for (int i = 0; i < size; i++) {
+ minEdgesFromNode[i][0] = getMinEdge(weight, i);
+ minEdgesFromNode[i][1] = getSecondMinEdge(weight, i);
+ cost += minEdgesFromNode[i][0] + minEdgesFromNode[i][1];
+ }
+ cost = (cost == 1) ? 1 : cost / 2;
+ path[0] = size - 1;
+ visited[size - 1] = true;
+ branchAndBoundRecursive(weight, cost, 0, 1, path, visited, minEdgesFromNode);
+ setTime(System.currentTimeMillis() - startTime);
+ }
+
+ private void branchAndBoundRecursive(int[][] weight, int cost, int currentCost, int level, int[] p, boolean[] visited, int[][] minEdgesFromNode) {
+ if (level == weight.length) {
+ setCombinations(getCombinations() + 1);
+ if (currentCost < getCost()) {
+ this.path = p.clone();
+ setCost(currentCost);
+ return;
+ }
+ }
+ for (int i = 0; i < weight.length; i++) {
+ if (!visited[i]) {
+ int temp = cost;
+ currentCost += weight[p[level - 1]][i];
+ if (level == 1) {
+ cost -= (minEdgesFromNode[p[level - 1]][0] + minEdgesFromNode[i][0]) / 2;
+ } else {
+ cost -= (minEdgesFromNode[p[level - 1]][1] + minEdgesFromNode[i][0]) / 2;
+ }
+ if (cost + currentCost < getCost()) {
+ p[level] = i;
+ visited[i] = true;
+ branchAndBoundRecursive(weight, cost, currentCost, level + 1, p, visited, minEdgesFromNode);
+ }
+ currentCost -= weight[p[level - 1]][i];
+ cost = temp;
+ visited[i] = false;
+ }
+ }
+ }
+
+ public void nearestNeighbor() {
+ long startTime = System.currentTimeMillis();
+ int size = getAdjacencyMatrix().length;
+ setCost(0);
+ path = new int[size];
+ boolean[] visited = new boolean[size - 1];
+ int level = 1;
+ int indexOfMinimum = -1;
+ getPath()[level - 1] = size - 1;
+ while (level < size) {
+ int minimum = Integer.MAX_VALUE;
+ for (int i = 0; i < size - 1; i++) {
+ int temp = getAdjacencyMatrix()[getPath()[level - 1]][i];
+ if (temp < minimum && temp != -1 && !visited[i]) {
+ minimum = temp;
+ indexOfMinimum = i;
+ }
+ }
+ if (indexOfMinimum != -1) {
+ visited[indexOfMinimum] = true;
+ setCost(getCost() + minimum);
+ level++;
+ getPath()[level - 1] = indexOfMinimum;
+ }
+ }
+ setTime(System.currentTimeMillis() - startTime);
+ }
+
+ public void genetic() {
+ Random rnd = new Random();
+ int size = getAdjacencyMatrix().length;
+ Path[] chromosomes = new Path[population];
+ Path best = new Path();
+ path = new int[size];
+ best.setCost(Integer.MAX_VALUE);
+ for (int i = 0; i < population; i++) {
+ chromosomes[i] = new Path(getRandomPath(rnd));
+ }
+ for (int i = 1; i <= epoch; i++) {
+ chromosomes = selection(chromosomes);
+ crossOver(crossOverRate, chromosomes, rnd);
+ mutate(mutationRate, chromosomes, rnd);
+ computeCost(chromosomes);
+ best = new Path(getBestPath(best, chromosomes));
+ System.out.println("Epoch: " + i + "\n\tBest Path: " + best.printGenetic() + "\tCost: " + best.cost());
+ }
+ this.setPath(best.getPath());
+ }
+
+ private void crossOver(double crossOverRate, Path[] chromosomes, Random rnd) {
+ Arrays.sort(chromosomes, new compare());
+ for (int i = 0; i < chromosomes.length - 1; i++) {
+ if (Math.random() < crossOverRate) {
+ int i1 = rnd.nextInt(chromosomes[i].getGeneticPath().size() - 1) + 1;
+ for (int k = i1; k < chromosomes[i].getGeneticPath().size(); k++) {
+ for (int j = 0; j < chromosomes[i].getGeneticPath().size(); j++) {
+ if (!chromosomes[i + 1].getGeneticPath().subList(0, k).contains(chromosomes[i].getGeneticPath().get(j))) {
+ chromosomes[i + 1].getGeneticPath().set(k, chromosomes[i].getGeneticPath().get(j));
+ break;
+ }
+ }
+ }
+ for (int k = i1; k < chromosomes[i].getGeneticPath().size(); k++) {
+ for (int j = 0; j < chromosomes[i].getGeneticPath().size(); j++) {
+ if (!chromosomes[i].getGeneticPath().subList(0, k).contains(chromosomes[i + 1].getGeneticPath().get(j))) {
+ chromosomes[i].getGeneticPath().set(k, chromosomes[i + 1].getGeneticPath().get(j));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private Path[] selection(Path[] chromosomes) {
+ Path[] toReturn = new Path[chromosomes.length];
+ Arrays.sort(chromosomes, new compare());
+ Hashtable dict = new Hashtable<>(createRoulette(chromosomes));
+ for (int i = 0; i < chromosomes.length; i++) {
+ double random = Math.random();
+ for (int j = 0; j < chromosomes.length; j++) {
+ if (random < dict.get(j)) {
+ toReturn[i] = new Path(chromosomes[j]);
+ break;
+ }
+ }
+ }
+ return toReturn;
+ }
+
+ private Hashtable createRoulette(Path[] chromosomes) {
+ Hashtable toReturn = new Hashtable<>();
+ double cumm = 0;
+ double totalFitness = 0;
+ for (Path chromosome : chromosomes) {
+ totalFitness += chromosome.getFitness();
+ }
+ for (int i = 0; i < chromosomes.length; i++) {
+ cumm = (chromosomes[i].getFitness() / totalFitness) + cumm;
+ toReturn.put(i, cumm);
+ }
+ return toReturn;
+ }
+
+ private void mutate(double mutationRate, Path[] paths, Random rnd) {
+ for (Path path : paths) {
+ if (Math.random() < mutationRate) {
+ for (int i = 0; i < rnd.nextInt(path.getGeneticPath().size() - 1) + 1; i++) {
+ int i1 = rnd.nextInt(path.getGeneticPath().size() - 1) + 1;
+ int i2 = rnd.nextInt(path.getGeneticPath().size() - 1) + 1;
+ Collections.swap(path.getGeneticPath(), i1, i2);
+ }
+ }
+ }
+ }
+
+ private Path getBestPath(Path best, Path[] paths) {
+ for (Path p : paths) {
+ if (p.cost() < best.cost()) {
+ best = new Path(p);
+ }
+ }
+ return best;
+ }
+
+ private void computeCost(Path[] paths) {
+ int cost = 0;
+ for (Path p : paths) {
+ for (int i = 1; i < p.getGeneticPath().size(); i++) {
+ cost += getAdjacencyMatrix()[p.getGeneticPath().get(i - 1)][p.getGeneticPath().get(i)];
+ p.setCost(cost);
+ }
+ }
+ }
+
+ private Path getRandomPath(Random rnd) {
+ int size = getAdjacencyMatrix().length;
+ int cost = 0;
+ Path toReturn = new Path();
+ int[] path = new int[size];
+ int i = 1;
+ boolean[] visited = new boolean[size];
+ path[0] = size - 1;
+ while (i < size) {
+ int node = rnd.nextInt(size - 1);
+ if (!visited[node]) {
+ cost += getAdjacencyMatrix()[path[i - 1]][path[i]];
+ visited[node] = true;
+ path[i] = node;
+ i++;
+ }
+ }
+ toReturn.setGeneticPath(path);
+ toReturn.setCost(cost);
+ return toReturn;
+ }
+
+ static int getMinEdge(int[][] weight, int i) {
+ int minimum = Integer.MAX_VALUE;
+ for (int j = 0; j < weight[0].length; j++) {
+ if (weight[i][j] < minimum && weight[i][j] != -1 && i != j) {
+ minimum = weight[i][j];
+ }
+ }
+ return minimum;
+ }
+
+ static int getSecondMinEdge(int[][] weight, int i) {
+ int minimum = Integer.MAX_VALUE, secondMinimum = Integer.MAX_VALUE;
+ for (int j = 0; j < weight[0].length; j++) {
+ if (i == j)
+ continue;
+ if (weight[i][j] <= minimum && weight[i][j] != -1) {
+ secondMinimum = minimum;
+ minimum = weight[i][j];
+ } else if (weight[i][j] <= secondMinimum && weight[i][j] != minimum && weight[i][j] != -1) {
+ secondMinimum = weight[i][j];
+ }
+ }
+ return secondMinimum;
+ }
+
+ public int[][] getAdjacencyMatrix() {
+ return adjacencyMatrix;
+ }
+
+
+ public int[] getPath() {
+ return path;
+ }
+
+ public void setPath(int[] path) {
+ this.path = path;
+ }
+
+ public Path[][] getPathBetweenNode() {
+ return pathBetweenNode;
+ }
+
+
+ public Point getPosition() {
+ return position;
+ }
+
+ public void setPosition(Point p) {
+ position = new Point(p);
+ }
+
+ public int getCost() {
+ return cost;
+ }
+
+ public void setCost(int c) {
+ cost = c;
+ }
+
+ public int getCombinations() {
+ return combinations;
+ }
+
+ public void setCombinations(int c) {
+ combinations = c;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ public void setTime(long t) {
+ time = t;
+ }
+
+ public Point getNextMove() {
+ return null;
+ }
+
+ public void setNextMove(Point p) {
+ }
+
+ public void setDepth(int value) {
+ }
+}
+
+class compare implements Comparator {
+ @Override
+ public int compare(Path o1, Path o2) {
+ return (o1.cost() - o2.cost());
+ }
+}
+
+//class compareMem implements Comparator {
+// @Override
+// public int compare(Point o1, Point o2) {
+// return Agent.observedRoom[o1.y][o1.x].getCount() - Agent.observedRoom[o2.y][o2.x].getCount();
+// }
+//}
diff --git a/src/main/java/com/iea/gui/AgentBad.java b/src/main/java/com/iea/gui/AgentBad.java
new file mode 100644
index 0000000..fa7fbad
--- /dev/null
+++ b/src/main/java/com/iea/gui/AgentBad.java
@@ -0,0 +1,153 @@
+package com.iea.gui;
+
+import javafx.scene.image.Image;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.ImagePattern;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+public class AgentBad extends Agent {
+ Point nextMove;
+ int maxGain;
+ int minGain;
+ int depth = 5;
+ static Image bad;
+
+ static {
+ bad = new Image(String.valueOf(AgentBad.class.getResource("Images/Garfield.png")));
+ }
+
+ // Point position;
+ AgentBad(Point position, int x, int y, int w, int h, String type) {
+ super(position, x, y, w, h, type);
+ nextMove = new Point(getPosition());
+// this.position=position;
+ status = 1;
+ setFill(new ImagePattern(bad));
+ }
+
+ AgentBad(AgentBad agent) {
+ super(agent);
+ status = 1;
+ setFill(new ImagePattern(bad));
+ }
+
+ public int start(Room room, boolean notRumba) {
+ maxGain = Integer.MIN_VALUE;
+ minGain = Integer.MAX_VALUE;
+ Main.game.moves = new ArrayDeque<>();
+ nextMove = getPosition();
+ if (type.endsWith("Minimax")) {
+ return minimax(Main.game, depth, notRumba);
+ } else {
+ return minimax(Main.game, depth, notRumba, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ }
+ }
+
+ public int minimax(Room room, int moves, boolean notRumba) {
+ if (done(room, moves)) {
+ return room.dirtArray.size();
+ }
+ if (notRumba) {
+ ArrayList neighbors = room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ if (room.tiles[n.y][n.x].getStatus() != 1) {
+ Room room1 = new Room(room);
+ room1.goToNext(room1.agents.get(Main.game.agents.indexOf(this)), n);
+ room1.moves.offer(new Point(n));
+ int gain = minimax(room1, moves - 1, false);
+ if (maxGain < gain) {
+ maxGain = gain;
+ nextMove = new Point(room1.moves.peek());
+ }
+ }
+ }
+ return maxGain;
+ } else {
+ for (Agent agent : room.agents) {
+ if (agent.type.startsWith("Good")) {
+ ArrayList neighbors = agent.getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ Room room2 = new Room(room);
+ room2.goToNext(room2.agents.get(room.agents.indexOf(agent)), n);
+ int gain = minimax(room2, moves - 1, true);
+ if (minGain > gain) {
+ minGain = gain;
+ }
+ }
+ }
+ }
+ return minimax(room, moves - 1, true);
+ }
+ }
+
+ private boolean done(Room room, int moves) {
+ boolean noTiles = true;
+ for (Point p : room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room)) {
+ if (room.tiles[p.y][p.x].getStatus() == 0) {
+ noTiles = false;
+ }
+ }
+ return moves == 0 || noTiles || room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room).size() == 0;
+ }
+
+ public int minimax(Room room, int moves, boolean notRumba, int alpha, int beta) {
+ if (done(room, moves)) {
+ return room.dirtArray.size();
+ }
+ if (notRumba) {
+ ArrayList neighbors = room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ if (room.tiles[n.y][n.x].getStatus() != 1) {
+ Room room1 = new Room(room);
+ room1.goToNext(room1.agents.get(Main.game.agents.indexOf(this)), n);
+ room1.moves.offer(new Point(n));
+ int gain = minimax(room1, moves - 1, false, alpha, beta);
+ if (maxGain < gain) {
+ maxGain = gain;
+ nextMove = new Point(room1.moves.peek());
+ }
+ alpha = Math.max(alpha, maxGain);
+ if (beta <= alpha) {
+ break;
+ }
+ }
+ }
+ return maxGain;
+ } else {
+ for (Agent agent : room.agents) {
+ if (agent.type.startsWith("Good")) {
+ ArrayList neighbors = agent.getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ Room room2 = new Room(room);
+ room2.goToNext(room2.agents.get(room.agents.indexOf(agent)), n);
+ int gain = minimax(room2, moves - 1, true, alpha, beta);
+ if (minGain > gain) {
+ minGain = gain;
+ }
+ beta = Math.min(beta, minGain);
+ if (beta <= alpha) {
+ break;
+ }
+ }
+ }
+ }
+ return minimax(room, moves - 1, true, alpha, beta);
+ }
+ }
+
+ public Point getNextMove() {
+ return nextMove;
+ }
+
+ public void setNextMove(Point nextMove) {
+ this.nextMove = nextMove;
+ }
+
+ public void setDepth(int depth) {
+ this.depth = depth;
+ }
+}
diff --git a/src/main/java/com/iea/gui/AgentGood.java b/src/main/java/com/iea/gui/AgentGood.java
new file mode 100644
index 0000000..c3af635
--- /dev/null
+++ b/src/main/java/com/iea/gui/AgentGood.java
@@ -0,0 +1,214 @@
+package com.iea.gui;
+
+import javafx.scene.image.Image;
+import javafx.scene.paint.ImagePattern;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.*;
+
+public class AgentGood extends Agent {
+ Point nextMove;
+ int maxGain;
+ int minGain;
+ int depth = 5;
+ int count;
+ Image rumba;
+
+ public AgentGood(Point position, int x, int y, int w, int h, String type) {
+ super(position, x, y, w, h, type);
+ nextMove = new Point(getPosition());
+ rumba = new Image(String.valueOf(AgentGood.class.getResource("Images/Rumba.png")));
+ setFill(new ImagePattern(rumba));
+ status = 0;
+ }
+
+ public AgentGood(AgentGood agent) {
+ super(agent);
+ rumba = new Image(String.valueOf(AgentGood.class.getResource("Images/Rumba.png")));
+ setFill(new ImagePattern(rumba));
+ status = 0;
+ }
+
+ public AgentGood(AgentGood agent, int x, int y, int size) {
+ super(agent, x, y, size);
+ }
+
+ public int start(Room room, boolean isRumba) {
+ maxGain = Integer.MIN_VALUE;
+ minGain = Integer.MAX_VALUE;
+ Main.game.moves = new ArrayDeque<>();
+ nextMove = getPosition();
+ if (type.endsWith("Minimax")) {
+ return minimax(Main.game, depth, isRumba);
+ } else if (type.endsWith("AlphaBeta")) {
+ return minimax(Main.game, depth, isRumba, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ } else {
+ minGain = 0;
+ return expectimax(Main.game, depth, isRumba);
+ }
+ }
+
+
+ public int minimax(Room room, int moves, boolean isRumba) {
+ if (done(room, moves)) {
+ return evaluate(room);
+ }
+ if (isRumba) {
+ ArrayList neighbors = room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ Room room1 = new Room(room);
+ room1.goToNext(room1.agents.get(Main.game.agents.indexOf(this)), new Point(n));
+ room1.moves.offer(new Point(n));
+ int gain = minimax(room1, moves - 1, false);
+ if (maxGain < gain) {
+ maxGain = gain;
+ nextMove = new Point(room1.moves.peek());
+ }
+ }
+ return maxGain;
+ } else {
+ for (Agent agent : room.agents) {
+ if (agent.type.startsWith("Bad")) {
+ ArrayList neighbors = agent.getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ if (room.tiles[n.y][n.x].getStatus() != 1) {
+ Room room2 = new Room(room);
+ room2.goToNext(room2.agents.get(room.agents.indexOf(agent)), new Point(n));
+ int gain = minimax(room2, moves - 1, true);
+ if (minGain > gain) {
+ minGain = gain;
+ }
+ }
+ }
+ }
+ }
+ if (minGain == Integer.MAX_VALUE)
+ return minimax(room, moves - 1, true);
+ else
+ return minGain;
+ }
+ }
+
+ private boolean done(Room room, int moves) {
+ return moves == 0 || room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room).size() == 0;
+ }
+
+ public int minimax(Room room, int moves, boolean isRumba, int alpha, int beta) {
+ if (done(room, moves)) {
+ return evaluate(room);
+ }
+ if (isRumba) {
+ ArrayList neighbors = room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ Room room1 = new Room(room);
+ room1.goToNext(room1.agents.get(Main.game.agents.indexOf(this)), new Point(n));
+ room1.moves.offer(new Point(n));
+ int gain = minimax(room1, moves - 1, false, alpha, beta);
+ if (maxGain < gain) {
+ maxGain = gain;
+ nextMove = new Point(room1.moves.peek());
+ }
+ alpha = Math.max(alpha, maxGain);
+ if (beta <= alpha) {
+ break;
+ }
+ }
+ return maxGain;
+ } else {
+ for (Agent agent : room.agents) {
+ if (agent.type.startsWith("Bad")) {
+ ArrayList neighbors = agent.getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ if (room.tiles[n.y][n.x].getStatus() != 1) {
+ Room room2 = new Room(room);
+ room2.goToNext(room2.agents.get(room.agents.indexOf(agent)), new Point(n));
+ int gain = minimax(room2, moves - 1, true, alpha, beta);
+ if (minGain > gain) {
+ minGain = gain;
+ }
+ beta = Math.min(beta, minGain);
+ if (beta <= alpha) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (minGain == Integer.MAX_VALUE)
+ return minimax(room, moves - 1, true, alpha, beta);
+ else
+ return minGain;
+ }
+ }
+
+ public int expectimax(Room room, int moves, boolean isRumba) {
+ if (done(room, moves)) {
+ return evaluate(room);
+ }
+ if (isRumba) {
+ ArrayList neighbors = room.agents.get(Main.game.agents.indexOf(this)).getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ Room room1 = new Room(room);
+ room1.goToNext(room1.agents.get(Main.game.agents.indexOf(this)), new Point(n));
+ room1.moves.offer(new Point(n));
+ int gain = expectimax(room1, moves - 1, false);
+ if (maxGain < gain) {
+ maxGain = gain;
+ nextMove = new Point(room1.moves.peek());
+ }
+ }
+ return maxGain;
+ } else {
+ for (Agent agent : room.agents) {
+ if (agent.type.startsWith("Bad")) {
+ count=0;
+ ArrayList neighbors = agent.getPosition().getNeighbors(room);
+ for (Point n : neighbors) {
+ if (room.tiles[n.y][n.x].getStatus() != 1) {
+ Room room2 = new Room(room);
+ room2.goToNext(room2.agents.get(room.agents.indexOf(agent)), new Point(n));
+ minGain += expectimax(room2, moves - 1, true);
+ count++;
+ }
+ }
+ if (count != 0)
+ minGain /= count;
+ }
+ }
+ if (minGain == 0)
+ return expectimax(room, moves - 1, true);
+ else
+ return minGain;
+ }
+ }
+
+ private int evaluate(Room room) {
+ int toReturn = 2*room.dirtArray.size();
+ for (Agent agent : room.agents) {
+ if (agent.type.startsWith("Bad")) {
+ toReturn += room.agents.get(Main.game.agents.indexOf(this)).getDistance(room, agent.getPosition());
+ } else {
+ toReturn -= room.agents.get(Main.game.agents.indexOf(this)).getDistance(room, agent.getPosition());
+ }
+ }
+ int min = Integer.MAX_VALUE;
+ for (Point p : room.dirtArray) {
+ min = Math.min(min, room.agents.get(Main.game.agents.indexOf(this)).getDistance(room, p));
+ }
+ toReturn += min ;
+ return -toReturn;
+ }
+
+ public Point getNextMove() {
+ return nextMove;
+ }
+
+ public void setNextMove(Point nextMove) {
+ this.nextMove = nextMove;
+ }
+
+ public void setDepth(int depth) {
+ this.depth = depth;
+ }
+}
diff --git a/src/main/java/com/iea/gui/GuiAdv.java b/src/main/java/com/iea/gui/GuiAdv.java
new file mode 100644
index 0000000..6b4c317
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiAdv.java
@@ -0,0 +1,213 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ResourceBundle;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class GuiAdv implements Initializable {
+ private Stage stage;
+ private static int size;
+ @FXML
+ public Group room;
+ @FXML
+ private AnchorPane pane;
+ @FXML
+ private Spinner depth;
+ private Timer timer;
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ depth.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 100, 5));
+ try {
+ int pWidth = (int) pane.getPrefWidth();
+ int pHeight = (int) pane.getPrefHeight();
+ System.out.println(pWidth + "," + pHeight);
+ int w = Main.game.getWidth();
+ int h = Main.game.getHeight();
+ size = Math.min((pWidth / w), (pHeight / h)) - 5;
+ System.out.println("size=" + size);
+ Main.game.tiles = new Tile[Main.game.getHeight()][Main.game.getWidth()];
+ Main.game.verWalls = new Wall[Main.game.getHeight()][Main.game.getWidth()];
+ Main.game.horWalls = new Wall[Main.game.getHeight()][Main.game.getWidth()];
+ System.out.println("Adding tiles");
+ drawRoom();
+ } catch (Exception e) {
+ }
+ }
+
+ private void drawRoom() {
+ for (int i = 0; i < Main.game.getHeight(); i++) {
+ for (int j = 0; j < Main.game.getWidth(); j++) {
+ Main.game.tiles[i][j] = new Tile(i, j, j * (size + 5) + 3, i * (size + 5) + 3, size, size);
+ room.getChildren().add(Main.game.tiles[i][j]);
+ room.getChildren().add(Main.game.tiles[i][j].getWallDown());
+ room.getChildren().add(Main.game.tiles[i][j].getWallRight());
+ }
+ }
+ room.addEventFilter(MouseEvent.MOUSE_CLICKED, Main.game.handleMouse);
+ room.addEventFilter(MouseEvent.MOUSE_DRAGGED, Main.game.handleMouse);
+ }
+
+
+ public void handleReset(ActionEvent event) {
+ try {
+ timer.cancel();
+ }catch (Exception e){}
+ timer.cancel();
+ timer.purge();
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(Main.scene1);
+ Main.game = new Room();
+ }
+
+
+ public void handleRandom(ActionEvent event) throws IOException {
+ FXMLLoader GuiRandom = new FXMLLoader(Main.class.getResource("GuiRandom.fxml"));
+ Scene scene3 = new Scene(GuiRandom.load());
+ Stage stage = new Stage();
+ stage.setScene(scene3);
+ stage.show();
+ }
+
+ public void handleAddWalls(ActionEvent event) {
+ Main.game.tileStatus = 2;
+ }
+
+ public void handleAddDirt(ActionEvent event) {
+ Main.game.tileStatus = 1;
+ }
+
+ public static int getSize() {
+ return size;
+ }
+
+ public void handleChooseDim(ActionEvent event) throws IOException {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("GuiDimensions.fxml"));
+ Scene guiDim = new Scene(fxmlLoader.load());
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(guiDim);
+ }
+
+ public boolean handleGoodMiniMax(ActionEvent event) {
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - Main.game.agents.size();
+ for (int i = 0; i < Main.game.getWidth(); i++) {
+ for (int j = 0; j < Main.game.getHeight(); j++) {
+ if (Main.game.tiles[j][i].isClean()) {
+ Agent agent = new AgentGood(new Point(i, j), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "GoodMinimax");
+ agent.setDepth(depth.getValue());
+ Main.game.agents.add(agent);
+ Main.game.tiles[j][i].setStatus(3);
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(Main.game.agents.get(Main.game.agents.size() - 1));
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean handleGoodAlphaBeta(ActionEvent event) {
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - Main.game.agents.size();
+ for (int i = 0; i < Main.game.getWidth(); i++) {
+ for (int j = 0; j < Main.game.getHeight(); j++) {
+ if (Main.game.tiles[j][i].isClean()) {
+ Agent agent = new AgentGood(new Point(i, j), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "GoodAlphaBeta");
+ agent.setDepth(depth.getValue());
+ Main.game.agents.add(agent);
+ Main.game.tiles[j][i].setStatus(3);
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(agent);
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean handleGoodExpectimax(ActionEvent event) {
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - Main.game.agents.size();
+ for (int i = 0; i < Main.game.getWidth(); i++) {
+ for (int j = 0; j < Main.game.getHeight(); j++) {
+ if (Main.game.tiles[j][i].isClean()) {
+ Agent agent = new AgentGood(new Point(i, j), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "GoodExpectimax");
+ agent.setDepth(depth.getValue());
+ Main.game.agents.add(agent);
+ Main.game.tiles[j][i].setStatus(3);
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(Main.game.agents.get(Main.game.agents.size() - 1));
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean handleBadMinimax(ActionEvent event) {
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - Main.game.agents.size();
+ for (int i = 0; i < Main.game.getWidth(); i++) {
+ for (int j = 0; j < Main.game.getHeight(); j++) {
+ if (Main.game.tiles[j][i].isClean()) {
+ Agent agent = new AgentBad(new Point(i, j), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "BadMinimax");
+ agent.setDepth(depth.getValue());
+ Main.game.agents.add(agent);
+ Main.game.tiles[j][i].setStatus(3);
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(agent);
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean handleBadAlphaBeta(ActionEvent event) {
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - Main.game.agents.size();
+ for (int i = 0; i < Main.game.getWidth(); i++) {
+ for (int j = 0; j < Main.game.getHeight(); j++) {
+ if (Main.game.tiles[j][i].isClean()) {
+ Agent agent = new AgentBad(new Point(i, j), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "BadAlphaBeta");
+ agent.setDepth(depth.getValue());
+ Main.game.agents.add(agent);
+ Main.game.tiles[j][i].setStatus(3);
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(agent);
+ }
+ }
+ }
+ return false;
+ }
+
+
+ public void handleSolve(ActionEvent event) {
+ timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ for (Agent agent : Main.game.agents) {
+ System.out.println("Gain: "+agent.start(Main.game, true));
+// System.out.println("Agent Position: "+agent.getPosition()+"\nMove: "+agent.getNextMove());
+ Main.game.goToNext(agent,agent.getNextMove());
+ }
+ System.out.println("\n\n\n");
+ } catch (Exception e) {
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, 250);
+ }
+}
+
+
diff --git a/src/main/java/com/iea/gui/GuiCompare.java b/src/main/java/com/iea/gui/GuiCompare.java
new file mode 100644
index 0000000..b849161
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiCompare.java
@@ -0,0 +1,126 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.CheckBox;
+import javafx.scene.effect.Blend;
+import javafx.scene.effect.BlendMode;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ResourceBundle;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class GuiCompare implements Initializable {
+ private Stage stage;
+ private Room r;
+ private static int size;
+ @FXML
+ public Group room;
+ @FXML
+ private AnchorPane pane;
+ @FXML
+ private CheckBox bruteForce, branchAndBound, nearestNeighbor, genetic;
+ private Path brute, branch, nearest, gen;
+ private int delay = 300;
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ try {
+ int pWidth = (int) pane.getPrefWidth();
+ int pHeight = (int) pane.getPrefHeight();
+ int w = Main.game.getWidth();
+ int h = Main.game.getHeight();
+ size = Math.min((pWidth / w), (pHeight / h)) - 5;
+ System.out.println(pWidth + "," + pHeight);
+ r = drawRoom(Main.game);
+ } catch (Exception e) {
+ }
+ }
+
+ public Room drawRoom(Room room1) {
+ Room r = new Room(room1);
+ r.agent = new AgentGood(new Point(room1.agent.getPosition()), room1.agent.getPosition().x * (size + 5) + 3, room1.agent.getPosition().y * (size + 5) + 3, size, size, "");
+ for (int i = 0; i < r.getHeight(); i++) {
+ for (int j = 0; j < r.getWidth(); j++) {
+ r.tiles[i][j] = new Tile(Main.game.tiles[i][j], i, j, j * (size + 5) + 3, i * (size + 5) + 3, size);
+ r.tiles[i][j].setBlendMode(BlendMode.MULTIPLY);
+ room.getChildren().add(r.tiles[i][j]);
+ room.getChildren().add(r.tiles[i][j].getWallDown());
+ room.getChildren().add(r.tiles[i][j].getWallRight());
+ }
+ }
+ room.getChildren().add(r.agent);
+ return r;
+ }
+
+ public void handleStart(ActionEvent event) {
+ Main.game.agent.generateAdjacencyMatrix(Main.game.dirtArray, Main.game.tiles, false);
+ if (branchAndBound.isSelected()) {
+ Main.game.agent.branchAndBound();
+ branch = new Path(Main.game.getPath(Main.game.agent));
+ }
+ if (bruteForce.isSelected()) {
+ Main.game.agent.bruteForce();
+ brute = new Path(Main.game.getPath(Main.game.agent));
+ }
+ if (nearestNeighbor.isSelected()) {
+ Main.game.agent.nearestNeighbor();
+ nearest = new Path(Main.game.getPath(Main.game.agent));
+ }
+ if (genetic.isSelected()) {
+ Main.game.agent.genetic();
+ gen = new Path(Main.game.getPath(Main.game.agent));
+ }
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ if (bruteForce.isSelected()) {
+ Point p = brute.path().remove(0);
+ r.tiles[p.y][p.x].setFill(Color.YELLOW);
+ }
+ if (branchAndBound.isSelected()) {
+ Point p = branch.path().remove(0);
+ r.tiles[p.y][p.x].setFill(Color.GREEN);
+ }
+ if (nearestNeighbor.isSelected()) {
+ Point p = nearest.path().remove(0);
+ r.tiles[p.y][p.x].setFill(Color.RED);
+ }
+ if (genetic.isSelected()) {
+ Point p = gen.path().remove(0);
+ r.tiles[p.y][p.x].setFill(Color.BLUE);
+ }
+ } catch (Exception e) {
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, delay);
+ }
+
+ public void handleGenetic(ActionEvent event) throws IOException {
+ GuiMain.geneticCaller="GuiCompare";
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("GuiGenetic.fxml"));
+ Scene guiGen = new Scene(fxmlLoader.load());
+ Stage stage = new Stage();
+ stage.setScene(guiGen);
+ stage.show();
+ }
+ public void handleReset(ActionEvent event) {
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(Main.scene1);
+ Main.game = new Room();
+ }
+}
diff --git a/src/main/java/com/iea/gui/GuiDimensions.java b/src/main/java/com/iea/gui/GuiDimensions.java
new file mode 100644
index 0000000..24b70e1
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiDimensions.java
@@ -0,0 +1,49 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class GuiDimensions implements Initializable {
+ @FXML
+ private Button ok;
+ @FXML
+ private Spinner width;
+ @FXML
+ private Spinner height;
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ width.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(2, 100, 7));
+ height.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(2, 100, 7));
+ }
+
+ public void handleOk(ActionEvent event) throws IOException {
+ FXMLLoader GuiRoom = null;
+ System.out.println("Width: " + width.getValue() + "\n" + "Height: " + height.getValue());
+ Stage stage = (Stage) ok.getScene().getWindow();
+ Main.game.setRoomWidth(width.getValue());
+ Main.game.setRoomHeight(height.getValue());
+ if (GuiMain.menu.equals("Fully")) {
+ GuiRoom = new FXMLLoader(Main.class.getResource("GuiFully.fxml"));
+ } else if (GuiMain.menu.equals("Partly")) {
+ GuiRoom = new FXMLLoader(Main.class.getResource("GuiPartly.fxml"));
+ } else if (GuiMain.menu.equals("Adv")) {
+ GuiRoom = new FXMLLoader(Main.class.getResource("GuiAdv.fxml"));
+ }
+ Scene scene = new Scene(GuiRoom.load());
+ stage.setScene(scene);
+ stage.setMaximized(true);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/iea/gui/GuiFully.java b/src/main/java/com/iea/gui/GuiFully.java
new file mode 100644
index 0000000..f1099bc
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiFully.java
@@ -0,0 +1,220 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+import java.io.*;
+import java.net.URL;
+import java.util.ResourceBundle;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class GuiFully implements Initializable {
+ private Stage stage;
+ private int size;
+ @FXML
+ public Group room;
+ @FXML
+ private AnchorPane pane;
+ int delay = 300;
+
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ try {
+ int pWidth = (int) pane.getPrefWidth();
+ int pHeight = (int) pane.getPrefHeight();
+ int w = Main.game.getWidth();
+ int h = Main.game.getHeight();
+ System.out.println(w + "," + h);
+ size = Math.min((pWidth / w), (pHeight / h)) - 5;
+ Main.game.tiles = new Tile[Main.game.getHeight()][Main.game.getWidth()];
+ Main.game.verWalls = new Wall[Main.game.getHeight()][Main.game.getWidth()];
+ Main.game.horWalls = new Wall[Main.game.getHeight()][Main.game.getWidth()];
+ drawRoom();
+ } catch (Exception ignored) {
+ }
+ }
+
+ private void drawRoom() {
+ for (int i = 0; i < Main.game.getHeight(); i++) {
+ for (int j = 0; j < Main.game.getWidth(); j++) {
+ Main.game.tiles[i][j] = new Tile(i, j, j * (size + 5) + 3, i * (size + 5) + 3, size, size);
+ room.getChildren().add(Main.game.tiles[i][j]);
+ room.getChildren().add(Main.game.tiles[i][j].getWallDown());
+ room.getChildren().add(Main.game.tiles[i][j].getWallRight());
+ }
+ }
+ room.addEventFilter(MouseEvent.MOUSE_CLICKED, Main.game.handleMouse);
+ room.addEventFilter(MouseEvent.MOUSE_DRAGGED, Main.game.handleMouse);
+ }
+ public static void runGenetic(){
+ Main.game.agent.generateAdjacencyMatrix(Main.game.dirtArray, Main.game.tiles, false);
+ Main.game.agent.genetic();
+ Path path = Main.game.getPath(Main.game.agent);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Point p = path.path().remove(0);
+// System.out.println(p);
+ Main.game.goToNext(Main.game.agent, p);
+ } catch (Exception ignored) {
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, 300);
+ }
+
+ public boolean handleAddAgent(ActionEvent event) {
+ if (!Main.game.rumbaIsPlaced) {
+ Main.game.rumbaIsPlaced = true;
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - 1;
+ for (int i = 0; i < Main.game.getHeight(); i++) {
+ for (int j = 0; j < Main.game.getWidth(); j++) {
+ if (Main.game.tiles[i][j].isClean()) {
+ Main.game.agent = new AgentGood(new Point(j, i), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "Agent");
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(Main.game.agent);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public void handleReset(ActionEvent event) {
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(Main.scene1);
+ Main.game = new Room();
+ }
+
+ public void handleBruteForce(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.agent.generateAdjacencyMatrix(Main.game.dirtArray, Main.game.tiles, false);
+ Main.game.agent.bruteForce();
+ Path path = Main.game.getPath(Main.game.agent);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Point p = path.path().remove(0);
+// System.out.println(p);
+ Main.game.goToNext(Main.game.agent, p);
+ } catch (Exception e) {
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, delay);
+ }
+ }
+
+ public void handleLCBB(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.agent.generateAdjacencyMatrix(Main.game.dirtArray, Main.game.tiles, false);
+ Main.game.agent.branchAndBound();
+ Path path = Main.game.getPath(Main.game.agent);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Point p = path.path().remove(0);
+// System.out.println(p);
+ Main.game.goToNext(Main.game.agent, p);
+ } catch (Exception ignored) {
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, delay);
+ }
+ }
+
+ public void handleNN(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.agent.generateAdjacencyMatrix(Main.game.dirtArray, Main.game.tiles, false);
+ Main.game.agent.nearestNeighbor();
+ Path path = Main.game.getPath(Main.game.agent);
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Point p = path.path().remove(0);
+// System.out.println(p);
+ Main.game.goToNext(Main.game.agent, p);
+ } catch (Exception ignored) {
+ timer.purge();
+ timer.cancel();
+ }
+ }
+ }, 0, delay);
+ }
+ }
+
+ public void handleGenetic(ActionEvent event) throws IOException {
+ if (Main.game.rumbaIsPlaced) {
+ GuiMain.geneticCaller="GuiFully";
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("GuiGenetic.fxml"));
+ Scene guiGen = new Scene(fxmlLoader.load());
+ Stage stage = new Stage();
+ stage.setScene(guiGen);
+ stage.show();
+ }
+ }
+
+ public void handleRandom(ActionEvent event) throws IOException {
+ if (Main.game.rumbaIsPlaced) {
+ FXMLLoader GuiRandom = new FXMLLoader(Main.class.getResource("GuiRandom.fxml"));
+ Scene scene3 = new Scene(GuiRandom.load());
+ Stage stage = new Stage();
+ stage.setScene(scene3);
+ stage.show();
+ }
+ }
+
+ public void handleAddWalls(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.tileStatus = 2;
+ }
+ }
+
+ public void handleAddDirt(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.tileStatus = 1;
+ }
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void handleChooseDim(ActionEvent event) throws IOException {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("GuiDimensions.fxml"));
+ Scene guiDim = new Scene(fxmlLoader.load());
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(guiDim);
+ }
+
+ public void handleCompare(ActionEvent event) throws IOException {
+ if (Main.game.rumbaIsPlaced) {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("GuiCompare.fxml"));
+ Scene guiComp = new Scene(fxmlLoader.load());
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(guiComp);
+ }
+ }
+
+}
diff --git a/src/main/java/com/iea/gui/GuiGenetic.java b/src/main/java/com/iea/gui/GuiGenetic.java
new file mode 100644
index 0000000..40720bb
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiGenetic.java
@@ -0,0 +1,47 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.stage.Stage;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class GuiGenetic implements Initializable {
+ @FXML
+ private Spinner population;
+ @FXML
+ private Spinner epochs;
+ @FXML
+ private Spinner crossOverRate;
+ @FXML
+ private Spinner mutationRate;
+
+ public GuiGenetic() {
+ }
+
+ public void handleOk(ActionEvent event) {
+ Main.game.agent.population=population.getValue();
+ Main.game.agent.epoch=epochs.getValue();
+ Main.game.agent.crossOverRate=crossOverRate.getValue();
+ Main.game.agent.mutationRate=mutationRate.getValue();
+ Stage stage = (Stage) population.getScene().getWindow();
+ stage.close();
+ if(GuiMain.geneticCaller.equals("GuiFully")){
+ GuiFully.runGenetic();
+ }
+ }
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ population.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100000, 1000));
+ epochs.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100000, 100));
+ crossOverRate.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0, 1, 0.7,0.1));
+ mutationRate.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0, 1, 0.3,0.1));
+ }
+}
diff --git a/src/main/java/com/iea/gui/GuiMain.java b/src/main/java/com/iea/gui/GuiMain.java
new file mode 100644
index 0000000..f63753a
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiMain.java
@@ -0,0 +1,52 @@
+package com.iea.gui;
+
+import javafx.application.Application;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+
+public class GuiMain {
+ @FXML
+ private Button fullyObs;
+ Stage stage;
+ public static String menu;
+ public static String geneticCaller;
+ private void getStage() {
+ stage = (Stage) fullyObs.getScene().getWindow();
+
+ }
+
+ public void handleFullyObs(ActionEvent event) throws IOException {
+ menu="Fully";
+ getStage();
+ FXMLLoader GuiFully = new FXMLLoader(Main.class.getResource("GuiFully.fxml"));
+ Scene scene1 = new Scene(GuiFully.load());
+ stage.setScene(scene1);
+ stage.setMaximized(true);
+ }
+
+ public void handlePartlyObs(ActionEvent event) throws IOException {
+ menu="Partly";
+ getStage();
+ FXMLLoader GuiPartly = new FXMLLoader(Main.class.getResource("GuiPartly.fxml"));
+ Scene scene2 = new Scene(GuiPartly.load());
+ stage.setScene(scene2);
+ stage.setMaximized(true);
+ }
+
+ public void handleAdversarial(ActionEvent event) throws IOException {
+ menu="Adv";
+ getStage();
+ FXMLLoader GuiAdv = new FXMLLoader(Main.class.getResource("GuiAdv.fxml"));
+ Scene scene3 = new Scene(GuiAdv.load());
+ stage.setScene(scene3);
+ stage.setMaximized(true);
+ }
+}
diff --git a/src/main/java/com/iea/gui/GuiPartly.java b/src/main/java/com/iea/gui/GuiPartly.java
new file mode 100644
index 0000000..f4439cc
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiPartly.java
@@ -0,0 +1,115 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.Initializable;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ResourceBundle;
+
+
+public class GuiPartly implements Initializable {
+ private Stage stage;
+ private static int size;
+ @FXML
+ public Group room;
+ @FXML
+ private AnchorPane pane;
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ try {
+ int pWidth = (int) pane.getPrefWidth();
+ int pHeight = (int) pane.getPrefHeight();
+ int w = Main.game.getWidth();
+ int h = Main.game.getHeight();
+ size = Math.min((pWidth / w), (pHeight / h)) - 5;
+ System.out.println(pWidth + "," + pHeight);
+ Main.game.tiles = new Tile[Main.game.getHeight()][Main.game.getWidth()];
+ Main.game.verWalls = new Wall[Main.game.getHeight()][Main.game.getWidth()];
+ Main.game.horWalls = new Wall[Main.game.getHeight()][Main.game.getWidth()];
+ System.out.println("Adding tiles");
+ drawRoom(Main.game);
+ } catch (Exception e) {
+ }
+ }
+
+ public void drawRoom(Room r) {
+ for (int i = 0; i < r.getHeight(); i++) {
+ for (int j = 0; j < r.getWidth(); j++) {
+ r.tiles[i][j] = new Tile(i, j, j * (size + 5) + 3, i * (size + 5) + 3, size, size);
+ room.getChildren().add(r.tiles[i][j]);
+ room.getChildren().add(r.tiles[i][j].getWallDown());
+ room.getChildren().add(r.tiles[i][j].getWallRight());
+ }
+ }
+ room.addEventFilter(MouseEvent.MOUSE_CLICKED, r.handleMouse);
+ room.addEventFilter(MouseEvent.MOUSE_DRAGGED, r.handleMouse);
+ }
+
+ public boolean handleAddAgent(ActionEvent event) {
+ if (!Main.game.rumbaIsPlaced) {
+ Main.game.rumbaIsPlaced = true;
+ Main.game.emptyTiles = (Main.game.getWidth() * Main.game.getHeight()) - 1;
+ for (int i = 0; i < Main.game.getHeight(); i++) {
+ for (int j = 0; j < Main.game.getWidth(); j++) {
+ if (Main.game.tiles[i][j].isClean()) {
+ Main.game.agent = new AgentGood(new Point(j, i), i * (size + 5) + 3, j * (size + 5) + 3, size, size, "Agent");
+ Main.game.tileStatus = 3;
+ return room.getChildren().add(Main.game.agent);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public void handleReset(ActionEvent event) {
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(Main.scene1);
+ Main.game = new Room();
+ }
+
+ public void handleRandom(ActionEvent event) throws IOException {
+ FXMLLoader GuiRandom = new FXMLLoader(Main.class.getResource("GuiRandom.fxml"));
+ Scene scene3 = new Scene(GuiRandom.load());
+ Stage stage = new Stage();
+ stage.setScene(scene3);
+ stage.show();
+ }
+
+ public void handleAddWalls(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.tileStatus = 2;
+ }
+ }
+
+ public void handleAddDirt(ActionEvent event) {
+ if (Main.game.rumbaIsPlaced) {
+ Main.game.tileStatus = 1;
+ }
+ }
+
+ public static int getSize() {
+ return size;
+ }
+
+ public void handleChooseDim(ActionEvent event) throws IOException {
+ FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("GuiDimensions.fxml"));
+ Scene guiDim = new Scene(fxmlLoader.load());
+ stage = (Stage) room.getScene().getWindow();
+ stage.setScene(guiDim);
+ }
+
+ public void handleSolve(ActionEvent event) {
+ Main.game.agent.partiallyObservable();
+ }
+}
+
diff --git a/src/main/java/com/iea/gui/GuiRandom.java b/src/main/java/com/iea/gui/GuiRandom.java
new file mode 100644
index 0000000..6189a75
--- /dev/null
+++ b/src/main/java/com/iea/gui/GuiRandom.java
@@ -0,0 +1,34 @@
+package com.iea.gui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.stage.Stage;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class GuiRandom implements Initializable {
+ @FXML
+ private Spinner dirt;
+ @FXML
+ private Spinner walls;
+
+ public void handleOk(ActionEvent event) {
+ Main.game.dirt = dirt.getValue();
+ Main.game.wall = walls.getValue();
+ Main.game.randomDirt(Main.game.dirt, Main.game.dirtArray);
+ Main.game.randomWalls(Main.game.wall);
+ Main.game.tileStatus = 0;
+ Stage stage = (Stage) dirt.getScene().getWindow();
+ stage.close();
+ }
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ dirt.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 10000, 15));
+ walls.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 10000, 20));
+ }
+}
diff --git a/src/main/java/com/iea/gui/Launcher.java b/src/main/java/com/iea/gui/Launcher.java
new file mode 100644
index 0000000..3f87962
--- /dev/null
+++ b/src/main/java/com/iea/gui/Launcher.java
@@ -0,0 +1,7 @@
+package com.iea.gui;
+
+public class Launcher {
+ public static void main(String[] args) {
+ Main.main(args);
+ }
+}
diff --git a/src/main/java/com/iea/gui/Main.java b/src/main/java/com/iea/gui/Main.java
new file mode 100644
index 0000000..ff27d0d
--- /dev/null
+++ b/src/main/java/com/iea/gui/Main.java
@@ -0,0 +1,31 @@
+package com.iea.gui;
+
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class Main extends Application {
+ public static Scene scene1;
+ public static Room game;
+ @Override
+ public void start(Stage stage) {
+ try {
+ FXMLLoader GuiMain = new FXMLLoader(Main.class.getResource("GuiMain.fxml"));
+ scene1 = new Scene(GuiMain.load());
+ stage.setTitle("Rumba");
+ stage.setScene(scene1);
+ stage.show();
+ stage.setOnCloseRequest(windowEvent -> {
+ Platform.exit();
+ System.exit(0);
+ });
+ game=new Room();
+ }catch (Exception ignored){}
+ }
+
+ public static void main(String[] args) {
+ launch();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/iea/gui/Path.java b/src/main/java/com/iea/gui/Path.java
new file mode 100644
index 0000000..6dfa042
--- /dev/null
+++ b/src/main/java/com/iea/gui/Path.java
@@ -0,0 +1,119 @@
+package com.iea.gui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+// This class is used to store the paths (edges), and their weights
+public class Path {
+ private int cost;
+ private final List path;
+ private List geneticPath;
+ private double fitness;
+
+ public Path() {
+ this.geneticPath = new ArrayList<>();
+ this.path = new ArrayList<>();
+ this.cost = 0;
+ }
+
+ public Path(Path p) {
+ this.geneticPath = new ArrayList<>(p.getGeneticPath());
+ this.path = new ArrayList<>(p.path());
+ this.cost = p.cost();
+ }
+
+ public void addNode(Point N) {
+ path.add(new Point(N));
+ }
+
+ public int size() {
+ return path.size();
+ }
+
+ public String print() {
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ for (Point x : path) {
+ sb.append(x).append("; ");
+ if (i > 20) {
+ i = 0;
+ sb.append("
");
+ continue;
+ }
+ i++;
+ }
+ sb.append("
Cost = ").append(cost);
+ return sb.toString();
+ }
+
+ public String printGenetic() {
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ for (int x = 0; x < geneticPath.size(); x++) {
+ sb.append(geneticPath.get(x)).append("; ");
+ if (i > 20) {
+ i = 0;
+ sb.append("
");
+ continue;
+ }
+ i++;
+ }
+ sb.append("
Fitness = ").append(this.getFitness());
+ return sb.toString();
+ }
+
+ public int cost() {
+ return cost;
+ }
+
+ public List path() {
+ return path;
+ }
+
+ public void setCost(int c) {
+ cost = c;
+ }
+
+ public void addNodeFirst(Point current) {
+ path.add(0, current);
+ }
+
+ public Path reverse() {
+ Path inv = new Path(this);
+ Collections.reverse(inv.path());
+ return inv;
+ }
+
+ public double getFitness() {
+ return 1.0 / (1 + this.cost);
+ }
+
+ public void setFitness(double fitness) {
+ this.fitness = fitness;
+ }
+
+ public void addNode(int n) {
+ this.geneticPath.add(n);
+ }
+
+ public void setGeneticPath(int[] p) {
+ this.geneticPath = new ArrayList<>();
+ for (int i = 0; i < p.length; i++) {
+ this.geneticPath.add(p[i]);
+ }
+ }
+
+ public List getGeneticPath() {
+ return geneticPath;
+ }
+
+ public int[] getPath() {
+ int[] toReturn = new int[this.geneticPath.size()];
+ for (int i = 0; i < this.geneticPath.size(); i++) {
+ toReturn[i] = this.geneticPath.get(i);
+ }
+ return toReturn;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/iea/gui/Point.java b/src/main/java/com/iea/gui/Point.java
new file mode 100644
index 0000000..d931129
--- /dev/null
+++ b/src/main/java/com/iea/gui/Point.java
@@ -0,0 +1,102 @@
+package com.iea.gui;
+
+import java.util.ArrayList;
+
+public class Point extends java.awt.Point {
+ static int value;
+
+ public Point(Point p) {
+ super(p);
+ }
+
+ public Point(int x, int y) {
+ super(x, y);
+ }
+
+ @Override
+ public String toString() {
+ return "[" + x + ";" + y + "]";
+ }
+
+ public boolean isValid() {
+ return this.x >= 0 && this.y >= 0 && this.x < Main.game.roomWidth && this.y < Main.game.roomHeight;
+ }
+
+ public ArrayList getNeighbors() {
+ ArrayList toReturn = new ArrayList<>();
+ if (!Main.game.tiles[this.y][this.x].hasWallRight() && new Point(this.x + 1, this.y).isValid()) {
+ if (Main.game.tiles[this.y][this.x + 1].getStatus() != 3)
+ toReturn.add(new Point(this.x + 1, this.y));
+ }
+ if (!Main.game.tiles[this.y][this.x].hasWallLeft() && new Point(this.x - 1, this.y).isValid()) {
+ if (Main.game.tiles[this.y][this.x - 1].getStatus() != 3)
+ toReturn.add(new Point(this.x - 1, this.y));
+ }
+ if (!Main.game.tiles[this.y][this.x].hasWallUp() && new Point(this.x, this.y - 1).isValid()) {
+ if (Main.game.tiles[this.y - 1][this.x].getStatus() != 3)
+ toReturn.add(new Point(this.x, this.y - 1));
+ }
+ if (!Main.game.tiles[this.y][this.x].hasWallDown() && new Point(this.x, this.y + 1).isValid()) {
+ if (Main.game.tiles[this.y + 1][this.x].getStatus() != 3)
+ toReturn.add(new Point(this.x, this.y + 1));
+ }
+ return toReturn;
+ }
+ public ArrayList getNeighbors(boolean b) {
+ ArrayList toReturn = new ArrayList<>();
+ if (!Main.game.tiles[this.y][this.x].hasWallRight() && new Point(this.x + 1, this.y).isValid()) {
+ toReturn.add(new Point(this.x + 1, this.y));
+ }
+ if (!Main.game.tiles[this.y][this.x].hasWallLeft() && new Point(this.x - 1, this.y).isValid()) {
+ toReturn.add(new Point(this.x - 1, this.y));
+ }
+ if (!Main.game.tiles[this.y][this.x].hasWallUp() && new Point(this.x, this.y - 1).isValid()) {
+ toReturn.add(new Point(this.x, this.y - 1));
+ }
+ if (!Main.game.tiles[this.y][this.x].hasWallDown() && new Point(this.x, this.y + 1).isValid()) {
+ toReturn.add(new Point(this.x, this.y + 1));
+ }
+ return toReturn;
+ }
+ public ArrayList getNeighbors(Room room) {
+ ArrayList toReturn = new ArrayList<>();
+ if (!room.tiles[this.y][this.x].hasWallRight() && new Point(this.x + 1, this.y).isValid()) {
+ if (room.tiles[this.y][this.x + 1].getStatus() != 3)
+ toReturn.add(new Point(this.x + 1, this.y));
+ }
+ if (!room.tiles[this.y][this.x].hasWallLeft() && new Point(this.x - 1, this.y).isValid()) {
+ if (room.tiles[this.y][this.x - 1].getStatus() != 3)
+ toReturn.add(new Point(this.x - 1, this.y));
+ }
+ if (!room.tiles[this.y][this.x].hasWallUp() && new Point(this.x, this.y - 1).isValid()) {
+ if (room.tiles[this.y - 1][this.x].getStatus() != 3)
+ toReturn.add(new Point(this.x, this.y - 1));
+ }
+ if (!room.tiles[this.y][this.x].hasWallDown() && new Point(this.x, this.y + 1).isValid()) {
+ if (room.tiles[this.y + 1][this.x].getStatus() != 3)
+ toReturn.add(new Point(this.x, this.y + 1));
+ }
+ return toReturn;
+ }
+ public ArrayList getNeighbors(Room room,Point destination) {
+ ArrayList toReturn = new ArrayList<>();
+ if (!room.tiles[this.y][this.x].hasWallRight() && new Point(this.x + 1, this.y).isValid()) {
+ if (room.tiles[this.y][this.x + 1].getStatus() != 3 || new Point(this.x+1,this.y).equals(destination))
+ toReturn.add(new Point(this.x + 1, this.y));
+ }
+ if (!room.tiles[this.y][this.x].hasWallLeft() && new Point(this.x - 1, this.y).isValid()) {
+ if (room.tiles[this.y][this.x - 1].getStatus() != 3|| new Point(this.x-1,this.y).equals(destination))
+ toReturn.add(new Point(this.x - 1, this.y));
+ }
+ if (!room.tiles[this.y][this.x].hasWallUp() && new Point(this.x, this.y - 1).isValid()) {
+ if (room.tiles[this.y - 1][this.x].getStatus() != 3|| new Point(this.x,this.y-1).equals(destination))
+ toReturn.add(new Point(this.x, this.y - 1));
+ }
+ if (!room.tiles[this.y][this.x].hasWallDown() && new Point(this.x, this.y + 1).isValid()) {
+ if (room.tiles[this.y + 1][this.x].getStatus() != 3|| new Point(this.x,this.y+1).equals(destination))
+ toReturn.add(new Point(this.x, this.y + 1));
+ }
+ return toReturn;
+ }
+}
+
diff --git a/src/main/java/com/iea/gui/Room.java b/src/main/java/com/iea/gui/Room.java
new file mode 100644
index 0000000..c172708
--- /dev/null
+++ b/src/main/java/com/iea/gui/Room.java
@@ -0,0 +1,280 @@
+package com.iea.gui;
+
+
+import javafx.event.EventHandler;
+import javafx.scene.input.MouseEvent;
+
+import java.util.*;
+
+public class Room {
+ public Wall[][] horWalls;
+ public Wall[][] verWalls;
+ List dirtArray;
+ List wallArray;
+ Random rnd;
+ boolean rumbaIsPlaced;
+ int roomWidth;
+ int roomHeight;
+ int tileStatus;
+ int emptyTiles;
+ int dirt;
+ int wall;
+ int prevI;
+ int prevJ;
+ Tile[][] tiles;
+ AgentGood agent;
+ ArrayList agents;
+ Queue moves;
+
+ Room() {
+ moves = new ArrayDeque<>();
+ tileStatus = 0;
+ rumbaIsPlaced = false;
+ rnd = new Random();
+ dirtArray = new ArrayList<>();
+ agents = new ArrayList<>();
+ }
+
+ Room(Room room) {
+ roomHeight = room.getHeight();
+ roomWidth = room.getWidth();
+ moves = new ArrayDeque<>();
+ agents = new ArrayList<>();
+ dirtArray = new ArrayList<>();
+ tiles = new Tile[room.getHeight()][room.getWidth()];
+ tileStatus = 0;
+ for (Point move:room.moves){
+ this.moves.add(move);
+ }
+ for (int i = 0; i < room.getHeight(); i++) {
+ for (int j = 0; j < room.getWidth(); j++) {
+ tiles[i][j] = new Tile(room.getTiles()[i][j]);
+ }
+ }
+ for (Agent agent : room.getAgents()) {
+ agents.add(new Agent(agent));
+ }
+ for (Point dirt : room.getDirtArray()) {
+ dirtArray.add(new Point(dirt));
+ }
+ }
+
+
+ Path getPath(Agent agent) {
+ Path fullPath = new Path();
+ for (int i = 0; i < agent.getPath().length - 1; i++) {
+ Path pathToNode = new Path(agent.getPathBetweenNode()[agent.getPath()[i]][agent.getPath()[i + 1]]);
+ for (Point p : pathToNode.path().subList(1, pathToNode.size())) {
+ fullPath.addNode(p);
+ }
+ }
+ fullPath.setCost(agent.getCost());
+ return fullPath;
+ }
+
+ void goToNext(Agent agent, Point p) {
+ if (tiles[p.y][p.x].getStatus() != 3) {
+ moveTo(agent, p);
+ tiles[p.y][p.x].setStatus(3);
+ }
+ }
+
+ void randomWalls(int number) {
+ int row, column;
+ for (int i = 0; i < number; i++) {
+ double alpha = Math.random();
+ row = rnd.nextInt(roomHeight);
+ column = rnd.nextInt(roomWidth);
+ if (alpha < 0.25) {
+ if (!tiles[row][column].hasWallDown())
+ tiles[row][column].AddRemoveWallDown();
+ } else if (alpha < 0.5) {
+ if (!tiles[row][column].hasWallUp())
+ tiles[row][column].AddRemoveWallUp();
+ } else if (alpha < 0.75) {
+ if (!tiles[row][column].hasWallLeft())
+ tiles[row][column].AddRemoveWallLeft();
+ } else {
+ if (!tiles[row][column].hasWallRight())
+ tiles[row][column].AddRemoveWallRight();
+ }
+ }
+ }
+
+ void randomDirt(int number, List array) {
+ Point p;
+ int row, column;
+ if (number <= emptyTiles) {
+ emptyTiles -= number;
+ } else {
+ number = emptyTiles;
+ emptyTiles = 0;
+ }
+ for (int i = 0; i < number; i++) {
+ do {
+ row = rnd.nextInt(roomHeight);
+ column = rnd.nextInt(roomWidth);
+ p = new Point(column, row);
+ } while (!tiles[row][column].isClean());
+ array.add(p);
+ tiles[row][column].setStatus(1);
+ }
+ }
+
+ EventHandler handleMouse = new EventHandler<>() {
+ @Override
+ public void handle(MouseEvent mouseEvent) {
+ try {
+ int size = (int) tiles[0][0].getWidth();
+ int i = (int) ((mouseEvent.getX() - 3) / (size + 5));
+ int j = (int) ((mouseEvent.getY() - 3) / (size + 5));
+ try {
+ if (prevI == i && prevJ == j)
+ return;
+ } catch (Exception e) {
+ }
+ prevI = i;
+ prevJ = j;
+ double x = (mouseEvent.getX() - 3) / (size + 5);
+ double y = (mouseEvent.getY() - 3) / (size + 5);
+ System.out.println("Clicked: " + i + "," + j);
+ switch (tileStatus) {
+ case 0 -> {
+ }
+ case 1 -> {
+ if (tiles[j][i].getStatus() != 3) {
+ if (tiles[j][i].isClean()) {
+ dirtArray.add(new Point(i, j));
+ tiles[j][i].setStatus(1);
+ emptyTiles--;
+ } else {
+ dirtArray.remove(new Point(i, j));
+ tiles[j][i].setStatus(0);
+ emptyTiles++;
+ }
+ }
+ }
+ case 2 -> {
+ if (Math.abs(x - i) <= 0.1 || Math.abs(y - j) <= 0.1) {
+ if (Math.abs(x - i) <= Math.abs(y - j)) {
+ tiles[j][i].AddRemoveWallLeft();
+ } else {
+ tiles[j][i].AddRemoveWallUp();
+ }
+ } else {
+ if (Math.abs(x - i) >= Math.abs(y - j)) {
+ tiles[j][i].AddRemoveWallRight();
+ } else {
+ tiles[j][i].AddRemoveWallDown();
+ }
+ }
+ }
+ case 3 -> {
+ if (GuiMain.menu.equals("Fully") || GuiMain.menu.equals("Partly")) {
+ if (tiles[j][i].isClean()) {
+ Point prevPosition = new Point(agent.getPosition());
+ tiles[prevPosition.y][prevPosition.x].setStatus(0);
+ tiles[j][i].setStatus(3);
+ agent.setTranslateX(i * (size + 5) + 3);
+ agent.setTranslateY(j * (size + 5) + 3);
+ agent.setPosition(new Point(i, j));
+ }
+ } else {
+ if (tiles[j][i].isClean()) {
+ Point prevPosition = new Point(agents.get(agents.size() - 1).getPosition());
+ tiles[prevPosition.y][prevPosition.x].setStatus(0);
+ tiles[j][i].setStatus(3);
+ agents.get(agents.size() - 1).setTranslateX(i * (size + 5) + 3);
+ agents.get(agents.size() - 1).setTranslateY(j * (size + 5) + 3);
+ agents.get(agents.size() - 1).setPosition(new Point(i, j));
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ }
+ }
+ };
+
+ public void moveTo(Agent agent, Point point) {
+ if (agent.status == 1) {
+ Point prev = new Point(agent.getPosition());
+ if (agent.getPosition().x < point.x) {
+ agent.moveRight();
+ dirtify(prev);
+ } else if (agent.getPosition().x > point.x) {
+ agent.moveLeft();
+ dirtify(prev);
+ } else if (agent.getPosition().y < point.y) {
+ agent.moveDown();
+ dirtify(prev);
+ } else if (agent.getPosition().y > point.y) {
+ agent.moveUp();
+ dirtify(prev);
+ }
+ } else {
+ Point prev = new Point(agent.getPosition());
+ if (agent.getPosition().x < point.x) {
+ clean(point);
+ agent.moveRight();
+ } else if (agent.getPosition().x > point.x) {
+ clean(point);
+ agent.moveLeft();
+ } else if (agent.getPosition().y < point.y) {
+ clean(point);
+ agent.moveDown();
+ } else if (agent.getPosition().y > point.y) {
+ clean(point);
+ agent.moveUp();
+ }
+ tiles[prev.y][prev.x].setStatus(0);
+ }
+ }
+
+ private void dirtify(Point p) {
+ tiles[p.y][p.x].setStatus(1);
+ if (!dirtArray.contains(new Point(p))) {
+ dirtArray.add(new Point(p));
+ }
+ }
+
+ private void clean(Point position) {
+// tiles[position.y][position.x].setStatus(0);
+ dirtArray.remove(new Point(position));
+ }
+
+
+ public int getWidth() {
+ return roomWidth;
+ }
+
+
+ public int getHeight() {
+ return roomHeight;
+ }
+
+ public void setRoomWidth(int roomWidth) {
+ this.roomWidth = roomWidth;
+ }
+
+ public void setRoomHeight(int roomHeight) {
+ this.roomHeight = roomHeight;
+ }
+
+ public ArrayList getAgents() {
+ return agents;
+ }
+
+ public List getDirtArray() {
+ return dirtArray;
+ }
+
+ public List getDirt() {
+ return dirtArray;
+ }
+
+ public Tile[][] getTiles() {
+ return tiles;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/iea/gui/Tile.java b/src/main/java/com/iea/gui/Tile.java
new file mode 100644
index 0000000..4cebe9c
--- /dev/null
+++ b/src/main/java/com/iea/gui/Tile.java
@@ -0,0 +1,204 @@
+package com.iea.gui;
+
+
+import javafx.scene.image.Image;
+import javafx.scene.paint.ImagePattern;
+import javafx.scene.shape.Rectangle;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+
+class Tile extends Rectangle {
+ private int row, column;
+ private int status;
+ private int count;
+ boolean hasWallUp;
+ boolean hasWallDown;
+ boolean hasWallLeft;
+ boolean hasWallRight;
+ private Wall wallRight;
+ private Wall wallDown;
+ private static Image dirt;
+ private static Image clean;
+
+ static {
+ clean=new Image(String.valueOf(Tile.class.getResource("Images/tile.png")));
+ dirt = new Image(String.valueOf(Tile.class.getResource("Images/Dirt.png")));
+ }
+
+
+ // Initialize tile through row and column
+ Tile(int row, int column, int x, int y, int width, int height) {
+ setFill(new ImagePattern(clean));
+ setX(x);
+ setY(y);
+ setWidth(width);
+ setHeight(height);
+ this.row = row;
+ this.column = column;
+ this.hasWallDown = false;
+ this.hasWallUp = false;
+ this.hasWallLeft = false;
+ this.hasWallRight = false;
+ this.wallDown = new Wall(column * (width + 5) + 3, (row) * (width + 5) + 3 + width, width, 5);
+ this.wallRight = new Wall((column) * (width + 5) + 3 + width, (row) * (width + 5) + 3, 5, width);
+ count = 0;
+ }
+
+ public Tile(int i, int j) {
+ this.hasWallDown = false;
+ this.hasWallUp = false;
+ this.hasWallLeft = false;
+ this.hasWallRight = false;
+ }
+
+ public Tile(Tile tile) {
+ this.hasWallDown = tile.hasWallDown;
+ this.hasWallUp = tile.hasWallUp;
+ this.hasWallLeft = tile.hasWallLeft;
+ this.hasWallRight = tile.hasWallRight;
+ this.row = tile.row;
+ this.column = tile.column;
+ this.setStatus(tile.getStatus());
+ }
+
+ public Tile(Tile tile, int i, int j, int x, int y, int size) {
+ setX(x);
+ setY(y);
+ setWidth(size);
+ setHeight(size);
+ this.hasWallDown = tile.hasWallDown;
+ this.hasWallUp = tile.hasWallUp;
+ this.hasWallLeft = tile.hasWallLeft;
+ this.hasWallRight = tile.hasWallRight;
+ this.row = tile.row;
+ this.column = tile.column;
+ this.setStatus(tile.getStatus());
+ this.wallDown = new Wall(column * (size + 5) + 3, (row) * (size + 5) + 3+ size, size, 5);
+ this.wallRight = new Wall((column) * (size + 5) + 3 + size, (row) * (size + 5) +3, 5, size);
+ this.wallDown.setStatus(tile.wallDown.getStatus());
+ this.wallRight.setStatus(tile.wallRight.getStatus());
+ }
+
+ int getStatus() {
+ return status;
+ }
+
+ void setStatus(int s) {
+ status = s;
+ switch (getStatus()) {
+ case 0 -> setFill(new ImagePattern(clean));
+ case 1 -> setFill(new ImagePattern(dirt));
+// case 2 -> setFill(Color.BLACK);
+// case 3 -> setFill(Color.BLACK);
+ }
+ }
+
+ boolean isClean() {
+ return status == 0;
+ }
+
+ boolean isDirty() {
+ return status == 1;
+ }
+
+ public boolean isValid() {
+ return (status == 0 || status == 1);
+ }
+
+ public void incrementCount() {
+ count++;
+ }
+
+ public boolean hasWallRight() {
+ return this.hasWallRight;
+ }
+
+ public boolean hasWallDown() {
+ return this.hasWallDown;
+ }
+
+ public boolean hasWallLeft() {
+ try {
+ return Main.game.tiles[row][column - 1].hasWallRight();
+ } catch (Exception e) {
+ return true;
+ }
+ }
+
+ public boolean hasWallUp() {
+ try {
+ return Main.game.tiles[row - 1][column].hasWallDown();
+ } catch (Exception e) {
+ return true;
+ }
+ }
+
+ public void AddRemoveWallDown() {
+ try {
+ if(row==Main.game.getHeight()-1)
+ return;
+ boolean wall = !this.hasWallDown;
+ this.hasWallDown = wall;
+ if (wall) {
+ Main.game.tiles[row][column].wallDown.addWall();
+ } else {
+ Main.game.tiles[row][column].wallDown.removeWall();
+ }
+ Main.game.tiles[row + 1][column].hasWallUp = wall;
+ } catch (Exception e) {
+ }
+ }
+
+ public void AddRemoveWallRight() {
+ try {
+ if(column==Main.game.getWidth()-1)
+ return;
+ boolean wall = !this.hasWallRight;
+ this.hasWallRight = wall;
+ if (wall) {
+ Main.game.tiles[row][column].wallRight.addWall();
+ } else {
+ Main.game.tiles[row][column].wallRight.removeWall();
+ }
+ Main.game.tiles[row][column - 1].hasWallLeft = wall;
+ } catch (Exception e) {
+ }
+ }
+
+ public void AddRemoveWallLeft() {
+ try {
+ boolean wall = !this.hasWallLeft;
+ this.hasWallLeft = wall;
+ if (wall) {
+ Main.game.tiles[row][column - 1].wallRight.addWall();
+ } else {
+ Main.game.tiles[row][column - 1].wallRight.removeWall();
+ }
+ Main.game.tiles[row][column - 1].hasWallRight = wall;
+ } catch (Exception e) {
+ }
+ }
+
+ public void AddRemoveWallUp() {
+ try {
+ boolean wall = !this.hasWallUp;
+ this.hasWallUp = wall;
+ if (wall) {
+ Main.game.tiles[row - 1][column].wallDown.addWall();
+ } else {
+ Main.game.tiles[row - 1][column].wallDown.removeWall();
+ }
+ Main.game.tiles[row - 1][column].hasWallDown = wall;
+ } catch (Exception e) {
+ }
+ }
+
+ public Wall getWallRight() {
+ return wallRight;
+ }
+
+ public Wall getWallDown() {
+ return wallDown;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/iea/gui/Wall.java b/src/main/java/com/iea/gui/Wall.java
new file mode 100644
index 0000000..e564639
--- /dev/null
+++ b/src/main/java/com/iea/gui/Wall.java
@@ -0,0 +1,42 @@
+package com.iea.gui;
+
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+
+
+public class Wall extends Rectangle {
+ private boolean isWall;
+
+
+ Wall(int x, int y, int width, int height) {
+ setFill(Color.WHITE);
+ setX(x);
+ setY(y);
+ setWidth(width);
+ setHeight(height);
+ this.isWall = false;
+ }
+
+ public boolean getStatus() {
+ return isWall;
+ }
+
+ public void setStatus(boolean wall) {
+ this.isWall = wall;
+ if (isWall)
+ addWall();
+ else
+ removeWall();
+ }
+
+ public void addWall() {
+ setFill(Color.BLACK);
+ isWall = true;
+ }
+
+ public void removeWall() {
+ setFill(Color.WHITE);
+ isWall = false;
+ }
+}
+
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..731196a
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,17 @@
+module com.iea.gui {
+ requires javafx.controls;
+ requires javafx.fxml;
+ requires javafx.web;
+
+ requires org.controlsfx.controls;
+ requires com.dlsc.formsfx;
+ requires validatorfx;
+ requires org.kordamp.ikonli.javafx;
+ requires org.kordamp.bootstrapfx.core;
+ requires eu.hansolo.tilesfx;
+ requires java.desktop;
+ requires javafx.graphics;
+
+ opens com.iea.gui to javafx.fxml;
+ exports com.iea.gui;
+}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1f22613
--- /dev/null
+++ b/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Main-Class: com.iea.gui.Launcher
+Class-Path: javafx-controls-17-ea+11-win.jar javafx-fxml-17-ea+11.jar ti
+ lesfx-11.48.jar javafx-media-17-ea+11-win.jar formsfx-core-11.3.2.jar j
+ avafx-fxml-17-ea+11-win.jar javafx-graphics-17-ea+11.jar javafx-web-17-
+ ea+11-win.jar javafx-media-17-ea+11.jar ikonli-javafx-12.2.0.jar javafx
+ -controls-17-ea+11.jar controlsfx-11.1.0.jar validatorfx-0.1.13.jar jav
+ afx-graphics-17-ea+11-win.jar bootstrapfx-core-0.4.0.jar javafx-base-17
+ -ea+11.jar javafx-base-17-ea+11-win.jar javafx-web-17-ea+11.jar ikonli-
+ core-12.2.0.jar
+
diff --git a/src/main/resources/com/iea/gui/GUIGenetic.fxml b/src/main/resources/com/iea/gui/GUIGenetic.fxml
new file mode 100644
index 0000000..cba7662
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GUIGenetic.fxml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiAdv.fxml b/src/main/resources/com/iea/gui/GuiAdv.fxml
new file mode 100644
index 0000000..deccdfa
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiAdv.fxml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiCompare.fxml b/src/main/resources/com/iea/gui/GuiCompare.fxml
new file mode 100644
index 0000000..030179e
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiCompare.fxml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiDimensions.fxml b/src/main/resources/com/iea/gui/GuiDimensions.fxml
new file mode 100644
index 0000000..0b4db9e
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiDimensions.fxml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiFully.fxml b/src/main/resources/com/iea/gui/GuiFully.fxml
new file mode 100644
index 0000000..1ed4e7b
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiFully.fxml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiMain.fxml b/src/main/resources/com/iea/gui/GuiMain.fxml
new file mode 100644
index 0000000..94c4885
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiMain.fxml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiPartly.fxml b/src/main/resources/com/iea/gui/GuiPartly.fxml
new file mode 100644
index 0000000..1d57463
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiPartly.fxml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/GuiRandom.fxml b/src/main/resources/com/iea/gui/GuiRandom.fxml
new file mode 100644
index 0000000..2ffe23a
--- /dev/null
+++ b/src/main/resources/com/iea/gui/GuiRandom.fxml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/iea/gui/Images/Bad.png b/src/main/resources/com/iea/gui/Images/Bad.png
new file mode 100644
index 0000000000000000000000000000000000000000..63291f201604ff2887df3444d7069f0bdc17165d
GIT binary patch
literal 31087
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
NWd#lSN?FU${{hN7@mBx<
literal 0
HcmV?d00001
diff --git a/src/main/resources/com/iea/gui/Images/Dirt.png b/src/main/resources/com/iea/gui/Images/Dirt.png
new file mode 100644
index 0000000000000000000000000000000000000000..3bb5a6b60c9d7720fdb4bbe0910070debe032c90
GIT binary patch
literal 1228835
zcmWifWmpql8^-A=`ByrpB11(&8U~CI5s9fN-JpPU*JuIhm`XDc1r_P8(J^TeM{mG@
zAvMN;0Rvv&Pv?9(pPuV_?)!J&C*i4~4hth6BNY`Di|*rx >B{$1&)t}y&NA_T=^
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