0% found this document useful (0 votes)
122 views

Project Report On Algorithm Visualizer

Uploaded by

Charchit Agarwal
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
122 views

Project Report On Algorithm Visualizer

Uploaded by

Charchit Agarwal
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 108

Algorithm Visualizer

A Major Project

PROJECT REPORT SUBMITTED IN PARTIAL


FULFILLMENT OF THE REQUIREMENTS FOR
THE DEGREE OF

MASTER OF COMPUTER APPLICATION

BY

VINAY PRATAP
(MCA/40015/21)

UNDER THE GUIDANCE OF

Dr. Amar Kumar

DEPARTMENT OF CSE

BIRLA INSTITUTEOF TECHNOLOGY- MESRA


Off- Campus, Lalpur, Ranchi.
(2021-2023

1
BIRLA INSTITUTE OF TECHNOLOGY
MESRA
Off- Campus, Lalpur, Ranchi

Certificate

This is to certify that the contents of this major project entitled “Algorithm
Visualizer” is a bonafide work carried out by Vinay Pratap | (Class Roll No.:
MCA/40015/21), of MCA (4th Semester), may be accepted in partial fulfillment for
the degree of Master of Computer Application under my guidance.

This is to further certify that the matter of this of this project has not been
submitted by anyone else for the award of any degree.

………………………………………
(Dr. Amar Kumar)
Department of CSE
Birla Institute of Technology- Lalpur

2
BIRLA INSTITUTE OF TECHNOLOGY
MESRA
Off- Campus, Lalpur, Ranchi

Certificate Of Approval

The foregoing major project is hereby approved as a creditable on “Algorithm


Visualizer”, carried out and presented in a manner satisfactory to warrant its
acceptance as a prerequisite to the degree for which it has been entitled.
It is understood that by this approval the undersigned do not
necessarily endorse or approve any statement made, opinion expressed or
conclusion drawn therein, but approve the project only for the purpose for which
it is submitted.

Committee for evaluation of the project


External Examiner Internal Examiner

Date: Date:

Director (In-Charge)
Birla Institute of Technology
Lalpur, Ranchi

3
ACKNOWLEDGEMENT

We would like to express special thanks & gratitude to our guide, Dr. Amar
Kumar who gave us this golden opportunity to work on this scalable project
on the topic of “Algorithm Visualizer”, which led us into doing a lot of
Research which diversified our knowledge to a huge extent for which we
are thankful.

Also, we would like to thank our parents and friends who supported us
a lot in finalizing this project within the limited time frame.

--------------------------------------
VINAY PRATAP
(MCA/40015/21)

4
Table of Contents

Page No.

Abstract …………………………………………………………………… 6

1. Introduction ………………………………………………………………. 8

2. Review of Literature ……………………………………………………… 11

3. Objective of the Project …………………………………………………... 13

4. System Design……………………………………………………………… 16

5. Methodology for implementation (Formulation/Algorithm) ………….... 21

6. Implementation Details ……………………………………………………. 44

7. Results/Sample output ……………………………………………………... 50

8. Conclusion…………………………………………………………………… 103

Appendix-: Program Source code with adequate comments. 105


References 108

5
Abstract

The Algorithm Visualizer project is a web-based platform that


allows users to interactively explore and visualize various
sorting algorithms and pathfinding algorithms. The platform
includes a range of algorithms such as Bubble Sort, Quick Sort,
Merge Sort, Dijkstra's Algorithm, and A* Search Algorithm,
among others. Users can input their own datasets or use pre-set
datasets to observe how each algorithm operates on the data. The
visualizations are designed to provide an intuitive and interactive
way to understand how these algorithms work and the
differences between them. The pathfinding algorithm
visualization can be particularly useful in real-world
applications, such as map routing and logistics planning. Overall,
the Algorithm Visualizer project is an educational tool that
makes learning about sorting and pathfinding algorithms both
fun and accessible.

In this project, the main aim of the Algorithm Visualizer project


is to provide an interactive and visual platform that helps users
understand the working and differences of various sorting and
pathfinding algorithms. The project aims to make the learning
process of algorithms more engaging, accessible, and intuitive
for beginners and experts alike. Through interactive
visualizations and step-by-step animations, the project aims to
help users gain a deeper understanding of how each algorithm
works and how to apply them in real-world scenarios. The
project also aims to provide explanations of the algorithms'

6
underlying code to help users understand the logic and
mechanics behind each algorithm. Ultimately, the Algorithm
Visualizer project aims to promote algorithm literacy and
empower users to use these tools effectively in their work or
personal projects.

7
1. Introduction

Algorithm Visualizer (also referred as algorithmic animation) uses


dynamic graphics to see computation of a given algorithmic program.
First makes an attempt to robustness of algorithms date to mid-80’s
(Brown, 1988; Brown and Sedgewick, 1985), and also the golden age of
algorithmic visualization was around the year 2000, when outstanding
software tools for a dynamic algorithmic visualization (e.g., the language
Java and its graphic libraries) and sufficiently powerful hardware were
already available on the market. It had been expected that algorithmic
visualization would replace the way algorithms are taught. Many
algorithmic animations had appeared, largely for straight forward
problems like basic tree data structures and sorting. There have been even
attempts to automatize development of animated algorithmic program and
algorithmic visualization.

The goal of Algorithm Visualizer is to provide a more accessible


and intuitive way to understand complex algorithms. By providing
interactive visualizations and real-time code explanations, users can
develop a deeper understanding of how algorithms work and how
they can be applied to solve different problems. The platform also
includes customization options that allow users to adjust parameters
and inputs to enhance their understanding of the algorithms.

Algorithm Visualizer covers a wide range of algorithms and data


structures, including pathfinding algorithms, sorting algorithms, tree
algorithms, and graph algorithms, among others. The platform is
constantly being updated with new algorithms and features to ensure that
users have access to the latest tools and techniques for learning and
understanding algorithms.

8
In this report, an e-learning tool for Pathfinder, Prime Numbers, Sorting
Algorithms, N Queen, Convex Hull, Binary Search Game visualization is
described.

For example,

1. Path-finding Algorithm: - In path finding making the starting


and the end node be able to move around or the user to choose
wherever he wants it to start or end. The developed e-learning
tool permits visualizes the algorithm rule steps execution. It’s
mean to be used as a supplement to face-to-face instruction
or as a complete application.

 Dijkstra’s Algorithm

 A* Search

 Greedy Best-First Search

 Swarm Algorithm

 Depth-First Search (DFS)

 Breadth-First Search (BFS)

9
2. Sorting Algorithm: - In sorting the animation tool would
represent information as a bar and once choosing a data-
ordering and algorithms, the user will run an automatic
animation or step through it at their own pace.

 Merge Sort

 Quick Sort

 Heap Sort

 Bubble Sort

10
2. Review of Literature

The algorithm visualizer project is a software tool designed to help


developers and students understand how algorithms work by visualizing
each step of the algorithm's execution. This report summarizes the results of
the project, including the features implemented, the technologies used, and
the challenges encountered.

The most fundamental problems of the Algorithm Visualizer project is the


challenge of designing a platform that can effectively visualize a wide range
of algorithms in a user-friendly and intuitive way. Each algorithm has its
unique features and mechanics, which can be challenging to convey
visually. Additionally, algorithms can operate on a wide variety of data
structures, from simple arrays to complex graphs, which requires a flexible
and adaptable visualization platform.

There can be several major problems or challenges that we face while


working on a project on algorithm visualizer. Some of them are:

 Designing a flexible and adaptable platform: One of the biggest


challenges in algorithm visualizer projects is designing a platform that
can accommodate a wide range of algorithms and data structures. This
requires a lot of planning and foresight to create a platform that can
handle new algorithms and data structures as they are developed.

 Ensuring accuracy and correctness: Another major challenge in


algorithm visualizer projects is ensuring that the visualizations
accurately reflect the behavior of the algorithms being displayed. If
the visualizations are inaccurate, they can be misleading and
counterproductive in helping users understand the algorithms.

11
 Balancing simplicity and complexity: A key challenge in algorithm
visualizer projects is balancing simplicity and complexity. While it's
essential to provide an accessible platform that is easy to use for
beginners, it's also important to provide in-depth insights for more
experienced users. Striking the right balance can be challenging.

 User engagement and retention: Finally, a major challenge in


algorithm visualizer projects is user engagement and retention. While
the platform may be engaging initially, it's essential to provide enough
depth and variety to keep users engaged over the long term. This
requires constant updates and improvements to the platform, as well
as effective outreach and marketing strategies to attract and retain
users.

12
3. Objective of the Project
The objectives of a project on algorithm visualizer can vary depending on
the specific goals of the project, but some common objectives might include:
-

Education: - A primary objective of many algorithm visualizer


projects is to help people learn about algorithms and how they work.
By creating visual representations of algorithms, these projects can
help make complex concepts easier to understand.

Debugging: - Another objective might be to help developers debug


their algorithms. By visualizing the steps of an algorithm, it can be
easier to identify errors or inefficiencies in the code.

Optimization: - Algorithm visualizers can also be used to optimize


algorithms. By visually comparing different algorithms or different
implementations of the same algorithm, developers can identify which
ones are more efficient and make improvements accordingly.

Accessibility: - Another objective might be to make algorithms more


accessible to people who are not programmers or computer scientists.
By creating visual representations of algorithms, these projects can
help make algorithms more understandable and engaging to a wider
audience.

Research: - Finally, some algorithm visualizer projects might be


designed for research purposes. By studying how people interact with
different visualizations of algorithms, researchers can gain insights
into how people think about and understand algorithms, which can
inform future developments in computer science education and
human-computer interaction.

13
User

Binary
Sorting Search
Algorithm
Prime
Numbers

Visualization

Fig: Data Flow Diagram

14
Features:
The algorithm visualizer project includes the following features:

 Support for a variety of algorithms, including sorting algorithms, search

algorithms, and graph algorithms

 Step-by-step visualization of the algorithm's execution, including the state of

variables and data structures at each step

 Customizable visualization options, including color schemes, animation

speed, and step size

 Ability to pause, resume, and step through the algorithm's execution

 Integration with popular programming languages, including Python, Java,

and JavaScript

Challenges Encountered

The algorithm visualizer project encountered several challenges during

development, including:

 Choosing the right visualization techniques to accurately represent the

algorithms being visualized

 Optimizing performance to ensure smooth and responsive animation

 Integrating with multiple programming languages and environments

 Testing and debugging complex JavaScript code.

15
4. System Design
Hardware Requirements:
 Laptop/ Desktop
 Core i5/i7 processor
 At least 8 GB RAM
 At least 60 GB of Usable Hard Disk Space

Software Requirements:
 IDE: - Visual Studio Code
 Development Environment: - React.js
 Coding Language: - HTML, CSS, JavaScript
 Browser: - Google Chrome
 Operating System: - Windows 7 or above

Data Information:

 Project Background: Describe the purpose and motivation of the


project, including the problem or challenge that the algorithm
visualizer aims to solve.
 Objectives: List the specific goals and objectives of the project,
such as improving the understanding of algorithms or facilitating
the teaching of computer science.
 Methodology: Explain the approach taken to design and develop
the algorithm visualizer, including the choice of HTML, CSS,
JavaScript, and React.js, and any relevant design decisions.
 Features: Describe the key features of the algorithm visualizer,
such as its ability to show step-by-step execution of algorithms or to
support different programming languages.
16
 User Interface: Present the user interface of the algorithm
visualizer, including screenshots and descriptions of how the
visualizer works and how users can interact with it.

 Implementation Details: Provide technical details on how the


algorithm visualizer was implemented using HTML, CSS,
JavaScript, and React.js, including any notable algorithms or data
structures used.

 Code Samples: Include code samples to illustrate key aspects of


the implementation, such as the use of React components or the
data structures used to represent the algorithms.

 Evaluation: Assess the effectiveness of the algorithm visualizer in


achieving its objectives, including any user feedback or metrics
collected.

 Conclusion: Summarize the key findings and lessons learned from


the project, including any future improvements or extensions that
could be made.

 References: Include a list of any sources cited in the report, such


as academic papers, books, or online resources.

 Acknowledgments: Recognize any individuals or organizations


that contributed to the project, such as funding agencies, software
libraries, or project collaborators.

17
Start

Login to the Visualization Tool

Check User Level and Permissions

Choose action

Input Suitable Data Values

Control the animation parameters if necessary

Visualize

Logout from the system

End

Fig: Activity Diagram for User

18
Data Format:
The sample of the dataset is given below.

class BinarySearch {
constructor(array, searchValue) {
this.array = array;
this.left = 0;
this.right = array.length - 1;
this.mid = Math.floor((this.left + this.right) / 2);
this.searchValue = searchValue;
}

executeStep() {
if (this.left > this.right) {
return null;
}
if (this.array[this.mid] === this.searchValue) {
return this.mid;
} else if (this.array[this.mid] > this.searchValue) {
this.right = this.mid - 1;
} else {
this.left = this.mid + 1;
}
this.mid = Math.floor((this.left + this.right) / 2);
return "continue";
}
}

19
In this example, the data format includes a class called 'BinarySearch' that
represents the binary search algorithm being visualized. The class includes a
constructor that initializes the algorithm with an array to search and the value
being searched for. The class also includes a method called 'executeStep()' that
executes one step of the algorithm and updates the state of the algorithm
accordingly.

The data format in this example is represented using JavaScript code, which
can be easily executed by the algorithm visualizer to visualize the algorithm's
execution. The visualizer can use the state of the ‘BinarySearch’ object to show
the current state of the algorithm's execution, such as the current value of 'left',
'right', and 'mid'.

20
5 . Methodology for Implementation
(Formulation/Algorithm)

DATA COLLECTION:

Here are some examples of data collection methods for an algorithm visualizer
using pathfinding visualization and sorting algorithm:

 User testing: For pathfinding visualization, we can conduct user testing


to collect data on how users interact with the algorithm visualizer. For
example, we can ask participants to find the shortest path between two
points on a grid using the A* algorithm and collect data on the time taken
to find the path, the number of nodes explored, and the accuracy of the
path. Similarly, for sorting algorithms, we can ask participants to sort a
list of numbers using different algorithms and collect data on the time
taken to sort the list, the number of comparisons made, and the accuracy
of the sorting.

 Performance measurement: For pathfinding visualization, we can


measure the performance of the algorithm visualizer by collecting data
on the number of nodes explored, the time taken to find the path, and the
memory usage. For sorting algorithms, we can collect data on the time
taken to sort the list, the number of comparisons made, and the memory
usage. For example, we can use Chrome DevTools to measure the
performance of the algorithm visualizer and collect data on the CPU and
memory usage.

 Surveys: For pathfinding visualization, we can conduct surveys to


collect data on the user's perception of the algorithm visualizer. For
example, we can ask participants to rate the ease of use of the visualizer,
the accuracy of the paths found, and the overall usefulness of the
visualizer. Similarly, for sorting algorithms, we can ask participants to
rate the ease of use of the visualizer, the accuracy of the sorting, and the
overall usefulness of the visualizer.

21
 Analytics: For pathfinding visualization, we can use analytics tools such
as Google Analytics to track user engagement with the algorithm
visualizer. For example, we can collect data on the number of users who
use the visualizer, the duration of use, and the paths that are most
frequently used. Similarly, for sorting algorithms, we can collect data on
the number of users who use the visualizer, the duration of use, and the
sorting algorithms that are most frequently used.

 A/B testing: For pathfinding visualization, we can conduct A/B testing


to collect data on the effectiveness of different design elements,
algorithms, or visualizations. For example, we can test different
algorithms such as Dijkstra's algorithm or Breadth-First Search and
collect data on the time taken to find the path, the accuracy of the path,
and the number of nodes explored. Similarly, for sorting algorithms, we
can test different algorithms such as Bubble Sort or Quick Sort and collect
data on the time taken to sort the list, the number of comparisons made,
and the memory usage.

 Heatmaps: For pathfinding visualization, we can use heatmaps to


visualize user behavior and engagement with the algorithm visualizer.
For example, we can collect data on which parts of the visualizer are most
frequently clicked, hovered over, or interacted with. Similarly, for sorting
algorithms, we can collect data on which parts of the visualizer are most
frequently clicked, hovered over, or interacted with.

By using these data collection methods, we can gather valuable insights


on the effectiveness of the algorithm visualizer and make necessary
improvements to enhance user experience and engagement.

22
Path-finding Algorithm

Pathfinding algorithms are used to find the shortest path between two points
in a graph or grid. There are several pathfinding algorithms available,
including Dijkstra's algorithm, A* algorithm, and breadth-first search
algorithm.

1. Dijkstra’s Algorithm: -

Dijkstra's algorithm is a shortest path algorithm that works by iteratively


finding the next closest node to the source node and updating the distance
and path information for all of its neighbors.

we will discuss the methodology of implementing Dijkstra's algorithm with a


code example.

Methodology:

The Dijkstra's algorithm works as follows:

1. Initialize the source node with distance 0 and all other nodes with infinite
distance.
2. Add the source node to a priority queue.
3. While the priority queue is not empty, do the following:
a. Pop the node with the smallest distance from the priority queue.
b. For each neighbor of the popped node, do the following:
i. Calculate the distance from the source node to the neighbor through
the popped node.
ii. If this distance is less than the current distance for the neighbor,
update the distance and the path information for the neighbor.
iii. Add the neighbor to the priority queue.
4. Return the distance and path information for all nodes.

23
Code Example:
Here is an example implementation of Dijkstra's algorithm in Python:

import heapq

def dijkstra(graph, source):


distances = {node: float('inf') for node in graph}
distances[source] = 0
queue = [(0, source)]
path = {}

while queue:
current_distance, current_node = heapq.heappop(queue)

if current_distance > distances[current_node]:


continue

for neighbor, weight in graph[current_node].items():


distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
path[neighbor] = current_node
heapq.heappush(queue, (distance, neighbor))

return distances, path

24
In this example, the 'dijkstra' function takes two arguments: 'graph', which is
a dictionary that represents the graph, and 'source', which is the starting
node.

The function initializes the 'distances' dictionary with infinite distances for all
nodes except the source node, which has a distance of 0. It then creates a
priority queue 'queue' with the source node.

The function uses the 'heapq' module to implement the priority queue. Each
item in the queue is a tuple with the distance and the node.

The function then enters a loop where it pops the node with the smallest
distance from the queue, and updates the distance and path information for
its neighbors. It checks if the distance from the source node to the neighbor
through the popped node is less than the current distance for the neighbor. If
it is, the function updates the distance and path information for the neighbor,
and adds the neighbor to the queue.

Finally, the function returns the 'distances' dictionary and the 'path'
dictionary for all nodes. The 'path' dictionary stores the parent of each node
in the shortest path from the source node to that node.

2. A* algorithm: -
The A* (pronounced "A star") algorithm is a heuristic-based search algorithm
that finds the shortest path between a starting node and a goal node in a
weighted graph. It works by maintaining two lists of nodes: an open list of
nodes that have been visited but not yet explored, and a closed list of nodes
that have been explored. The algorithm selects the next node to visit by
25
choosing the node with the lowest total cost, which is the sum of the cost from
the starting node to the current node (the g-value) and an estimated cost from
the current node to the goal node (the h-value).

we will discuss the methodology of implementing the A* algorithm with a code


example.

Methodology:

The A* algorithm works as follows:

1. Initialize the start node as the current node.


2. Add the start node to the open set.
3. While the open set is not empty, do the following:
a. Find the node in the open set with the lowest f-score (the sum of the g-
score and the h-score).
b. If the current node is the goal node, return the path.
c. Remove the current node from the open set and add it to the closed set.
d. For each neighbor of the current node, do the following:
i. If the neighbor is already in the closed set, skip it.
ii. If the neighbor is not in the open set, add it to the open set and set its
parent to the current node.
iii. If the neighbor is already in the open set, update its g-score if a better
path has been found.

iv. Calculate the h-score for the neighbor.


iv. Calculate the f-score for the neighbor.
4. If there is no path to the goal node, stop and return "no path".

26
Code Example:

Here is an example implementation of the A* algorithm in Python:

def a_star(start, goal, graph):


open_set = {start}
closed_set = set()
g_score = {start: 0}
f_score = {start: heuristic(start, goal)}
parent = {}

while open_set:
current = min(open_set, key=lambda node: f_score[node])
if current == goal:
return construct_path(parent, current)

open_set.remove(current)
closed_set.add(current)

for neighbor in graph[current]:


if neighbor in closed_set:
continue

tentative_g_score = g_score[current] + distance(current, neighbor)


if neighbor not in open_set:
open_set.add(neighbor)
elif tentative_g_score >= g_score[neighbor]:
continue

parent[neighbor] = current
g_score[neighbor] = tentative_g_score
f_score[neighbor] = g_score[neighbor] + heuristic(neighbor, goal)

return None

def heuristic(node, goal):


return distance(node, goal)

def distance(node1, node2):


return 1 # in this example, we assume all edges have a weight of 1

27
def construct_path(parent, node):
path = [node]
while node in parent:
node = parent[node]
path.append(node)
path.reverse()
return path

In this example, the 'a_star' function takes three arguments: 'start' and
'goal', which are the start and goal nodes, and ‘graph’, which is a dictionary
that represents the graph. The keys of the dictionary are the nodes, and the
values are lists of the nodes that are adjacent to the key node.

The function uses the 'open_set' and 'closed_set' sets.

3. Breadth-first search Algorithm: -

Breadth-first search (BFS) is a graph traversal algorithm that visits all the
nodes of a graph or tree in breadth-first order, i.e., it visits all the nodes at the
same level before moving down to the next level. BFS is often used for finding
the shortest path between two nodes in an unweighted graph.

Here is the methodology of implementing the BFS algorithm with a code


example:

Methodology:

1. Initialize a queue and a visited set. Enqueue the start node to the queue
and add it to the visited set.
2. While the queue is not empty, do the following:
a. Dequeue the front node from the queue.
b. For each of the node's neighbors, if the neighbor has not been visited,
mark it as visited and enqueue it to the queue.
c. If the goal node is found, return the path.
3. If the goal node is not found, return null.

28
Code Example:

Here is an example implementation of the BFS algorithm in Python:

def bfs(start, goal, graph):


queue = [start]
visited = {start}
parent = {start: None}

while queue:
node = queue.pop(0)
if node == goal:
return construct_path(parent, goal)

for neighbor in graph[node]:


if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
parent[neighbor] = node

return None

def construct_path(parent, node):


path = [node]
while parent[node] is not None:
node = parent[node]
path.append(node)
path.reverse()
return path

29
In this example, the 'bfs' function takes three arguments: 'start' and 'goal',
which are the start and goal nodes, and 'graph', which is a dictionary that
represents the graph. The keys of the dictionary are the nodes, and the values
are lists of the nodes that are adjacent to the key node.

The function uses a 'queue' to keep track of the nodes to visit and a 'visited' set
to keep track of the visited nodes. The 'parent' dictionary is used to keep track
of the parent of each visited node in order to reconstruct the path once the
goal node is found.

The algorithm starts by enqueueing the start node to the queue and marking
it as visited. It then enters a loop where it dequeues the front node from the
queue, and for each of its neighbors, if the neighbor has not been visited, it
marks it as visited, enqueues it to the queue, and adds the parent of the
neighbor to the 'parent' dictionary. If the goal node is found, the function calls
the 'construct_path' function to reconstruct the path from the start node to
the goal node. If the goal node is not found, the function returns 'None'.

30
Sorting Algorithm

Sorting algorithms are used to rearrange a collection of elements in a specific


order, such as ascending or descending order. There are several sorting
algorithms available, including bubble sort, insertion sort, selection sort,
merge sort, quicksort, and heapsort.

1. Quick-Sort Algorithm: -

The quicksort algorithm is a divide-and-conquer sorting algorithm that


works by partitioning an array into two subarrays, one with elements
smaller than a pivot and the other with elements greater than the pivot,
and then recursively sorting the subarrays. The algorithm has an average-
case time complexity of O(n log n) and a worst-case time complexity of
O(n^2), but its worst-case performance can be avoided with careful
selection of the pivot.

We will discuss the methodology of implementing the quicksort algorithm


with a code example.

Methodology:

The quicksort algorithm works as follows:

1. If the array has fewer than two elements, return the array.

2. Select a pivot element from the array.

3. Partition the array into two subarrays, one with elements smaller than the

pivot and the other with elements greater than the pivot.

4. Recursively sort the subarrays using quicksort.

5. Concatenate the sorted subarrays and return the result.

31
Code Example:

Here is an example implementation of the quicksort algorithm in Python:

def quicksort(arr):
if len(arr) < 2:
return arr

pivot = arr[0]
less = [x for x in arr[1:] if x <= pivot]
greater = [x for x in arr[1:] if x > pivot]

return quicksort(less) + [pivot] + quicksort(greater)

In this example, the ‘quicksort’ function takes one argument, ‘arr’, which is the
array to be sorted. The function first checks if the length of the array is less
than two, and if so, returns the array. If the length of the array is two or
greater, the function selects the first element of the array as the pivot, and
partitions the rest of the array into two subarrays, one with elements smaller
than or equal to the pivot, and the other with elements greater than the pivot.

32
The function then recursively sorts the subarrays using quicksort, and
concatenates the sorted subarrays with the pivot element in between them, in
ascending order. Finally, the function returns the sorted array.

This implementation of quicksort uses list comprehensions to create the ‘less’


and ‘greater’ subarrays. The ‘less’ subarray contains all elements of the input
array that are less than or equal to the pivot, and the ‘greater’ subarray
contains all elements of the input array that are greater than the pivot. The
‘quicksort’ function is then recursively called on the ‘less’ and ‘greater’
subarrays, and the results are concatenated with the pivot element in between
them.

2. Bubble-Sort Algorithm: -
Bubble sort is a simple sorting algorithm that works by repeatedly swapping
adjacent elements if they are in the wrong order. It gets its name from the way
smaller elements "bubble" to the top of the list as the algorithm progresses.

Methodology:

The algorithm works as follows:

1. Starting from the first element, compare the current element with the
next element.
2. If the current element is greater than the next element, swap them.
3. Move to the next pair of elements and repeat step 2.
4. Continue this process until the end of the list is reached.
5. Repeat steps 1-4 for each element in the list.

Here's an example implementation of the bubble sort algorithm in Python:


33
def bubble_sort(arr):
n = len(arr)
# Traverse through all array elements
for i in range(n):
# Last i elements are already sorted
for j in range(0, n-i-1):
# Traverse the array from 0 to n-i-1
# Swap if the element found is greater
# than the next element
if arr[j] > arr[j+1] :
arr[j], arr[j+1] = arr[j+1], arr[j]

# Test the bubble sort algorithm with an example array


arr = [64, 34, 25, 12, 22, 11, 90]
bubble_sort(arr)
print("Sorted array is:", arr)

In this code example, we define a function 'bubble_sort' that takes an array


'arr' as input. We use a nested loop to compare each pair of adjacent elements
in the array, swapping them if they are out of order. We repeat this process for
each element in the array until the entire list is sorted. Finally, we test the
function with an example array and print the sorted result.

34
3. Insertion-Sort Algorithm: -
Insertion sort is a simple sorting algorithm that sorts an array or list of
elements by repeatedly iterating over the list and inserting each element
in its correct position in a sorted sub-list.

Methodology:
The algorithm works as follows:
1. Start with the first element of the list and assume it is sorted.
2. Iterate over the list, starting with the second element. For each
element, compare it with all the elements in the sorted sub-list that
come before it and insert it at the correct position.
3. Repeat step 2 until all the elements are sorted.

Python Code:

def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j=i-1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
return arr

35
In this code, 'arr' is the input list to be sorted. We start iterating from the
second element ('i=1') and assume the first element is sorted. We store the
current element in a variable called 'key' and start comparing it with the
elements in the sorted sub-list that come before it. If an element is greater
than 'key', we shift it to the right by one position and continue the
comparison until we find the correct position for 'key'. We then insert 'key'
at the correct position in the sorted sub-list.

The outer loop iterates over all the elements in the list, and the inner loop
compares and shifts the elements in the sorted sub-list. The time
complexity of the insertion sort algorithm is O(n^2) in the worst case,
where n is the number of elements in the list. However, it performs well
on small lists and is an efficient sorting algorithm for nearly sorted lists.

4. Selection-Sort Algorithm: -

The selection sort algorithm is a simple sorting algorithm that sorts an array
by repeatedly finding the minimum element from the unsorted part of the
array and placing it at the beginning.

The algorithm maintains two subarrays in a given array.


a. The subarray which is already sorted.
b. Remaining subarray which is unsorted.

In every iteration of the selection sort, the minimum element from the
unsorted subarray is picked and swapped with the leftmost element of the
unsorted subarray. The leftmost element is then considered sorted, and the
process continues until the entire array is sorted.
36
Methodology:
The step-by-step methodology of implementing the selection-sort algorithm:
1. Set the first element as the minimum.
2. Compare the minimum element with the second element. If the second
element is smaller than the minimum, set the second element as the new
minimum.
3. Repeat step 2 for the rest of the elements in the array.
4. Swap the minimum element with the first element of the array.
5. Repeat steps 1 through 4 for the remaining elements of the array.

Here's a code example in Python:

def selection_sort(arr):
n = len(arr)
for i in range(n):
# Find the minimum element in the unsorted part of the array
min_index = i
for j in range(i+1, n):
if arr[j] < arr[min_index]:
min_index = j

# Swap the found minimum element with the first element


arr[i], arr[min_index] = arr[min_index], arr[i]
return arr

37
Here, we first find the length of the array and then iterate through each
element of the array using a for loop. In each iteration, we find the minimum
element in the unsorted part of the array and swap it with the first element of
the unsorted subarray. We then continue this process until the entire array is
sorted.

5. Merge-Sort Algorithm: -
Mergesort is a popular sorting algorithm that works by recursively
dividing an array into smaller subarrays until each subarray contains only
one element. Then, it combines the subarrays by sorting and merging
them.

Methodology:
The following is the methodology of implementing the Mergesort
algorithm along with a code example in Python:
1. Divide the array into two halves
2. Sort the left half recursively using mergesort
3. Sort the right half recursively using mergesort
4. Merge the two sorted halves

Here is the Python code for implementing the Mergesort algorithm:

def mergeSort(arr):
if len(arr) > 1:
mid = len(arr) // 2 # Find the mid-point of the array

38
left_half = arr[:mid] # Divide the array into two halves
right_half = arr[mid:]

mergeSort(left_half) # Sort the left half recursively


mergeSort(right_half) # Sort the right half recursively

i=j=k=0

# Merge the two sorted halves


while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
arr[k] = left_half[i]
i += 1
else:
arr[k] = right_half[j]
j += 1
k += 1

while i < len(left_half):


arr[k] = left_half[i]
i += 1
k += 1

while j < len(right_half):


arr[k] = right_half[j]
j += 1
k += 1

# Example usage of the function


39
arr = [64, 34, 25, 12, 22, 11, 90]
mergeSort(arr)
print("Sorted array:", arr)

In this code, the 'mergeSort()' function takes an array as input and sorts it
using the Mergesort algorithm. The function first checks if the length of
the array is greater than 1. If the array contains only one element, it is
already sorted and the function returns. Otherwise, it divides the array into
two halves using the mid-point of the array. Then, it recursively calls itself
to sort the left and right halves of the array. Once both halves are sorted, it
merges them using the 'while' loop until all the elements are sorted in the
final output array. Finally, it returns the sorted array.

The output of the code example above is:

Sorted array: [11, 12, 22, 25, 34, 64, 90]

which is the sorted version of the input array '[64, 34, 25, 12, 22, 11, 90]'.

6. Heap-Sort Algorithm: -
Heapsort is a sorting algorithm that works by building a heap data
structure from the array to be sorted and then repeatedly extracting the
maximum element from the heap and placing it at the end of the sorted
array. The process is repeated until the entire array is sorted.
40
Methodology:

Here's the step-by-step methodology for implementing Heapsort:


1. Build a heap from the array to be sorted. The heap can be either a max-

heap or a min-heap, depending on whether you want to sort the array

in ascending or descending order.

In this example, we'll build a max-heap.

2. Swap the first element of the heap with the last element of the heap.

3. Discard the last element of the heap (it is now sorted) and reduce the

heap size by one.

4. Restore the heap property by calling the max-heapify function on the

root element.

5. Repeat steps 2-4 until the heap size is 1.

6. The sorted array is obtained by reversing the array obtained after

applying the heap sort algorithm.

Here's the Python code for implementing Heapsort:

def heap_sort(arr):
n = len(arr)

41
# Build a max heap
for i in range(n // 2 - 1, -1, -1):
max_heapify(arr, n, i)

# Extract elements one by one


for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i] # swap
max_heapify(arr, i, 0)

# Reverse the sorted array


arr.reverse()

def max_heapify(arr, n, i):


# Find largest among root, left child and right child
largest = i
l=2*i+1
r=2*i+2

if l < n and arr[l] > arr[largest]:


largest = l

if r < n and arr[r] > arr[largest]:


largest = r

# Swap and continue heapifying if root is not largest


if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
max_heapify(arr, n, largest)

42
Let's test the code with an example:

arr = [12, 11, 13, 5, 6, 7]


heap_sort(arr)
print("Sorted array:", arr)

Output:

Sorted array: [5, 6, 7, 11, 12, 13]

43
6. Implementation Details

An algorithm visualizer project is a web-based application that allows users to


see a step-by-step visualization of how an algorithm works.

Here are some implementation details for an algorithm visualizer project:

7. Choose the programming language and libraries: The first step is to


choose the programming language and libraries that will be used for
developing the project. Some popular choices for web-based
applications include JavaScript, HTML, CSS, and the React library.

8. Choose the algorithms to visualize: Next, choose the algorithms that


you want to visualize. You can start with simple sorting algorithms such
as Bubble sort or Insertion sort, and then move on to more complex
algorithms such as Quick sort or Merge sort.

9. Design the user interface: The user interface should be designed in a


way that is easy to use and understand. The user should be able to input
the data and select the algorithm to visualize. The visualizer should
display the steps of the algorithm and highlight the relevant data
structures.

10. Implement the algorithm: Once the user interface is designed, the
next step is to implement the algorithm. This involves writing the code
for the algorithm and incorporating it into the visualizer.

44
11. Implement the visualization: After the algorithm is implemented, the
next step is to implement the visualization. This involves using graphics
and animation to show the steps of the algorithm as it progresses.

12. Test the application: After the application is implemented, it should be


thoroughly tested to ensure that it is working correctly. This involves
testing the various features of the visualizer and ensuring that the
algorithm is being visualized accurately.

13. Deploy the application: Once the application is tested and working
correctly, it can be deployed to a web server so that users can access it
online.

Here are some additional features that you can add to the algorithm visualizer
project:

 Allow users to select the speed of the visualization.


 Allow users to pause, rewind, or fast-forward the visualization.
 Allow users to select the size of the input data.
 Allow users to choose different color schemes or themes for the
visualizer.
 Provide explanations or descriptions of the algorithm at each step.

Here's an example of a simple algorithm visualizer using JavaScript and the


D3.js library to visualize the Bubble Sort algorithm:

<!DOCTYPE html>
45
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
svg {
background-color: white;
border: 1px solid black;
}
</style>
</head>
<body>
<svg width="500" height="500"></svg>
<script>
// Bubble Sort implementation
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}

