Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back CSE240 - Level 5: The Linked List Labyrinth Programming
Download Open
Show description 2,227 chars · Programming

CSE240 - Level 5: The Linked List Labyrinth

CSE240 - Level 5: The Linked List Labyrinth








CSE240: Linked List Labyrinth

Your Complete Guide to HW5





LVL 1: Structs
LVL 2: Pointers
LVL 3: Linked Lists
Boss Battles
Final Code




LEVEL 1: FORGING YOUR CHARACTER (Structs & Enums)


Before we can venture into the labyrinth, we need to define our character's attributes. In C, we use a struct to group different data types into a single "record". For this quest, our character is a studentRecord.


The `studentRecord` Blueprint


Our struct holds all the essential stats for one student. Notice how it contains character arrays for strings, an integer for the ID, and a special enum type for the school year.



struct studentRecord {
char studentName[MAX_NAME_LENGTH];
char major[MAX_NAME_LENGTH];
schoolYearType schoolYear; // This is our special Enum type!
unsigned int IDNumber;
struct studentRecord* next; // The magic pointer that links to the next character!
};

The `enum` Scroll


An enum (enumeration) is a way to create a set of named integer constants. It makes our code more readable than using magic numbers like 0, 1, 2, 3. Here, `freshman` is secretly `0`, `sophomore` is `1`, and so on.


typedef enum { freshman = 0, sophomore, junior, senior } schoolYearType;




LEVEL 2: MASTERING THE POINTER WAND (Memory Management)


Pointers are the magic wands of C. They don't hold data themselves; they hold the memory address *where* data lives. To create new student records on the fly, we must master memory allocation.


Casting `malloc()`


The malloc() spell (memory allocation) reserves a chunk of memory from the "heap". We tell it how much space we need (the size of one `studentRecord`), and it gives us back a generic pointer to that space. We then "cast" it to the correct pointer type.


// Reserve memory for one new student and get a pointer to it.
struct studentRecord* newNode = (struct studentRecord*)malloc(sizeof(struct studentRecord));

The `free()` Spell of Banishment


Memory you allocate with `malloc()` is not automatically cleaned up! If you lose the pointer to it, that memory becomes a "memory leak," draining your system's power. The free() spell is essential.…

CSE240 - Level 5: The Linked List Labyrinth

