diff --git a/public/Sorting_Visualizer/index.html b/public/Sorting_Visualizer/index.html new file mode 100644 index 0000000000..3ca6341052 --- /dev/null +++ b/public/Sorting_Visualizer/index.html @@ -0,0 +1,55 @@ + + + + + + Advanced Sorting Visualizer + + + + + + + + +
+
+
+

Sorting Visualizer

+

Demystifying data structures and algorithms through animation.

+
+ +
+

The Algorithms

+ +
+
+
+

Bubble Sort

+

Repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order.

O(n²) Time

+
+
+
+

Insertion Sort

+

Builds the final sorted array one item at a time. It is much less efficient on large lists than more advanced algorithms such as quicksort, heapsort, or merge sort.

O(n²) Time

+
+
+
+

Merge Sort

+

An efficient, stable sorting algorithm that makes use of the divide and conquer paradigm.

O(n log n) Time

+
+
+
+

Quick Sort

+

An extremely efficient, unstable algorithm that uses a pivot to partition the array into smaller subarrays.

O(n log n) Time

+
+
+
+ +
+ Open Visualizer +
+
+ + + diff --git a/public/Sorting_Visualizer/script.js b/public/Sorting_Visualizer/script.js new file mode 100644 index 0000000000..606be2d08b --- /dev/null +++ b/public/Sorting_Visualizer/script.js @@ -0,0 +1,288 @@ +/** + * Advanced Sorting Visualizer + */ + +// --- Settings Management (Colors) --- +const DEFAULTS = { + primary: '#3b82f6', + compare: '#ff0055', + swap: '#ffcc00', + sorted: '#00ff99' +}; + +function getSetting(key) { + return localStorage.getItem('sort_' + key) || DEFAULTS[key]; +} + +function setSetting(key, value) { + localStorage.setItem('sort_' + key, value); +} + +function applySettings() { + document.documentElement.style.setProperty('--color-primary', getSetting('primary')); + document.documentElement.style.setProperty('--color-compare', getSetting('compare')); + document.documentElement.style.setProperty('--color-swap', getSetting('swap')); + document.documentElement.style.setProperty('--color-sorted', getSetting('sorted')); +} +applySettings(); + +// --- Settings Page Logic --- +if (window.location.pathname.includes('settings.html')) { + const ids = ['primary', 'compare', 'swap', 'sorted']; + ids.forEach(id => { + const input = document.getElementById('color-' + id); + input.value = getSetting(id); + input.addEventListener('change', e => { + setSetting(id, e.target.value); + applySettings(); + }); + }); + + document.getElementById('btn-reset-colors').addEventListener('click', () => { + ids.forEach(id => { + setSetting(id, DEFAULTS[id]); + document.getElementById('color-' + id).value = DEFAULTS[id]; + }); + applySettings(); + }); +} + +// --- Visualizer Logic --- +if (window.location.pathname.includes('visualizer.html')) { + + const container = document.getElementById('array-container'); + const sliderSize = document.getElementById('slider-size'); + const sliderSpeed = document.getElementById('slider-speed'); + const btnGenerate = document.getElementById('btn-generate'); + const btnSort = document.getElementById('btn-sort'); + const algoSelector = document.getElementById('algo-selector'); + + let array = []; + let isSorting = false; + + // --- State Generation --- + function generateArray() { + if (isSorting) return; + array = []; + container.innerHTML = ''; + const size = sliderSize.value; + const containerWidth = container.clientWidth - 40; // padding + const barWidth = Math.max(1, Math.floor(containerWidth / size) - 2); + + for (let i = 0; i < size; i++) { + // Random value between 10 and 100 for percentage height + const value = Math.floor(Math.random() * 90) + 10; + array.push(value); + + const bar = document.createElement('div'); + bar.classList.add('array-bar'); + bar.style.height = `${value}%`; + bar.style.width = `${barWidth}px`; + container.appendChild(bar); + } + } + + sliderSize.addEventListener('input', generateArray); + btnGenerate.addEventListener('click', generateArray); + window.addEventListener('resize', generateArray); + + // Initial load + generateArray(); + + + // --- Animation Engine --- + function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + function getSpeed() { + // Speed slider: 1 (slow) to 100 (fast). + // Return ms delay. + const val = parseInt(sliderSpeed.value); + return Math.floor(1000 / val) - 9; // Range approx 1ms to 1000ms + } + + async function playAnimations(animations) { + isSorting = true; + const bars = document.getElementsByClassName('array-bar'); + + for (let i = 0; i < animations.length; i++) { + const anim = animations[i]; + const type = anim.type; // 'compare', 'swap', 'overwrite', 'revert' + + if (type === 'compare') { + const [idx1, idx2] = anim.indices; + bars[idx1].style.backgroundColor = 'var(--color-compare)'; + bars[idx2].style.backgroundColor = 'var(--color-compare)'; + await sleep(getSpeed()); + bars[idx1].style.backgroundColor = 'var(--color-primary)'; + bars[idx2].style.backgroundColor = 'var(--color-primary)'; + } + else if (type === 'swap') { + const [idx1, idx2] = anim.indices; + bars[idx1].style.backgroundColor = 'var(--color-swap)'; + bars[idx2].style.backgroundColor = 'var(--color-swap)'; + + // Swap heights + const temp = bars[idx1].style.height; + bars[idx1].style.height = bars[idx2].style.height; + bars[idx2].style.height = temp; + + await sleep(getSpeed()); + bars[idx1].style.backgroundColor = 'var(--color-primary)'; + bars[idx2].style.backgroundColor = 'var(--color-primary)'; + } + else if (type === 'overwrite') { + // Used for Merge Sort + const [idx, value] = anim.indices; + bars[idx].style.backgroundColor = 'var(--color-swap)'; + bars[idx].style.height = `${value}%`; + await sleep(getSpeed()); + bars[idx].style.backgroundColor = 'var(--color-primary)'; + } + } + + // Sorted Animation Sweep + for (let i = 0; i < bars.length; i++) { + bars[i].style.backgroundColor = 'var(--color-sorted)'; + await sleep(10); + } + + isSorting = false; + } + + + // --- Algorithms --- + + function bubbleSort(arr) { + const anims = []; + let n = arr.length; + for (let i = 0; i < n - 1; i++) { + for (let j = 0; j < n - i - 1; j++) { + anims.push({ type: 'compare', indices: [j, j+1] }); + if (arr[j] > arr[j+1]) { + anims.push({ type: 'swap', indices: [j, j+1] }); + let temp = arr[j]; + arr[j] = arr[j+1]; + arr[j+1] = temp; + } + } + } + return anims; + } + + function insertionSort(arr) { + const anims = []; + let n = arr.length; + for (let i = 1; i < n; i++) { + let key = arr[i]; + let j = i - 1; + while (j >= 0 && arr[j] > key) { + anims.push({ type: 'compare', indices: [j, j+1] }); + anims.push({ type: 'swap', indices: [j, j+1] }); // visually swap + arr[j + 1] = arr[j]; + j = j - 1; + } + arr[j + 1] = key; + } + return anims; + } + + // Merge Sort + function mergeSort(arr) { + const anims = []; + const aux = [...arr]; + mergeSortHelper(arr, 0, arr.length - 1, aux, anims); + return anims; + } + + function mergeSortHelper(mainArray, startIdx, endIdx, auxiliaryArray, animations) { + if (startIdx === endIdx) return; + const middleIdx = Math.floor((startIdx + endIdx) / 2); + mergeSortHelper(auxiliaryArray, startIdx, middleIdx, mainArray, animations); + mergeSortHelper(auxiliaryArray, middleIdx + 1, endIdx, mainArray, animations); + doMerge(mainArray, startIdx, middleIdx, endIdx, auxiliaryArray, animations); + } + + function doMerge(mainArray, startIdx, middleIdx, endIdx, auxiliaryArray, animations) { + let k = startIdx; + let i = startIdx; + let j = middleIdx + 1; + + while (i <= middleIdx && j <= endIdx) { + animations.push({ type: 'compare', indices: [i, j] }); + if (auxiliaryArray[i] <= auxiliaryArray[j]) { + animations.push({ type: 'overwrite', indices: [k, auxiliaryArray[i]] }); + mainArray[k++] = auxiliaryArray[i++]; + } else { + animations.push({ type: 'overwrite', indices: [k, auxiliaryArray[j]] }); + mainArray[k++] = auxiliaryArray[j++]; + } + } + while (i <= middleIdx) { + animations.push({ type: 'compare', indices: [i, i] }); // pseudo compare + animations.push({ type: 'overwrite', indices: [k, auxiliaryArray[i]] }); + mainArray[k++] = auxiliaryArray[i++]; + } + while (j <= endIdx) { + animations.push({ type: 'compare', indices: [j, j] }); // pseudo compare + animations.push({ type: 'overwrite', indices: [k, auxiliaryArray[j]] }); + mainArray[k++] = auxiliaryArray[j++]; + } + } + + // Quick Sort + function quickSort(arr) { + const anims = []; + quickSortHelper(arr, 0, arr.length - 1, anims); + return anims; + } + + function quickSortHelper(arr, low, high, anims) { + if (low < high) { + let pi = partition(arr, low, high, anims); + quickSortHelper(arr, low, pi - 1, anims); + quickSortHelper(arr, pi + 1, high, anims); + } + } + + function partition(arr, low, high, anims) { + let pivot = arr[high]; + let i = (low - 1); + + for (let j = low; j <= high - 1; j++) { + anims.push({ type: 'compare', indices: [j, high] }); + if (arr[j] < pivot) { + i++; + anims.push({ type: 'swap', indices: [i, j] }); + let temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } + anims.push({ type: 'swap', indices: [i + 1, high] }); + let temp2 = arr[i + 1]; + arr[i + 1] = arr[high]; + arr[high] = temp2; + return (i + 1); + } + + // --- Action Trigger --- + btnSort.addEventListener('click', () => { + if (isSorting) return; + + const algo = algoSelector.value; + const copyArr = [...array]; + let animations = []; + + if (algo === 'bubble') animations = bubbleSort(copyArr); + else if (algo === 'insertion') animations = insertionSort(copyArr); + else if (algo === 'merge') animations = mergeSort(copyArr); + else if (algo === 'quick') animations = quickSort(copyArr); + + // State is updated behind the scenes. + // We now play the visual animations. + playAnimations(animations); + }); + +} diff --git a/public/Sorting_Visualizer/settings.html b/public/Sorting_Visualizer/settings.html new file mode 100644 index 0000000000..1a924ec693 --- /dev/null +++ b/public/Sorting_Visualizer/settings.html @@ -0,0 +1,72 @@ + + + + + + Settings - Sorting Visualizer + + + + + + + + +
+ + + + +
+ +
+