// Visualization elements
const svg = d3.select("svg");
46
const width = +svg.attr("width");
const height = +svg.attr("height");
const padding = 50;
const numRects = 10;
const rectWidth = (width - padding * 2) / numRects;

const data = d3.shuffle(d3.range(numRects));

svg
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => padding + i * rectWidth)
.attr("y", (d) => height - padding - d * rectWidth)
.attr("width", rectWidth)
.attr("height", (d) => d * rectWidth)

function bubbleSort() {
let n = data.length;
let swapped;
do {
swapped = false;
for (let i = 0; i < n - 1; i++) {
if (data[i] > data[i + 1]) {
const temp = data[i];
data[i] = data[i + 1];
data[i + 1] = temp;
47
swapped = true;
}
}
bars.data(data)
.transition()
.duration(1000)
.attr("y", (d) => svgHeight - yScale(d));
n--;
} while (swapped);
}

d3.select("#sort-button").on("click", bubbleSort);

In this example, we start by defining an array of data to be sorted and some


variables to control the size and spacing of the bars in the visualization. We
then create an SVG element using the D3.js library, and use scale functions to
map the data to the x and y coordinates of the bars.

Next, we create a selection of bars using the selectAll method, bind the data to
the bars using the data method, and create a rect element for each data point
using the enter method. We set the x and y positions and the width and height
of the bars using the scale functions.

