Pathfinder Project
Pathfinder Project
A PROJECT REPORT
Submitted by
ABHINAV PREMKUMAR
(2023179053)
SELVAKUMAR R
(2023179049)
DEPARTMENT OF INFORMATION
SCIENCE AND TECHNOLOGY
COLLEGE OF ENGINEERING,
CHENNAI 600025
JANUARY 2024
ABHINAV PREMKUMAR 2023179053 SELVAKUMAR R 2023179049
INDEX
CONTENT
1. PROJECT OVERVIEW.
1.1 INTRODUCTION
1.2 ABSTRACT
2. SYSTEM REQUIREMENTS.
3. ALGORITHMS USED.
3.1 PATHFINDER
4. SOFTWARE DESCRIPTION.
5. PROJECT DESCRIPTION.
5.1 INTRODUCTION
6.1 A* ALGORITHM
7. CONCLUSION.
8. APPENDIX.
8.2 SCREENSHOTS
1.1 INTRODUCTION
The A* Pathfinding Visualizer represents a sophisticated desktop graphical user interface (GUI)
application meticulously crafted in Java, utilizing the Swing framework as its foundational GUI
tool. This application encapsulates a suite of features designed to provide users with a
comprehensive exploration of pathfinding algorithms within a visually dynamic environment.
At the forefront of its capabilities is a responsive and dynamic GUI, affording users the
flexibility to interact seamlessly with the application. The visualizer empowers users to create
intricate 2D mazes by placing walls with precision, defining pivotal start and end nodes that
serve as the locus for pathfinding endeavors.
Central to the application's functionality are two prominent pathfinding algorithms: the A*
Pathfinding Algorithm and the Breadth-First Search algorithm. These algorithms, renowned for
their efficacy in determining optimal routes, form the computational backbone of the visualizer.
The application seamlessly computes the shortest path from the designated start to end nodes,
providing users with a nuanced understanding of algorithmic decision-making.
A distinctive feature of the A* Pathfinding Visualizer lies in its real-time visualization of the
computational process. As users manipulate the maze and nodes, the application dynamically
illustrates the algorithmic journey, revealing step-by-step insights into the decision-making
mechanisms employed to navigate the maze optimally.
In essence, the A* Pathfinding Visualizer serves as an advanced educational tool, inviting users
to engage with the intricacies of pathfinding algorithms within a formal Java-based
environment. The integration of the Swing framework, coupled with the precise implementation
of algorithms, positions this application as a valuable resource for those seeking a formal and in-
depth exploration of the A* Pathfinding Algorithm and Breadth-First Search in a visual manner.
System description are the configuration that a system must have in order for a
hardware or software application to run smoothly and effectively. Failure to meet these
requirements can result in performance problems.
• 4 GB RAM or more.
2.2.1 A computer running a 64-bit version of Windows (7,8, 10, or 11), Linux,
mac-OS (10.14 Mojave or later), or Chrome-OS.
3.1. PATHFINDING
Pathfinding typically involves determining the most efficient route or path from
a starting node to an end node. The problem may also include additional constraints, such as
avoiding certain nodes or minimizing the total cost.
Common Pathfinding Algorithms:
A* Algorithm:
An informed search algorithm that uses heuristics to guide the search.
Balances the cost of reaching a node and the estimated cost from that node to the goal.
Breadth-First Search (BFS):
Systematically explores all nodes at the current depth before moving on to the next level.
Guarantees the shortest path for unweighted graphs.
Applications:
Robotics:
Pathfinding is crucial in robotics for planning the movement of robots to navigate through
environments, avoid obstacles, and reach destinations.
Video Games:
Pathfinding is extensively used in game development to enable non-player characters (NPCs) or
entities to navigate game environments intelligently.
Network Routing:
In computer networks, pathfinding algorithms are employed to determine optimal routes for data
packets to traverse the network efficiently.
openSet.remove(current)
1.1 FRONT-END
JAVA SWING:
Swing has about four times the number of User Interface [UI] components as
AWT and is part of the standard Java distribution. By today's application GUI requirements,
AWT is a limited implementation, not quite capable of providing the components required for
developing complex GUls required in modern commercial applications. The AWT
component set has quite a few bugs and does take up a lot of system resources when
compared to equivalent Swing resources. Netscape introduced its Internet Foundation Classes
[IFC] library for use with Java. Its Classes became very popular with programmers creating
GUI's for commercial applications.
4.2. BACK-END
JAVA:
HISTORY:
The Java language has a very interesting history. Patrick Naughton, Mike Sheridan,
and Jame Gosling, known as the Green team, started the development of Java in the year
1991. These people were the engineers at Sun Microsystems. In 1996, the first public
implementation was released as Java 1.0. The compiler of Java 1.0 was rewritten by Arthur
Van Hoff to comply strictly with its specification. With the introduction of Java 2, the new
versions have multiple different configurations that have been built for the various platforms.
It is worth noting that James Gosling is also known as the father of Java.
The ISO standard body was approached by Sun Microsystems in the year 1997 to formalize
Java, but the process was withdrawn soon. At one point in time, Sun Microsystems provided
most of its implementation of Java available without any cost, despite having the status of
proprietary software.
APPLICATIONS PROGRAMS:
It is worth noting here that JDK (Java Development Kit) should be installed properly on the
system, and the path should also be set.
5.PROJECT DESCRIPTION
5.1 INTRODUCTION.
The Pathfinding Visualizer project is a Java-based application designed
to evaluate the performance of algorithms. The application provides a platform
for users to assess the performance of A* algorithm and breadth first search
algorithm.
1. Dynamic GUI.
2. Drawing 2D "maze" by placing walls, as well as a start and end node.
3. Computing the shortest path from start to end node, using
A* Pathfinding Algorithm
Breadth First Search
4.Visualizing the computational process of the shortest path.
ARCHITECTURE DIAGRAM
The project has been successful in achieving its objectives, and the following
are the key outcomes:
This project has provided valuable learning opportunities for those involved,
Including:
In conclusion, the path finding visualiser project in Java using the Swing API
has successfully created a use friendly and interactive platform for analyzing
algorithms . The project has provided valuable learning opportunities and has
demonstrated the potential of java and the Swing API in developing efficient
and interactive software solutions.
9. APPENDIX
8.1 SAMPLING CODING:
JAVA CODE:
App.java
public class App {
public static void main(String[] args) throws Exception {
Board.java
/**
* Enum representing different cell types
*/
enum Cell {
FREE(0),
START(1),
WALL(2),
END(3);
/**
* A class representing a board/grid consisting of Cells.
*/
public class Board {
private Cell[][] board;
private int xSize;
private int ySize;
/**
* Fills the board with free cells and notes start and end nodes as not set.
*/
public void clearBoard() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
board[i][j] = Cell.FREE;
}
}
startset = false;
endset = false;
}
/**
* Places a specific tile type at a point on the board.
* @param tileType - Type of tile/cell to place.
* @param xPos - x-position on the board to place tile.
* @param yPos - y-position on the board to place tile.
*/
public void setTile(Cell tileType, int xPos, int yPos) {
// If tile being set is START or END tile, update information
automatically, and reset already placed start and end tiles
if (tileType == Cell.START) {
if (startset) {
setTile(Cell.FREE, start[0], start[1]); // remove already placed
start cell
}
start = new int[]{xPos, yPos}; // place current startcell
startset = true;
} else if (tileType == Cell.END) {
if (endset) {
setTile(Cell.FREE, end[0], end[1]); // remove already placed end
cell
}
end = new int[]{xPos, yPos}; // place current endcell
endset = true;
} else {
// check if tile that is being overwritten is a start or end tile
if (getTile(xPos, yPos) == Cell.START) {
startset = false;
} else if (getTile(xPos, yPos) == Cell.END) {
endset = false;
}
}
/**
* Gets type of tile/cell at specific position.
* @param xPos
* @param yPos
* @return
*/
public Cell getTile(int xPos, int yPos) {
return board[yPos][xPos];
}
/**
* Returns the board.
* @return
*/
public Cell[][] getBoard() {
return board;
}
/**
* Returns the x-Size of the board.
* @return
*/
public int getXSize() {
return xSize;
}
/**
* Returns the y-Size of the board.
* @return
*/
public int getYSize() {
return ySize;
}
/**
* Returns whether the start node has been set.
* @return
*/
public boolean isStartSet() {
return startset;
}
/**
* Returns whether the end node has been set.
* @return
*/
public boolean isEndSet() {
return endset;
}
/**
* Returns the coordinates of the start node.
* @return
*/
public int[] getStart() {
return start;
}
/**
* Returns the coordinates of the end node.
* @return
*/
public int[] getEnd() {
return end;
}
/**
* Computes coordinates of the cells adjacent straight to cell with
specified x and y coordinates.
* @param xPos
* @param yPos
* @return
*/
private int[][] getAdjacent(int xPos, int yPos) {
return adj;
}
/**
* Computes coordinates of the cells adjacent diagonally to cell with
specified x and y coordinates.
* @param xPos
* @param yPos
* @return
*/
private int[][] getAdjacent_Diagonal(int xPos, int yPos) {
return adj;
}
/**
* Parses the board and computes weighted bidirectional graph.
* @return - weighted bidirectional graph in adjacency matrix
representation.
*/
public int[][][][] getGraph(boolean diagonals) {
/**
* Edges are stored in adjacency matrix representation on the form:
* new int[xSize][ySize][4][3]
* ^ xCoordinate, yCoordinate and weight of
edge
* ^ number of adjacent nodes (8 if with
diagonals, 4 if without)
* ^ yCoordinate of edge target node
* ^ xCoordinate of edge target node
*/
GraphicsCanvas.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.image.*;
import javax.swing.plaf.DimensionUIResource;
import java.util.LinkedList;
import java.util.ArrayList;
enum Mode {
FREEPLACE(0),
STARTPLACE(1),
WALLPLACE(2),
ENDPLACE(3);
enum ComputationalMethod {
ASTAR("A*"),
BFS("Breadth First Search");
/**
* A Graphics Canvas, used as the main viewport in the application.
*/
public class GraphicsCanvas extends Canvas {
/**
* Constructor for GraphicsCanvas class. UI references to certain components
are required.
* @param showVizualizationCheckbox
* @param enableDiagonalsCB
* @param vizualizationSpeedSlider
* @param algorithmComboBox
* @param startPointLabel
* @param endPointLabel
* @param shortestPathLabel
* @param computationalTimeLabel
* @param outputLog
*/
public GraphicsCanvas(JCheckBox showVizualizationCheckbox, JCheckBox
enableDiagonalsCB, JSlider vizualizationSpeedSlider, JComboBox
algorithmComboBox, JLabel startPointLabel, JLabel endPointLabel, JLabel
shortestPathLabel, JLabel computationalTimeLabel, JTextArea outputLog) {
super();
//this.createBufferStrategy(2);
// Call reset() to set initial zoom and pan depending on viewport size
resetViewport(width, height);
/**
* Sets the mode for this graphicscanvas
* @param mode - Desired mode.
*/
public void setMode(Mode mode) {
this.mode = mode;
}
/**
* Resets the board and repaints canvas with current viewport dimensions.
*/
public void reset() {
reset(getWidth(), getHeight());
}
/**
* Resets the board and repaints canvas, using specified parameters for
width and height of viewport-
* @param viewportWidth
* @param viewportHeight
*/
public void reset(int viewportWidth, int viewportHeight) {
// Clear board
board.clearBoard();
// Update labels
startPointLabel.setText("NOT SET");
endPointLabel.setText("NOT SET");
shortestPathLabel.setText("N/A");
computationalTimeLabel.setText("N/A");
/**
* Resets the pan and zoom only, using specified viewport sizes.
* @param viewportWidth
* @param viewportHeight
*/
public void resetViewport(int viewportWidth, int viewportHeight) {
// Reset zoom
zoom = 1;
// Repaint canvas
repaint();
}
/**
* Forces parsing of the board, and a run of the specified algorithm.
*/
public void run() {
// Parse graph
int[][][][] adj = board.getGraph(enableDiagonals);
// Empty calculation list for collecting results
computationList = new LinkedList<>();
currentPath = null;
// Run thread
thread.start();
// Stop vizualizatiotimer
vizualizationTimer.stop();
if (showVizualization) {
// Not finished visualizing
finishedVisualizing = false;
if (computationInstance != null) {
finishedVisualizing = false;
currentComputation = computationInstance;
repaint();
} else {
finishedVisualizing = true;
repaint();
}
}
};
Thread t = new Thread(r);
t.start();
}
});
vizualizationTimer.start();
} else {
finishedVisualizing = true;
currentComputation = computationList.pollLast();
}
} else {
// Write error message to log
writeLog("ERROR: START and END nodes required.\n");
}
/**
* Update method used for creating and updating the double buffer, as well
as repainting the canvas using double-buffer.
* This method is called through regular update() calls by the ui, as well
as through paint(), to trigger proper repaint when calling repaint().
* @param g - The graphics object used to draw to the actual canvas
*/
public void update(Graphics g) {
// Update grid dimensions based on possible resize
//cellDimension = Math.min(this.getWidth()/board.getXSize(),
this.getHeight()/board.getYSize());
// Initialize buffer
if (buffer == null) {
buffer = new BufferedImage(this.getSize().width,
this.getSize().height, BufferedImage.TYPE_INT_RGB);
bufferGraphics = buffer.getGraphics();
}
/**
* Calls update which utilizes double-buffering.
*/
public void paint(Graphics g) {
update(g);
}
/**
* Paints all content using specified Graphics object.
* @param g - Graphics object used for painting all content.
*/
private void paintContent(Graphics g) {
/**
* Draws the grid to the screen.
* @param g - Graphics object to draw grid with.
*/
private void paintGrid(Graphics g) {
// Draw grid
for (int x = 0; x < board.getXSize() + 1; x++) {
double p = x * cellDimension * zoom;
g.drawLine((int)(p + offsets[0]), (int)(0 + offsets[1]), (int)(p +
offsets[0]), (int)(sc[1] + offsets[1]));
}
/**
* Draws the saved board contents to the screen.
* @param g - Graphics object to draw board with.
*/
private void paintBoard(Graphics g) {
// Fetch board
Cell[][] board = this.board.getBoard();
/**
* Draws the computation instance to screen, if any is available
* @param g - Graphics object to draw computation instance with
*/
private void paintComputation(Graphics g) {
if (currentComputation != null) {
int s = currentComputation.size();
if (s > 0) {
ArrayList<int[]> graynodes = currentComputation.get(0);
for (int[] n : graynodes) {
drawTile(g, Color.GREEN, n[0]*cellDimension,
n[1]*cellDimension, cellDimension);
}
if (s > 1) {
ArrayList<int[]> blacknodes = currentComputation.get(1);
for (int[] n : blacknodes) {
drawTile(g, Color.YELLOW, n[0]*cellDimension,
n[1]*cellDimension, cellDimension);
}
}
}
}
}
/**
* Draws the last computed shortest path to the screen
* @param g - Graphics object to draw path with
*/
private void paintPath(Graphics g) {
if (currentPath != null && finishedVisualizing) {
for (int[] n : currentPath) {
drawTile(g, Color.CYAN, n[0]*cellDimension, n[1]*cellDimension,
cellDimension);
}
}
}
/**
* Paints draws a tile with specified coordinates, size and color, using
specified graphics object.
* @param g - Graphics object to draw tile with.
* @param c - Color to draw tile with.
* @param xPos - desired x-position of tile
* @param yPos - desired y-position of tile
* @param size - desired size (dimension) of tile.
*/
private void drawTile(Graphics g, Color c, int xPos, int yPos, int size) {
// Apply zoom before drawing
double[] pos = worldToScreen(xPos, yPos);
int scale = (int)Math.ceil(size*zoom);
// Draw tile
g.setColor(c);
g.fillRect((int) pos[0], (int) pos[1], scale, scale);
}
/**
* Converts a set of world coordinates to screen coordinates.
* @param x
* @param y
* @return
*/
private double[] worldToScreen(int x, int y) {
double newX = ((x - panX)*zoom);
double newY = ((y - panY)*zoom);
return new double[]{newX, newY};
}
/**
* Converts a set of screen coordinates to world coordinates.
* @param x
* @param y
* @return
*/
private double[] screenToWorld(int x, int y) {
double newX = (x/zoom + panX);
double newY = (y/zoom + panY);
return new double[]{newX, newY};
}
/**
* Writes specified text to outputLog. Automatically moves the caret
position down to bottom.
* @param text - text to write
*/
private void writeLog(String text) {
outputLog.append(text);
outputLog.setCaretPosition(outputLog.getDocument().getLength());
}
/**
* Places tile at specified x and y coordinates to board, using currently
active mode.
* If the placed tile is a start or end tile, updates the labels.
* @param xPos
* @param yPos
*/
private void placeTile(int xPos, int yPos) {
// If tile that is being placed on top of (effectively erased) is a
start or end tile, update the labels
if (board.getTile(xPos, yPos).equals(Cell.START)) {
startPointLabel.setText("NOT SET");
}
if (board.getTile(xPos, yPos).equals(Cell.END)) {
endPointLabel.setText("NOT SET");
}
if (e.getButton() == 1) { // if left-click
// Transform pressed coordinates into proper tile
coordinates in board
double[] worldPos = screenToWorld(e.getX(), e.getY());
int xTile = (int) (worldPos[0] / cellDimension);
int yTile = (int) (worldPos[1] / cellDimension);
if ((xTile >= 0 && xTile < cellCountX) && (yTile >= 0 &&
yTile < cellCountY)) {
// Place tile
placeTile(xTile, yTile);
// Repaint canvas
repaint();
}
};
}
// Update pan
panX = nPanX;
panY = nPanY;
startPanX = e.getX();
startPanY = e.getY();
// Repaint canvas
repaint();
} else if (SwingUtilities.isLeftMouseButton(e)) { // If drawing
// Transform pressed coordinates into proper tile
coordinates in board
double[] worldPos = screenToWorld(e.getX(), e.getY());
int xTile = (int) (worldPos[0] / cellDimension);
int yTile = (int) (worldPos[1] / cellDimension);
if ((xTile >= 0 && xTile < cellCountX) && (yTile >= 0 &&
yTile < cellCountY)) {
// Place Tile
placeTile(xTile, yTile);
GUI.java
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.plaf.InsetsUIResource;
/**
* GUI Class for managing the Graphical User Interface of the application.
*/
public class GUI {
/**
* Constructor for GUI class
*/
public GUI() {
makeFrame();
}
/**
* Initializes the frame for this class instance
*/
private void makeFrame() {
// Create frame
frame = new JFrame("Pathfinding Visualizer");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // Allow
application to terminate peacefully when being exited
/**
* Initializes a menu bar for the specified frame
* @return - the created menubar
*/
private JMenuBar makeMenuBar() {
// Create a menubar and assign it to frame
JMenuBar menubar = new JMenuBar();
//frame.setJMenuBar(menubar);
return menubar;
}
/**
* Creates the west side of the content in the Borderlayout of the specified
content pane
* @return - The created layout packed inside a Component
*/
private Component makeWestLayout() {
// Create a panel to hold the components
JPanel westPanel = new JPanel();
westPanel.setLayout(new GridBagLayout());
// Create GridBagContraints
GridBagConstraints c = new GridBagConstraints();
c.insets = new InsetsUIResource(10, 10, 10, 10);
return westPanel;
}
/**
* Creates a Settings panel containing controls for all settings, such as
vizualization speed, and vizualization type
* @return - Created settingspanel
*/
private Component makeSettingsSubPanel() {
// Create a "Settings" panel to hold some components
JPanel settingsPanel = new JPanel();
settingsPanel.setLayout(new GridBagLayout());
settingsPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLin
eBorder(Color.black), "Settings"));
// Create GridBagConstraits
GridBagConstraints c = new GridBagConstraints();
c.insets = new InsetsUIResource(5, 10, 5, 5);
// Create checkbox
JCheckBox enableDiagonalsCB = new JCheckBox();
enableDiagonalsCB.setSelected(false);
enableDiagonalsCB.addActionListener(e ->
graphicsCanvas.setEnableDiagonals());
c.gridx = 1;
c.gridy = 1;
c.anchor = GridBagConstraints.WEST;
settingsPanel.add(enableDiagonalsCB, c);
this.enableDiagonalsCB = enableDiagonalsCB;
// Create slider
JSlider vizualizationSpeedSlider = new JSlider(10, 100, 50);
vizualizationSpeedSlider.addChangeListener(e ->
graphicsCanvas.updateTimer());
c.gridx = 1;
c.gridy = 2;
c.anchor = GridBagConstraints.WEST;
settingsPanel.add(vizualizationSpeedSlider, c);
this.vizualizationSpeedSlider = vizualizationSpeedSlider;
return settingsPanel;
}
/**
* Creates a Configuration sub panel containing information about
current/last simulation, as well as RUN and CLEAR buttons
* @return - created configurationpanel
*/
private Component makeConfigurationSubPanel() {
// Create configuration panel
JPanel configurationPanel = new JPanel();
configurationPanel.setLayout(new GridBagLayout());
configurationPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.crea
teLineBorder(Color.black), "Configuration"));
c.gridy = 0;
configurationPanel.add(startLabel, c);
c.gridy = 1;
configurationPanel.add(endLabel, c);
c.gridy = 2;
configurationPanel.add(shortestPathLabel, c);
c.gridy = 3;
configurationPanel.add(computationTimeLabel, c);
c.gridy = 0;
configurationPanel.add(startValLabel, c);
c.gridy = 1;
configurationPanel.add(endValLabel, c);
c.gridy = 2;
configurationPanel.add(shortestPathValLabel, c);
c.gridy = 3;
configurationPanel.add(computationTimeValLabel, c);
return configurationPanel;
}
/**
* Creates the center part of the content in the Borderlayout of the
specified content pane
* @return - The created layout packed indside a Component
*/
private Component makeCenterLayout() {
// Create a sample canvas
GraphicsCanvas canvas = new GraphicsCanvas(showVizualizationCheckbox,
enableDiagonalsCB, vizualizationSpeedSlider, algorithmComboBox, startPointLabel,
endPointLabel, shortestPathLabel, computationalTimeLabel, outputLog);
canvas.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
canvas.resized();
}
});
this.graphicsCanvas = canvas;
return canvas;
}
/**
* Creates the south part of the content in the Borderlayout of the
specified content pane
* @return - the created layout packed inside a Component
*/
private Component makeSouthLayout() {
return logPanel;
}
// Create a toolbar
JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
eastPanel.add(toolbar);
/**
* Calmly terminates the application
*/
private void quit() {
System.exit(0);
}
}
8.2 SCREENSHOTS :