The Assignment That Broke Every Plagiarism Checker

It was a third-year Data Structures course at a large public university, and Professor Aris Thorne had seen it all. For the final project, he assigned a classic: implement a red-black tree with insertion, deletion, and a basic search function. The spec was precise—eight required public methods, a specific package structure, JUnit tests provided. He ran the 187 submissions through the department's standard tool, MOSS, and got the usual scatter plot of similarity scores. A few pairs flagged, the rest clean. He graded, submitted final marks, and thought nothing of it.

Two weeks later, a teaching assistant reviewing the code for a tutorial example noticed something odd. Two submissions, from students in different lab sections, had nearly identical helper functions for the tree rotation. Not copied-and-pasted identical, but structurally identical. The variable names were different. One used while loops where the other used for loops. The comments were in different places. But the logic, the step-by-step algorithmic fingerprint, was the same. MOSS had scored them at 12% similarity—well below the 60% threshold that triggered a review.

The TA dug deeper. Then wider. By the end of the day, they had found not two, but forty-three submissions sharing the same core algorithmic skeleton. The students had engineered a distributed, collaborative cheat. They hadn't shared code. They had shared logic.

"We weren't looking for copied code. We were looking for copied thought. And our tools were blind to it." – Professor Aris Thorne, in a later departmental review.

This incident, now a semi-legendary case study in academic integrity circles, didn't involve AI. It predated ChatGPT by years. It exposed a critical vulnerability in how we conceptualize and detect plagiarism in a programming context. We built detectors to find matching strings and tokens, but students evolved to share solutions at a higher level of abstraction. The red-black tree assignment broke the checker because the checker was looking for the wrong thing.

How Traditional Similarity Detection Works (And Why It Fails)

Most code plagiarism detectors, from the venerable MOSS (Measure of Software Similarity) to JPlag and older commercial systems, rely on a pipeline that reduces code to a comparable fingerprint. The process usually looks like this:

  1. Normalization: Strip comments, whitespace, and sometimes standardize identifiers (turning all variable names to var1, var2, etc.).
  2. Tokenization: Convert the source code into a stream of tokens—keywords, operators, identifiers, literals.
  3. Fingerprinting: Use a rolling hash algorithm (like winnowing) to create a set of "fingerprints" from overlapping sequences of tokens (e.g., 5-token chunks).
  4. Comparison: Compare the fingerprint sets between submissions. The percentage of shared fingerprints becomes the similarity score.

This method is excellent at catching direct copy-paste, even with light reformatting or renamed variables. Its weakness is fundamental: it is syntax-bound, not logic-bound. Two programs can implement the same algorithm with zero overlapping token fingerprints.

Consider a simple function to find the maximum value in an array of integers.

// Version A: "for" loop, standard idiom
public int findMax(int[] arr) {
    int max = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}
// Version B: "while" loop, different variable names
public int getMaximum(int[] numbers) {
    int currentMax = numbers[0];
    int index = 1;
    while (index < numbers.length) {
        if (numbers[index] > currentMax) {
            currentMax = numbers[index];
        }
        index++;
    }
    return currentMax;
}
// Version C: Using Streams (Java 8+)
public int findMax(int[] arr) {
    return Arrays.stream(arr).max().getAsInt();
}

A token-based checker will see Version A and B as different. Version C is a total outlier. Yet all three satisfy the same requirement identically. The "red-black tree conspiracy" exploited this exact gap. Students agreed on the algorithmic approach: "We'll use the standard CLRS pseudocode, but everyone implements it in their own syntactic style." The result was a set of programs that were logically plagiarized but syntactically distinct.

The Evolution of Student Collusion: From Copying to Engineering

The naive cheater copies a file and changes the header comment. That era is over. Modern student collusion is a sophisticated exercise in software engineering itself. Here are the patterns that emerged from the red-black tree case and subsequent investigations:

  • Algorithmic Skeleton Sharing: Groups agree on the high-level structure, class diagrams, and key algorithm steps before coding independently. This yields identical logic flows with unique surface-level code.
  • Systematic Obfuscation Pipelines: A single "source solution" is run through a series of automated transformations: renaming all identifiers, altering loop constructs (for to while), reordering independent statements, adding dead code or redundant variables, and changing code formatting. Simple scripts can do this.
  • Cross-Language Translation: In courses that allow multiple languages, a solution written in Python is translated to Java or C++ by another student. The logic is identical, the tokens are completely different.
  • API Contract Exploitation: Students adhere strictly to the required public method signatures but coordinate on the private implementation details. Since many checkers focus on public interfaces for normalization, the shared internals go unnoticed.

This isn't mere cheating. It's a form of adversarial machine learning, where students probe and learn the detection model's decision boundary.

Moving Beyond Tokens: The Structural and Semantic Frontier

Catching this evolved form of plagiarism requires detectors to operate at a higher level of abstraction. The next generation of tools, including advanced modes in platforms like Codequiry, are moving in this direction. The key is to analyze the program's structure and semantics, not just its text.

1. Abstract Syntax Tree (AST) Comparison

An AST represents the grammatical structure of the code, ignoring superficial differences. Comparing ASTs can reveal logical similarity even when syntax varies.

# A simplified AST representation for `if (x > y) { max = x; }`
IfStatement
├── Condition: BinaryExpression (operator: >)
│   ├── Left: Identifier (name: "x")
│   └── Right: Identifier (name: "y")
└── ThenBlock: ExpressionStatement
    └── Assignment (operator: =)
        ├── Left: Identifier (name: "max")
        └── Right: Identifier (name: "x")