The bubbleSort function implements the Bubble Sort algorithm using a do-
while loop. Each time two elements are swapped, we update the data array
and the position of the bars using the data and transition methods. The
duration argument specifies how long the transition should take, in
milliseconds.
48
Finally, we add a button to the HTML document with an ID of sort-button,
and use the on method to call the bubbleSort function when the button is
clicked.

This example is just a starting point - you can customize the visualization to
add more features, such as highlighting the bars being compared, or
animating the swaps in a different way.

49
7. Results and Sample Output

Algorithm visualizers are tools that can help developers and students better
understand how algorithms work by visualizing their execution step-by-step.

Here are some potential results of using an algorithm visualizer, along with
examples and sample outputs:

Results:

 Improved understanding: Algorithm visualizers can help users better


understand how an algorithm works by visualizing the execution step-
by-step. This can lead to a deeper understanding of the algorithm's
behavior and logic.

 Enhanced debugging: Algorithm visualizers can help users identify and


resolve issues by visualizing the values of variables and data structures
at each step of the algorithm's execution.

 Increased efficiency: By understanding the behavior of the algorithm,


users can optimize the algorithm for performance and reduce the
number of iterations required to achieve the desired output.

 Increased collaboration: Algorithm visualizers can facilitate


collaboration among team members by providing a shared
understanding of how an algorithm works and its execution.

50
 Enhanced learning: Algorithm visualizers are particularly useful for
educational purposes, as they can help students visualize and
understand complex algorithms and data structures.

Examples and sample outputs:

Pathfinding Algorithms

Pathfinding algorithms are used to find the shortest or most efficient path
between two points in a graph or network. There are several pathfinding
algorithms such as Dijkstra's algorithm, A* algorithm, BFS, and DFS.

Code:

<html>
<head>
<title>Pathfinding Visualizer</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link id="cssTheme" rel="stylesheet" href="public/styling/cssBasic.css"/>
<link rel="shortcut icon" type="image/png" href="public/styling/c_icon.png"/>
</head>
<body>
<div id='navbarDiv'>
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<a id="refreshButton" class="navbar-brand" href="#">Pathfinding Visualizer</a>
</div>
<ul class="nav navbar-nav">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Algorithms
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li id='startButtonDijkstra'><a href="#">Dijkstra's Algorithm</a></li>
<li id='startButtonAStar2'><a href="#">A* Search</a></li>
<li id='startButtonGreedy'><a href="#">Greedy Best-first Search</a></li>

