Skip to content

Commit

Permalink
reorg files; add animation classes; tune up quickSort
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewoestreich committed Jul 31, 2024
1 parent cbaed81 commit f1a9cb5
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 213 deletions.
9 changes: 9 additions & 0 deletions algoAnimation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AlgoAnimation {
constructor(animation = { type: "", value: null, index: null, indexes: [], elements: [] }) {
this.type = animation.type;
this.value = animation.value;
this.index = animation.index;
this.indexes = animation.indexes;
this.elements = animation.elements;
}
}
17 changes: 17 additions & 0 deletions algos/bubbleSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function* bubbleSort(array = []) {
for (let i = 0; i < array.length; i++) {
for (let j = 0; j < array.length - 1 - i; j++) {
yield new AlgoAnimation({ type: "colors", elements: [{ index: j, value: BAR_COLORS.compare }, { index: j+1, value: BAR_COLORS.compare }] });

if (array[j].dataset.value > array[j + 1].dataset.value) {
yield new AlgoAnimation({ type: "colors", elements: [{ index: j, value: BAR_COLORS.correct }, { index: j+1, value: BAR_COLORS.incorrect }] });
yield new AlgoAnimation({ type: "swap", indexes: [j, j+1] });
[array[j], array[j+1]] = [array[j+1], array[j]];
}

yield new AlgoAnimation({ type: "colors", elements: [{ index: j+1, value: BAR_COLORS.correct }, { index: j, value: BAR_COLORS.correct }] });
yield new AlgoAnimation({ type: "colors", elements: [{ index: j, value: BAR_COLORS.default }, { index: j+1, value: BAR_COLORS.compare }] });
}
yield new AlgoAnimation({ type: "color", index: (array.length-1-i), value: BAR_COLORS.completed });
}
}
27 changes: 7 additions & 20 deletions mergeSort.js → algos/mergeSort.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,33 @@ function* merge(start, mid, end) {
for (let i = start; i <= end; i++) {
if (p > mid) {
arr.push(ARRAY[q].cloneNode());
yield { type: "backgroundColor", value: BAR_COLORS.incorrect, index: q };
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.incorrect, index: q });
q++;
} else if (q > end || Number(ARRAY[p].dataset.value) < Number(ARRAY[q].dataset.value)) {
arr.push(ARRAY[p].cloneNode());
yield { type: "backgroundColor", value: BAR_COLORS.incorrect, index: p };
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.incorrect, index: p });
p++;
} else {
arr.push(ARRAY[q].cloneNode());
yield { type: "backgroundColor", value: BAR_COLORS.incorrect, index: q };
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.incorrect, index: q });
q++;
}
}

for (let i = 0; i < arr.length; i++) {
ARRAY[start] = arr[i];
yield { type: "backgroundColor", index: start, value: backgroundColor };
yield { type: "height", index: start, value: arr[i].style.height };
yield new AlgoAnimation({ type: "color", index: start, value: backgroundColor });
yield new AlgoAnimation({ type: "height", index: start, value: arr[i].style.height });
start++;
}
}

function* mergePartition(start, end) {
if (start < end) {
const mid = Math.floor((start + end) / 2);
yield { type: "backgroundColor", value: BAR_COLORS.compare, index: mid };
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.compare, index: mid });
yield* mergePartition(start, mid);
yield* mergePartition(mid + 1, end);
yield* merge(start, mid, end);
}
}

async function renderMergeSort(animations) {
let next = animations.next();
if (next.done) {
return;
}

const { type, value, index } = next.value;
divRenderBars.childNodes[index].style[type] = value;
await sleep(sliderSpeed.dataset.value);

await renderMergeSort(animations);
}
}
81 changes: 81 additions & 0 deletions algos/quickSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
function* quickSort(array) {
yield* sort(array, 0, array.length - 1);
}

function* partition(arr, start, end) {
const mid = Math.floor((start + end) / 2);
// select the pivot
let pivot = Number(arr[mid].dataset.value);
// move pivot to end;
[arr[mid], arr[end]] = [arr[end], arr[mid]];
yield new AlgoAnimation({ type: "swap", indexes: [mid, end] });
yield new AlgoAnimation({ type: "color", value: "cyan", index: end });

// set bounds
let left = start;
let right = end - 1;

while (left <= right) {
yield new AlgoAnimation({
type: "colors",
elements: [
{ index: left, value: BAR_COLORS.compare },
{ index: right, value: BAR_COLORS.compare },
{ index: start, value: "orange" },
{ index: end - 1, value: "orange" },
],
});

// Once we have left value greater or equal to pivot AND a right value that is less than the pivot we swap those two values
// (or right bound crosses left bound, which would break the while condition)
if (Number(arr[left].dataset.value) >= pivot && Number(arr[right].dataset.value) < pivot) {
yield new AlgoAnimation({
type: "colors",
elements: [
{ index: left, value: BAR_COLORS.incorrect },
{ index: right, value: BAR_COLORS.incorrect },
],
});
yield new AlgoAnimation({ type: "swap", indexes: [left, right] });
[arr[left], arr[right]] = [arr[right], arr[left]];
continue;
}

if (Number(arr[left].dataset.value) < pivot) {
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.default, index: left });
left++;
}
if (Number(arr[right].dataset.value) >= pivot) {
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.default, index: right });
right--;
}