Theme Configuration

+
+ +
+ +
+

Bar Colors

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ +
+ +
+
+ + + + diff --git a/public/Sorting_Visualizer/style.css b/public/Sorting_Visualizer/style.css new file mode 100644 index 0000000000..fd8e70aa8d --- /dev/null +++ b/public/Sorting_Visualizer/style.css @@ -0,0 +1,277 @@ +:root { + /* Base Variables (Default Dark Theme) */ + --bg-main: #0a0a0a; + --bg-panel: #171717; + --bg-sidebar: #000000; + + --text-primary: #f5f5f5; + --text-secondary: #a3a3a3; + + --border: #262626; + + --accent: #6366f1; + --accent-hover: #4f46e5; + + /* Dynamic settings via JS */ + --color-primary: #3b82f6; + --color-compare: #ff0055; + --color-swap: #ffcc00; + --color-sorted: #00ff99; + + --font-ui: 'Outfit', sans-serif; + --font-mono: 'JetBrains Mono', monospace; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: var(--bg-main); + color: var(--text-primary); + font-family: var(--font-ui); + height: 100vh; + overflow-x: hidden; +} + +/* --- Landing Page --- */ +.landing-page { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + background: radial-gradient(circle at top center, var(--bg-panel) 0%, var(--bg-main) 100%); +} + +.landing-container { + max-width: 900px; + padding: 40px; +} + +.landing-header { + text-align: center; + margin-bottom: 60px; +} + +.logo-icon { + font-size: 4rem; + color: var(--accent); + margin-bottom: 20px; +} + +.landing-header h1 { + font-size: 3.5rem; + font-weight: 800; + margin-bottom: 16px; + letter-spacing: -1px; +} + +.landing-header .subtitle { + font-size: 1.25rem; + color: var(--text-secondary); +} + +.algo-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 24px; + margin-top: 24px; + margin-bottom: 40px; +} + +.algo-card { + background-color: var(--bg-panel); + border: 1px solid var(--border); + border-radius: 12px; + padding: 24px; + transition: transform 0.3s, border-color 0.3s; +} + +.algo-card:hover { + transform: translateY(-5px); + border-color: var(--accent); +} + +.algo-icon { + font-size: 2rem; + color: var(--text-primary); + margin-bottom: 16px; +} + +.algo-card h3 { + margin-bottom: 8px; + font-size: 1.2rem; +} + +.algo-card p { + color: var(--text-secondary); + font-size: 0.95rem; + line-height: 1.5; +} +.algo-card p strong { color: var(--color-sorted); } + +.cta-section { text-align: center; margin-top: 40px; } + +/* Buttons & Inputs */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 16px; + border-radius: 6px; + font-weight: 600; + border: none; + cursor: pointer; + font-size: 0.95rem; + font-family: var(--font-ui); + transition: all 0.2s; + text-decoration: none; +} + +.btn-primary { background-color: var(--accent); color: #fff; } +.btn-primary:hover { background-color: var(--accent-hover); } +.btn-secondary { background-color: transparent; border: 1px solid var(--border); color: var(--text-primary); } +.btn-secondary:hover { background-color: var(--border); } +.btn-large { padding: 16px 32px; font-size: 1.1rem; border-radius: 8px; } + +.custom-select { + background-color: var(--bg-main); + color: var(--text-primary); + border: 1px solid var(--border); + padding: 8px 12px; + border-radius: 6px; + font-family: var(--font-ui); + font-size: 0.95rem; + outline: none; + cursor: pointer; +} +.custom-select:focus { border-color: var(--accent); } + +/* --- App Layout --- */ +.app-layout { display: flex; height: 100vh; } + +.sidebar { + width: 60px; + background-color: var(--bg-sidebar); + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; + align-items: center; + padding: 16px 0; + z-index: 10; +} + +.sidebar-brand { + font-size: 1.5rem; + color: var(--accent); + margin-bottom: 40px; +} + +.sidebar-nav { + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; +} + +.sidebar-nav a { + color: var(--text-secondary); + text-decoration: none; + font-size: 1.25rem; + width: 100%; + text-align: center; + padding: 12px 0; + transition: all 0.2s; + position: relative; +} + +.sidebar-nav a:hover { color: var(--text-primary); } +.sidebar-nav a.active { color: var(--accent); } +.sidebar-nav a.active::before { + content: ''; position: absolute; left: 0; top: 0; height: 100%; width: 3px; background-color: var(--accent); +} + +/* --- Workspace --- */ +.workspace { flex: 1; display: flex; flex-direction: column; overflow: hidden; } + +.toolbar { + min-height: 70px; + background-color: var(--bg-panel); + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: center; + padding: 0 24px; + gap: 20px; + flex-wrap: wrap; +} + +.toolbar-group { display: flex; align-items: center; gap: 12px; } +.toolbar-divider { width: 1px; height: 30px; background-color: var(--border); } + +.slider-control { + display: flex; + flex-direction: column; + gap: 4px; +} +.slider-control label { + font-size: 0.75rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.custom-slider { + -webkit-appearance: none; + appearance: none; + width: 120px; + height: 4px; + background: var(--border); + border-radius: 2px; + outline: none; +} +.custom-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--accent); + cursor: pointer; +} + +/* --- Array Container --- */ +.visualization-container { + flex: 1; + display: flex; + align-items: flex-end; + justify-content: center; + padding: 40px 20px; + background-color: var(--bg-main); +} + +.array-bar { + background-color: var(--color-primary); + margin: 0 1px; + border-radius: 2px 2px 0 0; + transition: height 0.1s ease-in-out, background-color 0.1s; +} + +/* --- Settings Page --- */ +.settings-workspace { overflow-y: auto; padding: 40px; display: block; } +.settings-header { margin-bottom: 40px; } +.settings-card { background-color: var(--bg-panel); border: 1px solid var(--border); border-radius: 12px; padding: 32px; margin-bottom: 24px; max-width: 600px; } +.settings-card h3 { margin-bottom: 24px; color: var(--text-primary); display: flex; align-items: center; gap: 12px; } +.settings-card h3 i { color: var(--accent); } +.setting-group { display: flex; justify-content: space-between; align-items: center; padding: 16px 0; border-bottom: 1px solid var(--border); } +.setting-group:last-child { border-bottom: none; } +.setting-group label { color: var(--text-primary); } + +.setting-group input[type="color"] { + -webkit-appearance: none; appearance: none; border: none; width: 32px; height: 32px; border-radius: 4px; cursor: pointer; background: none; +} +.setting-group input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; } +.setting-group input[type="color"]::-webkit-color-swatch { border: 1px solid var(--border); border-radius: 4px; } diff --git a/public/Sorting_Visualizer/visualizer.html b/public/Sorting_Visualizer/visualizer.html new file mode 100644 index 0000000000..ca499d27e5 --- /dev/null +++ b/public/Sorting_Visualizer/visualizer.html @@ -0,0 +1,80 @@ + + + + + + Visualizer - Sorting Algorithms + + + + + + + + +
+ + + + +
+ + +
+ +
+ +
+ +
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+
+ + + +