51
<li id='startButtonAStar'><a href="#">Swarm Algorithm</a></li>
<li id='startButtonAStar3'><a href="#">Convergent Swarm Algorithm</a></li>
<li id='startButtonBidirectional'><a href="#">Bidirectional Swarm
Algorithm</a></li>
<li id='startButtonBFS'><a href="#">Breadth-first Search</a></li>
<li id='startButtonDFS'><a href="#">Depth-first Search</a></li>
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Mazes &amp;
Patterns
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li id='startButtonCreateMazeTwo'><a href="#">Recursive Division</a></li>
<li id='startButtonCreateMazeThree'><a href="#">Recursive Division
(vertical skew)</a></li>
<li id='startButtonCreateMazeFour'><a href="#">Recursive Division
(horizontal skew)</a></li>
<li id='startButtonCreateMazeOne'><a href="#">Basic Random Maze</a></li>
<li id='startButtonCreateMazeWeights'><a href="#">Basic Weight
Maze</a></li>
<li id='startStairDemonstration'><a href="#">Simple Stair Pattern</a></li>
</ul>
</li>
<li id='startButtonAddObject'><a href="#">Add Bomb</a></li>
<li id='startButtonStart'><button id="actualStartButton" class="btn btn-default
navbar-btn" type="button">Visualize!</button></li>
<li id='startButtonClearBoard'><a href="#">Clear Board</a></li>
<li id='startButtonClearWalls'><a href="#">Clear Walls &amp; Weights</a></li>
<li id='startButtonClearPath'><a href="#">Clear Path</a></li>
<li class="dropdown">
<a id="adjustSpeed" class="dropdown-toggle" data-toggle="dropdown"
href="#">Speed: Fast
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li id='adjustFast'><a href="#">Fast</a></li>
<li id='adjustAverage'><a href="#">Average</a></li>
<li id='adjustSlow'><a href="#">Slow</a></li>
</ul>
</li>
</ul>
</div>
</nav>
</div>
<div id="tutorial">
<h3>Welcome to Pathfinding Visualizer!</h3>

52
<h6>This short tutorial will walk you through all of the features of this
application.</h6>
<p>If you want to dive right in, feel free to press the "Skip Tutorial" button
below. Otherwise, press "Next"!</p>
<div id="tutorialCounter">1/9</div>
<img id="mainTutorialImage" src="public/styling/c_icon.png">
<button id="nextButton" class="btn btn-default navbar-btn"
type="button">Next</button>
<button id="previousButton" class="btn btn-default navbar-btn"
type="button">Previous</button>
<button id="skipButton" class="btn btn-default navbar-btn" type="button">Skip
Tutorial</button>
</div>
<div id='mainGrid'>
<div id='mainText'>
<ul>
<li>
<div class="start"></div>Start Node</li>
<li>
<div class="target"></div>Target Node</li>
<li id="bombLegend">
<div class="object"></div>Bomb Node</li>
<li id="weightLegend">
<div class="borderlessWeight"></div>Weight Node</li>
<li>
<div class="unvisited"></div>Unvisited Node</li>
<li>
<div class="visited"></div><div class="visitedobject"></div>Visited Nodes</li>
<li>
<div class="shortest-path"></div>Shortest-path Node</li>
<li>
<div class="wall"></div>Wall Node</li>
</ul>
</div>
<div id="algorithmDescriptor">Pick an algorithm and visualize it!</div>
<table id='board'/>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src='public/browser/bundle.js'></script>
</html>

53
Output:

Here are some examples of the results and sample output of pathfinding
algorithms:

1. Dijkstra's algorithm:
Given a weighted graph with vertices A, B, C, D, E, and F, and edges with
weights as follows:
AB = 6
AD = 1
AE = 4
BC = 3
BD = 2
CD = 1
CE = 8
DE = 2
DF = 7
EF = 5
54
To find the shortest path from A to F, Dijkstra's algorithm would give the
following output:

Vertex | Distance from A | Previous vertex


A | 0 | null
B|6|A
C|7|D
D|1|A
E|3|D
F|8|E

The shortest path from A to F would be A -> D -> E -> F with a distance of 8.

Code:

function test(nodes, start, target, nodesToAnimate, boardArray, name, heuristic) {


if (!start || !target || start === target) {
return false;
}
nodes[start].distance = 0;
nodes[start].direction = "up";
let unvisitedNodes = Object.keys(nodes);
while (unvisitedNodes.length) {
let currentNode = closestNode(nodes, unvisitedNodes);
while (currentNode.status === "wall" && unvisitedNodes.length) {
currentNode = closestNode(nodes, unvisitedNodes)
}
if (currentNode.distance === Infinity) return false;
currentNode.status = "visited";
if (currentNode.id === target) {
while (currentNode.id !== start) {
nodesToAnimate.unshift(currentNode);
currentNode = nodes[currentNode.previousNode];
}

55
return "success!";
}
if (name === "astar" || name === "greedy") {
updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic);
} else if (name === "dijkstra") {
updateNeighbors(nodes, currentNode, boardArray);
}
}
}

function closestNode(nodes, unvisitedNodes) {


let currentClosest, index;
for (let i = 0; i < unvisitedNodes.length; i++) {
if (!currentClosest || currentClosest.distance > nodes[unvisitedNodes[i]].distance) {
currentClosest = nodes[unvisitedNodes[i]];
index = i;
}
}
unvisitedNodes.splice(index, 1);
return currentClosest;
}

function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) {


let neighbors = getNeighbors(node.id, nodes, boardArray);
for (let neighbor of neighbors) {
if (target) {
updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic,
boardArray);
} else {
updateNode(node, nodes[neighbor]);
}
}
}

function averageNumberOfNodesBetween(currentNode) {
let num = 0;
while (currentNode.previousNode) {
num++;
currentNode = currentNode.previousNode;
}
return num;
}

function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode,


heuristic, boardArray) {
let distance = getDistance(currentNode, targetNode);
let distanceToCompare;

56
if (actualTargetNode && name === "astar") {
if (heuristic === "manhattanDistance") {
distanceToCompare = currentNode.distance + targetNode.weight + distance[0] +
manhattanDistance(targetNode, actualTargetNode);
} else if (heuristic === "poweredManhattanDistance") {
distanceToCompare = currentNode.distance + targetNode.weight + distance[0] +
Math.pow(manhattanDistance(targetNode, actualTargetNode), 3);
} else if (heuristic === "extraPoweredManhattanDistance") {
distanceToCompare = currentNode.distance + targetNode.weight + distance[0] +
Math.pow(manhattanDistance(targetNode, actualTargetNode), 5);
}
let startNodeManhattanDistance = manhattanDistance(actualStartNode, actualTargetNode);
} else if (actualTargetNode && name === "greedy") {
distanceToCompare = targetNode.weight + distance[0] + manhattanDistance(targetNode,
actualTargetNode);
} else {
distanceToCompare = currentNode.distance + targetNode.weight + distance[0];
}
if (distanceToCompare < targetNode.distance) {
targetNode.distance = distanceToCompare;
targetNode.previousNode = currentNode.id;
targetNode.path = distance[1];
targetNode.direction = distance[2];
}
}

function getNeighbors(id, nodes, boardArray) {


let coordinates = id.split("-");
let x = parseInt(coordinates[0]);
let y = parseInt(coordinates[1]);
let neighbors = [];
let potentialNeighbor;
if (boardArray[x - 1] && boardArray[x - 1][y]) {
potentialNeighbor = `${(x - 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x + 1] && boardArray[x + 1][y]) {
potentialNeighbor = `${(x + 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y - 1]) {
potentialNeighbor = `${x.toString()}-${(y - 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y + 1]) {
potentialNeighbor = `${x.toString()}-${(y + 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}

57
return neighbors;
}

function getDistance(nodeOne, nodeTwo) {


let currentCoordinates = nodeOne.id.split("-");
let targetCoordinates = nodeTwo.id.split("-");
let x1 = parseInt(currentCoordinates[0]);
let y1 = parseInt(currentCoordinates[1]);
let x2 = parseInt(targetCoordinates[0]);
let y2 = parseInt(targetCoordinates[1]);
if (x2 < x1) {
if (nodeOne.direction === "up") {
return [1, ["f"], "up"];
} else if (nodeOne.direction === "right") {
return [2, ["l", "f"], "up"];
} else if (nodeOne.direction === "left") {
return [2, ["r", "f"], "up"];
} else if (nodeOne.direction === "down") {
return [3, ["r", "r", "f"], "up"];
}
} else if (x2 > x1) {
if (nodeOne.direction === "up") {
return [3, ["r", "r", "f"], "down"];
} else if (nodeOne.direction === "right") {
return [2, ["r", "f"], "down"];
} else if (nodeOne.direction === "left") {
return [2, ["l", "f"], "down"];
} else if (nodeOne.direction === "down") {
return [1, ["f"], "down"];
}
}
if (y2 < y1) {
if (nodeOne.direction === "up") {
return [2, ["l", "f"], "left"];
} else if (nodeOne.direction === "right") {
return [3, ["l", "l", "f"], "left"];
} else if (nodeOne.direction === "left") {
return [1, ["f"], "left"];
} else if (nodeOne.direction === "down") {
return [2, ["r", "f"], "left"];
}
} else if (y2 > y1) {
if (nodeOne.direction === "up") {
return [2, ["r", "f"], "right"];
} else if (nodeOne.direction === "right") {
return [1, ["f"], "right"];
} else if (nodeOne.direction === "left") {

58
return [3, ["r", "r", "f"], "right"];
} else if (nodeOne.direction === "down") {
return [2, ["l", "f"], "right"];
}
}
}

function manhattanDistance(nodeOne, nodeTwo) {


let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]);
let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]);
return (xChange + yChange);
}

module.exports = test;

Output:

59
2. A* algorithm:
Given a grid with obstacles, start point (S), and target point (T):

S.....
.#.#..
...#..

.#...
....#T

Using the A* algorithm to find the shortest path from S to T would give the
following output:

Path found!
S -> (0, 1) -> (1, 2) -> (2, 3) -> (2, 2) -> (2, 1) -> (3, 0) -> (4, 1) -> (4, 2) -> (4,
3) -> (3, 4) -> T

The path would look like this:


S.....

.#..
..
*#**.
....#T

Where * denotes the path from S to T.

60
Code:

function astar(nodes, start, target, nodesToAnimate, boardArray, name, heuristic) {


if (!start || !target || start === target) {
return false;
}
nodes[start].distance = 0;
nodes[start].totalDistance = 0;
nodes[start].direction = "up";
let unvisitedNodes = Object.keys(nodes);
while (unvisitedNodes.length) {
let currentNode = closestNode(nodes, unvisitedNodes);
while (currentNode.status === "wall" && unvisitedNodes.length) {
currentNode = closestNode(nodes, unvisitedNodes)
}
if (currentNode.distance === Infinity) return false;
nodesToAnimate.push(currentNode);
currentNode.status = "visited";
if (currentNode.id === target) {
return "success!";
}
updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic);
}
}

function closestNode(nodes, unvisitedNodes) {


let currentClosest, index;
for (let i = 0; i < unvisitedNodes.length; i++) {
if (!currentClosest || currentClosest.totalDistance >
nodes[unvisitedNodes[i]].totalDistance) {
currentClosest = nodes[unvisitedNodes[i]];
index = i;
} else if (currentClosest.totalDistance === nodes[unvisitedNodes[i]].totalDistance) {
if (currentClosest.heuristicDistance > nodes[unvisitedNodes[i]].heuristicDistance) {
currentClosest = nodes[unvisitedNodes[i]];
index = i;
}
}
}
unvisitedNodes.splice(index, 1);
return currentClosest;
}

function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) {


let neighbors = getNeighbors(node.id, nodes, boardArray);
for (let neighbor of neighbors) {
if (target) {
61
updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic,
boardArray);
} else {
updateNode(node, nodes[neighbor]);
}
}
}

function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode,


heuristic, boardArray) {
let distance = getDistance(currentNode, targetNode);
if (!targetNode.heuristicDistance) targetNode.heuristicDistance =
manhattanDistance(targetNode, actualTargetNode);
let distanceToCompare = currentNode.distance + targetNode.weight + distance[0];
if (distanceToCompare < targetNode.distance) {
targetNode.distance = distanceToCompare;
targetNode.totalDistance = targetNode.distance + targetNode.heuristicDistance;
targetNode.previousNode = currentNode.id;
targetNode.path = distance[1];
targetNode.direction = distance[2];
}
}