yield new AlgoAnimation({
type: "colors",
elements: [
{ index: left, value: BAR_COLORS.correct },
{ index: right, value: BAR_COLORS.correct },
],
});
}

// right bound has crossed left bound
yield new AlgoAnimation({ type: "swap", indexes: [left, end] });
yield new AlgoAnimation({ type: "color", index: end, value: BAR_COLORS.correct });
[arr[left], arr[end]] = [arr[end], arr[left]];

return left;
}

function* sort(arr, start, end) {
if (start < end) {
const pivot = yield* partition(arr, start, end);
yield* sort(arr, start, pivot - 1);
yield* sort(arr, pivot + 1, end);
if (start === 0 && end === arr.length - 1) {
for (let i = start; i <= end; i++) {
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.completed, index: i });
}
}
}
}
24 changes: 10 additions & 14 deletions quickSort.js → algos/quickSortOld.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,23 @@ function* quickPartition(start, end) {

for (let j = start + 1; j <= end; j++) {
if (Number(ARRAY[j].dataset.value) < Number(pivotElement.dataset.value)) {
yield { type: "color", index: j, value: BAR_COLORS.compare };
yield { type: "color", index: left, value: BAR_COLORS.incorrect };
yield { type: "color", index: j, value: BAR_COLORS.incorrect };
yield new AlgoAnimation({ type: "colors", elements: [{ index: j, value: BAR_COLORS.compare }, { index: left, value: BAR_COLORS.incorrect }] });
yield new AlgoAnimation({ type: "color", index: j, value: BAR_COLORS.incorrect });
// swap
yield { type: "swap", leftIndex: left, rightIndex: j };
yield new AlgoAnimation({ type: "swap", indexes: [left, j] });
[ARRAY[left], ARRAY[j]] = [ARRAY[j], ARRAY[left]];
yield { type: "color", index: left, value: BAR_COLORS.incorrect };
yield { type: "color", index: j, value: BAR_COLORS.incorrect };
yield { type: "color", index: j, value: BAR_COLORS.default };
yield { type: "color", index: left, value: BAR_COLORS.default };
yield new AlgoAnimation({ type: "colors", elements: [{ index: left, value: BAR_COLORS.incorrect }, { index: j, value: BAR_COLORS.incorrect }] });
yield new AlgoAnimation({ type: "colors", elements: [{ index: j, value: BAR_COLORS.default }, { index: left, value: BAR_COLORS.default }] });
left++;
}
}

yield { type: "swap", leftIndex: start, rightIndex: left - 1 };
yield new AlgoAnimation({ type: "swap", indexes: [start, left-1] });
[ARRAY[start], ARRAY[left - 1]] = [ARRAY[left - 1], ARRAY[start]];
yield { type: "color", index: start, value: BAR_COLORS.incorrect };
yield { type: "color", index: left - 1, value: BAR_COLORS.incorrect };
yield new AlgoAnimation({ type: "colors", elements: [{ index: start, value: BAR_COLORS.incorrect }, { index: left-1, value: BAR_COLORS.incorrect }] });

for (let i = start; i <= left; i++) {
yield { type: "color", index: i, value: BAR_COLORS.correct };
yield new AlgoAnimation({ type: "color", index: i, value: BAR_COLORS.correct });
}

return left - 1;
Expand All @@ -41,8 +37,8 @@ function* sort(start, end) {
yield* sort(start, pivot - 1);
yield* sort(pivot + 1, end);
// done with these bars, color bars as completed
for (let i = 0; i <= end; i++) {
divRenderBars.childNodes[i].style.backgroundColor = BAR_COLORS.completed;
for (let i = start; i <= end; i++) {
yield new AlgoAnimation({ type: "color", value: BAR_COLORS.completed, index: i });
}
}
}
Expand Down
55 changes: 55 additions & 0 deletions algos/shakerSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
function* shakerSort(array) {
let isSwap = true;
// Since each iteration will produce the largest and smallest elements,
// we can keep track of how many loops we have completed as to not process
// unnecessary elements (which we already know are correct).
let loops = 0;

while (isSwap) {
isSwap = false;

for (let i = 0 + loops; i < array.length - 1 - loops; i++) {
yield new AlgoAnimation({ type: "colors", elements: [{ index: i, value: BAR_COLORS.compare }, { index: i + 1, value: BAR_COLORS.compare }] });

if (Number(array[i].dataset.value) > Number(array[i + 1].dataset.value)) {
yield new AlgoAnimation({ type: "colors", elements: [{ index: i, value: BAR_COLORS.incorrect }, { index: i + 1, value: BAR_COLORS.incorrect }] });
yield new AlgoAnimation({ type: "swap", indexes: [i, i+1] });
[array[i], array[i + 1]] = [array[i + 1], array[i]];
isSwap = true;
}

yield new AlgoAnimation({ type: "colors", elements: [{ index: i, value: BAR_COLORS.default }, { index: i + 1, value: BAR_COLORS.correct }] });
}

// "Last" element is now in correct position.
yield new AlgoAnimation({ type: "color", index: array.length - 1 - loops, value: BAR_COLORS.completed });

// If nothing was swapped we know we are sorted and can break early.
if (!isSwap) {
break;
}

isSwap = false;
for (let i = array.length - 2 - loops; i > 0 + loops; i--) {
yield new AlgoAnimation({ type: "colors", elements: [{ index: i, value: BAR_COLORS.compare }, { index: i - 1, value: BAR_COLORS.compare }] });

if (Number(array[i].dataset.value) < Number(array[i - 1].dataset.value)) {
yield new AlgoAnimation({ type: "colors", elements: [{ index: i, value: BAR_COLORS.compare }, { index: i - 1, value: BAR_COLORS.compare }] });
yield new AlgoAnimation({ type: "swap", indexes: [i, i-1] });
[array[i], array[i - 1]] = [array[i - 1], array[i]];
isSwap = true;
}

yield new AlgoAnimation({ type: "colors", elements: [{ index: i, value: BAR_COLORS.default }, { index: i - 1, value: BAR_COLORS.correct }] });
}

// "First" element is now in correct position.
yield new AlgoAnimation({ type: "color", index: 0 + loops, value: BAR_COLORS.completed });
loops++;
}

// Mark remaining as completed
for (let i = loops; i < array.length - loops; i++) {
yield new AlgoAnimation({ type: "color", index: i, value: BAR_COLORS.completed });
}
}
62 changes: 62 additions & 0 deletions animator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
class Animator {
/**
*
* @param {AlgoAnimation} animation
*/
constructor(animation) {
this.animation = animation;
}

/**
*
* @param {HTMLDivElement} divWhereArrayIsRendered
* @param {Number} sleepTimeMilliseconds
*/
async animate(divWhereArrayIsRendered, sleepTimeMilliseconds) {
if (!divWhereArrayIsRendered.childElementCount || !divWhereArrayIsRendered) {
return;
}

switch (this.animation.type) {
case "color": {
const { index, value } = this.animation;
if (index >= 0 && index <= divWhereArrayIsRendered.childElementCount - 1) {
divWhereArrayIsRendered.childNodes[index].style.backgroundColor = value;
}
await sleep(sleepTimeMilliseconds);
break;
}

case "colors": {
for (let i = 0; i < this.animation.elements.length; i++) {
const { index, value } = this.animation.elements[i];
if (index >= 0 && index <= divWhereArrayIsRendered.childElementCount - 1) {
divWhereArrayIsRendered.childNodes[index].style.backgroundColor = value;
}
}
await sleep(sleepTimeMilliseconds);
break;
}

case "swap": {
const [left, right] = this.animation.indexes;
const temp = divWhereArrayIsRendered.childNodes[left].style.height;
divWhereArrayIsRendered.childNodes[left].style.height = divWhereArrayIsRendered.childNodes[right].style.height;
divWhereArrayIsRendered.childNodes[right].style.height = temp;
await sleep(sleepTimeMilliseconds);
break;
}

case "height": {
const { index, value } = this.animation;
divWhereArrayIsRendered.childNodes[index].style.height = value;
await sleep(sleepTimeMilliseconds);
break;
}

default: {
throw new Error(`[Animator.animate] animation type (${this.animation.type}) not found!`);
}
}
}
}
44 changes: 0 additions & 44 deletions bubbleSort.js

This file was deleted.

Loading

0 comments on commit f1a9cb5

Please sign in to comment.