// Author: Mykolas Ruth // 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 5: "({[{[()]}]})" function case5() { resetAnimation("{[([])]}"); } // Initialize the animation init(); case1(); // Start with Case 1 by default