Contextualizing Programming Problems to Reduce Cheating

Why Generic Assignments Invite Copying

Every semester, thousands of CS students are given the same problem: "Write a function that computes the Fibonacci sequence" or "Implement a stack data structure." The expected outputs are identical for every student. Copying is trivial — a quick Ctrl+C, Ctrl+V from a friend or a Stack Overflow snippet, and the assignment is done. The grader sees the same logic, same variable names, same comments.

This isn't just a problem for academic integrity. It also creates a disservice to students who don't learn the material. And it wastes instructors' time chasing down similarities with tools like MOSS, JPlag, or Codequiry. But there's a better way: design the assignment so that the correct answer is unique to each student.

The most effective anti-plagiarism strategy is not detection — it's prevention through context.

When every student's submission must differ to be correct, copying becomes immediately visible. This is the core idea behind contextualized programming problems.

How Contextualization Works

Contextualization means embedding student-specific information directly into the problem requirements. This could be a student ID, a unique input file, a random seed, or even a personalized narrative (e.g., "Your university's mascot is the Bear; write a program that prints bear emoji if the user enters your birth month"). The key is that the correct output depends on data that only the student has or can retrieve from a provided dataset.

Here's a concrete example. Suppose you want to teach sorting algorithms. Instead of:

// Generic assignment
Write a program that sorts an array of integers.
Input: 5 2 8 1 9
Output: 1 2 5 8 9

You give each student a parameterized version:

// Contextual assignment
Given a directory of data files: student_<ID>.csv
Each file contains 100 numbers. Sort the numbers in descending order.
But: if your student ID ends with an even digit, sort ascending.

Then output the sorted list and the sum of the first 10 elements.

Now, two students who copy each other's code will produce the wrong order or the wrong sum. The grader can instantly spot duplicates by checking the output against the expected result for that student's ID.

This technique is not limited to sorting. It works for virtually any data structure or algorithm assignment: BST insertion order, graph traversal paths, hashing with custom hash functions, regex matching against student-specific patterns, etc.

Parameterized Test Cases: The Technical Implementation

The cleanest way to implement contextualization is through parameterized test harnesses. You provide a skeleton that reads a unique identifier (e.g., environment variable, command-line argument, or a file in the repo) and then behaves differently for each student. Here's a Python example:

import os

def main():
    student_id = os.environ.get('STUDENT_ID')
    if not student_id:
        raise ValueError("Set your STUDENT_ID variable")

    # Use the last two digits to modify behavior
    offset = int(student_id[-2:]) % 10

    # Load a shared data file
    with open('data.csv') as f:
        data = [int(line.strip()) for line in f]

    # Shift each element by offset (for example)
    transformed = [x + offset for x in data]

    # Now the student must implement the core function
    result = core_algorithm(transformed)

    # Output must include a checksum that depends on student_id
    checksum = sum(ord(c) for c in student_id) % 1000
    print(f"Result: {result}, Checksum: {checksum}")

def core_algorithm(arr):
    # Student fills in this function
    pass

In this setup, the student must write the core_algorithm and also handle the contextual details correctly. Copy-pasting from another student will fail because the checksum will differ — even if the algorithm is identical, the output won't match the expected checksum for that student.

You can automate grading by having each student's test environment inject their STUDENT_ID. Tools like GitHub Classroom and Autolab support per-student environment variables natively. Pair this with a simple script that compares the checksum, and you have an automated grader that detects mismatched outputs — and flags likely plagiarism.

At the University of California, San Diego, this approach reduced uncorrected copy-paste cases by over 35% in their introductory data structures course. Instructors reported that the remaining plagiarism cases were easier to adjudicate because the output differences were immediately obvious.

Including Local Data and Unique Input Files

Another effective variant is to give each student a unique input file that contains different data but follows the same format. For example, in a graph traversal assignment, each student gets a CSV of edges that describes a different graph. The graph may have the same number of nodes but different connections. A student who copies the solution from a friend will likely get incorrect traversal results because the friend's graph structure is different.

Generating these files is straightforward with a script:

import random, csv, os

for student_id in student_ids:
    # Generate a random graph with controlled density
    nodes = 20
    edges = [(random.randint(0, nodes-1), random.randint(0, nodes-1)) for _ in range(30)]
    # Deduplicate
    edges = list(set(edges))
    with open(f'student_{student_id}_graph.csv', 'w') as f:
        writer = csv.writer(f)
        writer.writerows(edges)

Then distribute each file to the respective student via your LMS or Git repository. The grader can also have a mapping file so that when reviewing submissions, they can feed the correct input file to the student's program and verify the output. Any two submissions with identical output when given different inputs are almost certainly copies.

Making It Harder for AI Tools

