-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- 20 books
+
+
+
+ Bubble Sort
+ Bubble sort is a simple algorithm for sorting integers or other objects. The procedure examines each group of neighboring items from left to right, swapping their locations if they are out of order. The algorithm repeats this until it finds no items to swap in the entire array.
+ Try Bubble Sort
+
+
+
+
+
+ Selection Sort
+ Selection sort is an in-place comparison sorting algorithm. It divides the input array into a sorted and an unsorted region. It repeatedly selects the smallest element from the unsorted region and swaps it with the leftmost element in the unsorted region, expanding the sorted region by one element.
+ Try Selection Sort
+
+
+
+
+
+ Quick Sort
+ Quick sort is a highly efficient sorting algorithm and is based on the divide-and-conquer strategy. It works by selecting a pivot element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively. This process is repeated for the sub-arrays until the entire array is sorted.
+ Try Quick Sort
+
+
+
+
+
+ Insertion Sort
+ Insertion sort is a simple sorting algorithm that builds the final sorted array one item at a time. It works by iterating through the array, comparing each element with the previous elements, and moving the elements greater than the current element one position ahead.
+ Try Insertion Sort
+
+
+
+
+
+ Back to top
+
+
+
+
-
-
-
-
-
-
\ No newline at end of file
diff --git a/main.css b/main.css
new file mode 100644
index 0000000..8d395c1
--- /dev/null
+++ b/main.css
@@ -0,0 +1,554 @@
+body {
+ font-family: sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+.logo {
+ position: absolute;
+ top: 90px;
+ left: 60px;
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+}
+
+header {
+ position: relative;
+ text-align: center;
+ padding: 20px 0;
+ height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ font-size: 100px;
+ height: 100vh;
+ background-image: url("./image/bubble_sort_bg.jpg");
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+header h1 {
+ margin: 0;
+ font-size: 100px;
+ opacity: 0;
+ animation: fade-in 2s forwards;
+ text-align: center;
+ white-space: nowrap;
+ font-family: 'sans-serif';
+
+}
+
+@keyframes fade-in {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.vertical-line1:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1;
+}
+
+.vertical-line1:after {
+ content: '';
+ position: absolute;
+ top: 10%;
+ left: 50%;
+ width: 2px;
+ height: 26%;
+ background: #fff;
+ z-index: 2;
+ transform-origin: top;
+ transform: scaleY(0);
+ animation: animate1 1.5s forwards;
+ animation-fill-mode: forwards;
+}
+
+@keyframes animate1 {
+ 0% {
+ transform: translateY(-50%) scaleY(0);
+ transform-origin: top;
+ }
+ 100% {
+ transform: translateY(0) scaleY(1);
+ transform-origin: top;
+ }
+}
+
+.vertical-line2:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1;
+}
+
+.vertical-line2:after {
+ content: '';
+ position: absolute;
+ top: 65%;
+ left: 50%;
+ width: 2px;
+ height: 25%;
+ background: #fff;
+ z-index: 2;
+ transform-origin: top;
+ transform: scaleY(0);
+ animation: animate2 1.5s forwards;
+ animation-delay: 1s;
+ animation-fill-mode: forwards;
+}
+
+@keyframes animate2 {
+ 0% {
+ transform: translateY(-20%) scaleY(0);
+ transform-origin: top;
+ }
+ 100% {
+ transform: translateY(0) scaleY(1);
+ transform-origin: top;
+ }
+}
+
+nav {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0.8);
+ height: 50px;
+ z-index: 9999;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: 'Source Sans Pro'
+}
+
+nav a {
+ color: rgb(255, 255, 255);
+ text-decoration: none;
+ margin: 0 20px;
+ font-size: 25px;
+ transition: transform 0.3s;
+ font-weight: bold;
+}
+
+nav a:hover {
+ transform: scale(1.1);
+}
+
+
+section {
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 100px;
+ color: #000;
+ text-align: center;
+ cursor: pointer;
+}
+
+.sort-info {
+ display: flex;
+ align-items: flex-start;
+ gap: 50px;
+ flex-direction: column;
+ justify-content: flex-start;
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 20px;
+ position: relative;
+}
+
+
+#bubble-sort .sort-info::before {
+ content: '';
+ position: absolute;
+ top: -15%;
+ left: -75%;
+ width: 253%;
+ height: 3px;
+ background-color: #000;
+ transform-origin: left;
+ transform: scaleX(0);
+ animation: animate-line 1s forwards;
+ animation-delay: 1s;
+}
+
+@keyframes animate-line {
+ 0% {
+ transform: scaleX(0);
+ }
+ 100% {
+ transform: scaleX(1);
+ }
+}
+
+
+#selection-sort .sort-info::before {
+ content: '';
+ position: absolute;
+ top: -10%;
+ left: -75%;
+ width: 253%;
+ height: 3px;
+ background-color: #fff;
+ transform-origin: left;
+ transform: scaleX(0);
+ animation: animate-line 1s forwards;
+ animation-delay: 1s;
+}
+
+@keyframes animate-line {
+ 0% {
+ transform: scaleX(0);
+ }
+ 100% {
+ transform: scaleX(1);
+ }
+}
+
+
+#quick-sort .sort-info::before {
+ content: '';
+ position: absolute;
+ top: -5%;
+ left: -75%;
+ width: 253%;
+ height: 3px;
+ background-color: #000;
+ transform-origin: left;
+ transform: scaleX(0);
+ animation: animate-line 1s forwards;
+ animation-delay: 1s;
+}
+
+@keyframes animate-line {
+ 0% {
+ transform: scaleX(0);
+ }
+ 100% {
+ transform: scaleX(1);
+ }
+}
+
+
+#insertion-sort .sort-info::before {
+ content: '';
+ position: absolute;
+ top: -15%;
+ left: -75%;
+ width: 253%;
+ height: 3px;
+ background-color: #fff;
+ transform-origin: left;
+ transform: scaleX(0);
+ animation: animate-line 1s forwards;
+ animation-delay: 1s;
+}
+
+@keyframes animate-line {
+ 0% {
+ transform: scaleX(0);
+ }
+ 100% {
+ transform: scaleX(1);
+ }
+}
+
+#pathfinder .sort-info::before {
+ content: '';
+ position: absolute;
+ top: -20%;
+ left: -75%;
+ width: 253%;
+ height: 3px;
+ background-color: #000;
+ transform-origin: left;
+ transform: scaleX(0);
+ animation: animate-line 1s forwards;
+ animation-delay: 1s;
+}
+
+@keyframes animate-line {
+ 0% {
+ transform: scaleX(0);
+ }
+ 100% {
+ transform: scaleX(1);
+ }
+}
+
+
+.sort-name {
+ font-size: 65px;
+ font-weight: bold;
+ font-family: 'Sources Sans Pro';
+ text-align: left;
+ margin-left: -55%;
+}
+
+.sort-definition {
+ font-size: 25px;
+ line-height: 1.4;
+ flex-grow: 1;
+ margin-left: -67%;
+ color: #000;
+ text-align: left;
+ width:500px;
+}
+
+.fade-out{
+ opacity: 0;
+ transition: opacity 0.5s ease-in;
+}
+
+.fade-out.appear{
+ opacity: 1;
+}
+
+.try-link {
+ font-size: 24px;
+ background-color: black;
+ color: white;
+ padding: 12px 36px;
+ border-radius: 5px;
+ transition: transform 0.3s;
+ position: relative;
+ overflow: hidden;
+ display: inline-block;
+ letter-spacing: 2px;
+ border-radius: 40px;
+ background: linear-gradient(90deg, #000);
+ font-weight: bold;
+ align-self: flex-start;
+ margin-left: -52%;
+ margin-top: 30px;
+}
+
+.try-link::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 0;
+ height: 0;
+ background-color: rgba(255, 255, 255, 0.3);
+ border-radius: 50%;
+ opacity: 0;
+ transition: width 0.4s ease-out, height 0.4s ease-out, opacity 0.4s ease-out;
+}
+
+.try-link:hover::after {
+ width: 300px;
+ height: 300px;
+ opacity: 1;
+}
+
+#bubble-sort {
+ background-color: white;
+}
+#bubble-sort .sort-name {
+ color: black;
+}
+#bubble-sort .sort-definition {
+ color: black;
+}
+
+
+#selection-sort {
+ background-color: black;
+}
+#selection-sort .sort-name {
+ color: white;
+}
+#selection-sort .sort-definition {
+ color: white;
+}
+#selection-sort .try-link{
+ background-color: white;
+ color: black;
+}
+#selection-sort .try-link::after{
+ background-color: rgba(255, 255, 255, 0.635);
+}
+
+
+#quick-sort {
+ background-color: white;
+}
+#quick-sort .sort-name {
+ color: black;
+}
+#quick-sort .sort-definition {
+ color: black;
+}
+
+
+#insertion-sort {
+ background-color: black;
+}
+#insertion-sort .sort-name {
+ color: white;
+}
+#insertion-sort .sort-definition {
+ color: white;
+}
+#insertion-sort .try-link{
+ background-color: white;
+ color: black;
+}
+#insertion-sort .try-link::after{
+ background-color: rgba(255, 255, 255, 0.635);
+}
+
+
+#pathfinder {
+ background-color: white;
+}
+#pathfinder .sort-name {
+ color: black;
+}
+#pathfinder .sort-definition {
+ color: black;
+}
+
+
+#back-to-top {
+ position: fixed;
+ bottom: 30px;
+ right: 30px;
+ display: none;
+ width: 40px;
+ height: 40px;
+ background-color: #333;
+ border-radius: 50%;
+ text-decoration: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: opacity 0.3s;
+}
+
+#back-to-top img {
+ width: 40px;
+ height: 40px;
+ display: block;
+}
+
+#back-to-top:hover {
+ opacity: 0.8;
+}
+#back-to-top .tooltip {
+ position: absolute;
+ bottom: 50px;
+ left: 50%;
+ transform: translateX(-60%);
+ background-color: #ffffff;
+ color: black;
+ padding: 0px 10px;
+ font-size: 16px;
+ border-radius: 4px;
+ opacity: 0;
+ transition: opacity 0.3s;
+ white-space: nowrap;
+}
+
+#back-to-top:hover .tooltip {
+ opacity: 1.3;
+}
+
+#information {
+ background-color: #f9f9f9;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 30vh;
+}
+
+.information-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 60%;
+ max-width: 1000px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+.information-logo {
+ font-size: 40px;
+ font-weight: bold;
+ color: #000;
+ font-family: 'Proxima Nova';
+}
+
+.contact-info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.team-member {
+ margin-bottom: 20px;
+ font-size: 25px;
+ color: #000;
+}
+
+.hidden{
+ opacity: 0;
+ transform: translateY(20px);
+ transition: opacity 1.5s, transform 1.5s;
+}
+.show{
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.video-container {
+ position: absolute;
+ top: 50%;
+ right: 10%;
+ transform: translateY(-50%);
+ width: 550px;
+ height: auto;
+}
+
+.video-container video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.image-container {
+ position: absolute;
+ top: 50%;
+ right: 10%;
+ transform: translateY(-50%);
+ width: 550px;
+ height: auto;
+}
+
+.image-container img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..314c99c
--- /dev/null
+++ b/main.js
@@ -0,0 +1,70 @@
+document.addEventListener("DOMContentLoaded", function() {
+ const fadeInElements = document.querySelectorAll(".fade-in");
+
+ function checkVisibility() {
+ for (const element of fadeInElements) {
+ if (isElementInViewport(element)) {
+ element.classList.add("show");
+ } else {
+ element.classList.remove("show");
+ }
+ }
+ }
+
+ function isElementInViewport(element) {
+ const rect = element.getBoundingClientRect();
+ return (
+ rect.top >= 0 &&
+ rect.left >= 0 &&
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
+ );
+ }
+
+ window.addEventListener('scroll', function() {
+ var backToTopButton = document.getElementById('back-to-top');
+ if (window.scrollY > 200) {
+ backToTopButton.style.display = 'block';
+ } else {
+ backToTopButton.style.display = 'none';
+ }
+ });
+
+ document.getElementById('back-to-top').addEventListener('click', function(e) {
+ e.preventDefault();
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ });
+
+ const observer = new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ entry.target.classList.add('show');
+ const line = entry.target.querySelector('.horizontal-line');
+ setTimeout(() => {
+ line.classList.add('animate-line');
+ }, 2000); // Adjust the delay as needed
+ } else {
+ entry.target.classList.remove('show');
+ }
+ });
+ });
+
+ const hiddenElements = document.querySelectorAll('.hidden');
+ hiddenElements.forEach((el) => observer.observe(el));
+
+ // Call checkVisibility on page load
+ checkVisibility();
+
+ // Recheck visibility on scroll
+ window.addEventListener('scroll', checkVisibility);
+});
+
+
+
+
+
+
+
+
+
+
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json
index acecb8c..59c0dee 100644
--- a/node_modules/.package-lock.json
+++ b/node_modules/.package-lock.json
@@ -1,5 +1,5 @@
{
- "name": "typescript-pain",
+ "name": "the-library",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
diff --git a/package-lock.json b/package-lock.json
index d8839d5..b7afebe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "typescript-pain",
+ "name": "the-library",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "typescript-pain",
+ "name": "the-library",
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
diff --git a/package.json b/package.json
index 5b24c9e..eed0262 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "typescript-pain",
+ "name": "the-library",
"version": "1.0.0",
"description": "",
"main": "main.js",
diff --git a/src/graph/code/.hintrc b/src/graph/code/.hintrc
new file mode 100644
index 0000000..2fa1a2e
--- /dev/null
+++ b/src/graph/code/.hintrc
@@ -0,0 +1,13 @@
+{
+ "extends": [
+ "development"
+ ],
+ "hints": {
+ "axe/forms": [
+ "default",
+ {
+ "select-name": "off"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/graph/code/main.ts b/src/graph/code/main.ts
new file mode 100644
index 0000000..3d7a872
--- /dev/null
+++ b/src/graph/code/main.ts
@@ -0,0 +1,400 @@
+// Project: Algorithms and Data Structures
+// Author: nmvkhoi
+
+// ts-check
+// constants
+
+import getAdjacencyList from '../code/pathFindingAlgorithms/utility.js';
+import {getEndNode, getSourceNode, getNodeXCoordinates, getNodeYCoordinates} from "./pathFindingAlgorithms/utility.js";
+import {getShortestDistanceBFS} from "./pathFindingAlgorithms/bfs.js";
+import {getPathDFS} from "./pathFindingAlgorithms/dfs.js";
+import createText from "./popup.js";
+
+const canvas = document.getElementById('canvas') as HTMLCanvasElement;
+const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
+const clear = document.getElementById('clear') as HTMLButtonElement;
+const begin = document.getElementById('begin') as HTMLButtonElement;
+const end = document.getElementById('end') as HTMLButtonElement;
+const wall = document.getElementById('wall') as HTMLButtonElement;
+const start = document.getElementById('start') as HTMLButtonElement;
+const select = document.getElementById('select') as HTMLSelectElement;
+
+document.querySelector('#slider_time input')!.addEventListener('input', function(this: HTMLInputElement) {
+ delay = Number(this.value);
+ document.querySelector('#value_time')!.textContent = this.value +"ms";
+});
+
+const width = canvas.width;
+const height = canvas.height;
+const cellSize = 20;
+export let delay:number = 10;
+
+const rows = height / cellSize;
+const cols = width / cellSize;
+
+let matrix: number[][] = [];
+let isDragging: boolean = false;
+let isStart = false;
+let prevStart = [-1, -1];
+let isEnd = false;
+let prevEnd = [-1, -1];
+let selectedAlgorithm = '';
+// console.log(rows, cols);
+
+let adjList: number[][] = [];
+let startNode: number;
+let endNode: number = -1;
+
+// Functions
+// draw a square with animation zoom out
+export async function drawSquareWithAnimation(x: number, y: number, color: string) {
+ const initialSize = 1;
+ const targetSize = cellSize;
+
+ let currentSize = initialSize;
+ let animationStartTime= 0; // milliseconds
+
+ function animate() {
+ const now = Date.now();
+ const elapsedTime = now - animationStartTime;
+ const progress = elapsedTime / 100; // Divide by 1000 to convert milliseconds to seconds
+
+ ctx.clearRect(y * cellSize, x * cellSize, cellSize, cellSize); // Clear the previous frame
+
+ if (progress < 1) {
+ currentSize = initialSize + (targetSize - initialSize) * progress;
+
+ ctx.fillStyle = color;
+ ctx.fillRect(
+ y * cellSize + (cellSize - currentSize) / 2,
+ x * cellSize + (cellSize - currentSize) / 2,
+ currentSize,
+ currentSize
+ );
+
+ requestAnimationFrame(animate);
+ } else {
+ ctx.fillStyle = color;
+ ctx.fillRect(y * cellSize, x * cellSize, cellSize, cellSize);
+ }
+ }
+
+ animationStartTime = Date.now();
+ animate();
+}
+// create a matrix
+function createMatrix([]: number[][]) {
+ for (let i = 0; i < rows; i++) {
+ matrix[i] = [];
+ for (let j = 0; j < cols; j++) {
+ matrix[i][j] = 0;
+ }
+ }
+}
+// clear the matrix
+function clearMatrix([]: number[][]) {
+ for (let i = 0; i < rows; i++) {
+ matrix[i] = [];
+ for (let j = 0; j < cols; j++) {
+ matrix[i][j] = 0;
+ }
+ }
+}
+// print the matrix
+function printMatrix([]: number[][]) {
+ for (let i = 0; i < rows; i++) {
+ let line = '';
+ for (let j = 0; j < cols; j++) {
+ line += matrix[i][j] + ' ';
+ }
+ console.log(line);
+ }
+}
+createMatrix(matrix);
+
+// draw the grid
+function drawGrid() {
+ for (let i = cellSize; i < height; i += cellSize) {
+ ctx.beginPath();
+ ctx.moveTo(0, i);
+ ctx.lineTo(width, i);
+ ctx.stroke();
+ }
+ for (let j = cellSize; j < width; j += cellSize) {
+ ctx.beginPath();
+ ctx.moveTo(j, 0);
+ ctx.lineTo(j, height);
+ ctx.stroke();
+ }
+}
+// draw a border
+function drawSquare(event: MouseEvent) {
+ let x = event.offsetX;
+ let y = event.offsetY;
+ let i = Math.floor(y / cellSize);
+ let j = Math.floor(x / cellSize);
+ // ctx.fillStyle = '#19A7CE';
+ // ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
+ matrix[i][j] = 2;
+ // printMatrix(matrix);
+ drawSquareWithAnimation(i, j, '#1239C6');
+}
+// clear the canvas
+function clearCanvas() {
+ ctx.clearRect(0, 0, width, height);
+ drawGrid();
+ clearMatrix(matrix);
+ prevStart = [-1, -1];
+}
+// clear the path and keep the wall and start and end point
+// function clearPath() {
+// ctx.clearRect(0, 0, width, height);
+// drawGrid();
+// for (let i in matrix) {
+// for (let j in matrix[i]) {
+// if (matrix[i][j] === 2) {
+// ctx.fillStyle = '#1239C6';
+// ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
+// } else if (matrix[i][j] === 1) {
+// ctx.fillStyle = '#43c943';
+// ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
+// } else if (matrix[i][j] === 3) {
+// ctx.fillStyle = '#ff4d4d';
+// ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
+// }
+// }
+// }
+// }
+// delete a square
+function deleteSquare(event: MouseEvent) {
+ let x = event.offsetX;
+ let y = event.offsetY;
+ let i = Math.floor(y / cellSize);
+ let j = Math.floor(x / cellSize);
+ ctx.clearRect(j * cellSize, i * cellSize, cellSize, cellSize);
+ // draw the line again
+ ctx.beginPath();
+ ctx.moveTo(j * cellSize, i * cellSize);
+ ctx.lineTo((j + 1) * cellSize, i * cellSize);
+ // draw 4 edges of the square
+ ctx.moveTo((j + 1) * cellSize, i * cellSize);
+ ctx.lineTo((j + 1) * cellSize, (i + 1) * cellSize);
+ ctx.moveTo((j + 1) * cellSize, (i + 1) * cellSize);
+ ctx.lineTo(j * cellSize, (i + 1) * cellSize);
+ ctx.moveTo(j * cellSize, (i + 1) * cellSize);
+ ctx.lineTo(j * cellSize, i * cellSize);
+ ctx.moveTo(j * cellSize, i * cellSize);
+ ctx.stroke();
+ matrix[i][j] = 0;
+}
+function initPoint(event: MouseEvent) {
+ let x = Math.floor(event.offsetY / cellSize);
+ let y = Math.floor(event.offsetX / cellSize);
+ // delete the previous start point
+ if (prevStart[0] !== -1 && prevStart[1] !== -1) {
+ ctx.clearRect(prevStart[1] * cellSize, prevStart[0] * cellSize, cellSize, cellSize);
+ // draw the new start point
+ matrix[x][y] = 1;
+ updateAdjacencyList();
+
+ ctx.fillStyle = '#43c943';
+ ctx.fillRect(y * cellSize, x * cellSize, cellSize, cellSize);
+ console.log(prevStart);
+ // draw the line again
+ ctx.beginPath();
+ ctx.moveTo(prevStart[1] * cellSize, prevStart[0] * cellSize);
+ ctx.lineTo((prevStart[1] + 1) * cellSize, prevStart[0] * cellSize);
+ // draw 4 edges of the square
+ ctx.moveTo((prevStart[1] + 1) * cellSize, prevStart[0] * cellSize);
+ ctx.lineTo((prevStart[1] + 1) * cellSize, (prevStart[0] + 1) * cellSize);
+ ctx.moveTo((prevStart[1] + 1) * cellSize, (prevStart[0] + 1) * cellSize);
+ ctx.lineTo(prevStart[1] * cellSize, (prevStart[0] + 1) * cellSize);
+ ctx.moveTo(prevStart[1] * cellSize, (prevStart[0] + 1) * cellSize);
+ ctx.lineTo(prevStart[1] * cellSize, prevStart[0] * cellSize);
+ ctx.moveTo(prevStart[1] * cellSize, prevStart[0] * cellSize);
+ ctx.stroke();
+ return [x, y];
+ } else {
+ return [0,0];
+ }
+}
+function setEndPoint(event: MouseEvent) {
+ let x = Math.floor(event.offsetY / cellSize);
+ let y = Math.floor(event.offsetX / cellSize);
+
+ // delete the previous start point
+ if (prevEnd[0] !== -1 && prevEnd[1] !== -1) {
+ ctx.clearRect(prevEnd[1] * cellSize, prevEnd[0] * cellSize, cellSize, cellSize);
+ // draw the new start point
+ matrix[x][y] = 3;
+ updateAdjacencyList();
+ ctx.fillStyle = '#ff4d4d';
+ ctx.fillRect(y * cellSize, x * cellSize, cellSize, cellSize);
+ console.log(prevEnd);
+ // draw the line again
+ ctx.beginPath();
+ ctx.moveTo(prevEnd[1] * cellSize, prevEnd[0] * cellSize);
+ ctx.lineTo((prevEnd[1] + 1) * cellSize, prevEnd[0] * cellSize);
+ // draw 4 edges of the square
+ ctx.moveTo((prevEnd[1] + 1) * cellSize, prevEnd[0] * cellSize);
+ ctx.lineTo((prevEnd[1] + 1) * cellSize, (prevEnd[0] + 1) * cellSize);
+ ctx.moveTo((prevEnd[1] + 1) * cellSize, (prevEnd[0] + 1) * cellSize);
+ ctx.lineTo(prevEnd[1] * cellSize, (prevEnd[0] + 1) * cellSize);
+ ctx.moveTo(prevEnd[1] * cellSize, (prevEnd[0] + 1) * cellSize);
+ ctx.lineTo(prevEnd[1] * cellSize, prevEnd[0] * cellSize);
+ ctx.moveTo(prevEnd[1] * cellSize, prevEnd[0] * cellSize);
+ ctx.stroke();
+ return [x, y];
+ } else {
+ return [0,0];
+ }
+}
+// check if the matrix is set begin node and end node
+function checkMatrix(matrix: number[][]): boolean {
+ let count = 0;
+ for (let i in matrix) {
+ for (let j in matrix[i]) {
+ if (matrix[i][j] === 1 || matrix[i][j] === 3) {
+ count++;
+ }
+ }
+ }
+ if (count === 2) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+function updateAdjacencyList() {
+ adjList = getAdjacencyList(matrix);
+ startNode = getSourceNode(matrix);
+ endNode = getEndNode(matrix);
+}
+
+function resetAdjacencyList() {
+ for (let i in adjList) {
+ for (let j in adjList[i]) {
+ adjList[i][j] = 0;
+ }
+ }
+ startNode = -1;
+ endNode = -1;
+}
+
+export function initPath(node : number){
+ drawSquareWithAnimation(getNodeXCoordinates(node), getNodeYCoordinates(node), '#FFEA00');
+}
+export function initPrevPath(node : number){
+ drawSquareWithAnimation(getNodeXCoordinates(node), getNodeYCoordinates(node), '#33A3FF');
+}
+
+// Add event listeners
+clear.addEventListener('click', ()=>{
+ clearCanvas()
+ resetAdjacencyList();
+});
+begin.addEventListener('click', (event)=>{
+ isStart = true;
+ console.log('begin');
+});
+wall.addEventListener('click', (event)=>{
+ isStart = false;
+ isEnd = false;
+});
+
+end.addEventListener('click', ()=>{
+ console.log(endNode);
+ updateAdjacencyList();
+ console.log('set-end');
+ isStart = false;
+ isEnd = true;
+});
+start.addEventListener('click', ()=>{
+ if (checkMatrix(matrix)) {
+ updateAdjacencyList();
+ // switch case
+ if (selectedAlgorithm !== '') {
+ switch (selectedAlgorithm) {
+ case 'bfs': {
+ getShortestDistanceBFS(adjList, startNode, endNode);matrix[prevStart[0]][prevStart[1]] = 1;
+ break;
+ }
+ case 'dfs': {
+ getPathDFS(adjList, startNode, endNode);
+ break;
+ }
+ // case 'aStar': {getShortestPathAStar(adjList); break;}
+ default: {
+ createText("Please select an algorithm","red");
+ break;
+ }
+ }
+ } else {
+ createText("Please select an algorithms","red");
+ }
+ } else {
+ createText('Please set the start and end point',"red");
+ }
+});
+select.addEventListener('change', (event)=>{
+ const target = event.target as HTMLSelectElement;
+ selectedAlgorithm = target.value;
+ console.log(selectedAlgorithm);
+});
+canvas.addEventListener('mousedown', (event)=>{
+ if (event.button === 0 && !isStart && !isEnd) {
+ isDragging = true;
+ drawSquare(event);
+ } else if (event.button === 0 && isStart) {
+ prevStart = initPoint(event);
+ } else if (event.button === 0 && isEnd === true) {
+ console.log('end');
+ prevEnd = setEndPoint(event);
+ }
+});
+canvas.addEventListener('mousemove', (event)=>{
+ if(isDragging && event.button === 0){
+ drawSquare(event) ;
+ }
+})
+canvas.addEventListener('mouseup', ()=>{
+ isDragging = false;
+});
+canvas.addEventListener('contextmenu', (event)=>{
+ deleteSquare(event);
+});
+
+
+// Run the functions once the page is loaded
+(
+ function once(arrays: number[][]) {
+ // console.log(cellSize);
+ // console.log(width, height);
+ // console.log(rows, cols);
+ createMatrix(arrays);
+ printMatrix(arrays);
+ drawGrid();
+ }
+)(matrix);
+
+function featureEnabling(bool:boolean):void{
+ if(!bool){
+ clear.setAttribute('disabled','true');
+ begin.setAttribute('disabled','true');
+ wall.setAttribute('disabled','true');
+ end.setAttribute('disabled','true');
+ start.setAttribute('disabled','true');
+ select.setAttribute('disabled','true');
+ }else{
+ clear.removeAttribute('disabled');
+ begin.removeAttribute('disabled');
+ wall.removeAttribute('disabled');
+ end.removeAttribute('disabled');
+ start.removeAttribute('disabled');
+ select.removeAttribute('disabled');
+ }
+}
+
+
+export {matrix, cellSize, width, height, rows, cols, ctx, canvas};
diff --git a/src/graph/code/pathFindingAlgorithms/Maze.ts b/src/graph/code/pathFindingAlgorithms/Maze.ts
new file mode 100644
index 0000000..23c06ce
--- /dev/null
+++ b/src/graph/code/pathFindingAlgorithms/Maze.ts
@@ -0,0 +1,65 @@
+export function generateMazeUsingKruskal(maze: number[][]): number[][] {
+ const rows = maze.length;
+ const cols = maze[0].length;
+
+ // Create a list of all walls in the maze
+ const walls: [number, number, number, number][] = [];
+ for (let i = 0; i < rows; i++) {
+ for (let j = 0; j < cols; j++) {
+ // Add vertical walls
+ if (j < cols - 1) {
+ walls.push([i, j, i, j + 1]);
+ }
+ // Add horizontal walls
+ if (i < rows - 1) {
+ walls.push([i, j, i + 1, j]);
+ }
+ }
+ }
+
+ // Create a disjoint set to track the connected components
+ const parent: number[] = [];
+ for (let i = 0; i < rows * cols; i++) {
+ parent[i] = i;
+ }
+
+ // Helper function to find the parent of a set
+ function find(parent: number[], i: number): number {
+ if (parent[i] !== i) {
+ parent[i] = find(parent, parent[i]);
+ }
+ return parent[i];
+ }
+
+ // Helper function to join two sets
+ function union(parent: number[], i: number, j: number): void {
+ const rootA = find(parent, i);
+ const rootB = find(parent, j);
+ parent[rootA] = rootB;
+ }
+
+ // Randomize the order of walls
+ walls.sort(() => Math.random() - 0.5);
+
+ // Process each wall and remove it if it connects two different sets
+ for (const wall of walls) {
+ const [x1, y1, x2, y2] = wall;
+ const indexA = x1 * cols + y1;
+ const indexB = x2 * cols + y2;
+ const rootA = find(parent, indexA);
+ const rootB = find(parent, indexB);
+
+ if (rootA !== rootB) {
+ union(parent, rootA, rootB);
+ maze[x1][y1] = 0;
+ maze[x2][y2] = 0;
+ }
+ }
+
+ // Set the beginning node (start) as 1 and the end node as 3
+ maze[0][0] = 1;
+ maze[rows - 1][cols - 1] = 3;
+
+ return maze;
+}
+
\ No newline at end of file
diff --git a/src/graph/code/pathFindingAlgorithms/bfs.ts b/src/graph/code/pathFindingAlgorithms/bfs.ts
new file mode 100644
index 0000000..749857d
--- /dev/null
+++ b/src/graph/code/pathFindingAlgorithms/bfs.ts
@@ -0,0 +1,69 @@
+//BFS algorithm
+import {initPath,delay} from "../main.js";
+import {initPrevPath} from "../main.js";
+import {delayRender} from "./utility.js";
+import createText from "../popup.js";
+
+
+//Helper function that uses BFS to transverse the graph
+export async function bfs(adj: number[][],
+ src: number,
+ dest: number,
+ v: number,
+ prev: number[],
+ dist: number[]){
+ let queue: number[] = [];
+ let visited = new Array(v);
+
+ for (let i = 0; i < v; i++){
+ visited[i] = false;
+ dist[i] = Number.MAX_VALUE;
+ prev[i] = -1;
+ }
+
+ visited[src] = true;
+ dist[src] = 0;
+ queue.push(src);
+
+ while(queue.length > 0){
+ let u = queue[0];
+ queue.shift();
+ for (let i in adj[u]){
+ if (visited[adj[u][i]] == false){
+ visited[adj[u][i]] = true;
+ dist[adj[u][i]] = dist[u] + 1;
+ prev[adj[u][i]] = u;
+ queue.push(adj[u][i]);
+ initPrevPath(adj[u][i]);
+ await delayRender(delay);
+ if (adj[u][i] == dest){
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+//Helper function to backtrack the path and print the shortest path
+export async function getShortestDistanceBFS(adj: number[][], src: number, dest: number){
+ let v = adj.length;
+ let prev = new Array(v).fill(0);
+ let dist = new Array(v).fill(0);
+
+ if (!await bfs(adj, src, dest, v, prev, dist)){
+ createText('Source and destination vertex is not connected!',"red");
+ }
+
+ let path = [];
+ let crawl = dest;
+
+ path.push(crawl);
+ while (prev[crawl] != -1) {
+ path.push(prev[crawl]);
+ crawl = prev[crawl];
+ }
+ for (let i = path.length - 1; i >= 0; i--){
+ await delayRender(4*delay);
+ initPath(path[i]);
+ }
+}
diff --git a/src/graph/code/pathFindingAlgorithms/dfs.ts b/src/graph/code/pathFindingAlgorithms/dfs.ts
new file mode 100644
index 0000000..74ea892
--- /dev/null
+++ b/src/graph/code/pathFindingAlgorithms/dfs.ts
@@ -0,0 +1,49 @@
+import {initPath,delay} from "../main.js";
+import {initPrevPath} from "../main.js";
+import createText from "../popup.js";
+import {delayRender} from "./utility.js";
+
+export async function drawPath(stack: number[]) {
+ for(let i in stack) {
+ initPath(stack[i]);
+ await delayRender(delay);
+ }
+}
+
+export async function DFS(visited: boolean[],
+ adjList: number[][],
+ source: number,
+ destination: number,
+ stack: number[]) {
+ visited[source] = true;
+ stack.push(source);
+
+ if (source == destination) {
+ await drawPath(stack);
+ return;
+ }
+
+ for (let i in adjList[source]) {
+ if (!visited[adjList[source][i]]) {
+ await DFS(visited, adjList, adjList[source][i], destination, stack);
+ }
+ }
+ stack.pop();
+}
+
+export async function getPathDFS(adjList: number[][], source: number, destination: number) {
+ let n = adjList.length;
+ let visited: boolean[] = new Array(n + 1);
+ let stack: number[] = [];
+
+ for (let i = 0; i < n + 1; i++) {
+ visited[i] = false;
+ }
+
+ await DFS(visited, adjList, source, destination, stack);
+
+ if (stack.length === 0) {
+ createText("No path found!","red");
+ }
+}
+
diff --git a/src/graph/code/pathFindingAlgorithms/dijkstra.js b/src/graph/code/pathFindingAlgorithms/dijkstra.js
new file mode 100644
index 0000000..d33e07b
--- /dev/null
+++ b/src/graph/code/pathFindingAlgorithms/dijkstra.js
@@ -0,0 +1,72 @@
+let V = 5;
+
+function minDistance(dist, sptSet) {
+ let min = Number.MAX_VALUE;
+ let min_index = -1;
+
+ for (let v = 0; v < V; v++) {
+ if (sptSet[v] == false && dist[v] <= min) {
+ min = dist[v];
+ min_index = v;
+ }
+ }
+ return min_index;
+}
+
+function printSolution(dist) {
+ console.log("Vertex \t\t Distance from Source");
+ for (let i = 0; i < V; i++) {
+ console.log(i + " \t\t " + dist[i]);
+ }
+}
+function printShortestPath(prev, src, des){
+ let path = new Set;
+ let crawl = des;
+
+ path.add(crawl);
+ while(prev[crawl] != src){
+ path.add(prev[crawl]);
+ crawl = prev[crawl];
+ }
+ path.add(src);
+
+ path = Array.from(path).reverse();
+ console.log("Shortest path from " + src + " to " + des + " is: ")
+ console.log(path);
+}
+
+function dijkstra(graph, src){
+ let dist = new Array(V);
+ let sptSet = new Array(V);
+ let prev = new Array(V);
+
+ for (let i = 0; i < V; i++){
+ dist[i] = Number.MAX_VALUE;
+ sptSet[i] = false;
+ }
+
+ dist[src] = 0;
+
+ for (let count = 0; count < V; count++){
+ let u = minDistance(dist, sptSet);
+ sptSet[u] = true;
+ for (let v = 0; v < V; v++){
+ if (!sptSet[v] && graph[u][v] != 0 &&
+ dist[u] != Number.MAX_VALUE &&
+ dist[u] + graph[u][v] < dist[v]){
+ dist[v] = dist[u] + graph[u][v];
+ prev[v] = u;
+ }
+ }
+ }
+ printSolution(dist);
+ printShortestPath(prev, src, 4);
+}
+let graph = [
+ [0, 6, 0, 1, 0],
+ [6, 0, 5, 2, 2],
+ [0, 5, 0, 0, 5],
+ [1, 2, 0, 0, 1],
+ [0, 2, 5, 1, 0]
+]
+dijkstra(graph, 0);
diff --git a/src/graph/code/pathFindingAlgorithms/utility.ts b/src/graph/code/pathFindingAlgorithms/utility.ts
new file mode 100644
index 0000000..26b2f67
--- /dev/null
+++ b/src/graph/code/pathFindingAlgorithms/utility.ts
@@ -0,0 +1,132 @@
+let vertexIndex: number[][] = [];
+
+export function addEdge(adj: number[][], u: number, v: number){
+ adj[u].push(v);
+}
+
+//Check if a vertex is in the grid, if it is return its index in the vertexIndex array
+//If it is not in the grid, return -1
+export function findVertexIndex (vertexIndex: number[][], row: number, col: number){
+ for (let i = 0; i < vertexIndex.length; i++){
+ if (vertexIndex[i][0] === row && vertexIndex[i][1] === col){
+ return i;
+ }
+ }
+ return -1;
+}
+
+export function resetVertexIndex(){
+ vertexIndex = [];
+}
+
+//# is wall, . is empty, A is start, B is destination
+//every . and A or B are considered a vertex
+export default function getAdjacencyList (graph: number[][]){
+ //number of vertices
+ let v: number = 0;
+
+ //adjacency list
+ let adj: number[][] = new Array(graph.length * graph[0].length);
+
+ resetVertexIndex();
+
+ for (let i = 0; i < graph.length; i++){
+ for (let j = 0; j < graph[i].length; j++){
+ //If the current element is not a wall, add it to the vertexIndex array
+ //and increment the number of vertices
+ if (graph[i][j] != 2){
+ vertexIndex[v] = [];
+ vertexIndex[v].push(i);
+ vertexIndex[v].push(j);
+ adj[v] = [];
+ v++;
+ }
+ }
+ }
+
+ //Check for each vertex if there is a path to another vertex
+ //if there is, add an edge between them
+ for (let i = 0; i < vertexIndex.length; i++){
+ let row: number = vertexIndex[i][0];
+ let col: number = vertexIndex[i][1];
+ //check if there is a path to the vertex above
+ if (findVertexIndex(vertexIndex, row - 1, col) !== -1){
+ addEdge(adj, i, findVertexIndex(vertexIndex, row - 1, col));
+ }
+ //check if there is a path to the vertex below
+ if (findVertexIndex(vertexIndex, row + 1, col) !== -1){
+ addEdge(adj, i, findVertexIndex(vertexIndex, row + 1, col));
+ }
+ //check if there is a path to the vertex to the left
+ if (findVertexIndex(vertexIndex, row, col - 1) !== -1){
+ addEdge(adj, i, findVertexIndex(vertexIndex, row, col - 1));
+ }
+ //check if there is a path to the vertex to the right
+ if (findVertexIndex(vertexIndex, row, col + 1) !== -1){
+ addEdge(adj, i, findVertexIndex(vertexIndex, row, col + 1));
+ }
+ }
+ return adj;
+}
+export function getSourceNode (graph: number[][]): number{
+ let v: number = 0;
+ for (let i = 0; i < graph.length; i++){
+ for (let j = 0; j < graph[i].length; j++){
+ if (graph[i][j] != 2){
+ if (graph[i][j] == 1){
+ return v;
+ }
+ v++;
+ }
+ }
+ }
+ return -1;
+}
+
+export function getEndNode(graph: number[][]): number{
+ let v: number = 0;
+ for (let i = 0; i < graph.length; i++){
+ for (let j = 0; j < graph[i].length; j++){
+ if (graph[i][j] != 2){
+ if (graph[i][j] == 3){
+ return v;
+ }
+ v++;
+ }
+ }
+ }
+ return -1;
+}
+export function getNodeXCoordinates(v: number): number{
+ for (let i = 0; i < vertexIndex.length; i++){
+ if (i === v){
+ return vertexIndex[i][0];
+ }
+ }
+ return -1;
+}
+export function getNodeYCoordinates(v: number): number{
+ for (let i = 0; i < vertexIndex.length; i++){
+ if (i === v){
+ return vertexIndex[i][1];
+ }
+ }
+ return -1;
+}
+
+export function delayRender(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+
+// let graph: string[][] = [
+// ['#', '#', '#', '#', '#'],
+// ['#', 'A', '.', '.', '#'],
+// ['#', '#', '#', '.', '#'],
+// ['#', '.', '#', '.', '#'],
+// ['#', '.', '.', '.', '#'],
+// ['#', '#', 'B', '#', '#']
+// ];
+// let adj: number[][] = [];
+//
+// fromGridToList(graph, adj);
\ No newline at end of file
diff --git a/src/graph/code/popup.ts b/src/graph/code/popup.ts
new file mode 100644
index 0000000..c87795e
--- /dev/null
+++ b/src/graph/code/popup.ts
@@ -0,0 +1,76 @@
+const warn = document.getElementsByClassName('warning')[0] as HTMLDivElement;
+const FADE_IN_TIME = 1000; //ms
+const FADE_OUT_TIME = 1000;
+const APPEAR_TIME = 8000;
+
+
+export default function createText(message: string, color: string) {
+ warn.textContent = message;
+ warn.style.color = color;
+ warn.style.opacity = '0';
+ warn.style.top = '-50px';
+
+ fadeIn(warn, FADE_IN_TIME);
+
+ // Fade out after 10 seconds with 2 second fade-out time
+ setTimeout(function() {
+ fadeOut(warn, FADE_OUT_TIME);
+ }, APPEAR_TIME);
+}
+
+function fadeIn(element: HTMLElement, duration: number) {
+ var interval = 10;
+ var opacity = 0;
+ var targetOpacity = 1;
+ var delta = (interval / duration) * (targetOpacity - opacity);
+
+ var timer = setInterval(function() {
+ opacity += delta;
+ element.style.opacity = String(opacity);
+
+ if (opacity >= targetOpacity) {
+ clearInterval(timer);
+ animate(element, 'rise', FADE_IN_TIME);
+ }
+ }, interval);
+}
+
+function fadeOut(element: HTMLElement, duration: number) {
+ var interval = 10;
+ var opacity = 1;
+ var targetOpacity = 0;
+ var delta = (interval / duration) * (opacity - targetOpacity);
+
+ var timer = setInterval(function() {
+ opacity -= delta;
+ element.style.opacity = String(opacity);
+
+ if (opacity <= targetOpacity) {
+ clearInterval(timer);
+ animate(element, 'fall', FADE_OUT_TIME);
+ }
+ }, interval);
+}
+
+function animate(element: HTMLElement, direction: string, duration: number) {
+ var interval = 10;
+ var start = parseInt(element.style.top);
+ var end:number;
+
+ if (direction === 'rise') {
+ end = 0;
+ } else {
+ end = -50;
+ }
+
+ var delta = (interval / duration) * (end - start);
+
+ var timer = setInterval(function() {
+ start += delta;
+ element.style.top = String(start) + 'px';
+
+ if ((direction === 'rise' && start >= end) || (direction === 'fall' && start <= end)) {
+ clearInterval(timer);
+ }
+ }, interval);
+}
diff --git a/src/graph/css-style/icons/begin.png b/src/graph/css-style/icons/begin.png
new file mode 100644
index 0000000..582a765
Binary files /dev/null and b/src/graph/css-style/icons/begin.png differ
diff --git a/src/graph/css-style/icons/end.png b/src/graph/css-style/icons/end.png
new file mode 100644
index 0000000..43893db
Binary files /dev/null and b/src/graph/css-style/icons/end.png differ
diff --git a/src/graph/css-style/icons/eraser.png b/src/graph/css-style/icons/eraser.png
new file mode 100644
index 0000000..3aaed5c
Binary files /dev/null and b/src/graph/css-style/icons/eraser.png differ
diff --git a/src/graph/css-style/icons/start.png b/src/graph/css-style/icons/start.png
new file mode 100644
index 0000000..4f582f7
Binary files /dev/null and b/src/graph/css-style/icons/start.png differ
diff --git a/src/graph/css-style/icons/wall.png b/src/graph/css-style/icons/wall.png
new file mode 100644
index 0000000..5e6c975
Binary files /dev/null and b/src/graph/css-style/icons/wall.png differ
diff --git a/src/graph/css-style/styles.css b/src/graph/css-style/styles.css
new file mode 100644
index 0000000..313e9d6
--- /dev/null
+++ b/src/graph/css-style/styles.css
@@ -0,0 +1,222 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ height: 100%;
+ width: 100%;
+ font-family: "Gill Sans", sans-serif;
+ background-color: #e3f2fd;
+}
+html {
+ height: 100%;
+ width: 100%;
+ font-size: 16px;
+}
+header{
+ background: #5a909c;
+ width: 100%;
+ height: 10%;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+}
+
+main{
+ height: 90%;
+ width: 100%;
+}
+
+.algorithms {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+}
+#h1 {
+ text-decoration: none;
+ cursor: pointer;
+}
+.algorithms > a {
+ padding: 10px;
+ transition: 0.3s;
+ text-decoration: none;
+ font-family: "Gill Sans", sans-serif;
+ font-weight: bold;
+ color: #19A7CE;
+ font-size: 1.5rem;
+ background-color: #F9FBE7;
+ border-radius: 5px;
+}
+a:hover {
+ color: black;
+ text-decoration: none;
+ cursor: pointer;
+}
+h1 {
+ font-family: "Gill Sans", sans-serif;
+ color: #F9FBE7;
+ font-size: 2.5rem;
+ font-weight: bold;
+}
+
+
+.control-panel{
+ position: relative;
+ margin-top: 5px;
+ height: 20%;
+ margin-left: 1vh;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ justify-content: center;
+}
+
+.box{
+ max-width: 300px;
+ height: 120px;
+ background: white;
+ padding: 20px 20px 30px 30px;
+ border-radius: 5px;
+ box-shadow: 0 0 5px rgba(0,0,0,0.5);
+ align-items: center;
+ }
+
+.box .content{
+ font-size: 20px;
+ font-weight: 300;
+ color: #5a909c;;
+ display: flex;
+ justify-content: center;
+ }
+
+.box .slider{
+ height: 40px;
+ width: 200px;
+ display: flex;
+ align-items: center;
+ margin-right: 15px;
+ }
+
+.box .slider input{
+ height: 10px;
+ width: 100%;
+ -webkit-appearance: none;
+ outline: none;
+ background: #f2f2f2;
+ box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2);
+ }
+
+.box .value{
+ width: 100px;
+ text-align: center;
+}
+
+.control-panel .right .lower{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 10px;
+}
+
+select {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ -ms-appearance: none;
+ appearance: none;
+ outline: 0;
+ border: 0;
+ background: #ffffff;
+ background-image: none;
+ padding: 20px 20px 30px 30px;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
+ flex: 1;
+ padding: 0 .5em;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ font-size: 1.4rem;
+ font-weight: bold;
+ color: #5a909c;
+}
+
+select::-ms-expand {
+ display: none;
+}
+
+.select {
+ position: relative;
+ display: flex;
+ width: 17em;
+ height: 3.5em;
+ line-height: 3;
+ background: #D3D3D3;
+ overflow: hidden;
+ border-radius: .25em;
+}
+.select::after {
+ content: '\25BC';
+ position: absolute;
+ top: -12px;
+ right: 0;
+ padding: 1em 1em;
+ background: #D3D3D3;
+ cursor:pointer;
+ pointer-events:none;
+ transition:.25s all ease;
+}
+.select:hover::after {
+color: #7ce8ff;
+}
+
+button {
+ background: #5a909c;
+ width: 50px;
+ height: 50px;
+ padding: 10px 15px;
+ border: none;
+ border-radius: 3px;
+ font-size: 16px;
+ color: #fff;
+ cursor: pointer;
+ text-transform: uppercase;
+ transition: background-color 0.5s ease;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 2px;
+ }
+ button:hover {
+ cursor: pointer;
+ box-shadow: 0 0 10px #737b7e;
+ opacity: 0.8;
+ }
+
+ button img{
+ width: 40px;
+ height: 40px;
+ }
+
+.container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ height: 80%;
+ width: 100%;
+}
+#canvas {
+ border : 1px solid black;
+}
+
+#upper{
+ display: flex;
+}
+
+.warning{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/graph/css-style/tooltip.css b/src/graph/css-style/tooltip.css
new file mode 100644
index 0000000..c3004bb
--- /dev/null
+++ b/src/graph/css-style/tooltip.css
@@ -0,0 +1,44 @@
+[data-tooltip] {
+ position: relative;
+ z-index: 2;
+ cursor: pointer;
+}
+
+/* Hide the tooltip content by default */
+[data-tooltip]:before,
+[data-tooltip]:after {
+ visibility: hidden;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=0);
+ opacity: 0;
+ pointer-events: none;
+ z-index: 2;
+}
+
+/* Position tooltip above the element */
+[data-tooltip]:before {
+ position: absolute;
+ bottom: 100%;
+ padding: 7px;
+ width: 60px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ background-color: #ffffff;
+ background-color: hsla(0, 0%, 100%, 0.9);
+ color: #5a909c;;
+ content: attr(data-tooltip);
+ text-align: center;
+ font-size: 14px;
+ line-height: 1.2;
+ z-index: 2;
+}
+
+/* Show tooltip content on hover */
+[data-tooltip]:hover:before,
+[data-tooltip]:hover:after {
+ visibility: visible;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+ filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=100);
+ opacity: 1;
+}
\ No newline at end of file
diff --git a/src/graph/index.html b/src/graph/index.html
new file mode 100644
index 0000000..3c09e01
--- /dev/null
+++ b/src/graph/index.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Algorithms Visualizer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Book.ts b/src/sort/code/Book.ts
similarity index 100%
rename from src/Book.ts
rename to src/sort/code/Book.ts
diff --git a/src/main.ts b/src/sort/code/main.ts
similarity index 52%
rename from src/main.ts
rename to src/sort/code/main.ts
index b307b8f..3c8a0b1 100644
--- a/src/main.ts
+++ b/src/sort/code/main.ts
@@ -1,13 +1,16 @@
-import Book from './Book';
-import bubbleSort from './sorting/BubbleSort';
-import selectionSort from './sorting/SelectionSort';
-import insertionSort from './sorting/InsertionSort';
+import Book from './Book.js';
+import createText from './popup.js';
+import bubbleSort from './sortingAlgorithms/BubbleSort.js';
+import selectionSort from './sortingAlgorithms/SelectionSort.js';
+import insertionSort from './sortingAlgorithms/InsertionSort.js';
+import quickSort from './sortingAlgorithms/QuickSort.js';
+// import mergeSort from './sortingAlgorithms/MergeSort';
const bookshelfContainer = document.getElementById('bookshelf_container')!;
-const above = document.getElementById('augmented_container_above')!;
+const above = document.getElementById('augmented_container')!;
//DEFAULT
const DEFAULT_NUM_BOOKS = 20;
-const DEFAULT_TIMEOUT = 500;
+const DEFAULT_TIMEOUT = 100;
type SortingAlgo = {
name: string;
@@ -27,6 +30,14 @@ const sortingBooks: Record = {
name: 'Insertion Sort',
algo: insertionSort,
},
+ quick: {
+ name: 'Quick Sort',
+ algo: quickSort,
+ },
+ // merge:{
+ // name: 'Merge Sort',
+ // algo: mergeSort,
+ // }
};
let books: number = DEFAULT_NUM_BOOKS;
@@ -36,56 +47,49 @@ let timeOut: number = DEFAULT_TIMEOUT;
let selectedAlgo: string = ""
createBookshelf();
-//DOM elements
-const goButton = document.getElementById('go-button')!;
-const randomButton = document.getElementById('random-button')!;
-const resetButton = document.getElementById('reset-button')!;
-const worstCaseButton = document.getElementById('worst-case-button')!;
-const sortingAlgoSelection = document.getElementById('format') as HTMLSelectElement;
-const sliderBooks = document.querySelector('.slider_books input') as HTMLInputElement;
-const valueBooks = document.querySelector('.value_books') as HTMLDivElement;
-const sliderTime = document.querySelector('.slider_time input') as HTMLInputElement;
-const valueTime = document.querySelector('.value_time') as HTMLDivElement;
-
-//Add event listeners
-
-goButton.addEventListener('click', () => {
+//Add event listeners to DOM elements
+document.getElementById('go-button')!.addEventListener('click', () => {
go();
});
-randomButton.addEventListener('click', () => {
+document.getElementById('random-button')!.addEventListener('click', () => {
random();
});
-resetButton.addEventListener('click', () => {
+document.getElementById('reset-button')!.addEventListener('click', () => {
reset()
});
-worstCaseButton.addEventListener('click', () => {
+document.getElementById('worst-case-button')!.addEventListener('click', () => {
createWorstBookshelf();
});
-sliderBooks.addEventListener('input', function() {
+document.querySelector('#slider_books input')!.addEventListener('input', function(this: HTMLInputElement) {
books = Number(this.value);
- valueBooks.textContent = this.value +" books.";
+ document.querySelector('#value_books')!.textContent = this.value +" books.";
createBookshelf();
});
-sliderTime.addEventListener('input', function() {
+document.querySelector('#slider_time input')!.addEventListener('input', function(this: HTMLInputElement) {
timeOut = Number(this.value);
- valueTime.textContent = this.value +"ms";
+ document.querySelector('#value_time')!.textContent = this.value +"ms";
});
-sortingAlgoSelection.addEventListener('change', () => {
- selectedAlgo = sortingAlgoSelection.value;
+document.getElementById('format')!.addEventListener('change', function(this: HTMLInputElement) {
+ selectedAlgo = this.value;
});
+
//Helper functions
function go(){
const copy = [...bookshelf];
const sort = sortingBooks[selectedAlgo];
- if (sort==undefined) alert("Please select a sorting algorithm");
+ if (sort==undefined){
+ createText("Please select an algorithm.", "red");
+ return;
+ }
+ featureEnabling(false);
const moves = sort.algo(copy);
animate(moves);
}
@@ -139,12 +143,7 @@ function visualizeBookshelf(move?: { indices: number[]; type: string }) {
bookshelfContainer.innerHTML = '';
above.innerHTML = '';
for (let i = 0; i < bookshelf.length; i++) {
- const book = document.createElement('div');
- const bookName = document.createElement('span');
- book.classList.add('book');
- book.style.backgroundColor = bookshelf[i].color;
- bookName.textContent = bookshelf[i].name;
- book.appendChild(bookName);
+ const book = visualizeBook(bookshelf[i]);
let bookAbove = document.createElement('div');
if (move && move.indices.includes(i)) {
book.style.opacity = '0';
@@ -163,6 +162,8 @@ function visualizeBookshelf(move?: { indices: number[]; type: string }) {
function animate(moves: Move[]) {
if (moves.length == 0) {
visualizeBookshelf();
+ createText("The bookshelf is sorted!", "lime");
+ featureEnabling(true);
return;
}
const move = moves.shift()!;
@@ -172,14 +173,42 @@ function animate(moves: Move[]) {
[bookshelf[i], bookshelf[j]] = [bookshelf[j], bookshelf[i]];
}
visualizeBookshelf(move);
+ // setTimeout(function () {
+ // visualizeBookshelf();
+ // }, timeOut);
setTimeout(function () {
animate(moves);
}, timeOut);
}
+function visualizeBook(book: Book){
+ const newBook = document.createElement('div');
+ newBook.classList.add('book');
+ newBook.style.backgroundColor = book.color;
+ const newBookName = document.createElement('span');
+ newBookName.textContent = book.name;
+ newBook.appendChild(newBookName);
+ return newBook;
+}
+
function displayBookshelfConsole(bookshelf: Book[]) {
const bookNames = bookshelf.map((book) => book.name);
console.log(bookNames.join(", "));
}
displayBookshelfConsole(bookshelf);
-
+
+function featureEnabling(bool:boolean):void{
+ if (!bool){
+ (document.getElementById('go-button') as HTMLButtonElement).setAttribute('disabled', String(bool));
+ (document.getElementById('random-button') as HTMLButtonElement).setAttribute('disabled', String(bool));
+ (document.getElementById('reset-button') as HTMLButtonElement).setAttribute('disabled', String(bool));
+ (document.getElementById('worst-case-button') as HTMLButtonElement).setAttribute('disabled', String(bool));
+ (document.getElementById('number_book_input') as HTMLInputElement).setAttribute('disabled', String(bool));
+ } else {
+ (document.getElementById('go-button') as HTMLButtonElement).removeAttribute('disabled');
+ (document.getElementById('random-button') as HTMLButtonElement).removeAttribute('disabled');
+ (document.getElementById('reset-button') as HTMLButtonElement).removeAttribute('disabled');
+ (document.getElementById('worst-case-button') as HTMLButtonElement).removeAttribute('disabled');
+ (document.getElementById('number_book_input') as HTMLInputElement).removeAttribute('disabled');
+ }
+}
diff --git a/src/sort/code/popup.ts b/src/sort/code/popup.ts
new file mode 100644
index 0000000..c87795e
--- /dev/null
+++ b/src/sort/code/popup.ts
@@ -0,0 +1,76 @@
+const warn = document.getElementsByClassName('warning')[0] as HTMLDivElement;
+const FADE_IN_TIME = 1000; //ms
+const FADE_OUT_TIME = 1000;
+const APPEAR_TIME = 8000;
+
+
+export default function createText(message: string, color: string) {
+ warn.textContent = message;
+ warn.style.color = color;
+ warn.style.opacity = '0';
+ warn.style.top = '-50px';
+
+ fadeIn(warn, FADE_IN_TIME);
+
+ // Fade out after 10 seconds with 2 second fade-out time
+ setTimeout(function() {
+ fadeOut(warn, FADE_OUT_TIME);
+ }, APPEAR_TIME);
+}
+
+function fadeIn(element: HTMLElement, duration: number) {
+ var interval = 10;
+ var opacity = 0;
+ var targetOpacity = 1;
+ var delta = (interval / duration) * (targetOpacity - opacity);
+
+ var timer = setInterval(function() {
+ opacity += delta;
+ element.style.opacity = String(opacity);
+
+ if (opacity >= targetOpacity) {
+ clearInterval(timer);
+ animate(element, 'rise', FADE_IN_TIME);
+ }
+ }, interval);
+}
+
+function fadeOut(element: HTMLElement, duration: number) {
+ var interval = 10;
+ var opacity = 1;
+ var targetOpacity = 0;
+ var delta = (interval / duration) * (opacity - targetOpacity);
+
+ var timer = setInterval(function() {
+ opacity -= delta;
+ element.style.opacity = String(opacity);
+
+ if (opacity <= targetOpacity) {
+ clearInterval(timer);
+ animate(element, 'fall', FADE_OUT_TIME);
+ }
+ }, interval);
+}
+
+function animate(element: HTMLElement, direction: string, duration: number) {
+ var interval = 10;
+ var start = parseInt(element.style.top);
+ var end:number;
+
+ if (direction === 'rise') {
+ end = 0;
+ } else {
+ end = -50;
+ }
+
+ var delta = (interval / duration) * (end - start);
+
+ var timer = setInterval(function() {
+ start += delta;
+ element.style.top = String(start) + 'px';
+
+ if ((direction === 'rise' && start >= end) || (direction === 'fall' && start <= end)) {
+ clearInterval(timer);
+ }
+ }, interval);
+}
diff --git a/src/sort/code/sortingAlgorithms/BubbleSort.ts b/src/sort/code/sortingAlgorithms/BubbleSort.ts
new file mode 100644
index 0000000..bb92b62
--- /dev/null
+++ b/src/sort/code/sortingAlgorithms/BubbleSort.ts
@@ -0,0 +1,30 @@
+import Book from '../Book';
+
+type Move = {
+ indices: number[],
+ type: string
+}
+
+export default function selectionSort(bookshelf: Book[]) {
+ const moves: Move[] = [];
+ var swapped:boolean;
+ do {
+ swapped = false;
+ for (let i = 0; i < bookshelf.length - 1; i++) {
+ moves.push({
+ indices: [i, i+1],
+ type: "compare"
+ });
+ if (bookshelf[i].name > bookshelf[i+1].name) {
+ swapped = true;
+ moves.push({
+ indices: [i, i+1],
+ type: "swap"
+ });
+ [bookshelf[i], bookshelf[i+1]] = [bookshelf[i+1], bookshelf[i]];
+ }
+ }
+ } while (swapped);
+
+ return moves;
+}
diff --git a/src/sorting/InsertionSort.ts b/src/sort/code/sortingAlgorithms/InsertionSort.ts
similarity index 76%
rename from src/sorting/InsertionSort.ts
rename to src/sort/code/sortingAlgorithms/InsertionSort.ts
index 182a242..f0bf32c 100644
--- a/src/sorting/InsertionSort.ts
+++ b/src/sort/code/sortingAlgorithms/InsertionSort.ts
@@ -5,24 +5,26 @@ type Move = {
type: string
}
-export default function insertionSort(bookshelf: Book[]) {
+export default function insertionSort(bookshelf: Book[]): Move[] {
const moves: Move[] = [];
+
for (let i = 1; i < bookshelf.length; i++) {
const current = bookshelf[i];
let j = i - 1;
while (j >= 0 && bookshelf[j].name > current.name) {
+ bookshelf[j + 1] = bookshelf[j];
moves.push({
indices: [j, j + 1],
- type: "shift"
+ type: "swap"
});
- bookshelf[j + 1] = bookshelf[j];
j--;
}
+ bookshelf[j + 1] = current;
moves.push({
- indices: [i, j + 1],
- type: "swap"
+ indices: [j + 1],
+ type: "compare"
});
- bookshelf[j + 1] = current;
}
+
return moves;
-}
+}
\ No newline at end of file
diff --git a/src/sort/code/sortingAlgorithms/MergeSort.ts b/src/sort/code/sortingAlgorithms/MergeSort.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/sort/code/sortingAlgorithms/QuickSort.ts b/src/sort/code/sortingAlgorithms/QuickSort.ts
new file mode 100644
index 0000000..cae62f9
--- /dev/null
+++ b/src/sort/code/sortingAlgorithms/QuickSort.ts
@@ -0,0 +1,60 @@
+import Book from '../Book';
+
+type Move = {
+ indices: number[],
+ type: string
+};
+
+export default function quickSort(bookshelf: Book[]): Move[] {
+ const moves: Move[] = [];
+
+ const partition = (left: number, right: number) => {
+ const pivotIndex = Math.floor((left + right) / 2);
+ const pivot = bookshelf[pivotIndex];
+
+ let i = left;
+ let j = right;
+
+ while (i <= j) {
+ while (bookshelf[i].name < pivot.name) {
+ i++;
+ moves.push({
+ indices: [i, pivotIndex],
+ type: "compare"
+ });
+ }
+ while (bookshelf[j].name > pivot.name) {
+ j--;
+ moves.push({
+ indices: [j, pivotIndex],
+ type: "compare"
+ });
+ }
+ if (i <= j) {
+ if (i !== j) {
+ moves.push({
+ indices: [i, j, pivotIndex],
+ type: "swap"
+ });
+ }
+ [bookshelf[i], bookshelf[j]] = [bookshelf[j], bookshelf[i]];
+ i++;
+ j--;
+ }
+ }
+ return i;
+ };
+
+ const quickSortRecursive = (left: number, right: number) => {
+ if (left >= right) {
+ return;
+ }
+ const index = partition(left, right);
+ quickSortRecursive(left, index - 1);
+ quickSortRecursive(index, right);
+ };
+
+ quickSortRecursive(0, bookshelf.length - 1);
+
+ return moves;
+}
diff --git a/src/sorting/SelectionSort.ts b/src/sort/code/sortingAlgorithms/SelectionSort.ts
similarity index 88%
rename from src/sorting/SelectionSort.ts
rename to src/sort/code/sortingAlgorithms/SelectionSort.ts
index b8ca720..5ee9482 100644
--- a/src/sorting/SelectionSort.ts
+++ b/src/sort/code/sortingAlgorithms/SelectionSort.ts
@@ -9,6 +9,10 @@ export default function selectionSort(bookshelf: Book[]) {
const moves: Move[] = [];
for (let i = 0; i < bookshelf.length; i++) {
let minIndex = i;
+ moves.push({
+ indices: [i, minIndex],
+ type: "compare"
+ });
for (let j = i + 1; j < bookshelf.length; j++) {
if (bookshelf[j].name < bookshelf[minIndex].name) {
minIndex = j;
diff --git a/src/sort/css-style/background.css b/src/sort/css-style/background.css
new file mode 100644
index 0000000..868c5fe
--- /dev/null
+++ b/src/sort/css-style/background.css
@@ -0,0 +1,98 @@
+@import url('https://fonts.googleapis.com/css2?family=Lilita+One');
+* {
+ font-family: "Gill Sans", sans-serif;
+}
+body{
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ margin: 0;
+ height: 100vh;
+ background-color: #ffffff;
+}
+
+header{
+ background: #5a909c;
+
+ font-family: "Gill Sans", sans-serif;
+ color: white;
+
+ height: 10%;
+ width: 100%;
+
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+
+ margin-bottom: 10px;
+}
+h1 {
+ font-size: 40px;
+ font-weight: bold;
+ font-family: "Gill Sans", sans-serif;
+}
+#h1 {
+ text-decoration: none;
+ cursor: pointer;
+ color: #F9FBE7;
+}
+main{
+ height: 85vh;
+ width: 100%;
+ position: relative;
+ align-items: center;
+ justify-content: center;
+}
+.algorithms{
+ height: 100%;
+ gap: 10px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+.algorithms > a {
+ text-decoration: none;
+ font-family: "Gill Sans", sans-serif;
+ font-weight: bold;
+ padding: 10px;
+ transition: 0.3s;
+ color: #19A7CE;
+ font-size: 25px;
+ padding: 10px;
+ background-color: #F9FBE7;
+ border-radius: 5px;
+}
+.algorithms > a:hover {
+ color: black;
+ text-decoration: none;
+ cursor: pointer;
+}
+footer{
+ background: linear-gradient(to bottom, #F0ECEC, #BFD7EA);
+ height: 5vh;
+ display: flex;
+ justify-content: left;
+ align-items: left;
+}
+
+.button_container {
+ position: relative;
+ margin-top: 5px;
+ height: 10vh;
+ margin-left: 1vh;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ justify-content: center;
+}
+
+#bookshelf_container, #augmented_container{
+ height: 25vh;
+ width: 100%;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+}
\ No newline at end of file
diff --git a/style/canvas.css b/src/sort/css-style/canvas.css
similarity index 84%
rename from style/canvas.css
rename to src/sort/css-style/canvas.css
index 8ec0293..b860899 100644
--- a/style/canvas.css
+++ b/src/sort/css-style/canvas.css
@@ -1,14 +1,17 @@
.book {
width: 30px;
height: 90%;
- border: 2px solid black;
border-radius: 5px 5px 0px 0px;
margin: 1px;
display: flex;
+ flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: bold;
- color: black;
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white;
}
+
+.canvas{
+ width: 100%;
+}
\ No newline at end of file
diff --git a/style/control.css b/src/sort/css-style/control.css
similarity index 71%
rename from style/control.css
rename to src/sort/css-style/control.css
index 51d0d3d..fd3d6d5 100644
--- a/style/control.css
+++ b/src/sort/css-style/control.css
@@ -1,9 +1,16 @@
@import url('https://fonts.googleapis.com/css2?family=Lilita+One');
+
+
+body{
+ background: #E3F2FD;
+}
*{
- font-family: "Lilita One", serif;
+ font-family: "Gill Sans", sans-serif;
+ font-weight: bold;
}
+
button {
- background: linear-gradient(to bottom right, #5BCEFA, #F5A9B8);
+ background: #5a909c;
width: 50px;
height: 50px;
padding: 10px 15px;
@@ -17,6 +24,12 @@ button {
display: flex;
justify-content: center;
align-items: center;
+ margin: 2px;
+}
+button:hover {
+ cursor: pointer;
+ box-shadow: 0 0 10px #737b7e;
+ opacity: 0.8;
}
button img{
@@ -24,36 +37,46 @@ button img{
height: 40px;
}
-button:hover {
- opacity: 0.8;
+
+.button-box{
+ position: relative;
+ margin-right: 2px;
+ z-index: 1;
+}
+
+.button-box #below-buttons{
+ display: flex;
+ justify-content: space-between;
+ margin-top: 3px;
}
.box{
+ max-width: 300px;
+ height: 55px;
background: white;
padding: 20px 20px 30px 30px;
border-radius: 5px;
box-shadow: 0 0 5px rgba(0,0,0,0.5);
- display: flex;
align-items: center;
}
-.box .slider_books{
- height: 40px;
- width: 100px;
+.box .content{
+ font-size: 20px;
+ font-weight: 300;
+ color: #5a909c;;
display: flex;
- align-items: center;
- margin-right: 15px;
+ justify-content: center;
}
-.box .slider_time{
+.box .slider{
height: 40px;
- width: 100px;
+ width: 200px;
display: flex;
align-items: center;
margin-right: 15px;
}
-.box .slider_books input{
+.box .slider input{
height: 10px;
width: 100%;
-webkit-appearance: none;
@@ -62,65 +85,40 @@ button:hover {
box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2);
}
-.box .slider_time input{
- height: 10px;
- width: 100%;
- -webkit-appearance: none;
- outline: none;
- background: #f2f2f2;
- box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2);
-}
-
-.box .value_books{
- font-size: 20px;
- font-weight: 300;
- font-family: 'Lilita One', serif;
- color: #3498db;
- width: 55px;
- text-align: center;
-}
-
-.box .value_time{
- font-size: 20px;
- font-weight: 300;
- font-family: 'Lilita One', serif;
- color: #3498db;
- width: 55px;
+.box .value{
+ width: 100px;
text-align: center;
}
-body{
- background: #E3F2FD;
-}
-
select {
-webkit-appearance: none;
-moz-appearance: none;
-ms-appearance: none;
appearance: none;
outline: 0;
- border: 0!important;
+ border: 0;
background: #ffffff;
background-image: none;
padding: 20px 20px 30px 30px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
flex: 1;
padding: 0 .5em;
- color: #3498db;
cursor: pointer;
- font-size: 1em;
display: flex;
align-items: center;
+ font-size: 20px;
+ font-weight: bold;
+ color: #5a909c;
}
-
select::-ms-expand {
display: none;
}
+
.select {
position: relative;
display: flex;
- width: 13em;
+ width: 16em;
height: 3em;
line-height: 3;
background: #D3D3D3;
@@ -139,5 +137,18 @@ select::-ms-expand {
transition:.25s all ease;
}
.select:hover::after {
- color: #3498db;
+ color: #7ce8ff;
+}
+
+label{
+ max-width: 100px;
+}
+
+.warning{
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ font-size: 20px;
+ margin-top: 10px;
+ height: 30px;
}
\ No newline at end of file
diff --git a/src/sort/css-style/icons/go-button.png b/src/sort/css-style/icons/go-button.png
new file mode 100644
index 0000000..4f582f7
Binary files /dev/null and b/src/sort/css-style/icons/go-button.png differ
diff --git a/icons/random-button.png b/src/sort/css-style/icons/random-button.png
similarity index 100%
rename from icons/random-button.png
rename to src/sort/css-style/icons/random-button.png
diff --git a/icons/reset-button.png b/src/sort/css-style/icons/reset-button.png
similarity index 100%
rename from icons/reset-button.png
rename to src/sort/css-style/icons/reset-button.png
diff --git a/icons/worst-case-button.png b/src/sort/css-style/icons/worst-case-button.png
similarity index 100%
rename from icons/worst-case-button.png
rename to src/sort/css-style/icons/worst-case-button.png
diff --git a/src/sort/css-style/tooltip.css b/src/sort/css-style/tooltip.css
new file mode 100644
index 0000000..8861eb3
--- /dev/null
+++ b/src/sort/css-style/tooltip.css
@@ -0,0 +1,44 @@
+[data-tooltip] {
+ position: relative;
+ z-index: 2;
+ cursor: pointer;
+}
+
+/* Hide the tooltip content by default */
+[data-tooltip]:before,
+[data-tooltip]:after {
+ visibility: hidden;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=0);
+ opacity: 0;
+ pointer-events: none;
+ z-index: 2;
+}
+
+/* Position tooltip above the element */
+[data-tooltip]:before {
+ position: absolute;
+ bottom: -100%;
+ padding: 7px;
+ width: 60px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ background-color: #ffffff;
+ background-color: hsla(0, 0%, 100%, 0.9);
+ color: #5a909c;;
+ content: attr(data-tooltip);
+ text-align: center;
+ font-size: 14px;
+ line-height: 1.2;
+ z-index: 2;
+}
+
+/* Show tooltip content on hover */
+[data-tooltip]:hover:before,
+[data-tooltip]:hover:after {
+ visibility: visible;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+ filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=100);
+ opacity: 1;
+}
\ No newline at end of file
diff --git a/src/sort/index.html b/src/sort/index.html
new file mode 100644
index 0000000..c77a4ad
--- /dev/null
+++ b/src/sort/index.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+ Algorithms Visualizer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/sorting/BubbleSort.ts b/src/sorting/BubbleSort.ts
deleted file mode 100644
index 65bea0b..0000000
--- a/src/sorting/BubbleSort.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import Book from '../Book';
-
-type Move = {
- indices: number[],
- type: string
-}
-
-export default function bubbleSort(bookshelf: Book[]) {
- const moves:Move[] = [];
- do{
- var swapped:boolean = false;
- for (let i=1; i bookshelf[i].name) {
- swapped = true;
- moves.push({
- indices: [i-1,i],
- type: "swap"
- });
- [bookshelf[i-1], bookshelf[i]] = [bookshelf[i], bookshelf[i-1]];
- }
- }
- } while(swapped);
- return moves;
-}
diff --git a/style/background.css b/style/background.css
deleted file mode 100644
index 19a9f00..0000000
--- a/style/background.css
+++ /dev/null
@@ -1,64 +0,0 @@
-@import url('https://fonts.googleapis.com/css2?family=Lilita+One');
-
-body{
- position: relative;
- display: flex;
- flex-direction: column;
- align-items: left;
- justify-content: center;
- margin: 0;
- height: 100vh;
- background-color: #ffffff;
-}
-
-header{
- background: linear-gradient(to right, #FF0000, #FF7F00, #FFFF00, #00FF00, #0000FF, #4B0082, #8F00FF);
- height: 10vh;
- font-family: 'Lilita One', serif;
- color: white;
- -webkit-text-stroke: 1px #000000;
- opacity: 0.8;
- position: relative;
- height: 10vh;
- width: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- margin: 0;
- padding: 0;
- z-index: 1;
-}
-main{
- height: 85vh;
-}
-
-footer{
- background: linear-gradient(to bottom, #F0ECEC, #BFD7EA);
- height: 5vh;
- display: flex;
- justify-content: left;
- align-items: left;
-}
-
-body{
- position: relative;
- display: flex;
- align-items: center;
- flex-direction: column;
-}
-
-.button_container {
- position: relative;
- margin-top: 5px;
- height: 10vh;
- margin-left: 1vh;
- display: flex;
- gap: 10px;
-}
-
-#bookshelf_container, #augmented_container_above{
- height: 25vh;
- display: flex;
- align-items: flex-end;
- justify-content: center;
- }
diff --git a/video/BubbleSort.mp4 b/video/BubbleSort.mp4
new file mode 100644
index 0000000..1fd6413
Binary files /dev/null and b/video/BubbleSort.mp4 differ
diff --git a/video/InsertionSort.mp4 b/video/InsertionSort.mp4
new file mode 100644
index 0000000..3b067d3
Binary files /dev/null and b/video/InsertionSort.mp4 differ
diff --git a/video/QuickSort.mp4 b/video/QuickSort.mp4
new file mode 100644
index 0000000..9150677
Binary files /dev/null and b/video/QuickSort.mp4 differ
diff --git a/video/SelectionSort.mp4 b/video/SelectionSort.mp4
new file mode 100644
index 0000000..f72aac6
Binary files /dev/null and b/video/SelectionSort.mp4 differ
Pathfinder
+
+ Pathfinder is a tool or concept used to find the best path between two points, considering obstacles and constraints. It involves algorithms and techniques for efficient navigation or routing.
-
+
+
+
+
+
+
+ 

+
- Algorithms Visualizer
+
+
-
-
-
-
- Đỗ Anh Quân
+ Phạm Vũ Quang
+ Nguyễn Mạnh Việt Khôi
+ Lê Tuấn Phúc
-
-
- Algorithms Visualizer
+ +
+
+
+
+
+
+
+
+
+
+
+
+ 100ms
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Algorithms Visualizer
+ +
+
+
+
+
+
+
+
+
+
+ 20 books.
+
+
+
+
+
+
+
+
+ 100ms
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+