The problem is not the problem
The problem, when you sit down to grind Leetcode or CSES, is never the problem itself. The problem is the thirty seconds between reading the statement and typing your first line of code. It is the friction of opening a file, remembering the right template, wiring the input, running it, seeing the wrong answer, comparing against the expected output, and then, an hour later, forgetting whether you did this problem before.
Between January 2022 and March 2026 I built a small pile of tooling around competitive programming. None of it is impressive on its own. Together it is a rough diary of what a serious CP participant actually spends time on: not the algorithms, but the sixteen inches of desk between the algorithm and the submission.
Leetcode-practice (January 2022)
This is the oldest repo. It is a folder with somewhere north of three hundred subfolders, each named after a Leetcode slug, each containing a .py file and sometimes a note. It was created on 10 January 2022, back when I was a college student who had just discovered that being able to solve the medium-difficulty tree problem was a real skill you could get better at with practice.
The interesting thing about the repo is not the solutions. The solutions are what you would expect: one-file Python, no test wrapper, a lot of two-pointer and hashmap and BFS. The interesting thing is that an external tool pushed the folder structure itself. Leetcode has a Chrome extension called LeetHub that pushes your accepted solution to GitHub the moment you accept it. So the repo is really a diary of my Leetcode sessions, not a designed artifact. Every folder name maps to an exact URL. Every file is stamped with an accept.
That felt low-effort at the time. It became the substrate for everything downstream.
Leetcode-Stats-Scraping-Heroku (June 2022)
Once the diary existed, I wanted to see the diary as a graph. I wanted to know how my rank moved week over week, whether I was doing more mediums than easies, and whether I was slacking off. Leetcode's own profile page has all of this, but it lives in a single-page React app with class names like .mr-\[5px\].text-base.font-medium.leading-\[20px\].text-label-1.dark\:text-dark-label-1. Not exactly a stable API.
So I wrote a Selenium scraper. temp.py, roughly a hundred lines, a save_data_to_mongo function that maximized the browser, waited ten seconds, grabbed eight CSS selectors, dumped a document into MongoDB Atlas keyed by today's date. Then apscheduler at cron hour 3. Then a Procfile with clock: python temp.py, runtime.txt pinning Python 3.7.8, and a Heroku free-tier dyno that ran the job every morning at 3 AM IST while I was asleep.
The scraper is embarrassing to read now. The selectors are copy-pasted directly out of Chrome DevTools, escape characters and all. There is a hardcoded MongoDB URI in the source (rotated since; I checked). There is a stanza of Selenium setup code repeated verbatim twice, same GOOGLE_CHROME_BIN binary_location, same headless flags, same driver instantiation, because I kept switching between local and remote and never cleaned up. The comment # To replace this part of code every find element must be upgraded to the newly published selinium syntax is still there. It never got upgraded.
But every night for months, that dyno woke up, walked into my Leetcode profile, wrote a row to Mongo, and went back to sleep. When Heroku killed its free tier in November 2022 the pipeline died and I never revived it. The Mongo collection still exists somewhere with about six months of data in it.
leetcode-streamlit-anaysis (June 2022, private)
The natural next step. Same week as the scraper. A Streamlit dashboard that read the same Mongo collection and drew line charts of rank over time and stacked bars of easy/medium/hard split. Never deployed publicly. The dashboard's job was to make the graph visible to me on Sunday nights so I could feel bad or good about the week. Private because it hardcoded my username and connection string. That instinct, dashboard for an audience of one, would show up again three years later in a much larger form.
Sublime_Build_Python_CP (August 2022)
A build variant defined inside cppython.sublime-build that changed how the desk actually felt.
{
"name": "CP",
"windows": {
"shell_cmd": "python \"${file}\" < input.txt > output.txt"
}
}That is the whole innovation. A Sublime Text build variant that runs the current Python file with stdin redirected from input.txt and stdout captured to output.txt. Ctrl+Shift+B, pick "Python - CP", and you go from editing to a filled output.txt in one keystroke.
Before this variant, I was doing the standard beginner move: paste the sample input into a triple-quoted string at the top of the file and read from it. That works for one test case. It falls over the moment you want to keep the sample and the actual test on disk, or diff your output against expected, or switch problems without editing the source. The build variant made the file, the input, and the output three separate artifacts sitting side by side in the same folder. That is the shape most CP judges expect anyway. Once you have that shape, you stop rewriting your test loop for every problem.
Python-CP-Tempalte-Meta-Hacker-Cup (August 2022)
The Meta Hacker Cup template was born from a specific pain: Hacker Cup ships you a text file with T test cases, expects you to output Case #i: <answer> for each, and does not care about how you got there. If you built your solution reading input() one line at a time and printing as you go, you already have what you need. You just need a place to put the boilerplate.
The main.py in this repo is my template for that. The typo in the folder name (Tempalte) is period. The template is the outer for _ in range(int(input())) loop with a Case #<n>: <out> print at the bottom. Everything else is the specific problem I was warming up on, a grid where you place lasers marked ^ and check that no laser sees another. The included input.txt has three cases: 1 3 / .^., 3 1 / . / . / ., and 4 4 / ..^. / ..^. / .... / ...^.
I never made it far in the actual Hacker Cup. The template was the point. Every August when Hacker Cup registration opened, I would open this repo, copy main.py to a new folder, replace the test function, and the outer loop and the Case # formatting would already be correct. That is what a template is for.
cses-practice (June 2025)
Three years later, halfway through my working life, I opened CSES and started the Introductory Problems set on 28 June 2025. One folder, Introductory_Problems/, with four files: increasing_array.py, missing_number.py, repetetions.py, weird_number.py. Ten more lines of setup and I could have kept going. I did not.
What is interesting to me about the repo is that the files are annotated. weird_number.py ends with a #Learnings: docstring: "Forgot to print final 1: Printed inside loop but skipped post-loop value. Always include the terminal value when loop ends before printing it." Followed by a # Prevention: docstring. Twenty-three-year-old me would have shipped the file and moved on. Twenty-six-year-old me had learned that the value is in the note, not the solve.
Also in this class of thing
Lc-notes (July 2022, private). A markdown vault of the pattern behind each problem I solved. Not the code, the shape. "This is a monotonic stack because you need to know the next-greater element." Private because most of the content is one-line and half-formed.
Leet_Insights (March 2026, private, TypeScript). The 2026 return of the Streamlit dashboard from 2022, this time in TypeScript, this time reading a modern API instead of a Selenium scraper. Same instinct, four years apart.
What the tooling taught me
The one thing I did not expect from writing these is that the tooling itself teaches you the same lesson the problems do: identify the exact friction, write the smallest thing that removes it, keep it. A Sublime build variant is not a project. A Heroku dyno running one Selenium scraper is not a project. A folder of learnings comments at the bottom of solved files is not a project. Each of them is a fifteen-minute idea I actually shipped, and each one shortened the loop between reading a problem and knowing whether I could solve it.
The problems are the visible part. The desk is the load-bearing part.
See also
- /blog/undergrad-lab-repos-what-i-built-on, same PSG Tech era, complementary to lab work.
- /blog/college-python-utility-scripts, parallel afternoon-script mindset.