function getNeighbors(id, nodes, boardArray) {


let coordinates = id.split("-");
let x = parseInt(coordinates[0]);
let y = parseInt(coordinates[1]);
let neighbors = [];
let potentialNeighbor;
if (boardArray[x - 1] && boardArray[x - 1][y]) {
potentialNeighbor = `${(x - 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x + 1] && boardArray[x + 1][y]) {
potentialNeighbor = `${(x + 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y - 1]) {
potentialNeighbor = `${x.toString()}-${(y - 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y + 1]) {
potentialNeighbor = `${x.toString()}-${(y + 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
// if (boardArray[x - 1] && boardArray[x - 1][y - 1]) {
// potentialNeighbor = `${(x - 1).toString()}-${(y - 1).toString()}`
// let potentialWallOne = `${(x - 1).toString()}-${y.toString()}`

62
// let potentialWallTwo = `${x.toString()}-${(y - 1).toString()}`
// if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status ===
"wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor);
// }
// if (boardArray[x + 1] && boardArray[x + 1][y - 1]) {
// potentialNeighbor = `${(x + 1).toString()}-${(y - 1).toString()}`
// let potentialWallOne = `${(x + 1).toString()}-${y.toString()}`
// let potentialWallTwo = `${x.toString()}-${(y - 1).toString()}`
// if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status ===
"wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor);
// }
// if (boardArray[x - 1] && boardArray[x - 1][y + 1]) {
// potentialNeighbor = `${(x - 1).toString()}-${(y + 1).toString()}`
// let potentialWallOne = `${(x - 1).toString()}-${y.toString()}`
// let potentialWallTwo = `${x.toString()}-${(y + 1).toString()}`
// if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status ===
"wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor);
// }
// if (boardArray[x + 1] && boardArray[x + 1][y + 1]) {
// potentialNeighbor = `${(x + 1).toString()}-${(y + 1).toString()}`
// let potentialWallOne = `${(x + 1).toString()}-${y.toString()}`
// let potentialWallTwo = `${x.toString()}-${(y + 1).toString()}`
// if (nodes[potentialNeighbor].status !== "wall" && !(nodes[potentialWallOne].status ===
"wall" && nodes[potentialWallTwo].status === "wall")) neighbors.push(potentialNeighbor);
// }
return neighbors;
}

function getDistance(nodeOne, nodeTwo) {


let currentCoordinates = nodeOne.id.split("-");
let targetCoordinates = nodeTwo.id.split("-");
let x1 = parseInt(currentCoordinates[0]);
let y1 = parseInt(currentCoordinates[1]);
let x2 = parseInt(targetCoordinates[0]);
let y2 = parseInt(targetCoordinates[1]);
if (x2 < x1 && y1 === y2) {
if (nodeOne.direction === "up") {
return [1, ["f"], "up"];
} else if (nodeOne.direction === "right") {
return [2, ["l", "f"], "up"];
} else if (nodeOne.direction === "left") {
return [2, ["r", "f"], "up"];
} else if (nodeOne.direction === "down") {
return [3, ["r", "r", "f"], "up"];
} else if (nodeOne.direction === "up-right") {
return [1.5, null, "up"];
} else if (nodeOne.direction === "down-right") {

63
return [2.5, null, "up"];
} else if (nodeOne.direction === "up-left") {
return [1.5, null, "up"];
} else if (nodeOne.direction === "down-left") {
return [2.5, null, "up"];
}
} else if (x2 > x1 && y1 === y2) {
if (nodeOne.direction === "up") {
return [3, ["r", "r", "f"], "down"];
} else if (nodeOne.direction === "right") {
return [2, ["r", "f"], "down"];
} else if (nodeOne.direction === "left") {
return [2, ["l", "f"], "down"];
} else if (nodeOne.direction === "down") {
return [1, ["f"], "down"];
} else if (nodeOne.direction === "up-right") {
return [2.5, null, "down"];
} else if (nodeOne.direction === "down-right") {
return [1.5, null, "down"];
} else if (nodeOne.direction === "up-left") {
return [2.5, null, "down"];
} else if (nodeOne.direction === "down-left") {
return [1.5, null, "down"];
}
}
if (y2 < y1 && x1 === x2) {
if (nodeOne.direction === "up") {
return [2, ["l", "f"], "left"];
} else if (nodeOne.direction === "right") {
return [3, ["l", "l", "f"], "left"];
} else if (nodeOne.direction === "left") {
return [1, ["f"], "left"];
} else if (nodeOne.direction === "down") {
return [2, ["r", "f"], "left"];
} else if (nodeOne.direction === "up-right") {
return [2.5, null, "left"];
} else if (nodeOne.direction === "down-right") {
return [2.5, null, "left"];
} else if (nodeOne.direction === "up-left") {
return [1.5, null, "left"];
} else if (nodeOne.direction === "down-left") {
return [1.5, null, "left"];
}
} else if (y2 > y1 && x1 === x2) {
if (nodeOne.direction === "up") {
return [2, ["r", "f"], "right"];
} else if (nodeOne.direction === "right") {
return [1, ["f"], "right"];

64
} else if (nodeOne.direction === "left") {
return [3, ["r", "r", "f"], "right"];
} else if (nodeOne.direction === "down") {
return [2, ["l", "f"], "right"];
} else if (nodeOne.direction === "up-right") {
return [1.5, null, "right"];
} else if (nodeOne.direction === "down-right") {
return [1.5, null, "right"];
} else if (nodeOne.direction === "up-left") {
return [2.5, null, "right"];
} else if (nodeOne.direction === "down-left") {
return [2.5, null, "right"];
}
} /*else if (x2 < x1 && y2 < y1) {
if (nodeOne.direction === "up") {
return [1.5, ["f"], "up-left"];
} else if (nodeOne.direction === "right") {
return [2.5, ["l", "f"], "up-left"];
} else if (nodeOne.direction === "left") {
return [1.5, ["r", "f"], "up-left"];
} else if (nodeOne.direction === "down") {
return [2.5, ["r", "r", "f"], "up-left"];
} else if (nodeOne.direction === "up-right") {
return [2, null, "up-left"];
} else if (nodeOne.direction === "down-right") {
return [3, null, "up-left"];
} else if (nodeOne.direction === "up-left") {
return [1, null, "up-left"];
} else if (nodeOne.direction === "down-left") {
return [2, null, "up-left"];
}
} else if (x2 < x1 && y2 > y1) {
if (nodeOne.direction === "up") {
return [1.5, ["f"], "up-right"];
} else if (nodeOne.direction === "right") {
return [1.5, ["l", "f"], "up-right"];
} else if (nodeOne.direction === "left") {
return [2.5, ["r", "f"], "up-right"];
} else if (nodeOne.direction === "down") {
return [2.5, ["r", "r", "f"], "up-right"];
} else if (nodeOne.direction === "up-right") {
return [1, null, "up-right"];
} else if (nodeOne.direction === "down-right") {
return [2, null, "up-right"];
} else if (nodeOne.direction === "up-left") {
return [2, null, "up-right"];
} else if (nodeOne.direction === "down-left") {
return [3, null, "up-right"];

65
}
} else if (x2 > x1 && y2 > y1) {
if (nodeOne.direction === "up") {
return [2.5, ["f"], "down-right"];
} else if (nodeOne.direction === "right") {
return [1.5, ["l", "f"], "down-right"];
} else if (nodeOne.direction === "left") {
return [2.5, ["r", "f"], "down-right"];
} else if (nodeOne.direction === "down") {
return [1.5, ["r", "r", "f"], "down-right"];
} else if (nodeOne.direction === "up-right") {
return [2, null, "down-right"];
} else if (nodeOne.direction === "down-right") {
return [1, null, "down-right"];
} else if (nodeOne.direction === "up-left") {
return [3, null, "down-right"];
} else if (nodeOne.direction === "down-left") {
return [2, null, "down-right"];
}
} else if (x2 > x1 && y2 < y1) {
if (nodeOne.direction === "up") {
return [2.5, ["f"], "down-left"];
} else if (nodeOne.direction === "right") {
return [2.5, ["l", "f"], "down-left"];
} else if (nodeOne.direction === "left") {
return [1.5, ["r", "f"], "down-left"];
} else if (nodeOne.direction === "down") {
return [1.5, ["r", "r", "f"], "down-left"];
} else if (nodeOne.direction === "up-right") {
return [3, null, "down-left"];
} else if (nodeOne.direction === "down-right") {
return [2, null, "down-left"];
} else if (nodeOne.direction === "up-left") {
return [2, null, "down-left"];
} else if (nodeOne.direction === "down-left") {
return [1, null, "down-left"];
}
}*/
}

function manhattanDistance(nodeOne, nodeTwo) {


let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
let xOne = nodeOneCoordinates[0];
let xTwo = nodeTwoCoordinates[0];
let yOne = nodeOneCoordinates[1];
let yTwo = nodeTwoCoordinates[1];

66
let xChange = Math.abs(xOne - xTwo);
let yChange = Math.abs(yOne - yTwo);

return (xChange + yChange);


}

module.exports = astar;

Output:

3. Breadth-First-Search (BFS) Algorithm:


Given a graph with vertices A, B, C, D, E, and F, and edges as follows:
AB
AC
BD
CE
CF

67
Using BFS algorithm to find the shortest path from A to F would give the
following output:

Path found!
A -> C -> F

The path would look like this:


A --- C --- F
\/
B --- D ---
|
E

Where the shortest path from A to F is A -> C -> F.

Code:

const astar = require("./astar");

function weightedSearchAlgorithm(nodes, start, target, nodesToAnimate, boardArray, name,


heuristic) {
if (name === "astar") return astar(nodes, start, target, nodesToAnimate, boardArray, name)
if (!start || !target || start === target) {
return false;
}
nodes[start].distance = 0;
nodes[start].direction = "right";
let unvisitedNodes = Object.keys(nodes);
while (unvisitedNodes.length) {
let currentNode = closestNode(nodes, unvisitedNodes);
while (currentNode.status === "wall" && unvisitedNodes.length) {
currentNode = closestNode(nodes, unvisitedNodes)
}
if (currentNode.distance === Infinity) {
return false;
68
}
nodesToAnimate.push(currentNode);
currentNode.status = "visited";
if (currentNode.id === target) return "success!";
if (name === "CLA" || name === "greedy") {
updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic);
} else if (name === "dijkstra") {
updateNeighbors(nodes, currentNode, boardArray);
}
}
}

function closestNode(nodes, unvisitedNodes) {


let currentClosest, index;
for (let i = 0; i < unvisitedNodes.length; i++) {
if (!currentClosest || currentClosest.distance > nodes[unvisitedNodes[i]].distance) {
currentClosest = nodes[unvisitedNodes[i]];
index = i;
}
}
unvisitedNodes.splice(index, 1);
return currentClosest;
}

function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) {


let neighbors = getNeighbors(node.id, nodes, boardArray);
for (let neighbor of neighbors) {
if (target) {
updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic,
boardArray);
} else {
updateNode(node, nodes[neighbor]);
}
}
}

function averageNumberOfNodesBetween(currentNode) {
let num = 0;
while (currentNode.previousNode) {
num++;
currentNode = currentNode.previousNode;
}
return num;
}

function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode,


heuristic, boardArray) {

69
let distance = getDistance(currentNode, targetNode);
let distanceToCompare;
if (actualTargetNode && name === "CLA") {
let weight = targetNode.weight === 15 ? 15 : 1;
if (heuristic === "manhattanDistance") {
distanceToCompare = currentNode.distance + (distance[0] + weight) *
manhattanDistance(targetNode, actualTargetNode);
} else if (heuristic === "poweredManhattanDistance") {
distanceToCompare = currentNode.distance + targetNode.weight + distance[0] +
Math.pow(manhattanDistance(targetNode, actualTargetNode), 2);
} else if (heuristic === "extraPoweredManhattanDistance") {
distanceToCompare = currentNode.distance + (distance[0] + weight) *
Math.pow(manhattanDistance(targetNode, actualTargetNode), 7);
}
let startNodeManhattanDistance = manhattanDistance(actualStartNode, actualTargetNode);
} else if (actualTargetNode && name === "greedy") {
distanceToCompare = targetNode.weight + distance[0] + manhattanDistance(targetNode,
actualTargetNode);
} else {
distanceToCompare = currentNode.distance + targetNode.weight + distance[0];
}
if (distanceToCompare < targetNode.distance) {
targetNode.distance = distanceToCompare;
targetNode.previousNode = currentNode.id;
targetNode.path = distance[1];
targetNode.direction = distance[2];
}
}