Contextualization also frustrates large language models. Tools like ChatGPT and GitHub Copilot perform poorly when the prompt requires knowledge of specific local context — a student ID, a unique file on a private server, or a dataset that does not exist in the model's training data. The model may generate a generic solution, but it will not match the required unique output. For example, if you ask ChatGPT to "write a Python function that reads from /var/assignment/student_1234_input.txt and outputs the median," the model has no knowledge of that file's contents. The resulting code may be syntactically correct but will not produce the expected answer. Combined with parameterized checks, this makes it much harder for students to pass off AI-generated code as their own.

Of course, a determined student could still run the AI-generated code against their own input file manually, but that requires understanding and debugging — which defeats the purpose of cheating. The contextual barrier raises the effort threshold well above the "paste and submit" level.

Common Misconceptions and Pitfalls

"Contextualization makes grading harder." Not if you automate. With a parameterized test harness and a grader script that uses the same student-specific inputs, grading can be fully automated. The same script that generates the input files can also compute the expected outputs.

"Students will share their unique context." Yes, they could, but then they'd have to also share their entire implementation and trust that the grader doesn't detect the same output across different contexts. If you use a checksum that depends on the student ID, transferring the context doesn't help — the checksums will still mismatch. Also, you can easily detect if one student's input file appears in another student's repository.

"This only works for small classes." Not true. Large-scale courses (500+ students) at institutions like Georgia Tech and the University of Illinois use randomized input generation successfully. The key is automation: scripts to generate per-student files, a test runner that reads environment variables, and a grading pipeline that compares output to expected values stored in a database.

"What about open-ended assignments like web applications?" You can still contextualize them. For a web project, assign each student a unique theme color, a required footer with their initials, or a database schema that uses their ID as part of a table name. These superficial differences still make copying visible — two sites with identical code but different colors are easier to spot.

Balancing Context with Learning Goals

Some instructors worry that adding too much context distracts from the core learning objective. That's a valid concern. The context should be orthogonal to the skill being assessed. For example, if you want to test dynamic programming, have all students solve the same knapsack problem but with different item sets. The algorithm is the same; the data varies. The student must still understand and implement the DP recurrence — there's no shortcut. The context is just a thin wrapper that prevents wholesale copying of output-specific solutions.

Another approach is to use a narrative context that makes the problem more engaging while still varying. For a statistics assignment, give each student a different dataset (e.g., "You are analyzing sales data for the city where you were born" – use a randomly assigned city). The dataset structure is identical, but the numbers differ. This actually reinforces the learning goal of working with real data.

Tools That Complement Contextualization

Even with the best assignment design, some plagiarism will still occur — students may copy logic despite differing output. That's where detection tools come in. A platform like Codequiry (a code plagiarism checker) can analyze the source code structure itself, catching similarities that output-based checks miss. By combining contextualized assignments with a robust similarity scan, you build a two-layer defense: the first layer makes copying obvious and inconvenient; the second catches the subtle cases. Many universities pair these techniques to reduce overall cheating rates significantly.

For example, a 2022 pilot at a large public university used contextualized assignments in three core CS courses. They then ran all submissions through Codequiry's AI and similarity detection. The combination flagged 18% more suspicious pairs than a standalone similarity check would have — largely because the contextual mismatches gave instructors an easy manual verification path.

Getting Started

Begin small: pick one assignment this semester and add a single parameter that varies per student. Use student IDs or email addresses as seeds. Set up a simple test runner that checks output against a CSV of expected results. Once you see how well it works — both for reducing plagiarism and for engaging students — you can scale to more assignments.

The upfront effort (writing a script to generate unique inputs, setting up environment variables, modifying the assignment spec) pays off quickly. You'll spend less time at the end of the term adjudicating plagiarism cases and more time teaching.

Frequently Asked Questions

Doesn't this make assignments more complex for students?
Only slightly. The core problem is unchanged. The extra complexity is minimal — often just reading an environment variable or loading a specific file. Provide a clear skeleton and instructions, and most students adapt quickly.

Can students still cheat by copying the algorithm and then running it with their own context?
Yes, but that requires understanding the algorithm enough to adapt it. If they can do that, they have likely achieved the learning outcome. The goal is to prevent mindless copy-paste, not to eliminate all forms of collaboration.

Will AI detectors like Codequiry still work on contextualized assignments?
Yes. AI detection looks at coding style, structure, and statistical signatures — not just output. Contextualized assignments don't interfere with that analysis. In fact, they provide an additional signal: if the AI-generated code fails to incorporate the context correctly, it's a red flag.

How do I prevent students from sharing their unique input files?
Store input files on a private repository or LMS that requires authentication to download. If a student uploads their input file to a public GitHub repo, you can detect it with code search. Most students don't bother because the file alone is useless without the assignment code.