Guest User

pathfinding Κανάργιας

a guest
May 9th, 2015
635
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 93.74 KB | None | 0 0
  1.  
  2.  
  3. import java.awt.*;
  4. import java.awt.event.*;
  5. import java.awt.geom.AffineTransform;
  6. import java.util.ArrayList;
  7. import java.util.Collections;
  8. import java.util.Comparator;
  9. import java.util.Random;
  10. import java.util.Stack;
  11. import javax.swing.*;
  12. import javax.swing.event.*;
  13.  
  14. /**
  15. *
  16. * @author Νίκος Κανάργιας, φοιτητής ΕΑΠ, ΠΛΗ31 2012-13
  17. * E-mail: [email protected]
  18. * @version 4.1
  19. *
  20. * Το πρόγραμμα λύνει και οπτικοποιεί το πρόβλημα του σχεδιασμού κίνησης ρομπότ
  21. * (robot motion planning) υλοποιώντας μια παραλλαγή των αλγόριθμων
  22. * DFS, BFS και A*, όπως αυτοί περιγράφονται στο βιβλίο
  23. * "Τεχνητή Νοημοσύνη και Έμπειρα Συστήματα" της Ε. Κεραυνού, ΠΑΤΡΑ 2000
  24. * καθώς και τον αλγόριθμο της άπληστης αναζήτησης, σαν ειδική περίπτωση του Α*.
  25. *
  26. * Το πρόγραμμα ακόμη υλοποιεί τον αλγόριθμο του Dijkstra όπως ακριβώς αυτός
  27. * περιγράφεται το σχετικό άρθρο της Wikipedia.
  28. * http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
  29. *
  30. * Γίνεται ολοφάνερη η υπεροχή των αλγόριθμων Α* και Dijkstra απέναντι στους άλλους τρεις.
  31. *
  32. * Ο χρήστης μπορεί μεταβάλει το πλήθος των κελιών του πλέγματος, δηλώνοντας
  33. * τον επιθυμητό αριθμό γραμμών και στηλών.
  34. *
  35. * Ο χρήστης μπορεί να προσθέσει όσα εμπόδια θέλει, όπως θα σχεδίαζε ελεύθερες
  36. * καμπύλες με ένα σχεδιαστικό πρόγραμμα.
  37. *
  38. * Αφαίρεση μεμονωμένων εμποδίων γίνεται κάνοντας κλικ επάνω τους.
  39. *
  40. * Η θέση του ρομπότ ή/και του στόχου μπορεί να αλλάξει με σύρσιμο με το ποντίκι.
  41. *
  42. * Μεταπήδηση από την αναζήτηση "Βήμα-Βήμα" στην αναζήτηση με "Κίνηση" και αντίστροφα
  43. * γίνεται πιέζοντας το αντίστοιχο κουμπί, ακόμη και όταν η αναζήτηση είναι σε εξέλιξη.
  44. *
  45. * Η ταχύτητα μιας αναζήτησης με κίνηση μπορεί να μεταβληθεί, ακόμη και αν η
  46. * αναζήτηση είναι σε εξέλιξη, αρκεί να τοποθετηθεί ο slider "Ταχύτητα" στην νέα
  47. * επιθυμητή θέση και στη συνέχεια πιεστεί το κουμπί "Κίνηση".
  48. *
  49. * Η εφαρμογή θεωρεί ότι το ίδιο το ρομπότ έχει κάποιον όγκο. Συνεπώς δεν μπορεί
  50. * να κινηθεί διαγώνια προς ελεύθερο κελί ανάμεσα από δύο εμπόδια που εφάπτονται
  51. * σε μία κορυφή τους.
  52. *
  53. * Δεν είναι δυνατή η αλλαγή των θέσεων εμποδίων, ρομπότ και στόχου όπως και του
  54. * είδους του αλγόριθμου, ενόσω η αναζήτηση είναι σε εξέλιξη.
  55. *
  56. * Συνιστάται η αποφυγή σχεδίασης βελών προς προκατόχους σε πλέγματα μεγάλης διάστασης.
  57. */
  58.  
  59. public class AntiTowerAndPhx {
  60.  
  61. public static JFrame mazeFrame; // Η κύρια φόρμα του προγράμματος
  62. /**
  63. * @param args the command line arguments
  64. */
  65. public static void main(String[] args) {
  66. int width = 693;
  67. int height = 545;
  68. mazeFrame = new JFrame("Maze 4.1");
  69. mazeFrame.setContentPane(new MazePanel(width,height));
  70. mazeFrame.pack();
  71. mazeFrame.setResizable(false);
  72.  
  73. // τοποθετούμε τη φόρμα στο κέντρο της οθόνης
  74. Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  75. double screenWidth = screenSize.getWidth();
  76. double ScreenHeight = screenSize.getHeight();
  77. int x = ((int)screenWidth-width)/2;
  78. int y = ((int)ScreenHeight-height)/2;
  79.  
  80. mazeFrame.setLocation(x,y);
  81. mazeFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  82. mazeFrame.setVisible(true);
  83. } // end main()
  84.  
  85. /**
  86. * Αυτή η κλάση καθορίζει το περιεχόμενο της κύριας φόρμας
  87. * και περιέχει όλη την λειτουργικότητα του προγράμματος.
  88. */
  89. public static class MazePanel extends JPanel {
  90.  
  91. /*
  92. **********************************************************
  93. * Ένθετες κλάσεις στην MazePanel
  94. **********************************************************
  95. */
  96.  
  97. /**
  98. * Βοηθητική κλάση που αναπαριστά το κελί του πλέγματος
  99. */
  100. private class Cell {
  101. int row; // ο αριθμός γραμμής του κελιού (Η γραμμή 0 είναι η πάνω)
  102. int col; // ο αριθμός στήλης του κελιού (Η στήλη 0 είναι η αριστερή)
  103. int g; // η τιμή της συνάρτησης g των αλγόριθμων Α* και Greedy
  104. int h; // η τιμή της συνάρτησης h των αλγόριθμων Α* και Greedy
  105. int f; // η τιμή της συνάρτησης f των αλγόριθμων Α* και Greedy
  106. int dist; // η απόσταση του κελιού από την αρχική θέση του ρομπότ
  107. // δηλαδή η ετικέτα που ενημερώνει ο αλγόριθμος Dijkstra
  108. Cell prev; // κάθε κατάσταση αντιστοιχεί σε κάποιο cell
  109. // και κάθε κατάσταση έχει μια προκάτοχο η οποία
  110. // αποθηκεύεται σε αυτή τη μεταβλητή
  111.  
  112. public Cell(int row, int col){
  113. this.row = row;
  114. this.col = col;
  115. }
  116. } // end nested class Cell
  117.  
  118. /**
  119. * Βοηθητική κλάση που καθορίζει ότι τα κελιά θα ταξινομούνται
  120. * με βάση το πεδίο τους f
  121. */
  122. private class CellComparatorByF implements Comparator<Cell>{
  123. @Override
  124. public int compare(Cell cell1, Cell cell2){
  125. return cell1.f-cell2.f;
  126. }
  127. } // end nested class CellComparatorByF
  128.  
  129. /**
  130. * Βοηθητική κλάση που καθορίζει ότι τα κελιά θα ταξινομούνται
  131. * με βάση το πεδίο τους dist
  132. */
  133. private class CellComparatorByDist implements Comparator<Cell>{
  134. @Override
  135. public int compare(Cell cell1, Cell cell2){
  136. return cell1.dist-cell2.dist;
  137. }
  138. } // end nested class CellComparatorByDist
  139.  
  140. /**
  141. * Κλάση που χειρίζεται τις κινήσεις του ποντικιού καθώς "ζωγραφίζουμε"
  142. * εμπόδια ή μετακινούμε το ρομπότ ή/και τον στόχο.
  143. */
  144. private class MouseHandler implements MouseListener, MouseMotionListener {
  145. private int cur_row, cur_col, cur_val;
  146. @Override
  147. public void mousePressed(MouseEvent evt) {
  148. int row = (evt.getY() - 10) / squareSize;
  149. int col = (evt.getX() - 10) / squareSize;
  150. if (row >= 0 && row < rows && col >= 0 && col < columns && !searching && !found) {
  151. cur_row = row;
  152. cur_col = col;
  153. cur_val = grid[row][col];
  154. if (cur_val == EMPTY){
  155. grid[row][col] = OBST;
  156. }
  157. if (cur_val == OBST){
  158. grid[row][col] = EMPTY;
  159. }
  160. }
  161. repaint();
  162. }
  163.  
  164. @Override
  165. public void mouseDragged(MouseEvent evt) {
  166. int row = (evt.getY() - 10) / squareSize;
  167. int col = (evt.getX() - 10) / squareSize;
  168. if (row >= 0 && row < rows && col >= 0 && col < columns && !searching && !found){
  169. if ((row*columns+col != cur_row*columns+cur_col) && (cur_val == ROBOT || cur_val == TARGET)){
  170. int new_val = grid[row][col];
  171. if (new_val == EMPTY){
  172. grid[row][col] = cur_val;
  173. if (cur_val == ROBOT) {
  174. robotStart.row = row;
  175. robotStart.col = col;
  176. } else {
  177. targetPos.row = row;
  178. targetPos.col = col;
  179. }
  180. grid[cur_row][cur_col] = new_val;
  181. cur_row = row;
  182. cur_col = col;
  183. if (cur_val == ROBOT) {
  184. robotStart.row = cur_row;
  185. robotStart.col = cur_col;
  186. } else {
  187. targetPos.row = cur_row;
  188. targetPos.col = cur_col;
  189. }
  190. cur_val = grid[row][col];
  191. }
  192. } else if (grid[row][col] != ROBOT && grid[row][col] != TARGET){
  193. grid[row][col] = OBST;
  194. }
  195. }
  196. repaint();
  197. }
  198.  
  199. @Override
  200. public void mouseReleased(MouseEvent evt) { }
  201. @Override
  202. public void mouseEntered(MouseEvent evt) { }
  203. @Override
  204. public void mouseExited(MouseEvent evt) { }
  205. @Override
  206. public void mouseMoved(MouseEvent evt) { }
  207. @Override
  208. public void mouseClicked(MouseEvent evt) { }
  209.  
  210. } // end nested class MouseHandler
  211.  
  212. /**
  213. * Όταν ο χρήστης πιέζει ένα κουμπί εκτελεί την αντίστοιχη λειτουργικότητα
  214. */
  215. private class ActionHandler implements ActionListener {
  216. @Override
  217. public void actionPerformed(ActionEvent evt) {
  218. String cmd = evt.getActionCommand();
  219. if (cmd.equals("Καθάρισμα")) {
  220. fillGrid();
  221. dfs.setEnabled(true);
  222. bfs.setEnabled(true);
  223. aStar.setEnabled(true);
  224. greedy.setEnabled(true);
  225. dijkstra.setEnabled(true);
  226. diagonal.setEnabled(true);
  227. drawArrows.setEnabled(true);
  228. } else if (cmd.equals("Βήμα - Βήμα") && !found && !endOfSearch) {
  229. // Η αρχικοποίηση του Dijkstra πρέπει να γίνει ακριβώς πριν την
  230. // έναρξη της αναζήτησης, γιατί τα εμπόδια πρέπει να είναι στη θέση τους.
  231. if (!searching && dijkstra.isSelected()) {
  232. initializeDijkstra();
  233. }
  234. searching = true;
  235. message.setText(msgSelectStepByStepEtc);
  236. dfs.setEnabled(false);
  237. bfs.setEnabled(false);
  238. aStar.setEnabled(false);
  239. greedy.setEnabled(false);
  240. dijkstra.setEnabled(false);
  241. diagonal.setEnabled(false);
  242. drawArrows.setEnabled(false);
  243. timer.stop();
  244. // Εδώ αποφασίζουμε αν μπορούμε να συνεχίσουμε την
  245. // 'Βήμα-Βήμα' αναζήτηση ή όχι
  246. // Για την περίπτωση των αλγόριθμων DFS, BFS, A* και Greedy
  247. // εδώ έχουμε το 2ο βήμα τους:
  248. // 2. Εάν ΑΝΟΙΚΤΕΣ = [], τότε τερμάτισε. Δεν υπάρχει λύση.
  249. if ((dijkstra.isSelected() && graph.isEmpty()) ||
  250. (!dijkstra.isSelected() && openSet.isEmpty()) ) {
  251. endOfSearch = true;
  252. grid[robotStart.row][robotStart.col]=ROBOT;
  253. message.setText(msgNoSolution);
  254. } else {
  255. expandNode();
  256. if (found) {
  257. plotRoute();
  258. }
  259. }
  260. repaint();
  261. } else if (cmd.equals("Κίνηση") && !endOfSearch) {
  262. if (!searching && dijkstra.isSelected()) {
  263. initializeDijkstra();
  264. }
  265. searching = true;
  266. message.setText(msgSelectStepByStepEtc);
  267. dfs.setEnabled(false);
  268. bfs.setEnabled(false);
  269. aStar.setEnabled(false);
  270. greedy.setEnabled(false);
  271. dijkstra.setEnabled(false);
  272. diagonal.setEnabled(false);
  273. drawArrows.setEnabled(false);
  274. timer.setDelay(delay);
  275. timer.start();
  276. }
  277. }
  278. } // end nested class ActionHandler
  279.  
  280. /**
  281. * Η κλάση που είναι υπεύθυνη για το animation
  282. */
  283. private class RepaintAction implements ActionListener {
  284. @Override
  285. public void actionPerformed(ActionEvent evt) {
  286. // Εδώ αποφασίζουμε αν μπορούμε να συνεχίσουμε ή όχι
  287. // την αναζήτηση με 'Κίνηση'.
  288. // Για την περίπτωση των αλγόριθμων DFS, BFS, A* και Greedy
  289. // εδώ έχουμε το 2ο βήμα τους:
  290. // 2. Εάν ΑΝΟΙΚΤΕΣ = [], τότε τερμάτισε. Δεν υπάρχει λύση.
  291. if ((dijkstra.isSelected() && graph.isEmpty()) ||
  292. (!dijkstra.isSelected() && openSet.isEmpty()) ) {
  293. endOfSearch = true;
  294. grid[robotStart.row][robotStart.col]=ROBOT;
  295. message.setText(msgNoSolution);
  296. } else {
  297. expandNode();
  298. if (found) {
  299. timer.stop();
  300. endOfSearch = true;
  301. plotRoute();
  302. }
  303. }
  304. repaint();
  305. }
  306. } // end nested class RepaintAction
  307.  
  308. /**
  309. * Η κλάση που δημιουργεί το AboutBox
  310. */
  311. private class AboutBox extends JDialog{
  312.  
  313. public AboutBox(Frame parent, boolean modal){
  314. super(parent, modal);
  315. // τοποθετούμε το aboutBox στο κέντρο της οθόνης
  316. Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  317. double screenWidth = screenSize.getWidth();
  318. double ScreenHeight = screenSize.getHeight();
  319. int width = 450;
  320. int height = 185;
  321. int x = ((int)screenWidth-width)/2;
  322. int y = ((int)ScreenHeight-height)/2;
  323. setSize(width,height);
  324. setLocation(x, y);
  325.  
  326. setResizable(false);
  327. setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  328.  
  329. JLabel title = new JLabel("Maze", JLabel.CENTER);
  330. title.setFont(new Font("Helvetica",Font.PLAIN,24));
  331. title.setForeground(new java.awt.Color(255, 153, 102));
  332.  
  333. JLabel version = new JLabel("Έκδοση: 4.1", JLabel.CENTER);
  334. version.setFont(new Font("Helvetica",Font.BOLD,14));
  335.  
  336. JLabel programmer = new JLabel("Προγραμματιστής: Νίκος Κανάργιας", JLabel.CENTER);
  337. programmer.setFont(new Font("Helvetica",Font.PLAIN,16));
  338.  
  339. JLabel email = new JLabel("E-mail: [email protected]", JLabel.CENTER);
  340. email.setFont(new Font("Helvetica",Font.PLAIN,14));
  341.  
  342. JLabel sourceCode = new JLabel("Κώδικας και τεκμηρίωση:", JLabel.CENTER);
  343. sourceCode.setFont(new Font("Helvetica",Font.PLAIN,14));
  344.  
  345. JLabel link = new JLabel("https://www.dropbox.com/sh/3tl3vmsd8ebxo24/xKmjl8uXX7", JLabel.CENTER);
  346. link.setFont(new Font("Helvetica",Font.PLAIN,16));
  347.  
  348. JLabel dummy = new JLabel("");
  349.  
  350. add(title);
  351. add(version);
  352. add(programmer);
  353. add(email);
  354. add(sourceCode);
  355. add(link);
  356. add(dummy);
  357.  
  358. title. setBounds(5, 0, 430, 30);
  359. version. setBounds(5, 30, 430, 20);
  360. programmer.setBounds(5, 55, 430, 20);
  361. email. setBounds(5, 80, 430, 20);
  362. sourceCode.setBounds(5,105, 430, 20);
  363. link .setBounds(5,130, 430, 20);
  364. dummy. setBounds(5,155, 430, 20);
  365. }
  366. } // end nested class AboutBox
  367.  
  368. /**
  369. * Δημιουργεί έναν τυχαίο, τέλειο (χωρίς κύκλους) λαβύρινθο
  370. *
  371. * Ο κώδικας είναι μια προσαρμογή, με τον αυθεντικό σχολιασμό, της απάντησης που έδωσε ο χρήστης
  372. * DoubleMx2 στις 25 Αυγούστου σε σχετικό ερώτημα του χρήστη nazar_art στο stackoverflow.com
  373. * http://stackoverflow.com/questions/18396364/maze-generation-arrayindexoutofboundsexception
  374. */
  375. private class MyMaze {
  376. private int dimensionX, dimensionY; // dimension of maze
  377. private int gridDimensionX, gridDimensionY; // dimension of output grid
  378. private char[][] mazeGrid; // output grid
  379. private Cell[][] cells; // 2d array of Cells
  380. private Random random = new Random(); // The random object
  381.  
  382. // initialize with x and y the same
  383. public MyMaze(int aDimension) {
  384. // Initialize
  385. this(aDimension, aDimension);
  386. }
  387. // constructor
  388. public MyMaze(int xDimension, int yDimension) {
  389. dimensionX = xDimension;
  390. dimensionY = yDimension;
  391. gridDimensionX = xDimension * 2 + 1;
  392. gridDimensionY = yDimension * 2 + 1;
  393. mazeGrid = new char[gridDimensionX][gridDimensionY];
  394. init();
  395. generateMaze();
  396. }
  397.  
  398. private void init() {
  399. // create cells
  400. cells = new Cell[dimensionX][dimensionY];
  401. for (int x = 0; x < dimensionX; x++) {
  402. for (int y = 0; y < dimensionY; y++) {
  403. cells[x][y] = new Cell(x, y, false); // create cell (see Cell constructor)
  404. }
  405. }
  406. }
  407.  
  408. // inner class to represent a cell
  409. private class Cell {
  410. int x, y; // coordinates
  411. // cells this cell is connected to
  412. ArrayList<Cell> neighbors = new ArrayList<>();
  413. // impassable cell
  414. boolean wall = true;
  415. // if true, has yet to be used in generation
  416. boolean open = true;
  417. // construct Cell at x, y
  418. Cell(int x, int y) {
  419. this(x, y, true);
  420. }
  421. // construct Cell at x, y and with whether it isWall
  422. Cell(int x, int y, boolean isWall) {
  423. this.x = x;
  424. this.y = y;
  425. this.wall = isWall;
  426. }
  427. // add a neighbor to this cell, and this cell as a neighbor to the other
  428. void addNeighbor(Cell other) {
  429. if (!this.neighbors.contains(other)) { // avoid duplicates
  430. this.neighbors.add(other);
  431. }
  432. if (!other.neighbors.contains(this)) { // avoid duplicates
  433. other.neighbors.add(this);
  434. }
  435. }
  436. // used in updateGrid()
  437. boolean isCellBelowNeighbor() {
  438. return this.neighbors.contains(new Cell(this.x, this.y + 1));
  439. }
  440. // used in updateGrid()
  441. boolean isCellRightNeighbor() {
  442. return this.neighbors.contains(new Cell(this.x + 1, this.y));
  443. }
  444. // useful Cell equivalence
  445. @Override
  446. public boolean equals(Object other) {
  447. if (!(other instanceof Cell)) return false;
  448. Cell otherCell = (Cell) other;
  449. return (this.x == otherCell.x && this.y == otherCell.y);
  450. }
  451.  
  452. // should be overridden with equals
  453. @Override
  454. public int hashCode() {
  455. // random hash code method designed to be usually unique
  456. return this.x + this.y * 256;
  457. }
  458.  
  459. }
  460. // generate from upper left (In computing the y increases down often)
  461. private void generateMaze() {
  462. generateMaze(0, 0);
  463. }
  464. // generate the maze from coordinates x, y
  465. private void generateMaze(int x, int y) {
  466. generateMaze(getCell(x, y)); // generate from Cell
  467. }
  468. private void generateMaze(Cell startAt) {
  469. // don't generate from cell not there
  470. if (startAt == null) return;
  471. startAt.open = false; // indicate cell closed for generation
  472. ArrayList<Cell> cellsList = new ArrayList<>();
  473. cellsList.add(startAt);
  474.  
  475. while (!cellsList.isEmpty()) {
  476. Cell cell;
  477. // this is to reduce but not completely eliminate the number
  478. // of long twisting halls with short easy to detect branches
  479. // which results in easy mazes
  480. if (random.nextInt(10)==0)
  481. cell = cellsList.remove(random.nextInt(cellsList.size()));
  482. else cell = cellsList.remove(cellsList.size() - 1);
  483. // for collection
  484. ArrayList<Cell> neighbors = new ArrayList<>();
  485. // cells that could potentially be neighbors
  486. Cell[] potentialNeighbors = new Cell[]{
  487. getCell(cell.x + 1, cell.y),
  488. getCell(cell.x, cell.y + 1),
  489. getCell(cell.x - 1, cell.y),
  490. getCell(cell.x, cell.y - 1)
  491. };
  492. for (Cell other : potentialNeighbors) {
  493. // skip if outside, is a wall or is not opened
  494. if (other==null || other.wall || !other.open) continue;
  495. neighbors.add(other);
  496. }
  497. if (neighbors.isEmpty()) continue;
  498. // get random cell
  499. Cell selected = neighbors.get(random.nextInt(neighbors.size()));
  500. // add as neighbor
  501. selected.open = false; // indicate cell closed for generation
  502. cell.addNeighbor(selected);
  503. cellsList.add(cell);
  504. cellsList.add(selected);
  505. }
  506. updateGrid();
  507. }
  508. // used to get a Cell at x, y; returns null out of bounds
  509. public Cell getCell(int x, int y) {
  510. try {
  511. return cells[x][y];
  512. } catch (ArrayIndexOutOfBoundsException e) { // catch out of bounds
  513. return null;
  514. }
  515. }
  516. // draw the maze
  517. public void updateGrid() {
  518. char backChar = ' ', wallChar = 'X', cellChar = ' ';
  519. // fill background
  520. for (int x = 0; x < gridDimensionX; x ++) {
  521. for (int y = 0; y < gridDimensionY; y ++) {
  522. mazeGrid[x][y] = backChar;
  523. }
  524. }
  525. // build walls
  526. for (int x = 0; x < gridDimensionX; x ++) {
  527. for (int y = 0; y < gridDimensionY; y ++) {
  528. if (x % 2 == 0 || y % 2 == 0)
  529. mazeGrid[x][y] = wallChar;
  530. }
  531. }
  532. // make meaningful representation
  533. for (int x = 0; x < dimensionX; x++) {
  534. for (int y = 0; y < dimensionY; y++) {
  535. Cell current = getCell(x, y);
  536. int gridX = x * 2 + 1, gridY = y * 2 + 1;
  537. mazeGrid[gridX][gridY] = cellChar;
  538. if (current.isCellBelowNeighbor()) {
  539. mazeGrid[gridX][gridY + 1] = cellChar;
  540. }
  541. if (current.isCellRightNeighbor()) {
  542. mazeGrid[gridX + 1][gridY] = cellChar;
  543. }
  544. }
  545. }
  546.  
  547. // Δημιουργούμε ένα καθαρό πλέγμα ...
  548. searching = false;
  549. endOfSearch = false;
  550. fillGrid();
  551. // ... και αντιγράφουμε μέσα του τις θέσεις των εμποδίων
  552. // που δημιούργησε ο αλγόριθμος κατασκευής του λαβύρινθου
  553. for (int x = 0; x < gridDimensionX; x++) {
  554. for (int y = 0; y < gridDimensionY; y++) {
  555. if (mazeGrid[x][y] == wallChar && grid[x][y] != ROBOT && grid[x][y] != TARGET){
  556. grid[x][y] = OBST;
  557. }
  558. }
  559. }
  560. }
  561. } // end nested class MyMaze
  562.  
  563. /*
  564. **********************************************************
  565. * Σταθερές της κλάσης MazePanel
  566. **********************************************************
  567. */
  568.  
  569. private final static int
  570. INFINITY = Integer.MAX_VALUE, // Η αναπαράσταση του άπειρου
  571. EMPTY = 0, // κενό κελί
  572. OBST = 1, // κελί με εμπόδιο
  573. ROBOT = 2, // η θέση του ρομπότ
  574. TARGET = 3, // η θέση του στόχου
  575. FRONTIER = 4, // κελιά του μετώπου αναζήτησης (ΑΝΟΙΚΤΈΣ καταστάσεις)
  576. CLOSED = 5, // κελιά κλειστών καταστάσεων
  577. ROUTE = 6; // κελιά που σχηματίζουν τη διαδρομή ρομπότ-στόχος
  578.  
  579. // Μηνύματα προς τον χρήστη
  580. private final static String
  581. msgDrawAndSelect =
  582. "\"Σχεδιάστε\" εμπόδια και επιλέξτε 'Βήμα-Βήμα' ή 'Κίνηση'",
  583. msgSelectStepByStepEtc =
  584. "Επιλέξτε 'Βήμα-Βήμα' ή 'Κίνηση' ή 'Καθάρισμα'",
  585. msgNoSolution =
  586. "Δεν υπάρχει διαδρομή για τον στόχο !!!";
  587.  
  588. /*
  589. **********************************************************
  590. * Μεταβλητές της κλάσης MazePanel
  591. **********************************************************
  592. */
  593.  
  594. JTextField rowsField, columnsField;
  595.  
  596. int rows = 41, // Ο αριθμός των γραμμών του πλέγματος
  597. columns = 41, // Ο αριθμός των στηλών του πλέγματος
  598. squareSize = 500/rows; // Το μέγεθος του κελιού σε pixels
  599.  
  600.  
  601. int arrowSize = squareSize/2; // Το μέγεθος της μύτης του βέλους
  602. // που δείχνει το προκάτοχο κελί
  603. ArrayList<Cell> openSet = new ArrayList();// το σύνολο ανοικτών καταστάσεων
  604. ArrayList<Cell> closedSet = new ArrayList();// το σύνολο κλειστών καταστάσεων
  605. ArrayList<Cell> graph = new ArrayList();// το σύνολο των κορυφών του γράφου
  606. // που εξερευνά ο αλγόριθμος Dijkstra
  607.  
  608. Cell robotStart; // η αρχική θέση του ρομπότ
  609. Cell targetPos; // η θέση του στόχου
  610.  
  611. JLabel message; // μήνυμα προς τον χρήστη
  612.  
  613. // τα κουμπιά για την επιλογή του αλγόριθμου
  614. JRadioButton dfs, bfs, aStar, greedy, dijkstra;
  615.  
  616. // ο slider για την ρύθμιση της ταχύτητας του animation
  617. JSlider slider;
  618.  
  619. // Επιτρέπονται διαγώνιες κινήσεις;
  620. JCheckBox diagonal;
  621. // Σχεδίαση βελών προς προκατόχους
  622. JCheckBox drawArrows;
  623.  
  624. int[][] grid; // το πλέγμα
  625. boolean found; // flag ότι βρέθηκε ο στόχος
  626. boolean searching; // flag ότι η αναζήτηση είναι σε εξέλιξη
  627. boolean endOfSearch; // flag ότι η αναζήτηση έφθασε στο τέρμα
  628. int delay; // ο χρόνος της καθυστέρησης σε msec του animation
  629. int expanded; // ο αριθμός των κόμβων που έχουν αναπτυχθεί
  630.  
  631. // το αντικείμενο που ελέγχει το animation
  632. RepaintAction action = new RepaintAction();
  633.  
  634. // ο Timer που ρυθμίζει την ταχύτητα εκτέλεσης του animation
  635. Timer timer;
  636.  
  637. /**
  638. * Ο δημιουργός του panel
  639. * @param width το πλάτος του panel.
  640. * @param height το ύψος panel.
  641. */
  642. public MazePanel(int width, int height) {
  643.  
  644. setLayout(null);
  645.  
  646. MouseHandler listener = new MouseHandler();
  647. addMouseListener(listener);
  648. addMouseMotionListener(listener);
  649.  
  650. setBorder(BorderFactory.createMatteBorder(2,2,2,2,Color.blue));
  651.  
  652. setPreferredSize( new Dimension(width,height) );
  653.  
  654. grid = new int[rows][columns];
  655.  
  656. // Δημιουργούμε τα περιεχόμενα του panel
  657.  
  658. message = new JLabel(msgDrawAndSelect, JLabel.CENTER);
  659. message.setForeground(Color.blue);
  660. message.setFont(new Font("Helvetica",Font.PLAIN,16));
  661.  
  662. JLabel rowsLbl = new JLabel("Πλήθος γραμμών (5-83):", JLabel.RIGHT);
  663. rowsLbl.setFont(new Font("Helvetica",Font.PLAIN,13));
  664.  
  665. rowsField = new JTextField();
  666. rowsField.setText(Integer.toString(rows));
  667.  
  668. JLabel columnsLbl = new JLabel("Πλήθος στηλών (5-83):", JLabel.RIGHT);
  669. columnsLbl.setFont(new Font("Helvetica",Font.PLAIN,13));
  670.  
  671. columnsField = new JTextField();
  672. columnsField.setText(Integer.toString(columns));
  673.  
  674. JButton resetButton = new JButton("Νέο πλέγμα");
  675. resetButton.addActionListener(new ActionHandler());
  676. resetButton.setBackground(Color.lightGray);
  677. resetButton.setToolTipText
  678. ("Καθαρίζει και επανασχεδιάζει το πλέγμα με βάση τις δοθείσες διαστάσεις του");
  679. resetButton.addActionListener(new java.awt.event.ActionListener() {
  680. @Override
  681. public void actionPerformed(java.awt.event.ActionEvent evt) {
  682. resetButtonActionPerformed(evt);
  683. }
  684. });
  685.  
  686. JButton mazeButton = new JButton("Λαβύρινθος");
  687. mazeButton.addActionListener(new ActionHandler());
  688. mazeButton.setBackground(Color.lightGray);
  689. mazeButton.setToolTipText
  690. ("Δημιουργεί έναν τυχαίο λαβύρινθο");
  691. mazeButton.addActionListener(new java.awt.event.ActionListener() {
  692. @Override
  693. public void actionPerformed(java.awt.event.ActionEvent evt) {
  694. mazeButtonActionPerformed(evt);
  695. }
  696. });
  697.  
  698. JButton clearButton = new JButton("Καθάρισμα");
  699. clearButton.addActionListener(new ActionHandler());
  700. clearButton.setBackground(Color.lightGray);
  701. clearButton.setToolTipText
  702. ("Πρώτο κλικ: καθάρισμα αναζήτησης, Δεύτερο κλικ: καθάρισμα εμποδίων");
  703.  
  704. JButton stepButton = new JButton("Βήμα - Βήμα");
  705. stepButton.addActionListener(new ActionHandler());
  706. stepButton.setBackground(Color.lightGray);
  707. stepButton.setToolTipText
  708. ("Η αναζήτηση γίνεται βήμα-βήμα για κάθε κλικ");
  709.  
  710. JButton animationButton = new JButton("Κίνηση");
  711. animationButton.addActionListener(new ActionHandler());
  712. animationButton.setBackground(Color.lightGray);
  713. animationButton.setToolTipText
  714. ("Η αναζήτηση γίνεται αυτόματα");
  715.  
  716. JLabel velocity = new JLabel("Ταχύτητα", JLabel.CENTER);
  717. velocity.setFont(new Font("Helvetica",Font.PLAIN,10));
  718.  
  719. slider = new JSlider(0,1000,500); // αρχική τιμή καθυστέρησης 500 msec
  720. slider.setToolTipText
  721. ("Ρυθμίζει την καθυστέρηση σε κάθε βήμα (0 μέχρι 1 sec)");
  722.  
  723. delay = 1000-slider.getValue();
  724. slider.addChangeListener(new ChangeListener() {
  725. @Override
  726. public void stateChanged(ChangeEvent evt) {
  727. JSlider source = (JSlider)evt.getSource();
  728. if (!source.getValueIsAdjusting()) {
  729. delay = 1000-source.getValue();
  730. }
  731. }
  732. });
  733.  
  734. // ButtonGroup που συγχρονίζει τα πέντε RadioButtons
  735. // που επιλέγουν τον αλγόριθμο, έτσι ώστε ένα μόνο από
  736. // αυτά να μπορεί να επιλεγεί ανά πάσα στιγμή
  737. ButtonGroup algoGroup = new ButtonGroup();
  738.  
  739. dfs = new JRadioButton("DFS");
  740. dfs.setToolTipText("Αλγόριθμος αναζήτησης πρώτα σε βάθος");
  741. algoGroup.add(dfs);
  742. dfs.addActionListener(new ActionHandler());
  743.  
  744. bfs = new JRadioButton("BFS");
  745. bfs.setToolTipText("Αλγόριθμος αναζήτησης πρώτα σε πλάτος");
  746. algoGroup.add(bfs);
  747. bfs.addActionListener(new ActionHandler());
  748.  
  749. aStar = new JRadioButton("A*");
  750. aStar.setToolTipText("Αλγόριθμος αναζήτησης Α*");
  751. algoGroup.add(aStar);
  752. aStar.addActionListener(new ActionHandler());
  753.  
  754. greedy = new JRadioButton("Greedy");
  755. greedy.setToolTipText("Αλγόριθμος άπληστης αναζήτησης");
  756. algoGroup.add(greedy);
  757. greedy.addActionListener(new ActionHandler());
  758.  
  759. dijkstra = new JRadioButton("Dijkstra");
  760. dijkstra.setToolTipText("Αλγόριθμος του Dijkstra");
  761. algoGroup.add(dijkstra);
  762. dijkstra.addActionListener(new ActionHandler());
  763.  
  764. JPanel algoPanel = new JPanel();
  765. algoPanel.setBorder(javax.swing.BorderFactory.
  766. createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(),
  767. "Αλγόριθμοι", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
  768. javax.swing.border.TitledBorder.TOP, new java.awt.Font("Helvetica", 0, 14)));
  769.  
  770. dfs.setSelected(true); // επιλέγουμε αρχικά τον DFS
  771.  
  772. diagonal = new
  773. JCheckBox("Διαγώνιες κινήσεις");
  774. diagonal.setToolTipText("Επιτρέπονται και διαγώνιες κινήσεις");
  775.  
  776. drawArrows = new
  777. JCheckBox("Βέλη προς προκατόχους");
  778. drawArrows.setToolTipText("Σχεδίαση βελών προς προκατόχους καταστάσεις");
  779.  
  780. JLabel robot = new JLabel("Ρομπότ", JLabel.CENTER);
  781. robot.setForeground(Color.red);
  782. robot.setFont(new Font("Helvetica",Font.PLAIN,14));
  783.  
  784. JLabel target = new JLabel("Στόχος", JLabel.CENTER);
  785. target.setForeground(Color.GREEN);
  786. target.setFont(new Font("Helvetica",Font.PLAIN,14));
  787.  
  788. JLabel frontier = new JLabel("Μέτωπο", JLabel.CENTER);
  789. frontier.setForeground(Color.blue);
  790. frontier.setFont(new Font("Helvetica",Font.PLAIN,14));
  791.  
  792. JLabel closed = new JLabel("Κλειστές", JLabel.CENTER);
  793. closed.setForeground(Color.CYAN);
  794. closed.setFont(new Font("Helvetica",Font.PLAIN,14));
  795.  
  796. JButton aboutButton = new JButton("Σχετικά με το Maze");
  797. aboutButton.addActionListener(new ActionHandler());
  798. aboutButton.setBackground(Color.lightGray);
  799. aboutButton.addActionListener(new java.awt.event.ActionListener() {
  800. @Override
  801. public void actionPerformed(java.awt.event.ActionEvent evt) {
  802. aboutButtonActionPerformed(evt);
  803. }
  804. });
  805.  
  806. // προσθέτουμε τα περιεχόμενα στο panel
  807. add(message);
  808. add(rowsLbl);
  809. add(rowsField);
  810. add(columnsLbl);
  811. add(columnsField);
  812. add(resetButton);
  813. add(mazeButton);
  814. add(clearButton);
  815. add(stepButton);
  816. add(animationButton);
  817. add(velocity);
  818. add(slider);
  819. add(dfs);
  820. add(bfs);
  821. add(aStar);
  822. add(greedy);
  823. add(dijkstra);
  824. add(algoPanel);
  825. add(diagonal);
  826. add(drawArrows);
  827. add(robot);
  828. add(target);
  829. add(frontier);
  830. add(closed);
  831. add(aboutButton);
  832.  
  833. // ρυθμίζουμε τα μεγέθη και τις θέσεις τους
  834. message.setBounds(0, 515, 500, 23);
  835. rowsLbl.setBounds(520, 5, 140, 25);
  836. rowsField.setBounds(665, 5, 25, 25);
  837. columnsLbl.setBounds(520, 35, 140, 25);
  838. columnsField.setBounds(665, 35, 25, 25);
  839. resetButton.setBounds(520, 65, 170, 25);
  840. mazeButton.setBounds(520, 95, 170, 25);
  841. clearButton.setBounds(520, 125, 170, 25);
  842. stepButton.setBounds(520, 155, 170, 25);
  843. animationButton.setBounds(520, 185, 170, 25);
  844. velocity.setBounds(520, 215, 170, 10);
  845. slider.setBounds(520, 225, 170, 25);
  846. dfs.setBounds(530, 270, 70, 25);
  847. bfs.setBounds(600, 270, 70, 25);
  848. aStar.setBounds(530, 295, 70, 25);
  849. greedy.setBounds(600, 295, 85, 25);
  850. dijkstra.setBounds(530, 320, 85, 25);
  851. algoPanel.setLocation(520,250);
  852. algoPanel.setSize(170, 100);
  853. diagonal.setBounds(520, 355, 170, 25);
  854. drawArrows.setBounds(520, 380, 170, 25);
  855. robot.setBounds(520, 465, 80, 25);
  856. target.setBounds(605, 465, 80, 25);
  857. frontier.setBounds(520, 485, 80, 25);
  858. closed.setBounds(605, 485, 80, 25);
  859. aboutButton.setBounds(520, 515, 170, 25);
  860.  
  861. // δημιουργούμε τον timer
  862. timer = new Timer(delay, action);
  863.  
  864. // δίνουμε στα κελιά του πλέγματος αρχικές τιμές
  865. // εδώ γίνεται και το πρώτο βήμα των αλγόριθμων
  866. fillGrid();
  867.  
  868. } // end constructor
  869.  
  870. /**
  871. * Λειτουργία που εκτελείται αν ο χρήστης πιέσει το κουμπί "Νέο Πλέγμα"
  872. */
  873. private void resetButtonActionPerformed(java.awt.event.ActionEvent evt) {
  874. initializeGrid(false);
  875. } // end resetButtonActionPerformed()
  876.  
  877. /**
  878. * Λειτουργία που εκτελείται αν ο χρήστης πιέσει το κουμπί "Λαβύριθμος"
  879. */
  880. private void mazeButtonActionPerformed(java.awt.event.ActionEvent evt) {
  881. initializeGrid(true);
  882. } // end mazeButtonActionPerformed()
  883.  
  884. /**
  885. * Δημιουργεί ένα νέο καθαρό πλέγμα ή ένα νέο λαβύρινθο
  886. */
  887. private void initializeGrid(Boolean makeMaze) {
  888. int oldRows = rows;
  889. int oldColumns = columns;
  890. try {
  891. if (!rowsField.getText().isEmpty()){
  892. rows = Integer.parseInt(rowsField.getText());
  893. } else {
  894. JOptionPane.showMessageDialog(this,
  895. "Το πεδίο \"Πλήθος γραμμών\" \nδέχεται μόνο αριθμούς μεταξύ 5 και 83",
  896. "Πρόβλημα", JOptionPane.ERROR_MESSAGE);
  897. rows = oldRows;
  898. return;
  899. }
  900. } catch (NumberFormatException ex) {
  901. JOptionPane.showMessageDialog(this,
  902. "Το πεδίο \"Πλήθος γραμμών\" \nδέχεται μόνο αριθμούς μεταξύ 5 και 83",
  903. "Πρόβλημα", JOptionPane.ERROR_MESSAGE);
  904. rows = oldRows;
  905. return;
  906. }
  907. if (rows < 5 || rows > 83) {
  908. JOptionPane.showMessageDialog(this,
  909. "Το πεδίο \"Πλήθος γραμμών\" \nδέχεται τιμές μεταξύ 5 και 83",
  910. "Πρόβλημα", JOptionPane.ERROR_MESSAGE);
  911. rows = oldRows;
  912. return;
  913. }
  914. try {
  915. if (!columnsField.getText().isEmpty()){
  916. columns = Integer.parseInt(columnsField.getText());
  917. } else {
  918. JOptionPane.showMessageDialog(this,
  919. "Το πεδίο \"Πλήθος στηλών\" \nδέχεται μόνο αριθμούς μεταξύ 5 και 83",
  920. "Πρόβλημα", JOptionPane.ERROR_MESSAGE);
  921. columns = oldColumns;
  922. return;
  923. }
  924. } catch (NumberFormatException ex) {
  925. JOptionPane.showMessageDialog(this,
  926. "Το πεδίο \"Πλήθος στηλών\" \nδέχεται μόνο αριθμούς μεταξύ 5 και 83",
  927. "Πρόβλημα", JOptionPane.ERROR_MESSAGE);
  928. columns = oldColumns;
  929. return;
  930. }
  931. if (columns < 5 || columns > 83) {
  932. JOptionPane.showMessageDialog(this,
  933. "Το πεδίο \"Πλήθος στηλών\" \nδέχεται τιμές μεταξύ 5 και 83",
  934. "Πρόβλημα", JOptionPane.ERROR_MESSAGE);
  935. columns = oldColumns;
  936. return;
  937. }
  938. squareSize = 500/(rows > columns ? rows : columns);
  939. arrowSize = squareSize/2;
  940. // ο λαβύρινθος πρέπει να έχει περιττό αριθμό γραμμών και στηλών
  941. if (makeMaze && rows % 2 == 0) {
  942. rows -= 1;
  943. }
  944. if (makeMaze && columns % 2 == 0) {
  945. columns -= 1;
  946. }
  947. grid = new int[rows][columns];
  948. robotStart = new Cell(rows-2,1);
  949. targetPos = new Cell(1,columns-2);
  950. dfs.setEnabled(true);
  951. dfs.setSelected(true);
  952. bfs.setEnabled(true);
  953. aStar.setEnabled(true);
  954. greedy.setEnabled(true);
  955. dijkstra.setEnabled(true);
  956. diagonal.setSelected(false);
  957. diagonal.setEnabled(true);
  958. drawArrows.setSelected(false);
  959. drawArrows.setEnabled(true);
  960. slider.setValue(500);
  961. if (makeMaze) {
  962. MyMaze maze = new MyMaze(rows/2,columns/2);
  963. } else {
  964. fillGrid();
  965. }
  966. } // end initializeGrid()
  967.  
  968.  
  969. /**
  970. * Λειτουργία που εκτελείται αν ο χρήστης πιέσει το κουμπί "Σχετικά με το Maze"
  971. */
  972. private void aboutButtonActionPerformed(java.awt.event.ActionEvent evt) {
  973. AboutBox aboutBox = new AboutBox(mazeFrame,true);
  974. aboutBox.setVisible(true);
  975. } // end aboutButtonActionPerformed()
  976.  
  977. /**
  978. * Επεκτείνει ένα κόμβο και δημιουργεί τους διαδόχους του
  979. */
  980. private void expandNode(){
  981. // Τον αλγόριθμο του Dijkstra τον χειριζόμαστε ξεχωριστά
  982. if (dijkstra.isSelected()){
  983. Cell u;
  984. // 11: while Q is not empty:
  985. if (graph.isEmpty()){
  986. return;
  987. }
  988. // 12: u := vertex in Q (graph) with smallest distance in dist[] ;
  989. // 13: remove u from Q (graph);
  990. u = graph.remove(0);
  991. // Προσθέτουμε την κορυφή u στις κλειστές
  992. closedSet.add(u);
  993. // Αν βρέθηκε ο στόχος ...
  994. if (u.row == targetPos.row && u.col == targetPos.col){
  995. found = true;
  996. return;
  997. }
  998. // Καταμετρούμε τους κόμβους που έχουμε αναπτύξει.
  999. expanded++;
  1000. // Ενημερώνουμε το χρώμα του κελιού
  1001. grid[u.row][u.col] = CLOSED;
  1002. // 14: if dist[u] = infinity:
  1003. if (u.dist == INFINITY){
  1004. // ... τότε δεν υπάρχει λύση.
  1005. // 15: break;
  1006. return;
  1007. // 16: end if
  1008. }
  1009. // Δημιουργούμε τους γείτονες της u
  1010. ArrayList<Cell> neighbors = createSuccesors(u, false);
  1011. // 18: for each neighbor v of u:
  1012. for (Cell v: neighbors){
  1013. // 20: alt := dist[u] + dist_between(u, v) ;
  1014. int alt = u.dist + distBetween(u,v);
  1015. // 21: if alt < dist[v]:
  1016. if (alt < v.dist){
  1017. // 22: dist[v] := alt ;
  1018. v.dist = alt;
  1019. // 23: previous[v] := u ;
  1020. v.prev = u;
  1021. // Ενημέρωσε το χρώμα του κελιού
  1022. grid[v.row][v.col] = FRONTIER;
  1023. // 24: decrease-key v in Q;
  1024. // (ταξινομούμε την λίστα των κόμβων ως προς dist)
  1025. Collections.sort(graph, new CellComparatorByDist());
  1026. }
  1027. }
  1028. // Ο χειρισμός των υπόλοιπων αλγόριθμων
  1029. } else {
  1030. Cell current;
  1031. if (dfs.isSelected() || bfs.isSelected()) {
  1032. // Εδώ έχουμε το 3ο βήμα των αλγόριθμων DFS και BFS
  1033. // 3. Αφαίρεσε την πρώτη κατάσταση Si, από τις ΑΝΟΙΚΤΕΣ ....
  1034. current = openSet.remove(0);
  1035. } else {
  1036. // Εδώ έχουμε το 3ο βήμα των αλγόριθμων Α* και Greedy
  1037. // 3. Αφαίρεσε την κατάσταση Si, από την λίστα ΑΝΟΙΚΤΕΣ,
  1038. // για την οποία f(Si) <= f(Sj) για όλες τις άλλες
  1039. // ανοικτές καταστάσεις Sj ...
  1040. // (ταξινομούμε πρώτα τη λίστα ΑΝΟΙΚΤΕΣ κατά αύξουσα σειρά ως προς f)
  1041. Collections.sort(openSet, new CellComparatorByF());
  1042. current = openSet.remove(0);
  1043. }
  1044. // ... και πρόσθεσέ την στις ΚΛΕΙΣΤΕΣ.
  1045. closedSet.add(0,current);
  1046. // Ενημέρωσε το χρώμα του κελιού
  1047. grid[current.row][current.col] = CLOSED;
  1048. // Αν ο επιλεγμένος κόμβος είναι ο στόχος ...
  1049. if (current.row == targetPos.row && current.col == targetPos.col) {
  1050. // ... τότε τερμάτισε κλπ
  1051. Cell last = targetPos;
  1052. last.prev = current.prev;
  1053. closedSet.add(last);
  1054. found = true;
  1055. return;
  1056. }
  1057. // Καταμετρούμε τους κόμβους που έχουμε αναπτύξει.
  1058. expanded++;
  1059. // Εδώ έχουμε το 4ο βήμα των αλγόριθμων
  1060. // 4. Δημιούργησε τις διαδόχους της Si, με βάση τις ενέργειες που μπορούν
  1061. // να εφαρμοστούν στην Si.
  1062. // Η κάθε διάδοχος έχει ένα δείκτη προς την Si, ως την προκάτοχό της.
  1063. // Στην περίπτωση των αλγόριθμων DFS και BFS οι διάδοχοι δεν πρέπει
  1064. // να ανήκουν ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ.
  1065. ArrayList<Cell> succesors;
  1066. succesors = createSuccesors(current, false);
  1067. // Εδώ έχουμε το 5ο βήμα των αλγόριθμων
  1068. // 5. Για κάθε διάδοχο της Si, ...
  1069. for (Cell cell: succesors){
  1070. // ... αν τρέχουμε τον DFS ...
  1071. if (dfs.isSelected()) {
  1072. // ... πρόσθεσε τον διάδοχο στην αρχή της λίστας ΑΝΟΙΚΤΕΣ
  1073. openSet.add(0, cell);
  1074. // Ενημέρωσε το χρώμα του κελιού
  1075. grid[cell.row][cell.col] = FRONTIER;
  1076. // ... αν τρέχουμε τον ΒFS ...
  1077. } else if (bfs.isSelected()){
  1078. // ... πρόσθεσε τον διάδοχο στο τέλος της λίστας ΑΝΟΙΚΤΕΣ
  1079. openSet.add(cell);
  1080. // Ενημέρωσε το χρώμα του κελιού
  1081. grid[cell.row][cell.col] = FRONTIER;
  1082. // ... αν τρέχουμε τους αλγόριθμους Α* ή Greedy (Βήμα 5 αλγόριθμου Α*) ...
  1083. } else if (aStar.isSelected() || greedy.isSelected()){
  1084. // ... υπολόγισε την τιμή f(Sj)...
  1085. int dxg = current.col-cell.col;
  1086. int dyg = current.row-cell.row;
  1087. int dxh = targetPos.col-cell.col;
  1088. int dyh = targetPos.row-cell.row;
  1089. if (diagonal.isSelected()){
  1090. // Με διαγώνιες κινήσεις υπολογίζουμε
  1091. // το 1000-πλάσιο των ευκλείδιων αποστάσεων
  1092. if (greedy.isSelected()) {
  1093. // ειδικά για τον Greedy ...
  1094. cell.g = 0;
  1095. } else {
  1096. cell.g = current.g+(int)((double)1000*Math.sqrt(dxg*dxg + dyg*dyg));
  1097. }
  1098. cell.h = (int)((double)1000*Math.sqrt(dxh*dxh + dyh*dyh));
  1099. } else {
  1100. // Χωρίς διαγώνιες κινήσεις υπολογίζουμε
  1101. // τις αποστάσεις Manhattan
  1102. if (greedy.isSelected()) {
  1103. // ειδικά για τον Greedy ...
  1104. cell.g = 0;
  1105. } else {
  1106. cell.g = current.g+Math.abs(dxg)+Math.abs(dyg);
  1107. }
  1108. cell.h = Math.abs(dxh)+Math.abs(dyh);
  1109. }
  1110. cell.f = cell.g+cell.h;
  1111. // ... αν η Sj δεν ανήκει ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1112. int openIndex = isInList(openSet,cell);
  1113. int closedIndex = isInList(closedSet,cell);
  1114. if (openIndex == -1 && closedIndex == -1) {
  1115. // ... τότε πρόσθεσε την Sj στις ΑΝΟΙΚΤΕΣ ...
  1116. // ... με τιμή αξιολόγησης f(Sj)
  1117. openSet.add(cell);
  1118. // Ενημέρωσε το χρώμα του κελιού
  1119. grid[cell.row][cell.col] = FRONTIER;
  1120. // Αλλιώς ...
  1121. } else {
  1122. // ... αν ανήκε στις ΑΝΟΙΚΤΕΣ, τότε ...
  1123. if (openIndex > -1){
  1124. // ... σύγκρινε την νέα τιμή αξιολόγισής της με την παλαιά.
  1125. // Αν παλαιά <= νέα ...
  1126. if (openSet.get(openIndex).f <= cell.f) {
  1127. // ... απόβαλε το νέο κόμβο με την κατάσταση Sj
  1128. // (δηλαδή μην κάνεις τίποτε για αυτόν τον κόμβο).
  1129. // Διαφορετικά, ...
  1130. } else {
  1131. // ... αφαίρεσε το στοιχείο (Sj,παλαιά) από τη λίστα
  1132. // στην οποία ανήκει ...
  1133. openSet.remove(openIndex);
  1134. // ... και πρόσθεσε το στοιχείο (Sj,νέα) στις ΑΝΟΙΚΤΕΣ
  1135. openSet.add(cell);
  1136. // Ενημέρωσε το χρώμα του κελιού
  1137. grid[cell.row][cell.col] = FRONTIER;
  1138. }
  1139. // ... αν ανήκε στις ΚΛΕΙΣΤΕΣ, τότε ...
  1140. } else {
  1141. // ... σύγκρινε την νέα τιμή αξιολόγισής της με την παλαιά.
  1142. // Αν παλαιά <= νέα ...
  1143. if (closedSet.get(closedIndex).f <= cell.f) {
  1144. // ... απόβαλε το νέο κόμβο με την κατάσταση Sj
  1145. // (δηλαδή μην κάνεις τίποτε για αυτόν τον κόμβο).
  1146. // Διαφορετικά, ...
  1147. } else {
  1148. // ... αφαίρεσε το στοιχείο (Sj,παλαιά) από τη λίστα
  1149. // στην οποία ανήκει ...
  1150. closedSet.remove(closedIndex);
  1151. // ... και πρόσθεσε το στοιχείο (Sj,νέα) στις ΑΝΟΙΚΤΕΣ
  1152. openSet.add(cell);
  1153. // Ενημέρωσε το χρώμα του κελιού
  1154. grid[cell.row][cell.col] = FRONTIER;
  1155. }
  1156. }
  1157. }
  1158. }
  1159. }
  1160. }
  1161. } //end expandNode()
  1162.  
  1163. /**
  1164. * Δημιουργεί τους διαδόχους μιας κατάστασης/κελιού
  1165. *
  1166. * @param current το κελί του οποίου ζητούμε τους διαδόχους
  1167. * @param makeConnected flag που δηλώνει ότι μας ενδιαφέρουν μόνο οι συντεταγμένες των
  1168. * κελιών χωρίς την ετικέτα τους dist (αφορά μόνον τον Dijkstra)
  1169. * @return οι διάδοχοι του κελιού με μορφή λίστας
  1170. */
  1171. private ArrayList<Cell> createSuccesors(Cell current, boolean makeConnected){
  1172. int r = current.row;
  1173. int c = current.col;
  1174. // Δημιουργούμε μια κενή λίστα για τους διαδόχους του τρέχοντος κελιού.
  1175. ArrayList<Cell> temp = new ArrayList<>();
  1176. // Με διαγώνιες κινήσεις η προτεραιότητα είναι:
  1177. // 1:Πάνω 2:Πάνω-δεξιά 3:Δεξιά 4:Κάτω-δεξιά
  1178. // 5:Κάτω 6:Κάτω-αριστερά 7:Αριστερά 8:Πάνω-αριστερά
  1179.  
  1180. // Χωρίς διαγώνιες κινήσεις η προτεραιότητα είναι:
  1181. // 1:Πάνω 2:Δεξιά 3:Κάτω 4:Αριστερά
  1182.  
  1183. // Αν δεν βρισκόμαστε στο πάνω όριο του πλέγματος
  1184. // και το πάνω κελί δεν είναι εμπόδιο ...
  1185. if (r > 0 && grid[r-1][c] != OBST &&
  1186. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1187. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1188. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1189. isInList(openSet,new Cell(r-1,c)) == -1 &&
  1190. isInList(closedSet,new Cell(r-1,c)) == -1)) {
  1191. Cell cell = new Cell(r-1,c);
  1192. // Στην περίπτωση του αλγόριθμου Dijkstra δεν μπορούμε να προσθέσουμε
  1193. // στην λίστα με τους διαδόχους το "γυμνό" κελί που μόλις δημιουργήσαμε.
  1194. // Το κελί πρέπει να συνοδεύεται από την ετικέτα του dist,
  1195. // γι' αυτό πρέπει να το εντοπίσουμε μέσα στην λίστα graph
  1196. // και από εκεί να το αντιγράψουμε στην λίστα των διαδόχων.
  1197. // Το flag makeConnected είναι απαραίτητο για να είναι δυνατόν να
  1198. // συνεργαστεί η παρούσα μέθοδος createSuccesors() με την
  1199. // μέθοδο findConnectedComponent() η οποία δημιουργεί κατά την
  1200. // αρχικοποίηση του Dijkstra την συνεκτική συνιστώσα.
  1201. if (dijkstra.isSelected()){
  1202. if (makeConnected) {
  1203. temp.add(cell);
  1204. } else {
  1205. int graphIndex = isInList(graph,cell);
  1206. if (graphIndex > -1) {
  1207. temp.add(graph.get(graphIndex));
  1208. }
  1209. }
  1210. } else {
  1211. // ... ενημέρωσε τον δείκτη του πάνω κελιού να δείχνει το τρέχον ...
  1212. cell.prev = current;
  1213. // ... και πρόσθεσε το πάνω κελί στους διαδόχους του τρέχοντος.
  1214. temp.add(cell);
  1215. }
  1216. }
  1217. if (diagonal.isSelected()){
  1218. // Αν δεν βρισκόμαστε ούτε στο πάνω ούτε στο δεξιό όριο του πλέγματος
  1219. // και το πάνω-δεξί κελί δεν είναι εμπόδιο ...
  1220. if (r > 0 && c < columns-1 && grid[r-1][c+1] != OBST &&
  1221. // ... και ένα από τα πάνω ή δεξιό κελιά δεν είναι εμπόδια ...
  1222. // (επειδή δεν είναι λογικό να επιτρέψουμε να περάσει
  1223. // το ρομπότ από μία σχισμή)
  1224. (grid[r-1][c] != OBST || grid[r][c+1] != OBST) &&
  1225. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1226. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1227. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1228. isInList(openSet,new Cell(r-1,c+1)) == -1 &&
  1229. isInList(closedSet,new Cell(r-1,c+1)) == -1)) {
  1230. Cell cell = new Cell(r-1,c+1);
  1231. if (dijkstra.isSelected()){
  1232. if (makeConnected) {
  1233. temp.add(cell);
  1234. } else {
  1235. int graphIndex = isInList(graph,cell);
  1236. if (graphIndex > -1) {
  1237. temp.add(graph.get(graphIndex));
  1238. }
  1239. }
  1240. } else {
  1241. // ... ενημέρωσε τον δείκτη του πάνω-δεξιού κελιού να δείχνει το τρέχον ...
  1242. cell.prev = current;
  1243. // ... και πρόσθεσε το πάνω-δεξί κελί στους διαδόχους του τρέχοντος.
  1244. temp.add(cell);
  1245. }
  1246. }
  1247. }
  1248. // Αν δεν βρισκόμαστε στο δεξί όριο του πλέγματος
  1249. // και το δεξί κελί δεν είναι εμπόδιο ...
  1250. if (c < columns-1 && grid[r][c+1] != OBST &&
  1251. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1252. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1253. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected())? true :
  1254. isInList(openSet,new Cell(r,c+1)) == -1 &&
  1255. isInList(closedSet,new Cell(r,c+1)) == -1)) {
  1256. Cell cell = new Cell(r,c+1);
  1257. if (dijkstra.isSelected()){
  1258. if (makeConnected) {
  1259. temp.add(cell);
  1260. } else {
  1261. int graphIndex = isInList(graph,cell);
  1262. if (graphIndex > -1) {
  1263. temp.add(graph.get(graphIndex));
  1264. }
  1265. }
  1266. } else {
  1267. // ... ενημέρωσε τον δείκτη του δεξιού κελιού να δείχνει το τρέχον ...
  1268. cell.prev = current;
  1269. // ... και πρόσθεσε το δεξί κελί στους διαδόχους του τρέχοντος.
  1270. temp.add(cell);
  1271. }
  1272. }
  1273. if (diagonal.isSelected()){
  1274. // Αν δεν βρισκόμαστε ούτε στο κάτω ούτε στο δεξιό όριο του πλέγματος
  1275. // και το κάτω-δεξί κελί δεν είναι εμπόδιο ...
  1276. if (r < rows-1 && c < columns-1 && grid[r+1][c+1] != OBST &&
  1277. // ... και ένα από τα κάτω ή δεξιό κελιά δεν είναι εμπόδια ...
  1278. (grid[r+1][c] != OBST || grid[r][c+1] != OBST) &&
  1279. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1280. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1281. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1282. isInList(openSet,new Cell(r+1,c+1)) == -1 &&
  1283. isInList(closedSet,new Cell(r+1,c+1)) == -1)) {
  1284. Cell cell = new Cell(r+1,c+1);
  1285. if (dijkstra.isSelected()){
  1286. if (makeConnected) {
  1287. temp.add(cell);
  1288. } else {
  1289. int graphIndex = isInList(graph,cell);
  1290. if (graphIndex > -1) {
  1291. temp.add(graph.get(graphIndex));
  1292. }
  1293. }
  1294. } else {
  1295. // ... ενημέρωσε τον δείκτη του κάτω-δεξιού κελιού να δείχνει το τρέχον ...
  1296. cell.prev = current;
  1297. // ... και πρόσθεσε το κάτω-δεξί κελί στους διαδόχους του τρέχοντος.
  1298. temp.add(cell);
  1299. }
  1300. }
  1301. }
  1302. // Αν δεν βρισκόμαστε στο κάτω όριο του πλέγματος
  1303. // και το κάτω κελί δεν είναι εμπόδιο ...
  1304. if (r < rows-1 && grid[r+1][c] != OBST &&
  1305. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1306. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1307. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1308. isInList(openSet,new Cell(r+1,c)) == -1 &&
  1309. isInList(closedSet,new Cell(r+1,c)) == -1)) {
  1310. Cell cell = new Cell(r+1,c);
  1311. if (dijkstra.isSelected()){
  1312. if (makeConnected) {
  1313. temp.add(cell);
  1314. } else {
  1315. int graphIndex = isInList(graph,cell);
  1316. if (graphIndex > -1) {
  1317. temp.add(graph.get(graphIndex));
  1318. }
  1319. }
  1320. } else {
  1321. // ... ενημέρωσε τον δείκτη του κάτω κελιού να δείχνει το τρέχον ...
  1322. cell.prev = current;
  1323. // ... και πρόσθεσε το κάτω κελί στους διαδόχους του τρέχοντος.
  1324. temp.add(cell);
  1325. }
  1326. }
  1327. if (diagonal.isSelected()){
  1328. // Αν δεν βρισκόμαστε ούτε στο κάτω ούτε στο αριστερό όριο του πλέγματος
  1329. // και το κάτω-αριστερό κελί δεν είναι εμπόδιο ...
  1330. if (r < rows-1 && c > 0 && grid[r+1][c-1] != OBST &&
  1331. // ... και ένα από τα κάτω ή αριστερό κελιά δεν είναι εμπόδια ...
  1332. (grid[r+1][c] != OBST || grid[r][c-1] != OBST) &&
  1333. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1334. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1335. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1336. isInList(openSet,new Cell(r+1,c-1)) == -1 &&
  1337. isInList(closedSet,new Cell(r+1,c-1)) == -1)) {
  1338. Cell cell = new Cell(r+1,c-1);
  1339. if (dijkstra.isSelected()){
  1340. if (makeConnected) {
  1341. temp.add(cell);
  1342. } else {
  1343. int graphIndex = isInList(graph,cell);
  1344. if (graphIndex > -1) {
  1345. temp.add(graph.get(graphIndex));
  1346. }
  1347. }
  1348. } else {
  1349. // ... ενημέρωσε τον δείκτη του κάτω-αριστερού κελιού να δείχνει το τρέχον ...
  1350. cell.prev = current;
  1351. // ... και πρόσθεσε το κάτω-αριστερό κελί στους διαδόχους του τρέχοντος.
  1352. temp.add(cell);
  1353. }
  1354. }
  1355. }
  1356. // Αν δεν βρισκόμαστε στο αριστερό όριο του πλέγματος
  1357. // και το αριστερό κελί δεν είναι εμπόδιο ...
  1358. if (c > 0 && grid[r][c-1] != OBST &&
  1359. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1360. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1361. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1362. isInList(openSet,new Cell(r,c-1)) == -1 &&
  1363. isInList(closedSet,new Cell(r,c-1)) == -1)) {
  1364. Cell cell = new Cell(r,c-1);
  1365. if (dijkstra.isSelected()){
  1366. if (makeConnected) {
  1367. temp.add(cell);
  1368. } else {
  1369. int graphIndex = isInList(graph,cell);
  1370. if (graphIndex > -1) {
  1371. temp.add(graph.get(graphIndex));
  1372. }
  1373. }
  1374. } else {
  1375. // ... ενημέρωσε τον δείκτη του αριστερού κελιού να δείχνει το τρέχον ...
  1376. cell.prev = current;
  1377. // ... και πρόσθεσε το αριστερό κελί στους διαδόχους του τρέχοντος.
  1378. temp.add(cell);
  1379. }
  1380. }
  1381. if (diagonal.isSelected()){
  1382. // Αν δεν βρισκόμαστε ούτε στο πάνω ούτε στο αριστερό όριο του πλέγματος
  1383. // και το πάνω-αριστερό κελί δεν είναι εμπόδιο ...
  1384. if (r > 0 && c > 0 && grid[r-1][c-1] != OBST &&
  1385. // ... και ένα από τα πάνω ή αριστερό κελιά δεν είναι εμπόδια ...
  1386. (grid[r-1][c] != OBST || grid[r][c-1] != OBST) &&
  1387. // ... και (στην περίπτωση μόνο που δεν εκτελούμε τον Α* ή τον Greedy)
  1388. // δεν ανήκει ήδη ούτε στις ΑΝΟΙΚΤΕΣ ούτε στις ΚΛΕΙΣΤΕΣ ...
  1389. ((aStar.isSelected() || greedy.isSelected() || dijkstra.isSelected()) ? true :
  1390. isInList(openSet,new Cell(r-1,c-1)) == -1 &&
  1391. isInList(closedSet,new Cell(r-1,c-1)) == -1)) {
  1392. Cell cell = new Cell(r-1,c-1);
  1393. if (dijkstra.isSelected()){
  1394. if (makeConnected) {
  1395. temp.add(cell);
  1396. } else {
  1397. int graphIndex = isInList(graph,cell);
  1398. if (graphIndex > -1) {
  1399. temp.add(graph.get(graphIndex));
  1400. }
  1401. }
  1402. } else {
  1403. // ... ενημέρωσε τον δείκτη του πάνω-αριστερού κελιού να δείχνει το τρέχον ...
  1404. cell.prev = current;
  1405. // ... και πρόσθεσε το πάνω-αριστερό κελί στους διαδόχους του τρέχοντος.
  1406. temp.add(cell);
  1407. }
  1408. }
  1409. }
  1410. // Επειδή στον αλγόριθμο DFS τα κελιά προστίθενται ένα-ένα στην
  1411. // αρχή της λίστας ΑΝΟΙΚΤΕΣ, αντιστρέφουμε την σειρά των διαδόχων
  1412. // που σχηματίστηκε, ώστε ο διάδοχος που αντιστοιχεί στην υψηλότερη
  1413. // προτεραιότητα, να βρεθεί πρώτος στην λίστα.
  1414. // Για τους Greedy, A* και Dijkstra δεν υπάρχει ζήτημα, γιατί η λίστα
  1415. // ταξινομείται ως προς f ή dist πριν την εξαγωγή του πρώτου στοιχείου της.
  1416. if (dfs.isSelected()){
  1417. Collections.reverse(temp);
  1418. }
  1419. return temp;
  1420. } // end createSuccesors()
  1421.  
  1422. /**
  1423. * Επιστρέφει τον δείκτη του κελιού current στη λίστα list
  1424. *
  1425. * @param list η λίστα μέσα στην οποία αναζητάμε
  1426. * @param current το κελί που αναζητάμε
  1427. * @return ο δείκτης το κελιού μέσα στη λίστα
  1428. * αν το κελί δεν βρεθεί επιστρέφει -1
  1429. */
  1430. private int isInList(ArrayList<Cell> list, Cell current){
  1431. int index = -1;
  1432. for (int i = 0 ; i < list.size(); i++) {
  1433. if (current.row == list.get(i).row && current.col == list.get(i).col) {
  1434. index = i;
  1435. break;
  1436. }
  1437. }
  1438. return index;
  1439. } // end isInList()
  1440.  
  1441. /**
  1442. * Επιστρέφει το προκάτοχο κελί του κελιού current της λίστας list
  1443. *
  1444. * @param list η λίστα μέσα στην οποία αναζητάμε
  1445. * @param current το κελί που αναζητάμε
  1446. * @return το κελί που αντιστοιχεί στον προκάτοχο του current
  1447. */
  1448. private Cell findPrev(ArrayList<Cell> list, Cell current){
  1449. int index = isInList(list, current);
  1450. return list.get(index).prev;
  1451. } // end findPrev()
  1452.  
  1453. /**
  1454. * Επιστρέφει την απόσταση μεταξύ δύο κελιών
  1455. *
  1456. * @param u το ένα κελί
  1457. * @param v το άλλο κελί
  1458. * @return η απόσταση μεταξύ των κελιών u και v
  1459. */
  1460. private int distBetween(Cell u, Cell v){
  1461. int dist;
  1462. int dx = u.col-v.col;
  1463. int dy = u.row-v.row;
  1464. if (diagonal.isSelected()){
  1465. // Με διαγώνιες κινήσεις υπολογίζουμε
  1466. // το 1000-πλάσιο των ευκλείδιων αποστάσεων
  1467. dist = (int)((double)1000*Math.sqrt(dx*dx + dy*dy));
  1468. } else {
  1469. // Χωρίς διαγώνιες κινήσεις υπολογίζουμε
  1470. // τις αποστάσεις Manhattan
  1471. dist = Math.abs(dx)+Math.abs(dy);
  1472. }
  1473. return dist;
  1474. } // end distBetween()
  1475.  
  1476. /**
  1477. * Υπολογίζει την διαδρομή από τον στόχο προς την αρχική θέση
  1478. * του ρομπότ και μετρά τα αντίστοιχα βήματα
  1479. * και την απόσταση που διανύθηκε.
  1480. */
  1481. private void plotRoute(){
  1482. searching = false;
  1483. endOfSearch = true;
  1484. int steps = 0;
  1485. double distance = 0;
  1486. int index = isInList(closedSet,targetPos);
  1487. Cell cur = closedSet.get(index);
  1488. grid[cur.row][cur.col]= TARGET;
  1489. do {
  1490. steps++;
  1491. if (diagonal.isSelected()) {
  1492. int dx = cur.col-cur.prev.col;
  1493. int dy = cur.row-cur.prev.row;
  1494. distance += Math.sqrt(dx*dx + dy*dy);
  1495. } else {
  1496. distance++;
  1497. }
  1498. cur = cur.prev;
  1499. grid[cur.row][cur.col] = ROUTE;
  1500. } while (!(cur.row == robotStart.row && cur.col == robotStart.col));
  1501. grid[robotStart.row][robotStart.col]=ROBOT;
  1502. String msg;
  1503. msg = String.format("Αναπτύχθηκαν κόμβοι: %d, Βήματα: %d, Απόσταση: %.3f",
  1504. expanded,steps,distance);
  1505. message.setText(msg);
  1506.  
  1507. } // end plotRoute()
  1508.  
  1509. /**
  1510. * Δίνει αρχικές τιμές στα κελιά του πλέγματος
  1511. * Με το πρώτο κλικ στο κουμπί 'Καθάρισμα' μηδενίζει τα στοιχεία
  1512. * της τυχόν αναζήτησης που είχε εκτελεστεί (Μέτωπο, Κλειστές, Διαδρομή)
  1513. * και αφήνει ανέπαφα τα εμπόδια και τις θέσεις ρομπότ και στόχου
  1514. * προκειμένου να είναι δυνατή η εκτέλεση άλλου αλγόριθμου
  1515. * με τα ίδια δεδομένα.
  1516. * Με το δεύτερο κλικ αφαιρεί και τα εμπόδια.
  1517. */
  1518. private void fillGrid() {
  1519. if (searching || endOfSearch){
  1520. for (int r = 0; r < rows; r++) {
  1521. for (int c = 0; c < columns; c++) {
  1522. if (grid[r][c] == FRONTIER || grid[r][c] == CLOSED || grid[r][c] == ROUTE) {
  1523. grid[r][c] = EMPTY;
  1524. }
  1525. if (grid[r][c] == ROBOT){
  1526. robotStart = new Cell(r,c);
  1527. }
  1528. if (grid[r][c] == TARGET){
  1529. targetPos = new Cell(r,c);
  1530. }
  1531. }
  1532. }
  1533. searching = false;
  1534. } else {
  1535. for (int r = 0; r < rows; r++) {
  1536. for (int c = 0; c < columns; c++) {
  1537. grid[r][c] = EMPTY;
  1538. }
  1539. }
  1540. robotStart = new Cell(rows-2,1);
  1541. targetPos = new Cell(1,columns-2);
  1542. }
  1543. if (aStar.isSelected() || greedy.isSelected()){
  1544. robotStart.g = 0;
  1545. robotStart.h = 0;
  1546. robotStart.f = 0;
  1547. }
  1548. expanded = 0;
  1549. found = false;
  1550. searching = false;
  1551. endOfSearch = false;
  1552.  
  1553. // Το πρώτο βήμα των υπόλοιπων αλγόριθμων γίνεται εδώ
  1554. // 1. ΑΝΟΙΚΤΕΣ:= [So], ΚΛΕΙΣΤΕΣ:= []
  1555. openSet.removeAll(openSet);
  1556. openSet.add(robotStart);
  1557. closedSet.removeAll(closedSet);
  1558.  
  1559. grid[targetPos.row][targetPos.col] = TARGET;
  1560. grid[robotStart.row][robotStart.col] = ROBOT;
  1561. message.setText(msgDrawAndSelect);
  1562. timer.stop();
  1563. repaint();
  1564.  
  1565. } // end fillGrid()
  1566.  
  1567. /**
  1568. * Προσθέτει στην λίστα που περιέχει τους κόμβους του γραφήματος μόνο
  1569. * εκείνα τα κελιά τα οποία ανήκουν στην ίδια συνεκτική συνιστώσα με τον κόμβο v.
  1570. * Πρόκειται για μια αναζήτηση σε πλάτος με αφετηρία τον κόμβο v.
  1571. *
  1572. * @param v ο κόμβος αφετηρία
  1573. */
  1574. private void findConnectedComponent(Cell v){
  1575. Stack<Cell> stack;
  1576. stack = new Stack();
  1577. ArrayList<Cell> succesors;
  1578. stack.push(v);
  1579. graph.add(v);
  1580. while(!stack.isEmpty()){
  1581. v = stack.pop();
  1582. succesors = createSuccesors(v, true);
  1583. for (Cell c: succesors) {
  1584. if (isInList(graph, c) == -1){
  1585. stack.push(c);
  1586. graph.add(c);
  1587. }
  1588. }
  1589. }
  1590. } // end findConnectedComponent()
  1591.  
  1592. /**
  1593. * Αρχικοποίηση του αλγόριθμου Dijkstra
  1594. *
  1595. * Αν προσέξουμε τον ψευδοκώδικα της Wikipedia παρατηρούμε ότι
  1596. * ο αλγόριθμος εξακολουθεί να ψάχνει για τον στόχο ενόσω υπάρχουν ακόμη
  1597. * κόμβοι στην ουρά Q.
  1598. * Μόνον όταν θα εξαντληθεί η ουρά και δεν έχει βρεθεί ο στόχος μπορεί να
  1599. * δώσει απάντηση ότι δεν υπάρχει λύση.
  1600. * Ως γνωστόν, ο αλγόριθμος μοντελοποιεί το πρόβλημα σαν ένα συνδεδεμένο
  1601. * γράφο. Αυτονόητο είναι πως λύση δεν υπάρχει μόνον όταν το γράφημα δεν
  1602. * είναι συνεκτικό και ο στόχος βρίσκεται σε διαφορετική συνεκτική συνιστώσα
  1603. * από αυτήν της αρχικής θέσης του ρομπότ.
  1604. * Για να είναι λοιπόν δυνατή η αρνητική απάντηση από τον αλγόριθμο
  1605. * θα πρέπει η αναζήτηση να γίνει ΜΟΝΟΝ στην συνεκτική συνιστώσα στην οποία
  1606. * ανήκει η αρχική θέση του ρομπότ.
  1607. */
  1608. private void initializeDijkstra() {
  1609. // Δημιουργούμε πρώτα την συνεκτική συνιστώσα
  1610. // στην οποία ανήκει η αρχική θέση του ρομπότ.
  1611. graph.removeAll(graph);
  1612. findConnectedComponent(robotStart);
  1613. // Εδώ γίνεται η αρχικοποίηση του αλγόριθμου Dijkstra
  1614. // 2: for each vertex v in Graph;
  1615. for (Cell v: graph) {
  1616. // 3: dist[v] := infinity ;
  1617. v.dist = INFINITY;
  1618. // 5: previous[v] := undefined ;
  1619. v.prev = null;
  1620. }
  1621. // 8: dist[source] := 0;
  1622. graph.get(isInList(graph,robotStart)).dist = 0;
  1623. // 9: Q := the set of all nodes in Graph;
  1624. // Εμείς, αντί για τη μεταβλητή Q θα χρησιμοποιήσουμε
  1625. // την ίδια τη λίστα graph την οποία αρχικοποιήσαμε ήδη.
  1626.  
  1627. // Ταξινομούμε την λίστα των κόμβων ως προς dist.
  1628. Collections.sort(graph, new CellComparatorByDist());
  1629. // Αρχικοποιούμε την λίστα των κλειστών κόμβων
  1630. closedSet.removeAll(closedSet);
  1631. } // end initializeDijkstra()
  1632.  
  1633. /**
  1634. * Ζωγραφίζει το πλέγμα
  1635. */
  1636. @Override
  1637. public void paintComponent(Graphics g) {
  1638.  
  1639. super.paintComponent(g); // Γεμίζει το background χρώμα.
  1640.  
  1641. g.setColor(Color.DARK_GRAY);
  1642. g.fillRect(10, 10, columns*squareSize+1, rows*squareSize+1);
  1643.  
  1644. for (int r = 0; r < rows; r++) {
  1645. for (int c = 0; c < columns; c++) {
  1646. if (grid[r][c] == EMPTY) {
  1647. g.setColor(Color.WHITE);
  1648. } else if (grid[r][c] == ROBOT) {
  1649. g.setColor(Color.RED);
  1650. } else if (grid[r][c] == TARGET) {
  1651. g.setColor(Color.GREEN);
  1652. } else if (grid[r][c] == OBST) {
  1653. g.setColor(Color.BLACK);
  1654. } else if (grid[r][c] == FRONTIER) {
  1655. g.setColor(Color.BLUE);
  1656. } else if (grid[r][c] == CLOSED) {
  1657. g.setColor(Color.CYAN);
  1658. } else if (grid[r][c] == ROUTE) {
  1659. g.setColor(Color.YELLOW);
  1660. }
  1661. g.fillRect(11 + c*squareSize, 11 + r*squareSize, squareSize - 1, squareSize - 1);
  1662. }
  1663. }
  1664.  
  1665.  
  1666. if (drawArrows.isSelected()) {
  1667. // Ζωγραφίζουμε όλα τα βέλη από κάθε ανοικτή ή κλειστή κατάσταση
  1668. // προς την προκάτοχό της.
  1669. for (int r = 0; r < rows; r++) {
  1670. for (int c = 0; c < columns; c++) {
  1671. // Αν το τρέχον κελί είναι ο στόχος και έχει βρεθεί λύση
  1672. // ή είναι κελί της διαδρομής προς στο στόχο
  1673. // ή είναι ανοικτή κατάσταση,
  1674. // ή κλειστή αλλά όχι η αρχική θέση του ρομπότ
  1675. if ((grid[r][c] == TARGET && found) || grid[r][c] == ROUTE ||
  1676. grid[r][c] == FRONTIER || (grid[r][c] == CLOSED &&
  1677. !(r == robotStart.row && c == robotStart.col))){
  1678. // Η ουρά του βέλους είναι το τρέχον κελί, ενώ
  1679. // η κορυφή του βέλους είναι το προκάτοχο κελί.
  1680. Cell head;
  1681. if (grid[r][c] == FRONTIER){
  1682. if (dijkstra.isSelected()){
  1683. head = findPrev(graph,new Cell(r,c));
  1684. } else {
  1685. head = findPrev(openSet,new Cell(r,c));
  1686. }
  1687. } else {
  1688. head = findPrev(closedSet,new Cell(r,c));
  1689. }
  1690. // Οι συντεταγμένες του κέντρου του τρέχοντος κελιού
  1691. int tailX = 11+c*squareSize+squareSize/2;
  1692. int tailY = 11+r*squareSize+squareSize/2;
  1693. // Οι συντεταγμένες του κέντρου του προκάτοχου κελιού
  1694. int headX = 11+head.col*squareSize+squareSize/2;
  1695. int headY = 11+head.row*squareSize+squareSize/2;
  1696. // Αν το τρέχον κελί είναι ο στόχος
  1697. // ή είναι κελί της διαδρομής προς το στόχο ...
  1698. if (grid[r][c] == TARGET || grid[r][c] == ROUTE){
  1699. // ... σχεδίασε ένα κόκκινο βέλος προς την κατεύθυνση του στόχου.
  1700. g.setColor(Color.RED);
  1701. drawArrow(g,tailX,tailY,headX,headY);
  1702. // Αλλιώς ...
  1703. } else {
  1704. // ... σχεδίασε ένα μαύρο βέλος προς το προκάτοχο κελί.
  1705. g.setColor(Color.BLACK);
  1706. drawArrow(g,headX,headY,tailX,tailY);
  1707. }
  1708. }
  1709. }
  1710. }
  1711. }
  1712. } // end paintComponent()
  1713.  
  1714. /**
  1715. * Ζωγραφίζει ένα βέλος από το σημείο (x2,y2) προς το σημείο (x1,y1)
  1716. */
  1717. private void drawArrow(Graphics g1, int x1, int y1, int x2, int y2) {
  1718. Graphics2D g = (Graphics2D) g1.create();
  1719.  
  1720. double dx = x2 - x1, dy = y2 - y1;
  1721. double angle = Math.atan2(dy, dx);
  1722. int len = (int) Math.sqrt(dx*dx + dy*dy);
  1723. AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
  1724. at.concatenate(AffineTransform.getRotateInstance(angle));
  1725. g.transform(at);
  1726.  
  1727. // Εμείς ζωγραφίζουμε ένα οριζόντιο βέλος μήκους len
  1728. // που καταλήγει στο σημείο (0,0) με τις δύο αιχμές μήκους arrowSize
  1729. // να σχηματίζουν γωνίες 20 μοιρών με τον άξονα του βέλους ...
  1730. g.drawLine(0, 0, len, 0);
  1731. g.drawLine(0, 0, (int)(arrowSize*Math.sin(70*Math.PI/180)) , (int)(arrowSize*Math.cos(70*Math.PI/180)));
  1732. g.drawLine(0, 0, (int)(arrowSize*Math.sin(70*Math.PI/180)) , -(int)(arrowSize*Math.cos(70*Math.PI/180)));
  1733. // ... και η κλάση AffineTransform αναλαμβάνει τα υπόλοιπα !!!!!!
  1734. // Πώς να μην θαυμάσει κανείς αυτήν την Java !!!!
  1735. } // end drawArrow()
  1736.  
  1737. } // end nested classs MazePanel
  1738.  
  1739. } // end class Maze
Advertisement
Add Comment
Please, Sign In to add comment