function getNeighbors(id, nodes, boardArray) {


let coordinates = id.split("-");
let x = parseInt(coordinates[0]);
let y = parseInt(coordinates[1]);
let neighbors = [];
let potentialNeighbor;
if (boardArray[x - 1] && boardArray[x - 1][y]) {
potentialNeighbor = `${(x - 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x + 1] && boardArray[x + 1][y]) {
potentialNeighbor = `${(x + 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y - 1]) {
potentialNeighbor = `${x.toString()}-${(y - 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y + 1]) {

70
potentialNeighbor = `${x.toString()}-${(y + 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
return neighbors;
}

function getDistance(nodeOne, nodeTwo) {


let currentCoordinates = nodeOne.id.split("-");
let targetCoordinates = nodeTwo.id.split("-");
let x1 = parseInt(currentCoordinates[0]);
let y1 = parseInt(currentCoordinates[1]);
let x2 = parseInt(targetCoordinates[0]);
let y2 = parseInt(targetCoordinates[1]);
if (x2 < x1) {
if (nodeOne.direction === "up") {
return [1, ["f"], "up"];
} else if (nodeOne.direction === "right") {
return [2, ["l", "f"], "up"];
} else if (nodeOne.direction === "left") {
return [2, ["r", "f"], "up"];
} else if (nodeOne.direction === "down") {
return [3, ["r", "r", "f"], "up"];
}
} else if (x2 > x1) {
if (nodeOne.direction === "up") {
return [3, ["r", "r", "f"], "down"];
} else if (nodeOne.direction === "right") {
return [2, ["r", "f"], "down"];
} else if (nodeOne.direction === "left") {
return [2, ["l", "f"], "down"];
} else if (nodeOne.direction === "down") {
return [1, ["f"], "down"];
}
}
if (y2 < y1) {
if (nodeOne.direction === "up") {
return [2, ["l", "f"], "left"];
} else if (nodeOne.direction === "right") {
return [3, ["l", "l", "f"], "left"];
} else if (nodeOne.direction === "left") {
return [1, ["f"], "left"];
} else if (nodeOne.direction === "down") {
return [2, ["r", "f"], "left"];
}
} else if (y2 > y1) {
if (nodeOne.direction === "up") {
return [2, ["r", "f"], "right"];

71
} else if (nodeOne.direction === "right") {
return [1, ["f"], "right"];
} else if (nodeOne.direction === "left") {
return [3, ["r", "r", "f"], "right"];
} else if (nodeOne.direction === "down") {
return [2, ["l", "f"], "right"];
}
}
}

function manhattanDistance(nodeOne, nodeTwo) {


let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]);
let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]);
return (xChange + yChange);
}

function weightedManhattanDistance(nodeOne, nodeTwo, nodes) {


let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]);
let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]);

if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] <


nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;

72
}
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
} else if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] >=
nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--)
{
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--)
{
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

73
if (additionalxChange + additionalyChange < otherAdditionalxChange +
otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
} else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] <
nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--)
{
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--)
{
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}

74
} else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] >=
nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--
) {
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--
) {
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--
) {
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--
) {
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
}

return xChange + yChange;

module.exports = weightedSearchAlgorithm;

75
Output:

4. Depth-First-Search (DFS) algorithm:


Given a graph with vertices A, B, C, D, E, and F, and edges as follows:
AB
AC
BD
CE
CF

Using DFS algorithm to find the path from A to F would give the following
output:

Path found!
A -> B -> D -> C -> F

76
The path would look like this:
A --- C --- F
\/
B --- D ---
|
E

Where a possible path from A to F is A -> B -> D -> C -> F.

Code:
function unweightedSearchAlgorithm(nodes, start, target, nodesToAnimate, boardArray, name) {
if (!start || !target || start === target) {
return false;
}
let structure = [nodes[start]];
let exploredNodes = {start: true};
while (structure.length) {
let currentNode = name === "bfs" ? structure.shift() : structure.pop();
nodesToAnimate.push(currentNode);
if (name === "dfs") exploredNodes[currentNode.id] = true;
currentNode.status = "visited";
if (currentNode.id === target) {
return "success";
}
let currentNeighbors = getNeighbors(currentNode.id, nodes, boardArray, name);
currentNeighbors.forEach(neighbor => {
if (!exploredNodes[neighbor]) {
if (name === "bfs") exploredNodes[neighbor] = true;
nodes[neighbor].previousNode = currentNode.id;
structure.push(nodes[neighbor]);
}
});
}
return false;
}