26,612 bytes · HTML source
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSE240 - Level 5: The Linked List Labyrinth</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-color: #12121e;
            --primary-text: #e0e0e0;
            --header-text: #a2facf;
            --accent-color-1: #ff00ff; /* Magenta */
            --accent-color-2: #ffff00; /* Yellow */
            --accent-color-3: #00ffff; /* Cyan */
            --container-bg: #1e1e3f;
            --border-color: #4f4f7a;
            --code-bg: #0d0d1a;
        }

        /* General Body Styling */
        body {
            background-color: var(--bg-color);
            color: var(--primary-text);
            font-family: 'VT323', monospace;
            font-size: 20px;
            line-height: 1.6;
            margin: 0;
            padding: 0;
            overflow-x: hidden;
        }

        /* 8-bit style containers */
        .container {
            max-width: 900px;
            margin: 40px auto;
            padding: 25px;
            background-color: var(--container-bg);
            border: 3px solid var(--border-color);
            box-shadow: 6px 6px 0px var(--accent-color-1);
            transition: transform 0.2s ease;
        }

        .container:hover {
            transform: translate(-3px, -3px);
            box-shadow: 9px 9px 0px var(--accent-color-1);
        }

        /* Typography */
        h1, h2, h3 {
            font-family: 'Press Start 2P', cursive;
            color: var(--header-text);
            text-shadow: 3px 3px 0px var(--accent-color-1);
            margin-bottom: 20px;
        }
        
        h1 { font-size: 2.5rem; text-align: center; }
        h2 { font-size: 1.8rem; color: var(--accent-color-2); border-bottom: 3px solid var(--border-color); padding-bottom: 10px;}
        h3 { font-size: 1.4rem; color: var(--accent-color-3); }

        a {
            color: var(--accent-color-2);
            text-decoration: none;
            transition: all 0.2s ease;
        }
        a:hover {
            background-color: var(--accent-color-2);
            color: var(--bg-color);
            text-shadow: none;
        }

        /* Header section with typing animation */
        header {
            text-align: center;
            padding: 60px 20px;
            border-bottom: 5px solid var(--border-color);
        }
        .typing-effect {
            display: inline-block;
            overflow: hidden;
            white-space: nowrap;
            border-right: .15em solid var(--accent-color-2);
            animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite;
        }
        
        @keyframes typing {
            from { width: 0 }
            to { width: 100% }
        }

        @keyframes blink-caret {
            from, to { border-color: transparent }
            50% { border-color: var(--accent-color-2); }
        }

        /* Navigation Bar */
        nav {
            background-color: var(--container-bg);
            border-bottom: 3px solid var(--border-color);
            padding: 10px 0;
            position: sticky;
            top: 0;
            z-index: 1000;
            text-align: center;
        }
        nav a {
            font-family: 'Press Start 2P', cursive;
            font-size: 0.8rem;
            margin: 0 15px;
            padding: 5px 10px;
        }

        /* Code Blocks */
        pre {
            background-color: var(--code-bg);
            border: 2px solid var(--border-color);
            padding: 20px;
            font-family: 'VT323', monospace;
            font-size: 18px;
            white-space: pre-wrap;
            word-wrap: break-word;
            box-shadow: inset 4px 4px 0px rgba(0,0,0,0.3);
            position: relative;
        }
        
        .code-comment {
            color: #888899; /* Grey for comments */
        }
        .code-keyword {
            color: #ff79c6; /* Pink for keywords */
        }
        .code-type {
            color: #8be9fd; /* Cyan for types */
        }
        .code-function {
             color: #50fa7b; /* Green for function names */
        }
        .code-string {
            color: #f1fa8c; /* Yellow for strings */
        }

        .copy-btn {
            position: absolute;
            top: 10px;
            right: 10px;
            background-color: var(--accent-color-3);
            color: var(--bg-color);
            border: 2px solid var(--border-color);
            padding: 5px 10px;
            font-family: 'Press Start 2P', cursive;
            font-size: 12px;
            cursor: pointer;
            box-shadow: 3px 3px 0px var(--border-color);
            transition: all 0.1s ease;
        }
        .copy-btn:active {
            transform: translate(3px, 3px);
            box-shadow: none;
        }
        
        /* Scroll reveal animation */
        .reveal {
            opacity: 0;
            transform: translateY(30px);
            transition: opacity 0.6s ease-out, transform 0.6s ease-out;
        }
        .reveal.active {
            opacity: 1;
            transform: translateY(0);
        }
        
        /* Image styling */
        img {
            max-width: 100%;
            height: auto;
            border: 3px solid var(--border-color);
            margin: 20px 0;
        }
        
        ul {
            list-style-type: none;
            padding-left: 0;
        }
        
        li::before {
            content: '>';
            color: var(--accent-color-2);
            font-weight: bold;
            display: inline-block;
            width: 1em;
            margin-left: -1em;
        }
        
        /* Footer */
        footer {
            text-align: center;
            padding: 40px 20px;
            margin-top: 50px;
            border-top: 5px solid var(--border-color);
            font-family: 'Press Start 2P', cursive;
            font-size: 0.8rem;
            color: var(--accent-color-3);
        }

    </style>
