From 4a88689106b6caf2b3bd8ad72dd5d49b5b12f08a Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Sun, 9 Mar 2025 21:33:05 -0400 Subject: [PATCH] adding the stack animation --- animations/stack/index.html | 22 ++ animations/stack/stack.js | 605 ++++++++++++++++++++++++++++++++++++ 2 files changed, 627 insertions(+) create mode 100644 animations/stack/index.html create mode 100644 animations/stack/stack.js diff --git a/animations/stack/index.html b/animations/stack/index.html new file mode 100644 index 0000000..7921f65 --- /dev/null +++ b/animations/stack/index.html @@ -0,0 +1,22 @@ + + + + + Valid Parentheses + + + + +

Valid Parentheses

+

This animation shows if parentheses entered are valid using stack

+ + + + + + + +
+ + + diff --git a/animations/stack/stack.js b/animations/stack/stack.js new file mode 100644 index 0000000..eafc6df --- /dev/null +++ b/animations/stack/stack.js @@ -0,0 +1,605 @@ +// Author: Based on previous examples +// Create a stage for the animation +var stage = new Konva.Stage({ + container: 'container', + width: 1200, + height: 700 +}); + +// Create layer +var layer = new Konva.Layer(); + +// Variables to track animation state +var stack = []; +var lastPopped = ""; +var currentString = ""; +var currentIndex = 0; +var pc = 0; // Program counter for animation steps +var isComplete = false; + +// Stack size and positioning +var stack_size = 10; +var stack_x = 1000; +var stack_y = 100; +var stack_width = 120; +var stack_height = 40; + +// Initialize the visualization +function init() { + // Reset layer + layer.destroyChildren(); + + // Create code display + createCodeDisplay(); + + // Create stack visualization + createStack(); + + // Create animation controls area + createNextStepButton(); + + // Add the layer to the stage + stage.add(layer); + layer.draw(); +} + +// Create the code display +function createCodeDisplay() { + var codeRect = new Konva.Rect({ + x: 50, + y: 50, + width: 450, + height: 600, + stroke: '#555', + strokeWidth: 2, + fill: '#f5f5f5', + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 5, + shadowOffsetY: 5, + shadowOpacity: 0.2, + cornerRadius: 10, + id: 'code_rect' + }); + layer.add(codeRect); + + // Add the code text + var codeLines = [ + "bool isValid(string s) {", + " std::stack myStack;", + " int len = s.length();", + " char c;", + " for(int i=0; i 0) { + line[0].fontStyle('normal'); + line[0].fill('black'); + } + } + + // highlight current line + var currentLine = stage.find('#text_code_line_' + lineNum); + if (currentLine.length > 0) { + currentLine[0].fontStyle('bold'); + currentLine[0].fill('blue'); + } + + layer.draw(); +} + +// Move the character pointer +function movePointer(index) { + if (index < 0 || index >= currentString.length) return; + + var charBox = stage.find('#char_box_' + index)[0]; + var pointer = stage.find('#char_pointer')[0]; + + if (charBox && pointer) { + pointer.x(charBox.x() + charBox.width()/2); + } + + layer.draw(); +} + + +// Reset character highlight +function resetCharHighlight(index) { + if (index < 0 || index >= currentString.length) return; + + var charBox = stage.find('#char_box_' + index)[0]; + if (charBox) { + charBox.fill('#f0f0f0'); + } + + layer.draw(); +} + +// Push a character onto the stack +function pushToStack(char) { + stack.push(char); + var stackIndex = stack_size - stack.length; + + // Add the character to the stack visual + var charText = new Konva.Text({ + x: stack_x + stack_width/2 - 10, + y: stack_y + stackIndex * stack_height + 10, + text: char, + fontSize: 24, + fontFamily: 'monospace', + fill: 'black', + id: 'stack_item_' + stackIndex + }); + + layer.add(charText); + layer.draw(); +} + +// Pop a character from the stack +function popFromStack() { + if (stack.length === 0) return null; + + var poppedChar = stack.pop(); + var stackIndex = stack_size - stack.length - 1; + + // Remove the character from the stack visual + stage.find('#stack_item_' + stackIndex).destroy(); + + // Reset the stack slot color + stage.find('#stack_rec' + stackIndex)[0].fill('#f0f0f0'); + + layer.draw(); + return poppedChar; +} + + +// Update the result text +function updateResult(text, color) { + var resultText = stage.find('#result_text')[0]; + if (resultText) { + resultText.text('Output: ' + text); + resultText.fill(color || 'black'); + } + layer.draw(); +} + +// Update the explanation text +function updateExplanation(text) { + var explanationText = stage.find('#explanation_text')[0]; + if (explanationText) { + explanationText.text(text); + } + layer.draw(); +} + +// Reset the animation state for a new case +function resetAnimation(str) { + // Reset state variables + currentString = str; + currentIndex = 0; + stack = []; + pc = 1; + isComplete = false; + + // Reset visual elements + init(); + createInputDisplay(str); + createResultDisplay(); + + // Reset all stack slots + for (var i = 0; i < stack_size; i++) { + stage.find('#stack_rec' + i)[0].fill('#f0f0f0'); + } + + // Initialize first step + highlightCodeLine(0); + updateResult('Code Not Finished', 'black'); + updateExplanation('Click "Next Step" to begin checking the brackets: ' + str); + + layer.draw(); +} + +// Process the next step of the animation +function nextStep() { + if (isComplete) return; + + switch (pc) { + case 1: + // Initialize stack + highlightCodeLine(1); + updateExplanation("Creating an empty stack to store opening brackets"); + pc++; + break; + + case 2: + // Get length + highlightCodeLine(2); + updateExplanation("Getting the length of the string of brackets: " + currentString.length); + pc++; + break; + + case 3: + // Initialize for loop + highlightCodeLine(4); + updateExplanation("Starting the loop to process each character"); + pc++; + break; + + case 4: + // Check if done with all characters + if (currentIndex >= currentString.length) { + // Final check if stack is empty + highlightCodeLine(21); + updateExplanation("All characters processed. Checking if the stack is empty..."); + + if (stack.length === 0) { + updateResult("True"); + updateExplanation("The stack is empty, which means all opening brackets were matched with their closing brackets in the correct order. The string is valid."); + } else { + updateResult("False", "red"); + updateExplanation("The stack still has " + stack.length + " unmatched opening bracket(s). This means some opening brackets were never matched with closing brackets. The string is invalid."); + } + + isComplete = true; + break; + } + + // Process current character + movePointer(currentIndex); + var currentChar = currentString[currentIndex]; + updateExplanation("Processing character at index " + currentIndex + ": '" + currentChar + "'"); + + // Check if opening bracket + if (currentChar === '(' || currentChar === '{' || currentChar === '[') { + highlightCodeLine(6); + pc = 5; // Go to opening bracket logic + } else { + highlightCodeLine(8); + pc = 6; // Go to closing bracket logic + } + break; + + case 5: + // Process opening bracket + var currentChar = currentString[currentIndex]; + highlightCodeLine(7); + updateExplanation("Found opening bracket '" + currentChar + "'. Pushing it onto the stack to remember it."); + pushToStack(currentChar); + + // Move to next character + resetCharHighlight(currentIndex); + currentIndex++; + pc = 4; // Return to character processing + break; + + case 6: + // Process closing bracket - check if stack is empty + var currentChar = currentString[currentIndex]; + highlightCodeLine(10); + + if (stack.length === 0) { + updateExplanation("Found closing bracket '" + currentChar + "' but the stack is empty. This means there's no matching opening bracket, so the string is invalid."); + updateResult("false", "red"); + isComplete = true; + break; + } + + updateExplanation("Found closing bracket '" + currentChar + "'. Checking the top of the stack for a matching opening bracket."); + pc++; + break; + + case 7: + // Get top of stack + var currentChar = currentString[currentIndex]; + var topChar = stack[stack.length - 1]; + highlightCodeLine(12); + updateExplanation("The top of the stack contains: '" + topChar + "'"); + pc++; + break; + + case 8: + // Pop from stack + var currentChar = currentString[currentIndex]; + var topChar = stack[stack.length - 1]; + highlightCodeLine(13); + updateExplanation("Popping '" + topChar + "' from the stack to compare with '" + currentChar + "'"); + lastPopped = popFromStack(); + pc++; + break; + + case 9: + // Check if brackets match + var currentChar = currentString[currentIndex]; + var topChar = lastPopped; + if (currentChar === ')') { + highlightCodeLine(15); + } else if (currentChar === '}') { + highlightCodeLine(16); + } else if (currentChar === ']') { + highlightCodeLine(17); + } + + // Check if match failed + if ((currentChar === ')' && topChar !== '(') || + (currentChar === '}' && topChar !== '{') || + (currentChar === ']' && topChar !== '[')) { + + updateExplanation("Mismatch! Closing bracket '" + currentChar + "' doesn't match with the opening bracket '" + topChar + "'. The string is invalid."); + updateResult("False"); + isComplete = true; + break; + } + + // Match succeeded + updateExplanation("Match found! Closing bracket '" + currentChar + "' correctly matches with opening bracket '" + topChar + "'." ); + + // Move to next character + resetCharHighlight(currentIndex); + currentIndex++; + pc = 4; // Return to character processing + break; + } + + layer.draw(); +} + +// Case 1: "()" +function case1() { + resetAnimation("()"); +} + +// Case 2: "()[]{}" +function case2() { + resetAnimation("()[]{}"); +} + +// Case 3: "(]" +function case3() { + resetAnimation("(]"); +} + +// Case 4: "([])" +function case4() { + resetAnimation("([])"); +} + +// Case 4: "({[{[()]}]})" +function case5() { + resetAnimation("{[([])]}"); +} + +// Initialize the animation +init(); +case1(); // Start with Case 1 by default \ No newline at end of file