function getNeighbors(id, nodes, boardArray, name) {


let coordinates = id.split("-");
let x = parseInt(coordinates[0]);
let y = parseInt(coordinates[1]);
77
let neighbors = [];
let potentialNeighbor;
if (boardArray[x - 1] && boardArray[x - 1][y]) {
potentialNeighbor = `${(x - 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") {
if (name === "bfs") {
neighbors.push(potentialNeighbor);
} else {
neighbors.unshift(potentialNeighbor);
}
}
}
if (boardArray[x][y + 1]) {
potentialNeighbor = `${x.toString()}-${(y + 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") {
if (name === "bfs") {
neighbors.push(potentialNeighbor);
} else {
neighbors.unshift(potentialNeighbor);
}
}
}
if (boardArray[x + 1] && boardArray[x + 1][y]) {
potentialNeighbor = `${(x + 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") {
if (name === "bfs") {
neighbors.push(potentialNeighbor);
} else {
neighbors.unshift(potentialNeighbor);
}
}
}
if (boardArray[x][y - 1]) {
potentialNeighbor = `${x.toString()}-${(y - 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") {
if (name === "bfs") {
neighbors.push(potentialNeighbor);
} else {
neighbors.unshift(potentialNeighbor);
}
}
}
return neighbors;
}

module.exports = unweightedSearchAlgorithm;

78
Output:

5. Bidirectional search algorithm:


Given a graph with vertices A, B, C, D, E, and F, and edges as follows:
AB
AC
BD
CE
CF

Using bidirectional search algorithm to find the shortest path from A to F


would give the following output:

Path found!
A -> C -> F

79
The path would look like this:
A --- C --- F
\/
B --- D ---
|
E

Where the shortest path from A to F is A -> C -> F.

In summary, pathfinding algorithms are used to find the shortest or most


efficient path between two points in a graph or network. Each algorithm has
its own strengths and weaknesses, and the choice of algorithm depends on the
specific problem being solved.

Code:
const astar = require("./astar");

function bidirectional(nodes, start, target, nodesToAnimate, boardArray, name, heuristic,


board) {
if (name === "astar") return astar(nodes, start, target, nodesToAnimate, boardArray, name)
if (!start || !target || start === target) {
return false;
}
nodes[start].distance = 0;
nodes[start].direction = "right";
nodes[target].otherdistance = 0;
nodes[target].otherdirection = "left";
let visitedNodes = {};
let unvisitedNodesOne = Object.keys(nodes);
let unvisitedNodesTwo = Object.keys(nodes);
while (unvisitedNodesOne.length && unvisitedNodesTwo.length) {
let currentNode = closestNode(nodes, unvisitedNodesOne);
let secondCurrentNode = closestNodeTwo(nodes, unvisitedNodesTwo);
while ((currentNode.status === "wall" || secondCurrentNode.status === "wall") &&
unvisitedNodesOne.length && unvisitedNodesTwo.length) {
if (currentNode.status === "wall") currentNode = closestNode(nodes, unvisitedNodesOne);
if (secondCurrentNode.status === "wall") secondCurrentNode = closestNodeTwo(nodes,
unvisitedNodesTwo);
80
}
if (currentNode.distance === Infinity || secondCurrentNode.otherdistance === Infinity) {
return false;
}
nodesToAnimate.push(currentNode);
nodesToAnimate.push(secondCurrentNode);
currentNode.status = "visited";
secondCurrentNode.status = "visited";
if (visitedNodes[currentNode.id]) {
board.middleNode = currentNode.id;
return "success";
} else if (visitedNodes[secondCurrentNode.id]) {
board.middleNode = secondCurrentNode.id;
return "success";
} else if (currentNode === secondCurrentNode) {
board.middleNode = secondCurrentNode.id;
return "success";
}
visitedNodes[currentNode.id] = true;
visitedNodes[secondCurrentNode.id] = true;
updateNeighbors(nodes, currentNode, boardArray, target, name, start, heuristic);
updateNeighborsTwo(nodes, secondCurrentNode, boardArray, start, name, target, heuristic);
}
}

function closestNode(nodes, unvisitedNodes) {


let currentClosest, index;
for (let i = 0; i < unvisitedNodes.length; i++) {
if (!currentClosest || currentClosest.distance > nodes[unvisitedNodes[i]].distance) {
currentClosest = nodes[unvisitedNodes[i]];
index = i;
}
}
unvisitedNodes.splice(index, 1);
return currentClosest;
}

function closestNodeTwo(nodes, unvisitedNodes) {


let currentClosest, index;
for (let i = 0; i < unvisitedNodes.length; i++) {
if (!currentClosest || currentClosest.otherdistance >
nodes[unvisitedNodes[i]].otherdistance) {
currentClosest = nodes[unvisitedNodes[i]];
index = i;
}
}
unvisitedNodes.splice(index, 1);
return currentClosest;

81
}

function updateNeighbors(nodes, node, boardArray, target, name, start, heuristic) {


let neighbors = getNeighbors(node.id, nodes, boardArray);
for (let neighbor of neighbors) {
updateNode(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic,
boardArray);
}
}

function updateNeighborsTwo(nodes, node, boardArray, target, name, start, heuristic) {


let neighbors = getNeighbors(node.id, nodes, boardArray);
for (let neighbor of neighbors) {
updateNodeTwo(node, nodes[neighbor], nodes[target], name, nodes, nodes[start], heuristic,
boardArray);
}
}

function updateNode(currentNode, targetNode, actualTargetNode, name, nodes, actualStartNode,


heuristic, boardArray) {
let distance = getDistance(currentNode, targetNode);
let weight = targetNode.weight === 15 ? 15 : 1;
let distanceToCompare = currentNode.distance + (weight + distance[0]) *
manhattanDistance(targetNode, actualTargetNode);
if (distanceToCompare < targetNode.distance) {
targetNode.distance = distanceToCompare;
targetNode.previousNode = currentNode.id;
targetNode.path = distance[1];
targetNode.direction = distance[2];
}
}

function updateNodeTwo(currentNode, targetNode, actualTargetNode, name, nodes,


actualStartNode, heuristic, boardArray) {
let distance = getDistanceTwo(currentNode, targetNode);
let weight = targetNode.weight === 15 ? 15 : 1;
let distanceToCompare = currentNode.otherdistance + (weight + distance[0]) *
manhattanDistance(targetNode, actualTargetNode);
if (distanceToCompare < targetNode.otherdistance) {
targetNode.otherdistance = distanceToCompare;
targetNode.otherpreviousNode = currentNode.id;
targetNode.path = distance[1];
targetNode.otherdirection = distance[2];
}
}

function getNeighbors(id, nodes, boardArray) {


let coordinates = id.split("-");

82
let x = parseInt(coordinates[0]);
let y = parseInt(coordinates[1]);
let neighbors = [];
let potentialNeighbor;
if (boardArray[x - 1] && boardArray[x - 1][y]) {
potentialNeighbor = `${(x - 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x + 1] && boardArray[x + 1][y]) {
potentialNeighbor = `${(x + 1).toString()}-${y.toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y - 1]) {
potentialNeighbor = `${x.toString()}-${(y - 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
if (boardArray[x][y + 1]) {
potentialNeighbor = `${x.toString()}-${(y + 1).toString()}`
if (nodes[potentialNeighbor].status !== "wall") neighbors.push(potentialNeighbor);
}
return neighbors;
}

function getDistance(nodeOne, nodeTwo) {


let currentCoordinates = nodeOne.id.split("-");
let targetCoordinates = nodeTwo.id.split("-");
let x1 = parseInt(currentCoordinates[0]);
let y1 = parseInt(currentCoordinates[1]);
let x2 = parseInt(targetCoordinates[0]);
let y2 = parseInt(targetCoordinates[1]);
if (x2 < x1) {
if (nodeOne.direction === "up") {
return [1, ["f"], "up"];
} else if (nodeOne.direction === "right") {
return [2, ["l", "f"], "up"];
} else if (nodeOne.direction === "left") {
return [2, ["r", "f"], "up"];
} else if (nodeOne.direction === "down") {
return [3, ["r", "r", "f"], "up"];
}
} else if (x2 > x1) {
if (nodeOne.direction === "up") {
return [3, ["r", "r", "f"], "down"];
} else if (nodeOne.direction === "right") {
return [2, ["r", "f"], "down"];
} else if (nodeOne.direction === "left") {
return [2, ["l", "f"], "down"];
} else if (nodeOne.direction === "down") {

83
return [1, ["f"], "down"];
}
}
if (y2 < y1) {
if (nodeOne.direction === "up") {
return [2, ["l", "f"], "left"];
} else if (nodeOne.direction === "right") {
return [3, ["l", "l", "f"], "left"];
} else if (nodeOne.direction === "left") {
return [1, ["f"], "left"];
} else if (nodeOne.direction === "down") {
return [2, ["r", "f"], "left"];
}
} else if (y2 > y1) {
if (nodeOne.direction === "up") {
return [2, ["r", "f"], "right"];
} else if (nodeOne.direction === "right") {
return [1, ["f"], "right"];
} else if (nodeOne.direction === "left") {
return [3, ["r", "r", "f"], "right"];
} else if (nodeOne.direction === "down") {
return [2, ["l", "f"], "right"];
}
}
}

function getDistanceTwo(nodeOne, nodeTwo) {


let currentCoordinates = nodeOne.id.split("-");
let targetCoordinates = nodeTwo.id.split("-");
let x1 = parseInt(currentCoordinates[0]);
let y1 = parseInt(currentCoordinates[1]);
let x2 = parseInt(targetCoordinates[0]);
let y2 = parseInt(targetCoordinates[1]);
if (x2 < x1) {
if (nodeOne.otherdirection === "up") {
return [1, ["f"], "up"];
} else if (nodeOne.otherdirection === "right") {
return [2, ["l", "f"], "up"];
} else if (nodeOne.otherdirection === "left") {
return [2, ["r", "f"], "up"];
} else if (nodeOne.otherdirection === "down") {
return [3, ["r", "r", "f"], "up"];
}
} else if (x2 > x1) {
if (nodeOne.otherdirection === "up") {
return [3, ["r", "r", "f"], "down"];
} else if (nodeOne.otherdirection === "right") {
return [2, ["r", "f"], "down"];

84
} else if (nodeOne.otherdirection === "left") {
return [2, ["l", "f"], "down"];
} else if (nodeOne.otherdirection === "down") {
return [1, ["f"], "down"];
}
}
if (y2 < y1) {
if (nodeOne.otherdirection === "up") {
return [2, ["l", "f"], "left"];
} else if (nodeOne.otherdirection === "right") {
return [3, ["l", "l", "f"], "left"];
} else if (nodeOne.otherdirection === "left") {
return [1, ["f"], "left"];
} else if (nodeOne.otherdirection === "down") {
return [2, ["r", "f"], "left"];
}
} else if (y2 > y1) {
if (nodeOne.otherdirection === "up") {
return [2, ["r", "f"], "right"];
} else if (nodeOne.otherdirection === "right") {
return [1, ["f"], "right"];
} else if (nodeOne.otherdirection === "left") {
return [3, ["r", "r", "f"], "right"];
} else if (nodeOne.otherdirection === "down") {
return [2, ["l", "f"], "right"];
}
}
}

function manhattanDistance(nodeOne, nodeTwo) {


let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]);
let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]);
return (xChange + yChange);
}

function weightedManhattanDistance(nodeOne, nodeTwo, nodes) {


let nodeOneCoordinates = nodeOne.id.split("-").map(ele => parseInt(ele));
let nodeTwoCoordinates = nodeTwo.id.split("-").map(ele => parseInt(ele));
let xChange = Math.abs(nodeOneCoordinates[0] - nodeTwoCoordinates[0]);
let yChange = Math.abs(nodeOneCoordinates[1] - nodeTwoCoordinates[1]);

if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] <


nodeTwoCoordinates[1]) {

let additionalxChange = 0,
additionalyChange = 0;

85
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
} else if (nodeOneCoordinates[0] < nodeTwoCoordinates[0] && nodeOneCoordinates[1] >=
nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--)
{

86
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--)
{
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx <= nodeTwoCoordinates[0]; currentx++)
{
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
} else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] <
nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--)
{
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;

87
for (let currenty = nodeOneCoordinates[1]; currenty <= nodeTwoCoordinates[1]; currenty++)
{
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--)
{
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
} else if (nodeOneCoordinates[0] >= nodeTwoCoordinates[0] && nodeOneCoordinates[1] >=
nodeTwoCoordinates[1]) {
let additionalxChange = 0,
additionalyChange = 0;
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--
) {
let currentId = `${currentx}-${nodeOne.id.split("-")[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--
) {
let currentId = `${nodeTwoCoordinates[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}

let otherAdditionalxChange = 0,
otherAdditionalyChange = 0;
for (let currenty = nodeOneCoordinates[1]; currenty >= nodeTwoCoordinates[1]; currenty--
) {
let currentId = `${nodeOne.id.split("-")[0]}-${currenty}`;
let currentNode = nodes[currentId];
additionalyChange += currentNode.weight;
}
for (let currentx = nodeOneCoordinates[0]; currentx >= nodeTwoCoordinates[0]; currentx--
) {

88
let currentId = `${currentx}-${nodeTwoCoordinates[1]}`;
let currentNode = nodes[currentId];
additionalxChange += currentNode.weight;
}

if (additionalxChange + additionalyChange < otherAdditionalxChange +


otherAdditionalyChange) {
xChange += additionalxChange;
yChange += additionalyChange;
} else {
xChange += otherAdditionalxChange;
yChange += otherAdditionalyChange;
}
}

return xChange + yChange;

module.exports = bidirectional;

Output:

89
Sorting Algorithms:

There are several sorting algorithms, each with their own strengths and
weaknesses. Below are some examples of sorting algorithms along with a step-
by-step example of how they work and what their output might look like.

1. Bubble Sort:
Bubble Sort is a simple sorting algorithm that repeatedly steps through the
list, compares adjacent elements and swaps them if they are in the wrong
order.

Example:
Consider an unsorted list of numbers: [6, 3, 9, 1, 5]
Step 1: Compare the first two elements of the list, 6 and 3. Since 6 is greater
than 3, swap them. List becomes [3, 6, 9, 1, 5].
Step 2: Compare the next two elements of the list, 6 and 9. Since they are in
the correct order, no swap is needed. List remains [3, 6, 9, 1, 5].
Step 3: Compare the next two elements of the list, 9 and 1. Since 9 is greater
than 1, swap them. List becomes [3, 6, 1, 9, 5].
Step 4: Compare the next two elements of the list, 9 and 5. Since 9 is greater
than 5, swap them. List becomes [3, 6, 1, 5, 9].
Step 5: The last two elements of the list, 5 and 9, are in the correct order. The
algorithm has completed one pass through the list.
Step 6: Repeat the previous steps until no swaps are needed.

Output:
The sorted list is [1, 3, 5, 6, 9].

90
Code:
import { setArray } from "../reducers/array";
import { setCurrentBubbleTwo } from "../reducers/bubbleSort";
import { setCurrentSwappers } from "../reducers/swappers";
import { setCurrentSorted } from "../reducers/sorted";
import { setRunning } from "../reducers/running";

function bubbleSort(stateArray, dispatch, speed) {


let array = stateArray.slice(0),
toDispatch = [],
sorted = false,
round = 0;
while (!sorted) {
sorted = true;
for (let i = 0; i < array.length - 1 - round; i++) {
toDispatch.push([i, i + 1]);
if (array[i] > array[i + 1]) {
toDispatch.push([i, i + 1, true]);
let temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
sorted = false;
toDispatch.push(array.slice(0));
toDispatch.push([]);
}
}
toDispatch.push([true, array.length - 1 - round]);
round++;
}
handleDispatch(toDispatch, dispatch, array, speed);
return array;
}

function handleDispatch(toDispatch, dispatch, array, speed) {


if (!toDispatch.length) {
dispatch(setCurrentBubbleTwo(array.map((num, index) => index)));
setTimeout(() => {
dispatch(setCurrentBubbleTwo([]));
dispatch(setCurrentSorted(array.map((num, index) => index)));
dispatch(setRunning(false));
}, 900);
return;
}
let dispatchFunction = toDispatch[0].length > 3 ?
setArray : toDispatch[0].length === 3 || toDispatch[0].length === 0 ?
setCurrentSwappers : toDispatch[0].length === 2 && typeof toDispatch[0][0] === "boolean"
?

91
setCurrentSorted : setCurrentBubbleTwo;
dispatch(dispatchFunction(toDispatch.shift()));
setTimeout(() => {
handleDispatch(toDispatch, dispatch, array, speed);
}, speed);
}

export default bubbleSort;

Output:

2. Merge Sort:
Merge Sort is a divide and conquer algorithm that divides the unsorted list
into n sublists, each containing one element, and then repeatedly merges
sublists to produce new sorted sublists until there is only one sublist
remaining.

Example:
Consider an unsorted list of numbers: [6, 3, 9, 1, 5]

92
Step 1: Divide the list into two sublists of roughly equal size: [6, 3, 9] and [1,
5].
Step 2: Recursively divide the sublists until each sublist contains only one
element.
Step 3: Merge the sublists by comparing the first element of each sublist and
adding the smaller element to the new list. List becomes [1, 5, 3, 6, 9].
Step 4: Repeat the previous step until all sublists have been merged into one
sorted list.

Output:
The sorted list is [1, 3, 5, 6, 9].

Code:
import { setArray } from "../reducers/array";
import { setCurrentMergeX } from "../reducers/mergeSort";
import { setCurrentSwappers } from "../reducers/swappers";
import { setCurrentSorted } from "../reducers/sorted";
import { setRunning } from "../reducers/running";

function mergeSort(stateArray, dispatch, speed) {


let array = stateArray.slice(0),
toDispatch = [];
let finalArray = mergeSortHelper(array.map((num, idx) => [num, idx]), toDispatch, 0,
array.length - 1, {array: array.slice(0)});
handleDispatch(toDispatch, dispatch, finalArray, speed);
}

function mergeSortHelper(array, toDispatch, start, end, obj) {


if (array.length === 1) {
return array;
}
let half = Math.floor(array.length / 2),
first = array.slice(0, half),
second = array.slice(half),
indexHalf = Math.floor((end + 1 + start) / 2),
actualFirst = mergeSortHelper(first, toDispatch, start, indexHalf - 1, obj),
actualSecond = mergeSortHelper(second, toDispatch, indexHalf, end, obj),
isFinalMerge = false;
if (actualFirst.length + actualSecond.length === obj.array.length) isFinalMerge = true;

93
return actualSort(actualFirst, actualSecond, toDispatch, obj, start, end, isFinalMerge);
}

function actualSort(first, second, toDispatch, obj, start, end, isFinalMerge) {


let sortedArray = [];
let indexToPush = start;
while (first.length && second.length) {
toDispatch.push([first[0][1], second[0][1]]);
if (first[0][0] <= second[0][0]) {
indexToPush++;
sortedArray.push(first.shift());
} else {
toDispatch.push([first[0][1], second[0][1], true]);
second[0][1] = indexToPush++;
sortedArray.push(second.shift());
first.forEach(subArr => subArr[1]++);
if (start === 0) {
obj.array = sortedArray.map(subArr => subArr[0]).concat(first.map(subArr =>
subArr[0])).concat(second.map(subArr => subArr[0])).concat(obj.array.slice(end + 1));
} else {
obj.array = obj.array.slice(0, start).concat(sortedArray.map(subArr =>
subArr[0])).concat(first.map(subArr => subArr[0])).concat(second.map(subArr =>
subArr[0])).concat(obj.array.slice(end + 1));
}
toDispatch.push(obj.array.concat([indexToPush - 1, indexToPush]));
toDispatch.push([]);
}
if (isFinalMerge) toDispatch.push([true, indexToPush - 1]);
}
return sortedArray.concat(first).concat(second);
}

function handleDispatch(toDispatch, dispatch, array, speed) {


if (!toDispatch.length) {
dispatch(setCurrentMergeX(array.map((num, index) => index)));
setTimeout(() => {
dispatch(setCurrentMergeX([]));
dispatch(setCurrentSorted(array.map((num, index) => index)));
dispatch(setRunning(false));
}, 900);
return;
}
let dispatchFunction = toDispatch[0].length > 3 ?
setArray : toDispatch[0].length === 3 && typeof toDispatch[0][2] === "boolean" ||
toDispatch[0].length === 0 ?
setCurrentSwappers : toDispatch[0].length === 2 && typeof toDispatch[0][0] === "boolean"
?
setCurrentSorted : setCurrentMergeX;

94
if (dispatchFunction === setArray) {
let currentToDispatch = toDispatch.shift();
dispatch(dispatchFunction(currentToDispatch.slice(0, currentToDispatch.length - 2)));
dispatch(setCurrentSwappers([]));
dispatch(setCurrentMergeX([]));
dispatch(setCurrentSwappers([currentToDispatch[currentToDispatch.length - 2],
currentToDispatch[currentToDispatch.length - 1]]));
dispatch(setCurrentMergeX([currentToDispatch[currentToDispatch.length - 2],
currentToDispatch[currentToDispatch.length - 1]]));
} else {
dispatch(dispatchFunction(toDispatch.shift()));
}
setTimeout(() => {
handleDispatch(toDispatch, dispatch, array, speed);
}, speed);
}

export default mergeSort;

Output:

95
3. Quick Sort:
Quick Sort is a divide and conquer algorithm that selects a pivot element and
partitions the array around the pivot, such that elements less than the pivot
are moved to the left of the pivot and elements greater than the pivot are
moved to the right of the pivot. The algorithm then recursively sorts the
subarrays on either side of the pivot.

Example:
Consider an unsorted list of numbers: [6, 3, 9, 1, 5]

Step 1: Select a pivot element. For this example, we will use the first element,
6.
Step 2: Partition the array around the pivot by moving all elements less than
the pivot to the left and all elements greater than the pivot to the right. List
becomes [3, 1, 5, 6, 9].
Step 3: Recursively sort the subarrays on either side of the pivot. For the left
subarray [3, 1, 5], select the pivot element 3 and repeat the previous steps. The
left subarray becomes [1, 3, 5]. For the right subarray [9], there is only one
element and it is already sorted.
Step 4: Merge the sorted subarrays into one final sorted array.

Output:
The sorted list is [1, 3, 5, 6, 9].

Note: These examples show the steps and output for sorting an array of
integers. However, sorting algorithms can be used to sort other types of data
structures as well, such as strings, floats, or objects, depending on the
implementation.

96
Code:
import { setArray } from "../reducers/array";
import { setCurrentQuickTwo, setPivot } from "../reducers/quickSort";
import { setCurrentSwappers } from "../reducers/swappers";
import { setCurrentSorted } from "../reducers/sorted";
import { setRunning } from "../reducers/running";

function quickSort(stateArray, dispatch, speed) {


let array = stateArray.slice(0),
toDispatch = [];
quickSortHelper(array, 0, array.length - 1, toDispatch);
handleDispatch(toDispatch, dispatch, array, speed);
return array;
}

function quickSortHelper(array, start, end, toDispatch) {


if (start >= end) {
toDispatch.push([true, start]);
return;
}
let pivot = start,
left = start + 1,
right = end;
toDispatch.push(pivot);
toDispatch.push([left, right]);
while (right >= left) {
if (array[right] < array[pivot] && array[left] > array[pivot]) {
toDispatch.push([left, right, true]);
let temp = array[right];
array[right] = array[left];
array[left] = temp;
toDispatch.push(array.slice(0));
toDispatch.push([]);
}
if (array[right] >= array[pivot]) {
right--;
}
if (array[left] <= array[pivot]) {
left++;
}
if (right >= left) toDispatch.push([left, right]);
}
toDispatch.push([pivot, right]);
if (pivot !== right) {
let temp = array[right];
array[right] = array[pivot];
array[pivot] = temp;

97
toDispatch.push([pivot, right, true]);
toDispatch.push(array.slice(0));
toDispatch.push([]);
toDispatch.push([true, right]);
}
quickSortHelper(array, start, right - 1, toDispatch);
quickSortHelper(array, right + 1, end, toDispatch);
}

function handleDispatch(toDispatch, dispatch, array, speed) {


if (!toDispatch.length) {
dispatch(setPivot(null));
dispatch(setCurrentQuickTwo(array.map((num, index) => index)));
setTimeout(() => {
dispatch(setCurrentQuickTwo([]));
dispatch(setRunning(false));
}, 900);
return;
}
let dispatchFunction = !(toDispatch[0] instanceof Array) ?
setPivot : toDispatch[0].length > 3 ?
setArray : toDispatch[0].length !== 2 ?
setCurrentSwappers : toDispatch[0].length === 2 && typeof toDispatch[0][0] ===
"boolean" ?
setCurrentSorted : setCurrentQuickTwo;
dispatch(dispatchFunction(toDispatch.shift()));
if (dispatchFunction === setPivot) dispatch(setCurrentQuickTwo(toDispatch.shift()));
setTimeout(() => {
handleDispatch(toDispatch, dispatch, array, speed);
}, speed);
}

export default quickSort;

98
Output:

4. Heap Sort:
Heap Sort is a comparison-based sorting algorithm that works by first creating
a max heap from the unsorted list, then repeatedly extracting the maximum
element and swapping it with the last element in the heap until the heap is
empty.

Example:
Consider an unsorted list of numbers: [6, 3, 9, 1, 5]

Step 1: Build a max heap from the unsorted list. List becomes [9, 6, 3, 1, 5].
Step 2: Swap the first element (the maximum element) with the last element
in the heap. List becomes [5, 6, 3, 1, 9].
Step 3: Discard the last element (which is now sorted) and re-heapify the
remaining elements to maintain the max heap property. List becomes [6, 5, 3,
1].
Step 4: Repeat the previous steps until the heap is empty.
99
Output:
The sorted list is [1, 3, 5, 6, 9].

Code:
import { setArray } from "../reducers/array";
import { setCurrentHeapThree } from "../reducers/heapSort";
import { setCurrentSwappers } from "../reducers/swappers";
import { setCurrentSorted } from "../reducers/sorted";
import { setRunning } from "../reducers/running";

function heapSort(stateArray, dispatch, speed) {


let array = stateArray.slice(0),
toDispatch = [];
buildMaxHeap(array, toDispatch);
let end = array.length - 1;
while (end > 0) {
toDispatch.push([0, end]);
let temp = array[end];
array[end] = array[0];
array[0] = temp;
toDispatch.push([0, end, true]);
toDispatch.push(array.slice(0));
toDispatch.push([]);
toDispatch.push([true, end]);
siftDown(array, 0, end, toDispatch);
end--;
}
toDispatch.push([true, end]);
handleDispatch(toDispatch, dispatch, array, speed);
return array;
}

function buildMaxHeap(array, toDispatch) {


let currentIndex = Math.floor(array.length / 2);
while (currentIndex >= 0) {
siftDown(array, currentIndex, array.length, toDispatch);
currentIndex--;
}
}

function siftDown(array, start, end, toDispatch) {


if (start >= Math.floor(end / 2)) {
return;
}
100
let left = start * 2 + 1,
right = start * 2 + 2 < end ? start * 2 + 2 : null,
swap;
if (right) {
toDispatch.push([start, left, right]);
swap = array[left] > array[right] ? left : right;
} else {
toDispatch.push([start, left]);
swap = left;
}
if (array[start] < array[swap]) {
let temp = array[swap];
array[swap] = array[start];
array[start] = temp;
toDispatch.push([start, swap, true]);
toDispatch.push(array.slice(0));
toDispatch.push([]);
siftDown(array, swap, end, toDispatch);
}
}

function handleDispatch(toDispatch, dispatch, array, speed) {


if (!toDispatch.length) {
dispatch(setCurrentHeapThree(array.map((num, index) => index)));
setTimeout(() => {
dispatch(setCurrentHeapThree([]));
dispatch(setRunning(false));
}, 900);
return;
}
let dispatchFunction = toDispatch[0].length > 3 ?
setArray : toDispatch[0].length === 3 && typeof toDispatch[0][2] === "boolean" ||
!toDispatch[0].length ?
setCurrentSwappers : toDispatch[0].length === 2 && typeof toDispatch[0][0] ===
"boolean" ?
setCurrentSorted : setCurrentHeapThree;
dispatch(dispatchFunction(toDispatch.shift()));
setTimeout(() => {
handleDispatch(toDispatch, dispatch, array, speed);
}, speed);
}

export default heapSort;

101
Output:

102
8. Conclusion

An algorithm visualizer is a tool that allows users to see the step-by-step


process of an algorithm as it executes on a given input. Two common types of
algorithms that are often visualized are pathfinding algorithms and sorting
algorithms.

Pathfinding algorithms, such as Dijkstra's algorithm and A* search algorithm,


are used to find the shortest path between two points in a graph or grid.
Algorithm visualizers can be used to help users understand how these
algorithms work by displaying the graph or grid and highlighting the nodes or
cells that are visited and the edges or paths that are taken during the execution
of the algorithm. This can help users to gain a deeper understanding of the
algorithm and to identify any potential issues or limitations.

Sorting algorithms, such as bubble sort and quicksort, are used to sort a list of
items in ascending or descending order. Algorithm visualizers can be used to
help users understand how these algorithms work by displaying the unsorted
list and highlighting the comparisons and swaps that are made during the
execution of the algorithm. This can help users to gain a deeper understanding
of the algorithm and to identify any potential issues or limitations, such as
worst-case time complexity.

Additionally, algorithm visualizers can also be used to experiment with


different input sizes and see how the algorithm's performance changes as the
size of the input increases. This can be particularly useful for understanding
the time and space complexity of an algorithm, as well as identifying potential
performance bottlenecks.

103
Furthermore, algorithm visualizers can be customized to suit different
learning styles and levels of experience. For example, some visualizers may
provide detailed explanations of each step of the algorithm, while others may
allow users to interact with the algorithm and make modifications to the input
or parameters.

In conclusion, algorithm visualizers using pathfinding algorithms and sorting


algorithms can be a valuable tool for teaching and learning about algorithms.
They can provide users with a visual representation of how algorithms work,
allow them to experiment with different inputs and parameters, and
customize the learning experience to suit their needs. By using algorithm
visualizers, students and professionals can gain a deeper understanding of
algorithms and how they can be applied to solve real-world problems.

104
Appendix

Code:

 Pathfinding Visualizer:

<html>
<head>
<title>Pathfinding Visualizer</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link id="cssTheme" rel="stylesheet" href="public/styling/cssBasic.css"/>
<link rel="shortcut icon" type="image/png" href="public/styling/c_icon.png"/>
</head>
<body>
<div id='navbarDiv'>
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<a id="refreshButton" class="navbar-brand" href="#">Pathfinding Visualizer</a>
</div>
<ul class="nav navbar-nav">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Algorithms
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li id='startButtonDijkstra'><a href="#">Dijkstra's Algorithm</a></li>
<li id='startButtonAStar2'><a href="#">A* Search</a></li>
<li id='startButtonGreedy'><a href="#">Greedy Best-first Search</a></li>
<li id='startButtonAStar'><a href="#">Swarm Algorithm</a></li>
<li id='startButtonAStar3'><a href="#">Convergent Swarm Algorithm</a></li>
<li id='startButtonBidirectional'><a href="#">Bidirectional Swarm
Algorithm</a></li>
<li id='startButtonBFS'><a href="#">Breadth-first Search</a></li>
<li id='startButtonDFS'><a href="#">Depth-first Search</a></li>
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Mazes &amp; Patterns
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li id='startButtonCreateMazeTwo'><a href="#">Recursive Division</a></li>
<li id='startButtonCreateMazeThree'><a href="#">Recursive Division (vertical
skew)</a></li>

105
<li id='startButtonCreateMazeFour'><a href="#">Recursive Division (horizontal
skew)</a></li>
<li id='startButtonCreateMazeOne'><a href="#">Basic Random Maze</a></li>
<li id='startButtonCreateMazeWeights'><a href="#">Basic Weight Maze</a></li>
<li id='startStairDemonstration'><a href="#">Simple Stair Pattern</a></li>
</ul>
</li>
<li id='startButtonAddObject'><a href="#">Add Bomb</a></li>
<li id='startButtonStart'><button id="actualStartButton" class="btn btn-default
navbar-btn" type="button">Visualize!</button></li>
<li id='startButtonClearBoard'><a href="#">Clear Board</a></li>
<li id='startButtonClearWalls'><a href="#">Clear Walls &amp; Weights</a></li>
<li id='startButtonClearPath'><a href="#">Clear Path</a></li>
<li class="dropdown">
<a id="adjustSpeed" class="dropdown-toggle" data-toggle="dropdown"
href="#">Speed: Fast
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li id='adjustFast'><a href="#">Fast</a></li>
<li id='adjustAverage'><a href="#">Average</a></li>
<li id='adjustSlow'><a href="#">Slow</a></li>
</ul>
</li>
</ul>
</div>
</nav>
</div>
<div id="tutorial">
<h3>Welcome to Pathfinding Visualizer!</h3>
<h6>This short tutorial will walk you through all of the features of this
application.</h6>
<p>If you want to dive right in, feel free to press the "Skip Tutorial" button below.
Otherwise, press "Next"!</p>
<div id="tutorialCounter">1/9</div>
<img id="mainTutorialImage" src="public/styling/c_icon.png">
<button id="nextButton" class="btn btn-default navbar-btn" type="button">Next</button>
<button id="previousButton" class="btn btn-default navbar-btn"
type="button">Previous</button>
<button id="skipButton" class="btn btn-default navbar-btn" type="button">Skip
Tutorial</button>
</div>
<div id='mainGrid'>
<div id='mainText'>
<ul>
<li>
<div class="start"></div>Start Node</li>
<li>
<div class="target"></div>Target Node</li>

106
<li id="bombLegend">
<div class="object"></div>Bomb Node</li>
<li id="weightLegend">
<div class="borderlessWeight"></div>Weight Node</li>
<li>
<div class="unvisited"></div>Unvisited Node</li>
<li>
<div class="visited"></div><div class="visitedobject"></div>Visited Nodes</li>
<li>
<div class="shortest-path"></div>Shortest-path Node</li>
<li>
<div class="wall"></div>Wall Node</li>
</ul>
</div>
<div id="algorithmDescriptor">Pick an algorithm and visualize it!</div>
<table id='board'/>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src='public/browser/bundle.js'></script>
</html>

 Sorting Visualizer:

<html>
<head>
<meta charset="utf-8">
<title>Sorting Visualizer</title>
<link rel="shortcut icon" type="image/ico" href="client/styling/icon.ico"/>
</head>
<body>
<div id="app" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="client/public/bundle.js" type="text/javascript"></script>
</body>
</html>

107
References

 Clément Mihailescu (Github link:

https://github.com/clementmihailescu/Sorting-Visualizer-Tutorial)

 Clément Mihailescu (Github link:

https://github.com/clementmihailescu/Pathfinding-Visualizer-Tutorial )

 International Research Journal of Modernization in Engineering Technology

and Science

(https://www.irjmets.com/uploadedfiles/paper/volume3/issue_3_march_2021/6743/16

28083287.pdf)

 ChatGPT (https://chat.openai.com/c/3da29522-95f6-47e5-9e47-77fc1be2f6d7)

 Swapnoneel Dutta Majumdar

(https://www.crio.do/projects/javascript-sorting-visualiser/)

 Github (https://algorithm-visualizer.org/)

108

You might also like