</head>
<body>

    <header>
        <h1><span class="typing-effect">CSE240: Linked List Labyrinth</span></h1>
        <p style="font-size: 24px; color: var(--accent-color-2);">Your Complete Guide to HW5</p>
    </header>

    <nav id="navbar">
        <a href="#level-1">LVL 1: Structs</a>
        <a href="#level-2">LVL 2: Pointers</a>
        <a href="#level-3">LVL 3: Linked Lists</a>
        <a href="#boss-battles">Boss Battles</a>
        <a href="#final-code">Final Code</a>
    </nav>

    <main>
        <div id="level-1" class="container reveal">
            <h2>LEVEL 1: FORGING YOUR CHARACTER (Structs & Enums)</h2>
            <p>
                Before we can venture into the labyrinth, we need to define our character's attributes. In C, we use a <code>struct</code> to group different data types into a single "record". For this quest, our character is a <code>studentRecord</code>.
            </p>
            <h3>The `studentRecord` Blueprint</h3>
            <p>
                Our struct holds all the essential stats for one student. Notice how it contains character arrays for strings, an integer for the ID, and a special <code>enum</code> type for the school year.
            </p>
            
            <pre><code class="language-c"><span class="code-keyword">struct</span> <span class="code-type">studentRecord</span> {
    <span class="code-type">char</span> studentName[MAX_NAME_LENGTH];
    <span class="code-type">char</span> major[MAX_NAME_LENGTH];
    <span class="code-type">schoolYearType</span> schoolYear; <span class="code-comment">// This is our special Enum type!</span>
    <span class="code-type">unsigned int</span> IDNumber;
    <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* next; <span class="code-comment">// The magic pointer that links to the next character!</span>
};</code></pre>

            <h3>The `enum` Scroll</h3>
            <p>
                An <code>enum</code> (enumeration) is a way to create a set of named integer constants. It makes our code more readable than using magic numbers like 0, 1, 2, 3. Here, `freshman` is secretly `0`, `sophomore` is `1`, and so on.
            </p>
            <pre><code class="language-c"><span class="code-keyword">typedef</span> <span class="code-keyword">enum</span> { freshman = 0, sophomore, junior, senior } <span class="code-type">schoolYearType</span>;</code></pre>
        </div>

        <div id="level-2" class="container reveal">
            <h2>LEVEL 2: MASTERING THE POINTER WAND (Memory Management)</h2>
            <p>
                Pointers are the magic wands of C. They don't hold data themselves; they hold the memory address *where* data lives. To create new student records on the fly, we must master memory allocation.
            </p>
            <h3>Casting `malloc()`</h3>
            <p>
                The <code>malloc()</code> spell (memory allocation) reserves a chunk of memory from the "heap". We tell it how much space we need (the size of one `studentRecord`), and it gives us back a generic pointer to that space. We then "cast" it to the correct pointer type.
            </p>
            <pre><code class="language-c"><span class="code-comment">// Reserve memory for one new student and get a pointer to it.</span>
<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* newNode = (<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>*)<span class="code-function">malloc</span>(<span class="code-keyword">sizeof</span>(<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>));</code></pre>
            
            <h3>The `free()` Spell of Banishment</h3>
            <p>
                Memory you allocate with `malloc()` is not automatically cleaned up! If you lose the pointer to it, that memory becomes a "memory leak," draining your system's power. The <code>free()</code> spell is essential. You must cast it on every piece of memory you're done with to return it to the system.
            </p>
             <pre><code class="language-c"><span class="code-comment">// When we delete a node, we must free its memory to prevent leaks.</span>
