<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aravind Sathesh</title>
    <description>The latest articles on DEV Community by Aravind Sathesh (@aravindsathesh).</description>
    <link>https://dev.to/aravindsathesh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2369469%2Fc9dc73a9-cc08-4b83-ab63-1d0cf81cb787.png</url>
      <title>DEV Community: Aravind Sathesh</title>
      <link>https://dev.to/aravindsathesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aravindsathesh"/>
    <language>en</language>
    <item>
      <title>Codeforces Buddy: A Focus Companion</title>
      <dc:creator>Aravind Sathesh</dc:creator>
      <pubDate>Sat, 03 Oct 2026 16:04:26 +0000</pubDate>
      <link>https://dev.to/aravindsathesh/codeforces-buddy-a-focus-companion-204i</link>
      <guid>https://dev.to/aravindsathesh/codeforces-buddy-a-focus-companion-204i</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My friend Rajeev (&lt;a href="https://codeforces.com/profile/ksrkasyap" rel="noopener noreferrer"&gt;ksrkasyap&lt;/a&gt; on Codeforces) wants to reach &lt;strong&gt;1800 by December 31&lt;/strong&gt;. He's currently at 1097, so that's a pretty big jump in about 90 days.&lt;/p&gt;

&lt;p&gt;I asked him what usually gets in the way when he tries to practise. It wasn't really that he didn't know what problems to solve. It was his phone.&lt;/p&gt;

&lt;p&gt;He'd sit down to solve something, pick up his phone for "a second", and suddenly a lot of time was gone.&lt;/p&gt;

&lt;p&gt;So I built him &lt;strong&gt;codeforces buddy&lt;/strong&gt;: an Android app that uses the phone as part of the focus system instead of letting it be the distraction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A focus tree, inspired by Forest.&lt;/strong&gt; He starts a session, puts his phone face-up beside his laptop, and a tree grows for 80 minutes through eight stages. If he leaves the app for more than 10 seconds, the tree dies. When Codeforces reports an accepted submission, the tree is saved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Today's problems, from the sheet he already uses.&lt;/strong&gt; Rajeev practises from TLE Eliminators' CP-31 sheet. The app reads his Codeforces history and figures out where he is on the sheet. Right now, he's on 1300, problem #19, so the next cards are &lt;strong&gt;#20, #21, #25, #29&lt;/strong&gt;, followed by the problems he skipped and then 1400 #1. He can swipe through them, start a focus session on one, or enter any other problem number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hints that try not to spoil the problem.&lt;/strong&gt; When a focus session starts, Gemma runs on his phone and turns the problem's editorial into four hints. They go from a vague direction to the important observation. The hints are face-down, and he has to hold a card to reveal the next one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A streak and daily progress.&lt;/strong&gt; There's one circle for each problem in the day's target. Solved problems are marked, and solving beyond the target gives a different marker. There's also a home screen widget showing the day's progress and streak.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I used throughout the app is simple: &lt;strong&gt;a problem is solved only when Codeforces says &lt;code&gt;OK&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There isn't a "mark as done" button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/9xSK1_QFeCY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Download the APK from the &lt;a href="https://github.com/Aravind-Sathesh/Hacktoberfest-2026/releases/latest" rel="noopener noreferrer"&gt;latest release&lt;/a&gt; (Android, arm64). GitHub Actions builds it on every tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Aravind-Sathesh" rel="noopener noreferrer"&gt;
        Aravind-Sathesh
      &lt;/a&gt; / &lt;a href="https://github.com/Aravind-Sathesh/Hacktoberfest-2026" rel="noopener noreferrer"&gt;
        Hacktoberfest-2026
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Hacktoberfest 2026&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Projects for the DEV Hacktoberfest 2026 challenges, one folder per week.&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Week&lt;/th&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Aravind-Sathesh/Hacktoberfest-2026/WEEK%200" rel="noopener noreferrer"&gt;WEEK 0&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;codeforces buddy&lt;/strong&gt;: a focus buddy for a friend's road to Codeforces 1800, with on-device Gemma&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Aravind-Sathesh/Hacktoberfest-2026" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;The app uses Expo SDK 57 / React Native 0.86 and TypeScript strict. It's Android-only.&lt;/p&gt;

&lt;p&gt;There are about 2,900 lines of app code and 35 unit tests covering the parts that actually make decisions: target calculation, CP-31 ordering, hint filtering, rate limiting and widget drawing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gemma 4 E2B, running on the phone
&lt;/h3&gt;