Two code snippets that generate nearly isomorphic ASTs are implementing the same logic. Tools can hash subtrees or compute edit distances between ASTs. This catches the for-to-while transformation, as the loop subtree structure remains similar.

2. Control Flow Graph (CFG) Analysis

A CFG maps all possible paths of execution through a program. It's a graph where nodes are basic blocks (straight-line code) and edges represent jumps/branches. Two programs with isomorphic CFGs are executing the same algorithm, regardless of variable names or even some statement ordering.

Comparing CFGs is computationally harder but incredibly powerful for detecting coordinated logic plagiarism in complex assignments like graph algorithms or state machines.

3. Program Dependence Graph (PDG) and Slicing

This is the heavyweight champion of semantic analysis. A PDG incorporates both control flow and data flow dependencies. It shows which statements affect which others. Using PDGs, you can perform "program slicing"—extracting only the statements relevant to a particular variable at a point in the program.

"When you compare slices for the same output across different submissions, you're comparing the core computational essence. Everything else is just decoration." – Dr. Lena Kovač, researcher in software similarity.

If two students compute the inorder traversal of their red-black tree using slices that are structurally identical, they are almost certainly collaborating at a deep level, even if one uses recursion and the other uses an explicit stack.

4. Metric-Based Clustering

Sometimes, you don't need to prove identical logic; you just need to spot statistical outliers. By extracting a vector of software metrics for each submission, you can cluster them.

  • Cyclomatic Complexity
  • Halstead Volume and Difficulty
  • Number of AST nodes
  • Depth of inheritance tree
  • Coupling between objects

In the red-black tree case, a later analysis showed that the 43 suspect submissions formed a tight cluster in a high-dimensional metric space, distinct from the truly independent solutions. Their structural complexity, comment-to-code ratio, and error-handling patterns were anomalously similar.

Designing the Plagiarism-Resistant Assignment

Detection is a reactive arms race. The superior strategy is proactive: design assignments that are inherently harder to collude on without engaging in the actual learning objective.

The fatal flaw of the red-black tree assignment was its determinism. There is one canonical, optimal way to implement a red-black tree from a textbook. The room for creative, divergent, yet correct implementation was minimal.

Here’s how to fix it:

Inject Controlled Variation

Provide each student with a unique seed parameter that subtly alters the specification.

// Instead of "implement a red-black tree"
// Assignment: Implement a balanced BST for a custom scenario.
// Your student ID modulo 5 determines your scenario:
// 0: Tree nodes store (key, priority). Rebalance using treap logic after insert.
// 1: Use the "left-leaning red-black" variant as per Sedgewick.
// 2: Implement deletion using the "top-down" method instead of the standard approach.
// 3: Add a `rangeCount(low, high)` method that must be O(log n).
// 4: Your tree must be serializable to JSON using this custom visitor pattern...

Now, sharing logic becomes exponentially harder because the target is moving. A solution for variant 1 is useless to a student with variant 3.

Require Integration with Unique Artifacts

Make the assignment part of a larger, personalized code ecosystem. "Integrate your red-black tree into the provided graph simulation framework, using it to manage the priority queue for Dijkstra's algorithm. Your graph data is generated from your student ID." The core algorithm might be similar, but the integration points, data flows, and debugging artifacts will diverge wildly, creating a rich fingerprint for both manual and automated review.

Emphasize Process Over Product

Incorporate elements that trace the development journey, which is nearly impossible to copy convincingly.

  • Require periodic git commits with meaningful messages over the project timeline.
  • Include a "design journal" document explaining key decisions and alternative approaches considered.
  • Mandate unit tests for edge cases the student must identify themselves.

A copied final product won't have a coherent, gradual commit history or a personalized journal.

Building a Multi-Layered Detection Strategy

No single tool is sufficient. Departments need a defense-in-depth approach.

  1. First Pass - Traditional Token Similarity (MOSS/JPlag): Catches the low-effort copy-paste and obfuscated copies. It's fast and effective for its niche.
  2. Second Pass - Structural AST/CFG Analysis: Run a subset of submissions (or those with middling similarity scores) through a tool capable of deeper analysis. This catches the "engineered similarity" cases.
  3. Third Pass - Metric-Based Anomaly Detection: Use scripts to extract code metrics and look for statistical clusters. A tight cluster of 20+ submissions in a class of 200 is a red flag warranting manual review.
  4. Fourth Pass - Manual "Spot Check" Review: For large classes, randomly select 10% of submissions for a 15-minute manual code review by a TA or instructor. Focus on design choices, commenting style, and error handling—the human fingerprint. This often catches collusion that machines miss.

The goal isn't to achieve 100% detection. It's to raise the cost of cheating to the point where simply doing the assignment becomes the easier path. When students know the system uses Codequiry's advanced structural analysis mode alongside metric clustering, the incentive to engage in sophisticated collusion drops sharply.

The Ethical and Educational Imperative

This isn't just about catching cheaters. It's about fairness to the vast majority of students who work independently. When 43 students share a logic skeleton, they distort the grading curve and devalue the achievement of the student who struggled through the problem alone.

More importantly, the student who copies logic learns nothing. The red-black tree is a fundamental data structure; misunderstanding it weakens their foundation for algorithms, databases, and systems design. They may pass the course but fail the interview, and ultimately, fail as engineers. Our responsibility is to ensure the learning outcome is met. Sometimes, that means building a better detector. Always, it means building a better assignment.

The red-black tree assignment didn't just break a checker. It broke a simplistic mindset. Code plagiarism isn't about text matching anymore. It's a problem of computational equivalence, behavioral analysis, and pedagogical design. Solving it requires tools and techniques that see the forest for the trees—the logic for the syntax.