From: LeonardoBizzoni Date: Sat, 19 Jun 2021 11:01:26 +0000 (+0200) Subject: Basic A* X-Git-Url: http://git.leonardobizzoni.com/?a=commitdiff_plain;h=0a2b1c28af8a4765f0f7c881a54f1ca698969f29;p=astar-visualizer Basic A* --- diff --git a/src/main/java/Main/AStar.java b/src/main/java/Main/AStar.java new file mode 100644 index 0000000..6a60a95 --- /dev/null +++ b/src/main/java/Main/AStar.java @@ -0,0 +1,92 @@ +package Main; + +public class AStar{ + private Map map; + + public AStar(Map map) { + this.map = map; + } + + public void start() { + searchPath(PathfinderUtils.startNode); + } + + public void searchPath(Node parent) { + for (int i = 0; i < 4; i++) { + int x = (int) Math.round(parent.getX() + (-map.size* Math.cos((Math.PI / 2) * i))); + int y = (int) Math.round(parent.getY() + (-map.size* Math.sin((Math.PI / 2) * i))); + + calculateOpenNode(x, y, parent); + } + + parent = getNextBestNode(); + + PathfinderUtils.closedNodes.add(parent); + PathfinderUtils.openNodes.remove(parent); + + if (parent.getX() == PathfinderUtils.endNode.getX() && parent.getY() == PathfinderUtils.endNode.getY()) { + ControlPanel.toggleRunBtn.setText("End"); + map.isFinished = true; + map.repaint(); + return; + } + + if (!map.isFinished) { + searchPath(parent); + } + else + System.exit(0); + } + + public void calculateOpenNode(int nextX, int nextY, Node parent) { + if (nextX >= Map.width || nextY >= Map.height || nextX < 0 || nextY < 0) + return; + if (PathfinderUtils.locateBarrier(nextX, nextY) != -1) + return; + if (nextX == PathfinderUtils.startNode.getX() && nextY == PathfinderUtils.startNode.getY()) + return; + + Node openNode = new Node(nextX, nextY); + openNode.setParentNode(parent); + + // Tests if a copy of the node has already been added + for (Node node : PathfinderUtils.closedNodes) { + if(openNode.getX() == node.getX() && openNode.getY() == node.getY()) + return; + } + + // Tests if a copy of the node has already been added + for (Node node : PathfinderUtils.openNodes) { + if(openNode.getX() == node.getX() && openNode.getY() == node.getY()) + return; + } + + int gx = openNode.getX() - parent.getX(); + int gy = openNode.getY() - parent.getY(); + int g = parent.getG(); + + if (gx != 0 && gy != 0) { + g += (int) (Math.sqrt(2 * (Math.pow(map.size, 2)))); + } else { + g += map.size; + } + openNode.setG(g); + + // Calculating H Cost + int hx = Math.abs(PathfinderUtils.endNode.getX() - openNode.getX()); + int hy = Math.abs(PathfinderUtils.endNode.getY() - openNode.getY()); + int h = hx + hy; + openNode.setH(h); + + // Calculating F Cost + int f = g + h; + openNode.setF(f); + + PathfinderUtils.openNodes.add(openNode); + } + + public Node getNextBestNode() { + PathfinderUtils.sort(); + return PathfinderUtils.openNodes.get(0); + } +} diff --git a/src/main/java/Main/ControlPanel.java b/src/main/java/Main/ControlPanel.java index 6c57821..e5186ef 100644 --- a/src/main/java/Main/ControlPanel.java +++ b/src/main/java/Main/ControlPanel.java @@ -2,22 +2,15 @@ package Main; import java.awt.Color; import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JComboBox; -import javax.swing.JOptionPane; public class ControlPanel { - private Map map; - - private JButton toggleRunBtn; - private JComboBox algo; + static JButton toggleRunBtn; + static JComboBox algo; public ControlPanel(Map map) { - this.map = map; - algo = new JComboBox<>(new String[] { "Select an algorithm", "A*", "Dijkstra", "Breadth-first search"}); algo.setVisible(true); @@ -25,48 +18,7 @@ public class ControlPanel { toggleRunBtn.setVisible(true); toggleRunBtn.setMargin(new Insets(0, 0, 0, 0)); toggleRunBtn.setBackground(Color.white); - toggleRunBtn.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - - if (toggleRunBtn.getText().equals("Run")) { - switch (algo.getItemAt(algo.getSelectedIndex())) { - - case ("A*"): - System.out.println("A* selected"); - // AStar.start(); - break; - - case ("Dijkstra"): - System.out.println("Dijkstra selected"); - // AStar.start(); - break; - - case ("Greedy best-first search"): - System.out.println("Greedy best-first search selected"); - // AStar.start(); - break; - - case ("Breadth-first search"): - System.out.println("Breadth-first search selected"); - // AStar.start(); - break; - - default: - JOptionPane.showMessageDialog(null, - "You must select an algorithm before starting the pathfinder", "Algorithm not selected", - JOptionPane.ERROR_MESSAGE); - return; - } - - toggleRunBtn.setText("Stop"); - } - - else { - toggleRunBtn.setText("Run"); - } - } - }); + toggleRunBtn.addActionListener(map); map.add(algo); map.add(toggleRunBtn); diff --git a/src/main/java/Main/Map.java b/src/main/java/Main/Map.java index 36f8ff2..167847a 100644 --- a/src/main/java/Main/Map.java +++ b/src/main/java/Main/Map.java @@ -9,25 +9,33 @@ */ package Main; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; - import java.awt.Color; import java.awt.Graphics; -import java.awt.event.MouseListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionListener; +import java.awt.Dimension; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; -class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListener { +class Map extends JPanel implements ActionListener, MouseListener, MouseMotionListener, KeyListener { private JFrame window; - private int size = 30; private char key = (char) 0; private ControlPanel menu = new ControlPanel(this); + static int width; + static int height; + boolean isFinished = false; + + int size = 30; + public Map() { this.setBackground(new Color(40, 40, 40)); @@ -40,10 +48,14 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe window = new JFrame(); window.setContentPane(this); window.setTitle("Pathfinding Algorithm Visualizer"); + window.getContentPane().setPreferredSize(new Dimension(1920, 1080)); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.pack(); window.setVisible(true); + width = this.getWidth(); + height = this.getHeight(); + this.revalidate(); this.repaint(); } @@ -78,6 +90,18 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe } menu.renderMenu(); + + // Draws open nodes + g.setColor(Color.white); + for (Node node : PathfinderUtils.openNodes) { + g.fillRect(node.getX() + 1, node.getY() + 1, size - 1, size - 1); + } + + // Draws closed nodes + g.setColor(Color.orange); + for (Node node : PathfinderUtils.closedNodes) { + g.fillRect(node.getX() + 1, node.getY() + 1, size - 1, size - 1); + } } // Drawing on the grid @@ -92,7 +116,8 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe // Checks if start node and end node are the same if (PathfinderUtils.endNode != null) { if (PathfinderUtils.endNode.getX() == posX && PathfinderUtils.endNode.getY() == posY) { - JOptionPane.showMessageDialog(null, "End node and start node can't be the same node", "Same node error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "End node and start node can't be the same node", + "Same node error", JOptionPane.ERROR_MESSAGE); return; } } @@ -115,7 +140,8 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe // Checks if end node and start node are the same if (PathfinderUtils.startNode != null) { if (PathfinderUtils.startNode.getX() == posX && PathfinderUtils.startNode.getY() == posY) { - JOptionPane.showMessageDialog(null, "End node and start node can't be the same node!", "SAME NODE ERROR", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "End node and start node can't be the same node!", + "SAME NODE ERROR", JOptionPane.ERROR_MESSAGE); return; } } @@ -137,7 +163,6 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe // TODO controlla se si sta creando una barriera su start o end - PathfinderUtils.barriers.add(barrierNode); repaint(); @@ -164,7 +189,7 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe } else { - int nodeID = PathfinderUtils.locate(posX, posY); + int nodeID = PathfinderUtils.locateBarrier(posX, posY); if (nodeID != -1) { PathfinderUtils.remove(nodeID); @@ -195,21 +220,65 @@ class Map extends JPanel implements MouseListener, MouseMotionListener, KeyListe } @Override - public void keyTyped(KeyEvent e) {} + public void actionPerformed(ActionEvent e) { + if (ControlPanel.toggleRunBtn.getText().equals("Run")) { + switch (ControlPanel.algo.getItemAt(ControlPanel.algo.getSelectedIndex())) { + + case ("A*"): + System.out.println("A* selected"); + new AStar(this).start(); + break; + + case ("Dijkstra"): + System.out.println("Dijkstra selected"); + // AStar.start(); + break; + + case ("Greedy best-first search"): + System.out.println("Greedy best-first search selected"); + // AStar.start(); + break; + + case ("Breadth-first search"): + System.out.println("Breadth-first search selected"); + // AStar.start(); + break; + + default: + JOptionPane.showMessageDialog(null, "You must select an algorithm before starting the pathfinder", + "Algorithm not selected", JOptionPane.ERROR_MESSAGE); + return; + } + + ControlPanel.toggleRunBtn.setText("Stop"); + } + + else { + ControlPanel.toggleRunBtn.setText("Run"); + } + } @Override - public void mouseClicked(MouseEvent e) {} + public void keyTyped(KeyEvent e) { + } @Override - public void mouseReleased(MouseEvent e) {} + public void mouseClicked(MouseEvent e) { + } @Override - public void mouseEntered(MouseEvent e) {} + public void mouseReleased(MouseEvent e) { + } @Override - public void mouseExited(MouseEvent e) {} + public void mouseEntered(MouseEvent e) { + } @Override - public void mouseMoved(MouseEvent e) {} + public void mouseExited(MouseEvent e) { + } + @Override + public void mouseMoved(MouseEvent e) { + } } diff --git a/src/main/java/Main/Node.java b/src/main/java/Main/Node.java index 479a5fb..1df44d5 100644 --- a/src/main/java/Main/Node.java +++ b/src/main/java/Main/Node.java @@ -1,7 +1,8 @@ package Main; public class Node { - private int x, y; + private int x, y, f = 0, g = 0, h = 0; + private Node parentNode; public Node(int x, int y) { this.x = x; @@ -23,4 +24,37 @@ public class Node { public void setX(int x) { this.x = x; } + + public Node getParentNode() { + return parentNode; + } + + public void setParentNode(Node parentNode) { + this.parentNode = parentNode; + } + + + public int getF() { + return f; + } + + public void setF(int f) { + this.f = f; + } + + public int getG() { + return g; + } + + public void setG(int g) { + this.g = g; + } + + public int getH() { + return h; + } + + public void setH(int h) { + this.h = h; + } } diff --git a/src/main/java/Main/PathfinderUtils.java b/src/main/java/Main/PathfinderUtils.java index 5604538..528a005 100644 --- a/src/main/java/Main/PathfinderUtils.java +++ b/src/main/java/Main/PathfinderUtils.java @@ -5,18 +5,36 @@ import java.util.List; public class PathfinderUtils { static List barriers = new ArrayList<>(); + static List openNodes = new ArrayList<>(); + static List closedNodes = new ArrayList<>(); static Node startNode, endNode; - static int locate(int x, int y) { + // Returns index of barrier if the coordinates are of a barrier node + static int locateBarrier(int x, int y) { for (int i = 0; i < barriers.size(); i++) { - if(barriers.get(i).getX() == x && barriers.get(i).getY() == y) + if (barriers.get(i).getX() == x && barriers.get(i).getY() == y) return i; } return -1; } + // Removes barrier node static void remove(int id) { barriers.remove(id); } + + static void sort() { + Node tmp; + + for (int i = 0; i < openNodes.size(); i++) { + for (int j = 0; j < openNodes.size(); j++) { + if (openNodes.get(i).getF() < openNodes.get(j).getF()) { + tmp = openNodes.get(j); + openNodes.set(j, openNodes.get(i)); + openNodes.set(i, tmp); + } + } + } + } } diff --git a/target/PathVisualizer-1.0-SNAPSHOT.jar b/target/PathVisualizer-1.0-SNAPSHOT.jar index f674501..39668b1 100644 Binary files a/target/PathVisualizer-1.0-SNAPSHOT.jar and b/target/PathVisualizer-1.0-SNAPSHOT.jar differ diff --git a/target/classes/Main/AStar.class b/target/classes/Main/AStar.class new file mode 100644 index 0000000..e722d34 Binary files /dev/null and b/target/classes/Main/AStar.class differ diff --git a/target/classes/Main/ControlPanel$1.class b/target/classes/Main/ControlPanel$1.class deleted file mode 100644 index fa3a702..0000000 Binary files a/target/classes/Main/ControlPanel$1.class and /dev/null differ diff --git a/target/classes/Main/ControlPanel.class b/target/classes/Main/ControlPanel.class index 340d99e..920e67e 100644 Binary files a/target/classes/Main/ControlPanel.class and b/target/classes/Main/ControlPanel.class differ diff --git a/target/classes/Main/Map.class b/target/classes/Main/Map.class index f084550..28b921d 100644 Binary files a/target/classes/Main/Map.class and b/target/classes/Main/Map.class differ diff --git a/target/classes/Main/Node.class b/target/classes/Main/Node.class index 8ff1fb2..930f295 100644 Binary files a/target/classes/Main/Node.class and b/target/classes/Main/Node.class differ diff --git a/target/classes/Main/PathfinderUtils.class b/target/classes/Main/PathfinderUtils.class index 78aa867..c3ec61a 100644 Binary files a/target/classes/Main/PathfinderUtils.class and b/target/classes/Main/PathfinderUtils.class differ diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index 6b22cb6..b1dccf0 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1,5 +1,5 @@ -Main/ControlPanel$1.class Main/Node.class +Main/AStar.class Main/ControlPanel.class Main/Map.class Main/App.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index b97cf8a..cdd74a9 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -1,5 +1,6 @@ /home/leo/Docs/Proj/PathVisualizer/src/main/java/Main/ControlPanel.java /home/leo/Docs/Proj/PathVisualizer/src/main/java/Main/Map.java /home/leo/Docs/Proj/PathVisualizer/src/main/java/Main/Node.java +/home/leo/Docs/Proj/PathVisualizer/src/main/java/Main/AStar.java /home/leo/Docs/Proj/PathVisualizer/src/main/java/Main/App.java /home/leo/Docs/Proj/PathVisualizer/src/main/java/Main/PathfinderUtils.java diff --git a/target/surefire-reports/Main.AppTest.txt b/target/surefire-reports/Main.AppTest.txt index b957bda..bdab1da 100644 --- a/target/surefire-reports/Main.AppTest.txt +++ b/target/surefire-reports/Main.AppTest.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: Main.AppTest ------------------------------------------------------------------------------- -Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.026 s - in Main.AppTest +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.02 s - in Main.AppTest diff --git a/target/surefire-reports/TEST-Main.AppTest.xml b/target/surefire-reports/TEST-Main.AppTest.xml index 9f3f13d..396d6b0 100644 --- a/target/surefire-reports/TEST-Main.AppTest.xml +++ b/target/surefire-reports/TEST-Main.AppTest.xml @@ -1,5 +1,5 @@ - + @@ -16,7 +16,7 @@ - + @@ -32,7 +32,7 @@ - +