&lt;p&gt;The interesting part of the app is that the AI doesn't run on a server.&lt;/p&gt;

&lt;p&gt;It uses &lt;strong&gt;Gemma 4 E2B&lt;/strong&gt; (&lt;a href="https://huggingface.co/unsloth/gemma-4-E2B-it-qat-GGUF" rel="noopener noreferrer"&gt;model&lt;/a&gt;), an instruction-tuned, quantization-aware trained 4-bit GGUF model that is about 2.6 GB, through &lt;a href="https://github.com/mybigday/llama.rn" rel="noopener noreferrer"&gt;&lt;code&gt;llama.rn&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The model is downloaded once over Wi-Fi. Before offering the download, the app checks whether the phone has enough memory to run it.&lt;/p&gt;

&lt;p&gt;I first tested the prompts on my Mac using Ollama, then tried the same prompts on the smaller model.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Gemma actually does
&lt;/h3&gt;

&lt;p&gt;The first version used Gemma for a lot more.&lt;/p&gt;

&lt;p&gt;It picked problems, chatted with Rajeev when he was stuck, and even had some ideas about what to say when his tree died.&lt;/p&gt;

&lt;p&gt;Rajeev didn't like it.&lt;/p&gt;

&lt;p&gt;His main points were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2.6 GB is already a big download.&lt;/li&gt;
&lt;li&gt;If he wants to talk to a chatbot about a problem, he already has a much smarter one open on his laptop.&lt;/li&gt;
&lt;li&gt;He didn't see a reason for the app to duplicate that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I agreed, so most of that was removed.&lt;/p&gt;

&lt;p&gt;Gemma now has one job: &lt;strong&gt;generate hints from an editorial without giving away the solution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The pipeline is roughly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Codeforces blocks normal fetches, so a hidden WebView follows the contest page to the tutorial/editorial. Before passing anything to Gemma, the app removes every &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;When a focus session starts, Gemma generates exactly four hints using a JSON schema. The first two are supposed to point in the right direction, while the later ones get closer to the main observation.&lt;/li&gt;
&lt;li&gt;Plain code checks the generated hints. The first two can't use the problem's tags or copy six or more consecutive words from the editorial. Hints 3 and 4 are allowed to overlap more because they're supposed to contain the important observation.&lt;/li&gt;
&lt;li&gt;Generation happens in the background while Rajeev is reading the problem, so the hints are ready by the time he actually needs them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The point isn't to make a worse chatbot. It's to make a chatbot that &lt;strong&gt;doesn't immediately give him the answer&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why E2B?
&lt;/h3&gt;

&lt;p&gt;Rajeev's other complaint was the model size, so I tested smaller models before settling on E2B.&lt;/p&gt;

&lt;p&gt;I ran the same hint prompt on four editorials in his rating range:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemma3:1b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.8 GB&lt;/td&gt;
&lt;td&gt;Often vague or wrong, and relied heavily on terminology from the editorial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;gemma4:e2b&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.6 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Produced useful hint ladders on 3 of 4 problems, around 3 seconds each on my Mac&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemma4:12b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;Better overall, but not practical for this use case on his phone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 1B model wasn't reliable enough for what I wanted. E2B was the smallest model that I was reasonably happy with, so that's what stayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything else is regular code
&lt;/h3&gt;

&lt;p&gt;One decision made the app much easier to reason about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code computes numbers. The model writes text.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model doesn't decide how many problems Rajeev should solve, which problems come next, or how busy his day is.&lt;/p&gt;

&lt;p&gt;Those are all deterministic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Daily target:&lt;/strong&gt; 4 problems on weekdays and 6 on weekends, reduced based on his busy time from the calendar. On contest days, the target becomes 2 warm-ups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CP-31 queue:&lt;/strong&gt; TLE Eliminators' sheet has 372 problems across ratings from 800 to 1900. It ships with the app. The queue starts from Rajeev's most recent solved problem rather than his current Codeforces rating. There's also a practice-level setting if he wants to jump ahead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codeforces API:&lt;/strong&gt; Requests are serialized and limited to one every 2 seconds. The 2.3 MB problem set is cached for a day, and the day's suggestions don't change every time the app refreshes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I initially had Gemma involved in choosing problems too, but there wasn't much benefit to that. A fixed, explainable order works better for something Rajeev does every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Feedback Loop
&lt;/h2&gt;

&lt;p&gt;I gave Rajeev a build on Friday night and let him use it.&lt;/p&gt;