<span class="code-function">free</span>(nodeToDelete);</code></pre>
        </div>

        <div id="level-3" class="container reveal">
            <h2>LEVEL 3: NAVIGATING THE LABYRINTH (Linked Lists)</h2>
            <p>
                A linked list is a chain of `structs` (we call them "nodes"). Each node holds its own data and, crucially, a pointer to the `next` node in the chain. The last node's `next` pointer is `NULL`, signaling the end of the path.
            </p>
            
            <h3>Traversal: The Basic Move</h3>
            <p>
                To walk through the list, you create a temporary pointer (let's call it `current`) and start it at the head of the list. Then, you loop, moving the pointer forward with `current = current->next;` until you hit `NULL`.
            </p>
            <pre><code class="language-c"><span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* current = list; <span class="code-comment">// Start at the beginning.</span>
<span class="code-keyword">while</span> (current != NULL) {
    <span class="code-comment">// Do something with the 'current' node...</span>
    current = current->next; <span class="code-comment">// Move to the next node.</span>
}</code></pre>
        </div>
        
        <div id="boss-battles" class="container reveal">
            <h2>BOSS BATTLES: IMPLEMENTING THE CORE FUNCTIONS</h2>
            <p>These are the key functions from your homework. Master them, and you've conquered the level.</p>
            
            <h3 id="countNodes">Mini-Boss: `countNodes()`</h3>
            <p>A simple traversal to count every node in the list. This is your warm-up.</p>
            <pre><button class="copy-btn">Copy</button><code class="language-c"><span class="code-type">int</span> <span class="code-function">countNodes</span>()
{
    <span class="code-type">int</span> count = 0; <span class="code-comment">// Start counter at zero.</span>
    <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* current = list; <span class="code-comment">// Temporary pointer to walk the list.</span>

    <span class="code-keyword">while</span> (current != NULL) {
        count++; <span class="code-comment">// Add one for the current student.</span>
        current = current->next; <span class="code-comment">// Move to the next student.</span>
    }
    <span class="code-keyword">return</span> count; <span class="code-comment">// Return the total.</span>
}</code></pre>

            <h3 id="displayList">Mini-Boss: `displayList()`</h3>
            <p>Traverse the list and print each student's stats. The key challenge is converting the `schoolYear` enum back to a human-readable string.</p>
            <pre><button class="copy-btn">Copy</button><code class="language-c"><span class="code-keyword">void</span> <span class="code-function">displayList</span>()
{
    <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* current = list; <span class="code-comment">// Temp pointer to walk the list.</span>
    <span class="code-keyword">while</span> (current != NULL) {
        <span class="code-function">printf</span>(<span class="code-string">"\nStudent name: %s"</span>, current->studentName);
        <span class="code-function">printf</span>(<span class="code-string">"\nStudent major: %s"</span>, current->major);
        <span class="code-function">printf</span>(<span class="code-string">"\nID number: %d"</span>, current->IDNumber);

        <span class="code-comment">// Use a switch to translate the enum number to a string.</span>
        <span class="code-keyword">switch</span> (current->schoolYear) {
            <span class="code-keyword">case</span> freshman: <span class="code-function">printf</span>(<span class="code-string">"\nSchoolYear: freshman\n"</span>); <span class="code-keyword">break</span>;
            <span class="code-keyword">case</span> sophomore: <span class="code-function">printf</span>(<span class="code-string">"\nSchoolYear: sophomore\n"</span>); <span class="code-keyword">break</span>;
            <span class="code-keyword">case</span> junior: <span class="code-function">printf</span>(<span class="code-string">"\nSchoolYear: junior\n"</span>); <span class="code-keyword">break</span>;
            <span class="code-keyword">case</span> senior: <span class="code-function">printf</span>(<span class="code-string">"\nSchoolYear: senior\n"</span>); <span class="code-keyword">break</span>;
        }
        current = current->next; <span class="code-comment">// Move to the next student.</span>
    }
}</code></pre>
            
            <h3 id="swapNodes">Helper Mob: `swapNodes()`</h3>
            <p>A crucial helper function for sorting. It swaps the *data* inside two nodes but leaves their `next` pointers untouched. This is much more efficient than re-wiring the list structure.</p>
            <pre><button class="copy-btn">Copy</button><code class="language-c"><span class="code-keyword">void</span> <span class="code-function">swapNodes</span>(<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* node1, <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* node2)
{
    <span class="code-comment">// We need temporary variables to hold the data from node1 during the swap.</span>
    <span class="code-type">char</span> tempName[MAX_NAME_LENGTH];
    <span class="code-type">char</span> tempMajor[MAX_NAME_LENGTH];
    <span class="code-type">schoolYearType</span> tempSchoolYear;
    <span class="code-type">unsigned int</span> tempID;

    <span class="code-comment">// Use strcpy for strings and direct assignment for numbers/enums.</span>
    <span class="code-function">strcpy</span>(tempName, node1->studentName);
    <span class="code-function">strcpy</span>(tempMajor, node1->major);
    tempSchoolYear = node1->schoolYear;
    tempID = node1->IDNumber;

    <span class="code-function">strcpy</span>(node1->studentName, node2->studentName);
    <span class="code-function">strcpy</span>(node1->major, node2->major);
    node1->schoolYear = node2->schoolYear;
    node1->IDNumber = node2->IDNumber;

    <span class="code-function">strcpy</span>(node2->studentName, tempName);
    <span class="code-function">strcpy</span>(node2->major, tempMajor);
    node2->schoolYear = tempSchoolYear;
    node2->IDNumber = tempID;
}</code></pre>
            
            <h3 id="deleteNode">Final Boss: `deleteNode()`</h3>
            <p>This is where your pointer skills are truly tested. You must find the node, carefully re-wire the `next` pointers to bypass it, and then `free()` its memory. Handling the head of the list is a critical special case!</p>
            
            <pre><button class="copy-btn">Copy</button><code class="language-c"><span class="code-type">int</span> <span class="code-function">deleteNode</span>(<span class="code-type">char</span>* studentName_input)
{
	<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* current = list; <span class="code-comment">// To find the node.</span>
	<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* previous = NULL; <span class="code-comment">// To keep track of the node before.</span>

	<span class="code-comment">// Case 1: The list is empty.</span>
	<span class="code-keyword">if</span> (list == NULL) {
		<span class="code-keyword">return</span> -1; <span class="code-comment">// Nothing to delete.</span>
	}

	<span class="code-comment">// Case 2: The head node is the one to delete.</span>
	<span class="code-keyword">if</span> (<span class="code-function">strcmp</span>(list->studentName, studentName_input) == 0) {
		current = list;       <span class="code-comment">// Mark the head for deletion.</span>
		list = list->next;    <span class="code-comment">// The second node is now the new head.</span>
		<span class="code-function">free</span>(current);        <span class="code-comment">// Banish the old head!</span>
		<span class="code-keyword">return</span> 1;             <span class="code-comment">// Success!</span>
	}

	<span class="code-comment">// Case 3: Search the rest of the list.</span>
	<span class="code-keyword">while</span> (current != NULL && <span class="code-function">strcmp</span>(current->studentName, studentName_input) != 0) {
		previous = current;      <span class="code-comment">// Move 'previous' to where 'current' is.</span>
		current = current->next; <span class="code-comment">// Move 'current' one step forward.</span>
	}

	<span class="code-comment">// If we finished the loop and 'current' is NULL, the student wasn't found.</span>
	<span class="code-keyword">if</span> (current == NULL) {
		<span class="code-keyword">return</span> 0; <span class="code-comment">// Not found.</span>
	}
	<span class="code-keyword">else</span> { <span class="code-comment">// We found the node at 'current'.</span>
		<span class="code-comment">// Stitch the list: make the previous node's 'next' skip over 'current'.</span>
		previous->next = current->next;
		<span class="code-function">free</span>(current); <span class="code-comment">// Banish the found node.</span>
		<span class="code-keyword">return</span> 1; <span class="code-comment">// Success!</span>
	}
}</code></pre>
        </div>
        
        <div id="final-code" class="container reveal">
            <h2>TREASURE ROOM: THE COMPLETE `addSort()` FUNCTION</h2>
            <p>This is the final challenge, combining all your skills: duplicate checking, memory allocation, appending to the list, and finally, sorting the list using a Bubble Sort and your `swapNodes` helper. This is the complete solution to Q1.</p>
            <pre><button class="copy-btn">Copy</button><code class="language-c"><span class="code-type">int</span> <span class="code-function">addSort</span>(<span class="code-type">char</span>* studentName_input, <span class="code-type">char</span>* major_input, <span class="code-type">char</span>* schoolYear_input, <span class="code-type">unsigned int</span> IDNumber_input)
{
    <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* current = list;
    
    <span class="code-comment">// 1. Check for Duplicates</span>
    <span class="code-keyword">while</span>(current != NULL){
        <span class="code-keyword">if</span>(<span class="code-function">strcmp</span>(current->studentName, studentName_input) == 0){
            <span class="code-keyword">return</span> 0; <span class="code-comment">// Found a duplicate, fail.</span>
        }
        current = current->next;
    }

    <span class="code-comment">// 2. Create New Node</span>
    <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* newNode = (<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>*)<span class="code-function">malloc</span>(<span class="code-keyword">sizeof</span>(<span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>));
    <span class="code-function">strcpy</span>(newNode->studentName, studentName_input);
    <span class="code-function">strcpy</span>(newNode->major, major_input);
    newNode->IDNumber = IDNumber_input;
    newNode->next = NULL;
    
    <span class="code-keyword">if</span> (<span class="code-function">strcmp</span>(schoolYear_input, <span class="code-string">"freshman"</span>) == 0) newNode->schoolYear = freshman;
    <span class="code-keyword">else if</span> (<span class="code-function">strcmp</span>(schoolYear_input, <span class="code-string">"sophomore"</span>) == 0) newNode->schoolYear = sophomore;
    <span class="code-keyword">else if</span> (<span class="code-function">strcmp</span>(schoolYear_input, <span class="code-string">"junior"</span>) == 0) newNode->schoolYear = junior;
    <span class="code-keyword">else if</span> (<span class="code-function">strcmp</span>(schoolYear_input, <span class="code-string">"senior"</span>) == 0) newNode->schoolYear = senior;

    <span class="code-comment">// 3. Append Node to List</span>
    <span class="code-keyword">if</span> (list == NULL) {
        list = newNode;
    } <span class="code-keyword">else</span> {
        current = list;
        <span class="code-keyword">while</span> (current->next != NULL) {
            current = current->next;
        }
        current->next = newNode;
    }

    <span class="code-comment">// 4. Sort The List (Bubble Sort)</span>
    <span class="code-type">int</span> swapped;
    <span class="code-keyword">struct</span> <span class="code-type">studentRecord</span>* ptr1;
    <span class="code-keyword">if</span> (list == NULL || list->next == NULL) {
        <span class="code-keyword">return</span> 1; <span class="code-comment">// Already sorted.</span>
    }
    <span class="code-keyword">do</span> {
        swapped = 0;
        ptr1 = list;
        <span class="code-keyword">while</span> (ptr1->next != NULL) {
            <span class="code-keyword">if</span> (<span class="code-function">strcmp</span>(ptr1->studentName, ptr1->next->studentName) > 0) { 
                <span class="code-function">swapNodes</span>(ptr1, ptr1->next);
                swapped = 1;
            }
            ptr1 = ptr1->next;
        }
    } <span class="code-keyword">while</span> (swapped);

    <span class="code-keyword">return</span> 1; <span class="code-comment">// Success!</span>
}</code></pre>
        </div>

    </main>
    
    <footer>
        <p>MISSION COMPLETE!</p>
        <p>You have conquered the Linked List Labyrinth.</p>
    </footer>

    <script>
        // Smooth scrolling for navigation links
        document.querySelectorAll('nav a').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                document.querySelector(this.getAttribute('href')).scrollIntoView({
                    behavior: 'smooth'
                });
            });
        });

        // Add copy functionality to all copy buttons
        document.querySelectorAll('.copy-btn').forEach(button => {
            button.addEventListener('click', () => {
                const pre = button.parentElement;
                const code = pre.querySelector('code');
                const text = code.innerText;

                const textArea = document.createElement('textarea');
                textArea.value = text;
                document.body.appendChild(textArea);
                textArea.select();
                try {
                    document.execCommand('copy');
                    button.innerText = 'Copied!';
                } catch (err) {
                    console.error('Failed to copy text: ', err);
                    button.innerText = 'Error!';
                }
                document.body.removeChild(textArea);

                setTimeout(() => {
                    button.innerText = 'Copy';
                }, 2000);
            });
        });

        // Scroll reveal animation
        const revealElements = document.querySelectorAll('.reveal');
        const revealObserver = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    entry.target.classList.add('active');
                }
            });
        }, { threshold: 0.1 });

        revealElements.forEach(el => {
            revealObserver.observe(el);
        });

    </script>
</body>
</html>