&lt;p&gt;He found a lot of things wrong with it.&lt;/p&gt;

&lt;p&gt;I fixed them and gave him another build on Saturday. Most of the app ended up being a response to something he actually complained about.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;He said&lt;/th&gt;
&lt;th&gt;What changed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Too much refreshing."&lt;/td&gt;
&lt;td&gt;Load once when the app opens, then use pull-to-refresh. Switching tabs doesn't reload everything.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"3 GB is too much. I'd just ask the chatbot on my laptop."&lt;/td&gt;
&lt;td&gt;Removed the chatbot and narrowed Gemma down to spoiler-free hints.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"When I keep pressing next, it still shows problem 1."&lt;/td&gt;
&lt;td&gt;Added a proper swipeable card stack with the sheet position shown as &lt;code&gt;#20 · CP-31 · 1300&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Refreshing changes the suggested problems."&lt;/td&gt;
&lt;td&gt;Suggestions are selected once per day and stored.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"I solve from TLE CP-31."&lt;/td&gt;
&lt;td&gt;CP-31 became the default source, continuing from his last solved problem.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"This doesn't really make sense as a mobile app. I barely solve on mobile."&lt;/td&gt;
&lt;td&gt;That's actually why the app is on mobile.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last point was something I had to think about.&lt;/p&gt;

&lt;p&gt;He's right. He shouldn't be solving problems on his phone.&lt;/p&gt;

&lt;p&gt;The problem statement, editor and Codeforces judge are all on his laptop. The phone is just sitting beside it, face-up and awake using &lt;code&gt;expo-keep-awake&lt;/code&gt;, with the tree growing.&lt;/p&gt;

&lt;p&gt;If he picks it up and starts scrolling, the tree starts dying.&lt;/p&gt;

&lt;p&gt;So the app isn't trying to move competitive programming onto a phone. It's trying to make the phone less useful as a distraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built With Agents
&lt;/h2&gt;

&lt;p&gt;I split the development work between two agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code was the planner.&lt;/strong&gt; It wrote the initial project spec and broke the work into tasks in &lt;code&gt;PLAN.md&lt;/code&gt;. I also gave it the project rules in &lt;code&gt;AGENTS.md&lt;/code&gt;, including things like conventional commits, no notifications because Rajeev didn't want them, and the privacy requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot was the implementer.&lt;/strong&gt; It took those tasks and actually built the app: writing the React Native code, adding the features, and writing the tests. I reviewed what it produced, fixed issues when needed, and fed the resulting changes back into the next tasks.&lt;/p&gt;

&lt;p&gt;The split worked pretty well for this project because Claude was focused on deciding &lt;strong&gt;what needed to be built and in what order&lt;/strong&gt;, while Copilot was focused on &lt;strong&gt;actually building it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;GitHub Actions handles the last part: whenever I push a &lt;code&gt;v*&lt;/code&gt; tag, it builds the Android APK and attaches it to a GitHub Release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;The open model ended up mattering less because it was "AI" and more because of what the app is doing with personal data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;His calendar.&lt;/strong&gt; The app uses his calendar only to estimate how busy he is that day. Events are immediately reduced to &lt;code&gt;{ start, end }&lt;/code&gt;; titles, locations and attendees are discarded and never stored, logged or sent to the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;His practice history.&lt;/strong&gt; Focus sessions, dead trees and skipped sessions are all stored locally on his phone. There is no backend, analytics system or account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cost.&lt;/strong&gt; There is no API key and no per-request bill. Once the model is downloaded, generating hints doesn't cost anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline use.&lt;/strong&gt; Once the model and the day's data are on the phone, the hint system doesn't need an internet connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Being able to change it.&lt;/strong&gt; Since the model is open, I could actually compare the 1B, E2B and 12B models and see which one made sense for this particular task. If I want to fine-tune a model around the kinds of mistakes Rajeev makes, I can experiment with that too.&lt;/p&gt;

&lt;p&gt;For this app, running the model locally wasn't just a technical experiment. It let me build something around one person's actual habits without sending those habits to a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 4 E2B runs on-device through llama.cpp (&lt;code&gt;llama.rn&lt;/code&gt;) and turns editorials into graded hints, with filtering to reduce spoilers. I also compared it against Gemma 3 1B and Gemma 4 12B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of GitHub Copilot:&lt;/strong&gt; Copilot implemented part of the app from the written project plan, and GitHub Actions builds and publishes the APK on every tag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks to Rajeev for being a brutally honest first user.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
